mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
262 lines
8 KiB
Solidity
262 lines
8 KiB
Solidity
pragma solidity ^0.5.2;
|
|
|
|
/**
|
|
* @title Registrar
|
|
* @author Gary Rong<garyrong@ethereum.org>
|
|
* @dev Implementation of the blockchain checkpoint registrar.
|
|
*/
|
|
contract Registrar {
|
|
/*
|
|
Definitions
|
|
*/
|
|
|
|
// Vote represents a checkpoint announcement from a trusted signer.
|
|
struct Vote {
|
|
address addr;
|
|
bytes sig;
|
|
}
|
|
|
|
// PendingProposal represents a tally for current pending new checkpoint
|
|
// proposal.
|
|
struct PendingProposal {
|
|
uint index; // Checkpoint section index
|
|
uint count; // Number of signers who have submitted checkpoint announcement
|
|
mapping(address => bytes32) usermap; // map between signer address and advertised checkpoint hash
|
|
mapping(bytes32 => Vote[]) votemap; // map between checkpoint hash and relative signer announcements.
|
|
}
|
|
|
|
/*
|
|
Modifiers
|
|
*/
|
|
|
|
/**
|
|
* @dev Check whether the message sender is authorized.
|
|
*/
|
|
modifier OnlyAuthorized() {
|
|
require(admins[msg.sender]);
|
|
_;
|
|
}
|
|
|
|
/*
|
|
Events
|
|
*/
|
|
|
|
// NewCheckpointEvent is emitted when a new checkpoint proposal receive enough approvals.
|
|
// We use checkpoint hash instead of the full checkpoint to make the transaction cheaper.
|
|
event NewCheckpointEvent(uint indexed index, bytes32 checkpointHash, bytes signature);
|
|
|
|
/*
|
|
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(uint, bytes32, uint) {
|
|
return (sectionIndex, hash, height);
|
|
}
|
|
|
|
/**
|
|
* @dev Submit a new checkpoint announcement
|
|
* 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.
|
|
*
|
|
* @param _sectionIndex section index
|
|
* @param _hash checkpoint hash calculated in the client side
|
|
* @param _sig admin's signature for checkpoint hash
|
|
* `checkpoint_hash = Hash(index, sectionHead, chtRoot, bloomRoot)`
|
|
* `_sig = Sign(privateKey, checkpoint_hash)`
|
|
* @return indicator whether set checkpoint successfully
|
|
*/
|
|
function SetCheckpoint(
|
|
uint _sectionIndex,
|
|
bytes32 _hash,
|
|
bytes memory _sig
|
|
)
|
|
OnlyAuthorized
|
|
public
|
|
returns(bool)
|
|
{
|
|
// 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 == "" || _sig.length == 0) {
|
|
return false;
|
|
}
|
|
// Delete stale pending proposal silently
|
|
if (pending_proposal.index != _sectionIndex) {
|
|
deletePending();
|
|
}
|
|
bytes32 old = pending_proposal.usermap[msg.sender];
|
|
// Filter out duplicate announcement
|
|
if (old == _hash) {
|
|
return false;
|
|
}
|
|
bool isNew = (old == "");
|
|
pending_proposal.usermap[msg.sender] = _hash;
|
|
|
|
if (isNew) {
|
|
// New checkpoint announcement
|
|
pending_proposal.count += 1;
|
|
pending_proposal.index = _sectionIndex;
|
|
pending_proposal.votemap[_hash].push(Vote({
|
|
addr: msg.sender,
|
|
sig: _sig
|
|
}));
|
|
} else {
|
|
// Checkpoint modification
|
|
Vote[] storage votes = pending_proposal.votemap[old];
|
|
for (uint i = 0; i < votes.length - 1; i++) {
|
|
if (votes[i].addr == msg.sender) {
|
|
votes[i] = votes[votes.length - 1];
|
|
break;
|
|
}
|
|
}
|
|
delete votes[votes.length-1];
|
|
votes.length -= 1;
|
|
pending_proposal.votemap[_hash].push(Vote({
|
|
addr: msg.sender,
|
|
sig: _sig
|
|
}));
|
|
}
|
|
if (pending_proposal.votemap[_hash].length < threshold) {
|
|
return true;
|
|
}
|
|
// Update latest checkpoint
|
|
hash = _hash;
|
|
height = block.number;
|
|
sectionIndex = _sectionIndex;
|
|
|
|
bytes memory sigs;
|
|
for (uint idx = 0; idx < threshold; idx++) {
|
|
sigs = abi.encodePacked(sigs, pending_proposal.votemap[_hash][idx].sig);
|
|
}
|
|
emit NewCheckpointEvent(_sectionIndex, _hash, sigs);
|
|
deletePending();
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @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;
|
|
}
|
|
|
|
/**
|
|
* @dev Get the detail of pending proposal
|
|
* @return checkpoint index
|
|
* @return signers who have submitted checkpoint announcement
|
|
* @return hashes corresponding checkpoint hash
|
|
*/
|
|
function GetPending()
|
|
public
|
|
view
|
|
returns(uint, address[] memory, bytes32[] memory)
|
|
{
|
|
uint idx = 0;
|
|
address[] memory addr = new address[](pending_proposal.count);
|
|
bytes32[] memory hashes = new bytes32[](pending_proposal.count);
|
|
for (uint i = 0; i < adminList.length; i++) {
|
|
bytes32 h = pending_proposal.usermap[adminList[i]];
|
|
if (h != "") {
|
|
addr[idx] = adminList[i];
|
|
hashes[idx] = h;
|
|
idx += 1;
|
|
}
|
|
}
|
|
return (pending_proposal.index, addr, hashes);
|
|
}
|
|
|
|
/**
|
|
* @dev Clear pending proposal
|
|
*/
|
|
function deletePending()
|
|
private
|
|
{
|
|
for (uint i = 0; i < adminList.length; i++) {
|
|
bytes32 h = pending_proposal.usermap[adminList[i]];
|
|
if (h != "") {
|
|
delete pending_proposal.votemap[h];
|
|
delete pending_proposal.usermap[adminList[i]];
|
|
}
|
|
}
|
|
delete pending_proposal;
|
|
}
|
|
|
|
/*
|
|
Fields
|
|
*/
|
|
// Inflight new stable checkpoint proposal.
|
|
PendingProposal pending_proposal;
|
|
|
|
// 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
|
|
// Note all registered checkpoint information should continuous with previous one.
|
|
uint 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;
|
|
}
|
|
|