From 536eb1632dab229b41a8c90b6c40685f9d82d2c1 Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Fri, 8 Jun 2018 10:41:31 +0800 Subject: [PATCH] les: update/recover stable checkpoint --- contracts/registrar/contract/registrar.go | 2 +- contracts/registrar/contract/registrar.sol | 2 +- contracts/registrar/registrar.go | 18 +-- contracts/registrar/registrar_test.go | 24 +++- les/server.go | 126 ++++++++++++++++++--- light/checkpoint.go | 25 +++- light/checkpoint_test.go | 10 ++ 7 files changed, 171 insertions(+), 36 deletions(-) diff --git a/contracts/registrar/contract/registrar.go b/contracts/registrar/contract/registrar.go index ef5f87e847..d79736abf0 100644 --- a/contracts/registrar/contract/registrar.go +++ b/contracts/registrar/contract/registrar.go @@ -19,7 +19,7 @@ import ( const ContractABI = "[{\"constant\":false,\"inputs\":[{\"name\":\"_addr\",\"type\":\"address\"},{\"name\":\"_description\",\"type\":\"string\"}],\"name\":\"AddAdmin\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"GetAllAdmin\",\"outputs\":[{\"name\":\"\",\"type\":\"address[]\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"GetLatestCheckpoint\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"},{\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_sectionIndex\",\"type\":\"uint256\"},{\"name\":\"_sectionHead\",\"type\":\"bytes32\"},{\"name\":\"_chtRoot\",\"type\":\"bytes32\"},{\"name\":\"_bloomTrieRoot\",\"type\":\"bytes32\"}],\"name\":\"SetCheckpoint\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_sectionIndex\",\"type\":\"uint256\"}],\"name\":\"GetCheckpoint\",\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_addr\",\"type\":\"address\"},{\"name\":\"_reason\",\"type\":\"string\"}],\"name\":\"RemoveAdmin\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"name\":\"_adminlist\",\"type\":\"address[]\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"index\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"grantor\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"checkpointHash\",\"type\":\"bytes32\"}],\"name\":\"NewCheckpointEvent\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"name\":\"addr\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"grantor\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"description\",\"type\":\"string\"}],\"name\":\"AddAdminEvent\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"name\":\"addr\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"grantor\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"reason\",\"type\":\"string\"}],\"name\":\"RemoveAdminEvent\",\"type\":\"event\"}]" // ContractBin is the compiled bytecode used for deploying new contracts. -const ContractBin = `608060405234801561001057600080fd5b50604051610e83380380610e8383398101806040528101908080518201929190505050600060016000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555060013390806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050600090505b81518110156101d2576001600080848481518110151561010057fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506001828281518110151561015857fe5b9060200190602002015190806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505080806001019150506100e4565b5050610ca0806101e36000396000f300608060405260043610610078576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680633561247d1461007d57806345848dfc1461011e5780634d6a304c1461018a578063651c7f49146101c4578063710aeac814610233578063a5ba0be21461027c575b600080fd5b34801561008957600080fd5b50610104600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001908201803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050919291929050505061031d565b604051808215151515815260200191505060405180910390f35b34801561012a57600080fd5b50610133610574565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561017657808201518184015260208101905061015b565b505050509050019250505060405180910390f35b34801561019657600080fd5b5061019f61065b565b6040518083815260200182600019166000191681526020019250505060405180910390f35b3480156101d057600080fd5b506102196004803603810190808035906020019092919080356000191690602001909291908035600019169060200190929190803560001916906020019092919050505061067a565b604051808215151515815260200191505060405180910390f35b34801561023f57600080fd5b5061025e60048036038101908080359060200190929190505050610874565b60405180826000191660001916815260200191505060405180910390f35b34801561028857600080fd5b50610303600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509192919290505050610891565b604051808215151515815260200191505060405180910390f35b6000806000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205411151561036b57600080fd5b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205411156103bb576000905061056e565b60016000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555060018390806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550507fecd88a3d10808aac9f879bbae95f9e4f9687f6c9f0615b88af2085f226d3cf93833384604051808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200180602001828103825283818151815260200191508051906020019080838360005b8381101561052d578082015181840152602081019050610512565b50505050905090810190601f16801561055a5780820380516001836020036101000a031916815260200191505b5094505050505060405180910390a1600190505b92915050565b60608060006001805490506040519080825280602002602001820160405280156105ad5781602001602082028038833980820191505090505b509150600090505b600180549050811015610653576001818154811015156105d157fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16828281518110151561060a57fe5b9060200190602002019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff168152505080806001019150506105b5565b819250505090565b600080600061066b600354610874565b90506003548192509250509091565b6000806000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541115156106c857600080fd5b60035485141580156106df57506001600354018514155b80156106ee5750600060035414155b156106fc576000905061086c565b6101f4618000600187010201431015610718576000905061086c565b8383836040516020018084600019166000191681526020018360001916600019168152602001826000191660001916815260200193505050506040516020818303038152906040526040518082805190602001908083835b6020831015156107955780518252602082019150602081019050602083039250610770565b6001836020036101000a038019825116818451168082178552505050505050905001915050604051809103902060026000878152602001908152602001600020816000191690555084600381905550847f5b3eab050ccf1a983a26b4f6acb13c199093c7d2e7523a0ff036519f7ddaf560336002600089815260200190815260200160002054604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182600019166000191681526020019250505060405180910390a25b949350505050565b600060026000838152602001908152602001600020549050919050565b6000806000806000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541115156108e257600080fd5b60008060008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205414156109325760009250610c1b565b6000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009055600091505b600180549050821015610b12578473ffffffffffffffffffffffffffffffffffffffff166001838154811015156109ac57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415610b05578190505b6001808054905003811015610aa6576001808201815481101515610a1757fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600182815481101515610a5157fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080806001019150506109f7565b600180808054905003815481101515610abb57fe5b9060005260206000200160006101000a81549073ffffffffffffffffffffffffffffffffffffffff021916905560018081818054905003915081610aff9190610c23565b50610b12565b8180600101925050610979565b7fe7d6ab069cde0507dc9c5971bf93e9fd183db04690725946e8cae907367e7c1f853386604051808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200180602001828103825283818151815260200191508051906020019080838360005b83811015610bda578082015181840152602081019050610bbf565b50505050905090810190601f168015610c075780820380516001836020036101000a031916815260200191505b5094505050505060405180910390a1600192505b505092915050565b815481835581811115610c4a57818360005260206000209182019101610c499190610c4f565b5b505050565b610c7191905b80821115610c6d576000816000905550600101610c55565b5090565b905600a165627a7a72305820791f0110aacecdc551cc4d3bda7cc4228762aa3acdce5f7b1b1d9efb1534b8320029` +const ContractBin = `608060405234801561001057600080fd5b50604051610e83380380610e8383398101806040528101908080518201929190505050600060016000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555060013390806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050600090505b81518110156101d2576001600080848481518110151561010057fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506001828281518110151561015857fe5b9060200190602002015190806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505080806001019150506100e4565b5050610ca0806101e36000396000f300608060405260043610610078576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680633561247d1461007d57806345848dfc1461011e5780634d6a304c1461018a578063651c7f49146101c4578063710aeac814610233578063a5ba0be21461027c575b600080fd5b34801561008957600080fd5b50610104600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001908201803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050919291929050505061031d565b604051808215151515815260200191505060405180910390f35b34801561012a57600080fd5b50610133610574565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561017657808201518184015260208101905061015b565b505050509050019250505060405180910390f35b34801561019657600080fd5b5061019f61065b565b6040518083815260200182600019166000191681526020019250505060405180910390f35b3480156101d057600080fd5b506102196004803603810190808035906020019092919080356000191690602001909291908035600019169060200190929190803560001916906020019092919050505061067a565b604051808215151515815260200191505060405180910390f35b34801561023f57600080fd5b5061025e60048036038101908080359060200190929190505050610874565b60405180826000191660001916815260200191505060405180910390f35b34801561028857600080fd5b50610303600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509192919290505050610891565b604051808215151515815260200191505060405180910390f35b6000806000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205411151561036b57600080fd5b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205411156103bb576000905061056e565b60016000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555060018390806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550507fecd88a3d10808aac9f879bbae95f9e4f9687f6c9f0615b88af2085f226d3cf93833384604051808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200180602001828103825283818151815260200191508051906020019080838360005b8381101561052d578082015181840152602081019050610512565b50505050905090810190601f16801561055a5780820380516001836020036101000a031916815260200191505b5094505050505060405180910390a1600190505b92915050565b60608060006001805490506040519080825280602002602001820160405280156105ad5781602001602082028038833980820191505090505b509150600090505b600180549050811015610653576001818154811015156105d157fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16828281518110151561060a57fe5b9060200190602002019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff168152505080806001019150506105b5565b819250505090565b600080600061066b600354610874565b90506003548192509250509091565b6000806000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541115156106c857600080fd5b60035485141580156106df57506001600354018514155b80156106ee5750600060035414155b156106fc576000905061086c565b610100618000600187010201431015610718576000905061086c565b8383836040516020018084600019166000191681526020018360001916600019168152602001826000191660001916815260200193505050506040516020818303038152906040526040518082805190602001908083835b6020831015156107955780518252602082019150602081019050602083039250610770565b6001836020036101000a038019825116818451168082178552505050505050905001915050604051809103902060026000878152602001908152602001600020816000191690555084600381905550847f5b3eab050ccf1a983a26b4f6acb13c199093c7d2e7523a0ff036519f7ddaf560336002600089815260200190815260200160002054604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182600019166000191681526020019250505060405180910390a25b949350505050565b600060026000838152602001908152602001600020549050919050565b6000806000806000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541115156108e257600080fd5b60008060008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205414156109325760009250610c1b565b6000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009055600091505b600180549050821015610b12578473ffffffffffffffffffffffffffffffffffffffff166001838154811015156109ac57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415610b05578190505b6001808054905003811015610aa6576001808201815481101515610a1757fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600182815481101515610a5157fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080806001019150506109f7565b600180808054905003815481101515610abb57fe5b9060005260206000200160006101000a81549073ffffffffffffffffffffffffffffffffffffffff021916905560018081818054905003915081610aff9190610c23565b50610b12565b8180600101925050610979565b7fe7d6ab069cde0507dc9c5971bf93e9fd183db04690725946e8cae907367e7c1f853386604051808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200180602001828103825283818151815260200191508051906020019080838360005b83811015610bda578082015181840152602081019050610bbf565b50505050905090810190601f168015610c075780820380516001836020036101000a031916815260200191505b5094505050505060405180910390a1600192505b505092915050565b815481835581811115610c4a57818360005260206000209182019101610c499190610c4f565b5b505050565b610c7191905b80821115610c6d576000816000905550600101610c55565b5090565b905600a165627a7a723058208a04962a64cdbd8ff23fe8ac9263f150ac5ef09bc46fe9c174e53bdd9f58e11d0029` // DeployContract deploys a new Ethereum contract, binding an instance of Contract to it. func DeployContract(auth *bind.TransactOpts, backend bind.ContractBackend, _adminlist []common.Address) (common.Address, *types.Transaction, *Contract, error) { diff --git a/contracts/registrar/contract/registrar.sol b/contracts/registrar/contract/registrar.sol index 37125bccb3..a5e1f8d0c8 100644 --- a/contracts/registrar/contract/registrar.sol +++ b/contracts/registrar/contract/registrar.sol @@ -209,6 +209,6 @@ contract Registrar { // 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 // chain reorg. - uint constant confirmations = 500; + uint constant confirmations = 256; } diff --git a/contracts/registrar/registrar.go b/contracts/registrar/registrar.go index f2059baf0d..dfa47570b1 100644 --- a/contracts/registrar/registrar.go +++ b/contracts/registrar/registrar.go @@ -32,19 +32,14 @@ import ( var ( // registrar contract address for mainnet and testnet. RegistrarAddr = map[common.Hash]common.Address{ - params.MainnetGenesisHash: common.HexToAddress(""), - params.TestnetGenesisHash: common.HexToAddress(""), + // params.MainnetGenesisHash: common.HexToAddress(""), + // params.TestnetGenesisHash: common.HexToAddress(""), params.RinkebyGenesisHash: common.HexToAddress("0xe3f2686a5d0c56a2d853c19c46b173a755263be8"), } ) 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 { contract *contract.Contract } @@ -67,15 +62,14 @@ func (registrar *Registrar) WatchNewCheckpointEvent(sink chan<- *contract.Contra } // FilterNewCheckpointEvent filters out NewCheckpointEvent for specific section number. -func (registrar *Registrar) FilterNewCheckpointEvent(head uint64, section uint64) (*contract.ContractNewCheckpointEventIterator, error) { - start := (section + 1) * sectionSize - end := head - checkpointConfirmation - if end < start { +func (registrar *Registrar) FilterNewCheckpointEvent(head, section, sectionSize, processConfirm uint64) (*contract.ContractNewCheckpointEventIterator, error) { + start := (section+1)*sectionSize + processConfirm + if head < start { return nil, errEventNotFound } opt := &bind.FilterOpts{ Start: start, - End: &end, + End: &head, } return registrar.contract.FilterNewCheckpointEvent(opt, []*big.Int{big.NewInt(int64(section))}) } diff --git a/contracts/registrar/registrar_test.go b/contracts/registrar/registrar_test.go index 9565cd9325..9add4c638d 100644 --- a/contracts/registrar/registrar_test.go +++ b/contracts/registrar/registrar_test.go @@ -213,9 +213,27 @@ func TestCheckpointRegister(t *testing.T) { return nil }, "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 validateOperation(t, c, contractBackend, func() { - contractBackend.ShiftBlocks(sectionSize + checkpointConfirmation) c.SetCheckpoint(transactOpts, 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 { @@ -223,7 +241,7 @@ func TestCheckpointRegister(t *testing.T) { if err != nil { 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") } if !validateEvents(1, events) { @@ -242,7 +260,7 @@ func TestCheckpointRegister(t *testing.T) { if err != nil { 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") } if !validateEvents(1, events) { diff --git a/les/server.go b/les/server.go index 7d935d1a31..5ad0fcb065 100644 --- a/les/server.go +++ b/les/server.go @@ -19,6 +19,7 @@ package les import ( "crypto/ecdsa" + "errors" "sync" "github.com/ethereum/go-ethereum/common" @@ -28,6 +29,7 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/les/flowcontrol" "github.com/ethereum/go-ethereum/light" "github.com/ethereum/go-ethereum/log" @@ -36,8 +38,13 @@ import ( "github.com/ethereum/go-ethereum/rpc" ) +// chainHeadChanSize is the size of channel listening to ChainHeadEvent. +const SubscribeChainHeadEvent = 10 + type LesServer struct { config *eth.Config + backend *eth.EthAPIBackend + chaindb ethdb.Database protocolManager *ProtocolManager fcManager *flowcontrol.ClientManager // nil if our node is client only fcCostStats *requestCostStats @@ -69,6 +76,8 @@ func NewLesServer(eth *eth.Ethereum, config *eth.Config) (*LesServer, error) { srv := &LesServer{ config: config, + backend: eth.APIBackend, + chaindb: eth.ChainDb(), protocolManager: pm, quitSync: quitSync, 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 chtLastSectionV1 := (chtLastSection+1)*(light.CHTFrequencyClient/light.CHTFrequencyServer) - 1 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) } bloomTrieSectionCount, _, _ := srv.bloomTrieIndexer.Sections() if bloomTrieSectionCount != 0 { bloomTrieLastSection := bloomTrieSectionCount - 1 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) } @@ -137,6 +146,7 @@ func (s *LesServer) Start(srvr *p2p.Server) { s.privateKey = srvr.PrivateKey s.protocolManager.blockLoop() if s.registrar != nil { + s.stableCheckpoint = s.recoverCheckpoint() go s.checkpointLoop() } } @@ -204,29 +214,66 @@ func (s *LesServer) getCheckpoint(index uint64) (common.Hash, common.Hash, commo 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) { - sink := make(chan *contract.ContractNewCheckpointEvent) - sub, err := s.registrar.WatchNewCheckpointEvent(sink) + var ( + 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 { - return - + return err + } + headSub := s.backend.SubscribeChainHeadEvent(headCh) + if headSub == nil { + eventSub.Unsubscribe() + return errors.New("subscribe head event failed") } defer func() { - sub.Unsubscribe() + eventSub.Unsubscribe() + headSub.Unsubscribe() }() for { select { - case event := <-sink: - // Note several duplicate events can be received because of latest checkpoint modification is allowed. - // Always update local checkpoint when the section index is not less than the local one. - // todo(rjl493456442) update local checkpoint - if event.Index.Uint64() >= s.stableCheckpoint.SectionIdx { - log.Info("update checkpoint", "section", event.Index, "hash", common.Hash(event.CheckpointHash).Hex(), - "grantor", event.Grantor.Hex()) + case event := <-eventCh: + if event == nil { + // This should never happen. + log.Info("Ignore empty checkpoint event") + continue + } + // 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: // Les server is closed. 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() { pm.wg.Add(1) headCh := make(chan core.ChainHeadEvent, 10) diff --git a/light/checkpoint.go b/light/checkpoint.go index 192471f30f..118cb646b8 100644 --- a/light/checkpoint.go +++ b/light/checkpoint.go @@ -20,17 +20,27 @@ import ( "io" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" ) -var ( - // checkpointKey tracks the latest stable checkpoint. - checkpointKey = []byte("Checkpoint") +const ( + // CheckpointFrequency is the block frequency for creating 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 // the appropriate section index and head hash. // @@ -68,6 +78,15 @@ func (c *TrustedCheckpoint) DecodeRLP(s *rlp.Stream) error { 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 ( // Hardcode checkpoint for mainnet and testnet(ropsten). Will be deleted eventually once checkpoint contract // works. diff --git a/light/checkpoint_test.go b/light/checkpoint_test.go index 483fb12724..3b419c5ea8 100644 --- a/light/checkpoint_test.go +++ b/light/checkpoint_test.go @@ -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 { return ckp1.SectionIdx == ckp2.SectionIdx && ckp1.SectionHead == ckp2.SectionHead && ckp1.ChtRoot == ckp2.ChtRoot && ckp1.BloomTrieRoot == ckp2.BloomTrieRoot