diff --git a/api/client/service/server_test.go b/api/client/service/server_test.go index c654aef2d..f679a3db1 100644 --- a/api/client/service/server_test.go +++ b/api/client/service/server_test.go @@ -6,6 +6,8 @@ import ( "strings" "testing" + "github.com/harmony-one/harmony/internal/chain" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" client "github.com/harmony-one/harmony/api/client/service/proto" @@ -15,7 +17,6 @@ import ( "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/params" - "github.com/harmony-one/harmony/consensus" "github.com/harmony-one/harmony/core" "github.com/harmony-one/harmony/core/vm" ) @@ -62,7 +63,7 @@ func TestFetchAccountState(test *testing.T) { genesis := gspec.MustCommit(database) _ = genesis - chain, _ := core.NewBlockChain(database, nil, gspec.Config, consensus.NewFaker(), vm.Config{}, nil) + chain, _ := core.NewBlockChain(database, nil, gspec.Config, chain.Engine, vm.Config{}, nil) hash := common.Hash{} hash.SetBytes([]byte("hello")) @@ -99,7 +100,7 @@ func TestGetStakingContractInfo(test *testing.T) { genesis := gspec.MustCommit(database) _ = genesis - chain, _ := core.NewBlockChain(database, nil, gspec.Config, consensus.NewFaker(), vm.Config{}, nil) + chain, _ := core.NewBlockChain(database, nil, gspec.Config, chain.Engine, vm.Config{}, nil) hash := common.Hash{} hash.SetBytes([]byte("hello")) diff --git a/api/service/explorer/storage_test.go b/api/service/explorer/storage_test.go index 96daf3717..5dec79c56 100644 --- a/api/service/explorer/storage_test.go +++ b/api/service/explorer/storage_test.go @@ -48,7 +48,7 @@ func TestDump(t *testing.T) { tx3 := types.NewTransaction(3, common.BytesToAddress([]byte{0x33}), 0, big.NewInt(333), 3333, big.NewInt(33333), []byte{0x33, 0x33, 0x33}) txs := []*types.Transaction{tx1, tx2, tx3} - block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, txs, nil, nil) + block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, txs, nil, nil, nil) ins := GetStorageInstance("1.1.1.1", "3333", true) ins.Dump(block, uint64(1)) db := ins.GetDB() @@ -75,7 +75,7 @@ func TestUpdateAddressStorage(t *testing.T) { tx3 := types.NewTransaction(3, common.BytesToAddress([]byte{0x33}), 0, big.NewInt(333), 3333, big.NewInt(33333), []byte{0x33, 0x33, 0x33}) txs := []*types.Transaction{tx1, tx2, tx3} - block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, txs, nil, nil) + block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, txs, nil, nil, nil) ins := GetStorageInstance("1.1.1.1", "3333", true) ins.Dump(block, uint64(1)) db := ins.GetDB() diff --git a/api/service/explorer/structs_test.go b/api/service/explorer/structs_test.go index 77d6292b8..120f4d51c 100644 --- a/api/service/explorer/structs_test.go +++ b/api/service/explorer/structs_test.go @@ -18,7 +18,7 @@ func TestGetTransaction(t *testing.T) { tx3 := types.NewTransaction(3, common.BytesToAddress([]byte{0x33}), 0, big.NewInt(333), 3333, big.NewInt(33333), []byte{0x33, 0x33, 0x33}) txs := []*types.Transaction{tx1, tx2, tx3} - block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, txs, nil, nil) + block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, txs, nil, nil, nil) tx := GetTransaction(tx1, block) assert.Equal(t, tx.ID, tx1.Hash().Hex(), "should be equal tx1.Hash()") diff --git a/api/service/syncing/syncing.go b/api/service/syncing/syncing.go index 0f87c8a6a..78e177f3e 100644 --- a/api/service/syncing/syncing.go +++ b/api/service/syncing/syncing.go @@ -532,6 +532,16 @@ func (ss *StateSync) getBlockFromLastMileBlocksByParentHash(parentHash common.Ha func (ss *StateSync) updateBlockAndStatus(block *types.Block, bc *core.BlockChain, worker *worker.Worker) bool { utils.Logger().Info().Str("blockHex", bc.CurrentBlock().Hash().Hex()).Msg("[SYNC] Current Block") + + // Verify block signatures + if block.NumberU64() > 1 { + err := core.VerifyBlockLastCommitSigs(bc, block) + if err != nil { + utils.Logger().Error().Err(err).Msgf("[SYNC] failed verifying signatures for new block %d", block.NumberU64()) + return false + } + } + _, err := bc.InsertChain([]*types.Block{block}) if err != nil { utils.Logger().Error().Err(err).Msg("[SYNC] Error adding new block to blockchain") @@ -726,7 +736,7 @@ func (ss *StateSync) SyncLoop(bc *core.BlockChain, worker *worker.Worker, willJo otherHeight := ss.getMaxPeerHeight() currentHeight := bc.CurrentBlock().NumberU64() if currentHeight >= otherHeight { - utils.Logger().Info().Msg("[SYNC] Node is now IN SYNC!") + utils.Logger().Info().Msgf("[SYNC] Node is now IN SYNC! (ShardID: %d)", bc.ShardID()) break } startHash := bc.CurrentBlock().Hash() diff --git a/core/block_validator.go b/core/block_validator.go index 20cbf2f63..a61eccbf3 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -17,8 +17,13 @@ package core import ( + "encoding/binary" "fmt" + "github.com/harmony-one/bls/ffi/go/bls" + bls2 "github.com/harmony-one/harmony/crypto/bls" + "github.com/harmony-one/harmony/internal/ctxerror" + "github.com/ethereum/go-ethereum/params" consensus_engine "github.com/harmony-one/harmony/consensus/engine" "github.com/harmony-one/harmony/core/state" @@ -104,6 +109,59 @@ func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *stat return nil } +// VerifyBlockLastCommitSigs verifies the last commit sigs of the block +func VerifyBlockLastCommitSigs(bc *BlockChain, block *types.Block) error { + header := block.Header() + parentBlock := bc.GetBlockByNumber(block.NumberU64() - 1) + if parentBlock == nil { + return ctxerror.New("[VerifyNewBlock] Failed to get parent block", "shardID", header.ShardID, "blockNum", header.Number) + } + parentHeader := parentBlock.Header() + shardState, err := bc.ReadShardState(parentHeader.Epoch) + committee := shardState.FindCommitteeByID(parentHeader.ShardID) + + if err != nil || committee == nil { + return ctxerror.New("[VerifyNewBlock] Failed to read shard state for cross link header", "shardID", header.ShardID, "blockNum", header.Number).WithCause(err) + } + var committerKeys []*bls.PublicKey + + parseKeysSuccess := true + for _, member := range committee.NodeList { + committerKey := new(bls.PublicKey) + err = member.BlsPublicKey.ToLibBLSPublicKey(committerKey) + if err != nil { + parseKeysSuccess = false + break + } + committerKeys = append(committerKeys, committerKey) + } + if !parseKeysSuccess { + return ctxerror.New("[VerifyNewBlock] cannot convert BLS public key", "shardID", header.ShardID, "blockNum", header.Number).WithCause(err) + } + + mask, err := bls2.NewMask(committerKeys, nil) + if err != nil { + return ctxerror.New("[VerifyNewBlock] cannot create group sig mask", "shardID", header.ShardID, "blockNum", header.Number).WithCause(err) + } + if err := mask.SetMask(header.LastCommitBitmap); err != nil { + return ctxerror.New("[VerifyNewBlock] cannot set group sig mask bits", "shardID", header.ShardID, "blockNum", header.Number).WithCause(err) + } + + aggSig := bls.Sign{} + err = aggSig.Deserialize(header.LastCommitSignature[:]) + if err != nil { + return ctxerror.New("[VerifyNewBlock] unable to deserialize multi-signature from payload").WithCause(err) + } + + blockNumBytes := make([]byte, 8) + binary.LittleEndian.PutUint64(blockNumBytes, header.Number.Uint64()-1) + commitPayload := append(blockNumBytes, header.ParentHash[:]...) + if !aggSig.VerifyHash(mask.AggregatePublic, commitPayload) { + return ctxerror.New("[VerifyNewBlock] Failed to verify the signature for last commit sig", "shardID", header.ShardID, "blockNum", header.Number) + } + return nil +} + // CalcGasLimit computes the gas limit of the next block after parent. It aims // to keep the baseline gas above the provided floor, and increase it towards the // ceil if the blocks are full. If the ceil is exceeded, it will always decrease diff --git a/core/blockchain.go b/core/blockchain.go index 8695cced4..4371d257c 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -2157,7 +2157,7 @@ func (bc *BlockChain) cleanCXReceiptsCheckpoints(shardID uint32, currentNum uint } } -// WriteCXReceiptsSpent mark the CXReceiptsProof list with given unspent status +// WriteCXReceiptsProofSpent mark the CXReceiptsProof list with given unspent status // true: unspent, false: spent func (bc *BlockChain) WriteCXReceiptsProofSpent(cxps []*types.CXReceiptsProof) { for _, cxp := range cxps { diff --git a/core/core_test.go b/core/core_test.go index ffbb504ca..15934f74d 100644 --- a/core/core_test.go +++ b/core/core_test.go @@ -9,13 +9,13 @@ import ( ) func TestIsEpochBlock(t *testing.T) { - block1 := types.NewBlock(&types.Header{Number: big.NewInt(10)}, nil, nil, nil) - block2 := types.NewBlock(&types.Header{Number: big.NewInt(0)}, nil, nil, nil) - block3 := types.NewBlock(&types.Header{Number: big.NewInt(344064)}, nil, nil, nil) - block4 := types.NewBlock(&types.Header{Number: big.NewInt(77)}, nil, nil, nil) - block5 := types.NewBlock(&types.Header{Number: big.NewInt(78)}, nil, nil, nil) - block6 := types.NewBlock(&types.Header{Number: big.NewInt(188)}, nil, nil, nil) - block7 := types.NewBlock(&types.Header{Number: big.NewInt(189)}, nil, nil, nil) + block1 := types.NewBlock(&types.Header{Number: big.NewInt(10)}, nil, nil, nil, nil) + block2 := types.NewBlock(&types.Header{Number: big.NewInt(0)}, nil, nil, nil, nil) + block3 := types.NewBlock(&types.Header{Number: big.NewInt(344064)}, nil, nil, nil, nil) + block4 := types.NewBlock(&types.Header{Number: big.NewInt(77)}, nil, nil, nil, nil) + block5 := types.NewBlock(&types.Header{Number: big.NewInt(78)}, nil, nil, nil, nil) + block6 := types.NewBlock(&types.Header{Number: big.NewInt(188)}, nil, nil, nil, nil) + block7 := types.NewBlock(&types.Header{Number: big.NewInt(189)}, nil, nil, nil, nil) tests := []struct { schedule shardingconfig.Schedule block *types.Block diff --git a/core/rawdb/accessors_indexes_test.go b/core/rawdb/accessors_indexes_test.go index 0b11be455..29fa24ee8 100644 --- a/core/rawdb/accessors_indexes_test.go +++ b/core/rawdb/accessors_indexes_test.go @@ -34,7 +34,7 @@ func TestLookupStorage(t *testing.T) { tx3 := types.NewTransaction(3, common.BytesToAddress([]byte{0x33}), 0, big.NewInt(333), 3333, big.NewInt(33333), []byte{0x33, 0x33, 0x33}) txs := []*types.Transaction{tx1, tx2, tx3} - block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, txs, nil, nil) + block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, txs, nil, nil, nil) // Check that no transactions entries are in a pristine database for i, tx := range txs { diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index b94e22fbd..0ef564ee6 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -26,7 +26,6 @@ import ( "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/params" "github.com/harmony-one/harmony/core/state" - "github.com/harmony-one/harmony/core/types" "github.com/harmony-one/harmony/core/vm" ) @@ -117,7 +116,6 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.DB, error) { input, cfg.GasLimit, cfg.Value, - types.SameShardTx, ) return ret, cfg.State, err @@ -166,7 +164,6 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er input, cfg.GasLimit, cfg.Value, - types.SameShardTx, ) return ret, leftOverGas, err diff --git a/drand/drand_test.go b/drand/drand_test.go index 6343d4350..e7b73492c 100644 --- a/drand/drand_test.go +++ b/drand/drand_test.go @@ -126,7 +126,7 @@ func TestVrf(test *testing.T) { tx1 := types.NewTransaction(1, common.BytesToAddress([]byte{0x11}), 0, big.NewInt(111), 1111, big.NewInt(11111), []byte{0x11, 0x11, 0x11}) txs := []*types.Transaction{tx1} - block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, txs, nil, nil) + block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, txs, nil, nil, nil) blockHash := block.Hash() dRand.vrf(blockHash) diff --git a/node/node_cross_shard.go b/node/node_cross_shard.go index c39367de0..9deb741fa 100644 --- a/node/node_cross_shard.go +++ b/node/node_cross_shard.go @@ -100,9 +100,9 @@ func (node *Node) verifyIncomingReceipts(block *types.Block) error { // ignore duplicated receipts if _, ok := m[hash]; ok { return ctxerror.New("[verifyIncomingReceipts] Double Spent!") - } else { - m[hash] = true } + + m[hash] = true } // TODO: add crosslink blockHeaderHash checking return nil diff --git a/node/node_handler.go b/node/node_handler.go index 40936f54d..ac80c9d48 100644 --- a/node/node_handler.go +++ b/node/node_handler.go @@ -3,7 +3,6 @@ package node import ( "bytes" "context" - "encoding/binary" "errors" "math" "math/big" @@ -30,7 +29,6 @@ import ( "github.com/harmony-one/harmony/contracts/structs" "github.com/harmony-one/harmony/core" "github.com/harmony-one/harmony/core/types" - bls_cosi "github.com/harmony-one/harmony/crypto/bls" nodeconfig "github.com/harmony-one/harmony/internal/configs/node" "github.com/harmony-one/harmony/internal/ctxerror" "github.com/harmony-one/harmony/internal/utils" @@ -343,7 +341,7 @@ func (node *Node) VerifyNewBlock(newBlock *types.Block) error { // e.g. "child.Number == child.IsGenesis() ? 0 : parent.Number+1"? if newBlock.NumberU64() > 1 { - err := node.VerifyBlockLastCommitSigs(newBlock) + err := core.VerifyBlockLastCommitSigs(node.Blockchain(), newBlock) if err != nil { return err } @@ -450,59 +448,6 @@ func (node *Node) VerifyBlockCrossLinks(block *types.Block) error { return nil } -// VerifyBlockLastCommitSigs verifies the last commit sigs of the block -func (node *Node) VerifyBlockLastCommitSigs(block *types.Block) error { - header := block.Header() - parentBlock := node.Blockchain().GetBlockByNumber(block.NumberU64() - 1) - if parentBlock == nil { - return ctxerror.New("[VerifyNewBlock] Failed to get parent block", "shardID", header.ShardID, "blockNum", header.Number) - } - parentHeader := parentBlock.Header() - shardState, err := node.Blockchain().ReadShardState(parentHeader.Epoch) - committee := shardState.FindCommitteeByID(parentHeader.ShardID) - - if err != nil || committee == nil { - return ctxerror.New("[VerifyNewBlock] Failed to read shard state for cross link header", "shardID", header.ShardID, "blockNum", header.Number).WithCause(err) - } - var committerKeys []*bls.PublicKey - - parseKeysSuccess := true - for _, member := range committee.NodeList { - committerKey := new(bls.PublicKey) - err = member.BlsPublicKey.ToLibBLSPublicKey(committerKey) - if err != nil { - parseKeysSuccess = false - break - } - committerKeys = append(committerKeys, committerKey) - } - if !parseKeysSuccess { - return ctxerror.New("[VerifyNewBlock] cannot convert BLS public key", "shardID", header.ShardID, "blockNum", header.Number).WithCause(err) - } - - mask, err := bls_cosi.NewMask(committerKeys, nil) - if err != nil { - return ctxerror.New("[VerifyNewBlock] cannot create group sig mask", "shardID", header.ShardID, "blockNum", header.Number).WithCause(err) - } - if err := mask.SetMask(header.LastCommitBitmap); err != nil { - return ctxerror.New("[VerifyNewBlock] cannot set group sig mask bits", "shardID", header.ShardID, "blockNum", header.Number).WithCause(err) - } - - aggSig := bls.Sign{} - err = aggSig.Deserialize(header.LastCommitSignature[:]) - if err != nil { - return ctxerror.New("[VerifyNewBlock] unable to deserialize multi-signature from payload").WithCause(err) - } - - blockNumBytes := make([]byte, 8) - binary.LittleEndian.PutUint64(blockNumBytes, header.Number.Uint64()-1) - commitPayload := append(blockNumBytes, header.ParentHash[:]...) - if !aggSig.VerifyHash(mask.AggregatePublic, commitPayload) { - return ctxerror.New("[VerifyNewBlock] Failed to verify the signature for last commit sig", "shardID", header.ShardID, "blockNum", header.Number) - } - return nil -} - // BigMaxUint64 is maximum possible uint64 value, that is, (1**64)-1. var BigMaxUint64 = new(big.Int).SetBytes([]byte{ 255, 255, 255, 255, 255, 255, 255, 255, diff --git a/node/worker/worker_test.go b/node/worker/worker_test.go index efad73c35..dd0648419 100644 --- a/node/worker/worker_test.go +++ b/node/worker/worker_test.go @@ -5,11 +5,12 @@ import ( "math/rand" "testing" + chain2 "github.com/harmony-one/harmony/internal/chain" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/params" "github.com/harmony-one/harmony/common/denominations" - "github.com/harmony-one/harmony/consensus" "github.com/harmony-one/harmony/core" "github.com/harmony-one/harmony/core/types" "github.com/harmony-one/harmony/core/vm" @@ -37,10 +38,10 @@ func TestNewWorker(t *testing.T) { genesis := gspec.MustCommit(database) _ = genesis - chain, _ := core.NewBlockChain(database, nil, gspec.Config, consensus.NewFaker(), vm.Config{}, nil) + chain, _ := core.NewBlockChain(database, nil, gspec.Config, chain2.Engine, vm.Config{}, nil) // Create a new worker - worker := New(params.TestChainConfig, chain, consensus.NewFaker(), 0) + worker := New(params.TestChainConfig, chain, chain2.Engine, 0) if worker.GetCurrentState().GetBalance(crypto.PubkeyToAddress(testBankKey.PublicKey)).Cmp(testBankFunds) != 0 { t.Error("Worker state is not setup correctly") @@ -54,15 +55,15 @@ func TestCommitTransactions(t *testing.T) { gspec = core.Genesis{ Config: chainConfig, Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}}, - ShardID: 10, + ShardID: 0, } ) gspec.MustCommit(database) - chain, _ := core.NewBlockChain(database, nil, gspec.Config, consensus.NewFaker(), vm.Config{}, nil) + chain, _ := core.NewBlockChain(database, nil, gspec.Config, chain2.Engine, vm.Config{}, nil) // Create a new worker - worker := New(params.TestChainConfig, chain, consensus.NewFaker(), 0) + worker := New(params.TestChainConfig, chain, chain2.Engine, 0) // Generate a test tx baseNonce := worker.GetCurrentState().GetNonce(crypto.PubkeyToAddress(testBankKey.PublicKey))