Merge pull request #422 from harmony-one/addID

BeaconLeader updating staked list.
pull/436/head
alajko 6 years ago committed by GitHub
commit 818deb67ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      consensus/consensus.go
  2. 23
      node/contract.go
  3. 82
      node/node.go
  4. 4
      node/node_handler.go
  5. 99
      node/node_test.go

@ -27,7 +27,6 @@ import (
"golang.org/x/crypto/sha3"
proto_discovery "github.com/harmony-one/harmony/api/proto/discovery"
proto_node "github.com/harmony-one/harmony/api/proto/node"
)
// Consensus is the main struct with all states and data related to consensus process.
@ -107,10 +106,6 @@ type Consensus struct {
// List of offline Peers
OfflinePeerList []p2p.Peer
//List of nodes related to beaconchain funcs
WaitingNodes []proto_node.Info
ActiveNodes []proto_node.Info
}
// BFTBlockInfo send the latest block that was in BFT consensus process as well as its consensusID to state syncing

@ -4,7 +4,6 @@ import (
"crypto/ecdsa"
"encoding/hex"
"math/big"
"strconv"
"strings"
"github.com/ethereum/go-ethereum/common"
@ -13,6 +12,7 @@ import (
"github.com/ethereum/go-ethereum/params"
"github.com/harmony-one/harmony/core/types"
"github.com/harmony-one/harmony/internal/utils/contract"
"golang.org/x/crypto/sha3"
)
// Constants related to smart contract.
@ -36,12 +36,13 @@ func (node *Node) AddStakingContractToPendingTransactions() {
dataEnc := common.FromHex(StakingContractBinary)
// Unsigned transaction to avoid the case of transaction address.
mycontracttx, _ := types.SignTx(types.NewContractCreation(uint64(0), node.Consensus.ShardID, contractFunds, params.TxGasContractCreation*10, nil, dataEnc), types.HomesteadSigner{}, priKey)
node.ContractAddresses = append(node.ContractAddresses, crypto.CreateAddress(contractAddress, uint64(0)))
//node.StakingContractAddress = crypto.CreateAddress(contractAddress, uint64(0))
node.StakingContractAddress = node.generateDeployedStakingContractAddress(mycontracttx, contractAddress)
node.addPendingTransactions(types.Transactions{mycontracttx})
}
//CreateStakingWithdrawTransaction creates a new withdraw stake transaction
func (node *Node) CreateStakingWithdrawTransaction(stake int) (*types.Transaction, error) {
func (node *Node) CreateStakingWithdrawTransaction(stake string) (*types.Transaction, error) {
//These should be read from somewhere.
DepositContractPriKey, _ := ecdsa.GenerateKey(crypto.S256(), strings.NewReader("Deposit Smart Contract Key")) //DepositContractPriKey is pk for contract
DepositContractAddress := crypto.PubkeyToAddress(DepositContractPriKey.PublicKey) //DepositContractAddress is the address for the contract
@ -50,9 +51,19 @@ func (node *Node) CreateStakingWithdrawTransaction(stake int) (*types.Transactio
log.Error("Failed to get chain state", "Error", err)
}
nonce := state.GetNonce(crypto.PubkeyToAddress(DepositContractPriKey.PublicKey))
callingFunction := "0x2e1a7d4d"
contractData := callingFunction + hex.EncodeToString([]byte(strconv.Itoa(stake)))
dataEnc := common.FromHex(contractData)
//Following: https://github.com/miguelmota/ethereum-development-with-go-book/blob/master/code/transfer_tokens.go
withdrawFnSignature := []byte("withdraw(uint)")
hash := sha3.NewLegacyKeccak256()
hash.Write(withdrawFnSignature)
methodID := hash.Sum(nil)[:4]
amount := new(big.Int)
amount.SetString(stake, 10)
paddedAmount := common.LeftPadBytes(amount.Bytes(), 32)
var dataEnc []byte
dataEnc = append(dataEnc, methodID...)
dataEnc = append(dataEnc, paddedAmount...)
tx, err := types.SignTx(types.NewTransaction(nonce, DepositContractAddress, node.Consensus.ShardID, big.NewInt(0), params.TxGasContractCreation*10, nil, dataEnc), types.HomesteadSigner{}, node.AccountKey)
return tx, err
}

@ -160,6 +160,11 @@ type Node struct {
// Service manager.
serviceManager *service_manager.Manager
//Staked Accounts and Contract
CurrentStakes map[common.Address]int64 //This will save the latest information about staked nodes.
StakingContractAddress common.Address
WithdrawStakeFunc []byte
//Node Account
AccountKey *ecdsa.PrivateKey
Address common.Address
@ -266,9 +271,12 @@ func New(host p2p.Host, consensus *bft.Consensus, db ethdb.Database) *Node {
node.Worker = worker.New(params.TestChainConfig, chain, node.Consensus, pki.GetAddressFromPublicKey(node.SelfPeer.PubKey), node.Consensus.ShardID)
node.AddFaucetContractToPendingTransactions()
if node.Role == BeaconLeader {
node.AddStakingContractToPendingTransactions()
node.AddStakingContractToPendingTransactions() //This will save the latest information about staked nodes in current staked
node.DepositToFakeAccounts()
}
if node.Role == BeaconLeader || node.Role == BeaconValidator {
node.CurrentStakes = make(map[common.Address]int64)
}
node.Consensus.ConsensusBlock = make(chan *bft.BFTBlockInfo)
node.Consensus.VerifiedNewBlock = make(chan *types.Block)
}
@ -292,6 +300,30 @@ func New(host p2p.Host, consensus *bft.Consensus, db ethdb.Database) *Node {
return &node
}
func (node *Node) getDeployedStakingContract() common.Address {
return node.StakingContractAddress
}
//In order to get the deployed contract address of a contract, we need to find the nonce of the address that created it.
//(Refer: https://solidity.readthedocs.io/en/v0.5.3/introduction-to-smart-contracts.html#index-8)
// Then we can (re)create the deployed address. Trivially, this is 0 for us.
// The deployed contract address can also be obtained via the receipt of the contract creating transaction.
func (node *Node) generateDeployedStakingContractAddress(mycontracttx *types.Transaction, contractAddress common.Address) common.Address {
//Ideally we send the transaction to
//Correct Way 1:
//node.SendTx(mycontracttx)
//receipts := node.worker.GetCurrentReceipts()
//deployedcontractaddress = recepits[len(receipts)-1].ContractAddress //get the address from the receipt
//Correct Way 2:
//nonce := GetNonce(contractAddress)
//deployedAddress := crypto.CreateAddress(contractAddress, uint64(nonce))
//deployedcontractaddress = recepits[len(receipts)-1].ContractAddress //get the address from the receipt
nonce := 0
return crypto.CreateAddress(contractAddress, uint64(nonce))
}
// IsOutOfSync checks whether the node is out of sync by comparing latest block with consensus block
func (node *Node) IsOutOfSync(consensusBlockInfo *bft.BFTBlockInfo) bool {
consensusBlock := consensusBlockInfo.Block
@ -585,11 +617,53 @@ func (node *Node) RemovePeersHandler() {
}
}
func (node *Node) getDeployedStakingContract() common.Address {
// TODO(ak): Please populate here
return common.Address{}
//UpdateStakingList updates the stakes of every node.
func (node *Node) UpdateStakingList(block *types.Block) error {
signerType := types.HomesteadSigner{}
txns := block.Transactions()
for i := range txns {
txn := txns[i]
value := txn.Value().Int64()
currentSender, _ := types.Sender(signerType, txn)
_, isPresent := node.CurrentStakes[currentSender]
toAddress := txn.To()
if *toAddress != node.StakingContractAddress { //Not a address aimed at the staking contract.
continue
}
//This should be based on a switch case on function signature.
//TODO (ak) https://github.com/harmony-one/harmony/issues/430
if value > int64(0) { //If value >0 means its a staking deposit transaction
if isPresent {
//This means this node has increaserd its stake
node.CurrentStakes[currentSender] += value
} else {
node.CurrentStakes[currentSender] = value
}
} else { //This means node has withdrawn stake.
getData := txn.Data()
value := decodeStakeCall(getData) //Value being withdrawn
if isPresent {
//This means this node has increaserd its stake
if node.CurrentStakes[currentSender] > value {
node.CurrentStakes[currentSender] -= value
} else if node.CurrentStakes[currentSender] == value {
delete(node.CurrentStakes, currentSender)
} else {
continue //Overdraft protection.
}
} else {
node.CurrentStakes[currentSender] = value
}
}
}
return nil
}
func decodeStakeCall(getData []byte) int64 {
value := new(big.Int)
value.SetBytes(getData[4:]) //Escape the method call.
return value.Int64()
}
func (node *Node) setupForShardLeader() {
// Register explorer service.
node.serviceManager.RegisterService(service_manager.SupportExplorer, explorer.New(&node.SelfPeer))

@ -241,10 +241,12 @@ func (node *Node) VerifyNewBlock(newBlock *types.Block) bool {
// 1. add the new block to blockchain
// 2. [leader] send new block to the client
func (node *Node) PostConsensusProcessing(newBlock *types.Block) {
if node.Role == BeaconLeader || node.Role == BeaconValidator {
node.UpdateStakingList(newBlock)
}
if node.Consensus.IsLeader {
node.BroadcastNewBlock(newBlock)
}
node.AddNewBlock(newBlock)
}

@ -2,16 +2,22 @@ package node
import (
"fmt"
"math/big"
"os"
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
proto_discovery "github.com/harmony-one/harmony/api/proto/discovery"
"github.com/harmony-one/harmony/consensus"
"github.com/harmony-one/harmony/core/types"
"github.com/harmony-one/harmony/crypto/pki"
"github.com/harmony-one/harmony/internal/utils"
"github.com/harmony-one/harmony/p2p"
"github.com/harmony-one/harmony/p2p/p2pimpl"
"golang.org/x/crypto/sha3"
)
func TestNewNode(t *testing.T) {
@ -171,3 +177,96 @@ func TestPingPongHandler(t *testing.T) {
go exitServer()
node.StartServer()
}
func TestUpdateStakingDeposit(t *testing.T) {
_, pubKey := utils.GenKey("1", "2")
leader := p2p.Peer{IP: "127.0.0.1", Port: "8882", PubKey: pubKey}
validator := p2p.Peer{IP: "127.0.0.1", Port: "8885"}
priKey, _, _ := utils.GenKeyP2P("127.0.0.1", "9902")
host, err := p2pimpl.NewHost(&leader, priKey)
if err != nil {
t.Fatalf("newhost failure: %v", err)
}
consensus := consensus.New(host, "0", []p2p.Peer{leader, validator}, leader)
node := New(host, consensus, nil)
node.CurrentStakes = make(map[common.Address]int64)
DepositContractPriKey, _ := crypto.GenerateKey() //DepositContractPriKey is pk for contract
DepositContractAddress := crypto.PubkeyToAddress(DepositContractPriKey.PublicKey) //DepositContractAddress is the address for the contract
node.StakingContractAddress = DepositContractAddress
node.AccountKey, _ = crypto.GenerateKey()
Address := crypto.PubkeyToAddress(node.AccountKey.PublicKey)
callingFunction := "0xd0e30db0"
amount := new(big.Int)
amount.SetString("10", 10)
dataEnc := common.FromHex(callingFunction) //Deposit Does not take a argument, stake is transferred via amount.
tx1, err := types.SignTx(types.NewTransaction(0, DepositContractAddress, node.Consensus.ShardID, amount, params.TxGasContractCreation*10, nil, dataEnc), types.HomesteadSigner{}, node.AccountKey)
var txs []*types.Transaction
txs = append(txs, tx1)
header := &types.Header{Extra: []byte("hello")}
block := types.NewBlock(header, txs, nil)
node.UpdateStakingList(block)
if len(node.CurrentStakes) == 0 {
t.Error("New node's stake was not added")
}
value, ok := node.CurrentStakes[Address]
if !ok {
t.Error("The correct address was not added")
}
if value != 10 {
t.Error("The correct stake value was not added")
}
}
func TestUpdateStakingWithdrawal(t *testing.T) {
_, pubKey := utils.GenKey("1", "2")
leader := p2p.Peer{IP: "127.0.0.1", Port: "8882", PubKey: pubKey}
validator := p2p.Peer{IP: "127.0.0.1", Port: "8885"}
priKey, _, _ := utils.GenKeyP2P("127.0.0.1", "9902")
host, err := p2pimpl.NewHost(&leader, priKey)
if err != nil {
t.Fatalf("newhost failure: %v", err)
}
consensus := consensus.New(host, "0", []p2p.Peer{leader, validator}, leader)
node := New(host, consensus, nil)
node.CurrentStakes = make(map[common.Address]int64)
DepositContractPriKey, _ := crypto.GenerateKey() //DepositContractPriKey is pk for contract
DepositContractAddress := crypto.PubkeyToAddress(DepositContractPriKey.PublicKey) //DepositContractAddress is the address for the contract
node.StakingContractAddress = DepositContractAddress
node.AccountKey, _ = crypto.GenerateKey()
Address := crypto.PubkeyToAddress(node.AccountKey.PublicKey)
node.CurrentStakes[Address] = int64(1010)
withdrawFnSignature := []byte("withdraw(uint)")
hash := sha3.NewLegacyKeccak256()
hash.Write(withdrawFnSignature)
methodID := hash.Sum(nil)[:4]
stake := "1000"
amount := new(big.Int)
amount.SetString(stake, 10)
paddedAmount := common.LeftPadBytes(amount.Bytes(), 32)
var dataEnc []byte
dataEnc = append(dataEnc, methodID...)
dataEnc = append(dataEnc, paddedAmount...)
tx, err := types.SignTx(types.NewTransaction(0, DepositContractAddress, node.Consensus.ShardID, big.NewInt(0), params.TxGasContractCreation*10, nil, dataEnc), types.HomesteadSigner{}, node.AccountKey)
var txs []*types.Transaction
txs = append(txs, tx)
header := &types.Header{Extra: []byte("hello")}
block := types.NewBlock(header, txs, nil)
node.UpdateStakingList(block)
value, ok := node.CurrentStakes[Address]
if !ok {
t.Error("The correct address was not present")
}
if value != 10 {
t.Error("The correct stake value was not subtracted")
}
}

Loading…
Cancel
Save