diff --git a/cmd/harmony.go b/cmd/harmony.go index 8ed487b79..7aba88723 100644 --- a/cmd/harmony.go +++ b/cmd/harmony.go @@ -143,7 +143,7 @@ func main() { var clientPeer *p2p.Peer var role string - stakingPriKey := utils.LoadStakingKeyFromFile(*stakingKeyFile) + stakingPriKey := node.LoadStakingKeyFromFile(*stakingKeyFile) nodePriKey, _, err := utils.LoadKeyFromFile(*keyFile) if err != nil { diff --git a/internal/utils/contract/constants.go b/internal/utils/contract/constants.go index 5daf0d624..f9de9a692 100644 --- a/internal/utils/contract/constants.go +++ b/internal/utils/contract/constants.go @@ -38,6 +38,16 @@ var GenesisBeaconAccountPublicKey = GenesisBeaconAccountPriKey.PublicKey // DeployedContractAddress is the deployed contract address of the staking smart contract in beacon chain. var DeployedContractAddress = crypto.CreateAddress(crypto.PubkeyToAddress(GenesisBeaconAccountPublicKey), uint64(0)) +// StakingAccounts is the accounts only used for staking. +var StakingAccounts = [...]DeployAccount{ + {Address: "0xE2bD4413172C98d5094B94de1A8AC6a383d68b84", Private: "e401343197a852f361e38ce6b46c99f1d6d1f80499864c6ae7effee42b46ab6b", Public: "0xE2bD4413172C98d5094B94de1A8AC6a383d68b84"}, + {Address: "0x183418934Fd8A97c98E086151317B2df6259b8A8", Private: "a7d764439a7619f703c97ee2a2cf0be2cd62ad4c9deebd5423d6f28de417b907", Public: "0x183418934Fd8A97c98E086151317B2df6259b8A8"}, + {Address: "0x9df0e70D4cb3E9beC0548D8Ac56F46596D1BcdB6", Private: "4e3f7c819a15249d2824834cd7ce20fe24d6eab8eb39ac63b78cb3713362cf78", Public: "0x9df0e70D4cb3E9beC0548D8Ac56F46596D1BcdB6"}, + {Address: "0x64570B8D2f9028b850340b6710bB0027baC5EFa2", Private: "8565eded3f70b2f8608495fa7e301fefc36cbc03286c0f4e75ffde7e02b49791", Public: "0x64570B8D2f9028b850340b6710bB0027baC5EFa2"}, + {Address: "0x7E653426a7E0c1D210A652bf0E74B15838059b80", Private: "23176da901759e231d7b2d8761f3448220fcfd0232ed0b9644689a6ad7d50990", Public: "0x7E653426a7E0c1D210A652bf0E74B15838059b80"}, + {Address: "0xAe44500A96756112C69F88008388037F6C6dd723", Private: "a9b4c273ffa398a1b1c8eeab63c40cef2c2fb2fc1fc4de9ab73a61f12e4d990a", Public: "0xAe44500A96756112C69F88008388037F6C6dd723"}, +} + // FakeAccounts is the accounts only used for development purpose. var FakeAccounts = [...]DeployAccount{ {Address: "0xE2bD4413172C98d5094B94de1A8AC6a383d68b84", Private: "e401343197a852f361e38ce6b46c99f1d6d1f80499864c6ae7effee42b46ab6b", Public: "0xE2bD4413172C98d5094B94de1A8AC6a383d68b84"}, diff --git a/internal/utils/utils.go b/internal/utils/utils.go index 4f8aa7a80..73fc96c71 100644 --- a/internal/utils/utils.go +++ b/internal/utils/utils.go @@ -2,7 +2,6 @@ package utils import ( "bytes" - "crypto/ecdsa" "encoding/binary" "encoding/json" "fmt" @@ -14,7 +13,6 @@ import ( "strconv" "sync" - crypto "github.com/ethereum/go-ethereum/crypto" "github.com/harmony-one/bls/ffi/go/bls" "github.com/harmony-one/harmony/p2p" p2p_crypto "github.com/libp2p/go-libp2p-crypto" @@ -216,23 +214,3 @@ func LoadKeyFromFile(keyfile string) (key p2p_crypto.PrivKey, pk p2p_crypto.PubK key, pk, err = LoadPrivateKey(keyStruct.Key) return key, pk, err } - -// LoadStakingKeyFromFile load staking private key from keyfile -// If the private key is not loadable or no file, it will generate -// a new random private key -func LoadStakingKeyFromFile(keyfile string) *ecdsa.PrivateKey { - key, err := crypto.LoadECDSA(keyfile) - if err != nil { - GetLogInstance().Error("no key file. Let's create a staking private key") - key, err = crypto.GenerateKey() - if err != nil { - GetLogInstance().Error("Unable to generate the private key") - os.Exit(1) - } - if err = crypto.SaveECDSA(keyfile, key); err != nil { - GetLogInstance().Error("Unable to save the private key", "error", err) - os.Exit(1) - } - } - return key -} diff --git a/node/contract.go b/node/contract.go index bfa159e73..505660d93 100644 --- a/node/contract.go +++ b/node/contract.go @@ -96,7 +96,7 @@ func (node *Node) getDeployedStakingContract() common.Address { // AddFaucetContractToPendingTransactions adds the faucet contract the genesis block. func (node *Node) AddFaucetContractToPendingTransactions() { // Add a contract deployment transactionv - priKey := node.ContractKeys[0] + priKey := node.ContractDeployerKey dataEnc := common.FromHex(FaucetContractBinary) // Unsigned transaction to avoid the case of transaction address. @@ -112,28 +112,37 @@ func (node *Node) AddFaucetContractToPendingTransactions() { // CallFaucetContract invokes the faucet contract to give the walletAddress initial money func (node *Node) CallFaucetContract(address common.Address) common.Hash { - return node.callGetFreeTokenWithTransaction(address) + return node.callGetFreeToken(address) } -func (node *Node) callGetFreeTokenWithTransaction(address common.Address) common.Hash { +func (node *Node) callGetFreeToken(address common.Address) common.Hash { state, err := node.blockchain.State() if err != nil { log.Error("Failed to get chain state", "Error", err) } - nonce := state.GetNonce(crypto.PubkeyToAddress(node.ContractKeys[0].PublicKey)) + nonce := state.GetNonce(crypto.PubkeyToAddress(node.ContractDeployerKey.PublicKey)) + return node.callGetFreeTokenWithNonce(address, nonce) +} + +func (node *Node) callGetFreeTokenWithNonce(address common.Address, nonce uint64) common.Hash { contractData := FaucetFreeMoneyMethodCall + hex.EncodeToString(address.Bytes()) dataEnc := common.FromHex(contractData) utils.GetLogInstance().Info("Sending Free Token to ", "Address", address.Hex()) - tx, _ := types.SignTx(types.NewTransaction(nonce, node.ContractAddresses[0], node.Consensus.ShardID, big.NewInt(0), params.TxGasContractCreation*10, nil, dataEnc), types.HomesteadSigner{}, node.ContractKeys[0]) + tx, _ := types.SignTx(types.NewTransaction(nonce, node.ContractAddresses[0], node.Consensus.ShardID, big.NewInt(0), params.TxGasContractCreation*10, nil, dataEnc), types.HomesteadSigner{}, node.ContractDeployerKey) node.addPendingTransactions(types.Transactions{tx}) return tx.Hash() } -// DepositToFakeAccounts invokes the faucet contract to give the fake accounts initial money -func (node *Node) DepositToFakeAccounts() { - for _, deployAccount := range contract.FakeAccounts { +// DepositToStakingAccounts invokes the faucet contract to give the staking accounts initial money +func (node *Node) DepositToStakingAccounts() { + state, err := node.blockchain.State() + if err != nil { + log.Error("Failed to get chain state", "Error", err) + } + nonce := state.GetNonce(crypto.PubkeyToAddress(node.ContractDeployerKey.PublicKey)) + 1 // + 1 because deployer key is already used for faucet contract deployment + for i, deployAccount := range contract.StakingAccounts { address := common.HexToAddress(deployAccount.Address) - node.callGetFreeTokenWithTransaction(address) + node.callGetFreeTokenWithNonce(address, nonce+uint64(i)) } } diff --git a/node/node.go b/node/node.go index e5c25d635..71d4c12cc 100644 --- a/node/node.go +++ b/node/node.go @@ -177,9 +177,9 @@ type Node struct { Address common.Address // For test only - TestBankKeys []*ecdsa.PrivateKey - ContractKeys []*ecdsa.PrivateKey - ContractAddresses []common.Address + TestBankKeys []*ecdsa.PrivateKey + ContractDeployerKey *ecdsa.PrivateKey + ContractAddresses []common.Address // Group Message Receiver groupReceiver p2p.GroupReceiver @@ -284,7 +284,7 @@ func New(host p2p.Host, consensusObj *consensus.Consensus, db ethdb.Database) *N node.AddFaucetContractToPendingTransactions() node.CurrentStakes = make(map[common.Address]int64) node.AddStakingContractToPendingTransactions() //This will save the latest information about staked nodes in current staked - // node.DepositToFakeAccounts() + node.DepositToStakingAccounts() } if consensusObj != nil && consensusObj.IsLeader { diff --git a/node/node_genesis.go b/node/node_genesis.go index a5d864a6f..9525024d7 100644 --- a/node/node_genesis.go +++ b/node/node_genesis.go @@ -24,12 +24,12 @@ const ( func (node *Node) GenesisBlockSetup(db ethdb.Database) (*core.BlockChain, error) { // Initialize genesis block and blockchain genesisAlloc := node.CreateGenesisAllocWithTestingAddresses(FakeAddressNumber) - contractKey, _ := ecdsa.GenerateKey(crypto.S256(), strings.NewReader("Test contract key string stream that is fixed so that generated test key are deterministic every time")) - contractAddress := crypto.PubkeyToAddress(contractKey.PublicKey) - contractFunds := big.NewInt(TotalInitFund) - contractFunds = contractFunds.Mul(contractFunds, big.NewInt(params.Ether)) - genesisAlloc[contractAddress] = core.GenesisAccount{Balance: contractFunds} - node.ContractKeys = append(node.ContractKeys, contractKey) + contractDeployerKey, _ := ecdsa.GenerateKey(crypto.S256(), strings.NewReader("Test contract key string stream that is fixed so that generated test key are deterministic every time")) + contractDeployerAddress := crypto.PubkeyToAddress(contractDeployerKey.PublicKey) + contractDeployerFunds := big.NewInt(TotalInitFund) + contractDeployerFunds = contractDeployerFunds.Mul(contractDeployerFunds, big.NewInt(params.Ether)) + genesisAlloc[contractDeployerAddress] = core.GenesisAccount{Balance: contractDeployerFunds} + node.ContractDeployerKey = contractDeployerKey chainConfig := params.TestChainConfig chainConfig.ChainID = big.NewInt(int64(node.Consensus.ShardID)) // Use ChainID as piggybacked ShardID diff --git a/node/node_handler.go b/node/node_handler.go index e59340ee8..bc889cf52 100644 --- a/node/node_handler.go +++ b/node/node_handler.go @@ -208,20 +208,8 @@ func (node *Node) processStakingMessage(msgPayload []byte) { stakingRequest := msg.GetStaking() txs := types.Transactions{} if err = rlp.DecodeBytes(stakingRequest.Transaction, &txs); err == nil { - for _, tx := range txs { - signerType := types.HomesteadSigner{} - sender, err := types.Sender(signerType, tx) - if err != nil { - utils.GetLogInstance().Error("Failed to get the staker address", "error", err) - } else { - node.CallFaucetContract(sender) - } - } utils.GetLogInstance().Info("Successfully added staking transaction to pending list.") - go func() { - time.Sleep(5 * time.Second) - node.addPendingTransactions(txs) - }() + node.addPendingTransactions(txs) } else { utils.GetLogInstance().Error("Failed to unmarshal staking transaction list", "error", err) } diff --git a/node/staking.go b/node/staking.go index cd5773605..82781169a 100644 --- a/node/staking.go +++ b/node/staking.go @@ -1,7 +1,12 @@ package node import ( + "crypto/ecdsa" "math/big" + "os" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/harmony-one/harmony/internal/utils/contract" "github.com/harmony-one/harmony/internal/utils" @@ -94,3 +99,36 @@ func decodeFuncSign(data []byte) string { funcSign := hexutil.Encode(data[:funcSingatureBytes]) //The function signature is first 4 bytes of data in ethereum return funcSign } + +// LoadStakingKeyFromFile load staking private key from keyfile +// If the private key is not loadable or no file, it will generate +// a new random private key +// Currently for deploy_newnode.sh, we hard-coded the first fake account as staking account and +// it is minted in genesis block. See genesis_node.go +func LoadStakingKeyFromFile(keyfile string) *ecdsa.PrivateKey { + // contract.FakeAccounts[0] gets minted tokens in genesis block of beacon chain. + key, err := crypto.HexToECDSA(contract.FakeAccounts[0].Private) + if err != nil { + utils.GetLogInstance().Error("Unable to get staking key") + os.Exit(1) + } + if err := crypto.SaveECDSA(keyfile, key); err != nil { + utils.GetLogInstance().Error("Unable to save the private key", "error", err) + os.Exit(1) + } + // TODO(minhdoan): Enable this back. + // key, err := crypto.LoadECDSA(keyfile) + // if err != nil { + // GetLogInstance().Error("no key file. Let's create a staking private key") + // key, err = crypto.GenerateKey() + // if err != nil { + // GetLogInstance().Error("Unable to generate the private key") + // os.Exit(1) + // } + // if err = crypto.SaveECDSA(keyfile, key); err != nil { + // GetLogInstance().Error("Unable to save the private key", "error", err) + // os.Exit(1) + // } + // } + return key +} diff --git a/test/deploy_newnode.sh b/test/deploy_newnode.sh index 50e97600a..8661a1b31 100755 --- a/test/deploy_newnode.sh +++ b/test/deploy_newnode.sh @@ -116,7 +116,7 @@ LOG_FILE=$log_folder/r.log HMY_OPT= # Change to the beacon chain output from deploy.sh -HMY_OPT2= +HMY_OPT2="-bootnodes /ip4/127.0.0.1/tcp/19876/p2p/QmWGSpah5QiBB1aKHKoJpM4VqJxcefc8kVxEbzXD59M8kP" HMY_OPT3= ($DRYRUN $ROOT/bin/harmony -ip 127.0.0.1 -port 9100 -log_folder $log_folder -is_newnode $DB -min_peers $MIN $HMY_OPT $HMY_OPT2 $HMY_OPT3 -key /tmp/127.0.0.1-9100.key 2>&1 | tee -a $LOG_FILE ) &