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
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;
|
||||
}
|
Reference in New Issue
Block a user