Merge pull request #1950 from chaosma/verify-shards

add shardState verification
pull/1955/head
Rongjian Lan 5 years ago committed by GitHub
commit f5d50cf73f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 10
      consensus/engine/consensus_engine.go
  2. 92
      core/blockchain.go
  3. 4
      core/chain_makers.go
  4. 36
      internal/chain/engine.go
  5. 10
      node/node_handler.go
  6. 4
      node/node_newblock.go
  7. 70
      node/worker/worker.go

@ -34,6 +34,9 @@ type ChainReader interface {
// GetHeaderByHash retrieves a block header from the database by its hash.
GetHeaderByHash(hash common.Hash) *block.Header
// ShardID returns shardID
ShardID() uint32
// GetBlock retrieves a block from the database by hash and number.
GetBlock(hash common.Hash, number uint64) *types.Block
@ -53,6 +56,10 @@ type ChainReader interface {
//ReadBlockRewardAccumulator is the block-reward given for block number
ReadBlockRewardAccumulator(uint64) (*big.Int, error)
//SuperCommitteeForNextEpoch calculates the next epoch's supper committee
// isVerify flag is to indicate which stage to call this function: true (verification stage), false(propose stage)
SuperCommitteeForNextEpoch(beacon ChainReader, header *block.Header, isVerify bool) (*shard.State, error)
}
// Engine is an algorithm agnostic consensus engine.
@ -84,6 +91,9 @@ type Engine interface {
// the consensus rules of the given engine.
VerifySeal(chain ChainReader, header *block.Header) error
// VerifyShardState verifies the shard state during epoch transition is valid
VerifyShardState(chain ChainReader, beacon ChainReader, header *block.Header) error
// Prepare initializes the consensus fields of a block header according to the
// rules of a particular engine. The changes are executed inline.
Prepare(chain ChainReader, header *block.Header) error

@ -50,6 +50,7 @@ import (
"github.com/harmony-one/harmony/internal/utils"
"github.com/harmony-one/harmony/numeric"
"github.com/harmony-one/harmony/shard"
"github.com/harmony-one/harmony/shard/committee"
staking "github.com/harmony-one/harmony/staking/types"
lru "github.com/hashicorp/golang-lru"
)
@ -2960,3 +2961,94 @@ func (bc *BlockChain) GetECDSAFromCoinbase(header *block.Header) (common.Address
}
return common.Address{}, ctxerror.New("cannot find corresponding ECDSA Address", "coinbaseAddr", header.Coinbase())
}
// SuperCommitteeForNextEpoch ...
// isVerify=true means validators use it to verify
// isVerify=false means leader is to propose
func (bc *BlockChain) SuperCommitteeForNextEpoch(
beacon consensus_engine.ChainReader,
header *block.Header,
isVerify bool,
) (*shard.State, error) {
var (
nextCommittee = new(shard.State)
err error
beaconEpoch = new(big.Int)
shardState = shard.State{}
)
switch header.ShardID() {
case shard.BeaconChainShardID:
if shard.Schedule.IsLastBlock(header.Number().Uint64()) {
nextCommittee, err = committee.WithStakingEnabled.Compute(
new(big.Int).Add(header.Epoch(), common.Big1),
beacon,
)
}
default:
// TODO: needs to make sure beacon chain sync works.
if isVerify {
//verify
shardState, err = header.GetShardState()
if err != nil {
return &shard.State{}, err
}
// before staking epoch
if shardState.Epoch == nil {
beaconEpoch = new(big.Int).Add(header.Epoch(), common.Big1)
} else { // after staking epoch
beaconEpoch = shardState.Epoch
}
} else {
//propose
beaconEpoch = beacon.CurrentHeader().Epoch()
}
utils.Logger().Debug().Msgf("[SuperCommitteeCalculation] isVerify: %+v, realBeaconEpoch:%+v, beaconEpoch: %+v, headerEpoch:%+v, shardStateEpoch:%+v",
isVerify, beacon.CurrentHeader().Epoch(), beaconEpoch, header.Epoch(), shardState.Epoch)
nextEpoch := new(big.Int).Add(header.Epoch(), common.Big1)
if bc.Config().IsStaking(nextEpoch) {
// If next epoch is staking epoch, I should wait and listen for beacon chain for epoch changes
switch beaconEpoch.Cmp(header.Epoch()) {
case 1:
// If beacon chain is bigger than shard chain in epoch, it means I should catch up with beacon chain now
nextCommittee, err = committee.WithStakingEnabled.ReadFromDB(
beaconEpoch, beacon,
)
utils.Logger().Debug().
Uint64("blockNum", header.Number().Uint64()).
Uint64("myCurEpoch", header.Epoch().Uint64()).
Uint64("beaconEpoch", beaconEpoch.Uint64()).
Msg("Propose new epoch as beacon chain's epoch")
case 0:
// If it's same epoch, no need to propose new shard state (new epoch change)
case -1:
// If beacon chain is behind, shard chain should wait for the beacon chain by not changing epochs.
}
} else {
if bc.Config().IsStaking(beaconEpoch) {
// If I am not even in the last epoch before staking epoch and beacon chain is already in staking epoch,
// I should just catch up with beacon chain's epoch
nextCommittee, err = committee.WithStakingEnabled.ReadFromDB(
beaconEpoch, beacon,
)
utils.Logger().Debug().
Uint64("blockNum", header.Number().Uint64()).
Uint64("myCurEpoch", header.Epoch().Uint64()).
Uint64("beaconEpoch", beaconEpoch.Uint64()).
Msg("Propose entering staking along with beacon chain's epoch")
} else {
// If I are not in staking nor has beacon chain proposed a staking-based shard state,
// do pre-staking committee calculation
if shard.Schedule.IsLastBlock(header.Number().Uint64()) {
nextCommittee, err = committee.WithStakingEnabled.Compute(
nextEpoch,
bc,
)
}
}
}
}
return nextCommittee, err
}

@ -265,6 +265,7 @@ func (cr *fakeChainReader) Config() *params.ChainConfig {
}
func (cr *fakeChainReader) CurrentHeader() *block.Header { return nil }
func (cr *fakeChainReader) ShardID() uint32 { return 0 }
func (cr *fakeChainReader) GetHeaderByNumber(number uint64) *block.Header { return nil }
func (cr *fakeChainReader) GetHeaderByHash(hash common.Hash) *block.Header { return nil }
func (cr *fakeChainReader) GetHeader(hash common.Hash, number uint64) *block.Header { return nil }
@ -273,6 +274,9 @@ func (cr *fakeChainReader) ReadShardState(epoch *big.Int) (*shard.State, error)
func (cr *fakeChainReader) ReadActiveValidatorList() ([]common.Address, error) { return nil, nil }
func (cr *fakeChainReader) ReadValidatorList() ([]common.Address, error) { return nil, nil }
func (cr *fakeChainReader) ValidatorCandidates() []common.Address { return nil }
func (cr *fakeChainReader) SuperCommitteeForNextEpoch(beacon consensus_engine.ChainReader, header *block.Header, isVerify bool) (*shard.State, error) {
return nil, nil
}
func (cr *fakeChainReader) ReadValidatorInformation(addr common.Address) (*staking.ValidatorWrapper, error) {
return nil, nil
}

@ -1,6 +1,7 @@
package chain
import (
"bytes"
"encoding/binary"
"math/big"
@ -145,6 +146,41 @@ func ReadPublicKeysFromLastBlock(bc engine.ChainReader, header *block.Header) ([
return GetPublicKeys(bc, parentHeader, false)
}
// VerifyShardState implements Engine, checking the shardstate is valid at epoch transition
func (e *engineImpl) VerifyShardState(bc engine.ChainReader, beacon engine.ChainReader, header *block.Header) error {
if bc.ShardID() != header.ShardID() {
return ctxerror.New("[VerifyShardState] shardID not match", "bc.ShardID", bc.ShardID(), "header.ShardID", header.ShardID())
}
headerShardStateBytes := header.ShardState()
// TODO: figure out leader withhold shardState
if headerShardStateBytes == nil || len(headerShardStateBytes) == 0 {
return nil
}
shardState, err := bc.SuperCommitteeForNextEpoch(beacon, header, true)
if err != nil {
return ctxerror.New("[VerifyShardState] SuperCommitteeForNexEpoch calculation had error", "shardState", shardState).WithCause(err)
}
isStaking := false
if shardState.Epoch != nil && bc.Config().IsStaking(shardState.Epoch) {
isStaking = true
}
shardStateBytes, err := shard.EncodeWrapper(*shardState, isStaking)
if err != nil {
return ctxerror.New("[VerifyShardState] ShardState Encoding had error", "shardStateBytes", shardStateBytes).WithCause(err)
}
if !bytes.Equal(shardStateBytes, headerShardStateBytes) {
headerSS, err := header.GetShardState()
if err != nil {
headerSS = shard.State{}
}
return ctxerror.New("[VerifyShardState] ShardState is Invalid", "shardStateEpoch", shardState.Epoch, "headerEpoch", header.Epoch(), "headerShardStateEpoch", headerSS.Epoch, "beaconEpoch", beacon.CurrentHeader().Epoch())
}
return nil
}
// VerifySeal implements Engine, checking whether the given block's parent block satisfies
// the PoS difficulty requirements, i.e. >= 2f+1 valid signatures from the committee
// Note that each block header contains the bls signature of the parent block

@ -264,6 +264,16 @@ func (node *Node) VerifyNewBlock(newBlock *types.Block) error {
"my shard ID", node.Blockchain().ShardID(),
"new block's shard ID", newBlock.ShardID())
}
err = node.Blockchain().Engine().VerifyShardState(node.Blockchain(), node.Beaconchain(), newBlock.Header())
if err != nil {
utils.Logger().Error().
Str("blockHash", newBlock.Hash().Hex()).
Err(err).
Msg("cannot VerifyShardState for the new block")
return ctxerror.New("cannot VerifyShardState for the new block", "blockHash", newBlock.Hash()).WithCause(err)
}
err = node.Blockchain().ValidateNewBlock(newBlock)
if err != nil {
utils.Logger().Error().

@ -151,8 +151,8 @@ func (node *Node) proposeNewBlock() (*types.Block, error) {
// Prepare shard state
shardState := new(shard.State)
if shardState, err = node.Worker.SuperCommitteeForNextEpoch(
node.Consensus.ShardID, node.Beaconchain(),
if shardState, err = node.Blockchain().SuperCommitteeForNextEpoch(
node.Beaconchain(), node.Worker.GetCurrentHeader(), false,
); err != nil {
return nil, err
}

@ -20,7 +20,6 @@ import (
"github.com/harmony-one/harmony/internal/params"
"github.com/harmony-one/harmony/internal/utils"
"github.com/harmony-one/harmony/shard"
"github.com/harmony-one/harmony/shard/committee"
staking "github.com/harmony-one/harmony/staking/types"
)
@ -290,75 +289,6 @@ func (w *Worker) IncomingReceipts() []*types.CXReceiptsProof {
return w.current.incxs
}
// SuperCommitteeForNextEpoch assumes only called by consensus leader
func (w *Worker) SuperCommitteeForNextEpoch(
shardID uint32,
beacon *core.BlockChain,
) (*shard.State, error) {
var (
nextCommittee = new(shard.State)
err error
)
switch shardID {
case shard.BeaconChainShardID:
if shard.Schedule.IsLastBlock(w.current.header.Number().Uint64()) {
nextCommittee, err = committee.WithStakingEnabled.Compute(
new(big.Int).Add(w.current.header.Epoch(), common.Big1),
beacon,
)
}
default:
// TODO: needs to make sure beacon chain sync works.
beaconEpoch := beacon.CurrentHeader().Epoch()
nextEpoch := new(big.Int).Add(w.current.header.Epoch(), common.Big1)
if w.config.IsStaking(nextEpoch) {
// If next epoch is staking epoch, I should wait and listen for beacon chain for epoch changes
switch beaconEpoch.Cmp(w.current.header.Epoch()) {
case 1:
// If beacon chain is bigger than shard chain in epoch, it means I should catch up with beacon chain now
nextCommittee, err = committee.WithStakingEnabled.ReadFromDB(
beaconEpoch, beacon,
)
utils.Logger().Debug().
Uint64("blockNum", w.current.header.Number().Uint64()).
Uint64("myCurEpoch", w.current.header.Epoch().Uint64()).
Uint64("beaconEpoch", beaconEpoch.Uint64()).
Msg("Propose new epoch as beacon chain's epoch")
case 0:
// If it's same epoch, no need to propose new shard state (new epoch change)
case -1:
// If beacon chain is behind, shard chain should wait for the beacon chain by not changing epochs.
}
} else {
if w.config.IsStaking(beaconEpoch) {
// If I am not even in the last epoch before staking epoch and beacon chain is already in staking epoch,
// I should just catch up with beacon chain's epoch
nextCommittee, err = committee.WithStakingEnabled.ReadFromDB(
beaconEpoch, beacon,
)
utils.Logger().Debug().
Uint64("blockNum", w.current.header.Number().Uint64()).
Uint64("myCurEpoch", w.current.header.Epoch().Uint64()).
Uint64("beaconEpoch", beaconEpoch.Uint64()).
Msg("Propose entering staking along with beacon chain's epoch")
} else {
// If I are not in staking nor has beacon chain proposed a staking-based shard state,
// do pre-staking committee calculation
if shard.Schedule.IsLastBlock(w.current.header.Number().Uint64()) {
nextCommittee, err = committee.WithStakingEnabled.Compute(
nextEpoch,
w.chain,
)
}
}
}
}
return nextCommittee, err
}
// FinalizeNewBlock generate a new block for the next consensus round.
func (w *Worker) FinalizeNewBlock(
sig []byte, signers []byte, viewID uint64, coinbase common.Address,

Loading…
Cancel
Save