mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 02:12:23 +00:00
les: update/recover stable checkpoint
This commit is contained in:
parent
2b319f6b3e
commit
536eb1632d
7 changed files with 171 additions and 36 deletions
File diff suppressed because one or more lines are too long
|
|
@ -209,6 +209,6 @@ contract Registrar {
|
||||||
// The number of confirmations needed before a checkpoint can be registered.
|
// The number of confirmations needed before a checkpoint can be registered.
|
||||||
// We have to make sure the checkpoint registered will not be invalid due to
|
// We have to make sure the checkpoint registered will not be invalid due to
|
||||||
// chain reorg.
|
// chain reorg.
|
||||||
uint constant confirmations = 500;
|
uint constant confirmations = 256;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,19 +32,14 @@ import (
|
||||||
var (
|
var (
|
||||||
// registrar contract address for mainnet and testnet.
|
// registrar contract address for mainnet and testnet.
|
||||||
RegistrarAddr = map[common.Hash]common.Address{
|
RegistrarAddr = map[common.Hash]common.Address{
|
||||||
params.MainnetGenesisHash: common.HexToAddress(""),
|
// params.MainnetGenesisHash: common.HexToAddress(""),
|
||||||
params.TestnetGenesisHash: common.HexToAddress(""),
|
// params.TestnetGenesisHash: common.HexToAddress(""),
|
||||||
params.RinkebyGenesisHash: common.HexToAddress("0xe3f2686a5d0c56a2d853c19c46b173a755263be8"),
|
params.RinkebyGenesisHash: common.HexToAddress("0xe3f2686a5d0c56a2d853c19c46b173a755263be8"),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
var errEventNotFound = errors.New("contract event not found")
|
var errEventNotFound = errors.New("contract event not found")
|
||||||
|
|
||||||
const (
|
|
||||||
sectionSize = 32768 // The frequency for creating a checkpoint
|
|
||||||
checkpointConfirmation = 500 // The number of confirmations needed before a checkpoint can be accepted
|
|
||||||
)
|
|
||||||
|
|
||||||
type Registrar struct {
|
type Registrar struct {
|
||||||
contract *contract.Contract
|
contract *contract.Contract
|
||||||
}
|
}
|
||||||
|
|
@ -67,15 +62,14 @@ func (registrar *Registrar) WatchNewCheckpointEvent(sink chan<- *contract.Contra
|
||||||
}
|
}
|
||||||
|
|
||||||
// FilterNewCheckpointEvent filters out NewCheckpointEvent for specific section number.
|
// FilterNewCheckpointEvent filters out NewCheckpointEvent for specific section number.
|
||||||
func (registrar *Registrar) FilterNewCheckpointEvent(head uint64, section uint64) (*contract.ContractNewCheckpointEventIterator, error) {
|
func (registrar *Registrar) FilterNewCheckpointEvent(head, section, sectionSize, processConfirm uint64) (*contract.ContractNewCheckpointEventIterator, error) {
|
||||||
start := (section + 1) * sectionSize
|
start := (section+1)*sectionSize + processConfirm
|
||||||
end := head - checkpointConfirmation
|
if head < start {
|
||||||
if end < start {
|
|
||||||
return nil, errEventNotFound
|
return nil, errEventNotFound
|
||||||
}
|
}
|
||||||
opt := &bind.FilterOpts{
|
opt := &bind.FilterOpts{
|
||||||
Start: start,
|
Start: start,
|
||||||
End: &end,
|
End: &head,
|
||||||
}
|
}
|
||||||
return registrar.contract.FilterNewCheckpointEvent(opt, []*big.Int{big.NewInt(int64(section))})
|
return registrar.contract.FilterNewCheckpointEvent(opt, []*big.Int{big.NewInt(int64(section))})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -213,9 +213,27 @@ func TestCheckpointRegister(t *testing.T) {
|
||||||
return nil
|
return nil
|
||||||
}, "register unstable checkpoint")
|
}, "register unstable checkpoint")
|
||||||
|
|
||||||
|
contractBackend.ShiftBlocks(light.CheckpointFrequency + light.CheckpointProcessConfirmations)
|
||||||
|
|
||||||
|
// Register by unauthorized user
|
||||||
|
validateOperation(t, c, contractBackend, func() {
|
||||||
|
user2, _ := crypto.GenerateKey()
|
||||||
|
unauthorized := bind.NewKeyedTransactor(user2)
|
||||||
|
c.SetCheckpoint(unauthorized, big.NewInt(int64(trustedCheckpoint.SectionIdx)), trustedCheckpoint.SectionHead,
|
||||||
|
trustedCheckpoint.ChtRoot, trustedCheckpoint.BloomTrieRoot)
|
||||||
|
}, func(events <-chan *contract.ContractNewCheckpointEvent, events2 <-chan *contract.ContractAddAdminEvent, events3 <-chan *contract.ContractRemoveAdminEvent) error {
|
||||||
|
hash, err := c.GetCheckpoint(nil, big.NewInt(int64(trustedCheckpoint.SectionIdx)))
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("get checkpoint failed")
|
||||||
|
}
|
||||||
|
if hash != emptyHash {
|
||||||
|
return errors.New("unstable checkpoint should be banned")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}, "register by unauthorized user")
|
||||||
|
|
||||||
// Register a stable checkpoint
|
// Register a stable checkpoint
|
||||||
validateOperation(t, c, contractBackend, func() {
|
validateOperation(t, c, contractBackend, func() {
|
||||||
contractBackend.ShiftBlocks(sectionSize + checkpointConfirmation)
|
|
||||||
c.SetCheckpoint(transactOpts, big.NewInt(int64(trustedCheckpoint.SectionIdx)), trustedCheckpoint.SectionHead,
|
c.SetCheckpoint(transactOpts, big.NewInt(int64(trustedCheckpoint.SectionIdx)), trustedCheckpoint.SectionHead,
|
||||||
trustedCheckpoint.ChtRoot, trustedCheckpoint.BloomTrieRoot)
|
trustedCheckpoint.ChtRoot, trustedCheckpoint.BloomTrieRoot)
|
||||||
}, func(events <-chan *contract.ContractNewCheckpointEvent, events2 <-chan *contract.ContractAddAdminEvent, events3 <-chan *contract.ContractRemoveAdminEvent) error {
|
}, func(events <-chan *contract.ContractNewCheckpointEvent, events2 <-chan *contract.ContractAddAdminEvent, events3 <-chan *contract.ContractRemoveAdminEvent) error {
|
||||||
|
|
@ -223,7 +241,7 @@ func TestCheckpointRegister(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("get checkpoint failed")
|
return errors.New("get checkpoint failed")
|
||||||
}
|
}
|
||||||
if common.Hash(hash).Hex() != crypto.Keccak256Hash(trustedCheckpoint.SectionHead.Bytes(), trustedCheckpoint.ChtRoot.Bytes(), trustedCheckpoint.BloomTrieRoot.Bytes()).Hex() {
|
if !trustedCheckpoint.HashEqual(common.Hash(hash)) {
|
||||||
return errors.New("register stable checkpoint failed")
|
return errors.New("register stable checkpoint failed")
|
||||||
}
|
}
|
||||||
if !validateEvents(1, events) {
|
if !validateEvents(1, events) {
|
||||||
|
|
@ -242,7 +260,7 @@ func TestCheckpointRegister(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("get checkpoint failed")
|
return errors.New("get checkpoint failed")
|
||||||
}
|
}
|
||||||
if common.Hash(hash).Hex() != crypto.Keccak256Hash(trustedCheckpoint.SectionHead.Bytes(), trustedCheckpoint.ChtRoot.Bytes(), trustedCheckpoint.BloomTrieRoot.Bytes()).Hex() {
|
if !trustedCheckpoint.HashEqual(common.Hash(hash)) {
|
||||||
return errors.New("register stable checkpoint failed")
|
return errors.New("register stable checkpoint failed")
|
||||||
}
|
}
|
||||||
if !validateEvents(1, events) {
|
if !validateEvents(1, events) {
|
||||||
|
|
|
||||||
126
les/server.go
126
les/server.go
|
|
@ -19,6 +19,7 @@ package les
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
|
"errors"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
@ -28,6 +29,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/eth"
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/les/flowcontrol"
|
"github.com/ethereum/go-ethereum/les/flowcontrol"
|
||||||
"github.com/ethereum/go-ethereum/light"
|
"github.com/ethereum/go-ethereum/light"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
|
@ -36,8 +38,13 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// chainHeadChanSize is the size of channel listening to ChainHeadEvent.
|
||||||
|
const SubscribeChainHeadEvent = 10
|
||||||
|
|
||||||
type LesServer struct {
|
type LesServer struct {
|
||||||
config *eth.Config
|
config *eth.Config
|
||||||
|
backend *eth.EthAPIBackend
|
||||||
|
chaindb ethdb.Database
|
||||||
protocolManager *ProtocolManager
|
protocolManager *ProtocolManager
|
||||||
fcManager *flowcontrol.ClientManager // nil if our node is client only
|
fcManager *flowcontrol.ClientManager // nil if our node is client only
|
||||||
fcCostStats *requestCostStats
|
fcCostStats *requestCostStats
|
||||||
|
|
@ -69,6 +76,8 @@ func NewLesServer(eth *eth.Ethereum, config *eth.Config) (*LesServer, error) {
|
||||||
|
|
||||||
srv := &LesServer{
|
srv := &LesServer{
|
||||||
config: config,
|
config: config,
|
||||||
|
backend: eth.APIBackend,
|
||||||
|
chaindb: eth.ChainDb(),
|
||||||
protocolManager: pm,
|
protocolManager: pm,
|
||||||
quitSync: quitSync,
|
quitSync: quitSync,
|
||||||
lesTopics: lesTopics,
|
lesTopics: lesTopics,
|
||||||
|
|
@ -85,14 +94,14 @@ func NewLesServer(eth *eth.Ethereum, config *eth.Config) (*LesServer, error) {
|
||||||
// convert last LES/2 section index back to LES/1 index for chtIndexer.SectionHead
|
// convert last LES/2 section index back to LES/1 index for chtIndexer.SectionHead
|
||||||
chtLastSectionV1 := (chtLastSection+1)*(light.CHTFrequencyClient/light.CHTFrequencyServer) - 1
|
chtLastSectionV1 := (chtLastSection+1)*(light.CHTFrequencyClient/light.CHTFrequencyServer) - 1
|
||||||
chtSectionHead := srv.chtIndexer.SectionHead(chtLastSectionV1)
|
chtSectionHead := srv.chtIndexer.SectionHead(chtLastSectionV1)
|
||||||
chtRoot := light.GetChtV2Root(pm.chainDb, chtLastSection, chtSectionHead)
|
chtRoot := light.GetChtV2Root(srv.chaindb, chtLastSection, chtSectionHead)
|
||||||
logger.Info("Loaded CHT", "section", chtLastSection, "head", chtSectionHead, "root", chtRoot)
|
logger.Info("Loaded CHT", "section", chtLastSection, "head", chtSectionHead, "root", chtRoot)
|
||||||
}
|
}
|
||||||
bloomTrieSectionCount, _, _ := srv.bloomTrieIndexer.Sections()
|
bloomTrieSectionCount, _, _ := srv.bloomTrieIndexer.Sections()
|
||||||
if bloomTrieSectionCount != 0 {
|
if bloomTrieSectionCount != 0 {
|
||||||
bloomTrieLastSection := bloomTrieSectionCount - 1
|
bloomTrieLastSection := bloomTrieSectionCount - 1
|
||||||
bloomTrieSectionHead := srv.bloomTrieIndexer.SectionHead(bloomTrieLastSection)
|
bloomTrieSectionHead := srv.bloomTrieIndexer.SectionHead(bloomTrieLastSection)
|
||||||
bloomTrieRoot := light.GetBloomTrieRoot(pm.chainDb, bloomTrieLastSection, bloomTrieSectionHead)
|
bloomTrieRoot := light.GetBloomTrieRoot(srv.chaindb, bloomTrieLastSection, bloomTrieSectionHead)
|
||||||
logger.Info("Loaded bloom trie", "section", bloomTrieLastSection, "head", bloomTrieSectionHead, "root", bloomTrieRoot)
|
logger.Info("Loaded bloom trie", "section", bloomTrieLastSection, "head", bloomTrieSectionHead, "root", bloomTrieRoot)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -137,6 +146,7 @@ func (s *LesServer) Start(srvr *p2p.Server) {
|
||||||
s.privateKey = srvr.PrivateKey
|
s.privateKey = srvr.PrivateKey
|
||||||
s.protocolManager.blockLoop()
|
s.protocolManager.blockLoop()
|
||||||
if s.registrar != nil {
|
if s.registrar != nil {
|
||||||
|
s.stableCheckpoint = s.recoverCheckpoint()
|
||||||
go s.checkpointLoop()
|
go s.checkpointLoop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -204,29 +214,66 @@ func (s *LesServer) getCheckpoint(index uint64) (common.Hash, common.Hash, commo
|
||||||
return sectionHead, chtRoot, bloomTrieRoot
|
return sectionHead, chtRoot, bloomTrieRoot
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkpointLoop starts a standalone goroutine to watch new checkpoint event and updates local's stable checkpoint.
|
// checkpointLoop starts a standalone goroutine to watch new checkpoint events and updates local's stable checkpoint.
|
||||||
func (s *LesServer) checkpointLoop() (err error) {
|
func (s *LesServer) checkpointLoop() (err error) {
|
||||||
sink := make(chan *contract.ContractNewCheckpointEvent)
|
var (
|
||||||
sub, err := s.registrar.WatchNewCheckpointEvent(sink)
|
eventCh = make(chan *contract.ContractNewCheckpointEvent)
|
||||||
|
headCh = make(chan core.ChainHeadEvent, SubscribeChainHeadEvent)
|
||||||
|
announcement = make(map[uint64]common.Hash)
|
||||||
|
)
|
||||||
|
eventSub, err := s.registrar.WatchNewCheckpointEvent(eventCh)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return err
|
||||||
|
}
|
||||||
|
headSub := s.backend.SubscribeChainHeadEvent(headCh)
|
||||||
|
if headSub == nil {
|
||||||
|
eventSub.Unsubscribe()
|
||||||
|
return errors.New("subscribe head event failed")
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
sub.Unsubscribe()
|
eventSub.Unsubscribe()
|
||||||
|
headSub.Unsubscribe()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case event := <-sink:
|
case event := <-eventCh:
|
||||||
// Note several duplicate events can be received because of latest checkpoint modification is allowed.
|
if event == nil {
|
||||||
// Always update local checkpoint when the section index is not less than the local one.
|
// This should never happen.
|
||||||
// todo(rjl493456442) update local checkpoint
|
log.Info("Ignore empty checkpoint event")
|
||||||
if event.Index.Uint64() >= s.stableCheckpoint.SectionIdx {
|
continue
|
||||||
log.Info("update checkpoint", "section", event.Index, "hash", common.Hash(event.CheckpointHash).Hex(),
|
}
|
||||||
"grantor", event.Grantor.Hex())
|
// Note several duplicate events may be received because of chain reorg and the modification of the latest checkpoint.
|
||||||
|
if s.stableCheckpoint == nil || event.Index.Uint64() >= s.stableCheckpoint.SectionIdx {
|
||||||
|
log.Info("Receive new checkpoint event", "section", event.Index, "hash", common.Hash(event.CheckpointHash).Hex(),
|
||||||
|
"grantor", event.Grantor.Hex())
|
||||||
|
announcement[event.Index.Uint64()] = common.Hash(event.CheckpointHash)
|
||||||
|
}
|
||||||
|
case head := <-headCh:
|
||||||
|
number := head.Block.NumberU64()
|
||||||
|
if number < light.CheckpointConfirmations+light.CheckpointFrequency {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
idx := (number-light.CheckpointConfirmations)/light.CheckpointFrequency - 1
|
||||||
|
if s.stableCheckpoint == nil || idx > s.stableCheckpoint.SectionIdx {
|
||||||
|
hash, ok := announcement[idx]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
sectionHead := s.bloomTrieIndexer.SectionHead(idx)
|
||||||
|
checkpoint := &light.TrustedCheckpoint{
|
||||||
|
SectionIdx: idx,
|
||||||
|
SectionHead: sectionHead,
|
||||||
|
ChtRoot: light.GetChtV2Root(s.chaindb, idx, sectionHead),
|
||||||
|
BloomTrieRoot: light.GetBloomTrieRoot(s.chaindb, idx, sectionHead),
|
||||||
|
}
|
||||||
|
if checkpoint.HashEqual(common.Hash(hash)) {
|
||||||
|
light.WriteTrustedCheckpoint(s.chaindb, checkpoint)
|
||||||
|
s.stableCheckpoint = checkpoint
|
||||||
|
log.Info("Update stable checkpoint", "section", checkpoint.SectionIdx)
|
||||||
|
delete(announcement, idx)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case <-s.quitSync:
|
case <-s.quitSync:
|
||||||
// Les server is closed.
|
// Les server is closed.
|
||||||
return
|
return
|
||||||
|
|
@ -234,6 +281,53 @@ func (s *LesServer) checkpointLoop() (err error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// recoveryCheckpoint filters checkpoint announcement events and recovers stable checkpoint.
|
||||||
|
func (s *LesServer) recoverCheckpoint() *light.TrustedCheckpoint {
|
||||||
|
var (
|
||||||
|
sectionCnt, _, _ = s.bloomTrieIndexer.Sections()
|
||||||
|
stable = light.ReadTrustedCheckpoint(s.chaindb)
|
||||||
|
unstableIdx = sectionCnt - 1
|
||||||
|
headHash = rawdb.ReadHeadHeaderHash(s.chaindb)
|
||||||
|
headNumber = rawdb.ReadHeaderNumber(s.chaindb, headHash)
|
||||||
|
)
|
||||||
|
if headNumber == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for stable == nil || stable.SectionIdx < unstableIdx {
|
||||||
|
if (unstableIdx+1)*light.CheckpointFrequency+light.CheckpointConfirmations <= *headNumber {
|
||||||
|
iter, err := s.registrar.FilterNewCheckpointEvent(*headNumber, unstableIdx, light.CheckpointFrequency, light.CheckpointProcessConfirmations)
|
||||||
|
if err == nil {
|
||||||
|
for iter.Next() {
|
||||||
|
sectionHead := s.bloomTrieIndexer.SectionHead(unstableIdx)
|
||||||
|
checkpoint := &light.TrustedCheckpoint{
|
||||||
|
SectionIdx: unstableIdx,
|
||||||
|
SectionHead: sectionHead,
|
||||||
|
ChtRoot: light.GetChtV2Root(s.chaindb, unstableIdx, sectionHead),
|
||||||
|
BloomTrieRoot: light.GetBloomTrieRoot(s.chaindb, unstableIdx, sectionHead),
|
||||||
|
}
|
||||||
|
if checkpoint.HashEqual(common.Hash(iter.Event.CheckpointHash)) {
|
||||||
|
light.WriteTrustedCheckpoint(s.chaindb, checkpoint)
|
||||||
|
iter.Close()
|
||||||
|
log.Info("Recover checkpoint", "index", checkpoint.SectionIdx)
|
||||||
|
return checkpoint
|
||||||
|
}
|
||||||
|
}
|
||||||
|
iter.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if unstableIdx == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
unstableIdx -= 1
|
||||||
|
}
|
||||||
|
if stable == nil {
|
||||||
|
log.Info("No stable checkpoint")
|
||||||
|
} else {
|
||||||
|
log.Info("Recover checkpoint", "index", stable.SectionIdx)
|
||||||
|
}
|
||||||
|
return stable
|
||||||
|
}
|
||||||
|
|
||||||
func (pm *ProtocolManager) blockLoop() {
|
func (pm *ProtocolManager) blockLoop() {
|
||||||
pm.wg.Add(1)
|
pm.wg.Add(1)
|
||||||
headCh := make(chan core.ChainHeadEvent, 10)
|
headCh := make(chan core.ChainHeadEvent, 10)
|
||||||
|
|
|
||||||
|
|
@ -20,17 +20,27 @@ import (
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
const (
|
||||||
// checkpointKey tracks the latest stable checkpoint.
|
// CheckpointFrequency is the block frequency for creating checkpoint
|
||||||
checkpointKey = []byte("Checkpoint")
|
CheckpointFrequency = 32768
|
||||||
|
|
||||||
|
// CheckpointProcessConfirmations is the number before a checkpoint is generated
|
||||||
|
CheckpointProcessConfirmations = 256
|
||||||
|
|
||||||
|
// CheckpointConfirmations is the number of confirmations before a checkpoint is stable
|
||||||
|
CheckpointConfirmations = 8192
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// checkpointKey tracks the latest stable checkpoint.
|
||||||
|
var checkpointKey = []byte("Checkpoint")
|
||||||
|
|
||||||
// TrustedCheckpoint represents a set of post-processed trie roots (CHT and BloomTrie) associated with
|
// TrustedCheckpoint represents a set of post-processed trie roots (CHT and BloomTrie) associated with
|
||||||
// the appropriate section index and head hash.
|
// the appropriate section index and head hash.
|
||||||
//
|
//
|
||||||
|
|
@ -68,6 +78,15 @@ func (c *TrustedCheckpoint) DecodeRLP(s *rlp.Stream) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HashEqual returns an indicator comparing the itself hash with given one.
|
||||||
|
// A nil argument is equivalent to an empty slice.
|
||||||
|
func (c *TrustedCheckpoint) HashEqual(hash common.Hash) bool {
|
||||||
|
if c.SectionHead == (common.Hash{}) && c.ChtRoot == (common.Hash{}) && c.BloomTrieRoot == (common.Hash{}) {
|
||||||
|
return hash == common.Hash{}
|
||||||
|
}
|
||||||
|
return crypto.Keccak256Hash(c.SectionHead.Bytes(), c.ChtRoot.Bytes(), c.BloomTrieRoot.Bytes()) == hash
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// Hardcode checkpoint for mainnet and testnet(ropsten). Will be deleted eventually once checkpoint contract
|
// Hardcode checkpoint for mainnet and testnet(ropsten). Will be deleted eventually once checkpoint contract
|
||||||
// works.
|
// works.
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,16 @@ func TestRWCheckpoint(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHashEqual(t *testing.T) {
|
||||||
|
if !testCheckpoint.HashEqual(common.HexToHash("0x6142a271d44a56107cd9de0be0a04211841593906b310f8c4d33be56b6e78959")) {
|
||||||
|
t.Error("checkpoint should hash equal to given one")
|
||||||
|
}
|
||||||
|
emptyCheckpoint := &TrustedCheckpoint{}
|
||||||
|
if !emptyCheckpoint.HashEqual(common.Hash{}) {
|
||||||
|
t.Error("empty checkpoint should equal to empty hash")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func assertCheckpointEqual(ckp1, ckp2 *TrustedCheckpoint) bool {
|
func assertCheckpointEqual(ckp1, ckp2 *TrustedCheckpoint) bool {
|
||||||
return ckp1.SectionIdx == ckp2.SectionIdx && ckp1.SectionHead == ckp2.SectionHead && ckp1.ChtRoot == ckp2.ChtRoot &&
|
return ckp1.SectionIdx == ckp2.SectionIdx && ckp1.SectionHead == ckp2.SectionHead && ckp1.ChtRoot == ckp2.ChtRoot &&
|
||||||
ckp1.BloomTrieRoot == ckp2.BloomTrieRoot
|
ckp1.BloomTrieRoot == ckp2.BloomTrieRoot
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue