mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 10:22:23 +00:00
contracts: modify checkpoint contract and rewrite unittests
This commit is contained in:
parent
88004ccbc2
commit
85e287fd88
5 changed files with 284 additions and 201 deletions
File diff suppressed because one or more lines are too long
|
|
@ -6,22 +6,6 @@ pragma solidity ^0.4.24;
|
||||||
* @dev Implementation of the blockchain checkpoint information registrar.
|
* @dev Implementation of the blockchain checkpoint information registrar.
|
||||||
*/
|
*/
|
||||||
contract Registrar {
|
contract Registrar {
|
||||||
/*
|
|
||||||
Definitions
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Checkpoint represents a set of post-processed trie roots (CHT and BloomTrie)
|
|
||||||
// associated with the appropriate section head hash.
|
|
||||||
//
|
|
||||||
// It is used to start light syncing from this checkpoint
|
|
||||||
// and avoid downloading the entire header chain while still being able to securely
|
|
||||||
// access old headers/logs.
|
|
||||||
struct Checkpoint {
|
|
||||||
bytes32 sectionHead;
|
|
||||||
bytes32 chtRoot;
|
|
||||||
bytes32 bloomTrieRoot;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Modifiers
|
Modifiers
|
||||||
*/
|
*/
|
||||||
|
|
@ -39,13 +23,17 @@ contract Registrar {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// NewCheckpointEvent is emitted when new checkpoint is registered.
|
// NewCheckpointEvent is emitted when new checkpoint is registered.
|
||||||
event NewCheckpointEvent(uint indexed index, bytes32 sectionHead, bytes32 chtRoot, bytes32 bloomTrieRoot);
|
// Grantor indicates the people register the checkpoint.
|
||||||
|
// We use checkpoint hash instead of the full checkpoint to make the transaction cheaper.
|
||||||
|
event NewCheckpointEvent(uint indexed index, address grantor, bytes32 checkpointHash);
|
||||||
|
|
||||||
// AddAdminEvent is emitted when new address is accepted as admin.
|
// AddAdminEvent is emitted when new address is accepted as admin.
|
||||||
event AddAdminEvent(address addr);
|
// Grantor indicates who authorizes the add admin operation.
|
||||||
|
event AddAdminEvent(address addr, address grantor, string description);
|
||||||
|
|
||||||
// RemoveAdminEvent is emitted when an admin is removed.
|
// RemoveAdminEvent is emitted when an admin is removed.
|
||||||
event RemoveAdminEvent(address addr);
|
// Grantor indicates who authorizes the remove admin operation.
|
||||||
|
event RemoveAdminEvent(address addr, address grantor, string reason);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Public Functions
|
Public Functions
|
||||||
|
|
@ -63,36 +51,37 @@ contract Registrar {
|
||||||
/**
|
/**
|
||||||
* @dev Get latest stable checkpoint information.
|
* @dev Get latest stable checkpoint information.
|
||||||
* @return section index
|
* @return section index
|
||||||
* @return section head
|
* @return checkpoint hash
|
||||||
* @return cht root hash
|
|
||||||
* @return bloom trie root hash
|
|
||||||
*/
|
*/
|
||||||
function GetLatestCheckpoint()
|
function GetLatestCheckpoint()
|
||||||
view
|
view
|
||||||
public
|
public
|
||||||
returns(uint, bytes32, bytes32, bytes32) {
|
returns(uint, bytes32) {
|
||||||
(bytes32 sectionHead, bytes32 chtRoot, bytes32 bloomRoot) = GetCheckpoint(latest);
|
bytes32 hash = GetCheckpoint(latest);
|
||||||
return (latest, sectionHead, chtRoot, bloomRoot);
|
return (latest, hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dev Get a stable checkpoint information with specified section index.
|
* @dev Get a stable checkpoint information with specified section index.
|
||||||
* @param _sectionIndex section index
|
* @param _sectionIndex section index
|
||||||
* @return section head
|
* @return checkpoint hash
|
||||||
* @return cht root hash
|
|
||||||
* @return bloom trie root hash
|
|
||||||
*/
|
*/
|
||||||
function GetCheckpoint(uint _sectionIndex)
|
function GetCheckpoint(uint _sectionIndex)
|
||||||
view
|
view
|
||||||
public
|
public
|
||||||
returns(bytes32, bytes32, bytes32)
|
returns(bytes32)
|
||||||
{
|
{
|
||||||
Checkpoint memory checkpoint = checkpoints[_sectionIndex];
|
return checkpoints[_sectionIndex];
|
||||||
return (checkpoint.sectionHead, checkpoint.chtRoot, checkpoint.bloomTrieRoot);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dev Set stable checkpoint information.
|
* @dev Set stable checkpoint information.
|
||||||
|
* Checkpoint represents a set of post-processed trie roots (CHT and BloomTrie)
|
||||||
|
* associated with the appropriate section head hash.
|
||||||
|
*
|
||||||
|
* It is used to start light syncing from this checkpoint
|
||||||
|
* and avoid downloading the entire header chain while still being able to securely
|
||||||
|
* access old headers/logs.
|
||||||
*
|
*
|
||||||
* Note we trust the given information here provided by foundation,
|
* Note we trust the given information here provided by foundation,
|
||||||
* need a trust less version for future.
|
* need a trust less version for future.
|
||||||
|
|
@ -113,7 +102,8 @@ contract Registrar {
|
||||||
returns(bool)
|
returns(bool)
|
||||||
{
|
{
|
||||||
// Ensure the checkpoint information provided is strictly continuous with previous one.
|
// Ensure the checkpoint information provided is strictly continuous with previous one.
|
||||||
if (_sectionIndex != latest + 1 && latest != 0) {
|
// But the latest checkpoint modification is allowed.
|
||||||
|
if (_sectionIndex != latest && _sectionIndex != latest + 1 && latest != 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Ensure the checkpoint is stable enough to be registered.
|
// Ensure the checkpoint is stable enough to be registered.
|
||||||
|
|
@ -121,14 +111,10 @@ contract Registrar {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
checkpoints[_sectionIndex] = Checkpoint({
|
checkpoints[_sectionIndex] = keccak256(abi.encodePacked(_sectionHead, _chtRoot, _bloomTrieRoot));
|
||||||
sectionHead: _sectionHead,
|
|
||||||
chtRoot: _chtRoot,
|
|
||||||
bloomTrieRoot: _bloomTrieRoot
|
|
||||||
});
|
|
||||||
latest = _sectionIndex;
|
latest = _sectionIndex;
|
||||||
|
|
||||||
emit NewCheckpointEvent(_sectionIndex, _sectionHead, _chtRoot, _bloomTrieRoot);
|
emit NewCheckpointEvent(_sectionIndex, msg.sender, checkpoints[_sectionIndex]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -136,7 +122,7 @@ contract Registrar {
|
||||||
* @param _addr specified new admin address.
|
* @param _addr specified new admin address.
|
||||||
* @return indicator whether add new admin successfully
|
* @return indicator whether add new admin successfully
|
||||||
*/
|
*/
|
||||||
function AddAdmin(address _addr)
|
function AddAdmin(address _addr, string _description)
|
||||||
OnlyAuthorized
|
OnlyAuthorized
|
||||||
public
|
public
|
||||||
returns(bool)
|
returns(bool)
|
||||||
|
|
@ -148,7 +134,7 @@ contract Registrar {
|
||||||
admins[_addr] = 1;
|
admins[_addr] = 1;
|
||||||
adminList.push(_addr);
|
adminList.push(_addr);
|
||||||
|
|
||||||
emit AddAdminEvent(_addr);
|
emit AddAdminEvent(_addr, msg.sender, _description);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -157,7 +143,7 @@ contract Registrar {
|
||||||
* @param _addr specified admin address to remove.
|
* @param _addr specified admin address to remove.
|
||||||
* @return indicator whether remove admin successfully
|
* @return indicator whether remove admin successfully
|
||||||
*/
|
*/
|
||||||
function RemoveAdmin(address _addr)
|
function RemoveAdmin(address _addr, string _reason)
|
||||||
OnlyAuthorized
|
OnlyAuthorized
|
||||||
public
|
public
|
||||||
returns(bool)
|
returns(bool)
|
||||||
|
|
@ -179,7 +165,7 @@ contract Registrar {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
emit RemoveAdminEvent(_addr);
|
emit RemoveAdminEvent(_addr, msg.sender, _reason);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -211,7 +197,7 @@ contract Registrar {
|
||||||
address[] adminList;
|
address[] adminList;
|
||||||
|
|
||||||
// Registered checkpoint information
|
// Registered checkpoint information
|
||||||
mapping(uint => Checkpoint) checkpoints;
|
mapping(uint => bytes32) checkpoints;
|
||||||
|
|
||||||
// Latest stored section id
|
// Latest stored section id
|
||||||
// Note all registered checkpoint information should continuous with previous one.
|
// Note all registered checkpoint information should continuous with previous one.
|
||||||
|
|
@ -221,6 +207,8 @@ contract Registrar {
|
||||||
uint constant sectionSize = 32768;
|
uint constant sectionSize = 32768;
|
||||||
|
|
||||||
// The number of confirmations needed before a checkpoint can be registered.
|
// The number of confirmations needed before a checkpoint can be registered.
|
||||||
uint constant confirmations = 10000;
|
// We have to make sure the checkpoint registered will not be invalid due to
|
||||||
|
// chain reorg.
|
||||||
|
uint constant confirmations = 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,11 +30,11 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// registrar contract address for mainnet or test chain.
|
// 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("0x3b934494985d17bcb49557671e1bc8ec32cccdd5"),
|
params.RinkebyGenesisHash: common.HexToAddress("0xe3f2686a5d0c56a2d853c19c46b173a755263be8"),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -42,7 +42,7 @@ var errEventNotFound = errors.New("contract event not found")
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sectionSize = 32768 // The frequency for creating a checkpoint
|
sectionSize = 32768 // The frequency for creating a checkpoint
|
||||||
checkpointConfirmation = 10000 // The number of confirmations needed before a checkpoint becoming stable.
|
checkpointConfirmation = 500 // The number of confirmations needed before a checkpoint can be accepted
|
||||||
)
|
)
|
||||||
|
|
||||||
type Registrar struct {
|
type Registrar struct {
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,11 @@
|
||||||
package registrar
|
package registrar
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"math/big"
|
"math/big"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||||
|
|
@ -43,101 +45,209 @@ var (
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// validateOperation executes the operation, watches and delivers all events fired by the backend and ensures the
|
||||||
|
// correctness by assert function.
|
||||||
|
func validateOperation(t *testing.T, c *contract.Contract, backend *backends.SimulatedBackend, operation func(),
|
||||||
|
assert func(<-chan *contract.ContractNewCheckpointEvent, <-chan *contract.ContractAddAdminEvent, <-chan *contract.ContractRemoveAdminEvent) error, opName string) {
|
||||||
|
// Watch all events and deliver them to assert function
|
||||||
|
var (
|
||||||
|
sink1 = make(chan *contract.ContractNewCheckpointEvent)
|
||||||
|
sink2 = make(chan *contract.ContractAddAdminEvent)
|
||||||
|
sink3 = make(chan *contract.ContractRemoveAdminEvent)
|
||||||
|
)
|
||||||
|
sub1, _ := c.WatchNewCheckpointEvent(nil, sink1, nil)
|
||||||
|
sub2, _ := c.WatchAddAdminEvent(nil, sink2)
|
||||||
|
sub3, _ := c.WatchRemoveAdminEvent(nil, sink3)
|
||||||
|
defer func() {
|
||||||
|
// Close all subscribers
|
||||||
|
sub1.Unsubscribe()
|
||||||
|
sub2.Unsubscribe()
|
||||||
|
sub3.Unsubscribe()
|
||||||
|
}()
|
||||||
|
operation()
|
||||||
|
|
||||||
|
// flush pending block
|
||||||
|
backend.Commit()
|
||||||
|
if err := assert(sink1, sink2, sink3); err != nil {
|
||||||
|
t.Errorf("operation {%s} failed, err %s", opName, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateEvents checks that the correct number of contract events
|
||||||
|
// fired by contract backend.
|
||||||
|
func validateEvents(target int, sink interface{}) bool {
|
||||||
|
chanval := reflect.ValueOf(sink)
|
||||||
|
chantyp := chanval.Type()
|
||||||
|
if chantyp.Kind() != reflect.Chan || chantyp.ChanDir()&reflect.RecvDir == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
count := 0
|
||||||
|
timeout := time.After(1 * time.Second)
|
||||||
|
cases := []reflect.SelectCase{{Chan: chanval, Dir: reflect.SelectRecv}, {Chan: reflect.ValueOf(timeout), Dir: reflect.SelectRecv}}
|
||||||
|
for {
|
||||||
|
chose, _, _ := reflect.Select(cases)
|
||||||
|
if chose == 1 {
|
||||||
|
// Not enough event received
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
count += 1
|
||||||
|
if count == target {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
done := time.After(50 * time.Millisecond)
|
||||||
|
cases = cases[:1]
|
||||||
|
cases = append(cases, reflect.SelectCase{Chan: reflect.ValueOf(done), Dir: reflect.SelectRecv})
|
||||||
|
chose, _, _ := reflect.Select(cases)
|
||||||
|
// If chose equal 0, it means receiving redundant events.
|
||||||
|
return chose == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests contract administrator managements.
|
||||||
func TestAdminManagement(t *testing.T) {
|
func TestAdminManagement(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
adminCandidate = common.HexToAddress("0x123")
|
adminCandidate = common.HexToAddress("0xdead")
|
||||||
adminCandidate2 = common.HexToAddress("0x456")
|
adminCandidate2 = common.HexToAddress("0xbeef")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Deploy registrar contract
|
// Deploy registrar contract
|
||||||
transactOpts := bind.NewKeyedTransactor(key)
|
transactOpts := bind.NewKeyedTransactor(key)
|
||||||
contractBackend := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(1000000000)}})
|
contractBackend := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(1000000000)}})
|
||||||
_, _, contract, err := contract.DeployContract(transactOpts, contractBackend, nil)
|
_, _, c, err := contract.DeployContract(transactOpts, contractBackend, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("deploy registrar contract failed", err)
|
t.Error("deploy registrar contract failed", err)
|
||||||
}
|
}
|
||||||
contractBackend.Commit()
|
contractBackend.Commit()
|
||||||
|
|
||||||
// Test AddAdmin function
|
// Test AddAdmin function
|
||||||
contract.AddAdmin(transactOpts, addr) // Contract should ignore the duplicate registration
|
validateOperation(t, c, contractBackend, func() {
|
||||||
contract.AddAdmin(transactOpts, adminCandidate)
|
for _, a := range []common.Address{addr, adminCandidate, adminCandidate2} {
|
||||||
contract.AddAdmin(transactOpts, adminCandidate2)
|
c.AddAdmin(transactOpts, a, "")
|
||||||
contractBackend.Commit()
|
}
|
||||||
adminList, err := contract.GetAllAdmin(nil)
|
}, func(sink1 <-chan *contract.ContractNewCheckpointEvent, sink2 <-chan *contract.ContractAddAdminEvent, sink3 <-chan *contract.ContractRemoveAdminEvent) error {
|
||||||
|
adminList, err := c.GetAllAdmin(nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("fetch admin list failed", err)
|
return errors.New("get admin list failed")
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(adminList, []common.Address{addr, adminCandidate, adminCandidate2}) {
|
if !reflect.DeepEqual(adminList, []common.Address{addr, adminCandidate, adminCandidate2}) {
|
||||||
t.Error("expect the returned admin list contain 3 address")
|
return errors.New("add admin failed")
|
||||||
}
|
}
|
||||||
|
if !validateEvents(2, sink2) {
|
||||||
|
return errors.New("receive incorrect number of events")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}, "add admin")
|
||||||
|
|
||||||
// Test RemoveAdmin function (remove at the middle)
|
// Test Remove admin function
|
||||||
contract.RemoveAdmin(transactOpts, adminCandidate)
|
validateOperation(t, c, contractBackend, func() {
|
||||||
contractBackend.Commit()
|
c.RemoveAdmin(transactOpts, adminCandidate, "")
|
||||||
adminList, err = contract.GetAllAdmin(nil)
|
}, func(events <-chan *contract.ContractNewCheckpointEvent, events2 <-chan *contract.ContractAddAdminEvent, events3 <-chan *contract.ContractRemoveAdminEvent) error {
|
||||||
|
adminList, err := c.GetAllAdmin(nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("fetch admin list failed", err)
|
return errors.New("get admin list failed")
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(adminList, []common.Address{addr, adminCandidate2}) {
|
if !reflect.DeepEqual(adminList, []common.Address{addr, adminCandidate2}) {
|
||||||
t.Error("expect the returned admin list contain 3 address")
|
return errors.New("remove admin failed")
|
||||||
}
|
}
|
||||||
|
if !validateEvents(1, events3) {
|
||||||
|
return errors.New("receive incorrect number of events")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}, "remove admin at middle")
|
||||||
|
|
||||||
// Test RemoveAdmin function (remove at the head)
|
// Test RemoveAdmin function (remove at the head)
|
||||||
contract.RemoveAdmin(transactOpts, addr)
|
validateOperation(t, c, contractBackend, func() {
|
||||||
contractBackend.Commit()
|
c.RemoveAdmin(transactOpts, addr, "")
|
||||||
adminList, err = contract.GetAllAdmin(nil)
|
}, func(events <-chan *contract.ContractNewCheckpointEvent, events2 <-chan *contract.ContractAddAdminEvent, events3 <-chan *contract.ContractRemoveAdminEvent) error {
|
||||||
|
adminList, err := c.GetAllAdmin(nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("fetch admin list failed", err)
|
return errors.New("get admin list failed")
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(adminList, []common.Address{adminCandidate2}) {
|
if !reflect.DeepEqual(adminList, []common.Address{adminCandidate2}) {
|
||||||
t.Error("expect the returned admin list contain 3 address")
|
return errors.New("remove admin failed")
|
||||||
}
|
}
|
||||||
|
if !validateEvents(1, events3) {
|
||||||
|
return errors.New("receive incorrect number of events")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}, "remove admin at head")
|
||||||
|
|
||||||
// Test unauthorized operation
|
// Test unauthorized operation
|
||||||
contract.AddAdmin(transactOpts, adminCandidate)
|
validateOperation(t, c, contractBackend, func() {
|
||||||
contractBackend.Commit()
|
c.AddAdmin(transactOpts, adminCandidate, "")
|
||||||
adminList, err = contract.GetAllAdmin(nil)
|
}, func(events <-chan *contract.ContractNewCheckpointEvent, events2 <-chan *contract.ContractAddAdminEvent, events3 <-chan *contract.ContractRemoveAdminEvent) error {
|
||||||
|
adminList, err := c.GetAllAdmin(nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("fetch admin list failed", err)
|
return errors.New("get admin list failed")
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(adminList, []common.Address{adminCandidate2}) {
|
if !reflect.DeepEqual(adminList, []common.Address{adminCandidate2}) {
|
||||||
t.Error("expect the returned admin list contain 3 address")
|
return errors.New("unauthorized operation should be banned")
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}, "unauthorized operation")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tests checkpoint managements.
|
||||||
func TestCheckpointRegister(t *testing.T) {
|
func TestCheckpointRegister(t *testing.T) {
|
||||||
// Deploy registrar contract
|
// Deploy registrar contract
|
||||||
transactOpts := bind.NewKeyedTransactor(key)
|
transactOpts := bind.NewKeyedTransactor(key)
|
||||||
contractBackend := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(1000000000)}})
|
contractBackend := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(1000000000)}})
|
||||||
_, _, contract, err := contract.DeployContract(transactOpts, contractBackend, []common.Address{addr})
|
_, _, c, err := contract.DeployContract(transactOpts, contractBackend, []common.Address{addr})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("deploy registrar contract failed", err)
|
t.Error("deploy registrar contract failed", err)
|
||||||
}
|
}
|
||||||
contractBackend.Commit()
|
contractBackend.Commit()
|
||||||
|
|
||||||
// Register an unstable checkpoint
|
// Register unstable checkpoint
|
||||||
contract.SetCheckpoint(transactOpts, big.NewInt(int64(trustedCheckpoint.SectionIdx)), trustedCheckpoint.SectionHead,
|
validateOperation(t, c, contractBackend, func() {
|
||||||
|
c.SetCheckpoint(transactOpts, big.NewInt(int64(trustedCheckpoint.SectionIdx)), trustedCheckpoint.SectionHead,
|
||||||
trustedCheckpoint.ChtRoot, trustedCheckpoint.BloomTrieRoot)
|
trustedCheckpoint.ChtRoot, trustedCheckpoint.BloomTrieRoot)
|
||||||
contractBackend.Commit()
|
}, func(events <-chan *contract.ContractNewCheckpointEvent, events2 <-chan *contract.ContractAddAdminEvent, events3 <-chan *contract.ContractRemoveAdminEvent) error {
|
||||||
head, chtRoot, bloomTrieRoot, err := contract.GetCheckpoint(nil, big.NewInt(int64(trustedCheckpoint.SectionIdx)))
|
hash, err := c.GetCheckpoint(nil, big.NewInt(int64(trustedCheckpoint.SectionIdx)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("fetch checkpoint failed", err)
|
return errors.New("get checkpoint failed")
|
||||||
}
|
}
|
||||||
if head != emptyHash || chtRoot != emptyHash || bloomTrieRoot != emptyHash {
|
if hash != emptyHash {
|
||||||
t.Error("the unstable checkpoint is not allowed to be registered")
|
return errors.New("unstable checkpoint should be banned")
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}, "register unstable checkpoint")
|
||||||
|
|
||||||
// Register a stable checkpoint
|
// Register a stable checkpoint
|
||||||
|
validateOperation(t, c, contractBackend, func() {
|
||||||
contractBackend.ShiftBlocks(sectionSize + checkpointConfirmation)
|
contractBackend.ShiftBlocks(sectionSize + checkpointConfirmation)
|
||||||
contract.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)
|
||||||
contractBackend.Commit()
|
}, func(events <-chan *contract.ContractNewCheckpointEvent, events2 <-chan *contract.ContractAddAdminEvent, events3 <-chan *contract.ContractRemoveAdminEvent) error {
|
||||||
head, chtRoot, bloomTrieRoot, err = contract.GetCheckpoint(nil, big.NewInt(int64(trustedCheckpoint.SectionIdx)))
|
hash, err := c.GetCheckpoint(nil, big.NewInt(int64(trustedCheckpoint.SectionIdx)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("fetch checkpoint failed", err)
|
return errors.New("get checkpoint failed")
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(head[:], trustedCheckpoint.SectionHead.Bytes()) || !reflect.DeepEqual(chtRoot[:], trustedCheckpoint.ChtRoot.Bytes()) ||
|
if common.Hash(hash).Hex() != crypto.Keccak256Hash(trustedCheckpoint.SectionHead.Bytes(), trustedCheckpoint.ChtRoot.Bytes(), trustedCheckpoint.BloomTrieRoot.Bytes()).Hex() {
|
||||||
!reflect.DeepEqual(bloomTrieRoot[:], trustedCheckpoint.BloomTrieRoot.Bytes()) {
|
return errors.New("register stable checkpoint failed")
|
||||||
t.Error("expect the returned checkpoint should be same with the given one")
|
|
||||||
}
|
}
|
||||||
|
if !validateEvents(1, events) {
|
||||||
|
return errors.New("receive incorrect number of events")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}, "register stable checkpoint")
|
||||||
|
|
||||||
|
// Modify the latest checkpoint
|
||||||
|
validateOperation(t, c, contractBackend, func() {
|
||||||
|
trustedCheckpoint.SectionHead = common.HexToHash("dead")
|
||||||
|
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 {
|
||||||
|
hash, err := c.GetCheckpoint(nil, big.NewInt(int64(trustedCheckpoint.SectionIdx)))
|
||||||
|
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() {
|
||||||
|
return errors.New("register stable checkpoint failed")
|
||||||
|
}
|
||||||
|
if !validateEvents(1, events) {
|
||||||
|
return errors.New("receive incorrect number of events")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}, "modify latest checkpoint")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -136,7 +136,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.checkpointLoop()
|
go s.checkpointLoop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -154,10 +154,9 @@ func (s *LesServer) Stop() {
|
||||||
<-s.protocolManager.noMorePeers
|
<-s.protocolManager.noMorePeers
|
||||||
}()
|
}()
|
||||||
s.protocolManager.Stop()
|
s.protocolManager.Stop()
|
||||||
close(s.quitSync)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkpointLoop starts a standalone goroutine to watch new checkpoint event and upgrades local's stable checkpoint.
|
// checkpointLoop starts a standalone goroutine to watch new checkpoint event and updates local's stable checkpoint.
|
||||||
func (s *LesServer) checkpointLoop() (err error) {
|
func (s *LesServer) checkpointLoop() (err error) {
|
||||||
sink := make(chan *contract.ContractNewCheckpointEvent)
|
sink := make(chan *contract.ContractNewCheckpointEvent)
|
||||||
sub, err := s.registrar.WatchNewCheckpointEvent(sink)
|
sub, err := s.registrar.WatchNewCheckpointEvent(sink)
|
||||||
|
|
@ -171,19 +170,12 @@ func (s *LesServer) checkpointLoop() (err error) {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case event := <-sink:
|
case event := <-sink:
|
||||||
// New stable checkpoint received
|
// Note several duplicate events can be received because of latest checkpoint modification is allowed.
|
||||||
// Note several duplicate events can be received due to chain reorg, just track the first arrive one.
|
// Always update local checkpoint when the section index is not less than the local one.
|
||||||
if event.Index.Uint64() > s.stableCheckpoint.SectionIdx {
|
// todo(rjl493456442) update local checkpoint
|
||||||
checkpoint := &light.TrustedCheckpoint{
|
if event.Index.Uint64() >= s.stableCheckpoint.SectionIdx {
|
||||||
SectionIdx: event.Index.Uint64(),
|
log.Info("update checkpoint", "section", event.Index, "hash", common.Hash(event.CheckpointHash).Hex(),
|
||||||
SectionHead: common.Hash(event.SectionHead),
|
"grantor", event.Grantor.Hex())
|
||||||
ChtRoot: common.Hash(event.ChtRoot),
|
|
||||||
BloomTrieRoot: common.Hash(event.BloomTrieRoot),
|
|
||||||
}
|
|
||||||
light.WriteTrustedCheckpoint(s.protocolManager.chainDb, checkpoint)
|
|
||||||
s.stableCheckpoint = checkpoint
|
|
||||||
log.Info("update checkpoint", "section", checkpoint.SectionIdx, "head", checkpoint.SectionHead.Hex(),
|
|
||||||
"chtRoot", checkpoint.ChtRoot.Hex(), "bloomTrieRoot", checkpoint.BloomTrieRoot.Hex())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case <-s.quitSync:
|
case <-s.quitSync:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue