|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/rand"
|
|
|
|
"encoding/hex"
|
|
|
|
"errors"
|
|
|
|
"flag"
|
|
|
|
"fmt"
|
|
|
|
"github.com/dedis/kyber"
|
|
|
|
"github.com/simple-rules/harmony-benchmark/blockchain"
|
|
|
|
"github.com/simple-rules/harmony-benchmark/client"
|
|
|
|
"github.com/simple-rules/harmony-benchmark/configr"
|
|
|
|
"github.com/simple-rules/harmony-benchmark/crypto"
|
|
|
|
"github.com/simple-rules/harmony-benchmark/crypto/pki"
|
|
|
|
"github.com/simple-rules/harmony-benchmark/node"
|
|
|
|
"github.com/simple-rules/harmony-benchmark/p2p"
|
|
|
|
proto_node "github.com/simple-rules/harmony-benchmark/proto/node"
|
|
|
|
"io"
|
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
|
|
|
"strconv"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
// Account subcommands
|
|
|
|
accountImportCommand := flag.NewFlagSet("import", flag.ExitOnError)
|
|
|
|
transferCommand := flag.NewFlagSet("transfer", flag.ExitOnError)
|
|
|
|
//accountListCommand := flag.NewFlagSet("list", flag.ExitOnError)
|
|
|
|
//
|
|
|
|
//// Transaction subcommands
|
|
|
|
//transactionNewCommand := flag.NewFlagSet("new", flag.ExitOnError)
|
|
|
|
//
|
|
|
|
//// Account subcommand flag pointers
|
|
|
|
//// Adding a new choice for --metric of 'substring' and a new --substring flag
|
|
|
|
accountImportPtr := accountImportCommand.String("privateKey", "", "Specify the private key to import")
|
|
|
|
|
|
|
|
transferSenderPtr := transferCommand.String("sender", "0", "Specify the sender account address or index")
|
|
|
|
transferReceiverPtr := transferCommand.String("receiver", "", "Specify the receiver account")
|
|
|
|
transferAmountPtr := transferCommand.Int("amount", 0, "Specify the amount to transfer")
|
|
|
|
//accountListPtr := accountNewCommand.Bool("new", false, "N/A")
|
|
|
|
//
|
|
|
|
//// Transaction subcommand flag pointers
|
|
|
|
//transactionNewPtr := transactionNewCommand.String("text", "", "Text to parse. (Required)")
|
|
|
|
|
|
|
|
// Verify that a subcommand has been provided
|
|
|
|
// os.Arg[0] is the main command
|
|
|
|
// os.Arg[1] will be the subcommand
|
|
|
|
if len(os.Args) < 2 {
|
|
|
|
fmt.Println("account or transaction subcommand is required")
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Switch on the subcommand
|
|
|
|
// Parse the flags for appropriate FlagSet
|
|
|
|
// FlagSet.Parse() requires a set of arguments to parse as input
|
|
|
|
// os.Args[2:] will be all arguments starting after the subcommand at os.Args[1]
|
|
|
|
switch os.Args[1] {
|
|
|
|
case "account":
|
|
|
|
switch os.Args[2] {
|
|
|
|
case "new":
|
|
|
|
fmt.Println("Creating new account...")
|
|
|
|
|
|
|
|
randomBytes := [32]byte{}
|
|
|
|
_, err := io.ReadFull(rand.Reader, randomBytes[:])
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println("Failed to create a new private key...")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
priKey := crypto.Ed25519Curve.Scalar().SetBytes(randomBytes[:])
|
|
|
|
priKeyBytes, err := priKey.MarshalBinary()
|
|
|
|
if err != nil {
|
|
|
|
panic("Failed to generate private key")
|
|
|
|
}
|
|
|
|
pubKey := pki.GetPublicKeyFromScalar(priKey)
|
|
|
|
address := pki.GetAddressFromPublicKey(pubKey)
|
|
|
|
StorePrivateKey(priKeyBytes)
|
|
|
|
fmt.Printf("New account created:\nAddress: {%x}\n", address)
|
|
|
|
case "list":
|
|
|
|
for i, address := range ReadAddresses() {
|
|
|
|
fmt.Printf("Account %d:\n {%x}\n", i+1, address)
|
|
|
|
}
|
|
|
|
case "clearAll":
|
|
|
|
fmt.Println("Deleting existing accounts...")
|
|
|
|
DeletePrivateKey()
|
|
|
|
case "import":
|
|
|
|
fmt.Println("Importing private key...")
|
|
|
|
accountImportCommand.Parse(os.Args[3:])
|
|
|
|
priKey := *accountImportPtr
|
|
|
|
if accountImportCommand.Parsed() {
|
|
|
|
fmt.Println(priKey)
|
|
|
|
} else {
|
|
|
|
fmt.Println("Failed to parse flags")
|
|
|
|
}
|
|
|
|
priKeyBytes, err := hex.DecodeString(priKey)
|
|
|
|
if err != nil {
|
|
|
|
panic("Failed to parse the private key into bytes")
|
|
|
|
}
|
|
|
|
StorePrivateKey(priKeyBytes)
|
|
|
|
case "showBalance":
|
|
|
|
utxoMap, err := FetchUtxos()
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
}
|
|
|
|
PrintUtxoBalance(utxoMap)
|
|
|
|
case "test":
|
|
|
|
priKey := pki.GetPrivateKeyScalarFromInt(444)
|
|
|
|
address := pki.GetAddressFromPrivateKey(priKey)
|
|
|
|
priKeyBytes, err := priKey.MarshalBinary()
|
|
|
|
if err != nil {
|
|
|
|
panic("Failed to deserialize private key scalar.")
|
|
|
|
}
|
|
|
|
fmt.Printf("Private Key :\n {%x}\n", priKeyBytes)
|
|
|
|
fmt.Printf("Address :\n {%x}\n", address)
|
|
|
|
}
|
|
|
|
case "transfer":
|
|
|
|
fmt.Println("Transfer...")
|
|
|
|
transferCommand.Parse(os.Args[2:])
|
|
|
|
priKey := *accountImportPtr
|
|
|
|
if transferCommand.Parsed() {
|
|
|
|
fmt.Println(priKey)
|
|
|
|
} else {
|
|
|
|
fmt.Println("Failed to parse flags")
|
|
|
|
}
|
|
|
|
sender := *transferSenderPtr
|
|
|
|
receiver := *transferReceiverPtr
|
|
|
|
amount := *transferAmountPtr
|
|
|
|
|
|
|
|
if amount <= 0 {
|
|
|
|
fmt.Println("Please specify positive amount to transfer")
|
|
|
|
}
|
|
|
|
priKeys := ReadPrivateKeys()
|
|
|
|
if len(priKeys) == 0 {
|
|
|
|
fmt.Println("No existing account to send money from.")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
senderIndex, err := strconv.Atoi(sender)
|
|
|
|
senderAddress := ""
|
|
|
|
addresses := ReadAddresses()
|
|
|
|
if err != nil {
|
|
|
|
senderIndex = -1
|
|
|
|
for i, address := range addresses {
|
|
|
|
if fmt.Sprintf("%x", address) == senderAddress {
|
|
|
|
senderIndex = i
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if senderIndex == -1 {
|
|
|
|
fmt.Println("Specified sender account is not imported yet.")
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if senderIndex >= len(priKeys) {
|
|
|
|
fmt.Println("Sender account index out of bounds.")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
senderPriKey := priKeys[senderIndex]
|
|
|
|
|
|
|
|
receiverAddress, err := hex.DecodeString(receiver)
|
|
|
|
if err != nil || len(receiverAddress) != 20 {
|
|
|
|
fmt.Println("The receiver address is not a valid address.")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Println(senderPriKey)
|
|
|
|
fmt.Println(amount)
|
|
|
|
fmt.Println(receiverAddress)
|
|
|
|
// Generate transaction
|
|
|
|
|
|
|
|
//txIn := blockchain.NewTXInput(blockchain.NewOutPoint(&txInfo.id, txInfo.index), txInfo.address, nodeShardID)
|
|
|
|
//txInputs := []blockchain.TXInput{*txIn}
|
|
|
|
default:
|
|
|
|
flag.PrintDefaults()
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func FetchUtxos() (blockchain.UtxoMap, error) {
|
|
|
|
configr := configr.NewConfigr()
|
|
|
|
configr.ReadConfigFile("local_config_shards.txt")
|
|
|
|
leaders, _ := configr.GetLeadersAndShardIds()
|
|
|
|
clientPeer := configr.GetClientPeer()
|
|
|
|
walletNode := node.New(nil, nil)
|
|
|
|
walletNode.Client = client.NewClient(&leaders)
|
|
|
|
go walletNode.StartServer(clientPeer.Port)
|
|
|
|
fmt.Println("Fetching account balance...")
|
|
|
|
walletNode.Client.ShardResponseTracker = make(map[uint32]bool)
|
|
|
|
walletNode.Client.UtxoMap = make(blockchain.UtxoMap)
|
|
|
|
p2p.BroadcastMessage(leaders, proto_node.ConstructFetchUtxoMessage(*clientPeer, ReadAddresses()))
|
|
|
|
|
|
|
|
doneSignal := make(chan int)
|
|
|
|
go func() {
|
|
|
|
for true {
|
|
|
|
if len(walletNode.Client.ShardResponseTracker) == len(leaders) {
|
|
|
|
doneSignal <- 0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
select {
|
|
|
|
case <-doneSignal:
|
|
|
|
return walletNode.Client.UtxoMap, nil
|
|
|
|
case <-time.After(5 * time.Second):
|
|
|
|
return nil, errors.New("Utxo fetch timed out")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func PrintUtxoBalance(utxoMap blockchain.UtxoMap) {
|
|
|
|
for address, txHash2Vout2AmountMap := range utxoMap {
|
|
|
|
balance := 0
|
|
|
|
for _, vout2AmountMap := range txHash2Vout2AmountMap {
|
|
|
|
for _, amount := range vout2AmountMap {
|
|
|
|
balance += amount
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
fmt.Printf("Address: {%x}\n", address)
|
|
|
|
fmt.Printf("Balance: %d\n", balance)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func ReadAddresses() [][20]byte {
|
|
|
|
priKeys := ReadPrivateKeys()
|
|
|
|
addresses := [][20]byte{}
|
|
|
|
for _, key := range priKeys {
|
|
|
|
addresses = append(addresses, pki.GetAddressFromPrivateKey(key))
|
|
|
|
}
|
|
|
|
return addresses
|
|
|
|
}
|
|
|
|
|
|
|
|
func StorePrivateKey(priKey []byte) {
|
|
|
|
for _, address := range ReadAddresses() {
|
|
|
|
if address == pki.GetAddressFromPrivateKey(crypto.Ed25519Curve.Scalar().SetBytes(priKey)) {
|
|
|
|
fmt.Println("The key already exists in the keystore")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
f, err := os.OpenFile("keystore", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
panic("Failed to open keystore")
|
|
|
|
}
|
|
|
|
_, err = f.Write(priKey)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
panic("Failed to write to keystore")
|
|
|
|
}
|
|
|
|
f.Close()
|
|
|
|
}
|
|
|
|
|
|
|
|
func DeletePrivateKey() {
|
|
|
|
ioutil.WriteFile("keystore", []byte{}, 0644)
|
|
|
|
}
|
|
|
|
|
|
|
|
func ReadPrivateKeys() []kyber.Scalar {
|
|
|
|
keys, err := ioutil.ReadFile("keystore")
|
|
|
|
if err != nil {
|
|
|
|
return []kyber.Scalar{}
|
|
|
|
}
|
|
|
|
keyScalars := []kyber.Scalar{}
|
|
|
|
for i := 0; i < len(keys); i += 32 {
|
|
|
|
priKey := crypto.Ed25519Curve.Scalar()
|
|
|
|
priKey.UnmarshalBinary(keys[i : i+32])
|
|
|
|
keyScalars = append(keyScalars, priKey)
|
|
|
|
}
|
|
|
|
return keyScalars
|
|
|
|
}
|