all: on-chain oracle checkpoint syncing (#19543)
* all: implement simple checkpoint syncing cmd, les, node: remove callback mechanism cmd, node: remove callback definition les: simplify the registrar les: expose checkpoint rpc services in the light client les, light: don't store untrusted receipt cmd, contracts, les: discard stale checkpoint cmd, contracts/registrar: loose restriction of registeration cmd, contracts: add replay-protection all: off-chain multi-signature contract params: deploy checkpoint contract for rinkeby cmd/registrar: add raw signing mode for registrar cmd/registrar, contracts/registrar, les: fixed messages * cmd/registrar, contracts/registrar: fix lints * accounts/abi/bind, les: address comments * cmd, contracts, les, light, params: minor checkpoint sync cleanups * cmd, eth, les, light: move checkpoint config to config file * cmd, eth, les, params: address comments * eth, les, params: address comments * cmd: polish up the checkpoint admin CLI * cmd, contracts, params: deploy new version contract * cmd/checkpoint-admin: add another flag for clef mode signing * cmd, contracts, les: rename and regen checkpoint oracle with abigen
This commit is contained in:
committed by
Péter Szilágyi
parent
702f52fb99
commit
f7cdea2bdc
415
contracts/checkpointoracle/contract/oracle.go
Normal file
415
contracts/checkpointoracle/contract/oracle.go
Normal file
@ -0,0 +1,415 @@
|
||||
// Code generated - DO NOT EDIT.
|
||||
// This file is a generated binding and any manual changes will be lost.
|
||||
|
||||
package contract
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"strings"
|
||||
|
||||
ethereum "github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var (
|
||||
_ = big.NewInt
|
||||
_ = strings.NewReader
|
||||
_ = ethereum.NotFound
|
||||
_ = abi.U256
|
||||
_ = bind.Bind
|
||||
_ = common.Big1
|
||||
_ = types.BloomLookup
|
||||
_ = event.NewSubscription
|
||||
)
|
||||
|
||||
// CheckpointOracleABI is the input ABI used to generate the binding from.
|
||||
const CheckpointOracleABI = "[{\"constant\":true,\"inputs\":[],\"name\":\"GetAllAdmin\",\"outputs\":[{\"name\":\"\",\"type\":\"address[]\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"GetLatestCheckpoint\",\"outputs\":[{\"name\":\"\",\"type\":\"uint64\"},{\"name\":\"\",\"type\":\"bytes32\"},{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_recentNumber\",\"type\":\"uint256\"},{\"name\":\"_recentHash\",\"type\":\"bytes32\"},{\"name\":\"_hash\",\"type\":\"bytes32\"},{\"name\":\"_sectionIndex\",\"type\":\"uint64\"},{\"name\":\"v\",\"type\":\"uint8[]\"},{\"name\":\"r\",\"type\":\"bytes32[]\"},{\"name\":\"s\",\"type\":\"bytes32[]\"}],\"name\":\"SetCheckpoint\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"name\":\"_adminlist\",\"type\":\"address[]\"},{\"name\":\"_sectionSize\",\"type\":\"uint256\"},{\"name\":\"_processConfirms\",\"type\":\"uint256\"},{\"name\":\"_threshold\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"index\",\"type\":\"uint64\"},{\"indexed\":false,\"name\":\"checkpointHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"v\",\"type\":\"uint8\"},{\"indexed\":false,\"name\":\"r\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"s\",\"type\":\"bytes32\"}],\"name\":\"NewCheckpointVote\",\"type\":\"event\"}]"
|
||||
|
||||
// CheckpointOracleBin is the compiled bytecode used for deploying new contracts.
|
||||
const CheckpointOracleBin = `0x608060405234801561001057600080fd5b506040516108153803806108158339818101604052608081101561003357600080fd5b81019080805164010000000081111561004b57600080fd5b8201602081018481111561005e57600080fd5b815185602082028301116401000000008211171561007b57600080fd5b505060208201516040830151606090930151919450925060005b84518110156101415760016000808784815181106100af57fe5b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060006101000a81548160ff02191690831515021790555060018582815181106100fc57fe5b60209081029190910181015182546001808201855560009485529290932090920180546001600160a01b0319166001600160a01b039093169290921790915501610095565b50600592909255600655600755506106b78061015e6000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806345848dfc146100465780634d6a304c1461009e578063d459fc46146100cf575b600080fd5b61004e6102b0565b60408051602080825283518183015283519192839290830191858101910280838360005b8381101561008a578181015183820152602001610072565b505050509050019250505060405180910390f35b6100a661034f565b6040805167ffffffffffffffff9094168452602084019290925282820152519081900360600190f35b61029c600480360360e08110156100e557600080fd5b81359160208101359160408201359167ffffffffffffffff6060820135169181019060a08101608082013564010000000081111561012257600080fd5b82018360208201111561013457600080fd5b8035906020019184602083028401116401000000008311171561015657600080fd5b91908080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525092959493602081019350359150506401000000008111156101a657600080fd5b8201836020820111156101b857600080fd5b803590602001918460208302840111640100000000831117156101da57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929594936020810193503591505064010000000081111561022a57600080fd5b82018360208201111561023c57600080fd5b8035906020019184602083028401116401000000008311171561025e57600080fd5b91908080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525092955061036a945050505050565b604080519115158252519081900360200190f35b6060806001805490506040519080825280602002602001820160405280156102e2578160200160208202803883390190505b50905060005b60015481101561034957600181815481106102ff57fe5b9060005260206000200160009054906101000a90046001600160a01b031682828151811061032957fe5b6001600160a01b03909216602092830291909101909101526001016102e8565b50905090565b60025460045460035467ffffffffffffffff90921691909192565b3360009081526020819052604081205460ff1661038657600080fd5b8688401461039357600080fd5b82518451146103a157600080fd5b81518451146103af57600080fd5b6006546005548660010167ffffffffffffffff1602014310156103d457506000610677565b60025467ffffffffffffffff90811690861610156103f457506000610677565b60025467ffffffffffffffff8681169116148015610426575067ffffffffffffffff8516151580610426575060035415155b1561043357506000610677565b8561044057506000610677565b60408051601960f81b6020808301919091526000602183018190523060601b60228401526001600160c01b031960c08a901b166036840152603e8084018b905284518085039091018152605e909301909352815191012090805b86518110156106715760006001848984815181106104b457fe5b60200260200101518985815181106104c857fe5b60200260200101518986815181106104dc57fe5b602002602001015160405160008152602001604052604051808581526020018460ff1660ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa15801561053b573d6000803e3d6000fd5b505060408051601f1901516001600160a01b03811660009081526020819052919091205490925060ff16905061057057600080fd5b826001600160a01b0316816001600160a01b03161161058e57600080fd5b8092508867ffffffffffffffff167fce51ffa16246bcaf0899f6504f473cd0114f430f566cef71ab7e03d3dde42a418b8a85815181106105ca57fe5b60200260200101518a86815181106105de57fe5b60200260200101518a87815181106105f257fe5b6020026020010151604051808581526020018460ff1660ff16815260200183815260200182815260200194505050505060405180910390a260075482600101106106685750505060048790555050436003556002805467ffffffffffffffff191667ffffffffffffffff86161790556001610677565b5060010161049a565b50600080fd5b97965050505050505056fea265627a7a723058207f6a191ce575596a2f1e907c8c0a01003d16b69fb2c4f432d10878e8c0a99a0264736f6c634300050a0032`
|
||||
|
||||
// DeployCheckpointOracle deploys a new Ethereum contract, binding an instance of CheckpointOracle to it.
|
||||
func DeployCheckpointOracle(auth *bind.TransactOpts, backend bind.ContractBackend, _adminlist []common.Address, _sectionSize *big.Int, _processConfirms *big.Int, _threshold *big.Int) (common.Address, *types.Transaction, *CheckpointOracle, error) {
|
||||
parsed, err := abi.JSON(strings.NewReader(CheckpointOracleABI))
|
||||
if err != nil {
|
||||
return common.Address{}, nil, nil, err
|
||||
}
|
||||
address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(CheckpointOracleBin), backend, _adminlist, _sectionSize, _processConfirms, _threshold)
|
||||
if err != nil {
|
||||
return common.Address{}, nil, nil, err
|
||||
}
|
||||
return address, tx, &CheckpointOracle{CheckpointOracleCaller: CheckpointOracleCaller{contract: contract}, CheckpointOracleTransactor: CheckpointOracleTransactor{contract: contract}, CheckpointOracleFilterer: CheckpointOracleFilterer{contract: contract}}, nil
|
||||
}
|
||||
|
||||
// CheckpointOracle is an auto generated Go binding around an Ethereum contract.
|
||||
type CheckpointOracle struct {
|
||||
CheckpointOracleCaller // Read-only binding to the contract
|
||||
CheckpointOracleTransactor // Write-only binding to the contract
|
||||
CheckpointOracleFilterer // Log filterer for contract events
|
||||
}
|
||||
|
||||
// CheckpointOracleCaller is an auto generated read-only Go binding around an Ethereum contract.
|
||||
type CheckpointOracleCaller struct {
|
||||
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
||||
}
|
||||
|
||||
// CheckpointOracleTransactor is an auto generated write-only Go binding around an Ethereum contract.
|
||||
type CheckpointOracleTransactor struct {
|
||||
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
||||
}
|
||||
|
||||
// CheckpointOracleFilterer is an auto generated log filtering Go binding around an Ethereum contract events.
|
||||
type CheckpointOracleFilterer struct {
|
||||
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
||||
}
|
||||
|
||||
// CheckpointOracleSession is an auto generated Go binding around an Ethereum contract,
|
||||
// with pre-set call and transact options.
|
||||
type CheckpointOracleSession struct {
|
||||
Contract *CheckpointOracle // Generic contract binding to set the session for
|
||||
CallOpts bind.CallOpts // Call options to use throughout this session
|
||||
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
|
||||
}
|
||||
|
||||
// CheckpointOracleCallerSession is an auto generated read-only Go binding around an Ethereum contract,
|
||||
// with pre-set call options.
|
||||
type CheckpointOracleCallerSession struct {
|
||||
Contract *CheckpointOracleCaller // Generic contract caller binding to set the session for
|
||||
CallOpts bind.CallOpts // Call options to use throughout this session
|
||||
}
|
||||
|
||||
// CheckpointOracleTransactorSession is an auto generated write-only Go binding around an Ethereum contract,
|
||||
// with pre-set transact options.
|
||||
type CheckpointOracleTransactorSession struct {
|
||||
Contract *CheckpointOracleTransactor // Generic contract transactor binding to set the session for
|
||||
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
|
||||
}
|
||||
|
||||
// CheckpointOracleRaw is an auto generated low-level Go binding around an Ethereum contract.
|
||||
type CheckpointOracleRaw struct {
|
||||
Contract *CheckpointOracle // Generic contract binding to access the raw methods on
|
||||
}
|
||||
|
||||
// CheckpointOracleCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract.
|
||||
type CheckpointOracleCallerRaw struct {
|
||||
Contract *CheckpointOracleCaller // Generic read-only contract binding to access the raw methods on
|
||||
}
|
||||
|
||||
// CheckpointOracleTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract.
|
||||
type CheckpointOracleTransactorRaw struct {
|
||||
Contract *CheckpointOracleTransactor // Generic write-only contract binding to access the raw methods on
|
||||
}
|
||||
|
||||
// NewCheckpointOracle creates a new instance of CheckpointOracle, bound to a specific deployed contract.
|
||||
func NewCheckpointOracle(address common.Address, backend bind.ContractBackend) (*CheckpointOracle, error) {
|
||||
contract, err := bindCheckpointOracle(address, backend, backend, backend)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &CheckpointOracle{CheckpointOracleCaller: CheckpointOracleCaller{contract: contract}, CheckpointOracleTransactor: CheckpointOracleTransactor{contract: contract}, CheckpointOracleFilterer: CheckpointOracleFilterer{contract: contract}}, nil
|
||||
}
|
||||
|
||||
// NewCheckpointOracleCaller creates a new read-only instance of CheckpointOracle, bound to a specific deployed contract.
|
||||
func NewCheckpointOracleCaller(address common.Address, caller bind.ContractCaller) (*CheckpointOracleCaller, error) {
|
||||
contract, err := bindCheckpointOracle(address, caller, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &CheckpointOracleCaller{contract: contract}, nil
|
||||
}
|
||||
|
||||
// NewCheckpointOracleTransactor creates a new write-only instance of CheckpointOracle, bound to a specific deployed contract.
|
||||
func NewCheckpointOracleTransactor(address common.Address, transactor bind.ContractTransactor) (*CheckpointOracleTransactor, error) {
|
||||
contract, err := bindCheckpointOracle(address, nil, transactor, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &CheckpointOracleTransactor{contract: contract}, nil
|
||||
}
|
||||
|
||||
// NewCheckpointOracleFilterer creates a new log filterer instance of CheckpointOracle, bound to a specific deployed contract.
|
||||
func NewCheckpointOracleFilterer(address common.Address, filterer bind.ContractFilterer) (*CheckpointOracleFilterer, error) {
|
||||
contract, err := bindCheckpointOracle(address, nil, nil, filterer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &CheckpointOracleFilterer{contract: contract}, nil
|
||||
}
|
||||
|
||||
// bindCheckpointOracle binds a generic wrapper to an already deployed contract.
|
||||
func bindCheckpointOracle(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
|
||||
parsed, err := abi.JSON(strings.NewReader(CheckpointOracleABI))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil
|
||||
}
|
||||
|
||||
// Call invokes the (constant) contract method with params as input values and
|
||||
// sets the output to result. The result type might be a single field for simple
|
||||
// returns, a slice of interfaces for anonymous returns and a struct for named
|
||||
// returns.
|
||||
func (_CheckpointOracle *CheckpointOracleRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error {
|
||||
return _CheckpointOracle.Contract.CheckpointOracleCaller.contract.Call(opts, result, method, params...)
|
||||
}
|
||||
|
||||
// Transfer initiates a plain transaction to move funds to the contract, calling
|
||||
// its default method if one is available.
|
||||
func (_CheckpointOracle *CheckpointOracleRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
|
||||
return _CheckpointOracle.Contract.CheckpointOracleTransactor.contract.Transfer(opts)
|
||||
}
|
||||
|
||||
// Transact invokes the (paid) contract method with params as input values.
|
||||
func (_CheckpointOracle *CheckpointOracleRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
|
||||
return _CheckpointOracle.Contract.CheckpointOracleTransactor.contract.Transact(opts, method, params...)
|
||||
}
|
||||
|
||||
// Call invokes the (constant) contract method with params as input values and
|
||||
// sets the output to result. The result type might be a single field for simple
|
||||
// returns, a slice of interfaces for anonymous returns and a struct for named
|
||||
// returns.
|
||||
func (_CheckpointOracle *CheckpointOracleCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error {
|
||||
return _CheckpointOracle.Contract.contract.Call(opts, result, method, params...)
|
||||
}
|
||||
|
||||
// Transfer initiates a plain transaction to move funds to the contract, calling
|
||||
// its default method if one is available.
|
||||
func (_CheckpointOracle *CheckpointOracleTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
|
||||
return _CheckpointOracle.Contract.contract.Transfer(opts)
|
||||
}
|
||||
|
||||
// Transact invokes the (paid) contract method with params as input values.
|
||||
func (_CheckpointOracle *CheckpointOracleTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
|
||||
return _CheckpointOracle.Contract.contract.Transact(opts, method, params...)
|
||||
}
|
||||
|
||||
// GetAllAdmin is a free data retrieval call binding the contract method 0x45848dfc.
|
||||
//
|
||||
// Solidity: function GetAllAdmin() constant returns(address[])
|
||||
func (_CheckpointOracle *CheckpointOracleCaller) GetAllAdmin(opts *bind.CallOpts) ([]common.Address, error) {
|
||||
var (
|
||||
ret0 = new([]common.Address)
|
||||
)
|
||||
out := ret0
|
||||
err := _CheckpointOracle.contract.Call(opts, out, "GetAllAdmin")
|
||||
return *ret0, err
|
||||
}
|
||||
|
||||
// GetAllAdmin is a free data retrieval call binding the contract method 0x45848dfc.
|
||||
//
|
||||
// Solidity: function GetAllAdmin() constant returns(address[])
|
||||
func (_CheckpointOracle *CheckpointOracleSession) GetAllAdmin() ([]common.Address, error) {
|
||||
return _CheckpointOracle.Contract.GetAllAdmin(&_CheckpointOracle.CallOpts)
|
||||
}
|
||||
|
||||
// GetAllAdmin is a free data retrieval call binding the contract method 0x45848dfc.
|
||||
//
|
||||
// Solidity: function GetAllAdmin() constant returns(address[])
|
||||
func (_CheckpointOracle *CheckpointOracleCallerSession) GetAllAdmin() ([]common.Address, error) {
|
||||
return _CheckpointOracle.Contract.GetAllAdmin(&_CheckpointOracle.CallOpts)
|
||||
}
|
||||
|
||||
// GetLatestCheckpoint is a free data retrieval call binding the contract method 0x4d6a304c.
|
||||
//
|
||||
// Solidity: function GetLatestCheckpoint() constant returns(uint64, bytes32, uint256)
|
||||
func (_CheckpointOracle *CheckpointOracleCaller) GetLatestCheckpoint(opts *bind.CallOpts) (uint64, [32]byte, *big.Int, error) {
|
||||
var (
|
||||
ret0 = new(uint64)
|
||||
ret1 = new([32]byte)
|
||||
ret2 = new(*big.Int)
|
||||
)
|
||||
out := &[]interface{}{
|
||||
ret0,
|
||||
ret1,
|
||||
ret2,
|
||||
}
|
||||
err := _CheckpointOracle.contract.Call(opts, out, "GetLatestCheckpoint")
|
||||
return *ret0, *ret1, *ret2, err
|
||||
}
|
||||
|
||||
// GetLatestCheckpoint is a free data retrieval call binding the contract method 0x4d6a304c.
|
||||
//
|
||||
// Solidity: function GetLatestCheckpoint() constant returns(uint64, bytes32, uint256)
|
||||
func (_CheckpointOracle *CheckpointOracleSession) GetLatestCheckpoint() (uint64, [32]byte, *big.Int, error) {
|
||||
return _CheckpointOracle.Contract.GetLatestCheckpoint(&_CheckpointOracle.CallOpts)
|
||||
}
|
||||
|
||||
// GetLatestCheckpoint is a free data retrieval call binding the contract method 0x4d6a304c.
|
||||
//
|
||||
// Solidity: function GetLatestCheckpoint() constant returns(uint64, bytes32, uint256)
|
||||
func (_CheckpointOracle *CheckpointOracleCallerSession) GetLatestCheckpoint() (uint64, [32]byte, *big.Int, error) {
|
||||
return _CheckpointOracle.Contract.GetLatestCheckpoint(&_CheckpointOracle.CallOpts)
|
||||
}
|
||||
|
||||
// SetCheckpoint is a paid mutator transaction binding the contract method 0xd459fc46.
|
||||
//
|
||||
// Solidity: function SetCheckpoint(uint256 _recentNumber, bytes32 _recentHash, bytes32 _hash, uint64 _sectionIndex, uint8[] v, bytes32[] r, bytes32[] s) returns(bool)
|
||||
func (_CheckpointOracle *CheckpointOracleTransactor) SetCheckpoint(opts *bind.TransactOpts, _recentNumber *big.Int, _recentHash [32]byte, _hash [32]byte, _sectionIndex uint64, v []uint8, r [][32]byte, s [][32]byte) (*types.Transaction, error) {
|
||||
return _CheckpointOracle.contract.Transact(opts, "SetCheckpoint", _recentNumber, _recentHash, _hash, _sectionIndex, v, r, s)
|
||||
}
|
||||
|
||||
// SetCheckpoint is a paid mutator transaction binding the contract method 0xd459fc46.
|
||||
//
|
||||
// Solidity: function SetCheckpoint(uint256 _recentNumber, bytes32 _recentHash, bytes32 _hash, uint64 _sectionIndex, uint8[] v, bytes32[] r, bytes32[] s) returns(bool)
|
||||
func (_CheckpointOracle *CheckpointOracleSession) SetCheckpoint(_recentNumber *big.Int, _recentHash [32]byte, _hash [32]byte, _sectionIndex uint64, v []uint8, r [][32]byte, s [][32]byte) (*types.Transaction, error) {
|
||||
return _CheckpointOracle.Contract.SetCheckpoint(&_CheckpointOracle.TransactOpts, _recentNumber, _recentHash, _hash, _sectionIndex, v, r, s)
|
||||
}
|
||||
|
||||
// SetCheckpoint is a paid mutator transaction binding the contract method 0xd459fc46.
|
||||
//
|
||||
// Solidity: function SetCheckpoint(uint256 _recentNumber, bytes32 _recentHash, bytes32 _hash, uint64 _sectionIndex, uint8[] v, bytes32[] r, bytes32[] s) returns(bool)
|
||||
func (_CheckpointOracle *CheckpointOracleTransactorSession) SetCheckpoint(_recentNumber *big.Int, _recentHash [32]byte, _hash [32]byte, _sectionIndex uint64, v []uint8, r [][32]byte, s [][32]byte) (*types.Transaction, error) {
|
||||
return _CheckpointOracle.Contract.SetCheckpoint(&_CheckpointOracle.TransactOpts, _recentNumber, _recentHash, _hash, _sectionIndex, v, r, s)
|
||||
}
|
||||
|
||||
// CheckpointOracleNewCheckpointVoteIterator is returned from FilterNewCheckpointVote and is used to iterate over the raw logs and unpacked data for NewCheckpointVote events raised by the CheckpointOracle contract.
|
||||
type CheckpointOracleNewCheckpointVoteIterator struct {
|
||||
Event *CheckpointOracleNewCheckpointVote // Event containing the contract specifics and raw log
|
||||
|
||||
contract *bind.BoundContract // Generic contract to use for unpacking event data
|
||||
event string // Event name to use for unpacking event data
|
||||
|
||||
logs chan types.Log // Log channel receiving the found contract events
|
||||
sub ethereum.Subscription // Subscription for errors, completion and termination
|
||||
done bool // Whether the subscription completed delivering logs
|
||||
fail error // Occurred error to stop iteration
|
||||
}
|
||||
|
||||
// Next advances the iterator to the subsequent event, returning whether there
|
||||
// are any more events found. In case of a retrieval or parsing error, false is
|
||||
// returned and Error() can be queried for the exact failure.
|
||||
func (it *CheckpointOracleNewCheckpointVoteIterator) Next() bool {
|
||||
// If the iterator failed, stop iterating
|
||||
if it.fail != nil {
|
||||
return false
|
||||
}
|
||||
// If the iterator completed, deliver directly whatever's available
|
||||
if it.done {
|
||||
select {
|
||||
case log := <-it.logs:
|
||||
it.Event = new(CheckpointOracleNewCheckpointVote)
|
||||
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
|
||||
it.fail = err
|
||||
return false
|
||||
}
|
||||
it.Event.Raw = log
|
||||
return true
|
||||
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Iterator still in progress, wait for either a data or an error event
|
||||
select {
|
||||
case log := <-it.logs:
|
||||
it.Event = new(CheckpointOracleNewCheckpointVote)
|
||||
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
|
||||
it.fail = err
|
||||
return false
|
||||
}
|
||||
it.Event.Raw = log
|
||||
return true
|
||||
|
||||
case err := <-it.sub.Err():
|
||||
it.done = true
|
||||
it.fail = err
|
||||
return it.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// Error returns any retrieval or parsing error occurred during filtering.
|
||||
func (it *CheckpointOracleNewCheckpointVoteIterator) Error() error {
|
||||
return it.fail
|
||||
}
|
||||
|
||||
// Close terminates the iteration process, releasing any pending underlying
|
||||
// resources.
|
||||
func (it *CheckpointOracleNewCheckpointVoteIterator) Close() error {
|
||||
it.sub.Unsubscribe()
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckpointOracleNewCheckpointVote represents a NewCheckpointVote event raised by the CheckpointOracle contract.
|
||||
type CheckpointOracleNewCheckpointVote struct {
|
||||
Index uint64
|
||||
CheckpointHash [32]byte
|
||||
V uint8
|
||||
R [32]byte
|
||||
S [32]byte
|
||||
Raw types.Log // Blockchain specific contextual infos
|
||||
}
|
||||
|
||||
// FilterNewCheckpointVote is a free log retrieval operation binding the contract event 0xce51ffa16246bcaf0899f6504f473cd0114f430f566cef71ab7e03d3dde42a41.
|
||||
//
|
||||
// Solidity: event NewCheckpointVote(uint64 indexed index, bytes32 checkpointHash, uint8 v, bytes32 r, bytes32 s)
|
||||
func (_CheckpointOracle *CheckpointOracleFilterer) FilterNewCheckpointVote(opts *bind.FilterOpts, index []uint64) (*CheckpointOracleNewCheckpointVoteIterator, error) {
|
||||
|
||||
var indexRule []interface{}
|
||||
for _, indexItem := range index {
|
||||
indexRule = append(indexRule, indexItem)
|
||||
}
|
||||
|
||||
logs, sub, err := _CheckpointOracle.contract.FilterLogs(opts, "NewCheckpointVote", indexRule)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &CheckpointOracleNewCheckpointVoteIterator{contract: _CheckpointOracle.contract, event: "NewCheckpointVote", logs: logs, sub: sub}, nil
|
||||
}
|
||||
|
||||
// WatchNewCheckpointVote is a free log subscription operation binding the contract event 0xce51ffa16246bcaf0899f6504f473cd0114f430f566cef71ab7e03d3dde42a41.
|
||||
//
|
||||
// Solidity: event NewCheckpointVote(uint64 indexed index, bytes32 checkpointHash, uint8 v, bytes32 r, bytes32 s)
|
||||
func (_CheckpointOracle *CheckpointOracleFilterer) WatchNewCheckpointVote(opts *bind.WatchOpts, sink chan<- *CheckpointOracleNewCheckpointVote, index []uint64) (event.Subscription, error) {
|
||||
|
||||
var indexRule []interface{}
|
||||
for _, indexItem := range index {
|
||||
indexRule = append(indexRule, indexItem)
|
||||
}
|
||||
|
||||
logs, sub, err := _CheckpointOracle.contract.WatchLogs(opts, "NewCheckpointVote", indexRule)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return event.NewSubscription(func(quit <-chan struct{}) error {
|
||||
defer sub.Unsubscribe()
|
||||
for {
|
||||
select {
|
||||
case log := <-logs:
|
||||
// New log arrived, parse the event and forward to the user
|
||||
event := new(CheckpointOracleNewCheckpointVote)
|
||||
if err := _CheckpointOracle.contract.UnpackLog(event, "NewCheckpointVote", log); err != nil {
|
||||
return err
|
||||
}
|
||||
event.Raw = log
|
||||
|
||||
select {
|
||||
case sink <- event:
|
||||
case err := <-sub.Err():
|
||||
return err
|
||||
case <-quit:
|
||||
return nil
|
||||
}
|
||||
case err := <-sub.Err():
|
||||
return err
|
||||
case <-quit:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}), nil
|
||||
}
|
||||
|
||||
// ParseNewCheckpointVote is a log parse operation binding the contract event 0xce51ffa16246bcaf0899f6504f473cd0114f430f566cef71ab7e03d3dde42a41.
|
||||
//
|
||||
// Solidity: event NewCheckpointVote(uint64 indexed index, bytes32 checkpointHash, uint8 v, bytes32 r, bytes32 s)
|
||||
func (_CheckpointOracle *CheckpointOracleFilterer) ParseNewCheckpointVote(log types.Log) (*CheckpointOracleNewCheckpointVote, error) {
|
||||
event := new(CheckpointOracleNewCheckpointVote)
|
||||
if err := _CheckpointOracle.contract.UnpackLog(event, "NewCheckpointVote", log); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return event, nil
|
||||
}
|
174
contracts/checkpointoracle/contract/oracle.sol
Normal file
174
contracts/checkpointoracle/contract/oracle.sol
Normal file
@ -0,0 +1,174 @@
|
||||
pragma solidity ^0.5.10;
|
||||
|
||||
/**
|
||||
* @title CheckpointOracle
|
||||
* @author Gary Rong<garyrong@ethereum.org>, Martin Swende <martin.swende@ethereum.org>
|
||||
* @dev Implementation of the blockchain checkpoint registrar.
|
||||
*/
|
||||
contract CheckpointOracle {
|
||||
/*
|
||||
Events
|
||||
*/
|
||||
|
||||
// NewCheckpointVote is emitted when a new checkpoint proposal receives a vote.
|
||||
event NewCheckpointVote(uint64 indexed index, bytes32 checkpointHash, uint8 v, bytes32 r, bytes32 s);
|
||||
|
||||
/*
|
||||
Public Functions
|
||||
*/
|
||||
constructor(address[] memory _adminlist, uint _sectionSize, uint _processConfirms, uint _threshold) public {
|
||||
for (uint i = 0; i < _adminlist.length; i++) {
|
||||
admins[_adminlist[i]] = true;
|
||||
adminList.push(_adminlist[i]);
|
||||
}
|
||||
sectionSize = _sectionSize;
|
||||
processConfirms = _processConfirms;
|
||||
threshold = _threshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Get latest stable checkpoint information.
|
||||
* @return section index
|
||||
* @return checkpoint hash
|
||||
* @return block height associated with checkpoint
|
||||
*/
|
||||
function GetLatestCheckpoint()
|
||||
view
|
||||
public
|
||||
returns(uint64, bytes32, uint) {
|
||||
return (sectionIndex, hash, height);
|
||||
}
|
||||
|
||||
// SetCheckpoint sets a new checkpoint. It accepts a list of signatures
|
||||
// @_recentNumber: a recent blocknumber, for replay protection
|
||||
// @_recentHash : the hash of `_recentNumber`
|
||||
// @_hash : the hash to set at _sectionIndex
|
||||
// @_sectionIndex : the section index to set
|
||||
// @v : the list of v-values
|
||||
// @r : the list or r-values
|
||||
// @s : the list of s-values
|
||||
function SetCheckpoint(
|
||||
uint _recentNumber,
|
||||
bytes32 _recentHash,
|
||||
bytes32 _hash,
|
||||
uint64 _sectionIndex,
|
||||
uint8[] memory v,
|
||||
bytes32[] memory r,
|
||||
bytes32[] memory s)
|
||||
public
|
||||
returns (bool)
|
||||
{
|
||||
// Ensure the sender is authorized.
|
||||
require(admins[msg.sender]);
|
||||
|
||||
// These checks replay protection, so it cannot be replayed on forks,
|
||||
// accidentally or intentionally
|
||||
require(blockhash(_recentNumber) == _recentHash);
|
||||
|
||||
// Ensure the batch of signatures are valid.
|
||||
require(v.length == r.length);
|
||||
require(v.length == s.length);
|
||||
|
||||
// Filter out "future" checkpoint.
|
||||
if (block.number < (_sectionIndex+1)*sectionSize+processConfirms) {
|
||||
return false;
|
||||
}
|
||||
// Filter out "old" announcement
|
||||
if (_sectionIndex < sectionIndex) {
|
||||
return false;
|
||||
}
|
||||
// Filter out "stale" announcement
|
||||
if (_sectionIndex == sectionIndex && (_sectionIndex != 0 || height != 0)) {
|
||||
return false;
|
||||
}
|
||||
// Filter out "invalid" announcement
|
||||
if (_hash == ""){
|
||||
return false;
|
||||
}
|
||||
|
||||
// EIP 191 style signatures
|
||||
//
|
||||
// Arguments when calculating hash to validate
|
||||
// 1: byte(0x19) - the initial 0x19 byte
|
||||
// 2: byte(0) - the version byte (data with intended validator)
|
||||
// 3: this - the validator address
|
||||
// -- Application specific data
|
||||
// 4 : checkpoint section_index(uint64)
|
||||
// 5 : checkpoint hash (bytes32)
|
||||
// hash = keccak256(checkpoint_index, section_head, cht_root, bloom_root)
|
||||
bytes32 signedHash = keccak256(abi.encodePacked(byte(0x19), byte(0), this, _sectionIndex, _hash));
|
||||
|
||||
address lastVoter = address(0);
|
||||
|
||||
// In order for us not to have to maintain a mapping of who has already
|
||||
// voted, and we don't want to count a vote twice, the signatures must
|
||||
// be submitted in strict ordering.
|
||||
for (uint idx = 0; idx < v.length; idx++){
|
||||
address signer = ecrecover(signedHash, v[idx], r[idx], s[idx]);
|
||||
require(admins[signer]);
|
||||
require(uint256(signer) > uint256(lastVoter));
|
||||
lastVoter = signer;
|
||||
emit NewCheckpointVote(_sectionIndex, _hash, v[idx], r[idx], s[idx]);
|
||||
|
||||
// Sufficient signatures present, update latest checkpoint.
|
||||
if (idx+1 >= threshold){
|
||||
hash = _hash;
|
||||
height = block.number;
|
||||
sectionIndex = _sectionIndex;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// We shouldn't wind up here, reverting un-emits the events
|
||||
revert();
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Get all admin addresses
|
||||
* @return address list
|
||||
*/
|
||||
function GetAllAdmin()
|
||||
public
|
||||
view
|
||||
returns(address[] memory)
|
||||
{
|
||||
address[] memory ret = new address[](adminList.length);
|
||||
for (uint i = 0; i < adminList.length; i++) {
|
||||
ret[i] = adminList[i];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
Fields
|
||||
*/
|
||||
// A map of admin users who have the permission to update CHT and bloom Trie root
|
||||
mapping(address => bool) admins;
|
||||
|
||||
// A list of admin users so that we can obtain all admin users.
|
||||
address[] adminList;
|
||||
|
||||
// Latest stored section id
|
||||
uint64 sectionIndex;
|
||||
|
||||
// The block height associated with latest registered checkpoint.
|
||||
uint height;
|
||||
|
||||
// The hash of latest registered checkpoint.
|
||||
bytes32 hash;
|
||||
|
||||
// The frequency for creating a checkpoint
|
||||
//
|
||||
// The default value should be the same as the checkpoint size(32768) in the ethereum.
|
||||
uint sectionSize;
|
||||
|
||||
// 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.
|
||||
//
|
||||
// The default value should be the same as the checkpoint process confirmations(256)
|
||||
// in the ethereum.
|
||||
uint processConfirms;
|
||||
|
||||
// The required signatures to finalize a stable checkpoint.
|
||||
uint threshold;
|
||||
}
|
91
contracts/checkpointoracle/oracle.go
Normal file
91
contracts/checkpointoracle/oracle.go
Normal file
@ -0,0 +1,91 @@
|
||||
// Copyright 2018 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Package checkpointoracle is a an on-chain light client checkpoint oracle.
|
||||
package checkpointoracle
|
||||
|
||||
//go:generate abigen --sol contract/oracle.sol --pkg contract --out contract/oracle.go
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/contracts/checkpointoracle/contract"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
)
|
||||
|
||||
// CheckpointOracle is a Go wrapper around an on-chain light client checkpoint oracle.
|
||||
type CheckpointOracle struct {
|
||||
contract *contract.CheckpointOracle
|
||||
}
|
||||
|
||||
// NewCheckpointOracle binds checkpoint contract and returns a registrar instance.
|
||||
func NewCheckpointOracle(contractAddr common.Address, backend bind.ContractBackend) (*CheckpointOracle, error) {
|
||||
c, err := contract.NewCheckpointOracle(contractAddr, backend)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &CheckpointOracle{contract: c}, nil
|
||||
}
|
||||
|
||||
// Contract returns the underlying contract instance.
|
||||
func (oracle *CheckpointOracle) Contract() *contract.CheckpointOracle {
|
||||
return oracle.contract
|
||||
}
|
||||
|
||||
// LookupCheckpointEvents searches checkpoint event for specific section in the
|
||||
// given log batches.
|
||||
func (oracle *CheckpointOracle) LookupCheckpointEvents(blockLogs [][]*types.Log, section uint64, hash common.Hash) []*contract.CheckpointOracleNewCheckpointVote {
|
||||
var votes []*contract.CheckpointOracleNewCheckpointVote
|
||||
|
||||
for _, logs := range blockLogs {
|
||||
for _, log := range logs {
|
||||
event, err := oracle.contract.ParseNewCheckpointVote(*log)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if event.Index == section && common.Hash(event.CheckpointHash) == hash {
|
||||
votes = append(votes, event)
|
||||
}
|
||||
}
|
||||
}
|
||||
return votes
|
||||
}
|
||||
|
||||
// RegisterCheckpoint registers the checkpoint with a batch of associated signatures
|
||||
// that are collected off-chain and sorted by lexicographical order.
|
||||
//
|
||||
// Notably all signatures given should be transformed to "ethereum style" which transforms
|
||||
// v from 0/1 to 27/28 according to the yellow paper.
|
||||
func (oracle *CheckpointOracle) RegisterCheckpoint(key *ecdsa.PrivateKey, index uint64, hash []byte, rnum *big.Int, rhash [32]byte, sigs [][]byte) (*types.Transaction, error) {
|
||||
var (
|
||||
r [][32]byte
|
||||
s [][32]byte
|
||||
v []uint8
|
||||
)
|
||||
for i := 0; i < len(sigs); i++ {
|
||||
if len(sigs[i]) != 65 {
|
||||
return nil, errors.New("invalid signature")
|
||||
}
|
||||
r = append(r, common.BytesToHash(sigs[i][:32]))
|
||||
s = append(s, common.BytesToHash(sigs[i][32:64]))
|
||||
v = append(v, sigs[i][64])
|
||||
}
|
||||
return oracle.contract.SetCheckpoint(bind.NewKeyedTransactor(key), rnum, rhash, common.BytesToHash(hash), index, v, r, s)
|
||||
}
|
333
contracts/checkpointoracle/oracle_test.go
Normal file
333
contracts/checkpointoracle/oracle_test.go
Normal file
@ -0,0 +1,333 @@
|
||||
// Copyright 2018 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package checkpointoracle
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ecdsa"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/contracts/checkpointoracle/contract"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
var (
|
||||
emptyHash = [32]byte{}
|
||||
|
||||
checkpoint0 = params.TrustedCheckpoint{
|
||||
SectionIndex: 0,
|
||||
SectionHead: common.HexToHash("0x7fa3c32f996c2bfb41a1a65b3d8ea3e0a33a1674cde43678ad6f4235e764d17d"),
|
||||
CHTRoot: common.HexToHash("0x98fc5d3de23a0fecebad236f6655533c157d26a1aedcd0852a514dc1169e6350"),
|
||||
BloomRoot: common.HexToHash("0x99b5adb52b337fe25e74c1c6d3835b896bd638611b3aebddb2317cce27a3f9fa"),
|
||||
}
|
||||
checkpoint1 = params.TrustedCheckpoint{
|
||||
SectionIndex: 1,
|
||||
SectionHead: common.HexToHash("0x2d4dee68102125e59b0cc61b176bd89f0d12b3b91cfaf52ef8c2c82fb920c2d2"),
|
||||
CHTRoot: common.HexToHash("0x7d428008ece3b4c4ef5439f071930aad0bb75108d381308df73beadcd01ded95"),
|
||||
BloomRoot: common.HexToHash("0x652571f7736de17e7bbb427ac881474da684c6988a88bf51b10cca9a2ee148f4"),
|
||||
}
|
||||
checkpoint2 = params.TrustedCheckpoint{
|
||||
SectionIndex: 2,
|
||||
SectionHead: common.HexToHash("0x61c0de578c0115b1dff8ef39aa600588c7c6ecb8a2f102003d7cf4c4146e9291"),
|
||||
CHTRoot: common.HexToHash("0x407a08a407a2bc3838b74ca3eb206903c9c8a186ccf5ef14af07794efff1970b"),
|
||||
BloomRoot: common.HexToHash("0x058b4161f558ce295a92925efc57f34f9210d5a30088d7475c183e0d3e58f5ac"),
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
// The block frequency for creating checkpoint(only used in test)
|
||||
sectionSize = big.NewInt(512)
|
||||
|
||||
// The number of confirmations needed to generate a checkpoint(only used in test).
|
||||
processConfirms = big.NewInt(4)
|
||||
)
|
||||
|
||||
// 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.CheckpointOracle, backend *backends.SimulatedBackend, operation func(),
|
||||
assert func(<-chan *contract.CheckpointOracleNewCheckpointVote) error, opName string) {
|
||||
// Watch all events and deliver them to assert function
|
||||
var (
|
||||
sink = make(chan *contract.CheckpointOracleNewCheckpointVote)
|
||||
sub, _ = c.WatchNewCheckpointVote(nil, sink, nil)
|
||||
)
|
||||
defer func() {
|
||||
// Close all subscribers
|
||||
sub.Unsubscribe()
|
||||
}()
|
||||
operation()
|
||||
|
||||
// flush pending block
|
||||
backend.Commit()
|
||||
if err := assert(sink); 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, []reflect.Value) {
|
||||
chanval := reflect.ValueOf(sink)
|
||||
chantyp := chanval.Type()
|
||||
if chantyp.Kind() != reflect.Chan || chantyp.ChanDir()&reflect.RecvDir == 0 {
|
||||
return false, nil
|
||||
}
|
||||
count := 0
|
||||
var recv []reflect.Value
|
||||
timeout := time.After(1 * time.Second)
|
||||
cases := []reflect.SelectCase{{Chan: chanval, Dir: reflect.SelectRecv}, {Chan: reflect.ValueOf(timeout), Dir: reflect.SelectRecv}}
|
||||
for {
|
||||
chose, v, _ := reflect.Select(cases)
|
||||
if chose == 1 {
|
||||
// Not enough event received
|
||||
return false, nil
|
||||
}
|
||||
count += 1
|
||||
recv = append(recv, v)
|
||||
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, recv
|
||||
}
|
||||
|
||||
func signCheckpoint(addr common.Address, privateKey *ecdsa.PrivateKey, index uint64, hash common.Hash) []byte {
|
||||
// EIP 191 style signatures
|
||||
//
|
||||
// Arguments when calculating hash to validate
|
||||
// 1: byte(0x19) - the initial 0x19 byte
|
||||
// 2: byte(0) - the version byte (data with intended validator)
|
||||
// 3: this - the validator address
|
||||
// -- Application specific data
|
||||
// 4 : checkpoint section_index(uint64)
|
||||
// 5 : checkpoint hash (bytes32)
|
||||
// hash = keccak256(checkpoint_index, section_head, cht_root, bloom_root)
|
||||
buf := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(buf, index)
|
||||
data := append([]byte{0x19, 0x00}, append(addr.Bytes(), append(buf, hash.Bytes()...)...)...)
|
||||
sig, _ := crypto.Sign(crypto.Keccak256(data), privateKey)
|
||||
sig[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper
|
||||
return sig
|
||||
}
|
||||
|
||||
// assertSignature verifies whether the recovered signers are equal with expected.
|
||||
func assertSignature(addr common.Address, index uint64, hash [32]byte, r, s [32]byte, v uint8, expect common.Address) bool {
|
||||
buf := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(buf, index)
|
||||
data := append([]byte{0x19, 0x00}, append(addr.Bytes(), append(buf, hash[:]...)...)...)
|
||||
pubkey, err := crypto.Ecrecover(crypto.Keccak256(data), append(r[:], append(s[:], v-27)...))
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
var signer common.Address
|
||||
copy(signer[:], crypto.Keccak256(pubkey[1:])[12:])
|
||||
return bytes.Equal(signer.Bytes(), expect.Bytes())
|
||||
}
|
||||
|
||||
type Account struct {
|
||||
key *ecdsa.PrivateKey
|
||||
addr common.Address
|
||||
}
|
||||
type Accounts []Account
|
||||
|
||||
func (a Accounts) Len() int { return len(a) }
|
||||
func (a Accounts) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a Accounts) Less(i, j int) bool { return bytes.Compare(a[i].addr.Bytes(), a[j].addr.Bytes()) < 0 }
|
||||
|
||||
func TestCheckpointRegister(t *testing.T) {
|
||||
// Initialize test accounts
|
||||
var accounts Accounts
|
||||
for i := 0; i < 3; i++ {
|
||||
key, _ := crypto.GenerateKey()
|
||||
addr := crypto.PubkeyToAddress(key.PublicKey)
|
||||
accounts = append(accounts, Account{key: key, addr: addr})
|
||||
}
|
||||
sort.Sort(accounts)
|
||||
|
||||
// Deploy registrar contract
|
||||
transactOpts := bind.NewKeyedTransactor(accounts[0].key)
|
||||
contractBackend := backends.NewSimulatedBackend(core.GenesisAlloc{accounts[0].addr: {Balance: big.NewInt(1000000000)}, accounts[1].addr: {Balance: big.NewInt(1000000000)}, accounts[2].addr: {Balance: big.NewInt(1000000000)}}, 10000000)
|
||||
// 3 trusted signers, threshold 2
|
||||
contractAddr, _, c, err := contract.DeployCheckpointOracle(transactOpts, contractBackend, []common.Address{accounts[0].addr, accounts[1].addr, accounts[2].addr}, sectionSize, processConfirms, big.NewInt(2))
|
||||
if err != nil {
|
||||
t.Error("Failed to deploy registrar contract", err)
|
||||
}
|
||||
contractBackend.Commit()
|
||||
|
||||
// getRecent returns block height and hash of the head parent.
|
||||
getRecent := func() (*big.Int, common.Hash) {
|
||||
parentNumber := new(big.Int).Sub(contractBackend.Blockchain().CurrentHeader().Number, big.NewInt(1))
|
||||
parentHash := contractBackend.Blockchain().CurrentHeader().ParentHash
|
||||
return parentNumber, parentHash
|
||||
}
|
||||
// collectSig generates specified number signatures.
|
||||
collectSig := func(index uint64, hash common.Hash, n int, unauthorized *ecdsa.PrivateKey) (v []uint8, r [][32]byte, s [][32]byte) {
|
||||
for i := 0; i < n; i++ {
|
||||
sig := signCheckpoint(contractAddr, accounts[i].key, index, hash)
|
||||
if unauthorized != nil {
|
||||
sig = signCheckpoint(contractAddr, unauthorized, index, hash)
|
||||
}
|
||||
r = append(r, common.BytesToHash(sig[:32]))
|
||||
s = append(s, common.BytesToHash(sig[32:64]))
|
||||
v = append(v, sig[64])
|
||||
}
|
||||
return v, r, s
|
||||
}
|
||||
// insertEmptyBlocks inserts a batch of empty blocks to blockchain.
|
||||
insertEmptyBlocks := func(number int) {
|
||||
for i := 0; i < number; i++ {
|
||||
contractBackend.Commit()
|
||||
}
|
||||
}
|
||||
// assert checks whether the current contract status is same with
|
||||
// the expected.
|
||||
assert := func(index uint64, hash [32]byte, height *big.Int) error {
|
||||
lindex, lhash, lheight, err := c.GetLatestCheckpoint(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if lindex != index {
|
||||
return errors.New("latest checkpoint index mismatch")
|
||||
}
|
||||
if !bytes.Equal(lhash[:], hash[:]) {
|
||||
return errors.New("latest checkpoint hash mismatch")
|
||||
}
|
||||
if lheight.Cmp(height) != 0 {
|
||||
return errors.New("latest checkpoint height mismatch")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Test future checkpoint registration
|
||||
validateOperation(t, c, contractBackend, func() {
|
||||
number, hash := getRecent()
|
||||
v, r, s := collectSig(0, checkpoint0.Hash(), 2, nil)
|
||||
c.SetCheckpoint(transactOpts, number, hash, checkpoint0.Hash(), 0, v, r, s)
|
||||
}, func(events <-chan *contract.CheckpointOracleNewCheckpointVote) error {
|
||||
return assert(0, emptyHash, big.NewInt(0))
|
||||
}, "test future checkpoint registration")
|
||||
|
||||
insertEmptyBlocks(int(sectionSize.Uint64() + processConfirms.Uint64()))
|
||||
|
||||
// Test transaction replay protection
|
||||
validateOperation(t, c, contractBackend, func() {
|
||||
number, hash := getRecent()
|
||||
v, r, s := collectSig(0, checkpoint0.Hash(), 2, nil)
|
||||
hash = common.HexToHash("deadbeef")
|
||||
c.SetCheckpoint(transactOpts, number, hash, checkpoint0.Hash(), 0, v, r, s)
|
||||
}, func(events <-chan *contract.CheckpointOracleNewCheckpointVote) error {
|
||||
return assert(0, emptyHash, big.NewInt(0))
|
||||
}, "test transaction replay protection")
|
||||
|
||||
// Test unauthorized signature checking
|
||||
validateOperation(t, c, contractBackend, func() {
|
||||
number, hash := getRecent()
|
||||
u, _ := crypto.GenerateKey()
|
||||
v, r, s := collectSig(0, checkpoint0.Hash(), 2, u)
|
||||
c.SetCheckpoint(transactOpts, number, hash, checkpoint0.Hash(), 0, v, r, s)
|
||||
}, func(events <-chan *contract.CheckpointOracleNewCheckpointVote) error {
|
||||
return assert(0, emptyHash, big.NewInt(0))
|
||||
}, "test unauthorized signature checking")
|
||||
|
||||
// Test un-multi-signature checkpoint registration
|
||||
validateOperation(t, c, contractBackend, func() {
|
||||
number, hash := getRecent()
|
||||
v, r, s := collectSig(0, checkpoint0.Hash(), 1, nil)
|
||||
c.SetCheckpoint(transactOpts, number, hash, checkpoint0.Hash(), 0, v, r, s)
|
||||
}, func(events <-chan *contract.CheckpointOracleNewCheckpointVote) error {
|
||||
return assert(0, emptyHash, big.NewInt(0))
|
||||
}, "test un-multi-signature checkpoint registration")
|
||||
|
||||
// Test valid checkpoint registration
|
||||
validateOperation(t, c, contractBackend, func() {
|
||||
number, hash := getRecent()
|
||||
v, r, s := collectSig(0, checkpoint0.Hash(), 2, nil)
|
||||
c.SetCheckpoint(transactOpts, number, hash, checkpoint0.Hash(), 0, v, r, s)
|
||||
}, func(events <-chan *contract.CheckpointOracleNewCheckpointVote) error {
|
||||
if valid, recv := validateEvents(2, events); !valid {
|
||||
return errors.New("receive incorrect number of events")
|
||||
} else {
|
||||
for i := 0; i < len(recv); i++ {
|
||||
event := recv[i].Interface().(*contract.CheckpointOracleNewCheckpointVote)
|
||||
if !assertSignature(contractAddr, event.Index, event.CheckpointHash, event.R, event.S, event.V, accounts[i].addr) {
|
||||
return errors.New("recover signer failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
number, _ := getRecent()
|
||||
return assert(0, checkpoint0.Hash(), number.Add(number, big.NewInt(1)))
|
||||
}, "test valid checkpoint registration")
|
||||
|
||||
distance := 3*sectionSize.Uint64() + processConfirms.Uint64() - contractBackend.Blockchain().CurrentHeader().Number.Uint64()
|
||||
insertEmptyBlocks(int(distance))
|
||||
|
||||
// Test uncontinuous checkpoint registration
|
||||
validateOperation(t, c, contractBackend, func() {
|
||||
number, hash := getRecent()
|
||||
v, r, s := collectSig(2, checkpoint2.Hash(), 2, nil)
|
||||
c.SetCheckpoint(transactOpts, number, hash, checkpoint2.Hash(), 2, v, r, s)
|
||||
}, func(events <-chan *contract.CheckpointOracleNewCheckpointVote) error {
|
||||
if valid, recv := validateEvents(2, events); !valid {
|
||||
return errors.New("receive incorrect number of events")
|
||||
} else {
|
||||
for i := 0; i < len(recv); i++ {
|
||||
event := recv[i].Interface().(*contract.CheckpointOracleNewCheckpointVote)
|
||||
if !assertSignature(contractAddr, event.Index, event.CheckpointHash, event.R, event.S, event.V, accounts[i].addr) {
|
||||
return errors.New("recover signer failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
number, _ := getRecent()
|
||||
return assert(2, checkpoint2.Hash(), number.Add(number, big.NewInt(1)))
|
||||
}, "test uncontinuous checkpoint registration")
|
||||
|
||||
// Test old checkpoint registration
|
||||
validateOperation(t, c, contractBackend, func() {
|
||||
number, hash := getRecent()
|
||||
v, r, s := collectSig(1, checkpoint1.Hash(), 2, nil)
|
||||
c.SetCheckpoint(transactOpts, number, hash, checkpoint1.Hash(), 1, v, r, s)
|
||||
}, func(events <-chan *contract.CheckpointOracleNewCheckpointVote) error {
|
||||
number, _ := getRecent()
|
||||
return assert(2, checkpoint2.Hash(), number)
|
||||
}, "test uncontinuous checkpoint registration")
|
||||
|
||||
// Test stale checkpoint registration
|
||||
validateOperation(t, c, contractBackend, func() {
|
||||
number, hash := getRecent()
|
||||
v, r, s := collectSig(2, checkpoint2.Hash(), 2, nil)
|
||||
c.SetCheckpoint(transactOpts, number, hash, checkpoint2.Hash(), 2, v, r, s)
|
||||
}, func(events <-chan *contract.CheckpointOracleNewCheckpointVote) error {
|
||||
number, _ := getRecent()
|
||||
return assert(2, checkpoint2.Hash(), number.Sub(number, big.NewInt(1)))
|
||||
}, "test stale checkpoint registration")
|
||||
}
|
Reference in New Issue
Block a user