diff --git a/core/blockchain.go b/core/blockchain.go index f7a2ab266..c6223aace 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -23,6 +23,7 @@ import ( "fmt" "io" "math/big" + "strings" "sync" "sync/atomic" "time" @@ -1731,6 +1732,10 @@ func (bc *BlockChain) ReadShardState(epoch *big.Int) (*shard.State, error) { } shardState, err := rawdb.ReadShardState(bc.db, epoch) if err != nil { + if strings.Contains(err.Error(), rawdb.MsgNoShardStateFromDB) && + shard.Schedule.IsSkippedEpoch(bc.ShardID(), epoch) { + return nil, fmt.Errorf("epoch skipped on chain: %w", err) + } return nil, err } bc.shardStateCache.Add(cacheKey, shardState) diff --git a/internal/configs/sharding/fixedschedule.go b/internal/configs/sharding/fixedschedule.go index 1fd9cd71e..477a8a514 100644 --- a/internal/configs/sharding/fixedschedule.go +++ b/internal/configs/sharding/fixedschedule.go @@ -61,6 +61,11 @@ func (s fixedSchedule) GetShardingStructure(numShard, shardID int) []map[string] return genShardingStructure(numShard, shardID, TestNetHTTPPattern, TestNetHTTPPattern) } +// IsSkippedEpoch returns if an epoch was skipped on shard due to staking epoch +func (s fixedSchedule) IsSkippedEpoch(shardID uint32, epoch *big.Int) bool { + return false +} + // NewFixedSchedule returns a sharding configuration schedule that uses the // given config instance for all epochs. Useful for testing. func NewFixedSchedule(instance Instance) Schedule { diff --git a/internal/configs/sharding/localnet.go b/internal/configs/sharding/localnet.go index 19b3cc2f2..7fc2c9d40 100644 --- a/internal/configs/sharding/localnet.go +++ b/internal/configs/sharding/localnet.go @@ -28,7 +28,7 @@ const ( localnetRandomnessStartingEpoch = 0 ) -func (localnetSchedule) InstanceForEpoch(epoch *big.Int) Instance { +func (ls localnetSchedule) InstanceForEpoch(epoch *big.Int) Instance { switch { case epoch.Cmp(params.LocalnetChainConfig.StakingEpoch) >= 0: return localnetV2 @@ -108,6 +108,11 @@ func (ls localnetSchedule) GetShardingStructure(numShard, shardID int) []map[str return res } +// IsSkippedEpoch returns if an epoch was skipped on shard due to staking epoch +func (ls localnetSchedule) IsSkippedEpoch(shardID uint32, epoch *big.Int) bool { + return false +} + var ( localnetReshardingEpoch = []*big.Int{ big.NewInt(0), big.NewInt(localnetV1Epoch), params.LocalnetChainConfig.StakingEpoch, diff --git a/internal/configs/sharding/mainnet.go b/internal/configs/sharding/mainnet.go index 0179e0fcb..b84e1fa0c 100644 --- a/internal/configs/sharding/mainnet.go +++ b/internal/configs/sharding/mainnet.go @@ -38,12 +38,21 @@ const ( MainNetWSPattern = "wss://ws.s%d.t.hmny.io" ) +var ( + // map of epochs skipped due to staking launch on mainnet + skippedEpochs = map[uint32][]*big.Int{ + 1: []*big.Int{big.NewInt(181), big.NewInt(182), big.NewInt(183), big.NewInt(184), big.NewInt(185)}, + 2: []*big.Int{big.NewInt(184), big.NewInt(185)}, + 3: []*big.Int{big.NewInt(183), big.NewInt(184), big.NewInt(185)}, + } +) + // MainnetSchedule is the mainnet sharding configuration schedule. var MainnetSchedule mainnetSchedule type mainnetSchedule struct{} -func (mainnetSchedule) InstanceForEpoch(epoch *big.Int) Instance { +func (ms mainnetSchedule) InstanceForEpoch(epoch *big.Int) Instance { switch { case epoch.Cmp(big.NewInt(mainnetV2_2Epoch)) >= 0: return mainnetV2_2 @@ -87,7 +96,7 @@ func (mainnetSchedule) InstanceForEpoch(epoch *big.Int) Instance { } } -func (mainnetSchedule) BlocksPerEpoch() uint64 { +func (ms mainnetSchedule) BlocksPerEpoch() uint64 { return blocksPerEpoch } @@ -147,6 +156,18 @@ func (ms mainnetSchedule) GetShardingStructure(numShard, shardID int) []map[stri return genShardingStructure(numShard, shardID, MainNetHTTPPattern, MainNetWSPattern) } +// IsSkippedEpoch returns if an epoch was skipped on shard due to staking epoch +func (ms mainnetSchedule) IsSkippedEpoch(shardID uint32, epoch *big.Int) bool { + if skipped, exists := skippedEpochs[shardID]; exists { + for _, e := range skipped { + if epoch.Cmp(e) == 0 { + return true + } + } + } + return false +} + var mainnetReshardingEpoch = []*big.Int{big.NewInt(0), big.NewInt(mainnetV0_1Epoch), big.NewInt(mainnetV0_2Epoch), big.NewInt(mainnetV0_3Epoch), big.NewInt(mainnetV0_4Epoch), big.NewInt(mainnetV1Epoch), big.NewInt(mainnetV1_1Epoch), big.NewInt(mainnetV1_2Epoch), big.NewInt(mainnetV1_3Epoch), big.NewInt(mainnetV1_4Epoch), big.NewInt(mainnetV1_5Epoch), big.NewInt(mainnetV2_0Epoch), big.NewInt(mainnetV2_1Epoch), big.NewInt(mainnetV2_2Epoch)} var ( diff --git a/internal/configs/sharding/pangaea.go b/internal/configs/sharding/pangaea.go index 0feba72f6..2c04c0fbc 100644 --- a/internal/configs/sharding/pangaea.go +++ b/internal/configs/sharding/pangaea.go @@ -26,7 +26,7 @@ const ( PangaeaWSPattern = "wss://ws.s%d.os.hmny.io" ) -func (pangaeaSchedule) InstanceForEpoch(epoch *big.Int) Instance { +func (ps pangaeaSchedule) InstanceForEpoch(epoch *big.Int) Instance { switch { case epoch.Cmp(params.PangaeaChainConfig.StakingEpoch) >= 0: return pangaeaV1 @@ -66,15 +66,20 @@ func (ps pangaeaSchedule) RandomnessStartingEpoch() uint64 { return mainnetRandomnessStartingEpoch } -func (pangaeaSchedule) GetNetworkID() NetworkID { +func (ps pangaeaSchedule) GetNetworkID() NetworkID { return Pangaea } // GetShardingStructure is the sharding structure for mainnet. -func (pangaeaSchedule) GetShardingStructure(numShard, shardID int) []map[string]interface{} { +func (ps pangaeaSchedule) GetShardingStructure(numShard, shardID int) []map[string]interface{} { return genShardingStructure(numShard, shardID, PangaeaHTTPPattern, PangaeaWSPattern) } +// IsSkippedEpoch returns if an epoch was skipped on shard due to staking epoch +func (ps pangaeaSchedule) IsSkippedEpoch(shardID uint32, epoch *big.Int) bool { + return false +} + var pangaeaReshardingEpoch = []*big.Int{ big.NewInt(0), params.PangaeaChainConfig.StakingEpoch, diff --git a/internal/configs/sharding/partner.go b/internal/configs/sharding/partner.go index 233793ae1..f4e1745f9 100644 --- a/internal/configs/sharding/partner.go +++ b/internal/configs/sharding/partner.go @@ -27,7 +27,7 @@ const ( PartnerWSPattern = "wss://ws.s%d.ps.hmny.io" ) -func (partnerSchedule) InstanceForEpoch(epoch *big.Int) Instance { +func (ps partnerSchedule) InstanceForEpoch(epoch *big.Int) Instance { switch { case epoch.Cmp(params.PartnerChainConfig.StakingEpoch) >= 0: return partnerV1 @@ -36,7 +36,7 @@ func (partnerSchedule) InstanceForEpoch(epoch *big.Int) Instance { } } -func (partnerSchedule) BlocksPerEpoch() uint64 { +func (ps partnerSchedule) BlocksPerEpoch() uint64 { return partnerBlocksPerEpoch } @@ -77,6 +77,11 @@ func (ps partnerSchedule) GetShardingStructure(numShard, shardID int) []map[stri return genShardingStructure(numShard, shardID, PartnerHTTPPattern, PartnerWSPattern) } +// IsSkippedEpoch returns if an epoch was skipped on shard due to staking epoch +func (ps partnerSchedule) IsSkippedEpoch(shardID uint32, epoch *big.Int) bool { + return false +} + var partnerReshardingEpoch = []*big.Int{ big.NewInt(0), params.TestnetChainConfig.StakingEpoch, diff --git a/internal/configs/sharding/shardingconfig.go b/internal/configs/sharding/shardingconfig.go index 3838b04b8..e246a812d 100644 --- a/internal/configs/sharding/shardingconfig.go +++ b/internal/configs/sharding/shardingconfig.go @@ -43,6 +43,9 @@ type Schedule interface { // GetShardingStructure returns sharding structure. GetShardingStructure(int, int) []map[string]interface{} + + // IsSkippedEpoch returns if epoch was skipped on shard chain + IsSkippedEpoch(uint32, *big.Int) bool } // Instance is one sharding configuration instance. diff --git a/internal/configs/sharding/stress.go b/internal/configs/sharding/stress.go index 809469379..2ee77d5ef 100644 --- a/internal/configs/sharding/stress.go +++ b/internal/configs/sharding/stress.go @@ -27,7 +27,7 @@ const ( StressNetWSPattern = "wss://ws.s%d.stn.hmny.io" ) -func (stressnetSchedule) InstanceForEpoch(epoch *big.Int) Instance { +func (ss stressnetSchedule) InstanceForEpoch(epoch *big.Int) Instance { switch { case epoch.Cmp(params.StressnetChainConfig.StakingEpoch) >= 0: return stressnetV1 @@ -36,7 +36,7 @@ func (stressnetSchedule) InstanceForEpoch(epoch *big.Int) Instance { } } -func (stressnetSchedule) BlocksPerEpoch() uint64 { +func (ss stressnetSchedule) BlocksPerEpoch() uint64 { return stressnetBlocksPerEpoch } @@ -77,6 +77,11 @@ func (ss stressnetSchedule) GetShardingStructure(numShard, shardID int) []map[st return genShardingStructure(numShard, shardID, StressNetHTTPPattern, StressNetWSPattern) } +// IsSkippedEpoch returns if an epoch was skipped on shard due to staking epoch +func (ss stressnetSchedule) IsSkippedEpoch(shardID uint32, epoch *big.Int) bool { + return false +} + var stressnetReshardingEpoch = []*big.Int{ big.NewInt(0), params.StressnetChainConfig.StakingEpoch, diff --git a/internal/configs/sharding/testnet.go b/internal/configs/sharding/testnet.go index 2fd7a4368..162903318 100644 --- a/internal/configs/sharding/testnet.go +++ b/internal/configs/sharding/testnet.go @@ -26,7 +26,7 @@ const ( TestNetWSPattern = "wss://ws.s%d.b.hmny.io" ) -func (testnetSchedule) InstanceForEpoch(epoch *big.Int) Instance { +func (ts testnetSchedule) InstanceForEpoch(epoch *big.Int) Instance { switch { case epoch.Cmp(params.TestnetChainConfig.StakingEpoch) >= 0: return testnetV1 @@ -35,7 +35,7 @@ func (testnetSchedule) InstanceForEpoch(epoch *big.Int) Instance { } } -func (testnetSchedule) BlocksPerEpoch() uint64 { +func (ts testnetSchedule) BlocksPerEpoch() uint64 { return testnetBlocksPerEpoch } @@ -76,6 +76,11 @@ func (ts testnetSchedule) GetShardingStructure(numShard, shardID int) []map[stri return genShardingStructure(numShard, shardID, TestNetHTTPPattern, TestNetWSPattern) } +// IsSkippedEpoch returns if an epoch was skipped on shard due to staking epoch +func (ts testnetSchedule) IsSkippedEpoch(shardID uint32, epoch *big.Int) bool { + return false +} + var testnetReshardingEpoch = []*big.Int{ big.NewInt(0), params.TestnetChainConfig.StakingEpoch,