Merge pull request #1403 from rlan35/state-sync-sig-validation

State sync sig validation
pull/1293/head
chaosma 5 years ago committed by GitHub
commit d48e1472e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      api/client/service/server_test.go
  2. 4
      api/service/explorer/storage_test.go
  3. 2
      api/service/explorer/structs_test.go
  4. 12
      api/service/syncing/syncing.go
  5. 58
      core/block_validator.go
  6. 2
      core/blockchain.go
  7. 14
      core/core_test.go
  8. 2
      core/rawdb/accessors_indexes_test.go
  9. 3
      core/vm/runtime/runtime.go
  10. 2
      drand/drand_test.go
  11. 4
      node/node_cross_shard.go
  12. 57
      node/node_handler.go
  13. 13
      node/worker/worker_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"))

@ -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()

@ -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()")

@ -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()

@ -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

@ -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 {

@ -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

@ -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 {

@ -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

@ -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)

@ -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

@ -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,

@ -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))

Loading…
Cancel
Save