227 lines
7.1 KiB
Nim
227 lines
7.1 KiB
Nim
# Nimbus
|
|
# Copyright (c) 2018 Status Research & Development GmbH
|
|
# Licensed under either of
|
|
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
|
# http://www.apache.org/licenses/LICENSE-2.0)
|
|
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or
|
|
# http://opensource.org/licenses/MIT)
|
|
# at your option. This file may not be copied, modified, or distributed except
|
|
# according to those terms.
|
|
|
|
##
|
|
## Descriptor Objects for Clique PoA Consensus Protocol
|
|
## ====================================================
|
|
##
|
|
## For details see
|
|
## `EIP-225 <https://github.com/ethereum/EIPs/blob/master/EIPS/eip-225.md>`_
|
|
## and
|
|
## `go-ethereum <https://github.com/ethereum/EIPs/blob/master/EIPS/eip-225.md>`_
|
|
##
|
|
|
|
import
|
|
std/[tables],
|
|
../../db/db_chain,
|
|
../../constants,
|
|
./clique_cfg,
|
|
./clique_defs,
|
|
./snapshot/[lru_snaps, snapshot_desc],
|
|
chronicles,
|
|
eth/[common, keys, rlp],
|
|
stew/results
|
|
|
|
const
|
|
enableCliqueAsyncLock* = ##\
|
|
## Async locks are currently unused by `Clique` but were part of the Go
|
|
## reference implementation. The unused code fragment from the reference
|
|
## implementation are buried in the file `clique_unused.nim` and not used
|
|
## otherwise.
|
|
defined(clique_async_lock)
|
|
|
|
when enableCliqueAsyncLock:
|
|
import chronos
|
|
|
|
type
|
|
RawSignature* = array[RawSignatureSize, byte]
|
|
|
|
# clique/clique.go(142): type SignerFn func(signer [..]
|
|
CliqueSignerFn* = ## Hashes and signs the data to be signed by
|
|
## a backing account
|
|
proc(signer: EthAddress;
|
|
message: openArray[byte]): Result[RawSignature, cstring] {.gcsafe.}
|
|
|
|
Proposals = Table[EthAddress,bool]
|
|
|
|
CliqueFailed* = ##\
|
|
## Last failed state: block hash and error result
|
|
(Hash256, CliqueError)
|
|
|
|
# clique/clique.go(172): type Clique struct { [..]
|
|
Clique* = ref object ##\
|
|
## Clique is the proof-of-authority consensus engine proposed to support
|
|
## the Ethereum testnet following the Ropsten attacks.
|
|
|
|
signer*: EthAddress ##\
|
|
## Ethereum address of the current signing key
|
|
|
|
signFn*: CliqueSignerFn ## Signer function to authorize hashes with
|
|
stopSealReq*: bool ## Stop running `seal()` function
|
|
stopVHeaderReq*: bool ## Stop running `verifyHeader()` function
|
|
# signatures => see CliqueCfg
|
|
|
|
cfg: CliqueCfg ##\
|
|
## Common engine parameters to fine tune behaviour
|
|
|
|
recents: LruSnaps ##\
|
|
## Snapshots cache for recent block search
|
|
|
|
snapshot: Snapshot ##\
|
|
## Last successful snapshot
|
|
|
|
failed: CliqueFailed ##\
|
|
## Last verification error (if any)
|
|
|
|
proposals: Proposals ##\
|
|
## Cu1rrent list of proposals we are pushing
|
|
|
|
applySnapsMinBacklog: bool ##\
|
|
## Epoch is a restart and sync point. Eip-225 requires that the epoch
|
|
## header contains the full list of currently authorised signers.
|
|
##
|
|
## If this flag is set `true`, then the `cliqueSnapshot()` function will
|
|
## walk back to the `epoch` header with at least `cfg.roThreshold` blocks
|
|
## apart from the current header. This is how it is done in the reference
|
|
## implementation.
|
|
##
|
|
## Leving the flag `false`, the assumption is that all the checkponts
|
|
## before have been vetted already regardless of the current branch. So
|
|
## the nearest `epoch` header is used.
|
|
|
|
when enableCliqueAsyncLock:
|
|
asyncLock: AsyncLock ##\
|
|
## Protects the signer fields
|
|
|
|
{.push raises: [Defect].}
|
|
|
|
logScope:
|
|
topics = "clique PoA constructor"
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Public constructor
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# clique/clique.go(191): func New(config [..]
|
|
proc newClique*(cfg: CliqueCfg): Clique =
|
|
## Initialiser for Clique proof-of-authority consensus engine with the
|
|
## initial signers set to the ones provided by the user.
|
|
result = Clique(cfg: cfg,
|
|
recents: cfg.initLruSnaps,
|
|
snapshot: cfg.newSnapshot(BlockHeader()),
|
|
proposals: initTable[EthAddress,bool]())
|
|
when enableCliqueAsyncLock:
|
|
result.asyncLock = newAsyncLock()
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Public /pretty print
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# Debugging only
|
|
proc getPrettyPrinters*(c: Clique): var PrettyPrinters =
|
|
## Mixin for pretty printers, see `clique/clique_cfg.pp()`
|
|
c.cfg.prettyPrint
|
|
|
|
proc `$`*(e: CliqueError): string =
|
|
## Join text fragments
|
|
result = $e[0]
|
|
if e[1] != "":
|
|
result &= ": " & e[1]
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Public getters
|
|
# ------------------------------------------------------------------------------
|
|
|
|
proc recents*(c: Clique): var LruSnaps {.inline.} =
|
|
## Getter
|
|
c.recents
|
|
|
|
proc proposals*(c: Clique): var Proposals {.inline.} =
|
|
## Getter
|
|
c.proposals
|
|
|
|
proc snapshot*(c: Clique): auto {.inline.} =
|
|
## Getter, last successfully processed snapshot.
|
|
c.snapshot
|
|
|
|
proc failed*(c: Clique): auto {.inline.} =
|
|
## Getter, last snapshot error.
|
|
c.failed
|
|
|
|
proc cfg*(c: Clique): auto {.inline.} =
|
|
## Getter
|
|
c.cfg
|
|
|
|
proc db*(c: Clique): auto {.inline.} =
|
|
## Getter
|
|
c.cfg.db
|
|
|
|
proc applySnapsMinBacklog*(c: Clique): auto {.inline.} =
|
|
## Getter.
|
|
##
|
|
## If this flag is set `true`, then the `cliqueSnapshot()` function will
|
|
## walk back to the `epoch` header with at least `cfg.roThreshold` blocks
|
|
## apart from the current header. This is how it is done in the reference
|
|
## implementation.
|
|
##
|
|
## Setting the flag `false` which is the default, the assumption is that all
|
|
## the checkponts before have been vetted already regardless of the current
|
|
## branch. So the nearest `epoch` header is used.
|
|
c.applySnapsMinBacklog
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Public setters
|
|
# ------------------------------------------------------------------------------
|
|
|
|
proc `db=`*(c: Clique; db: BaseChainDB) {.inline.} =
|
|
## Setter, re-set database
|
|
c.cfg.db = db
|
|
c.proposals = initTable[EthAddress,bool]()
|
|
c.recents = c.cfg.initLruSnaps
|
|
|
|
proc `snapshot=`*(c: Clique; snaps: Snapshot) =
|
|
## Setter
|
|
c.snapshot = snaps
|
|
|
|
proc `failed=`*(c: Clique; failure: CliqueFailed) =
|
|
## Setter
|
|
c.failed = failure
|
|
|
|
proc `applySnapsMinBacklog=`*(c: Clique; value: bool) {.inline.} =
|
|
## Setter
|
|
c.applySnapsMinBacklog = value
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Public lock/unlock
|
|
# ------------------------------------------------------------------------------
|
|
|
|
when enableCliqueAsyncLock:
|
|
proc lock*(c: Clique) {.inline, raises: [Defect,CatchableError].} =
|
|
## Lock descriptor
|
|
waitFor c.asyncLock.acquire
|
|
|
|
proc unLock*(c: Clique) {.inline, raises: [Defect,AsyncLockError].} =
|
|
## Unlock descriptor
|
|
c.asyncLock.release
|
|
|
|
template doExclusively*(c: Clique; action: untyped) =
|
|
## Handy helper
|
|
c.lock
|
|
action
|
|
c.unlock
|
|
|
|
else:
|
|
template doExclusively*(c: Clique; action: untyped) =
|
|
action
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# End
|
|
# ------------------------------------------------------------------------------
|