[engine] added additional cache epochCtxCache for engineImpl, also removed shardID in engineImpl (since it's redundant with chainReader.ShardID())

pull/3626/head
Jacky Wang 4 years ago
parent 54ab774353
commit 2d70b2adab
No known key found for this signature in database
GPG Key ID: 1085CE5F4FF5842C
  1. 2
      core/tx_pool_test.go
  2. 146
      internal/chain/engine.go
  3. 2
      node/node.go
  4. 4
      node/worker/worker_test.go
  5. 2
      test/chain/reward/main.go

@ -157,7 +157,7 @@ func createBlockChain() *BlockChain {
database := rawdb.NewMemoryDatabase()
genesis := gspec.MustCommit(database)
_ = genesis
engine := chain2.NewEngine(0)
engine := chain2.NewEngine()
blockchain, _ := NewBlockChain(database, nil, gspec.Config, engine, vm.Config{}, nil)
return blockchain
}

@ -16,6 +16,7 @@ import (
"github.com/harmony-one/harmony/consensus/signature"
"github.com/harmony-one/harmony/core/state"
"github.com/harmony-one/harmony/core/types"
"github.com/harmony-one/harmony/crypto/bls"
bls_cosi "github.com/harmony-one/harmony/crypto/bls"
"github.com/harmony-one/harmony/internal/utils"
"github.com/harmony-one/harmony/shard"
@ -27,22 +28,24 @@ import (
const (
verifiedSigCache = 20
epochCtxCache = 20
)
type engineImpl struct {
beacon engine.ChainReader
shardID uint32
beacon engine.ChainReader
// Caching field
epochCtxCache *lru.Cache // epochCtxKey -> epochCtx
verifiedSigCache *lru.Cache // verifiedSigKey -> struct{}{}
}
// NewEngine creates Engine with some cache
func NewEngine(shardID uint32) *engineImpl {
func NewEngine() *engineImpl {
sigCache, _ := lru.New(verifiedSigCache)
epochCtxCache, _ := lru.New(epochCtxCache)
return &engineImpl{
beacon: nil,
shardID: shardID,
epochCtxCache: epochCtxCache,
verifiedSigCache: sigCache,
}
}
@ -414,28 +417,24 @@ func (e *engineImpl) verifyHeaderSignatureCached(chain engine.ChainReader, heade
}
func (e *engineImpl) verifyHeaderSignature(chain engine.ChainReader, header *block.Header, commitSig bls_cosi.SerializedSignature, commitBitmap []byte) error {
ss, err := e.getShardState(chain, header.Epoch(), header.ShardID())
if err != nil {
return err
}
shardComm, err := ss.FindCommitteeByID(header.ShardID())
if err != nil {
return err
}
pubKeys, err := shardComm.BLSPublicKeys()
if err != nil {
return err
ec, ok := e.getCachedEpochCtx(header)
if !ok {
// Epoch context not in cache, read from chain
var err error
ec, err = readEpochCtxFromChain(chain, header.Epoch(), header.ShardID())
if err != nil {
return err
}
}
var (
pubKeys = ec.pubKeys
qrVerifier = ec.qrVerifier
)
aggSig, mask, err := DecodeSigBitmap(commitSig, commitBitmap, pubKeys)
if err != nil {
return errors.Wrap(err, "deserialize signature and bitmap")
}
isStaking := chain.Config().IsStaking(header.Epoch())
qrVerifier, err := quorum.NewVerifier(shardComm, header.Epoch(), isStaking)
if err != nil {
return err
}
// Verify signature, mask against quorum.Verifier and publicKeys
if !qrVerifier.IsQuorumAchievedByMask(mask) {
return errors.New("not enough signature collected")
@ -449,32 +448,13 @@ func (e *engineImpl) verifyHeaderSignature(chain engine.ChainReader, header *blo
return nil
}
func (e *engineImpl) getShardState(chain engine.ChainReader, epoch *big.Int, targetShardID uint32) (*shard.State, error) {
// (TODO) For now, when doing cross shard, we need recalcualte the shard state since we don't have
// hard state of other shards
if e.needRecalculateStateShard(chain, epoch, targetShardID) {
shardState, err := committee.WithStakingEnabled.Compute(epoch, chain)
if err != nil {
return nil, errors.Wrapf(err, "compute shard state for epoch %v", epoch)
}
return shardState, nil
} else {
shardState, err := chain.ReadShardState(epoch)
if err != nil {
return nil, errors.Wrapf(err, "read shard state for epoch %v", epoch)
}
return shardState, nil
}
}
// only recalculate for non-staking epoch and targetShardID is not the same
// as engine
func (e *engineImpl) needRecalculateStateShard(chain engine.ChainReader, epoch *big.Int, targetShardID uint32) bool {
if chain.Config().IsStaking(epoch) {
return false
func (e *engineImpl) getCachedEpochCtx(header *block.Header) (*epochCtx, bool) {
ecKey := newEpochCtxKeyFromHeader(header)
ec, ok := e.epochCtxCache.Get(ecKey)
if !ok || ec == nil {
return nil, false
}
return targetShardID != e.shardID
return ec.(*epochCtx), true
}
// Support 512 at most validator nodes
@ -498,6 +478,80 @@ func newVerifiedSigKey(blockHash common.Hash, sig bls_cosi.SerializedSignature,
}
}
type (
// epochCtxKey is the key for caching epochCtx
epochCtxKey struct {
shardID uint32
epoch uint64
}
// epochCtx is the epoch's context used for signature verification.
// The value is fixed for each epoch and is cached in engineImpl.
epochCtx struct {
qrVerifier quorum.Verifier
pubKeys []bls.PublicKeyWrapper
}
)
func newEpochCtxKeyFromHeader(header *block.Header) epochCtxKey {
return epochCtxKey{
shardID: header.ShardID(),
epoch: header.Epoch().Uint64(),
}
}
func readEpochCtxFromChain(chain engine.ChainReader, epoch *big.Int, targetShardID uint32) (*epochCtx, error) {
ss, err := readShardState(chain, epoch, targetShardID)
if err != nil {
return nil, err
}
shardComm, err := ss.FindCommitteeByID(targetShardID)
if err != nil {
return nil, err
}
pubKeys, err := shardComm.BLSPublicKeys()
if err != nil {
return nil, err
}
isStaking := chain.Config().IsStaking(epoch)
qrVerifier, err := quorum.NewVerifier(shardComm, epoch, isStaking)
if err != nil {
return nil, err
}
return &epochCtx{
qrVerifier: qrVerifier,
pubKeys: pubKeys,
}, nil
}
func readShardState(chain engine.ChainReader, epoch *big.Int, targetShardID uint32) (*shard.State, error) {
// When doing cross shard, we need recalcualte the shard state since we don't have
// shard state of other shards
if needRecalculateStateShard(chain, epoch, targetShardID) {
shardState, err := committee.WithStakingEnabled.Compute(epoch, chain)
if err != nil {
return nil, errors.Wrapf(err, "compute shard state for epoch %v", epoch)
}
return shardState, nil
} else {
shardState, err := chain.ReadShardState(epoch)
if err != nil {
return nil, errors.Wrapf(err, "read shard state for epoch %v", epoch)
}
return shardState, nil
}
}
// only recalculate for non-staking epoch and targetShardID is not the same
// as engine
func needRecalculateStateShard(chain engine.ChainReader, epoch *big.Int, targetShardID uint32) bool {
if chain.Config().IsStaking(epoch) {
return false
}
return targetShardID != chain.ShardID()
}
// GetLockPeriodInEpoch returns the delegation lock period for the given chain
func GetLockPeriodInEpoch(chain engine.ChainReader, epoch *big.Int) int {
lockPeriod := staking.LockPeriodInEpoch

@ -933,7 +933,7 @@ func New(
chainConfig := networkType.ChainConfig()
node.chainConfig = chainConfig
engine := chain.NewEngine(consensusObj.ShardID)
engine := chain.NewEngine()
collection := shardchain.NewCollection(
chainDBFactory, &genesisInitializer{&node}, engine, &chainConfig,

@ -38,7 +38,7 @@ func TestNewWorker(t *testing.T) {
Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}},
ShardID: 10,
}
engine = chain2.NewEngine(10)
engine = chain2.NewEngine()
)
genesis := gspec.MustCommit(database)
@ -66,7 +66,7 @@ func TestCommitTransactions(t *testing.T) {
Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}},
ShardID: 0,
}
engine = chain2.NewEngine(0)
engine = chain2.NewEngine()
)
gspec.MustCommit(database)

@ -108,7 +108,7 @@ func main() {
database := rawdb.NewMemoryDatabase()
genesis := gspec.MustCommit(database)
_ = genesis
engine := chain.NewEngine(0)
engine := chain.NewEngine()
bc, _ := core.NewBlockChain(database, nil, gspec.Config, engine, vm.Config{}, nil)
statedb, _ := state.New(common2.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
msg := createValidator()

Loading…
Cancel
Save