From ffd82d035ca7101c67201f4529a1306ea2d44983 Mon Sep 17 00:00:00 2001 From: alok Date: Mon, 11 Feb 2019 01:18:07 -0800 Subject: [PATCH 01/18] BeaconLeader adds IDs --- consensus/consensus.go | 5 ++--- node/node.go | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/consensus/consensus.go b/consensus/consensus.go index 70f75af11..501e03833 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.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. @@ -109,8 +108,8 @@ type Consensus struct { OfflinePeerList []p2p.Peer //List of nodes related to beaconchain funcs - WaitingNodes []proto_node.Info - ActiveNodes []proto_node.Info + WaitingNodes []common.Address + ActiveNodes []common.Address } // BFTBlockInfo send the latest block that was in BFT consensus process as well as its consensusID to state syncing diff --git a/node/node.go b/node/node.go index 74cd85d61..7190cf032 100644 --- a/node/node.go +++ b/node/node.go @@ -570,6 +570,44 @@ func (node *Node) RemovePeersHandler() { } } + +// This function will be used by the beaconLeader +// GetNewStakedNodes gives a list of all nodes that have deposited transaction +func (node *Node) GetNewStakedNodes() ([]common.Address) { + BlocksPerEpoch := 5 //Hardcoded, will take value from core.Blockchain later. + currentHeight := node.blockchain.CurrentBlock().NumberU64() + if currentHeight > BlocksPerEpoch { + prevBlock := currentHeight - BlocksPerEpoch + } else { + prevBlock := 0 + } + return node.getNewStakedNodesFromBlockNumToBlockNum(prevBlock,currentHeight) +} + +// This function will be used by the beaconLeader +//GetNewStakedNodesFromBlockNumToBlockNum gives a list of all nodes that have deposited transaction +func (node *Node) getNewStakedNodesFromBlockNumToBlockNum (prevBlockNum, toCurrentBlock uint64) ([]common.Address) { + Blockchain := node.Blockchain() + signerType := types.HomesteadSigner{} + newNodesMap = make(map[common.Address]int) + var newNodes []common.Address + for i := prevBlockNum,; i < toCurrentBlock + 1; i++ { + block = Blockchain.GetBlockByNumber(from) + txns = block.Transactions(), + for txn := range txns { + if txn.Value() > 0 { //If value >0 means its a staking deposit transaction + newSender := types.Sender(signerType,txn) + _, isPresent := newNodesMap[newSender] + if !isPresent { + newNodesMap[newSender] = 1 + newNodes := append(newNodes, newSender) + } + } + } + } + return newNodes +} + func (node *Node) setupForShardLeader() { // Register explorer service. node.serviceManager.RegisterService(service_manager.SupportExplorer, explorer.New(&node.SelfPeer)) From 970d3b2f0842fa71dd50f83598ad1e88be9b7817 Mon Sep 17 00:00:00 2001 From: Eugene Kim Date: Mon, 11 Feb 2019 15:47:13 -0800 Subject: [PATCH 02/18] Log connections when -log_conn is given This is for troubleshooting low-level network operations. --- cmd/harmony.go | 56 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/cmd/harmony.go b/cmd/harmony.go index 4024a25c4..ddfc7e868 100644 --- a/cmd/harmony.go +++ b/cmd/harmony.go @@ -11,6 +11,10 @@ import ( "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" + net "github.com/libp2p/go-libp2p-net" + peerstore "github.com/libp2p/go-libp2p-peerstore" + multiaddr "github.com/multiformats/go-multiaddr" + "github.com/harmony-one/harmony/consensus" "github.com/harmony-one/harmony/internal/attack" pkg_newnode "github.com/harmony-one/harmony/internal/newnode" @@ -19,8 +23,6 @@ import ( "github.com/harmony-one/harmony/node" "github.com/harmony-one/harmony/p2p" "github.com/harmony-one/harmony/p2p/p2pimpl" - peerstore "github.com/libp2p/go-libp2p-peerstore" - multiaddr "github.com/multiformats/go-multiaddr" ) var ( @@ -77,6 +79,50 @@ func loggingInit(logFolder, role, ip, port string, onlyLogTps bool) { log.Root().SetHandler(h) } +type connLogger struct{} + +func (connLogger) Listen(net net.Network, ma multiaddr.Multiaddr) { + log.Debug("[CONNECTIONS] Listener starting", "net", net, "addr", ma) +} + +func (connLogger) ListenClose(net net.Network, ma multiaddr.Multiaddr) { + log.Debug("[CONNECTIONS] Listener closing", "net", net, "addr", ma) +} + +func (connLogger) Connected(net net.Network, conn net.Conn) { + log.Debug("[CONNECTIONS] Connected", "net", net, + "localPeer", conn.LocalPeer(), "localAddr", conn.LocalMultiaddr(), + "remotePeer", conn.RemotePeer(), "remoteAddr", conn.RemoteMultiaddr(), + ) +} + +func (connLogger) Disconnected(net net.Network, conn net.Conn) { + log.Debug("[CONNECTIONS] Disconnected", "net", net, + "localPeer", conn.LocalPeer(), "localAddr", conn.LocalMultiaddr(), + "remotePeer", conn.RemotePeer(), "remoteAddr", conn.RemoteMultiaddr(), + ) +} + +func (connLogger) OpenedStream(net net.Network, stream net.Stream) { + conn := stream.Conn() + log.Debug("[CONNECTIONS] Stream opened", "net", net, + "localPeer", conn.LocalPeer(), "localAddr", conn.LocalMultiaddr(), + "remotePeer", conn.RemotePeer(), "remoteAddr", conn.RemoteMultiaddr(), + "protocol", stream.Protocol(), + ) +} + +func (connLogger) ClosedStream(net net.Network, stream net.Stream) { + conn := stream.Conn() + log.Debug("[CONNECTIONS] Stream closed", "net", net, + "localPeer", conn.LocalPeer(), "localAddr", conn.LocalMultiaddr(), + "remotePeer", conn.RemotePeer(), "remoteAddr", conn.RemoteMultiaddr(), + "protocol", stream.Protocol(), + ) +} + +var theConnLogger connLogger + func main() { // TODO: use http://getmyipaddress.org/ or http://www.get-myip.com/ to retrieve my IP address ip := flag.String("ip", "127.0.0.1", "IP of the node") @@ -111,6 +157,9 @@ func main() { // isLeader indicates this node is a beacon chain leader node during the bootstrap process isLeader := flag.Bool("is_leader", false, "true means this node is a beacon chain leader node") + // logConn logs incoming/outgoing connections + logConn := flag.Bool("log_conn", false, "log incoming/outgoing connections") + flag.Parse() if *versionFlag { @@ -211,6 +260,9 @@ func main() { } host, err := p2pimpl.NewHost(&selfPeer, nodePriKey) + if *logConn { + host.GetP2PHost().Network().Notify(theConnLogger) + } if err != nil { panic("unable to new host in harmony") } From db243c1676208ab3c269d5c9a0233c7f3c2fa4ee Mon Sep 17 00:00:00 2001 From: ak Date: Mon, 11 Feb 2019 17:19:34 -0800 Subject: [PATCH 03/18] update staking list --- node/contract.go | 22 ++++++++++--- node/node.go | 78 +++++++++++++++++++++++++------------------- node/node_handler.go | 4 ++- 3 files changed, 65 insertions(+), 39 deletions(-) diff --git a/node/contract.go b/node/contract.go index 03118bbf1..00d1505de 100644 --- a/node/contract.go +++ b/node/contract.go @@ -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. @@ -41,7 +41,7 @@ func (node *Node) AddStakingContractToPendingTransactions() { } //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 +50,21 @@ 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) + //callingFunction := "0x2e1a7d4d" + + //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 } diff --git a/node/node.go b/node/node.go index 7190cf032..9bea70027 100644 --- a/node/node.go +++ b/node/node.go @@ -159,6 +159,11 @@ type Node struct { // Service manager. serviceManager *service_manager.Manager + //Staked Accounts and Contract + CurrentStakes map[common.Address]int64 + StakeContractAddress common.Address + WithdrawStakeFunc []byte + //Node Account AccountKey *ecdsa.PrivateKey Address common.Address @@ -570,44 +575,51 @@ func (node *Node) RemovePeersHandler() { } } - -// This function will be used by the beaconLeader -// GetNewStakedNodes gives a list of all nodes that have deposited transaction -func (node *Node) GetNewStakedNodes() ([]common.Address) { - BlocksPerEpoch := 5 //Hardcoded, will take value from core.Blockchain later. - currentHeight := node.blockchain.CurrentBlock().NumberU64() - if currentHeight > BlocksPerEpoch { - prevBlock := currentHeight - BlocksPerEpoch - } else { - prevBlock := 0 - } - return node.getNewStakedNodesFromBlockNumToBlockNum(prevBlock,currentHeight) -} - -// This function will be used by the beaconLeader -//GetNewStakedNodesFromBlockNumToBlockNum gives a list of all nodes that have deposited transaction -func (node *Node) getNewStakedNodesFromBlockNumToBlockNum (prevBlockNum, toCurrentBlock uint64) ([]common.Address) { - Blockchain := node.Blockchain() - signerType := types.HomesteadSigner{} - newNodesMap = make(map[common.Address]int) - var newNodes []common.Address - for i := prevBlockNum,; i < toCurrentBlock + 1; i++ { - block = Blockchain.GetBlockByNumber(from) - txns = block.Transactions(), - for txn := range txns { - if txn.Value() > 0 { //If value >0 means its a staking deposit transaction - newSender := types.Sender(signerType,txn) - _, isPresent := newNodesMap[newSender] - if !isPresent { - newNodesMap[newSender] = 1 - newNodes := append(newNodes, newSender) +//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.StakeContractAddress { //Not a address aimed at the staking contract. + continue + } + 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 newNodes + return nil } +func decodeStakeCall(getData []byte) int64 { + value := new(big.Int) + value.SetBytes(getData[4:]) + return value.Int64() +} func (node *Node) setupForShardLeader() { // Register explorer service. node.serviceManager.RegisterService(service_manager.SupportExplorer, explorer.New(&node.SelfPeer)) diff --git a/node/node_handler.go b/node/node_handler.go index 78ecdce68..007eded21 100644 --- a/node/node_handler.go +++ b/node/node_handler.go @@ -214,10 +214,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) } From c30acdfa2f2b34efb4148efae277a89ff988b4d7 Mon Sep 17 00:00:00 2001 From: ak Date: Mon, 11 Feb 2019 21:43:39 -0800 Subject: [PATCH 04/18] adding tests for updating staking list --- consensus/consensus.go | 4 -- node/contract.go | 2 +- node/node.go | 13 +++--- node/node_test.go | 99 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 108 insertions(+), 10 deletions(-) diff --git a/consensus/consensus.go b/consensus/consensus.go index 501e03833..4e2c74ec7 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -106,10 +106,6 @@ type Consensus struct { // List of offline Peers OfflinePeerList []p2p.Peer - - //List of nodes related to beaconchain funcs - WaitingNodes []common.Address - ActiveNodes []common.Address } // BFTBlockInfo send the latest block that was in BFT consensus process as well as its consensusID to state syncing diff --git a/node/contract.go b/node/contract.go index 00d1505de..10edfc19c 100644 --- a/node/contract.go +++ b/node/contract.go @@ -36,7 +36,7 @@ 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.addPendingTransactions(types.Transactions{mycontracttx}) } diff --git a/node/node.go b/node/node.go index 9bea70027..1e9d1d559 100644 --- a/node/node.go +++ b/node/node.go @@ -160,9 +160,9 @@ type Node struct { serviceManager *service_manager.Manager //Staked Accounts and Contract - CurrentStakes map[common.Address]int64 - StakeContractAddress common.Address - WithdrawStakeFunc []byte + CurrentStakes map[common.Address]int64 + StakingContractAddress common.Address + WithdrawStakeFunc []byte //Node Account AccountKey *ecdsa.PrivateKey @@ -262,6 +262,9 @@ func New(host p2p.Host, consensus *bft.Consensus, db ethdb.Database) *Node { node.AddStakingContractToPendingTransactions() 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) } @@ -585,7 +588,7 @@ func (node *Node) UpdateStakingList(block *types.Block) error { currentSender, _ := types.Sender(signerType, txn) _, isPresent := node.CurrentStakes[currentSender] toAddress := txn.To() - if *toAddress != node.StakeContractAddress { //Not a address aimed at the staking contract. + if *toAddress != node.StakingContractAddress { //Not a address aimed at the staking contract. continue } if value > int64(0) { //If value >0 means its a staking deposit transaction @@ -617,7 +620,7 @@ func (node *Node) UpdateStakingList(block *types.Block) error { func decodeStakeCall(getData []byte) int64 { value := new(big.Int) - value.SetBytes(getData[4:]) + value.SetBytes(getData[4:]) //Escape the method call. return value.Int64() } func (node *Node) setupForShardLeader() { diff --git a/node/node_test.go b/node/node_test.go index 3e51c9040..d92484127 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -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") + } + +} From a9e9a731140ad9c05984de4637b1067228e19c0d Mon Sep 17 00:00:00 2001 From: chaosma Date: Tue, 12 Feb 2019 11:05:44 -0800 Subject: [PATCH 05/18] fixed state syncing the last mile issue for BLS consensus integration (#426) --- node/node.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/node/node.go b/node/node.go index 918895be6..5a257a781 100644 --- a/node/node.go +++ b/node/node.go @@ -326,14 +326,14 @@ func (node *Node) DoSyncing() { continue case consensusBlockInfo := <-node.Consensus.ConsensusBlock: if !node.IsOutOfSync(consensusBlockInfo) { + startHash := node.blockchain.CurrentBlock().Hash() + node.stateSync.StartStateSync(startHash[:], node.blockchain, node.Worker) if node.State == NodeNotInSync { utils.GetLogInstance().Info("[SYNC] Node is now IN SYNC!") } node.stateMutex.Lock() node.State = NodeReadyForConsensus node.stateMutex.Unlock() - // wait for last mile block finish; think a better way - time.Sleep(200 * time.Millisecond) node.stateSync.CloseConnections() node.stateSync = nil continue From 02abf88079bfe86abebe80c6e31a679905115db0 Mon Sep 17 00:00:00 2001 From: Eugene Kim Date: Tue, 12 Feb 2019 11:10:26 -0800 Subject: [PATCH 06/18] Move connection logger to internal/utils Suggested-by: Leo Chen --- cmd/harmony.go | 47 +---------------------------- internal/utils/connlogger.go | 57 ++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 46 deletions(-) create mode 100644 internal/utils/connlogger.go diff --git a/cmd/harmony.go b/cmd/harmony.go index ddfc7e868..b5c0c9753 100644 --- a/cmd/harmony.go +++ b/cmd/harmony.go @@ -11,7 +11,6 @@ import ( "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" - net "github.com/libp2p/go-libp2p-net" peerstore "github.com/libp2p/go-libp2p-peerstore" multiaddr "github.com/multiformats/go-multiaddr" @@ -79,50 +78,6 @@ func loggingInit(logFolder, role, ip, port string, onlyLogTps bool) { log.Root().SetHandler(h) } -type connLogger struct{} - -func (connLogger) Listen(net net.Network, ma multiaddr.Multiaddr) { - log.Debug("[CONNECTIONS] Listener starting", "net", net, "addr", ma) -} - -func (connLogger) ListenClose(net net.Network, ma multiaddr.Multiaddr) { - log.Debug("[CONNECTIONS] Listener closing", "net", net, "addr", ma) -} - -func (connLogger) Connected(net net.Network, conn net.Conn) { - log.Debug("[CONNECTIONS] Connected", "net", net, - "localPeer", conn.LocalPeer(), "localAddr", conn.LocalMultiaddr(), - "remotePeer", conn.RemotePeer(), "remoteAddr", conn.RemoteMultiaddr(), - ) -} - -func (connLogger) Disconnected(net net.Network, conn net.Conn) { - log.Debug("[CONNECTIONS] Disconnected", "net", net, - "localPeer", conn.LocalPeer(), "localAddr", conn.LocalMultiaddr(), - "remotePeer", conn.RemotePeer(), "remoteAddr", conn.RemoteMultiaddr(), - ) -} - -func (connLogger) OpenedStream(net net.Network, stream net.Stream) { - conn := stream.Conn() - log.Debug("[CONNECTIONS] Stream opened", "net", net, - "localPeer", conn.LocalPeer(), "localAddr", conn.LocalMultiaddr(), - "remotePeer", conn.RemotePeer(), "remoteAddr", conn.RemoteMultiaddr(), - "protocol", stream.Protocol(), - ) -} - -func (connLogger) ClosedStream(net net.Network, stream net.Stream) { - conn := stream.Conn() - log.Debug("[CONNECTIONS] Stream closed", "net", net, - "localPeer", conn.LocalPeer(), "localAddr", conn.LocalMultiaddr(), - "remotePeer", conn.RemotePeer(), "remoteAddr", conn.RemoteMultiaddr(), - "protocol", stream.Protocol(), - ) -} - -var theConnLogger connLogger - func main() { // TODO: use http://getmyipaddress.org/ or http://www.get-myip.com/ to retrieve my IP address ip := flag.String("ip", "127.0.0.1", "IP of the node") @@ -261,7 +216,7 @@ func main() { host, err := p2pimpl.NewHost(&selfPeer, nodePriKey) if *logConn { - host.GetP2PHost().Network().Notify(theConnLogger) + host.GetP2PHost().Network().Notify(utils.ConnLogger) } if err != nil { panic("unable to new host in harmony") diff --git a/internal/utils/connlogger.go b/internal/utils/connlogger.go new file mode 100644 index 000000000..d283867a3 --- /dev/null +++ b/internal/utils/connlogger.go @@ -0,0 +1,57 @@ +package utils + +import ( + "github.com/ethereum/go-ethereum/log" + net "github.com/libp2p/go-libp2p-net" + ma "github.com/multiformats/go-multiaddr" +) + +type connLogger struct{} + +func (connLogger) Listen(net net.Network, ma ma.Multiaddr) { + log.Debug("[CONNECTIONS] Listener starting", "net", net, "addr", ma) +} + +func (connLogger) ListenClose(net net.Network, ma ma.Multiaddr) { + log.Debug("[CONNECTIONS] Listener closing", "net", net, "addr", ma) +} + +func (connLogger) Connected(net net.Network, conn net.Conn) { + log.Debug("[CONNECTIONS] Connected", "net", net, + "localPeer", conn.LocalPeer(), "localAddr", conn.LocalMultiaddr(), + "remotePeer", conn.RemotePeer(), "remoteAddr", conn.RemoteMultiaddr(), + ) +} + +func (connLogger) Disconnected(net net.Network, conn net.Conn) { + log.Debug("[CONNECTIONS] Disconnected", "net", net, + "localPeer", conn.LocalPeer(), "localAddr", conn.LocalMultiaddr(), + "remotePeer", conn.RemotePeer(), "remoteAddr", conn.RemoteMultiaddr(), + ) +} + +func (connLogger) OpenedStream(net net.Network, stream net.Stream) { + conn := stream.Conn() + log.Debug("[CONNECTIONS] Stream opened", "net", net, + "localPeer", conn.LocalPeer(), "localAddr", conn.LocalMultiaddr(), + "remotePeer", conn.RemotePeer(), "remoteAddr", conn.RemoteMultiaddr(), + "protocol", stream.Protocol(), + ) +} + +func (connLogger) ClosedStream(net net.Network, stream net.Stream) { + conn := stream.Conn() + log.Debug("[CONNECTIONS] Stream closed", "net", net, + "localPeer", conn.LocalPeer(), "localAddr", conn.LocalMultiaddr(), + "remotePeer", conn.RemotePeer(), "remoteAddr", conn.RemoteMultiaddr(), + "protocol", stream.Protocol(), + ) +} + +// ConnLogger is a LibP2P connection logger. +// Add on a LibP2P host by calling: +// +// host.Network().Notify(utils.ConnLogger) +// +// It logs all listener/connection/stream open/close activities at debug level. +var ConnLogger connLogger From 3e4a8ca33bd58ab1af9d2d5dbec3b24b27c45f33 Mon Sep 17 00:00:00 2001 From: ak Date: Tue, 12 Feb 2019 11:26:21 -0800 Subject: [PATCH 07/18] interface for getting contract address --- node/node.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/node/node.go b/node/node.go index 1e9d1d559..ef64b9434 100644 --- a/node/node.go +++ b/node/node.go @@ -285,6 +285,26 @@ func New(host p2p.Host, consensus *bft.Consensus, db ethdb.Database) *Node { return &node } +//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) getDeployedStakingContract(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 From bd0f4401deb80eca14a31fce22ed30e64583c965 Mon Sep 17 00:00:00 2001 From: Eugene Kim Date: Tue, 12 Feb 2019 11:28:18 -0800 Subject: [PATCH 08/18] =?UTF-8?q?DYLD=5FLIBRARY=5FPATH=20=E2=86=92=20DYLD?= =?UTF-8?q?=5FFALLBACK=5FLIBRARY=5FPATH?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DYLD_LIBRARY_PATH has a “look here first before usual places” semantics, in order to let users use an alternative version of a shared library. On the other side, DYLD_FALLBACK_LIBRARY_PATH has a “look here if not found in usual places” semantics, in order to let users specify additional locations where a shared library is found. In our case, if a binary refers to a common shared library found in usual places, we want to use it; we simply want to specify additional locations where BLS/MCL/OpenSSL libraries are found. So it is more appropriate to use DYLD_FALLBACK_LIBRARY_PATH. This also fixes a nasty symbol table mismatch, which is the main reason for this change: Certain commonly used shared libraries - such as libJPEG.dylib, co-located within the ImageIO framework and expected by the main ImageIO library – are often also found in Homebrew or MacPorts root (/opt/local//lib for MacPort). If DYLD_LIBRARY_PATH included /opt/local/lib for OpenSSL, many system programs linked against ImageIO will erroneously pick up libJPEG.dylib in /opt/local/lib, which lacks certain internal symbols that the framework version has. Using DYLD_FALLBACK_LIBRARY_PATH makes dyld honor the full path to the framework version of libJPEG, which is hardcoded in the main ImageIO framework library. --- README.md | 2 +- scripts/setup_bls_build_flags.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4b315fcd3..983d09bd8 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ export CGO_CFLAGS="-I$GOPATH/src/github.com/harmony-one/bls/include -I$GOPATH/sr export CGO_LDFLAGS="-L$GOPATH/src/github.com/harmony-one/bls/lib -L/usr/local/opt/openssl/lib" export LD_LIBRARY_PATH=$GOPATH/src/github.com/harmony-one/bls/lib:$GOPATH/src/github.com/harmony-one/mcl/lib:/usr/local/opt/openssl/lib export LIBRARY_PATH=$LD_LIBRARY_PATH -export DYLD_LIBRARY_PATH=$LD_LIBRARY_PATH +export DYLD_FALLBACK_LIBRARY_PATH=$LD_LIBRARY_PATH mkdir -p $HOME//src/github.com/harmony-one diff --git a/scripts/setup_bls_build_flags.sh b/scripts/setup_bls_build_flags.sh index eb0115c18..c65e9c7f4 100644 --- a/scripts/setup_bls_build_flags.sh +++ b/scripts/setup_bls_build_flags.sh @@ -16,6 +16,6 @@ case $OS in export CGO_CFLAGS="-I${BLS_DIR}/include -I${MCL_DIR}/include -I${OPENSSL_DIR}/include" export CGO_LDFLAGS="-L${BLS_DIR}/lib -L${OPENSSL_DIR}/lib" export LD_LIBRARY_PATH=${BLS_DIR}/lib:${MCL_DIR}/lib:${OPENSSL_DIR}/lib - export DYLD_LIBRARY_PATH=$LD_LIBRARY_PATH + export DYLD_FALLBACK_LIBRARY_PATH=$LD_LIBRARY_PATH ;; esac From 6e7902a5efbb6b8d705913f407a4e3264f26cdb7 Mon Sep 17 00:00:00 2001 From: ak Date: Tue, 12 Feb 2019 13:29:43 -0800 Subject: [PATCH 09/18] responding to comments --- node/contract.go | 5 ++--- node/node.go | 6 +++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/node/contract.go b/node/contract.go index 10edfc19c..31c018bd1 100644 --- a/node/contract.go +++ b/node/contract.go @@ -36,7 +36,8 @@ 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.StakingContractAddress = crypto.CreateAddress(contractAddress, uint64(0)) + //node.StakingContractAddress = crypto.CreateAddress(contractAddress, uint64(0)) + node.StakingContractAddress = node.getDeployedStakingContract(mycontracttx, contractAddress) node.addPendingTransactions(types.Transactions{mycontracttx}) } @@ -50,8 +51,6 @@ func (node *Node) CreateStakingWithdrawTransaction(stake string) (*types.Transac log.Error("Failed to get chain state", "Error", err) } nonce := state.GetNonce(crypto.PubkeyToAddress(DepositContractPriKey.PublicKey)) - //callingFunction := "0x2e1a7d4d" - //Following: https://github.com/miguelmota/ethereum-development-with-go-book/blob/master/code/transfer_tokens.go withdrawFnSignature := []byte("withdraw(uint)") hash := sha3.NewLegacyKeccak256() diff --git a/node/node.go b/node/node.go index ef64b9434..372dcdf6a 100644 --- a/node/node.go +++ b/node/node.go @@ -160,7 +160,7 @@ type Node struct { serviceManager *service_manager.Manager //Staked Accounts and Contract - CurrentStakes map[common.Address]int64 + CurrentStakes map[common.Address]int64 //This will save the latest information about staked nodes. StakingContractAddress common.Address WithdrawStakeFunc []byte @@ -259,7 +259,7 @@ 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 { @@ -289,7 +289,7 @@ func New(host p2p.Host, consensus *bft.Consensus, db ethdb.Database) *Node { //(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) getDeployedStakingContract(mycontracttx types.Transaction, contractAddress common.Address) common.Address { +func (node *Node) getDeployedStakingContract(mycontracttx *types.Transaction, contractAddress common.Address) common.Address { //Ideally we send the transaction to //Correct Way 1: From f5e606f7a8f9e88e7cd3b674d8c9e4344bf8b00a Mon Sep 17 00:00:00 2001 From: ak Date: Tue, 12 Feb 2019 13:46:16 -0800 Subject: [PATCH 10/18] adding function for supporing client --- node/node.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/node/node.go b/node/node.go index 372dcdf6a..be0c484da 100644 --- a/node/node.go +++ b/node/node.go @@ -285,11 +285,14 @@ func New(host p2p.Host, consensus *bft.Consensus, db ethdb.Database) *Node { return &node } +func (node *Node) getDeployedStakingContractAddress() common.Address { + retrun 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) getDeployedStakingContract(mycontracttx *types.Transaction, contractAddress common.Address) common.Address { +func (node *Node) generateDeployedStakingContractAddress(mycontracttx *types.Transaction, contractAddress common.Address) common.Address { //Ideally we send the transaction to //Correct Way 1: @@ -611,6 +614,8 @@ func (node *Node) UpdateStakingList(block *types.Block) error { 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 From 4a691d5844c23ba7af32c859e5b6707c70266e55 Mon Sep 17 00:00:00 2001 From: ak Date: Tue, 12 Feb 2019 13:47:13 -0800 Subject: [PATCH 11/18] fixing typo --- node/node.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/node/node.go b/node/node.go index be0c484da..0512d064f 100644 --- a/node/node.go +++ b/node/node.go @@ -286,8 +286,9 @@ func New(host p2p.Host, consensus *bft.Consensus, db ethdb.Database) *Node { } func (node *Node) getDeployedStakingContractAddress() common.Address { - retrun node.StakingContractAddress + 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. From b8c632a0831d6af4a9ff1240601ee2dc7c71842a Mon Sep 17 00:00:00 2001 From: ak Date: Tue, 12 Feb 2019 13:51:22 -0800 Subject: [PATCH 12/18] typos --- node/contract.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/contract.go b/node/contract.go index 31c018bd1..86647ee7a 100644 --- a/node/contract.go +++ b/node/contract.go @@ -37,7 +37,7 @@ func (node *Node) AddStakingContractToPendingTransactions() { // 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.StakingContractAddress = crypto.CreateAddress(contractAddress, uint64(0)) - node.StakingContractAddress = node.getDeployedStakingContract(mycontracttx, contractAddress) + node.StakingContractAddress = node.generateDeployedStakingContractAddress(mycontracttx, contractAddress) node.addPendingTransactions(types.Transactions{mycontracttx}) } From aba13c252c50b464bde268c344816a4f6e01117a Mon Sep 17 00:00:00 2001 From: Minh Doan Date: Mon, 11 Feb 2019 23:30:04 -0800 Subject: [PATCH 13/18] add gen script --- api/client/service/proto/gen.sh | 1 + 1 file changed, 1 insertion(+) create mode 100755 api/client/service/proto/gen.sh diff --git a/api/client/service/proto/gen.sh b/api/client/service/proto/gen.sh new file mode 100755 index 000000000..7ebc0ef6d --- /dev/null +++ b/api/client/service/proto/gen.sh @@ -0,0 +1 @@ +protoc -I ./ client.proto --go_out=./ From b1ad9637a62cf0295e5f63bf313ae1946f366a7d Mon Sep 17 00:00:00 2001 From: Minh Doan Date: Mon, 11 Feb 2019 23:44:16 -0800 Subject: [PATCH 14/18] update proto file --- api/client/service/proto/client.proto | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/api/client/service/proto/client.proto b/api/client/service/proto/client.proto index da466c34a..18f833ab5 100644 --- a/api/client/service/proto/client.proto +++ b/api/client/service/proto/client.proto @@ -6,6 +6,7 @@ package client; service ClientService { rpc FetchAccountState(FetchAccountStateRequest) returns (FetchAccountStateResponse) {} rpc GetFreeToken(GetFreeTokenRequest) returns (GetFreeTokenResponse) {} + rpc GetStakingContractInfo(StakingContractInfoRequest) returns (StakingContractInfoResponse) {} } // FetchAccountStateRequest is the request to fetch an account's balance and nonce. @@ -33,3 +34,20 @@ message GetFreeTokenResponse { // The transaction Id that requests free token from the faucet. bytes txId = 1; } + +// StakingContractInfoRequest is the request to necessary info for stkaing. +message StakingContractInfoRequest { + // The account address + bytes address = 1; +} + +// StakingContractInfoResponse is the response of GetStakingContractInfo. +message StakingContractInfoResponse { + // Contract address. + string conctract_address = 1; + // The balance of the staking account. + bytes balance = 2; + // The nonce of the staking account. + uint64 nonce = 3; +} + From 230dbdc2e228e49324ecaac5390cf466de4a97b7 Mon Sep 17 00:00:00 2001 From: Minh Doan Date: Mon, 11 Feb 2019 23:44:30 -0800 Subject: [PATCH 15/18] modify logic of new proto --- api/client/service/client.go | 12 ++ api/client/service/gen.sh | 1 + api/client/service/proto/client.pb.go | 172 +++++++++++++++++++++++--- api/client/service/proto/gen.sh | 1 - api/client/service/server.go | 35 ++++-- api/client/service/server_test.go | 4 +- api/service/clientsupport/service.go | 10 +- 7 files changed, 207 insertions(+), 28 deletions(-) create mode 100755 api/client/service/gen.sh delete mode 100755 api/client/service/proto/gen.sh diff --git a/api/client/service/client.go b/api/client/service/client.go index 726a33381..59c7b9658 100644 --- a/api/client/service/client.go +++ b/api/client/service/client.go @@ -62,3 +62,15 @@ func (client *Client) GetFreeToken(address common.Address) *proto.GetFreeTokenRe } return response } + +// GetStakingContractInfo gets necessary info for staking. +func (client *Client) GetStakingContractInfo(address common.Address) *proto.StakingContractInfoResponse { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + request := &proto.StakingContractInfoRequest{Address: address.Bytes()} + response, err := client.clientServiceClient.GetStakingContractInfo(ctx, request) + if err != nil { + log.Fatalf("Error getting free token: %s", err) + } + return response +} diff --git a/api/client/service/gen.sh b/api/client/service/gen.sh new file mode 100755 index 000000000..ccaa5fb75 --- /dev/null +++ b/api/client/service/gen.sh @@ -0,0 +1 @@ +protoc -I proto/ proto/client.proto --go_out=plugins=grpc:proto diff --git a/api/client/service/proto/client.pb.go b/api/client/service/proto/client.pb.go index cc2b02400..b0bae2715 100644 --- a/api/client/service/proto/client.pb.go +++ b/api/client/service/proto/client.pb.go @@ -195,32 +195,139 @@ func (m *GetFreeTokenResponse) GetTxId() []byte { return nil } +// StakingContractInfoRequest is the request to necessary info for stkaing. +type StakingContractInfoRequest struct { + // The account address + Address []byte `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *StakingContractInfoRequest) Reset() { *m = StakingContractInfoRequest{} } +func (m *StakingContractInfoRequest) String() string { return proto.CompactTextString(m) } +func (*StakingContractInfoRequest) ProtoMessage() {} +func (*StakingContractInfoRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_014de31d7ac8c57c, []int{4} +} + +func (m *StakingContractInfoRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_StakingContractInfoRequest.Unmarshal(m, b) +} +func (m *StakingContractInfoRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_StakingContractInfoRequest.Marshal(b, m, deterministic) +} +func (m *StakingContractInfoRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_StakingContractInfoRequest.Merge(m, src) +} +func (m *StakingContractInfoRequest) XXX_Size() int { + return xxx_messageInfo_StakingContractInfoRequest.Size(m) +} +func (m *StakingContractInfoRequest) XXX_DiscardUnknown() { + xxx_messageInfo_StakingContractInfoRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_StakingContractInfoRequest proto.InternalMessageInfo + +func (m *StakingContractInfoRequest) GetAddress() []byte { + if m != nil { + return m.Address + } + return nil +} + +// StakingContractInfoResponse is the response of GetStakingContractInfo. +type StakingContractInfoResponse struct { + // Contract address. + ConctractAddress string `protobuf:"bytes,1,opt,name=conctract_address,json=conctractAddress,proto3" json:"conctract_address,omitempty"` + // The balance of the staking account. + Balance []byte `protobuf:"bytes,2,opt,name=balance,proto3" json:"balance,omitempty"` + // The nonce of the staking account. + Nonce uint64 `protobuf:"varint,3,opt,name=nonce,proto3" json:"nonce,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *StakingContractInfoResponse) Reset() { *m = StakingContractInfoResponse{} } +func (m *StakingContractInfoResponse) String() string { return proto.CompactTextString(m) } +func (*StakingContractInfoResponse) ProtoMessage() {} +func (*StakingContractInfoResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_014de31d7ac8c57c, []int{5} +} + +func (m *StakingContractInfoResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_StakingContractInfoResponse.Unmarshal(m, b) +} +func (m *StakingContractInfoResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_StakingContractInfoResponse.Marshal(b, m, deterministic) +} +func (m *StakingContractInfoResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_StakingContractInfoResponse.Merge(m, src) +} +func (m *StakingContractInfoResponse) XXX_Size() int { + return xxx_messageInfo_StakingContractInfoResponse.Size(m) +} +func (m *StakingContractInfoResponse) XXX_DiscardUnknown() { + xxx_messageInfo_StakingContractInfoResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_StakingContractInfoResponse proto.InternalMessageInfo + +func (m *StakingContractInfoResponse) GetConctractAddress() string { + if m != nil { + return m.ConctractAddress + } + return "" +} + +func (m *StakingContractInfoResponse) GetBalance() []byte { + if m != nil { + return m.Balance + } + return nil +} + +func (m *StakingContractInfoResponse) GetNonce() uint64 { + if m != nil { + return m.Nonce + } + return 0 +} + func init() { proto.RegisterType((*FetchAccountStateRequest)(nil), "client.FetchAccountStateRequest") proto.RegisterType((*FetchAccountStateResponse)(nil), "client.FetchAccountStateResponse") proto.RegisterType((*GetFreeTokenRequest)(nil), "client.GetFreeTokenRequest") proto.RegisterType((*GetFreeTokenResponse)(nil), "client.GetFreeTokenResponse") + proto.RegisterType((*StakingContractInfoRequest)(nil), "client.StakingContractInfoRequest") + proto.RegisterType((*StakingContractInfoResponse)(nil), "client.StakingContractInfoResponse") } func init() { proto.RegisterFile("client.proto", fileDescriptor_014de31d7ac8c57c) } var fileDescriptor_014de31d7ac8c57c = []byte{ - // 229 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x49, 0xce, 0xc9, 0x4c, - 0xcd, 0x2b, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x83, 0xf0, 0x94, 0x4c, 0xb8, 0x24, - 0xdc, 0x52, 0x4b, 0x92, 0x33, 0x1c, 0x93, 0x93, 0xf3, 0x4b, 0xf3, 0x4a, 0x82, 0x4b, 0x12, 0x4b, - 0x52, 0x83, 0x52, 0x0b, 0x4b, 0x53, 0x8b, 0x4b, 0x84, 0x24, 0xb8, 0xd8, 0x13, 0x53, 0x52, 0x8a, - 0x52, 0x8b, 0x8b, 0x25, 0x18, 0x15, 0x18, 0x35, 0x78, 0x82, 0x60, 0x5c, 0x25, 0x6f, 0x2e, 0x49, - 0x2c, 0xba, 0x8a, 0x0b, 0xf2, 0xf3, 0x8a, 0x53, 0x41, 0xda, 0x92, 0x12, 0x73, 0x12, 0xf3, 0x92, - 0x53, 0x61, 0xda, 0xa0, 0x5c, 0x21, 0x11, 0x2e, 0xd6, 0xbc, 0x7c, 0x90, 0x38, 0x93, 0x02, 0xa3, - 0x06, 0x4b, 0x10, 0x84, 0xa3, 0xa4, 0xcf, 0x25, 0xec, 0x9e, 0x5a, 0xe2, 0x56, 0x94, 0x9a, 0x1a, - 0x92, 0x9f, 0x9d, 0x9a, 0x47, 0xd8, 0x76, 0x2d, 0x2e, 0x11, 0x54, 0x0d, 0x50, 0x8b, 0x85, 0xb8, - 0x58, 0x4a, 0x2a, 0x3c, 0x53, 0xa0, 0xca, 0xc1, 0x6c, 0xa3, 0x1d, 0x8c, 0x5c, 0xbc, 0xce, 0x60, - 0xaf, 0x06, 0xa7, 0x16, 0x95, 0x65, 0x26, 0xa7, 0x0a, 0x45, 0x71, 0x09, 0x62, 0xb8, 0x5d, 0x48, - 0x41, 0x0f, 0x1a, 0x3a, 0xb8, 0x02, 0x43, 0x4a, 0x11, 0x8f, 0x0a, 0x88, 0xfd, 0x4a, 0x0c, 0x42, - 0xde, 0x5c, 0x3c, 0xc8, 0x2e, 0x13, 0x92, 0x86, 0x69, 0xc2, 0xe2, 0x41, 0x29, 0x19, 0xec, 0x92, - 0x30, 0xc3, 0x92, 0xd8, 0xc0, 0x31, 0x65, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0xc6, 0xd9, 0x35, - 0x0c, 0xb9, 0x01, 0x00, 0x00, + // 305 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x92, 0x4f, 0x4b, 0xc3, 0x40, + 0x10, 0xc5, 0x6d, 0xac, 0x15, 0x87, 0x08, 0x76, 0x2d, 0x12, 0x53, 0x0f, 0x71, 0xbd, 0x14, 0x85, + 0x0a, 0x2a, 0xde, 0x4b, 0xa1, 0xa5, 0xf4, 0x96, 0x78, 0xf2, 0x22, 0xdb, 0xcd, 0xa8, 0xa1, 0x65, + 0xb6, 0x26, 0x53, 0x11, 0xbf, 0x8b, 0xdf, 0x55, 0x9a, 0x3f, 0x25, 0x62, 0xd2, 0xde, 0xf2, 0x76, + 0xe6, 0xb7, 0x8f, 0xbc, 0xb7, 0x60, 0xeb, 0x45, 0x84, 0xc4, 0xfd, 0x65, 0x6c, 0xd8, 0x88, 0x56, + 0xa6, 0xe4, 0x03, 0x38, 0x23, 0x64, 0xfd, 0x3e, 0xd0, 0xda, 0xac, 0x88, 0x03, 0x56, 0x8c, 0x3e, + 0x7e, 0xac, 0x30, 0x61, 0xe1, 0xc0, 0xa1, 0x0a, 0xc3, 0x18, 0x93, 0xc4, 0x69, 0x78, 0x8d, 0x9e, + 0xed, 0x17, 0x52, 0x4e, 0xe1, 0xbc, 0x82, 0x4a, 0x96, 0x86, 0x12, 0x5c, 0x63, 0x33, 0xb5, 0x50, + 0xa4, 0xb1, 0xc0, 0x72, 0x29, 0x3a, 0x70, 0x40, 0x66, 0x7d, 0x6e, 0x79, 0x8d, 0x5e, 0xd3, 0xcf, + 0x84, 0xbc, 0x85, 0xd3, 0x31, 0xf2, 0x28, 0x46, 0x7c, 0x32, 0x73, 0xa4, 0xdd, 0xee, 0xd7, 0xd0, + 0xf9, 0x0b, 0xe4, 0xc6, 0x02, 0x9a, 0xfc, 0x35, 0x09, 0xf3, 0xf5, 0xf4, 0x5b, 0x3e, 0x82, 0x1b, + 0xb0, 0x9a, 0x47, 0xf4, 0x36, 0x34, 0xc4, 0xb1, 0xd2, 0x3c, 0xa1, 0x57, 0xb3, 0xdb, 0xe3, 0x1b, + 0xba, 0x95, 0x5c, 0x6e, 0x75, 0x03, 0x6d, 0x6d, 0x48, 0xa7, 0x83, 0x97, 0xf2, 0x15, 0x47, 0xfe, + 0xc9, 0x66, 0x30, 0xc8, 0xce, 0xcb, 0x81, 0x58, 0x35, 0x81, 0xec, 0x97, 0x02, 0xb9, 0xfb, 0xb1, + 0xe0, 0x78, 0x98, 0xd6, 0x13, 0x60, 0xfc, 0x19, 0x69, 0x14, 0xcf, 0xd0, 0xfe, 0x97, 0xb7, 0xf0, + 0xfa, 0x79, 0xa3, 0x75, 0x05, 0xba, 0x97, 0x5b, 0x36, 0xb2, 0x1f, 0x91, 0x7b, 0x62, 0x0a, 0x76, + 0x39, 0x4d, 0xd1, 0x2d, 0xa0, 0x8a, 0x52, 0xdc, 0x8b, 0xea, 0xe1, 0xe6, 0x32, 0x0d, 0x67, 0x63, + 0xe4, 0x8a, 0xe4, 0x84, 0x2c, 0xc8, 0xfa, 0x3a, 0xdc, 0xab, 0xad, 0x3b, 0x85, 0xc9, 0xac, 0x95, + 0x3e, 0xe1, 0xfb, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xa2, 0x16, 0x0e, 0x6f, 0xd2, 0x02, 0x00, + 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -237,6 +344,7 @@ const _ = grpc.SupportPackageIsVersion4 type ClientServiceClient interface { FetchAccountState(ctx context.Context, in *FetchAccountStateRequest, opts ...grpc.CallOption) (*FetchAccountStateResponse, error) GetFreeToken(ctx context.Context, in *GetFreeTokenRequest, opts ...grpc.CallOption) (*GetFreeTokenResponse, error) + GetStakingContractInfo(ctx context.Context, in *StakingContractInfoRequest, opts ...grpc.CallOption) (*StakingContractInfoResponse, error) } type clientServiceClient struct { @@ -265,10 +373,20 @@ func (c *clientServiceClient) GetFreeToken(ctx context.Context, in *GetFreeToken return out, nil } +func (c *clientServiceClient) GetStakingContractInfo(ctx context.Context, in *StakingContractInfoRequest, opts ...grpc.CallOption) (*StakingContractInfoResponse, error) { + out := new(StakingContractInfoResponse) + err := c.cc.Invoke(ctx, "/client.ClientService/GetStakingContractInfo", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // ClientServiceServer is the server API for ClientService service. type ClientServiceServer interface { FetchAccountState(context.Context, *FetchAccountStateRequest) (*FetchAccountStateResponse, error) GetFreeToken(context.Context, *GetFreeTokenRequest) (*GetFreeTokenResponse, error) + GetStakingContractInfo(context.Context, *StakingContractInfoRequest) (*StakingContractInfoResponse, error) } func RegisterClientServiceServer(s *grpc.Server, srv ClientServiceServer) { @@ -311,6 +429,24 @@ func _ClientService_GetFreeToken_Handler(srv interface{}, ctx context.Context, d return interceptor(ctx, in, info, handler) } +func _ClientService_GetStakingContractInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(StakingContractInfoRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ClientServiceServer).GetStakingContractInfo(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/client.ClientService/GetStakingContractInfo", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ClientServiceServer).GetStakingContractInfo(ctx, req.(*StakingContractInfoRequest)) + } + return interceptor(ctx, in, info, handler) +} + var _ClientService_serviceDesc = grpc.ServiceDesc{ ServiceName: "client.ClientService", HandlerType: (*ClientServiceServer)(nil), @@ -323,6 +459,10 @@ var _ClientService_serviceDesc = grpc.ServiceDesc{ MethodName: "GetFreeToken", Handler: _ClientService_GetFreeToken_Handler, }, + { + MethodName: "GetStakingContractInfo", + Handler: _ClientService_GetStakingContractInfo_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "client.proto", diff --git a/api/client/service/proto/gen.sh b/api/client/service/proto/gen.sh deleted file mode 100755 index 7ebc0ef6d..000000000 --- a/api/client/service/proto/gen.sh +++ /dev/null @@ -1 +0,0 @@ -protoc -I ./ client.proto --go_out=./ diff --git a/api/client/service/server.go b/api/client/service/server.go index 0453fe2f7..34e8e5581 100644 --- a/api/client/service/server.go +++ b/api/client/service/server.go @@ -6,17 +6,16 @@ import ( "net" "github.com/ethereum/go-ethereum/common" + proto "github.com/harmony-one/harmony/api/client/service/proto" "github.com/harmony-one/harmony/core/state" - "google.golang.org/grpc" - - proto "github.com/harmony-one/harmony/api/client/service/proto" ) // Server is the Server struct for client service package. type Server struct { - stateReader func() (*state.DB, error) - callFaucetContract func(common.Address) common.Hash + stateReader func() (*state.DB, error) + callFaucetContract func(common.Address) common.Hash + getDeployedStakingContractAddress func() common.Address } // FetchAccountState implements the FetchAccountState interface to return account state. @@ -39,6 +38,21 @@ func (s *Server) GetFreeToken(ctx context.Context, request *proto.GetFreeTokenRe return &proto.GetFreeTokenResponse{TxId: s.callFaucetContract(address).Bytes()}, nil } +// GetStakingContractInfo implements the GetStakingContractInfo interface to return necessary info for staking. +func (s *Server) GetStakingContractInfo(ctx context.Context, request *proto.StakingContractInfoRequest) (*proto.StakingContractInfoResponse, error) { + var address common.Address + address.SetBytes(request.Address) + state, err := s.stateReader() + if err != nil { + return nil, err + } + return &proto.StakingContractInfoResponse{ + ConctractAddress: s.getDeployedStakingContractAddress().Hex(), + Balance: state.GetBalance(address).Bytes(), + Nonce: state.GetNonce(address), + }, nil +} + // Start starts the Server on given ip and port. func (s *Server) Start(ip, port string) (*grpc.Server, error) { // TODO(minhdoan): Currently not using ip. Fix it later. @@ -55,7 +69,14 @@ func (s *Server) Start(ip, port string) (*grpc.Server, error) { } // NewServer creates new Server which implements ClientServiceServer interface. -func NewServer(stateReader func() (*state.DB, error), callFaucetContract func(common.Address) common.Hash) *Server { - s := &Server{stateReader: stateReader, callFaucetContract: callFaucetContract} +func NewServer( + stateReader func() (*state.DB, error), + callFaucetContract func(common.Address) common.Hash, + getDeployedStakingContractAddress func() common.Address) *Server { + s := &Server{ + stateReader: stateReader, + callFaucetContract: callFaucetContract, + getDeployedStakingContractAddress: getDeployedStakingContractAddress, + } return s } diff --git a/api/client/service/server_test.go b/api/client/service/server_test.go index edb7a6398..d8ce892f3 100644 --- a/api/client/service/server_test.go +++ b/api/client/service/server_test.go @@ -33,7 +33,7 @@ func TestGetFreeToken(test *testing.T) { return nil, nil }, func(common.Address) common.Hash { return hash - }) + }, nil) testBankKey, _ := crypto.GenerateKey() testBankAddress := crypto.PubkeyToAddress(testBankKey.PublicKey) @@ -67,7 +67,7 @@ func TestFetchAccountState(test *testing.T) { return chain.State() }, func(common.Address) common.Hash { return hash - }) + }, nil) response, err := server.FetchAccountState(nil, &client.FetchAccountStateRequest{Address: testBankAddress.Bytes()}) diff --git a/api/service/clientsupport/service.go b/api/service/clientsupport/service.go index 20f07bacd..db378f57e 100644 --- a/api/service/clientsupport/service.go +++ b/api/service/clientsupport/service.go @@ -23,9 +23,15 @@ type Service struct { } // New returns new client support service. -func New(stateReader func() (*state.DB, error), callFaucetContract func(common.Address) common.Hash, ip, nodePort string) *Service { +func New(stateReader func() (*state.DB, error), + callFaucetContract func(common.Address) common.Hash, + getDeployedStakingContract func() common.Address, + ip, nodePort string) *Service { port, _ := strconv.Atoi(nodePort) - return &Service{server: clientService.NewServer(stateReader, callFaucetContract), ip: ip, port: strconv.Itoa(port + ClientServicePortDiff)} + return &Service{ + server: clientService.NewServer(stateReader, callFaucetContract, getDeployedStakingContract), + ip: ip, + port: strconv.Itoa(port + ClientServicePortDiff)} } // StartService starts client support service. From f886b609d35626ee4875b9b2f70fc95943d6728b Mon Sep 17 00:00:00 2001 From: Minh Doan Date: Tue, 12 Feb 2019 00:05:38 -0800 Subject: [PATCH 16/18] modify logic in staking service --- api/client/service/proto/client.pb.go | 47 +++++++++++++-------------- api/client/service/proto/client.proto | 2 +- api/client/service/server.go | 6 ++-- api/service/staking/service.go | 13 ++++---- node/node.go | 9 +++-- 5 files changed, 40 insertions(+), 37 deletions(-) diff --git a/api/client/service/proto/client.pb.go b/api/client/service/proto/client.pb.go index b0bae2715..5df844c57 100644 --- a/api/client/service/proto/client.pb.go +++ b/api/client/service/proto/client.pb.go @@ -239,7 +239,7 @@ func (m *StakingContractInfoRequest) GetAddress() []byte { // StakingContractInfoResponse is the response of GetStakingContractInfo. type StakingContractInfoResponse struct { // Contract address. - ConctractAddress string `protobuf:"bytes,1,opt,name=conctract_address,json=conctractAddress,proto3" json:"conctract_address,omitempty"` + ContractAddress string `protobuf:"bytes,1,opt,name=contract_address,json=contractAddress,proto3" json:"contract_address,omitempty"` // The balance of the staking account. Balance []byte `protobuf:"bytes,2,opt,name=balance,proto3" json:"balance,omitempty"` // The nonce of the staking account. @@ -274,9 +274,9 @@ func (m *StakingContractInfoResponse) XXX_DiscardUnknown() { var xxx_messageInfo_StakingContractInfoResponse proto.InternalMessageInfo -func (m *StakingContractInfoResponse) GetConctractAddress() string { +func (m *StakingContractInfoResponse) GetContractAddress() string { if m != nil { - return m.ConctractAddress + return m.ContractAddress } return "" } @@ -307,27 +307,26 @@ func init() { func init() { proto.RegisterFile("client.proto", fileDescriptor_014de31d7ac8c57c) } var fileDescriptor_014de31d7ac8c57c = []byte{ - // 305 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x92, 0x4f, 0x4b, 0xc3, 0x40, - 0x10, 0xc5, 0x6d, 0xac, 0x15, 0x87, 0x08, 0x76, 0x2d, 0x12, 0x53, 0x0f, 0x71, 0xbd, 0x14, 0x85, - 0x0a, 0x2a, 0xde, 0x4b, 0xa1, 0xa5, 0xf4, 0x96, 0x78, 0xf2, 0x22, 0xdb, 0xcd, 0xa8, 0xa1, 0x65, - 0xb6, 0x26, 0x53, 0x11, 0xbf, 0x8b, 0xdf, 0x55, 0x9a, 0x3f, 0x25, 0x62, 0xd2, 0xde, 0xf2, 0x76, - 0xe6, 0xb7, 0x8f, 0xbc, 0xb7, 0x60, 0xeb, 0x45, 0x84, 0xc4, 0xfd, 0x65, 0x6c, 0xd8, 0x88, 0x56, - 0xa6, 0xe4, 0x03, 0x38, 0x23, 0x64, 0xfd, 0x3e, 0xd0, 0xda, 0xac, 0x88, 0x03, 0x56, 0x8c, 0x3e, - 0x7e, 0xac, 0x30, 0x61, 0xe1, 0xc0, 0xa1, 0x0a, 0xc3, 0x18, 0x93, 0xc4, 0x69, 0x78, 0x8d, 0x9e, - 0xed, 0x17, 0x52, 0x4e, 0xe1, 0xbc, 0x82, 0x4a, 0x96, 0x86, 0x12, 0x5c, 0x63, 0x33, 0xb5, 0x50, - 0xa4, 0xb1, 0xc0, 0x72, 0x29, 0x3a, 0x70, 0x40, 0x66, 0x7d, 0x6e, 0x79, 0x8d, 0x5e, 0xd3, 0xcf, - 0x84, 0xbc, 0x85, 0xd3, 0x31, 0xf2, 0x28, 0x46, 0x7c, 0x32, 0x73, 0xa4, 0xdd, 0xee, 0xd7, 0xd0, - 0xf9, 0x0b, 0xe4, 0xc6, 0x02, 0x9a, 0xfc, 0x35, 0x09, 0xf3, 0xf5, 0xf4, 0x5b, 0x3e, 0x82, 0x1b, - 0xb0, 0x9a, 0x47, 0xf4, 0x36, 0x34, 0xc4, 0xb1, 0xd2, 0x3c, 0xa1, 0x57, 0xb3, 0xdb, 0xe3, 0x1b, - 0xba, 0x95, 0x5c, 0x6e, 0x75, 0x03, 0x6d, 0x6d, 0x48, 0xa7, 0x83, 0x97, 0xf2, 0x15, 0x47, 0xfe, - 0xc9, 0x66, 0x30, 0xc8, 0xce, 0xcb, 0x81, 0x58, 0x35, 0x81, 0xec, 0x97, 0x02, 0xb9, 0xfb, 0xb1, - 0xe0, 0x78, 0x98, 0xd6, 0x13, 0x60, 0xfc, 0x19, 0x69, 0x14, 0xcf, 0xd0, 0xfe, 0x97, 0xb7, 0xf0, - 0xfa, 0x79, 0xa3, 0x75, 0x05, 0xba, 0x97, 0x5b, 0x36, 0xb2, 0x1f, 0x91, 0x7b, 0x62, 0x0a, 0x76, - 0x39, 0x4d, 0xd1, 0x2d, 0xa0, 0x8a, 0x52, 0xdc, 0x8b, 0xea, 0xe1, 0xe6, 0x32, 0x0d, 0x67, 0x63, - 0xe4, 0x8a, 0xe4, 0x84, 0x2c, 0xc8, 0xfa, 0x3a, 0xdc, 0xab, 0xad, 0x3b, 0x85, 0xc9, 0xac, 0x95, - 0x3e, 0xe1, 0xfb, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xa2, 0x16, 0x0e, 0x6f, 0xd2, 0x02, 0x00, - 0x00, + // 302 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x52, 0x41, 0x4f, 0xf2, 0x40, + 0x14, 0xfc, 0xda, 0x0f, 0x31, 0xbe, 0xd4, 0xa8, 0x2b, 0x31, 0xb5, 0x78, 0xa8, 0xeb, 0x05, 0x3d, + 0x60, 0xa2, 0xc6, 0x3b, 0x21, 0x81, 0x10, 0x6e, 0xad, 0x27, 0x2f, 0x66, 0xd9, 0x3e, 0xb5, 0x81, + 0xec, 0x62, 0xfb, 0x30, 0xfc, 0x19, 0xff, 0xab, 0xa1, 0xdd, 0x35, 0x35, 0xb6, 0x70, 0xdb, 0x99, + 0x7d, 0xf3, 0x26, 0x3b, 0xb3, 0xe0, 0xc9, 0x45, 0x8a, 0x8a, 0xfa, 0xcb, 0x4c, 0x93, 0x66, 0xed, + 0x12, 0xf1, 0x07, 0xf0, 0x47, 0x48, 0xf2, 0x7d, 0x20, 0xa5, 0x5e, 0x29, 0x8a, 0x49, 0x10, 0x46, + 0xf8, 0xb1, 0xc2, 0x9c, 0x98, 0x0f, 0xfb, 0x22, 0x49, 0x32, 0xcc, 0x73, 0xdf, 0x09, 0x9d, 0x9e, + 0x17, 0x59, 0xc8, 0xa7, 0x70, 0x5e, 0xa3, 0xca, 0x97, 0x5a, 0xe5, 0xb8, 0x91, 0xcd, 0xc4, 0x42, + 0x28, 0x89, 0x56, 0x66, 0x20, 0xeb, 0xc0, 0x9e, 0xd2, 0x1b, 0xde, 0x0d, 0x9d, 0x5e, 0x2b, 0x2a, + 0x01, 0xbf, 0x85, 0xd3, 0x31, 0xd2, 0x28, 0x43, 0x7c, 0xd2, 0x73, 0x54, 0xbb, 0xdd, 0x6f, 0xa0, + 0xf3, 0x5b, 0x60, 0x8c, 0x19, 0xb4, 0x68, 0x3d, 0x49, 0xcc, 0x78, 0x71, 0xe6, 0x8f, 0x10, 0xc4, + 0x24, 0xe6, 0xa9, 0x7a, 0x1b, 0x6a, 0x45, 0x99, 0x90, 0x34, 0x51, 0xaf, 0x7a, 0xb7, 0xc7, 0x1a, + 0xba, 0xb5, 0x3a, 0x63, 0x75, 0x0d, 0xc7, 0xd2, 0xf0, 0x2f, 0xd5, 0x0d, 0x07, 0xd1, 0x91, 0xe5, + 0x07, 0x25, 0x5d, 0x8d, 0xc3, 0x6d, 0x88, 0xe3, 0x7f, 0x25, 0x8e, 0xbb, 0x2f, 0x17, 0x0e, 0x87, + 0x45, 0x39, 0x31, 0x66, 0x9f, 0xa9, 0x44, 0xf6, 0x0c, 0x27, 0x7f, 0xd2, 0x66, 0x61, 0xdf, 0xf4, + 0xd9, 0x54, 0x5f, 0x70, 0xb9, 0x65, 0xa2, 0x7c, 0x06, 0xff, 0xc7, 0xa6, 0xe0, 0x55, 0xb3, 0x64, + 0x5d, 0x2b, 0xaa, 0xa9, 0x24, 0xb8, 0xa8, 0xbf, 0xfc, 0x59, 0x26, 0xe1, 0x6c, 0x8c, 0x54, 0x93, + 0x1b, 0xe3, 0x56, 0xd9, 0x5c, 0x46, 0x70, 0xb5, 0x75, 0xc6, 0x9a, 0xcc, 0xda, 0xc5, 0x07, 0xbe, + 0xff, 0x0e, 0x00, 0x00, 0xff, 0xff, 0xe9, 0x3e, 0x0b, 0x3e, 0xd0, 0x02, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/api/client/service/proto/client.proto b/api/client/service/proto/client.proto index 18f833ab5..2620f8e8c 100644 --- a/api/client/service/proto/client.proto +++ b/api/client/service/proto/client.proto @@ -44,7 +44,7 @@ message StakingContractInfoRequest { // StakingContractInfoResponse is the response of GetStakingContractInfo. message StakingContractInfoResponse { // Contract address. - string conctract_address = 1; + string contract_address = 1; // The balance of the staking account. bytes balance = 2; // The nonce of the staking account. diff --git a/api/client/service/server.go b/api/client/service/server.go index 34e8e5581..cab06cec3 100644 --- a/api/client/service/server.go +++ b/api/client/service/server.go @@ -47,9 +47,9 @@ func (s *Server) GetStakingContractInfo(ctx context.Context, request *proto.Stak return nil, err } return &proto.StakingContractInfoResponse{ - ConctractAddress: s.getDeployedStakingContractAddress().Hex(), - Balance: state.GetBalance(address).Bytes(), - Nonce: state.GetNonce(address), + ContractAddress: s.getDeployedStakingContractAddress().Hex(), + Balance: state.GetBalance(address).Bytes(), + Nonce: state.GetNonce(address), }, nil } diff --git a/api/service/staking/service.go b/api/service/staking/service.go index 0b2c0496f..002e274a5 100644 --- a/api/service/staking/service.go +++ b/api/service/staking/service.go @@ -4,11 +4,10 @@ import ( "crypto/ecdsa" "math/big" - "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" client "github.com/harmony-one/harmony/api/client/service" proto "github.com/harmony-one/harmony/api/client/service/proto" "github.com/harmony-one/harmony/api/proto/message" @@ -76,17 +75,17 @@ func (s *Service) DoService(peer p2p.Peer) { // See below of how to create a staking message. } -func (s *Service) getAccountState(beaconPeer p2p.Peer) *proto.FetchAccountStateResponse { +func (s *Service) getStakingInfo(beaconPeer p2p.Peer) *proto.StakingContractInfoResponse { client := client.NewClient(beaconPeer.IP, beaconPeer.Port) defer client.Close() - return client.GetBalance(crypto.PubkeyToAddress(s.accountKey.PublicKey)) + return client.GetStakingContractInfo(crypto.PubkeyToAddress(s.accountKey.PublicKey)) } func (s *Service) createStakingMessage(beaconPeer p2p.Peer) *message.Message { - accountState := s.getAccountState(beaconPeer) - toAddress := common.HexToAddress("0x4592d8f8d7b001e72cb26a73e4fa1806a51ac79d") + stakingInfo := s.getStakingInfo(beaconPeer) + toAddress := common.HexToAddress(stakingInfo.ContractAddress) tx := types.NewTransaction( - accountState.Nonce, + stakingInfo.Nonce, toAddress, 0, // beacon chain. big.NewInt(s.stakingAmount), diff --git a/node/node.go b/node/node.go index 5a257a781..418ab3bab 100644 --- a/node/node.go +++ b/node/node.go @@ -585,6 +585,11 @@ func (node *Node) RemovePeersHandler() { } } +func (node *Node) getDeployedStakingContract() common.Address { + // TODO(ak): Please populate here + return common.Address{} +} + func (node *Node) setupForShardLeader() { // Register explorer service. node.serviceManager.RegisterService(service_manager.SupportExplorer, explorer.New(&node.SelfPeer)) @@ -593,7 +598,7 @@ func (node *Node) setupForShardLeader() { // Register new block service. node.serviceManager.RegisterService(service_manager.BlockProposal, blockproposal.New(node.Consensus.ReadySignal, node.WaitForConsensusReady)) // Register client support service. - node.serviceManager.RegisterService(service_manager.ClientSupport, clientsupport.New(node.blockchain.State, node.CallFaucetContract, node.SelfPeer.IP, node.SelfPeer.Port)) + node.serviceManager.RegisterService(service_manager.ClientSupport, clientsupport.New(node.blockchain.State, node.CallFaucetContract, node.getDeployedStakingContract, node.SelfPeer.IP, node.SelfPeer.Port)) } func (node *Node) setupForShardValidator() { @@ -619,7 +624,7 @@ func (node *Node) setupForBeaconLeader() { // Register new block service. node.serviceManager.RegisterService(service_manager.BlockProposal, blockproposal.New(node.Consensus.ReadySignal, node.WaitForConsensusReady)) // Register client support service. - node.serviceManager.RegisterService(service_manager.ClientSupport, clientsupport.New(node.blockchain.State, node.CallFaucetContract, node.SelfPeer.IP, node.SelfPeer.Port)) + node.serviceManager.RegisterService(service_manager.ClientSupport, clientsupport.New(node.blockchain.State, node.CallFaucetContract, node.getDeployedStakingContract, node.SelfPeer.IP, node.SelfPeer.Port)) } func (node *Node) setupForBeaconValidator() { From e3442b78b66f64c903976976f34085c7909e2fb7 Mon Sep 17 00:00:00 2001 From: ak Date: Tue, 12 Feb 2019 14:11:22 -0800 Subject: [PATCH 17/18] more typos --- node/node.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/node.go b/node/node.go index ea21e326a..33d6a69e7 100644 --- a/node/node.go +++ b/node/node.go @@ -300,7 +300,7 @@ func New(host p2p.Host, consensus *bft.Consensus, db ethdb.Database) *Node { return &node } -func (node *Node) getDeployedStakingContractAddress() common.Address { +func (node *Node) getDeployedStakingContract() common.Address { return node.StakingContractAddress } From ef7c2e147ca0106f55970976e73c2fc62d993a16 Mon Sep 17 00:00:00 2001 From: chaosma Date: Tue, 12 Feb 2019 14:23:22 -0800 Subject: [PATCH 18/18] add resharding code (#404) * add resharding for epoch switch: 1) add randseed and shardStateHash in block header; 2) Read and Write shardState from/to blockchain; 3) perform resharding after every N (epoch length) blocks --- consensus/consensus_validator_test.go | 12 +- core/blockchain.go | 140 +++++++++++++++++---- core/error.go | 3 + core/rawdb/accessors_chain.go | 25 ++++ core/rawdb/schema.go | 6 + core/resharding.go | 174 ++++++++++++++++++++++++++ core/resharding.md | 12 ++ core/resharding_test.go | 18 +++ core/types/block.go | 14 ++- core/types/shard_state.go | 68 ++++++++++ core/types/shard_state_test.go | 33 +++++ node/node_handler.go | 8 ++ node/node_newblock.go | 33 +++++ 13 files changed, 518 insertions(+), 28 deletions(-) create mode 100644 core/resharding.go create mode 100644 core/resharding.md create mode 100644 core/resharding_test.go create mode 100644 core/types/shard_state.go create mode 100644 core/types/shard_state_test.go diff --git a/consensus/consensus_validator_test.go b/consensus/consensus_validator_test.go index 2e37bd78b..a98b551ce 100644 --- a/consensus/consensus_validator_test.go +++ b/consensus/consensus_validator_test.go @@ -44,9 +44,9 @@ func TestProcessMessageValidatorAnnounce(test *testing.T) { test.Fatalf("newhost failure: %v", err) } consensusLeader := New(host, "0", []p2p.Peer{validator1, validator2, validator3}, leader) - blockBytes, err := hex.DecodeString("f90242f9023da00000000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000a02b418211410ee3e75b32abd925bbeba215172afa509d65c1953d4b4e505a4a2aa056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000808502540be400808080a000000000000000000000000000000000000000000000000000000000000000008800000000000000008400000001b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080c0c0") + blockBytes, err := hex.DecodeString("f90264f9025fa00000000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000a02b418211410ee3e75b32abd925bbeba215172afa509d65c1953d4b4e505a4a2aa056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000808502540be400808080a000000000000000000000000000000000000000000000000000000000000000008800000000000000008400000001b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008080a00000000000000000000000000000000000000000000000000000000000000000c0c0") consensusLeader.block = blockBytes - hashBytes, err := hex.DecodeString("a0b3344bd84d41e59b8d84857196080dc8bf91df2787ed5e3e7d65bf8a8cea050b") + hashBytes, err := hex.DecodeString("26d7cdbbaf6cedcaf946ad1e8c0bc2567e17418ce63026db4160a7cc32d9e488") copy(consensusLeader.blockHash[:], hashBytes[:]) @@ -98,9 +98,9 @@ func TestProcessMessageValidatorPrepared(test *testing.T) { test.Fatalf("newhost failure: %v", err) } consensusLeader := New(host, "0", []p2p.Peer{validator1, validator2, validator3}, leader) - blockBytes, err := hex.DecodeString("f90242f9023da00000000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000a02b418211410ee3e75b32abd925bbeba215172afa509d65c1953d4b4e505a4a2aa056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000808502540be400808080a000000000000000000000000000000000000000000000000000000000000000008800000000000000008400000001b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080c0c0") + blockBytes, err := hex.DecodeString("f90264f9025fa00000000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000a02b418211410ee3e75b32abd925bbeba215172afa509d65c1953d4b4e505a4a2aa056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000808502540be400808080a000000000000000000000000000000000000000000000000000000000000000008800000000000000008400000001b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008080a00000000000000000000000000000000000000000000000000000000000000000c0c0") consensusLeader.block = blockBytes - hashBytes, err := hex.DecodeString("a0b3344bd84d41e59b8d84857196080dc8bf91df2787ed5e3e7d65bf8a8cea050b") + hashBytes, err := hex.DecodeString("26d7cdbbaf6cedcaf946ad1e8c0bc2567e17418ce63026db4160a7cc32d9e488") copy(consensusLeader.blockHash[:], hashBytes[:]) @@ -158,9 +158,9 @@ func TestProcessMessageValidatorCommitted(test *testing.T) { test.Fatalf("newhost failure: %v", err) } consensusLeader := New(host, "0", []p2p.Peer{validator1, validator2, validator3}, leader) - blockBytes, err := hex.DecodeString("f90242f9023da00000000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000a02b418211410ee3e75b32abd925bbeba215172afa509d65c1953d4b4e505a4a2aa056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000808502540be400808080a000000000000000000000000000000000000000000000000000000000000000008800000000000000008400000001b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080c0c0") + blockBytes, err := hex.DecodeString("f90264f9025fa00000000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000a02b418211410ee3e75b32abd925bbeba215172afa509d65c1953d4b4e505a4a2aa056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000808502540be400808080a000000000000000000000000000000000000000000000000000000000000000008800000000000000008400000001b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008080a00000000000000000000000000000000000000000000000000000000000000000c0c0") consensusLeader.block = blockBytes - hashBytes, err := hex.DecodeString("a0b3344bd84d41e59b8d84857196080dc8bf91df2787ed5e3e7d65bf8a8cea050b") + hashBytes, err := hex.DecodeString("26d7cdbbaf6cedcaf946ad1e8c0bc2567e17418ce63026db4160a7cc32d9e488") copy(consensusLeader.blockHash[:], hashBytes[:]) diff --git a/core/blockchain.go b/core/blockchain.go index f2c615d0f..90decdb11 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -43,6 +43,7 @@ import ( "github.com/harmony-one/harmony/core/state" "github.com/harmony-one/harmony/core/types" "github.com/harmony-one/harmony/core/vm" + "github.com/harmony-one/harmony/internal/utils" lru "github.com/hashicorp/golang-lru" ) @@ -62,6 +63,12 @@ const ( maxTimeFutureBlocks = 30 badBlockLimit = 10 triesInMemory = 128 + shardCacheLimit = 2 + + // BlocksPerEpoch is the number of blocks in one epoch + // currently set to small number for testing + // in future, this need to be adjusted dynamically instead of constant + BlocksPerEpoch = 5 // BlockChainVersion ensures that an incompatible database forces a resync from scratch. BlockChainVersion = 3 @@ -114,12 +121,13 @@ type BlockChain struct { currentBlock atomic.Value // Current head of the block chain currentFastBlock atomic.Value // Current head of the fast-sync chain (may be above the block chain!) - stateCache state.Database // State database to reuse between imports (contains state cache) - bodyCache *lru.Cache // Cache for the most recent block bodies - bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format - receiptsCache *lru.Cache // Cache for the most recent receipts per block - blockCache *lru.Cache // Cache for the most recent entire blocks - futureBlocks *lru.Cache // future blocks are blocks added for later processing + stateCache state.Database // State database to reuse between imports (contains state cache) + bodyCache *lru.Cache // Cache for the most recent block bodies + bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format + receiptsCache *lru.Cache // Cache for the most recent receipts per block + blockCache *lru.Cache // Cache for the most recent entire blocks + futureBlocks *lru.Cache // future blocks are blocks added for later processing + shardStateCache *lru.Cache quit chan struct{} // blockchain quit channel running int32 // running must be called atomically @@ -152,23 +160,25 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par blockCache, _ := lru.New(blockCacheLimit) futureBlocks, _ := lru.New(maxFutureBlocks) badBlocks, _ := lru.New(badBlockLimit) + shardCache, _ := lru.New(shardCacheLimit) bc := &BlockChain{ - chainConfig: chainConfig, - cacheConfig: cacheConfig, - db: db, - triegc: prque.New(nil), - stateCache: state.NewDatabase(db), - quit: make(chan struct{}), - shouldPreserve: shouldPreserve, - bodyCache: bodyCache, - bodyRLPCache: bodyRLPCache, - receiptsCache: receiptsCache, - blockCache: blockCache, - futureBlocks: futureBlocks, - engine: engine, - vmConfig: vmConfig, - badBlocks: badBlocks, + chainConfig: chainConfig, + cacheConfig: cacheConfig, + db: db, + triegc: prque.New(nil), + stateCache: state.NewDatabase(db), + quit: make(chan struct{}), + shouldPreserve: shouldPreserve, + bodyCache: bodyCache, + bodyRLPCache: bodyRLPCache, + receiptsCache: receiptsCache, + blockCache: blockCache, + futureBlocks: futureBlocks, + shardStateCache: shardCache, + engine: engine, + vmConfig: vmConfig, + badBlocks: badBlocks, } bc.SetValidator(NewBlockValidator(chainConfig, bc, engine)) bc.SetProcessor(NewStateProcessor(chainConfig, bc, engine)) @@ -299,6 +309,7 @@ func (bc *BlockChain) SetHead(head uint64) error { bc.receiptsCache.Purge() bc.blockCache.Purge() bc.futureBlocks.Purge() + bc.shardStateCache.Purge() // Rewind the block chain, ensuring we don't end up with a stateless head block if currentBlock := bc.CurrentBlock(); currentBlock != nil && currentHeader.Number.Uint64() < currentBlock.NumberU64() { @@ -1626,3 +1637,90 @@ func (bc *BlockChain) SubscribeChainSideEvent(ch chan<- ChainSideEvent) event.Su func (bc *BlockChain) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription { return bc.scope.Track(bc.logsFeed.Subscribe(ch)) } + +// GetShardState retrives sharding state given block hash and block number +func (bc *BlockChain) GetShardState(hash common.Hash, number uint64) types.ShardState { + if cached, ok := bc.shardStateCache.Get(hash); ok { + shardState := cached.(types.ShardState) + return shardState + } + shardState := rawdb.ReadShardState(bc.db, hash, number) + if shardState == nil { + return nil + } + bc.shardStateCache.Add(hash, shardState) + return shardState +} + +// GetShardStateByNumber retrieves sharding state given the block number +func (bc *BlockChain) GetShardStateByNumber(number uint64) types.ShardState { + hash := rawdb.ReadCanonicalHash(bc.db, number) + if hash == (common.Hash{}) { + return nil + } + return bc.GetShardState(hash, number) +} + +// GetShardStateByHash retrieves the shard state given the blockhash, return nil if not exist +func (bc *BlockChain) GetShardStateByHash(hash common.Hash) types.ShardState { + number := bc.hc.GetBlockNumber(hash) + if number == nil { + return nil + } + return bc.GetShardState(hash, *number) +} + +// GetRandSeedByNumber retrieves the rand seed given the block number, return 0 if not exist +func (bc *BlockChain) GetRandSeedByNumber(number uint64) int64 { + header := bc.GetHeaderByNumber(number) + if header == nil { + return 0 + } + return int64(header.RandSeed) +} + +// GetNewShardState will calculate (if not exist) and get the new shard state for epoch block or nil if block is not epoch block +// epoch block is where the new shard state stored +func (bc *BlockChain) GetNewShardState(block *types.Block) types.ShardState { + hash := block.Hash() + number := block.NumberU64() + // just ignore non-epoch block + if !CheckEpochBlock(number) { + return nil + } + shardState := bc.GetShardState(hash, number) + if shardState == nil { + epoch := GetEpochFromBlockNumber(number) + shardState = CalculateNewShardState(bc, epoch) + bc.shardStateCache.Add(hash, shardState) + } + return shardState +} + +// ValidateNewShardState validate whether the new shard state root matches +func (bc *BlockChain) ValidateNewShardState(block *types.Block) error { + shardState := bc.GetNewShardState(block) + if shardState == nil { + return nil + } + if shardState.Hash() != block.Header().ShardStateHash { + return ErrShardStateNotMatch + } + utils.GetLogInstance().Debug("[resharding] validate new shard state success", "shardStateHash", shardState.Hash()) + return nil +} + +// InsertNewShardState insert new shard state into epoch block +func (bc *BlockChain) InsertNewShardState(block *types.Block) { + shardState := bc.GetNewShardState(block) + if shardState == nil { + return + } + hash := block.Hash() + number := block.NumberU64() + rawdb.WriteShardState(bc.db, hash, number, shardState) + utils.GetLogInstance().Debug("[resharding] save new shard state success", "shardStateHash", shardState.Hash()) + for _, c := range shardState { + utils.GetLogInstance().Debug("[resharding] new shard information", "shardID", c.ShardID, "NodeList", c.NodeList) + } +} diff --git a/core/error.go b/core/error.go index 410eca1e1..1faf2bb0a 100644 --- a/core/error.go +++ b/core/error.go @@ -32,4 +32,7 @@ var ( // ErrNonceTooHigh is returned if the nonce of a transaction is higher than the // next one expected based on the local chain. ErrNonceTooHigh = errors.New("nonce too high") + + // ErrShardStateNotMatch is returned if the calculated shardState hash not equal that in the block header + ErrShardStateNotMatch = errors.New("shard state root hash not match") ) diff --git a/core/rawdb/accessors_chain.go b/core/rawdb/accessors_chain.go index 0c61fecdb..61df9bf6b 100644 --- a/core/rawdb/accessors_chain.go +++ b/core/rawdb/accessors_chain.go @@ -373,3 +373,28 @@ func FindCommonAncestor(db DatabaseReader, a, b *types.Header) *types.Header { } return a } + +// ReadShardState retrieves sharding state +func ReadShardState(db DatabaseReader, hash common.Hash, number uint64) types.ShardState { + data, _ := db.Get(shardStateKey(number, hash)) + if len(data) == 0 { + return nil + } + shardState := types.ShardState{} + if err := rlp.DecodeBytes(data, &shardState); err != nil { + log.Error("Fail to decode sharding state", "hash", hash, "number", number, "err", err) + return nil + } + return shardState +} + +// WriteShardState stores sharding state into database +func WriteShardState(db DatabaseWriter, hash common.Hash, number uint64, shardState types.ShardState) { + data, err := rlp.EncodeToBytes(shardState) + if err != nil { + log.Crit("Failed to encode sharding state", "err", err) + } + if err := db.Put(shardStateKey(number, hash), data); err != nil { + log.Crit("Failed to store sharding state", "err", err) + } +} diff --git a/core/rawdb/schema.go b/core/rawdb/schema.go index ef597ef30..c7fa807dd 100644 --- a/core/rawdb/schema.go +++ b/core/rawdb/schema.go @@ -53,6 +53,8 @@ var ( txLookupPrefix = []byte("l") // txLookupPrefix + hash -> transaction/receipt lookup metadata bloomBitsPrefix = []byte("B") // bloomBitsPrefix + bit (uint16 big endian) + section (uint64 big endian) + hash -> bloom bits + shardStatePrefix = []byte("ss") // shardStatePrefix + num (uint64 big endian) + hash -> shardState + preimagePrefix = []byte("secure-key-") // preimagePrefix + hash -> preimage configPrefix = []byte("ethereum-config-") // config prefix for the db @@ -132,3 +134,7 @@ func preimageKey(hash common.Hash) []byte { func configKey(hash common.Hash) []byte { return append(configPrefix, hash.Bytes()...) } + +func shardStateKey(number uint64, hash common.Hash) []byte { + return append(append(shardStatePrefix, encodeBlockNumber(number)...), hash.Bytes()...) +} diff --git a/core/resharding.go b/core/resharding.go new file mode 100644 index 000000000..ab928cd53 --- /dev/null +++ b/core/resharding.go @@ -0,0 +1,174 @@ +package core + +import ( + "math/rand" + "sort" + "strconv" + + "github.com/harmony-one/harmony/core/types" +) + +const ( + // InitialSeed is the initial random seed, a magic number to answer everything, remove later + InitialSeed int64 = 42 +) + +// ShardingState is data structure hold the sharding state +type ShardingState struct { + epoch uint64 // current epoch + rnd int64 // random seed for resharding + numShards int + shardState types.ShardState +} + +// sortedCommitteeBySize will sort shards by size +// Suppose there are N shards, the first N/2 larger shards are called active committees +// the rest N/2 smaller committees are called inactive committees +// actually they are all just normal shards +// TODO: sort the committee weighted by total staking instead of shard size +func (ss *ShardingState) sortCommitteeBySize() { + sort.Slice(ss.shardState, func(i, j int) bool { + return len(ss.shardState[i].NodeList) > len(ss.shardState[j].NodeList) + }) +} + +// assignNewNodes add new nodes into the N/2 active committees evenly +func (ss *ShardingState) assignNewNodes(newNodeList []types.NodeID) { + ss.sortCommitteeBySize() + numActiveShards := ss.numShards / 2 + Shuffle(newNodeList) + for i, nid := range newNodeList { + id := i % numActiveShards + ss.shardState[id].NodeList = append(ss.shardState[id].NodeList, nid) + } +} + +// cuckooResharding uses cuckoo rule to reshard X% of active committee(shards) into inactive committee(shards) +func (ss *ShardingState) cuckooResharding(percent float64) { + ss.sortCommitteeBySize() + numActiveShards := ss.numShards / 2 + kickedNodes := []types.NodeID{} + for i := range ss.shardState { + if i >= numActiveShards { + break + } + Shuffle(ss.shardState[i].NodeList) + numKicked := int(percent * float64(len(ss.shardState[i].NodeList))) + tmp := ss.shardState[i].NodeList[:numKicked] + kickedNodes = append(kickedNodes, tmp...) + ss.shardState[i].NodeList = ss.shardState[i].NodeList[numKicked:] + } + + Shuffle(kickedNodes) + for i, nid := range kickedNodes { + id := numActiveShards + i%(ss.numShards-numActiveShards) + ss.shardState[id].NodeList = append(ss.shardState[id].NodeList, nid) + } +} + +// UpdateShardState will first add new nodes into shards, then use cuckoo rule to reshard to get new shard state +func (ss *ShardingState) UpdateShardState(newNodeList []types.NodeID, percent float64) { + rand.Seed(ss.rnd) + ss.assignNewNodes(newNodeList) + ss.cuckooResharding(percent) +} + +// Shuffle will shuffle the list with result uniquely determined by seed, assuming there is no repeat items in the list +func Shuffle(list []types.NodeID) { + sort.Slice(list, func(i, j int) bool { + return types.CompareNodeID(list[i], list[j]) == -1 + }) + rand.Shuffle(len(list), func(i, j int) { + list[i], list[j] = list[j], list[i] + }) +} + +// GetBlockNumberFromEpoch calculates the block number where epoch sharding information is stored +func GetBlockNumberFromEpoch(epoch uint64) uint64 { + number := epoch * uint64(BlocksPerEpoch) // currently we use the first block in each epoch + return number +} + +// GetEpochFromBlockNumber calculates the epoch number the block belongs to +func GetEpochFromBlockNumber(blockNumber uint64) uint64 { + return blockNumber / uint64(BlocksPerEpoch) +} + +// CheckEpochBlock check whethere a given block number is the one to store epoch information +func CheckEpochBlock(blockNumber uint64) bool { + return blockNumber%uint64(BlocksPerEpoch) == 0 +} + +// GetPreviousEpochBlockNumber gets the epoch block number of previous epoch +func GetPreviousEpochBlockNumber(blockNumber uint64) uint64 { + epoch := GetEpochFromBlockNumber(blockNumber) + if epoch == 1 { + // no previous epoch + return epoch + } + return GetBlockNumberFromEpoch(epoch - 1) +} + +// GetShardingStateFromBlockChain will retrieve random seed and shard map from beacon chain for given a epoch +func GetShardingStateFromBlockChain(bc *BlockChain, epoch uint64) *ShardingState { + number := GetBlockNumberFromEpoch(epoch) + shardState := bc.GetShardStateByNumber(number) + rnd := bc.GetRandSeedByNumber(number) + + return &ShardingState{epoch: epoch, rnd: rnd, shardState: shardState, numShards: len(shardState)} +} + +// CalculateNewShardState get sharding state from previous epoch and calcualte sharding state for new epoch +// TODO: currently, we just mock everything +func CalculateNewShardState(bc *BlockChain, epoch uint64) types.ShardState { + if epoch == 1 { + return fakeGetInitShardState() + } + ss := GetShardingStateFromBlockChain(bc, epoch-1) + newNodeList := fakeNewNodeList(ss.rnd) + percent := ss.calculateKickoutRate(newNodeList) + ss.UpdateShardState(newNodeList, percent) + return ss.shardState +} + +// calculateKickoutRate calculates the cuckoo rule kick out rate in order to make committee balanced +func (ss *ShardingState) calculateKickoutRate(newNodeList []types.NodeID) float64 { + numActiveCommittees := ss.numShards / 2 + newNodesPerShard := len(newNodeList) / numActiveCommittees + ss.sortCommitteeBySize() + return float64(newNodesPerShard) / float64(len(ss.shardState[numActiveCommittees].NodeList)) +} + +// FakeGenRandSeed generate random seed based on previous rnd seed; remove later after VRF implemented +func FakeGenRandSeed(seed int64) int64 { + rand.Seed(seed) + return rand.Int63() +} + +// remove later after bootstrap codes ready +func fakeGetInitShardState() types.ShardState { + rand.Seed(InitialSeed) + shardState := types.ShardState{} + for i := 0; i < 6; i++ { + sid := uint32(i) + com := types.Committee{ShardID: sid} + for j := 0; j < 10; j++ { + nid := strconv.Itoa(int(rand.Int63())) + com.NodeList = append(com.NodeList, types.NodeID(nid)) + } + shardState = append(shardState, com) + } + return shardState +} + +// remove later after new nodes list generation ready +func fakeNewNodeList(seed int64) []types.NodeID { + rand.Seed(seed) + numNewNodes := rand.Intn(10) + nodeList := []types.NodeID{} + for i := 0; i < numNewNodes; i++ { + nid := strconv.Itoa(int(rand.Int63())) + nodeList = append(nodeList, types.NodeID(nid)) + } + return nodeList +} diff --git a/core/resharding.md b/core/resharding.md new file mode 100644 index 000000000..54dadb24d --- /dev/null +++ b/core/resharding.md @@ -0,0 +1,12 @@ +## Resharding + +In current design, the epoch is defined to be fixed length, the epoch length is a constant parameter BlocksPerEpoch. In future, it will be dynamically adjustable according to security parameter. During the epoch transition, suppose there are N shards, we sort the shards according to the size of active nodes (that had staking for next epoch). The first N/2 larger shards will be called active committees, and the last N/2 smaller shards will be called inactive committees. Don't be confused by +the name, they are all normal shards with same function. + +All the information about sharding will be stored in BeaconChain. A sharding state is defined as a map which maps each NodeID to the ShardID the node belongs to. Every node will have a unique NodeID and be mapped to one ShardID. At the beginning of a new epoch, the BeaconChain leader will propose a new block containing the new sharding state, the new sharding state is uniquely determined by the randomness generated by distributed randomness protocol. During the consensus process, all the validators will perform the same calculation and verify the proposed sharding state is valid. After consensus is reached, each node will write the new sharding state into the block. This block is called epoch block. In current code, it's the first block of each epoch in BeaconChain. + +The main function of resharding is CalculcateNewShardState. It will take 3 inputs: newNodeList, oldShardState, randomSeed and output newShardState. +The newNodeList will be retrieved from BeaconChain staking transaction during the previous epoch. The randomSeed and oldShardState is stored in previous epoch block. It should be noticed that the randomSeed generation currently is mocked. After the distributed randomness protocol(drand) is ready, the drand service will generate the random seed for resharding. + +The resharding process is as follows: we first get newNodeList from staking transactions from previous epoch and assign the new nodes evenly into the N/2 active committees. Then, we kick out X% of nodes from each active committees and put these kicked out nodes into inactive committees evenly. The percentage X roughly equals to the percentage of new nodes into active committee in order to balance the committee size. + diff --git a/core/resharding_test.go b/core/resharding_test.go new file mode 100644 index 000000000..a15e03c9c --- /dev/null +++ b/core/resharding_test.go @@ -0,0 +1,18 @@ +package core + +import ( + "fmt" + "testing" +) + +func TestFakeGetInitShardState(t *testing.T) { + ss := fakeGetInitShardState() + for i := range ss { + fmt.Printf("ShardID: %v, NodeList: %v\n", ss[i].ShardID, ss[i].NodeList) + } +} + +func TestFakeNewNodeList(t *testing.T) { + nodeList := fakeNewNodeList(42) + fmt.Println("newNodeList: ", nodeList) +} diff --git a/core/types/block.go b/core/types/block.go index 8065c9c60..82abfc449 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -97,7 +97,9 @@ type Header struct { PrepareBitmap []byte `json:"bitmap" gencodec:"required"` // Contains which validator signed CommitSignature [48]byte `json:"signature" gencodec:"required"` CommitBitmap []byte `json:"bitmap" gencodec:"required"` // Contains which validator signed - // TODO(RJ): add epoch info + + RandSeed uint64 `json:"randomSeed"` + ShardStateHash common.Hash `json:"shardStateRoot"` } // field type overrides for gencodec @@ -448,3 +450,13 @@ func (s blockSorter) Less(i, j int) bool { func Number(b1, b2 *Block) bool { return b1.header.Number.Cmp(b2.header.Number) < 0 } + +// AddRandSeed add random seed into block header +func (b *Block) AddRandSeed(randSeed int64) { + b.header.RandSeed = uint64(randSeed) +} + +// AddShardStateHash add shardStateHash into block header +func (b *Block) AddShardStateHash(shardStateHash common.Hash) { + b.header.ShardStateHash = shardStateHash +} diff --git a/core/types/shard_state.go b/core/types/shard_state.go new file mode 100644 index 000000000..879d62629 --- /dev/null +++ b/core/types/shard_state.go @@ -0,0 +1,68 @@ +package types + +import ( + "sort" + + "github.com/ethereum/go-ethereum/common" + "golang.org/x/crypto/sha3" +) + +// NodeID is a unique ID represent a node +type NodeID string + +// ShardState is the collection of all committees +type ShardState []Committee + +// Committee contains the active nodes in one shard +type Committee struct { + ShardID uint32 + NodeList []NodeID // a list of NodeID where NodeID is represented by a string +} + +// GetHashFromNodeList will sort the list, then use Keccak256 to hash the list +// notice that the input nodeList will be modified (sorted) +func GetHashFromNodeList(nodeList []NodeID) []byte { + // in general, nodeList should not be empty + if nodeList == nil || len(nodeList) == 0 { + return []byte{} + } + + sort.Slice(nodeList, func(i, j int) bool { + return CompareNodeID(nodeList[i], nodeList[j]) == -1 + }) + d := sha3.NewLegacyKeccak256() + for i := range nodeList { + d.Write(nodeList[i].Serialize()) + } + return d.Sum(nil) +} + +// Hash is the root hash of ShardState +func (ss ShardState) Hash() (h common.Hash) { + sort.Slice(ss, func(i, j int) bool { + return ss[i].ShardID < ss[j].ShardID + }) + d := sha3.NewLegacyKeccak256() + for i := range ss { + hash := GetHashFromNodeList(ss[i].NodeList) + d.Write(hash) + } + d.Sum(h[:0]) + return h +} + +// CompareNodeID compares two nodes by their ID; used to sort node list +func CompareNodeID(n1 NodeID, n2 NodeID) int { + if n1 < n2 { + return -1 + } + if n1 > n2 { + return 1 + } + return 0 +} + +// Serialize serialize NodeID into bytes +func (n NodeID) Serialize() []byte { + return []byte(n) +} diff --git a/core/types/shard_state_test.go b/core/types/shard_state_test.go new file mode 100644 index 000000000..fa6a902b4 --- /dev/null +++ b/core/types/shard_state_test.go @@ -0,0 +1,33 @@ +package types + +import ( + "bytes" + "testing" +) + +func TestGetHashFromNodeList(t *testing.T) { + l1 := []NodeID{"node1", "node2", "node3"} + l2 := []NodeID{"node2", "node1", "node3"} + h1 := GetHashFromNodeList(l1) + h2 := GetHashFromNodeList(l2) + + if bytes.Compare(h1, h2) != 0 { + t.Error("node list l1 and l2 should have equal hash") + } +} + +func TestHash(t *testing.T) { + com1 := Committee{ShardID: 22, NodeList: []NodeID{"node11", "node22", "node1"}} + com2 := Committee{ShardID: 2, NodeList: []NodeID{"node4", "node5", "node6"}} + shardState1 := ShardState{com1, com2} + h1 := shardState1.Hash() + + com3 := Committee{ShardID: 2, NodeList: []NodeID{"node6", "node5", "node4"}} + com4 := Committee{ShardID: 22, NodeList: []NodeID{"node1", "node11", "node22"}} + shardState2 := ShardState{com3, com4} + h2 := shardState2.Hash() + + if bytes.Compare(h1[:], h2[:]) != 0 { + t.Error("shardState1 and shardState2 should have equal hash") + } +} diff --git a/node/node_handler.go b/node/node_handler.go index 1137de8e2..cfef4b76d 100644 --- a/node/node_handler.go +++ b/node/node_handler.go @@ -229,6 +229,11 @@ func (node *Node) VerifyNewBlock(newBlock *types.Block) bool { utils.GetLogInstance().Debug("Failed verifying new block", "Error", err, "tx", newBlock.Transactions()[0]) return false } + + err = node.blockchain.ValidateNewShardState(newBlock) + if err != nil { + utils.GetLogInstance().Debug("Failed to verify new sharding state", "err", err) + } return true } @@ -246,11 +251,14 @@ func (node *Node) PostConsensusProcessing(newBlock *types.Block) { // AddNewBlock is usedd to add new block into the blockchain. func (node *Node) AddNewBlock(newBlock *types.Block) { blockNum, err := node.blockchain.InsertChain([]*types.Block{newBlock}) + if err != nil { utils.GetLogInstance().Debug("Error adding new block to blockchain", "blockNum", blockNum, "Error", err) } else { utils.GetLogInstance().Info("adding new block to blockchain", "blockNum", blockNum) } + // only insert new shardstate when newBlock is epoch block + node.blockchain.InsertNewShardState(newBlock) } func (node *Node) pingMessageHandler(msgPayload []byte) int { diff --git a/node/node_newblock.go b/node/node_newblock.go index 7eaa29fc4..0785d85d9 100644 --- a/node/node_newblock.go +++ b/node/node_newblock.go @@ -3,6 +3,7 @@ package node import ( "time" + "github.com/harmony-one/harmony/core" "github.com/harmony-one/harmony/core/types" "github.com/harmony-one/harmony/internal/utils" ) @@ -49,6 +50,8 @@ func (node *Node) WaitForConsensusReady(readySignal chan struct{}, stopChan chan if err != nil { utils.GetLogInstance().Debug("Failed commiting new block", "Error", err) } else { + // add new shard state if it's epoch block + node.addNewShardState(block) newBlock = block break } @@ -65,3 +68,33 @@ func (node *Node) WaitForConsensusReady(readySignal chan struct{}, stopChan chan } }() } + +func (node *Node) addNewShardState(block *types.Block) { + shardState := node.blockchain.GetNewShardState(block) + if shardState != nil { + shardHash := shardState.Hash() + utils.GetLogInstance().Debug("[resharding] adding new shard state", "shardHash", shardHash) + for _, c := range shardState { + utils.GetLogInstance().Debug("new shard information", "shardID", c.ShardID, "NodeList", c.NodeList) + } + block.AddShardStateHash(shardHash) + } +} + +func (node *Node) addNewRandSeed(block *types.Block) { + blockNumber := block.NumberU64() + if !core.CheckEpochBlock(blockNumber) { + return + } + + var rnd int64 + epoch := core.GetEpochFromBlockNumber(blockNumber) + if epoch == 1 { + rnd = core.InitialSeed + } else { + number := core.GetPreviousEpochBlockNumber(blockNumber) + oldrnd := node.blockchain.GetRandSeedByNumber(number) + rnd = core.FakeGenRandSeed(oldrnd) + } + block.AddRandSeed(rnd) +}