ethcore: add clique engine (#9981)

* fix broken sync

* correct seal fields

* ethcore: fix comment

* parity: remove duplicate params

* clique: fix whitespaces

* ethcore: fix goerli chain spec

* refactor signer_snapshot into pending/finalized state

* move close_block_extra_data after seal is applied

* refactor most of the logic into the signer_snapshot

* clique: refactor locking logic out of the consensus engine interface

* Fix jsonspec and add an unittest

* Replace space with tabs

* Unbroke sync

* Fix broken sync

* 1/2 state tracking without votes

* 2/2 implement vote tracking

* ci: use travis for goerli

* ci: setup a clique network

* ci: sync a görli node

* add clique deploy script

* ci: fix paths in clique deploy script

* ci: use docker compose

* ci: fix travis job names

* ci: fix build deps

* ci: massively reduce tests

* Revert "ci: massively reduce tests"

This reverts commit 6369f0b069ed2607a7e9f2e1d85489bacdc43384.

* ci: run cargo test directly

* ci: separate build and test stages

* ci: cache rust installation

* ci: simplify ci stages

* ci: make clique deploy script executable

* ci: shutdown goerli sync after 20min

* ci: remove slow sync stage

* ci: use timeout to finish jobs

* ci: fix build path

* ci: use absolute paths to end this confusion

* ci: add geth and parity to path

* ci: be more verbose

* ci: allow for more relaxed caching timeout

* ci: update repositories for custom ppa

* ci: fix typo in file name

* ci: fix docker compose file

* ci: add ethkey to docker

* ci: make sure deploy script is up to date with upstream

* ci: stop docker container after certain time

* ci: force superuser to update permissions on docker files

* ci: reduce run time of script to ~30 min

* ci: remove duplicate caching in travis

* remove trace statements

* clique: add more validation involving the recent signer list

* ethcore: enable constantinople for rinkeby

* ethcore: fix whitespaces in rinkeby spec

* ethcore: reformat goerli.json

* Revert "ci: remove duplicate caching in travis"

This reverts commit a562838d3d194d37f9871dcbe00b783637978f89.

* tmp commit

* another tmp commit

* it builds!

* add sealing capabilities

* add seal_header hook to allow separation of block seal/importing code paths

* clique: remove populate_from_parent.

* add panic

* make turn delay random

* initialize OpenBlock properly in 'enact'

* misc: remove duplicate lines

* misc: fix license headers

* misc: convert spaces to tabs

* misc: fix tabs

* Update Cargo.toml

* Update Cargo.toml

* Update Cargo.toml

* clique: ensure validator restores state before trying to seal

* clique: make 'state' return an Error.  Make some error messages more clear

* Fix compile error after rebase & toolchain upgrade

* fix a bunch of import warnings

* Refactor code

* Fix permissions

* Refactoring syncing

* Implement full validator checks

* Refactor util functions to seperate file

* mining 1

* ethcore: add chainspec for kotti

* ethcore: rename pre-goerli configs

* ethcore: load kotti chain spec

* cli: add kotti to params

* Implement working local sealing

* making sealing & syncing work together

* Relax timestamp checking

* ethcore: prepare for the real goerli to launch

* Implement NOTURN wiggle properly & cleanupnup warnings

* Implement vote casting

* Update docs & skip signing if no signer

* Optimize step-service interval

* Record state on local sealed block

* Fix script filemode

* Cleaning up codebase

* restore enact trace logging

* Delete clique.sh and move sync.sh

* remove travis.yml

* Remove dead code

* Cleanup compile warning

* address review comments

* adding more comments and removing unwrap()

* ci: remove sync script

* Address review comments

* fix compile error

* adding better debugging for timing

* Implement an dedicated thread for sealing timing

* fix(add helper for timestamp overflows) (#10330)

* fix(add helper timestamp overflows)

* fix(simplify code)

* fix(make helper private)

* snap: official image / test (#10168)

* official image / test

* fix / test

* bit more necromancy

* fix paths

* add source bin/df /test

* add source bin/df /test2

* something w paths /test

* something w paths /test

* add source-type /test

* show paths /test

* copy plugin /test

* plugin -> nil

* install rhash

* no questions while installing rhash

* publish snap only for release

* fix(docker): fix not receives SIGINT (#10059)

* fix(docker): fix not receives SIGINT

* fix: update with reviews

* update with review

* update

* update

* Don't add discovery initiators to the node table (#10305)

* Don't add discovery initiators to the node table

* Use enums for tracking state of the nodes in discovery

* Dont try to ping ourselves

* Fix minor nits

* Update timeouts when observing an outdated node

* Extracted update_bucket_record from update_node

* Fixed typo

* Fix two final nits from @todr

* change docker image based on debian instead of ubuntu due to the chan… (#10336)

* change docker image based on debian instead of ubuntu due to the changes of the build container

* role back docker build image and docker deploy image to ubuntu:xenial based (#10338)

* Bundle protocol and packet_id together in chain sync (#10315)

Define a new `enum` where devp2p subprotocol packet ids (currently eth and par) are defined. Additionally provide functionality to query id value and protocol of a given id object.

* snap: prefix version and populate candidate channel (#10343)

* snap: populate candidate releases with beta snaps to avoid stale channel

* snap: prefix version with v*

* addressing review comments

* engine: fix copyright header

* scripts: restore permissions on sign command

* ethcore: enforce tabs

* ethcore: enforce tabs

* ethcore: enforce tabs

* addressing comments

* addressing comments

* addressing more comments

* addressing more comments

* addressing more comments

* addressing more comments

* addressing more comments

* json-spec: fix clique epoch to non-zero u64

* ci: enable travis for parity goerli

* ci: don't separate build and test step

* ci: don't run c++ tests on travis

* ci: simplify cargo test to squeeze into travis timeout

* ci: don't run tests on travis at all

* style(fixes)

* fix(add tests)

* fix(recent_signer bug)

* fix(complete all tests)

* fix(nits)

* fix(simplify asserts)

* fix(cliqueState): simplify code

* fix(nits)

* docs(comments what's need to fixed)

* fix(revert unintended changes)

* fix(tests)

* fix(logs): voting logs

* fix(readability + more logs)

* fix(sync)

* docs(add missing licens header)

* fix(log): info! -> trace!

* docs(fix nits) + fix(remove assert)

* perf(use counter instead of vec)

* fix(remove needless block in match)

* fix(faulty comment)

* grumbles(docs for tests)

* fix(nits)

* fix(revert_vote): only remove vote when votes == 0

* fix(vote counter): checked arithmetics

* fix(simplify tests)

* fix(nits)

* fix(clique): err types

* fix(clique utils): make use of errors

* fix(cleanup nits)

* fix(clique sealing): don't read state no signer

* fix(replace Vec<Signers> with BTreeSet<Signers>)

* fix(tests): BTreeSet and more generic helpers

* fix(nits)

* fix(ethcore_block_seal): remove needless `Box`

* fix(faulty log): info -> trace

* fix(checked SystemTime): prevent SystemTime panics

* style(chain cfg): space after `:`

* style(fn enact): fix whitespace

* docs(clique): StepService

* docs(nit): fix faulty comment

* docs(fix typo)

* style(fix bad indentation)

* fix(bad regex match)

* grumble(on_seal_block): make `&mut` to avoid clone

* docs(on_seal_block): fix faulty documentation

* Delete .travis.yml

* docs: remove eth hf references in spec

* Update client.rs

* fix(nits)

* fix(clique step): `RwLock` -> `AtomicBool`

* fix(clique): use `Duration::as_millis`

* Clean up some Clique documentation

Co-authored-by: soc1c <soc1c@users.noreply.github.com>
Co-authored-by: HCastano <HCastano@users.noreply.github.com>
Co-authored-by: niklasad1 <niklasad1@users.noreply.github.com>
Co-authored-by: jwasinger <jwasinger@users.noreply.github.com>
Co-authored-by: ChainSafe <ChainSafe@users.noreply.github.com>
Co-authored-by: thefallentree <thefallentree@users.noreply.github.com>
Co-authored-by: 5chdn <5chdn@users.noreply.github.com>
This commit is contained in:
5chdn 2019-03-26 23:31:52 +01:00 committed by soc1c
parent 9cb8606103
commit aa8487c1d0
No known key found for this signature in database
GPG Key ID: 333099B0F66985F1
22 changed files with 5055 additions and 37 deletions

View File

@ -39,7 +39,7 @@ keccak-hasher = { path = "../util/keccak-hasher" }
kvdb = "0.1" kvdb = "0.1"
kvdb-memorydb = "0.1" kvdb-memorydb = "0.1"
kvdb-rocksdb = { version = "0.1.3", optional = true } kvdb-rocksdb = { version = "0.1.3", optional = true }
lazy_static = "1.0" lazy_static = "1.2.0"
len-caching-lock = { path = "../util/len-caching-lock" } len-caching-lock = { path = "../util/len-caching-lock" }
log = "0.4" log = "0.4"
lru-cache = "0.1" lru-cache = "0.1"

View File

@ -0,0 +1,911 @@
{
"name": "Görli Testnet",
"dataDir": "goerli",
"engine": {
"clique": {
"params": {
"period": 15,
"epoch": 30000
}
}
},
"params": {
"accountStartNonce": "0x0",
"chainID": "0x5",
"eip140Transition": "0x0",
"eip145Transition": "0x0",
"eip150Transition": "0x0",
"eip155Transition": "0x0",
"eip160Transition": "0x0",
"eip161abcTransition": "0x0",
"eip161dTransition": "0x0",
"eip211Transition": "0x0",
"eip214Transition": "0x0",
"eip658Transition": "0x0",
"eip1014Transition": "0x0",
"eip1052Transition": "0x0",
"eip1283Transition": "0x0",
"eip1283DisableTransition": "0x0",
"gasLimitBoundDivisor": "0x400",
"maxCodeSize": "0x6000",
"maxCodeSizeTransition": "0x0",
"maximumExtraDataSize": "0xffff",
"minGasLimit": "0x1388",
"networkID": "0x5"
},
"genesis": {
"author": "0x0000000000000000000000000000000000000000",
"difficulty": "0x1",
"extraData": "0x22466c6578692069732061207468696e6722202d204166726900000000000000e0a2bd4258d2768837baa26a28fe71dc079f84c70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0xa00000",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"seal": {
"ethereum": {
"nonce": "0x0000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
}
},
"timestamp": "0x5c51a607"
},
"nodes": [
"enode://011f758e6552d105183b1761c5e2dea0111bc20fd5f6422bc7f91e0fabbec9a6595caf6239b37feb773dddd3f87240d99d859431891e4a642cf2a0a9e6cbb98a@51.141.78.53:30303",
"enode://176b9417f511d05b6b2cf3e34b756cf0a7096b3094572a8f6ef4cdcb9d1f9d00683bf0f83347eebdf3b81c3521c2332086d9592802230bf528eaf606a1d9677b@13.93.54.137:30303"
],
"accounts": {
"0x0000000000000000000000000000000000000000": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000001": {
"balance": "0x1",
"builtin": {
"name": "ecrecover",
"pricing": {
"linear": {
"base": 3000,
"word": 0
}
}
}
},
"0x0000000000000000000000000000000000000002": {
"balance": "0x1",
"builtin": {
"name": "sha256",
"pricing": {
"linear": {
"base": 60,
"word": 12
}
}
}
},
"0x0000000000000000000000000000000000000003": {
"balance": "0x1",
"builtin": {
"name": "ripemd160",
"pricing": {
"linear": {
"base": 600,
"word": 120
}
}
}
},
"0x0000000000000000000000000000000000000004": {
"balance": "0x1",
"builtin": {
"name": "identity",
"pricing": {
"linear": {
"base": 15,
"word": 3
}
}
}
},
"0x0000000000000000000000000000000000000005": {
"balance": "0x1",
"builtin": {
"name": "modexp",
"activate_at": "0x0",
"pricing": {
"modexp": {
"divisor": 20
}
}
}
},
"0x0000000000000000000000000000000000000006": {
"balance": "0x1",
"builtin": {
"name": "alt_bn128_add",
"activate_at": "0x0",
"pricing": {
"linear": {
"base": 500,
"word": 0
}
}
}
},
"0x0000000000000000000000000000000000000007": {
"balance": "0x1",
"builtin": {
"name": "alt_bn128_mul",
"activate_at": "0x0",
"pricing": {
"linear": {
"base": 40000,
"word": 0
}
}
}
},
"0x0000000000000000000000000000000000000008": {
"balance": "0x1",
"builtin": {
"name": "alt_bn128_pairing",
"activate_at": "0x0",
"pricing": {
"alt_bn128_pairing": {
"base": 100000,
"pair": 80000
}
}
}
},
"0x0000000000000000000000000000000000000009": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000000a": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000000b": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000000c": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000000d": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000000e": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000000f": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000010": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000011": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000012": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000013": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000014": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000015": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000016": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000017": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000018": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000019": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000001a": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000001b": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000001c": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000001d": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000001e": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000001f": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000020": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000021": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000022": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000023": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000024": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000025": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000026": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000027": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000028": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000029": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000002a": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000002b": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000002c": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000002d": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000002e": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000002f": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000030": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000031": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000032": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000033": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000034": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000035": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000036": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000037": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000038": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000039": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000003a": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000003b": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000003c": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000003d": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000003e": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000003f": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000040": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000041": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000042": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000043": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000044": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000045": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000046": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000047": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000048": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000049": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000004a": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000004b": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000004c": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000004d": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000004e": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000004f": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000050": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000051": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000052": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000053": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000054": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000055": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000056": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000057": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000058": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000059": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000005a": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000005b": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000005c": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000005d": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000005e": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000005f": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000060": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000061": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000062": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000063": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000064": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000065": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000066": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000067": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000068": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000069": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000006a": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000006b": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000006c": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000006d": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000006e": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000006f": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000070": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000071": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000072": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000073": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000074": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000075": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000076": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000077": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000078": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000079": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000007a": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000007b": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000007c": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000007d": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000007e": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000007f": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000080": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000081": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000082": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000083": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000084": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000085": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000086": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000087": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000088": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000089": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000008a": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000008b": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000008c": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000008d": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000008e": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000008f": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000090": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000091": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000092": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000093": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000094": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000095": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000096": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000097": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000098": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000099": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000009a": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000009b": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000009c": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000009d": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000009e": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000009f": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000a0": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000a1": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000a2": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000a3": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000a4": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000a5": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000a6": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000a7": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000a8": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000a9": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000aa": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000ab": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000ac": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000ad": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000ae": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000af": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000b0": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000b1": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000b2": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000b3": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000b4": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000b5": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000b6": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000b7": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000b8": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000b9": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000ba": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000bb": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000bc": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000bd": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000be": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000bf": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000c0": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000c1": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000c2": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000c3": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000c4": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000c5": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000c6": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000c7": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000c8": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000c9": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000ca": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000cb": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000cc": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000cd": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000ce": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000cf": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000d0": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000d1": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000d2": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000d3": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000d4": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000d5": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000d6": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000d7": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000d8": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000d9": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000da": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000db": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000dc": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000dd": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000de": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000df": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000e0": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000e1": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000e2": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000e3": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000e4": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000e5": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000e6": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000e7": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000e8": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000e9": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000ea": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000eb": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000ec": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000ed": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000ee": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000ef": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000f0": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000f1": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000f2": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000f3": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000f4": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000f5": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000f6": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000f7": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000f8": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000f9": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000fa": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000fb": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000fc": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000fd": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000fe": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000ff": {
"balance": "0x1"
},
"0x4c2ae482593505f0163cdefc073e81c63cda4107": {
"balance": "0x152d02c7e14af6800000"
},
"0xa8e8f14732658e4b51e8711931053a8a69baf2b1": {
"balance": "0x152d02c7e14af6800000"
},
"0xd9a5179f091d85051d3c982785efd1455cec8699": {
"balance": "0x84595161401484a000000"
},
"0xe0a2bd4258d2768837baa26a28fe71dc079f84c7": {
"balance": "0x4a47e3c12448f4ad000000"
}
}
}

View File

@ -0,0 +1,855 @@
{
"name": "Kotti Testnet",
"dataDir": "kotti",
"engine": {
"clique": {
"params": {
"period": 15,
"epoch": 30000
}
}
},
"params": {
"accountStartNonce": "0x0",
"chainID": "0x6",
"eip150Transition": "0x0",
"eip155Transition": "0x0",
"eip160Transition": "0x0",
"eip161abcTransition": "0x7fffffffffffffff",
"eip161dTransition": "0x7fffffffffffffff",
"gasLimitBoundDivisor": "0x400",
"maximumExtraDataSize": "0xffff",
"minGasLimit": "0x1388",
"networkID": "0x6"
},
"genesis": {
"author": "0x0000000000000000000000000000000000000000",
"difficulty": "0x1",
"extraData": "0x000000000000000000000000000000000000000000000000000000000000000025b7955e43adf9c2a01a9475908702cce67f302a6aaf8cba3c9255a2b863415d4db7bae4f4bbca020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0xa00000",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"seal": {
"ethereum": {
"nonce": "0x0000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
}
},
"timestamp": "0x5c2d2287"
},
"nodes": [
"enode://06333009fc9ef3c9e174768e495722a7f98fe7afd4660542e983005f85e556028410fd03278944f44cfe5437b1750b5e6bd1738f700fe7da3626d52010d2954c@51.141.15.254:30303",
"enode://ae8658da8d255d1992c3ec6e62e11d6e1c5899aa1566504bc1ff96a0c9c8bd44838372be643342553817f5cc7d78f1c83a8093dee13d77b3b0a583c050c81940@18.232.185.151:30303"
],
"accounts": {
"0x0000000000000000000000000000000000000000": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000001": {
"balance": "0x1",
"builtin": {
"name": "ecrecover",
"pricing": {
"linear": {
"base": 3000,
"word": 0
}
}
}
},
"0x0000000000000000000000000000000000000002": {
"balance": "0x1",
"builtin": {
"name": "sha256",
"pricing": {
"linear": {
"base": 60,
"word": 12
}
}
}
},
"0x0000000000000000000000000000000000000003": {
"balance": "0x1",
"builtin": {
"name": "ripemd160",
"pricing": {
"linear": {
"base": 600,
"word": 120
}
}
}
},
"0x0000000000000000000000000000000000000004": {
"balance": "0x1",
"builtin": {
"name": "identity",
"pricing": {
"linear": {
"base": 15,
"word": 3
}
}
}
},
"0x0000000000000000000000000000000000000005": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000006": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000007": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000008": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000009": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000000a": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000000b": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000000c": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000000d": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000000e": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000000f": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000010": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000011": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000012": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000013": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000014": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000015": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000016": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000017": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000018": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000019": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000001a": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000001b": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000001c": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000001d": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000001e": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000001f": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000020": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000021": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000022": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000023": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000024": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000025": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000026": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000027": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000028": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000029": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000002a": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000002b": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000002c": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000002d": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000002e": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000002f": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000030": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000031": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000032": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000033": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000034": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000035": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000036": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000037": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000038": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000039": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000003a": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000003b": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000003c": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000003d": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000003e": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000003f": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000040": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000041": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000042": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000043": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000044": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000045": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000046": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000047": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000048": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000049": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000004a": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000004b": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000004c": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000004d": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000004e": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000004f": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000050": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000051": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000052": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000053": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000054": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000055": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000056": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000057": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000058": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000059": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000005a": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000005b": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000005c": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000005d": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000005e": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000005f": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000060": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000061": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000062": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000063": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000064": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000065": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000066": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000067": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000068": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000069": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000006a": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000006b": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000006c": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000006d": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000006e": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000006f": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000070": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000071": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000072": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000073": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000074": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000075": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000076": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000077": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000078": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000079": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000007a": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000007b": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000007c": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000007d": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000007e": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000007f": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000080": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000081": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000082": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000083": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000084": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000085": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000086": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000087": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000088": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000089": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000008a": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000008b": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000008c": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000008d": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000008e": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000008f": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000090": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000091": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000092": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000093": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000094": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000095": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000096": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000097": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000098": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000099": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000009a": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000009b": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000009c": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000009d": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000009e": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000009f": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000a0": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000a1": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000a2": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000a3": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000a4": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000a5": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000a6": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000a7": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000a8": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000a9": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000aa": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000ab": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000ac": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000ad": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000ae": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000af": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000b0": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000b1": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000b2": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000b3": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000b4": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000b5": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000b6": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000b7": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000b8": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000b9": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000ba": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000bb": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000bc": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000bd": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000be": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000bf": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000c0": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000c1": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000c2": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000c3": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000c4": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000c5": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000c6": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000c7": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000c8": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000c9": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000ca": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000cb": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000cc": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000cd": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000ce": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000cf": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000d0": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000d1": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000d2": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000d3": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000d4": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000d5": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000d6": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000d7": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000d8": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000d9": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000da": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000db": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000dc": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000dd": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000de": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000df": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000e0": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000e1": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000e2": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000e3": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000e4": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000e5": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000e6": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000e7": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000e8": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000e9": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000ea": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000eb": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000ec": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000ed": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000ee": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000ef": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000f0": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000f1": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000f2": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000f3": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000f4": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000f5": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000f6": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000f7": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000f8": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000f9": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000fa": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000fb": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000fc": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000fd": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000fe": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000ff": {
"balance": "0x1"
},
"0x25b7955e43adf9c2a01a9475908702cce67f302a": {
"balance": "0x84595161401484a000000"
},
"0x6aaf8cba3c9255a2b863415d4db7bae4f4bbca02": {
"balance": "0x4a723dc6b40b8a9a000000"
}
}
}

View File

@ -0,0 +1,902 @@
{
"name": "Rinkeby",
"dataDir": "rinkeby",
"engine": {
"clique": {
"params": {
"period": 15,
"epoch": 30000
}
}
},
"params": {
"accountStartNonce": "0x0",
"chainID": "0x4",
"eip140Transition": "0xfcc25",
"eip145Transition": "0x37db77",
"eip150Transition": "0x2",
"eip155Transition": "0x3",
"eip160Transition": "0x0",
"eip161abcTransition": "0x0",
"eip161dTransition": "0x0",
"eip211Transition": "0xfcc25",
"eip214Transition": "0xfcc25",
"eip658Transition": "0xfcc25",
"eip1014Transition": "0x37db77",
"eip1052Transition": "0x37db77",
"eip1283Transition": "0x37db77",
"gasLimitBoundDivisor": "0x400",
"maxCodeSize": "0x6000",
"maxCodeSizeTransition": "0x0",
"maximumExtraDataSize": "0xffff",
"minGasLimit": "0x1388",
"networkID": "0x4"
},
"genesis": {
"author": "0x0000000000000000000000000000000000000000",
"difficulty": "0x1",
"extraData": "0x52657370656374206d7920617574686f7269746168207e452e436172746d616e42eb768f2244c8811c63729a21a3569731535f067ffc57839b00206d1ad20c69a1981b489f772031b279182d99e65703f0076e4812653aab85fca0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0x47b760",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"seal": {
"ethereum": {
"nonce": "0x0000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
}
},
"timestamp": "0x58ee40ba"
},
"nodes": [
"enode://a24ac7c5484ef4ed0c5eb2d36620ba4e4aa13b8c84684e1b4aab0cebea2ae45cb4d375b77eab56516d34bfbd3c1a833fc51296ff084b770b94fb9028c4d25ccf@52.169.42.101:30303",
"enode://343149e4feefa15d882d9fe4ac7d88f885bd05ebb735e547f12e12080a9fa07c8014ca6fd7f373123488102fe5e34111f8509cf0b7de3f5b44339c9f25e87cb8@52.3.158.184:30303",
"enode://b6b28890b006743680c52e64e0d16db57f28124885595fa03a562be1d2bf0f3a1da297d56b13da25fb992888fd556d4c1a27b1f39d531bde7de1921c90061cc6@159.89.28.211:30303"
],
"accounts": {
"0x0000000000000000000000000000000000000000": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000001": {
"balance": "0x1",
"builtin": {
"name": "ecrecover",
"pricing": {
"linear": {
"base": 3000,
"word": 0
}
}
}
},
"0x0000000000000000000000000000000000000002": {
"balance": "0x1",
"builtin": {
"name": "sha256",
"pricing": {
"linear": {
"base": 60,
"word": 12
}
}
}
},
"0x0000000000000000000000000000000000000003": {
"balance": "0x1",
"builtin": {
"name": "ripemd160",
"pricing": {
"linear": {
"base": 600,
"word": 120
}
}
}
},
"0x0000000000000000000000000000000000000004": {
"balance": "0x1",
"builtin": {
"name": "identity",
"pricing": {
"linear": {
"base": 15,
"word": 3
}
}
}
},
"0x0000000000000000000000000000000000000005": {
"balance": "0x1",
"builtin": {
"name": "modexp",
"activate_at": "0xfcc25",
"pricing": {
"modexp": {
"divisor": 20
}
}
}
},
"0x0000000000000000000000000000000000000006": {
"balance": "0x1",
"builtin": {
"name": "alt_bn128_add",
"activate_at": "0xfcc25",
"pricing": {
"linear": {
"base": 500,
"word": 0
}
}
}
},
"0x0000000000000000000000000000000000000007": {
"balance": "0x1",
"builtin": {
"name": "alt_bn128_mul",
"activate_at": "0xfcc25",
"pricing": {
"linear": {
"base": 40000,
"word": 0
}
}
}
},
"0x0000000000000000000000000000000000000008": {
"balance": "0x1",
"builtin": {
"name": "alt_bn128_pairing",
"activate_at": "0xfcc25",
"pricing": {
"alt_bn128_pairing": {
"base": 100000,
"pair": 80000
}
}
}
},
"0x0000000000000000000000000000000000000009": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000000a": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000000b": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000000c": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000000d": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000000e": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000000f": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000010": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000011": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000012": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000013": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000014": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000015": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000016": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000017": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000018": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000019": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000001a": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000001b": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000001c": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000001d": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000001e": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000001f": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000020": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000021": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000022": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000023": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000024": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000025": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000026": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000027": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000028": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000029": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000002a": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000002b": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000002c": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000002d": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000002e": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000002f": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000030": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000031": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000032": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000033": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000034": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000035": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000036": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000037": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000038": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000039": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000003a": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000003b": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000003c": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000003d": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000003e": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000003f": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000040": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000041": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000042": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000043": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000044": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000045": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000046": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000047": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000048": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000049": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000004a": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000004b": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000004c": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000004d": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000004e": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000004f": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000050": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000051": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000052": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000053": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000054": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000055": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000056": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000057": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000058": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000059": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000005a": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000005b": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000005c": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000005d": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000005e": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000005f": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000060": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000061": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000062": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000063": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000064": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000065": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000066": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000067": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000068": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000069": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000006a": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000006b": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000006c": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000006d": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000006e": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000006f": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000070": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000071": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000072": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000073": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000074": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000075": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000076": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000077": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000078": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000079": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000007a": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000007b": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000007c": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000007d": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000007e": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000007f": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000080": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000081": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000082": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000083": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000084": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000085": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000086": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000087": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000088": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000089": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000008a": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000008b": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000008c": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000008d": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000008e": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000008f": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000090": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000091": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000092": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000093": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000094": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000095": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000096": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000097": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000098": {
"balance": "0x1"
},
"0x0000000000000000000000000000000000000099": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000009a": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000009b": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000009c": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000009d": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000009e": {
"balance": "0x1"
},
"0x000000000000000000000000000000000000009f": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000a0": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000a1": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000a2": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000a3": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000a4": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000a5": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000a6": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000a7": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000a8": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000a9": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000aa": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000ab": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000ac": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000ad": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000ae": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000af": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000b0": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000b1": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000b2": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000b3": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000b4": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000b5": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000b6": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000b7": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000b8": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000b9": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000ba": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000bb": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000bc": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000bd": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000be": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000bf": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000c0": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000c1": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000c2": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000c3": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000c4": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000c5": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000c6": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000c7": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000c8": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000c9": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000ca": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000cb": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000cc": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000cd": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000ce": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000cf": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000d0": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000d1": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000d2": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000d3": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000d4": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000d5": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000d6": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000d7": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000d8": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000d9": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000da": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000db": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000dc": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000dd": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000de": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000df": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000e0": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000e1": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000e2": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000e3": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000e4": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000e5": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000e6": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000e7": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000e8": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000e9": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000ea": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000eb": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000ec": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000ed": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000ee": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000ef": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000f0": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000f1": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000f2": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000f3": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000f4": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000f5": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000f6": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000f7": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000f8": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000f9": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000fa": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000fb": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000fc": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000fd": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000fe": {
"balance": "0x1"
},
"0x00000000000000000000000000000000000000ff": {
"balance": "0x1"
},
"0x31b98d14007bdee637298086988a0bbd31184523": {
"balance": "0x200000000000000000000000000000000000000000000000000000000000000"
}
}
}

View File

@ -284,7 +284,6 @@ impl<'x> OpenBlock<'x> {
self.block.header.set_difficulty(*header.difficulty()); self.block.header.set_difficulty(*header.difficulty());
self.block.header.set_gas_limit(*header.gas_limit()); self.block.header.set_gas_limit(*header.gas_limit());
self.block.header.set_timestamp(header.timestamp()); self.block.header.set_timestamp(header.timestamp());
self.block.header.set_author(*header.author());
self.block.header.set_uncles_hash(*header.uncles_hash()); self.block.header.set_uncles_hash(*header.uncles_hash());
self.block.header.set_transactions_root(*header.transactions_root()); self.block.header.set_transactions_root(*header.transactions_root());
// TODO: that's horrible. set only for backwards compatibility // TODO: that's horrible. set only for backwards compatibility
@ -405,15 +404,20 @@ impl LockedBlock {
/// Provide a valid seal in order to turn this into a `SealedBlock`. /// Provide a valid seal in order to turn this into a `SealedBlock`.
/// ///
/// NOTE: This does not check the validity of `seal` with the engine. /// NOTE: This does not check the validity of `seal` with the engine.
pub fn seal(self, engine: &EthEngine, seal: Vec<Bytes>) -> Result<SealedBlock, BlockError> { pub fn seal(self, engine: &EthEngine, seal: Vec<Bytes>) -> Result<SealedBlock, Error> {
let expected_seal_fields = engine.seal_fields(&self.block.header); let expected_seal_fields = engine.seal_fields(&self.header);
let mut s = self; let mut s = self;
if seal.len() != expected_seal_fields { if seal.len() != expected_seal_fields {
return Err(BlockError::InvalidSealArity( Err(BlockError::InvalidSealArity(Mismatch {
Mismatch { expected: expected_seal_fields, found: seal.len() })); expected: expected_seal_fields,
found: seal.len()
}))?;
} }
s.block.header.set_seal(seal); s.block.header.set_seal(seal);
engine.on_seal_block(&mut s.block)?;
s.block.header.compute_hash(); s.block.header.compute_hash();
Ok(SealedBlock { Ok(SealedBlock {
block: s.block block: s.block
}) })
@ -422,6 +426,7 @@ impl LockedBlock {
/// Provide a valid seal in order to turn this into a `SealedBlock`. /// Provide a valid seal in order to turn this into a `SealedBlock`.
/// This does check the validity of `seal` with the engine. /// This does check the validity of `seal` with the engine.
/// Returns the `ClosedBlock` back again if the seal is no good. /// Returns the `ClosedBlock` back again if the seal is no good.
/// TODO(https://github.com/paritytech/parity-ethereum/issues/10407): This is currently only used in POW chain call paths, we should really merge it with seal() above.
pub fn try_seal( pub fn try_seal(
self, self,
engine: &EthEngine, engine: &EthEngine,
@ -463,7 +468,7 @@ impl Drain for SealedBlock {
} }
/// Enact the block given by block header, transactions and uncles /// Enact the block given by block header, transactions and uncles
fn enact( pub(crate) fn enact(
header: Header, header: Header,
transactions: Vec<SignedTransaction>, transactions: Vec<SignedTransaction>,
uncles: Vec<Header>, uncles: Vec<Header>,
@ -476,13 +481,12 @@ fn enact(
is_epoch_begin: bool, is_epoch_begin: bool,
ancestry: &mut Iterator<Item=ExtendedHeader>, ancestry: &mut Iterator<Item=ExtendedHeader>,
) -> Result<LockedBlock, Error> { ) -> Result<LockedBlock, Error> {
{ // For trace log
if ::log::max_level() >= ::log::Level::Trace { let trace_state = if log_enabled!(target: "enact", ::log::Level::Trace) {
let s = State::from_existing(db.boxed_clone(), parent.state_root().clone(), engine.account_start_nonce(parent.number() + 1), factories.clone())?; Some(State::from_existing(db.boxed_clone(), parent.state_root().clone(), engine.account_start_nonce(parent.number() + 1), factories.clone())?)
trace!(target: "enact", "num={}, root={}, author={}, author_balance={}\n", } else {
header.number(), s.root(), header.author(), s.balance(&header.author())?); None
} };
}
let mut b = OpenBlock::new( let mut b = OpenBlock::new(
engine, engine,
@ -491,13 +495,23 @@ fn enact(
db, db,
parent, parent,
last_hashes, last_hashes,
Address::new(), // Engine such as Clique will calculate author from extra_data.
// this is only important for executing contracts as the 'executive_author'.
engine.executive_author(&header)?,
(3141562.into(), 31415620.into()), (3141562.into(), 31415620.into()),
vec![], vec![],
is_epoch_begin, is_epoch_begin,
ancestry, ancestry,
)?; )?;
if let Some(ref s) = trace_state {
let env = b.env_info();
let root = s.root();
let author_balance = s.balance(&env.author)?;
trace!(target: "enact", "num={}, root={}, author={}, author_balance={}\n",
b.block.header.number(), root, env.author, author_balance);
}
b.populate_from(&header); b.populate_from(&header);
b.push_transactions(transactions)?; b.push_transactions(transactions)?;
@ -563,6 +577,7 @@ mod tests {
last_hashes: Arc<LastHashes>, last_hashes: Arc<LastHashes>,
factories: Factories, factories: Factories,
) -> Result<LockedBlock, Error> { ) -> Result<LockedBlock, Error> {
let block = Unverified::from_rlp(block_bytes)?; let block = Unverified::from_rlp(block_bytes)?;
let header = block.header; let header = block.header;
let transactions: Result<Vec<_>, Error> = block let transactions: Result<Vec<_>, Error> = block

View File

@ -399,6 +399,7 @@ impl Importer {
let db = client.state_db.read().boxed_clone_canon(header.parent_hash()); let db = client.state_db.read().boxed_clone_canon(header.parent_hash());
let is_epoch_begin = chain.epoch_transition(parent.number(), *header.parent_hash()).is_some(); let is_epoch_begin = chain.epoch_transition(parent.number(), *header.parent_hash()).is_some();
let enact_result = enact_verified( let enact_result = enact_verified(
block, block,
engine, engine,
@ -2515,7 +2516,11 @@ impl SnapshotClient for Client {}
impl Drop for Client { impl Drop for Client {
fn drop(&mut self) { fn drop(&mut self) {
self.engine.stop(); if let Some(c) = Arc::get_mut(&mut self.engine) {
c.stop()
} else {
warn!(target: "shutdown", "unable to get mut ref for engine for shutdown.");
}
} }
} }

View File

@ -0,0 +1,369 @@
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
// This file is part of Parity Ethereum.
// Parity Ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity Ethereum 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 General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::collections::{HashMap, BTreeSet, VecDeque};
use std::fmt;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use engines::EngineError;
use engines::clique::util::{extract_signers, recover_creator};
use engines::clique::{VoteType, DIFF_INTURN, DIFF_NOTURN, NULL_AUTHOR, SIGNING_DELAY_NOTURN_MS};
use error::{Error, BlockError};
use ethereum_types::{Address, H64};
use rand::Rng;
use types::BlockNumber;
use types::header::Header;
use unexpected::Mismatch;
#[cfg(not(feature = "time_checked_add"))]
use time_utils::CheckedSystemTime;
/// Type that keeps track of the state for a given vote
// Votes that go against the proposal aren't counted since it's equivalent to not voting
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
pub struct VoteState {
kind: VoteType,
votes: u64,
}
/// Type that represent a vote
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
pub struct Vote {
block_number: BlockNumber,
beneficiary: Address,
kind: VoteType,
signer: Address,
reverted: bool,
}
/// Type that represent a pending vote
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd)]
pub struct PendingVote {
signer: Address,
beneficiary: Address,
}
/// Clique state for each block.
#[cfg(not(test))]
#[derive(Clone, Debug, Default)]
pub struct CliqueBlockState {
/// Current votes for a beneficiary
votes: HashMap<PendingVote, VoteState>,
/// A list of all votes for the given epoch
votes_history: Vec<Vote>,
/// a list of all valid signer, sorted by ascending order.
signers: BTreeSet<Address>,
/// a deque of recent signer, new entry should be pushed front, apply() modifies this.
recent_signers: VecDeque<Address>,
/// inturn signing should wait until this time
pub next_timestamp_inturn: Option<SystemTime>,
/// noturn signing should wait until this time
pub next_timestamp_noturn: Option<SystemTime>,
}
#[cfg(test)]
#[derive(Clone, Debug, Default)]
pub struct CliqueBlockState {
/// All recorded votes for a given signer, `Vec<PendingVote>` is a stack of votes
pub votes: HashMap<PendingVote, VoteState>,
/// A list of all votes for the given epoch
pub votes_history: Vec<Vote>,
/// a list of all valid signer, sorted by ascending order.
pub signers: BTreeSet<Address>,
/// a deque of recent signer, new entry should be pushed front, apply() modifies this.
pub recent_signers: VecDeque<Address>,
/// inturn signing should wait until this time
pub next_timestamp_inturn: Option<SystemTime>,
/// noturn signing should wait until this time
pub next_timestamp_noturn: Option<SystemTime>,
}
impl fmt::Display for CliqueBlockState {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let signers: Vec<String> = self.signers.iter()
.map(|s|
format!("{} {:?}",
s,
self.votes.iter().map(|(v, s)| format!("[beneficiary {}, votes: {}]", v.beneficiary, s.votes))
.collect::<Vec<_>>()
)
)
.collect();
let recent_signers: Vec<String> = self.recent_signers.iter().map(|s| format!("{}", s)).collect();
let num_votes = self.votes_history.len();
let add_votes = self.votes_history.iter().filter(|v| v.kind == VoteType::Add).count();
let rm_votes = self.votes_history.iter().filter(|v| v.kind == VoteType::Remove).count();
let reverted_votes = self.votes_history.iter().filter(|v| v.reverted).count();
write!(f,
"Votes {{ \n signers: {:?} \n recent_signers: {:?} \n number of votes: {} \n number of add votes {}
\r number of remove votes {} \n number of reverted votes: {}}}",
signers, recent_signers, num_votes, add_votes, rm_votes, reverted_votes)
}
}
impl CliqueBlockState {
/// Create new state with given information, this is used creating new state from Checkpoint block.
pub fn new(signers: BTreeSet<Address>) -> Self {
CliqueBlockState {
signers,
..Default::default()
}
}
// see https://github.com/ethereum/go-ethereum/blob/master/consensus/clique/clique.go#L474
fn verify(&self, header: &Header) -> Result<Address, Error> {
let creator = recover_creator(header)?.clone();
// The signer is not authorized
if !self.signers.contains(&creator) {
trace!(target: "engine", "current state: {}", self);
Err(EngineError::NotAuthorized(creator))?
}
// The signer has signed a block too recently
if self.recent_signers.contains(&creator) {
trace!(target: "engine", "current state: {}", self);
Err(EngineError::CliqueTooRecentlySigned(creator))?
}
// Wrong difficulty
let inturn = self.is_inturn(header.number(), &creator);
if inturn && *header.difficulty() != DIFF_INTURN {
Err(BlockError::InvalidDifficulty(Mismatch {
expected: DIFF_INTURN,
found: *header.difficulty(),
}))?
}
if !inturn && *header.difficulty() != DIFF_NOTURN {
Err(BlockError::InvalidDifficulty(Mismatch {
expected: DIFF_NOTURN,
found: *header.difficulty(),
}))?
}
Ok(creator)
}
/// Verify and apply a new header to current state
pub fn apply(&mut self, header: &Header, is_checkpoint: bool) -> Result<Address, Error> {
let creator = self.verify(header)?;
self.recent_signers.push_front(creator);
self.rotate_recent_signers();
if is_checkpoint {
// checkpoint block should not affect previous tallying, so we check that.
let signers = extract_signers(header)?;
if self.signers != signers {
let invalid_signers: Vec<String> = signers.into_iter()
.filter(|s| !self.signers.contains(s))
.map(|s| format!("{}", s))
.collect();
Err(EngineError::CliqueFaultyRecoveredSigners(invalid_signers))?
};
// TODO(niklasad1): I'm not sure if we should shrink here because it is likely that next epoch
// will need some memory and might be better for allocation algorithm to decide whether to shrink or not
// (typically doubles or halves the allocted memory when necessary)
self.votes.clear();
self.votes_history.clear();
self.votes.shrink_to_fit();
self.votes_history.shrink_to_fit();
}
// Contains vote
if *header.author() != NULL_AUTHOR {
let decoded_seal = header.decode_seal::<Vec<_>>()?;
if decoded_seal.len() != 2 {
Err(BlockError::InvalidSealArity(Mismatch { expected: 2, found: decoded_seal.len() }))?
}
let nonce: H64 = decoded_seal[1].into();
self.update_signers_on_vote(VoteType::from_nonce(nonce)?, creator, *header.author(), header.number())?;
}
Ok(creator)
}
fn update_signers_on_vote(
&mut self,
kind: VoteType,
signer: Address,
beneficiary: Address,
block_number: u64
) -> Result<(), Error> {
trace!(target: "engine", "Attempt vote {:?} {:?}", kind, beneficiary);
let pending_vote = PendingVote { signer, beneficiary };
let reverted = if self.is_valid_vote(&beneficiary, kind) {
self.add_vote(pending_vote, kind)
} else {
// This case only happens if a `signer` wants to revert their previous vote
// (does nothing if no previous vote was found)
self.revert_vote(pending_vote)
};
// Add all votes to the history
self.votes_history.push(
Vote {
block_number,
beneficiary,
kind,
signer,
reverted,
});
// If no vote was found for the beneficiary return `early` but don't propogate an error
let (votes, vote_kind) = match self.get_current_votes_and_kind(beneficiary) {
Some((v, k)) => (v, k),
None => return Ok(()),
};
let threshold = self.signers.len() / 2;
debug!(target: "engine", "{}/{} votes to have consensus", votes, threshold + 1);
trace!(target: "engine", "votes: {:?}", votes);
if votes > threshold {
match vote_kind {
VoteType::Add => {
if self.signers.insert(beneficiary) {
debug!(target: "engine", "added new signer: {}", beneficiary);
}
}
VoteType::Remove => {
if self.signers.remove(&beneficiary) {
debug!(target: "engine", "removed signer: {}", beneficiary);
}
}
}
self.rotate_recent_signers();
self.remove_all_votes_from(beneficiary);
}
Ok(())
}
/// Calculate the next timestamp for `inturn` and `noturn` fails if any of them can't be represented as
/// `SystemTime`
// TODO(niklasad1): refactor this method to be in constructor of `CliqueBlockState` instead.
// This is a quite bad API because we must mutate both variables even when already `inturn` fails
// That's why we can't return early and must have the `if-else` in the end
pub fn calc_next_timestamp(&mut self, timestamp: u64, period: u64) -> Result<(), Error> {
let inturn = UNIX_EPOCH.checked_add(Duration::from_secs(timestamp.saturating_add(period)));
self.next_timestamp_inturn = inturn;
let delay = Duration::from_millis(
rand::thread_rng().gen_range(0u64, (self.signers.len() as u64 / 2 + 1) * SIGNING_DELAY_NOTURN_MS));
self.next_timestamp_noturn = inturn.map(|inturn| {
inturn + delay
});
if self.next_timestamp_inturn.is_some() && self.next_timestamp_noturn.is_some() {
Ok(())
} else {
Err(BlockError::TimestampOverflow)?
}
}
/// Returns true if the block difficulty should be `inturn`
pub fn is_inturn(&self, current_block_number: u64, author: &Address) -> bool {
if let Some(pos) = self.signers.iter().position(|x| *author == *x) {
return current_block_number % self.signers.len() as u64 == pos as u64;
}
false
}
/// Returns whether the signer is authorized to sign a block
pub fn is_authorized(&self, author: &Address) -> bool {
self.signers.contains(author) && !self.recent_signers.contains(author)
}
/// Returns whether it makes sense to cast the specified vote in the
/// current state (e.g. don't try to add an already authorized signer).
pub fn is_valid_vote(&self, address: &Address, vote_type: VoteType) -> bool {
let in_signer = self.signers.contains(address);
match vote_type {
VoteType::Add => !in_signer,
VoteType::Remove => in_signer,
}
}
/// Returns the list of current signers
pub fn signers(&self) -> &BTreeSet<Address> {
&self.signers
}
// Note this method will always return `true` but it is intended for a uniform `API`
fn add_vote(&mut self, pending_vote: PendingVote, kind: VoteType) -> bool {
self.votes.entry(pending_vote)
.and_modify(|state| {
state.votes = state.votes.saturating_add(1);
})
.or_insert_with(|| VoteState { kind, votes: 1 });
true
}
fn revert_vote(&mut self, pending_vote: PendingVote) -> bool {
let mut revert = false;
let mut remove = false;
self.votes.entry(pending_vote).and_modify(|state| {
if state.votes.saturating_sub(1) == 0 {
remove = true;
}
revert = true;
});
if remove {
self.votes.remove(&pending_vote);
}
revert
}
fn get_current_votes_and_kind(&self, beneficiary: Address) -> Option<(usize, VoteType)> {
let kind = self.votes.iter()
.find(|(v, _t)| v.beneficiary == beneficiary)
.map(|(_v, t)| t.kind)?;
let votes = self.votes.keys()
.filter(|vote| vote.beneficiary == beneficiary)
.count();
Some((votes, kind))
}
fn rotate_recent_signers(&mut self) {
if self.recent_signers.len() >= ( self.signers.len() / 2 ) + 1 {
self.recent_signers.pop_back();
}
}
fn remove_all_votes_from(&mut self, beneficiary: Address) {
self.votes = std::mem::replace(&mut self.votes, HashMap::new())
.into_iter()
.filter(|(v, _t)| v.signer != beneficiary && v.beneficiary != beneficiary)
.collect();
}
}

View File

@ -0,0 +1,768 @@
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
// This file is part of Parity Ethereum.
// Parity Ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity Ethereum 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 General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
//! Implementation of the Clique PoA Engine.
//!
//! File structure:
//! - mod.rs -> Provides the engine API implementation, with additional block state tracking
//! - block_state.rs -> Records the Clique state for given block.
//! - params.rs -> Contains the parameters for the Clique engine.
//! - step_service.rs -> An event loop to trigger sealing.
//! - util.rs -> Various standalone utility functions.
//! - tests.rs -> Consensus tests as defined in EIP-225.
/// How syncing works:
///
/// 1. Client will call:
/// - `Clique::verify_block_basic()`
/// - `Clique::verify_block_unordered()`
/// - `Clique::verify_block_family()`
/// 2. Using `Clique::state()` we try and retrieve the parent state. If this isn't found
/// we need to back-fill it from the last known checkpoint.
/// 3. Once we have a good state, we can record it using `CliqueBlockState::apply()`.
/// How sealing works:
///
/// 1. Set a signer using `Engine::set_signer()`. If a miner account was set up through
/// a config file or CLI flag `MinerService::set_author()` will eventually set the signer
/// 2. We check that the engine seals internally through `Clique::seals_internally()`
/// Note: This is always true for Clique
/// 3. Calling `Clique::new()` will spawn a `StepService` thread. This thread will call `Engine::step()`
/// periodically. Internally, the Clique `step()` function calls `Client::update_sealing()`, which is
/// what makes and seals a block.
/// 4. `Clique::generate_seal()` will then be called by `miner`. This will return a `Seal` which
/// is either a `Seal::None` or `Seal:Regular`. The following shows how a `Seal` variant is chosen:
/// a. We return `Seal::None` if no signer is available or the signer is not authorized.
/// b. If period == 0 and block has transactions, we return `Seal::Regular`, otherwise return `Seal::None`.
/// c. If we're `INTURN`, wait for at least `period` since last block before trying to seal.
/// d. If we're not `INTURN`, we wait for a random amount of time using the algorithm specified
/// in EIP-225 before trying to seal again.
/// 5. Miner will create new block, in process it will call several engine methods to do following:
/// a. `Clique::open_block_header_timestamp()` must set timestamp correctly.
/// b. `Clique::populate_from_parent()` must set difficulty to correct value.
/// Note: `Clique::populate_from_parent()` is used in both the syncing and sealing code paths.
/// 6. We call `Clique::on_seal_block()` which will allow us to modify the block header during seal generation.
/// 7. Finally, `Clique::verify_local_seal()` is called. After this, the syncing code path will be followed
/// in order to import the new block.
use std::cmp;
use std::collections::HashMap;
use std::collections::VecDeque;
use std::sync::{Arc, Weak};
use std::thread;
use std::time;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use block::ExecutedBlock;
use client::{BlockId, EngineClient};
use engines::clique::util::{extract_signers, recover_creator};
use engines::{Engine, EngineError, Seal};
use error::{BlockError, Error};
use ethereum_types::{Address, H64, H160, H256, U256};
use ethkey::Signature;
use hash::KECCAK_EMPTY_LIST_RLP;
use itertools::Itertools;
use lru_cache::LruCache;
use machine::{Call, EthereumMachine};
use parking_lot::RwLock;
use rand::Rng;
use super::signer::EngineSigner;
use unexpected::{Mismatch, OutOfBounds};
use types::BlockNumber;
use types::header::{ExtendedHeader, Header};
#[cfg(not(feature = "time_checked_add"))]
use time_utils::CheckedSystemTime;
use self::block_state::CliqueBlockState;
use self::params::CliqueParams;
use self::step_service::StepService;
mod params;
mod block_state;
mod step_service;
mod util;
// TODO(niklasad1): extract tester types into a separate mod to be shared in the code base
#[cfg(test)]
mod tests;
// Protocol constants
/// Fixed number of extra-data prefix bytes reserved for signer vanity
pub const VANITY_LENGTH: usize = 32;
/// Fixed number of extra-data suffix bytes reserved for signer signature
pub const SIGNATURE_LENGTH: usize = 65;
/// Address length of signer
pub const ADDRESS_LENGTH: usize = 20;
/// Nonce value for DROP vote
pub const NONCE_DROP_VOTE: H64 = H64([0; 8]);
/// Nonce value for AUTH vote
pub const NONCE_AUTH_VOTE: H64 = H64([0xff; 8]);
/// Difficulty for INTURN block
pub const DIFF_INTURN: U256 = U256([2, 0, 0, 0]);
/// Difficulty for NOTURN block
pub const DIFF_NOTURN: U256 = U256([1, 0, 0, 0]);
/// Default empty author field value
pub const NULL_AUTHOR: Address = H160([0x00; 20]);
/// Default empty nonce value
pub const NULL_NONCE: H64 = NONCE_DROP_VOTE;
/// Default value for mixhash
pub const NULL_MIXHASH: H256 = H256([0; 32]);
/// Default value for uncles hash
pub const NULL_UNCLES_HASH: H256 = KECCAK_EMPTY_LIST_RLP;
/// Default noturn block wiggle factor defined in spec.
pub const SIGNING_DELAY_NOTURN_MS: u64 = 500;
/// How many CliqueBlockState to cache in the memory.
pub const STATE_CACHE_NUM: usize = 128;
/// Vote to add or remove the beneficiary
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
pub enum VoteType {
Add,
Remove,
}
impl VoteType {
/// Try to construct a `Vote` from a nonce
pub fn from_nonce(nonce: H64) -> Result<Self, Error> {
if nonce == NONCE_AUTH_VOTE {
Ok(VoteType::Add)
} else if nonce == NONCE_DROP_VOTE {
Ok(VoteType::Remove)
} else {
Err(EngineError::CliqueInvalidNonce(nonce))?
}
}
/// Get the rlp encoding of the vote
pub fn as_rlp(&self) -> Vec<Vec<u8>> {
match self {
VoteType::Add => vec![rlp::encode(&NULL_MIXHASH), rlp::encode(&NONCE_AUTH_VOTE)],
VoteType::Remove => vec![rlp::encode(&NULL_MIXHASH), rlp::encode(&NONCE_DROP_VOTE)],
}
}
}
/// Clique Engine implementation
// block_state_by_hash -> block state indexed by header hash.
#[cfg(not(test))]
pub struct Clique {
epoch_length: u64,
period: u64,
machine: EthereumMachine,
client: RwLock<Option<Weak<EngineClient>>>,
block_state_by_hash: RwLock<LruCache<H256, CliqueBlockState>>,
proposals: RwLock<HashMap<Address, VoteType>>,
signer: RwLock<Option<Box<EngineSigner>>>,
step_service: Option<Arc<StepService>>,
}
#[cfg(test)]
/// Test version of `CliqueEngine` to make all fields public
pub struct Clique {
pub epoch_length: u64,
pub period: u64,
pub machine: EthereumMachine,
pub client: RwLock<Option<Weak<EngineClient>>>,
pub block_state_by_hash: RwLock<LruCache<H256, CliqueBlockState>>,
pub proposals: RwLock<HashMap<Address, VoteType>>,
pub signer: RwLock<Option<Box<EngineSigner>>>,
pub step_service: Option<Arc<StepService>>,
}
impl Clique {
/// Initialize Clique engine from empty state.
pub fn new(our_params: CliqueParams, machine: EthereumMachine) -> Result<Arc<Self>, Error> {
let mut engine = Clique {
epoch_length: our_params.epoch,
period: our_params.period,
client: Default::default(),
block_state_by_hash: RwLock::new(LruCache::new(STATE_CACHE_NUM)),
proposals: Default::default(),
signer: Default::default(),
machine,
step_service: None,
};
let res = Arc::new(engine);
if our_params.period > 0 {
engine.step_service = Some(StepService::start(Arc::downgrade(&res) as Weak<Engine<_>>));
}
Ok(res)
}
#[cfg(test)]
/// Initialize test variant of `CliqueEngine`,
/// Note we need to `mock` the miner and it is introduced to test block verification to trigger new blocks
/// to mainly test consensus edge cases
pub fn with_test(epoch_length: u64, period: u64) -> Self {
use spec::Spec;
Self {
epoch_length,
period,
client: Default::default(),
block_state_by_hash: RwLock::new(LruCache::new(STATE_CACHE_NUM)),
proposals: Default::default(),
signer: Default::default(),
machine: Spec::new_test_machine(),
step_service: None,
}
}
fn sign_header(&self, header: &Header) -> Result<(Signature, H256), Error> {
match self.signer.read().as_ref() {
None => {
Err(EngineError::RequiresSigner)?
}
Some(signer) => {
let digest = header.hash();
match signer.sign(digest) {
Ok(sig) => Ok((sig, digest)),
Err(e) => Err(EngineError::Custom(e.into()))?,
}
}
}
}
/// Construct an new state from given checkpoint header.
fn new_checkpoint_state(&self, header: &Header) -> Result<CliqueBlockState, Error> {
debug_assert_eq!(header.number() % self.epoch_length, 0);
let mut state = CliqueBlockState::new(
extract_signers(header)?);
// TODO(niklasad1): refactor to perform this check in the `CliqueBlockState` constructor instead
state.calc_next_timestamp(header.timestamp(), self.period)?;
Ok(state)
}
fn state_no_backfill(&self, hash: &H256) -> Option<CliqueBlockState> {
self.block_state_by_hash.write().get_mut(hash).cloned()
}
/// Get `CliqueBlockState` for given header, backfill from last checkpoint if needed.
fn state(&self, header: &Header) -> Result<CliqueBlockState, Error> {
let mut block_state_by_hash = self.block_state_by_hash.write();
if let Some(state) = block_state_by_hash.get_mut(&header.hash()) {
return Ok(state.clone());
}
// If we are looking for an checkpoint block state, we can directly reconstruct it.
if header.number() % self.epoch_length == 0 {
let state = self.new_checkpoint_state(header)?;
block_state_by_hash.insert(header.hash(), state.clone());
return Ok(state);
}
// BlockState is not found in memory, which means we need to reconstruct state from last checkpoint.
match self.client.read().as_ref().and_then(|w| w.upgrade()) {
None => {
return Err(EngineError::RequiresClient)?;
}
Some(c) => {
let last_checkpoint_number = header.number() - header.number() % self.epoch_length as u64;
debug_assert_ne!(last_checkpoint_number, header.number());
let mut chain: &mut VecDeque<Header> = &mut VecDeque::with_capacity(
(header.number() - last_checkpoint_number + 1) as usize);
// Put ourselves in.
chain.push_front(header.clone());
// populate chain to last checkpoint
loop {
let (last_parent_hash, last_num) = {
let l = chain.front().expect("chain has at least one element; qed");
(*l.parent_hash(), l.number())
};
if last_num == last_checkpoint_number + 1 {
break;
}
match c.block_header(BlockId::Hash(last_parent_hash)) {
None => {
return Err(BlockError::UnknownParent(last_parent_hash))?;
}
Some(next) => {
chain.push_front(next.decode()?);
}
}
}
// Catching up state, note that we don't really store block state for intermediary blocks,
// for speed.
let backfill_start = time::Instant::now();
trace!(target: "engine",
"Back-filling block state. last_checkpoint_number: {}, target: {}({}).",
last_checkpoint_number, header.number(), header.hash());
// Get the state for last checkpoint.
let last_checkpoint_hash = *chain.front()
.expect("chain has at least one element; qed")
.parent_hash();
let last_checkpoint_header = match c.block_header(BlockId::Hash(last_checkpoint_hash)) {
None => return Err(EngineError::CliqueMissingCheckpoint(last_checkpoint_hash))?,
Some(header) => header.decode()?,
};
let last_checkpoint_state = match block_state_by_hash.get_mut(&last_checkpoint_hash) {
Some(state) => state.clone(),
None => self.new_checkpoint_state(&last_checkpoint_header)?,
};
block_state_by_hash.insert(last_checkpoint_header.hash(), last_checkpoint_state.clone());
// Backfill!
let mut new_state = last_checkpoint_state.clone();
for item in chain {
new_state.apply(item, false)?;
}
new_state.calc_next_timestamp(header.timestamp(), self.period)?;
block_state_by_hash.insert(header.hash(), new_state.clone());
let elapsed = backfill_start.elapsed();
trace!(target: "engine", "Back-filling succeed, took {} ms.", elapsed.as_millis());
Ok(new_state)
}
}
}
}
impl Engine<EthereumMachine> for Clique {
fn name(&self) -> &str { "Clique" }
fn machine(&self) -> &EthereumMachine { &self.machine }
// Clique use same fields, nonce + mixHash
fn seal_fields(&self, _header: &Header) -> usize { 2 }
fn maximum_uncle_count(&self, _block: BlockNumber) -> usize { 0 }
fn on_new_block(
&self,
_block: &mut ExecutedBlock,
_epoch_begin: bool,
_ancestry: &mut Iterator<Item=ExtendedHeader>,
) -> Result<(), Error> {
Ok(())
}
// Clique has no block reward.
fn on_close_block(&self, _block: &mut ExecutedBlock) -> Result<(), Error> {
Ok(())
}
fn on_seal_block(&self, block: &mut ExecutedBlock) -> Result<(), Error> {
trace!(target: "engine", "on_seal_block");
let header = &mut block.header;
let state = self.state_no_backfill(header.parent_hash())
.ok_or_else(|| BlockError::UnknownParent(*header.parent_hash()))?;
let is_checkpoint = header.number() % self.epoch_length == 0;
header.set_author(NULL_AUTHOR);
// Cast a random Vote if not checkpoint
if !is_checkpoint {
// TODO(niklasad1): this will always be false because `proposals` is never written to
let votes = self.proposals.read().iter()
.filter(|(address, vote_type)| state.is_valid_vote(*address, **vote_type))
.map(|(address, vote_type)| (*address, *vote_type))
.collect_vec();
if !votes.is_empty() {
// Pick a random vote.
let random_vote = rand::thread_rng().gen_range(0 as usize, votes.len());
let (beneficiary, vote_type) = votes[random_vote];
trace!(target: "engine", "Casting vote: beneficiary {}, type {:?} ", beneficiary, vote_type);
header.set_author(beneficiary);
header.set_seal(vote_type.as_rlp());
}
}
// Work on clique seal.
let mut seal: Vec<u8> = Vec::with_capacity(VANITY_LENGTH + SIGNATURE_LENGTH);
// At this point, extra_data should only contain miner vanity.
if header.extra_data().len() != VANITY_LENGTH {
Err(BlockError::ExtraDataOutOfBounds(OutOfBounds {
min: Some(VANITY_LENGTH),
max: Some(VANITY_LENGTH),
found: header.extra_data().len()
}))?;
}
// vanity
{
seal.extend_from_slice(&header.extra_data()[0..VANITY_LENGTH]);
}
// If we are building an checkpoint block, add all signers now.
if is_checkpoint {
seal.reserve(state.signers().len() * 20);
state.signers().iter().foreach(|addr| {
seal.extend_from_slice(&addr[..]);
});
}
header.set_extra_data(seal.clone());
// append signature onto extra_data
let (sig, _msg) = self.sign_header(&header)?;
seal.extend_from_slice(&sig[..]);
header.set_extra_data(seal.clone());
header.compute_hash();
// locally sealed block don't go through valid_block_family(), so we have to record state here.
let mut new_state = state.clone();
new_state.apply(&header, is_checkpoint)?;
new_state.calc_next_timestamp(header.timestamp(), self.period)?;
self.block_state_by_hash.write().insert(header.hash(), new_state);
trace!(target: "engine", "on_seal_block: finished, final header: {:?}", header);
Ok(())
}
/// Clique doesn't require external work to seal, so we always return true here.
fn seals_internally(&self) -> Option<bool> {
Some(true)
}
/// Returns if we are ready to seal, the real sealing (signing extra_data) is actually done in `on_seal_block()`.
fn generate_seal(&self, block: &ExecutedBlock, parent: &Header) -> Seal {
trace!(target: "engine", "tried to generate_seal");
let null_seal = util::null_seal();
if block.header.number() == 0 {
trace!(target: "engine", "attempted to seal genesis block");
return Seal::None;
}
// if sealing period is 0, and not an checkpoint block, refuse to seal
if self.period == 0 {
if block.transactions.is_empty() && block.header.number() % self.epoch_length != 0 {
return Seal::None;
}
return Seal::Regular(null_seal);
}
// Check we actually have authority to seal.
if let Some(author) = self.signer.read().as_ref().map(|x| x.address()) {
// ensure the voting state exists
match self.state(&parent) {
Err(e) => {
warn!(target: "engine", "generate_seal: can't get parent state(number: {}, hash: {}): {} ",
parent.number(), parent.hash(), e);
return Seal::None;
}
Ok(state) => {
// Are we authorized to seal?
if !state.is_authorized(&author) {
trace!(target: "engine", "generate_seal: Not authorized to sign right now.");
// wait for one third of period to try again.
thread::sleep(Duration::from_secs(self.period / 3 + 1));
return Seal::None;
}
let inturn = state.is_inturn(block.header.number(), &author);
let now = SystemTime::now();
let limit = match inturn {
true => state.next_timestamp_inturn.unwrap_or(now),
false => state.next_timestamp_noturn.unwrap_or(now),
};
// Wait for the right moment.
if now < limit {
trace!(target: "engine",
"generate_seal: sleeping to sign: inturn: {}, now: {:?}, to: {:?}.",
inturn, now, limit);
match limit.duration_since(SystemTime::now()) {
Ok(duration) => {
thread::sleep(duration);
},
Err(e) => {
warn!(target:"engine", "generate_seal: unable to sleep, err: {}", e);
return Seal::None;
}
}
}
trace!(target: "engine", "generate_seal: seal ready for block {}, txs: {}.",
block.header.number(), block.transactions.len());
return Seal::Regular(null_seal);
}
}
}
Seal::None
}
fn verify_local_seal(&self, _header: &Header) -> Result<(), Error> { Ok(()) }
fn verify_block_basic(&self, header: &Header) -> Result<(), Error> {
// Largely same as https://github.com/ethereum/go-ethereum/blob/master/consensus/clique/clique.go#L275
// Ignore genesis block.
if header.number() == 0 {
return Ok(());
}
// Don't waste time checking blocks from the future
{
let limit = SystemTime::now().checked_add(Duration::from_secs(self.period))
.ok_or(BlockError::TimestampOverflow)?;
// This should succeed under the contraints that the system clock works
let limit_as_dur = limit.duration_since(UNIX_EPOCH).map_err(|e| {
Box::new(format!("Converting SystemTime to Duration failed: {}", e))
})?;
let hdr = Duration::from_secs(header.timestamp());
if hdr > limit_as_dur {
let found = UNIX_EPOCH.checked_add(hdr).ok_or(BlockError::TimestampOverflow)?;
Err(BlockError::TemporarilyInvalid(OutOfBounds {
min: None,
max: Some(limit),
found,
}))?
}
}
let is_checkpoint = header.number() % self.epoch_length == 0;
if is_checkpoint && *header.author() != NULL_AUTHOR {
return Err(EngineError::CliqueWrongAuthorCheckpoint(Mismatch {
expected: 0.into(),
found: *header.author(),
}))?;
}
let seal_fields = header.decode_seal::<Vec<_>>()?;
if seal_fields.len() != 2 {
Err(BlockError::InvalidSealArity(Mismatch {
expected: 2,
found: seal_fields.len(),
}))?
}
let mixhash: H256 = seal_fields[0].into();
let nonce: H64 = seal_fields[1].into();
// Nonce must be 0x00..0 or 0xff..f
if nonce != NONCE_DROP_VOTE && nonce != NONCE_AUTH_VOTE {
Err(EngineError::CliqueInvalidNonce(nonce))?;
}
if is_checkpoint && nonce != NULL_NONCE {
Err(EngineError::CliqueInvalidNonce(nonce))?;
}
// Ensure that the mix digest is zero as Clique don't have fork protection currently
if mixhash != NULL_MIXHASH {
Err(BlockError::MismatchedH256SealElement(Mismatch {
expected: NULL_MIXHASH,
found: mixhash,
}))?
}
let extra_data_len = header.extra_data().len();
if extra_data_len < VANITY_LENGTH {
Err(EngineError::CliqueMissingVanity)?
}
if extra_data_len < VANITY_LENGTH + SIGNATURE_LENGTH {
Err(EngineError::CliqueMissingSignature)?
}
let signers = extra_data_len - (VANITY_LENGTH + SIGNATURE_LENGTH);
// Checkpoint blocks must at least contain one signer
if is_checkpoint && signers == 0 {
Err(EngineError::CliqueCheckpointNoSigner)?
}
// Addresses must be be divisable by 20
if is_checkpoint && signers % ADDRESS_LENGTH != 0 {
Err(EngineError::CliqueCheckpointInvalidSigners(signers))?
}
// Ensure that the block doesn't contain any uncles which are meaningless in PoA
if *header.uncles_hash() != NULL_UNCLES_HASH {
Err(BlockError::InvalidUnclesHash(Mismatch {
expected: NULL_UNCLES_HASH,
found: *header.uncles_hash(),
}))?
}
// Ensure that the block's difficulty is meaningful (may not be correct at this point)
if *header.difficulty() != DIFF_INTURN && *header.difficulty() != DIFF_NOTURN {
Err(BlockError::DifficultyOutOfBounds(OutOfBounds {
min: Some(DIFF_NOTURN),
max: Some(DIFF_INTURN),
found: *header.difficulty(),
}))?
}
// All basic checks passed, continue to next phase
Ok(())
}
fn verify_block_unordered(&self, _header: &Header) -> Result<(), Error> {
// Nothing to check here.
Ok(())
}
/// Verify block family by looking up parent state (backfill if needed), then try to apply current header.
/// see https://github.com/ethereum/go-ethereum/blob/master/consensus/clique/clique.go#L338
fn verify_block_family(&self, header: &Header, parent: &Header) -> Result<(), Error> {
// Ignore genesis block.
if header.number() == 0 {
return Ok(());
}
// parent sanity check
if parent.hash() != *header.parent_hash() || header.number() != parent.number() + 1 {
Err(BlockError::UnknownParent(parent.hash()))?
}
// Ensure that the block's timestamp isn't too close to it's parent
let limit = parent.timestamp().saturating_add(self.period);
if limit > header.timestamp() {
let max = UNIX_EPOCH.checked_add(Duration::from_secs(header.timestamp()));
let found = UNIX_EPOCH.checked_add(Duration::from_secs(limit))
.ok_or(BlockError::TimestampOverflow)?;
Err(BlockError::InvalidTimestamp(OutOfBounds {
min: None,
max,
found,
}))?
}
// Retrieve the parent state
let parent_state = self.state(&parent)?;
// Try to apply current state, apply() will further check signer and recent signer.
let mut new_state = parent_state.clone();
new_state.apply(header, header.number() % self.epoch_length == 0)?;
new_state.calc_next_timestamp(header.timestamp(), self.period)?;
self.block_state_by_hash.write().insert(header.hash(), new_state);
Ok(())
}
fn genesis_epoch_data(&self, header: &Header, _call: &Call) -> Result<Vec<u8>, String> {
let mut state = self.new_checkpoint_state(header).expect("Unable to parse genesis data.");
state.calc_next_timestamp(header.timestamp(), self.period).map_err(|e| format!("{}", e))?;
self.block_state_by_hash.write().insert(header.hash(), state);
// no proof.
Ok(Vec::new())
}
// Our task here is to set difficulty
fn populate_from_parent(&self, header: &mut Header, parent: &Header) {
// TODO(https://github.com/paritytech/parity-ethereum/issues/10410): this is a horrible hack,
// it is due to the fact that enact and miner both use OpenBlock::new() which will both call
// this function. more refactoring is definitely needed.
if header.extra_data().len() < VANITY_LENGTH + SIGNATURE_LENGTH {
trace!(target: "engine", "populate_from_parent in sealing");
// It's unclear how to prevent creating new blocks unless we are authorized, the best way (and geth does this too)
// it's just to ignore setting an correct difficulty here, we will check authorization in next step in generate_seal anyway.
if let Some(signer) = self.signer.read().as_ref() {
let state = match self.state(&parent) {
Err(e) => {
trace!(target: "engine", "populate_from_parent: Unable to find parent state: {}, ignored.", e);
return;
}
Ok(state) => state,
};
if state.is_authorized(&signer.address()) {
if state.is_inturn(header.number(), &signer.address()) {
header.set_difficulty(DIFF_INTURN);
} else {
header.set_difficulty(DIFF_NOTURN);
}
}
} else {
trace!(target: "engine", "populate_from_parent: no signer registered");
}
}
}
fn set_signer(&self, signer: Box<EngineSigner>) {
trace!(target: "engine", "set_signer: {}", signer.address());
*self.signer.write() = Some(signer);
}
fn register_client(&self, client: Weak<EngineClient>) {
*self.client.write() = Some(client.clone());
}
fn step(&self) {
if self.signer.read().is_some() {
if let Some(ref weak) = *self.client.read() {
if let Some(c) = weak.upgrade() {
c.update_sealing();
}
}
}
}
fn stop(&mut self) {
if let Some(mut s) = self.step_service.as_mut() {
Arc::get_mut(&mut s).map(|x| x.stop());
} else {
warn!(target: "engine", "Stopping `CliqueStepService` failed requires mutable access");
}
}
/// Clique timestamp is set to parent + period , or current time which ever is higher.
fn open_block_header_timestamp(&self, parent_timestamp: u64) -> u64 {
let now = time::SystemTime::now().duration_since(time::UNIX_EPOCH).unwrap_or_default();
cmp::max(now.as_secs() as u64, parent_timestamp.saturating_add(self.period))
}
fn is_timestamp_valid(&self, header_timestamp: u64, parent_timestamp: u64) -> bool {
header_timestamp >= parent_timestamp.saturating_add(self.period)
}
fn fork_choice(&self, new: &ExtendedHeader, current: &ExtendedHeader) -> super::ForkChoice {
super::total_difficulty_fork_choice(new, current)
}
// Clique uses the author field for voting, the real author is hidden in the `extra_data` field.
// So when executing tx's (like in `enact()`) we want to use the executive author
fn executive_author(&self, header: &Header) -> Result<Address, Error> {
recover_creator(header)
}
}

View File

@ -0,0 +1,41 @@
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
// This file is part of Parity Ethereum.
// Parity Ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity Ethereum 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 General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
//! Clique specific parameters.
use ethjson;
/// `Clique` params.
pub struct CliqueParams {
/// Period as defined in EIP
pub period: u64,
/// Epoch length as defined in EIP
pub epoch: u64,
}
impl From<ethjson::spec::CliqueParams> for CliqueParams {
fn from(p: ethjson::spec::CliqueParams) -> Self {
let period = p.period.map_or_else(|| 30000 as u64, Into::into);
let epoch = p.epoch.map_or_else(|| 15 as u64, Into::into);
assert!(epoch > 0);
CliqueParams {
period,
epoch,
}
}
}

View File

@ -0,0 +1,77 @@
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
// This file is part of Parity Ethereum.
// Parity Ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity Ethereum 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 General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::sync::Weak;
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::Duration;
use std::thread;
use std::sync::Arc;
use engines::Engine;
use machine::Machine;
/// Service that is managing the engine
pub struct StepService {
shutdown: Arc<AtomicBool>,
thread: Option<thread::JoinHandle<()>>,
}
impl StepService {
/// Start the `StepService`
pub fn start<M: Machine + 'static>(engine: Weak<Engine<M>>) -> Arc<Self> {
let shutdown = Arc::new(AtomicBool::new(false));
let s = shutdown.clone();
let thread = thread::Builder::new()
.name("CliqueStepService".into())
.spawn(move || {
// startup delay.
thread::sleep(Duration::from_secs(5));
loop {
// see if we are in shutdown.
if shutdown.load(Ordering::Acquire) {
trace!(target: "miner", "CliqueStepService: received shutdown signal!");
break;
}
trace!(target: "miner", "CliqueStepService: triggering sealing");
// Try sealing
engine.upgrade().map(|x| x.step());
// Yield
thread::sleep(Duration::from_millis(2000));
}
trace!(target: "miner", "CliqueStepService: shutdown.");
}).expect("CliqueStepService thread failed");
Arc::new(StepService {
shutdown: s,
thread: Some(thread),
})
}
/// Stop the `StepService`
pub fn stop(&mut self) {
trace!(target: "miner", "CliqueStepService: shutting down.");
self.shutdown.store(true, Ordering::Release);
if let Some(t) = self.thread.take() {
t.join().expect("CliqueStepService thread panicked!");
}
}
}

View File

@ -0,0 +1,804 @@
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
// This file is part of Parity Ethereum.
// Parity Ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity Ethereum 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 General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
//! Consensus tests for `PoA Clique Engine`, see http://eips.ethereum.org/EIPS/eip-225 for more information
use block::*;
use engines::Engine;
use error::{Error, ErrorKind};
use ethereum_types::{Address, H256};
use ethkey::{Secret, KeyPair};
use state_db::StateDB;
use super::*;
use test_helpers::get_temp_state_db;
use std::sync::Arc;
use std::collections::HashMap;
/// Possible signers
pub const SIGNER_TAGS: [char; 6] = ['A', 'B', 'C', 'D', 'E', 'F'];
/// Clique block types
pub enum CliqueBlockType {
/// Epoch transition block must contain list of signers
Checkpoint,
/// Block with no votes
Empty,
/// Vote
Vote(VoteType),
}
/// Clique tester
pub struct CliqueTester {
/// Mocked Clique
pub clique: Clique,
/// Mocked genesis state
pub genesis: Header,
/// StateDB
pub db: StateDB,
/// List of signers
pub signers: HashMap<char, KeyPair>,
}
impl CliqueTester {
/// Create a `Clique` tester with settings
pub fn with(epoch: u64, period: u64, initial_signers: Vec<char>) -> Self {
assert_eq!(initial_signers.iter().all(|s| SIGNER_TAGS.contains(s)), true,
"Not all the initial signers is in SIGNER_TAGS, possible keys are 'A' ..= 'F'");
let clique = Clique::with_test(epoch, period);
let mut genesis = Header::default();
let mut signers = HashMap::new();
let call = |_a, _b| {
unimplemented!("Clique doesn't use Engine::Call");
};
let mut extra_data = vec![0; VANITY_LENGTH];
for &signer in SIGNER_TAGS.iter() {
let secret = Secret::from(H256::from(signer as u64));
let keypair = KeyPair::from_secret(secret).unwrap();
if initial_signers.contains(&signer) {
extra_data.extend(&*keypair.address());
}
signers.insert(signer, keypair);
}
// append dummy signature
extra_data.extend(std::iter::repeat(0).take(SIGNATURE_LENGTH));
genesis.set_extra_data(extra_data);
genesis.set_gas_limit(U256::from(0xa00000));
genesis.set_difficulty(U256::from(1));
genesis.set_seal(util::null_seal());
clique.genesis_epoch_data(&genesis, &call).expect("Create genesis failed");
Self {clique, genesis, db: get_temp_state_db(), signers}
}
/// Get difficulty for a given block
pub fn get_difficulty(&self, block_num: BlockNumber, header: &Header, signer: &Address) -> U256 {
let state = self.clique.state(header).unwrap();
if state.is_inturn(block_num, signer) {
DIFF_INTURN
} else {
DIFF_NOTURN
}
}
/// Get the state of a given block
// Note, this will read the cache and `will` not work with more than 128 blocks
pub fn get_state_at_block(&self, hash: &H256) -> CliqueBlockState {
self.clique.block_state_by_hash.write()
.get_mut(hash)
.expect("CliqueBlockState not found tested failed")
.clone()
}
/// Get signers after a certain state
// This is generally used to fetch the state after a test has been executed and checked against
// the intial list of signers provided in the test
pub fn clique_signers(&self, hash: &H256) -> impl Iterator<Item = Address> {
self.get_state_at_block(hash).signers().clone().into_iter()
}
/// Fetches all addresses at current `block` and converts them back to `tags (char)` and sorts them
/// Addresses are supposed sorted based on address but these tests are using `tags` just for simplicity
/// and the order is not important!
pub fn into_tags<T: Iterator<Item = Address>>(&self, addr: T) -> Vec<char> {
let mut tags: Vec<char> = addr.filter_map(|addr| {
for (t, kp) in self.signers.iter() {
if addr == kp.address() {
return Some(*t)
}
}
None
})
.collect();
tags.sort();
tags
}
/// Create a new `Clique` block and import
pub fn new_block_and_import(
&self,
block_type: CliqueBlockType,
last_header: &Header,
beneficary: Option<Address>,
signer: char,
) -> Result<Header, Error> {
let mut extra_data = vec![0; VANITY_LENGTH];
let mut seal = util::null_seal();
let last_hash = last_header.hash();
match block_type {
CliqueBlockType::Checkpoint => {
let signers = self.clique.state(&last_header).unwrap().signers().clone();
for signer in signers {
extra_data.extend(&*signer);
}
}
CliqueBlockType::Vote(v) => seal = v.as_rlp(),
CliqueBlockType::Empty => (),
};
let db = self.db.boxed_clone();
let mut block = OpenBlock::new(
&self.clique,
Default::default(),
false,
db,
&last_header.clone(),
Arc::new(vec![last_hash]),
beneficary.unwrap_or_default(),
(3141562.into(), 31415620.into()),
extra_data,
false,
None,
).unwrap();
{
let difficulty = self.get_difficulty(block.header.number(), last_header, &self.signers[&signer].address());
let b = block.block_mut();
b.header.set_timestamp(last_header.timestamp() + self.clique.period);
b.header.set_difficulty(difficulty);
b.header.set_seal(seal);
let sign = ethkey::sign(self.signers[&signer].secret(), &b.header.hash()).unwrap();
let mut extra_data = b.header.extra_data().clone();
extra_data.extend_from_slice(&*sign);
b.header.set_extra_data(extra_data);
}
let current_header = &block.header;
self.clique.verify_block_basic(current_header)?;
self.clique.verify_block_family(current_header, &last_header)?;
Ok(current_header.clone())
}
}
#[test]
fn one_signer_with_no_votes() {
let tester = CliqueTester::with(10, 1, vec!['A']);
let empty_block = tester.new_block_and_import(CliqueBlockType::Empty, &tester.genesis, None, 'A').unwrap();
let tags = tester.into_tags(tester.clique_signers(&empty_block.hash()));
assert_eq!(&tags, &['A']);
}
#[test]
fn one_signer_two_votes() {
let tester = CliqueTester::with(10, 1, vec!['A']);
// Add a vote for `B` signed by `A`
let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &tester.genesis,
Some(tester.signers[&'B'].address()), 'A').unwrap();
let tags = tester.into_tags(tester.clique_signers(&vote.hash()));
assert_eq!(&tags, &['A', 'B']);
// Add a empty block signed by `B`
let empty = tester.new_block_and_import(CliqueBlockType::Empty, &vote, None, 'B').unwrap();
// Add vote for `C` signed by A but should not be accepted
let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &empty,
Some(tester.signers[&'C'].address()), 'A').unwrap();
let tags = tester.into_tags(tester.clique_signers(&vote.hash()));
assert_eq!(&tags, &['A', 'B']);
}
#[test]
fn two_signers_six_votes_deny_last() {
let tester = CliqueTester::with(10, 1, vec!['A', 'B']);
let mut prev_header = tester.genesis.clone();
// Add two votes for `C` signed by `A` and `B`
for &signer in SIGNER_TAGS.iter().take(2) {
let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &prev_header,
Some(tester.signers[&'C'].address()), signer).unwrap();
prev_header = vote.clone();
}
// Add two votes for `D` signed by `A` and `B`
for &signer in SIGNER_TAGS.iter().take(2) {
let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &prev_header,
Some(tester.signers[&'D'].address()), signer).unwrap();
prev_header = vote.clone();
}
// Add a empty block signed by `C`
let empty = tester.new_block_and_import(CliqueBlockType::Empty, &prev_header, None, 'C').unwrap();
prev_header = empty.clone();
// Add two votes for `E` signed by `A` and `B`
for &signer in SIGNER_TAGS.iter().take(2) {
let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &prev_header,
Some(tester.signers[&'E'].address()), signer).unwrap();
prev_header = vote.clone();
}
let tags = tester.into_tags(tester.clique_signers(&prev_header.hash()));
assert_eq!(&tags, &['A', 'B', 'C', 'D']);
}
#[test]
fn one_signer_dropping_itself() {
let tester = CliqueTester::with(10, 1, vec!['A']);
let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &tester.genesis,
Some(tester.signers[&'A'].address()), 'A').unwrap();
let signers = tester.clique_signers(&vote.hash());
assert!(signers.count() == 0);
}
#[test]
fn two_signers_one_remove_vote_no_consensus() {
let tester = CliqueTester::with(10, 1, vec!['A', 'B']);
let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &tester.genesis,
Some(tester.signers[&'B'].address()), 'A').unwrap();
let tags = tester.into_tags(tester.clique_signers(&vote.hash()));
assert_eq!(&tags, &['A', 'B']);
}
#[test]
fn two_signers_consensus_remove_b() {
let tester = CliqueTester::with(10, 1, vec!['A', 'B']);
let first_vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &tester.genesis,
Some(tester.signers[&'B'].address()), 'A').unwrap();
let second_vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &first_vote,
Some(tester.signers[&'B'].address()), 'B').unwrap();
let tags = tester.into_tags(tester.clique_signers(&second_vote.hash()));
assert_eq!(&tags, &['A']);
}
#[test]
fn three_signers_consensus_remove_c() {
let tester = CliqueTester::with(10, 1, vec!['A', 'B', 'C']);
let first_vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &tester.genesis,
Some(tester.signers[&'C'].address()), 'A').unwrap();
let second_vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &first_vote,
Some(tester.signers[&'C'].address()), 'B').unwrap();
let tags = tester.into_tags(tester.clique_signers(&second_vote.hash()));
assert_eq!(&tags, &['A', 'B']);
}
#[test]
fn four_signers_half_no_consensus() {
let tester = CliqueTester::with(10, 1, vec!['A', 'B', 'C', 'D']);
let first_vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &tester.genesis,
Some(tester.signers[&'C'].address()), 'A').unwrap();
let second_vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &first_vote,
Some(tester.signers[&'C'].address()), 'B').unwrap();
let tags = tester.into_tags(tester.clique_signers(&second_vote.hash()));
assert_eq!(&tags, &['A', 'B', 'C', 'D']);
}
#[test]
fn four_signers_three_consensus_rm() {
let tester = CliqueTester::with(10, 1, vec!['A', 'B', 'C', 'D']);
let mut prev_header = tester.genesis.clone();
// Three votes to remove `D` signed by ['A', 'B', 'C']
for signer in SIGNER_TAGS.iter().take(3) {
let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &prev_header,
Some(tester.signers[&'D'].address()), *signer).unwrap();
prev_header = vote.clone();
}
let tags = tester.into_tags(tester.clique_signers(&prev_header.hash()));
assert_eq!(&tags, &['A', 'B', 'C']);
}
#[test]
fn vote_add_only_counted_once_per_signer() {
let tester = CliqueTester::with(10, 1, vec!['A', 'B']);
// Add a vote for `C` signed by `A`
let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &tester.genesis,
Some(tester.signers[&'C'].address()), 'A').unwrap();
// Empty block signed by B`
let empty = tester.new_block_and_import(CliqueBlockType::Empty, &vote, None, 'B').unwrap();
// Add a vote for `C` signed by `A`
let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &empty,
Some(tester.signers[&'C'].address()), 'A').unwrap();
// Empty block signed by `B`
let empty = tester.new_block_and_import(CliqueBlockType::Empty, &vote, None, 'B').unwrap();
// Add a vote for `C` signed by `A`
let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &empty,
Some(tester.signers[&'C'].address()), 'A').unwrap();
let tags = tester.into_tags(tester.clique_signers(&vote.hash()));
assert_eq!(&tags, &['A', 'B']);
}
#[test]
fn vote_add_concurrently_is_permitted() {
let tester = CliqueTester::with(10, 1, vec!['A', 'B']);
// Add a vote for `C` signed by `A`
let b = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &tester.genesis,
Some(tester.signers[&'C'].address()), 'A').unwrap();
// Empty block signed by `B`
let b = tester.new_block_and_import(CliqueBlockType::Empty, &b, None, 'B').unwrap();
// Add a vote for `D` signed by `A`
let b = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &b,
Some(tester.signers[&'D'].address()), 'A').unwrap();
// Empty block signed by `B`
let b = tester.new_block_and_import(CliqueBlockType::Empty, &b, None, 'B').unwrap();
// Empty block signed by `A`
let b = tester.new_block_and_import(CliqueBlockType::Empty, &b, None, 'A').unwrap();
// Add a vote for `D` signed by `B`
let b = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &b,
Some(tester.signers[&'D'].address()), 'B').unwrap();
// Empty block signed by `A`
let b = tester.new_block_and_import(CliqueBlockType::Empty, &b, None, 'A').unwrap();
// Add a vote for `C` signed by `B`
let b = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &b,
Some(tester.signers[&'C'].address()), 'B').unwrap();
let tags = tester.into_tags(tester.clique_signers(&b.hash()));
assert_eq!(&tags, &['A', 'B', 'C', 'D']);
}
#[test]
fn vote_rm_only_counted_once_per_signer() {
let tester = CliqueTester::with(10, 1, vec!['A', 'B']);
let mut prev_header = tester.genesis.clone();
for _ in 0..2 {
// Vote to remove `B` signed by `A`
let b = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &prev_header,
Some(tester.signers[&'B'].address()), 'A').unwrap();
// Empty block signed by `B`
let b = tester.new_block_and_import(CliqueBlockType::Empty, &b, None, 'B').unwrap();
prev_header = b.clone();
}
// Add a vote for `B` signed by `A`
let b = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &prev_header,
Some(tester.signers[&'B'].address()), 'A').unwrap();
let tags = tester.into_tags(tester.clique_signers(&b.hash()));
assert_eq!(&tags, &['A', 'B']);
}
#[test]
fn vote_rm_concurrently_is_permitted() {
let tester = CliqueTester::with(100, 1, vec!['A', 'B', 'C', 'D']);
// Add a vote for `C` signed by `A`
let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &tester.genesis,
Some(tester.signers[&'C'].address()), 'A').unwrap();
// Empty block signed by `B`
let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'B').unwrap();
// Empty block signed by `C`
let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'C').unwrap();
// Add a vote for `D` signed by `A`
let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block,
Some(tester.signers[&'D'].address()), 'A').unwrap();
// Empty block signed by `B`
let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'B').unwrap();
// Empty block signed by `C`
let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'C').unwrap();
// Empty block signed by `A`
let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'A').unwrap();
// Add a vote for `D` signed by `B`
let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block,
Some(tester.signers[&'D'].address()), 'B').unwrap();
// Add a vote for `D` signed by `C`
let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block,
Some(tester.signers[&'D'].address()), 'C').unwrap();
// Empty block signed by `A`
let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'A').unwrap();
// Add a vote for `C` signed by `B`
let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block,
Some(tester.signers[&'C'].address()), 'B').unwrap();
let tags = tester.into_tags(tester.clique_signers(&block.hash()));
assert_eq!(&tags, &['A', 'B']);
}
#[test]
fn vote_to_rm_are_immediate_and_ensure_votes_are_rm() {
let tester = CliqueTester::with(100, 1, vec!['A', 'B', 'C']);
// Vote to remove `B` signed by `C`
let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &tester.genesis,
Some(tester.signers[&'B'].address()), 'C').unwrap();
// Vote to remove `C` signed by `A`
let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block,
Some(tester.signers[&'C'].address()), 'A').unwrap();
// Vote to remove `C` signed by `B`
let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block,
Some(tester.signers[&'C'].address()), 'B').unwrap();
// Vote to remove `B` signed by `A`
let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block,
Some(tester.signers[&'B'].address()), 'A').unwrap();
let tags = tester.into_tags(tester.clique_signers(&block.hash()));
assert_eq!(&tags, &['A', 'B']);
}
#[test]
fn vote_to_rm_are_immediate_and_votes_should_be_dropped_from_kicked_signer() {
let tester = CliqueTester::with(100, 1, vec!['A', 'B', 'C']);
// Vote to add `D` signed by `C`
let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &tester.genesis,
Some(tester.signers[&'D'].address()), 'C').unwrap();
// Vote to remove `C` signed by `A`
let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block,
Some(tester.signers[&'C'].address()), 'A').unwrap();
// Vote to remove `C` signed by `B`
let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block,
Some(tester.signers[&'C'].address()), 'B').unwrap();
// Vote to add `D` signed by `A`
let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &block,
Some(tester.signers[&'D'].address()), 'A').unwrap();
let tags = tester.into_tags(tester.clique_signers(&block.hash()));
assert_eq!(&tags, &['A', 'B']);
}
#[test]
fn cascading_not_allowed() {
let tester = CliqueTester::with(100, 1, vec!['A', 'B', 'C', 'D']);
// Vote against `C` signed by `A`
let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &tester.genesis,
Some(tester.signers[&'C'].address()), 'A').unwrap();
// Empty block signed by `B`
let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'B').unwrap();
// Empty block signed by `C`
let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'C').unwrap();
// Vote against `D` signed by `A`
let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block,
Some(tester.signers[&'D'].address()), 'A').unwrap();
// Vote against `C` signed by `B`
let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block,
Some(tester.signers[&'C'].address()), 'B').unwrap();
// Empty block signed by `C`
let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'C').unwrap();
// Empty block signed by `A`
let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'A').unwrap();
// Vote against `D` signed by `B`
let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block,
Some(tester.signers[&'D'].address()), 'B').unwrap();
// Vote against `D` signed by `C`
let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block,
Some(tester.signers[&'D'].address()), 'C').unwrap();
let tags = tester.into_tags(tester.clique_signers(&block.hash()));
assert_eq!(&tags, &['A', 'B', 'C']);
}
#[test]
fn consensus_out_of_bounds_consensus_execute_on_touch() {
let tester = CliqueTester::with(100, 1, vec!['A', 'B', 'C', 'D']);
// Vote against `C` signed by `A`
let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &tester.genesis,
Some(tester.signers[&'C'].address()), 'A').unwrap();
// Empty block signed by `B`
let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'B').unwrap();
// Empty block signed by `C`
let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'C').unwrap();
// Vote against `D` signed by `A`
let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block,
Some(tester.signers[&'D'].address()), 'A').unwrap();
// Vote against `C` signed by `B`
let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block,
Some(tester.signers[&'C'].address()), 'B').unwrap();
// Empty block signed by `C`
let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'C').unwrap();
// Empty block signed by `A`
let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'A').unwrap();
// Vote against `D` signed by `B`
let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block,
Some(tester.signers[&'D'].address()), 'B').unwrap();
// Vote against `D` signed by `C`
let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block,
Some(tester.signers[&'D'].address()), 'C').unwrap();
let tags = tester.into_tags(tester.clique_signers(&block.hash()));
assert_eq!(&tags, &['A', 'B', 'C'], "D should have been removed after 3/4 remove votes");
// Empty block signed by `A`
let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'A').unwrap();
// Vote for `C` signed by `C`
let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &block,
Some(tester.signers[&'C'].address()), 'C').unwrap();
let tags = tester.into_tags(tester.clique_signers(&block.hash()));
assert_eq!(&tags, &['A', 'B']);
}
#[test]
fn consensus_out_of_bounds_first_touch() {
let tester = CliqueTester::with(100, 1, vec!['A', 'B', 'C', 'D']);
// Vote against `C` signed by `A`
let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &tester.genesis,
Some(tester.signers[&'C'].address()), 'A').unwrap();
// Empty block signed by `B`
let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'B').unwrap();
// Empty block signed by `C`
let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'C').unwrap();
// Vote against `D` signed by `A`
let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block,
Some(tester.signers[&'D'].address()), 'A').unwrap();
// Vote against `C` signed by `B`
let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block,
Some(tester.signers[&'C'].address()), 'B').unwrap();
// Empty block signed by `C`
let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'C').unwrap();
// Empty block signed by `A`
let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'A').unwrap();
// Vote against `D` signed by `B`
let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block,
Some(tester.signers[&'D'].address()), 'B').unwrap();
// Vote against `D` signed by `C`
let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block,
Some(tester.signers[&'D'].address()), 'C').unwrap();
let tags = tester.into_tags(tester.clique_signers(&block.hash()));
assert_eq!(&tags, &['A', 'B', 'C']);
// Empty block signed by `A`
let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'A').unwrap();
// Vote for `C` signed by `B`
let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &block,
Some(tester.signers[&'C'].address()), 'B').unwrap();
let tags = tester.into_tags(tester.clique_signers(&block.hash()));
assert_eq!(&tags, &['A', 'B', 'C']);
}
#[test]
fn pending_votes_doesnt_survive_authorization_changes() {
let tester = CliqueTester::with(100, 1, vec!['A', 'B', 'C', 'D', 'E']);
let mut prev_header = tester.genesis.clone();
// Vote for `F` from [`A`, `B`, `C`]
for sign in SIGNER_TAGS.iter().take(3) {
let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &prev_header,
Some(tester.signers[&'F'].address()), *sign).unwrap();
prev_header = block.clone();
}
let tags = tester.into_tags(tester.clique_signers(&prev_header.hash()));
assert_eq!(&tags, &['A', 'B', 'C', 'D', 'E', 'F'], "F should have been added");
// Vote against `F` from [`D`, `E`, `B`, `C`]
for sign in SIGNER_TAGS.iter().skip(3).chain(SIGNER_TAGS.iter().skip(1).take(2)) {
let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &prev_header,
Some(tester.signers[&'F'].address()), *sign).unwrap();
prev_header = block.clone();
}
let tags = tester.into_tags(tester.clique_signers(&prev_header.hash()));
assert_eq!(&tags, &['A', 'B', 'C', 'D', 'E'], "F should have been removed");
// Vote for `F` from [`D`, `E`]
for sign in SIGNER_TAGS.iter().skip(3).take(2) {
let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &prev_header,
Some(tester.signers[&'F'].address()), *sign).unwrap();
prev_header = block.clone();
}
// Vote against `A` from [`B`, `C`, `D`]
for sign in SIGNER_TAGS.iter().skip(1).take(3) {
let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &prev_header,
Some(tester.signers[&'A'].address()), *sign).unwrap();
prev_header = block.clone();
}
let tags = tester.into_tags(tester.clique_signers(&prev_header.hash()));
assert_eq!(&tags, &['B', 'C', 'D', 'E'], "A should have been removed");
let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &prev_header,
Some(tester.signers[&'F'].address()), 'B').unwrap();
let tags = tester.into_tags(tester.clique_signers(&block.hash()));
assert_eq!(&tags, &['B', 'C', 'D', 'E', 'F'], "F should have been added again");
}
#[test]
fn epoch_transition_reset_all_votes() {
let tester = CliqueTester::with(3, 1, vec!['A', 'B']);
let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &tester.genesis,
Some(tester.signers[&'C'].address()), 'A').unwrap();
let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'B').unwrap();
let block = tester.new_block_and_import(CliqueBlockType::Checkpoint, &block, None, 'A').unwrap();
let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &block,
Some(tester.signers[&'C'].address()), 'B').unwrap();
let tags = tester.into_tags(tester.clique_signers(&block.hash()));
assert_eq!(&tags, &['A', 'B'], "Votes should have been reset after checkpoint");
}
#[test]
fn unauthorized_signer_should_not_be_able_to_sign_block() {
let tester = CliqueTester::with(3, 1, vec!['A']);
let err = tester.new_block_and_import(CliqueBlockType::Empty, &tester.genesis, None, 'B').unwrap_err();
match err.kind() {
ErrorKind::Engine(EngineError::NotAuthorized(_)) => (),
_ => assert!(true == false, "Wrong error kind"),
}
}
#[test]
fn signer_should_not_be_able_to_sign_two_consequtive_blocks() {
let tester = CliqueTester::with(3, 1, vec!['A', 'B']);
let b = tester.new_block_and_import(CliqueBlockType::Empty, &tester.genesis, None, 'A').unwrap();
let err = tester.new_block_and_import(CliqueBlockType::Empty, &b, None, 'A').unwrap_err();
match err.kind() {
ErrorKind::Engine(EngineError::CliqueTooRecentlySigned(_)) => (),
_ => assert!(true == false, "Wrong error kind"),
}
}
#[test]
fn recent_signers_should_not_reset_on_checkpoint() {
let tester = CliqueTester::with(3, 1, vec!['A', 'B', 'C']);
let block = tester.new_block_and_import(CliqueBlockType::Empty, &tester.genesis, None, 'A').unwrap();
let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'B').unwrap();
let block = tester.new_block_and_import(CliqueBlockType::Checkpoint, &block, None, 'A').unwrap();
let err = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'A').unwrap_err();
match err.kind() {
ErrorKind::Engine(EngineError::CliqueTooRecentlySigned(_)) => (),
_ => assert!(true == false, "Wrong error kind"),
}
}
// Not part of http://eips.ethereum.org/EIPS/eip-225
#[test]
fn bonus_consensus_should_keep_track_of_votes_before_latest_per_signer() {
let tester = CliqueTester::with(100, 1, vec!['A', 'B', 'C', 'D']);
// Add a vote for `E` signed by `A`
let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &tester.genesis,
Some(tester.signers[&'E'].address()), 'A').unwrap();
// Empty block signed by `B`
let vote = tester.new_block_and_import(CliqueBlockType::Empty, &vote, None, 'B').unwrap();
// Empty block signed by `C`
let vote = tester.new_block_and_import(CliqueBlockType::Empty, &vote, None, 'C').unwrap();
// Empty block signed by `D`
let vote = tester.new_block_and_import(CliqueBlockType::Empty, &vote, None, 'D').unwrap();
// Add a vote for `F` signed by `A`
let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &vote,
Some(tester.signers[&'F'].address()), 'A').unwrap();
// Empty block signed by `C`
let vote = tester.new_block_and_import(CliqueBlockType::Empty, &vote, None, 'C').unwrap();
// Empty block signed by `D`
let vote = tester.new_block_and_import(CliqueBlockType::Empty, &vote, None, 'D').unwrap();
// Add a vote for `E` signed by `B`
let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &vote,
Some(tester.signers[&'E'].address()), 'B').unwrap();
// Empty block signed by `A`
let vote = tester.new_block_and_import(CliqueBlockType::Empty, &vote, None, 'A').unwrap();
// Empty block signed by `C`
let vote = tester.new_block_and_import(CliqueBlockType::Empty, &vote, None, 'C').unwrap();
// Empty block signed by `D`
let vote = tester.new_block_and_import(CliqueBlockType::Empty, &vote, None, 'D').unwrap();
// Add a vote for `F` signed by `B`
let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &vote,
Some(tester.signers[&'F'].address()), 'B').unwrap();
// Empty block signed by A`
let vote = tester.new_block_and_import(CliqueBlockType::Empty, &vote, None, 'A').unwrap();
// Add a vote for `E` signed by `C`
let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &vote,
Some(tester.signers[&'E'].address()), 'C').unwrap();
let tags = tester.into_tags(tester.clique_signers(&vote.hash()));
assert_eq!(&tags, &['A', 'B', 'C', 'D', 'E']);
}

View File

@ -0,0 +1,115 @@
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
// This file is part of Parity Ethereum.
// Parity Ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity Ethereum 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 General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::collections::BTreeSet;
use engines::EngineError;
use engines::clique::{ADDRESS_LENGTH, SIGNATURE_LENGTH, VANITY_LENGTH, NULL_NONCE, NULL_MIXHASH};
use error::Error;
use ethereum_types::{Address, H256};
use ethkey::{public_to_address, recover as ec_recover, Signature};
use lru_cache::LruCache;
use parking_lot::RwLock;
use rlp::encode;
use types::header::Header;
/// How many recovered signature to cache in the memory.
pub const CREATOR_CACHE_NUM: usize = 4096;
lazy_static! {
/// key: header hash
/// value: creator address
static ref CREATOR_BY_HASH: RwLock<LruCache<H256, Address>> = RwLock::new(LruCache::new(CREATOR_CACHE_NUM));
}
/// Recover block creator from signature
pub fn recover_creator(header: &Header) -> Result<Address, Error> {
// Initialization
let mut cache = CREATOR_BY_HASH.write();
if let Some(creator) = cache.get_mut(&header.hash()) {
return Ok(*creator);
}
let data = header.extra_data();
if data.len() < VANITY_LENGTH {
Err(EngineError::CliqueMissingVanity)?
}
if data.len() < VANITY_LENGTH + SIGNATURE_LENGTH {
Err(EngineError::CliqueMissingSignature)?
}
// Split `signed_extra data` and `signature`
let (signed_data_slice, signature_slice) = data.split_at(data.len() - SIGNATURE_LENGTH);
// convert `&[u8]` to `[u8; 65]`
let signature = {
let mut s = [0; SIGNATURE_LENGTH];
s.copy_from_slice(signature_slice);
s
};
// modify header and hash it
let unsigned_header = &mut header.clone();
unsigned_header.set_extra_data(signed_data_slice.to_vec());
let msg = unsigned_header.hash();
let pubkey = ec_recover(&Signature::from(signature), &msg)?;
let creator = public_to_address(&pubkey);
cache.insert(header.hash(), creator.clone());
Ok(creator)
}
/// Extract signer list from extra_data.
///
/// Layout of extra_data:
/// ----
/// VANITY: 32 bytes
/// Signers: N * 32 bytes as hex encoded (20 characters)
/// Signature: 65 bytes
/// --
pub fn extract_signers(header: &Header) -> Result<BTreeSet<Address>, Error> {
let data = header.extra_data();
if data.len() <= VANITY_LENGTH + SIGNATURE_LENGTH {
Err(EngineError::CliqueCheckpointNoSigner)?
}
// extract only the portion of extra_data which includes the signer list
let signers_raw = &data[(VANITY_LENGTH)..data.len() - (SIGNATURE_LENGTH)];
if signers_raw.len() % ADDRESS_LENGTH != 0 {
Err(EngineError::CliqueCheckpointInvalidSigners(signers_raw.len()))?
}
let num_signers = signers_raw.len() / 20;
let signers: BTreeSet<Address> = (0..num_signers)
.map(|i| {
let start = i * ADDRESS_LENGTH;
let end = start + ADDRESS_LENGTH;
signers_raw[start..end].into()
})
.collect();
Ok(signers)
}
/// Retrieve `null_seal`
pub fn null_seal() -> Vec<Vec<u8>> {
vec![encode(&NULL_MIXHASH.to_vec()), encode(&NULL_NONCE.to_vec())]
}

View File

@ -18,6 +18,7 @@
mod authority_round; mod authority_round;
mod basic_authority; mod basic_authority;
mod clique;
mod instant_seal; mod instant_seal;
mod null_engine; mod null_engine;
mod validator_set; mod validator_set;
@ -30,6 +31,7 @@ pub use self::basic_authority::BasicAuthority;
pub use self::instant_seal::{InstantSeal, InstantSealParams}; pub use self::instant_seal::{InstantSeal, InstantSealParams};
pub use self::null_engine::NullEngine; pub use self::null_engine::NullEngine;
pub use self::signer::EngineSigner; pub use self::signer::EngineSigner;
pub use self::clique::Clique;
// TODO [ToDr] Remove re-export (#10130) // TODO [ToDr] Remove re-export (#10130)
pub use types::engines::ForkChoice; pub use types::engines::ForkChoice;
@ -50,7 +52,7 @@ use types::transaction::{self, UnverifiedTransaction, SignedTransaction};
use ethkey::{Signature}; use ethkey::{Signature};
use machine::{self, Machine, AuxiliaryRequest, AuxiliaryData}; use machine::{self, Machine, AuxiliaryRequest, AuxiliaryData};
use ethereum_types::{H256, U256, Address}; use ethereum_types::{H64, H256, U256, Address};
use unexpected::{Mismatch, OutOfBounds}; use unexpected::{Mismatch, OutOfBounds};
use bytes::Bytes; use bytes::Bytes;
use types::ancestry_action::AncestryAction; use types::ancestry_action::AncestryAction;
@ -85,12 +87,45 @@ pub enum EngineError {
RequiresClient, RequiresClient,
/// Invalid engine specification or implementation. /// Invalid engine specification or implementation.
InvalidEngine, InvalidEngine,
/// Requires signer ref, but none registered.
RequiresSigner,
/// Checkpoint is missing
CliqueMissingCheckpoint(H256),
/// Missing vanity data
CliqueMissingVanity,
/// Missing signature
CliqueMissingSignature,
/// Missing signers
CliqueCheckpointNoSigner,
/// List of signers is invalid
CliqueCheckpointInvalidSigners(usize),
/// Wrong author on a checkpoint
CliqueWrongAuthorCheckpoint(Mismatch<Address>),
/// Wrong checkpoint authors recovered
CliqueFaultyRecoveredSigners(Vec<String>),
/// Invalid nonce (should contain vote)
CliqueInvalidNonce(H64),
/// The signer signed a block to recently
CliqueTooRecentlySigned(Address),
/// Custom
Custom(String),
} }
impl fmt::Display for EngineError { impl fmt::Display for EngineError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::EngineError::*; use self::EngineError::*;
let msg = match *self { let msg = match *self {
CliqueMissingCheckpoint(ref hash) => format!("Missing checkpoint block: {}", hash),
CliqueMissingVanity => format!("Extra data is missing vanity data"),
CliqueMissingSignature => format!("Extra data is missing signature"),
CliqueCheckpointInvalidSigners(len) => format!("Checkpoint block list was of length: {} of checkpoint but
it needs to be bigger than zero and a divisible by 20", len),
CliqueCheckpointNoSigner => format!("Checkpoint block list of signers was empty"),
CliqueInvalidNonce(ref mis) => format!("Unexpected nonce {} expected {} or {}", mis, 0_u64, u64::max_value()),
CliqueWrongAuthorCheckpoint(ref oob) => format!("Unexpected checkpoint author: {}", oob),
CliqueFaultyRecoveredSigners(ref mis) => format!("Faulty recovered signers {:?}", mis),
CliqueTooRecentlySigned(ref address) => format!("The signer: {} has signed a block too recently", address),
Custom(ref s) => s.clone(),
DoubleVote(ref address) => format!("Author {} issued too many blocks.", address), DoubleVote(ref address) => format!("Author {} issued too many blocks.", address),
NotProposer(ref mis) => format!("Author is not a current proposer: {}", mis), NotProposer(ref mis) => format!("Author is not a current proposer: {}", mis),
NotAuthorized(ref address) => format!("Signer {} is not authorized.", address), NotAuthorized(ref address) => format!("Signer {} is not authorized.", address),
@ -100,6 +135,7 @@ impl fmt::Display for EngineError {
FailedSystemCall(ref msg) => format!("Failed to make system call: {}", msg), FailedSystemCall(ref msg) => format!("Failed to make system call: {}", msg),
MalformedMessage(ref msg) => format!("Received malformed consensus message: {}", msg), MalformedMessage(ref msg) => format!("Received malformed consensus message: {}", msg),
RequiresClient => format!("Call requires client but none registered"), RequiresClient => format!("Call requires client but none registered"),
RequiresSigner => format!("Call requires signer but none registered"),
InvalidEngine => format!("Invalid engine specification or implementation"), InvalidEngine => format!("Invalid engine specification or implementation"),
}; };
@ -120,7 +156,7 @@ pub enum Seal {
Proposal(Vec<Bytes>), Proposal(Vec<Bytes>),
/// Regular block seal; should be part of the blockchain. /// Regular block seal; should be part of the blockchain.
Regular(Vec<Bytes>), Regular(Vec<Bytes>),
/// Engine does generate seal for this block right now. /// Engine does not generate seal for this block right now.
None, None,
} }
@ -263,6 +299,9 @@ pub trait Engine<M: Machine>: Sync + Send {
Ok(()) Ok(())
} }
/// Allow mutating the header during seal generation. Currently only used by Clique.
fn on_seal_block(&self, _block: &mut ExecutedBlock) -> Result<(), Error> { Ok(()) }
/// None means that it requires external input (e.g. PoW) to seal a block. /// None means that it requires external input (e.g. PoW) to seal a block.
/// Some(true) means the engine is currently prime for seal generation (i.e. node is the current validator). /// Some(true) means the engine is currently prime for seal generation (i.e. node is the current validator).
/// Some(false) means that the node might seal internally but is not qualified now. /// Some(false) means that the node might seal internally but is not qualified now.
@ -387,7 +426,7 @@ pub trait Engine<M: Machine>: Sync + Send {
fn step(&self) {} fn step(&self) {}
/// Stops any services that the may hold the Engine and makes it safe to drop. /// Stops any services that the may hold the Engine and makes it safe to drop.
fn stop(&self) {} fn stop(&mut self) {}
/// Create a factory for building snapshot chunks and restoring from them. /// Create a factory for building snapshot chunks and restoring from them.
/// Returning `None` indicates that this engine doesn't support snapshot creation. /// Returning `None` indicates that this engine doesn't support snapshot creation.
@ -421,6 +460,11 @@ pub trait Engine<M: Machine>: Sync + Send {
/// Check whether the given new block is the best block, after finalization check. /// Check whether the given new block is the best block, after finalization check.
fn fork_choice(&self, new: &ExtendedHeader, best: &ExtendedHeader) -> ForkChoice; fn fork_choice(&self, new: &ExtendedHeader, best: &ExtendedHeader) -> ForkChoice;
/// Returns author should used when executing tx's for this block.
fn executive_author(&self, header: &Header) -> Result<Address, Error> {
Ok(*header.author())
}
} }
/// Check whether a given block is the best block based on the default total difficulty rule. /// Check whether a given block is the best block based on the default total difficulty rule.

View File

@ -94,6 +94,11 @@ pub fn new_mix<'a, T: Into<SpecParams<'a>>>(params: T) -> Spec {
load(params.into(), include_bytes!("../../res/ethereum/mix.json")) load(params.into(), include_bytes!("../../res/ethereum/mix.json"))
} }
/// Create a new Callisto chain spec
pub fn new_callisto<'a, T: Into<SpecParams<'a>>>(params: T) -> Spec {
load(params.into(), include_bytes!("../../res/ethereum/callisto.json"))
}
/// Create a new Morden testnet chain spec. /// Create a new Morden testnet chain spec.
pub fn new_morden<'a, T: Into<SpecParams<'a>>>(params: T) -> Spec { pub fn new_morden<'a, T: Into<SpecParams<'a>>>(params: T) -> Spec {
load(params.into(), include_bytes!("../../res/ethereum/morden.json")) load(params.into(), include_bytes!("../../res/ethereum/morden.json"))
@ -109,16 +114,26 @@ pub fn new_kovan<'a, T: Into<SpecParams<'a>>>(params: T) -> Spec {
load(params.into(), include_bytes!("../../res/ethereum/kovan.json")) load(params.into(), include_bytes!("../../res/ethereum/kovan.json"))
} }
/// Create a new Rinkeby testnet chain spec.
pub fn new_rinkeby<'a, T: Into<SpecParams<'a>>>(params: T) -> Spec {
load(params.into(), include_bytes!("../../res/ethereum/rinkeby.json"))
}
/// Create a new Görli testnet chain spec.
pub fn new_goerli<'a, T: Into<SpecParams<'a>>>(params: T) -> Spec {
load(params.into(), include_bytes!("../../res/ethereum/goerli.json"))
}
/// Create a new Kotti testnet chain spec.
pub fn new_kotti<'a, T: Into<SpecParams<'a>>>(params: T) -> Spec {
load(params.into(), include_bytes!("../../res/ethereum/kotti.json"))
}
/// Create a new POA Sokol testnet chain spec. /// Create a new POA Sokol testnet chain spec.
pub fn new_sokol<'a, T: Into<SpecParams<'a>>>(params: T) -> Spec { pub fn new_sokol<'a, T: Into<SpecParams<'a>>>(params: T) -> Spec {
load(params.into(), include_bytes!("../../res/ethereum/poasokol.json")) load(params.into(), include_bytes!("../../res/ethereum/poasokol.json"))
} }
/// Create a new Callisto chaun spec
pub fn new_callisto<'a, T: Into<SpecParams<'a>>>(params: T) -> Spec {
load(params.into(), include_bytes!("../../res/ethereum/callisto.json"))
}
// For tests // For tests
/// Create a new Foundation Frontier-era chain spec as though it never changes to Homestead. /// Create a new Foundation Frontier-era chain spec as though it never changes to Homestead.

View File

@ -214,7 +214,6 @@ impl Author {
} }
} }
struct SealingWork { struct SealingWork {
queue: UsingQueue<ClosedBlock>, queue: UsingQueue<ClosedBlock>,
enabled: bool, enabled: bool,
@ -630,7 +629,10 @@ impl Miner {
} }
} }
/// Attempts to perform internal sealing (one that does not require work) and handles the result depending on the type of Seal. // TODO: (https://github.com/paritytech/parity-ethereum/issues/10407)
// This is only used in authority_round path, and should be refactored to merge with the other seal() path.
// Attempts to perform internal sealing (one that does not require work) and handles the result depending on the
// type of Seal.
fn seal_and_import_block_internally<C>(&self, chain: &C, block: ClosedBlock) -> bool fn seal_and_import_block_internally<C>(&self, chain: &C, block: ClosedBlock) -> bool
where C: BlockChain + SealedBlockImporter, where C: BlockChain + SealedBlockImporter,
{ {

View File

@ -35,7 +35,7 @@ use vm::{EnvInfo, CallType, ActionValue, ActionParams, ParamsType};
use builtin::Builtin; use builtin::Builtin;
use engines::{ use engines::{
EthEngine, NullEngine, InstantSeal, InstantSealParams, BasicAuthority, EthEngine, NullEngine, InstantSeal, InstantSealParams, BasicAuthority, Clique,
AuthorityRound, DEFAULT_BLOCKHASH_CONTRACT AuthorityRound, DEFAULT_BLOCKHASH_CONTRACT
}; };
use error::Error; use error::Error;
@ -99,9 +99,9 @@ pub struct CommonParams {
pub validate_receipts_transition: BlockNumber, pub validate_receipts_transition: BlockNumber,
/// Validate transaction chain id. /// Validate transaction chain id.
pub validate_chain_id_transition: BlockNumber, pub validate_chain_id_transition: BlockNumber,
/// Number of first block where EIP-140 (Metropolis: REVERT opcode) rules begin. /// Number of first block where EIP-140 rules begin.
pub eip140_transition: BlockNumber, pub eip140_transition: BlockNumber,
/// Number of first block where EIP-210 (Metropolis: BLOCKHASH changes) rules begin. /// Number of first block where EIP-210 rules begin.
pub eip210_transition: BlockNumber, pub eip210_transition: BlockNumber,
/// EIP-210 Blockhash contract address. /// EIP-210 Blockhash contract address.
pub eip210_contract_address: Address, pub eip210_contract_address: Address,
@ -109,8 +109,7 @@ pub struct CommonParams {
pub eip210_contract_code: Bytes, pub eip210_contract_code: Bytes,
/// Gas allocated for EIP-210 blockhash update. /// Gas allocated for EIP-210 blockhash update.
pub eip210_contract_gas: U256, pub eip210_contract_gas: U256,
/// Number of first block where EIP-211 (Metropolis: RETURNDATASIZE/RETURNDATACOPY) rules /// Number of first block where EIP-211 rules begin.
/// begin.
pub eip211_transition: BlockNumber, pub eip211_transition: BlockNumber,
/// Number of first block where EIP-214 rules begin. /// Number of first block where EIP-214 rules begin.
pub eip214_transition: BlockNumber, pub eip214_transition: BlockNumber,
@ -611,6 +610,8 @@ impl Spec {
ethjson::spec::Engine::InstantSeal(Some(instant_seal)) => Arc::new(InstantSeal::new(instant_seal.params.into(), machine)), ethjson::spec::Engine::InstantSeal(Some(instant_seal)) => Arc::new(InstantSeal::new(instant_seal.params.into(), machine)),
ethjson::spec::Engine::InstantSeal(None) => Arc::new(InstantSeal::new(InstantSealParams::default(), machine)), ethjson::spec::Engine::InstantSeal(None) => Arc::new(InstantSeal::new(InstantSealParams::default(), machine)),
ethjson::spec::Engine::BasicAuthority(basic_authority) => Arc::new(BasicAuthority::new(basic_authority.params.into(), machine)), ethjson::spec::Engine::BasicAuthority(basic_authority) => Arc::new(BasicAuthority::new(basic_authority.params.into(), machine)),
ethjson::spec::Engine::Clique(clique) => Clique::new(clique.params.into(), machine)
.expect("Failed to start Clique consensus engine."),
ethjson::spec::Engine::AuthorityRound(authority_round) => AuthorityRound::new(authority_round.params.into(), machine) ethjson::spec::Engine::AuthorityRound(authority_round) => AuthorityRound::new(authority_round.params.into(), machine)
.expect("Failed to start AuthorityRound consensus engine."), .expect("Failed to start AuthorityRound consensus engine."),
} }
@ -827,7 +828,6 @@ impl Spec {
ethjson::spec::Spec::load(reader) ethjson::spec::Spec::load(reader)
.map_err(fmt_err) .map_err(fmt_err)
.map(load_machine_from) .map(load_machine_from)
} }
/// Loads spec from json file. Provide factories for executing contracts and ensuring /// Loads spec from json file. Provide factories for executing contracts and ensuring

57
json/src/spec/clique.rs Normal file
View File

@ -0,0 +1,57 @@
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
// This file is part of Parity Ethereum.
// Parity Ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity Ethereum 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 General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
//! Clique params deserialization.
use std::num::NonZeroU64;
/// Clique params deserialization.
#[derive(Debug, PartialEq, Deserialize)]
pub struct CliqueParams {
/// period as defined in EIP
pub period: Option<u64>,
/// epoch length as defined in EIP
pub epoch: Option<NonZeroU64>
}
/// Clique engine deserialization.
#[derive(Debug, PartialEq, Deserialize)]
pub struct Clique {
/// CliqueEngine params
pub params: CliqueParams,
}
#[cfg(test)]
mod tests {
use serde_json;
use uint::Uint;
use ethereum_types::U256;
use super::*;
#[test]
fn clique_deserialization() {
let s = r#"{
"params": {
"period": 5,
"epoch": 30000
}
}"#;
let deserialized: Clique = serde_json::from_str(s).unwrap();
assert_eq!(deserialized.params.period, Some(5u64));
assert_eq!(deserialized.params.epoch, NonZeroU64::new(30000));
}
}

View File

@ -16,7 +16,7 @@
//! Engine deserialization. //! Engine deserialization.
use super::{Ethash, BasicAuthority, AuthorityRound, NullEngine, InstantSeal}; use super::{Ethash, BasicAuthority, AuthorityRound, NullEngine, InstantSeal, Clique};
/// Engine deserialization. /// Engine deserialization.
#[derive(Debug, PartialEq, Deserialize)] #[derive(Debug, PartialEq, Deserialize)]
@ -34,6 +34,8 @@ pub enum Engine {
BasicAuthority(BasicAuthority), BasicAuthority(BasicAuthority),
/// AuthorityRound engine. /// AuthorityRound engine.
AuthorityRound(AuthorityRound), AuthorityRound(AuthorityRound),
/// Clique engine.
Clique(Clique)
} }
#[cfg(test)] #[cfg(test)]
@ -130,5 +132,19 @@ mod tests {
Engine::AuthorityRound(_) => {}, // AuthorityRound is unit tested in its own file. Engine::AuthorityRound(_) => {}, // AuthorityRound is unit tested in its own file.
_ => panic!(), _ => panic!(),
}; };
let s = r#"{
"clique": {
"params": {
"period": 15,
"epoch": 30000
}
}
}"#;
let deserialized: Engine = serde_json::from_str(s).unwrap();
match deserialized {
Engine::Clique(_) => {}, // Clique is unit tested in its own file.
_ => panic!(),
};
} }
} }

View File

@ -31,6 +31,7 @@ pub mod authority_round;
pub mod null_engine; pub mod null_engine;
pub mod instant_seal; pub mod instant_seal;
pub mod hardcoded_sync; pub mod hardcoded_sync;
pub mod clique;
pub use self::account::Account; pub use self::account::Account;
pub use self::builtin::{Builtin, Pricing, Linear}; pub use self::builtin::{Builtin, Pricing, Linear};
@ -44,6 +45,7 @@ pub use self::ethash::{Ethash, EthashParams, BlockReward};
pub use self::validator_set::ValidatorSet; pub use self::validator_set::ValidatorSet;
pub use self::basic_authority::{BasicAuthority, BasicAuthorityParams}; pub use self::basic_authority::{BasicAuthority, BasicAuthorityParams};
pub use self::authority_round::{AuthorityRound, AuthorityRoundParams}; pub use self::authority_round::{AuthorityRound, AuthorityRoundParams};
pub use self::clique::{Clique, CliqueParams};
pub use self::null_engine::{NullEngine, NullEngineParams}; pub use self::null_engine::{NullEngine, NullEngineParams};
pub use self::instant_seal::{InstantSeal, InstantSealParams}; pub use self::instant_seal::{InstantSeal, InstantSealParams};
pub use self::hardcoded_sync::HardcodedSync; pub use self::hardcoded_sync::HardcodedSync;

View File

@ -300,7 +300,7 @@ usage! {
ARG arg_chain: (String) = "foundation", or |c: &Config| c.parity.as_ref()?.chain.clone(), ARG arg_chain: (String) = "foundation", or |c: &Config| c.parity.as_ref()?.chain.clone(),
"--chain=[CHAIN]", "--chain=[CHAIN]",
"Specify the blockchain type. CHAIN may be either a JSON chain specification file or ethereum, classic, poacore, tobalaba, expanse, musicoin, ellaism, easthub, social, mix, callisto, morden, ropsten, kovan, poasokol, testnet, or dev.", "Specify the blockchain type. CHAIN may be either a JSON chain specification file or ethereum, classic, poacore, tobalaba, expanse, musicoin, ellaism, easthub, social, mix, callisto, morden, ropsten, kovan, rinkeby, goerli, kotti, poasokol, testnet, or dev.",
ARG arg_keys_path: (String) = "$BASE/keys", or |c: &Config| c.parity.as_ref()?.keys_path.clone(), ARG arg_keys_path: (String) = "$BASE/keys", or |c: &Config| c.parity.as_ref()?.keys_path.clone(),
"--keys-path=[PATH]", "--keys-path=[PATH]",

View File

@ -47,6 +47,9 @@ pub enum SpecType {
Morden, Morden,
Ropsten, Ropsten,
Kovan, Kovan,
Rinkeby,
Goerli,
Kotti,
Sokol, Sokol,
Dev, Dev,
Custom(String), Custom(String),
@ -77,6 +80,9 @@ impl str::FromStr for SpecType {
"morden" | "classic-testnet" => SpecType::Morden, "morden" | "classic-testnet" => SpecType::Morden,
"ropsten" => SpecType::Ropsten, "ropsten" => SpecType::Ropsten,
"kovan" | "testnet" => SpecType::Kovan, "kovan" | "testnet" => SpecType::Kovan,
"rinkeby" => SpecType::Rinkeby,
"goerli" | "görli" => SpecType::Goerli,
"kotti" => SpecType::Kotti,
"sokol" | "poasokol" => SpecType::Sokol, "sokol" | "poasokol" => SpecType::Sokol,
"dev" => SpecType::Dev, "dev" => SpecType::Dev,
other => SpecType::Custom(other.into()), other => SpecType::Custom(other.into()),
@ -102,6 +108,9 @@ impl fmt::Display for SpecType {
SpecType::Morden => "morden", SpecType::Morden => "morden",
SpecType::Ropsten => "ropsten", SpecType::Ropsten => "ropsten",
SpecType::Kovan => "kovan", SpecType::Kovan => "kovan",
SpecType::Rinkeby => "rinkeby",
SpecType::Goerli => "goerli",
SpecType::Kotti => "kotti",
SpecType::Sokol => "sokol", SpecType::Sokol => "sokol",
SpecType::Dev => "dev", SpecType::Dev => "dev",
SpecType::Custom(ref custom) => custom, SpecType::Custom(ref custom) => custom,
@ -127,6 +136,9 @@ impl SpecType {
SpecType::Morden => Ok(ethereum::new_morden(params)), SpecType::Morden => Ok(ethereum::new_morden(params)),
SpecType::Ropsten => Ok(ethereum::new_ropsten(params)), SpecType::Ropsten => Ok(ethereum::new_ropsten(params)),
SpecType::Kovan => Ok(ethereum::new_kovan(params)), SpecType::Kovan => Ok(ethereum::new_kovan(params)),
SpecType::Rinkeby => Ok(ethereum::new_rinkeby(params)),
SpecType::Goerli => Ok(ethereum::new_goerli(params)),
SpecType::Kotti => Ok(ethereum::new_kotti(params)),
SpecType::Sokol => Ok(ethereum::new_sokol(params)), SpecType::Sokol => Ok(ethereum::new_sokol(params)),
SpecType::Dev => Ok(Spec::new_instant()), SpecType::Dev => Ok(Spec::new_instant()),
SpecType::Custom(ref filename) => { SpecType::Custom(ref filename) => {
@ -385,6 +397,10 @@ mod tests {
assert_eq!(SpecType::Ropsten, "ropsten".parse().unwrap()); assert_eq!(SpecType::Ropsten, "ropsten".parse().unwrap());
assert_eq!(SpecType::Kovan, "kovan".parse().unwrap()); assert_eq!(SpecType::Kovan, "kovan".parse().unwrap());
assert_eq!(SpecType::Kovan, "testnet".parse().unwrap()); assert_eq!(SpecType::Kovan, "testnet".parse().unwrap());
assert_eq!(SpecType::Rinkeby, "rinkeby".parse().unwrap());
assert_eq!(SpecType::Goerli, "goerli".parse().unwrap());
assert_eq!(SpecType::Goerli, "görli".parse().unwrap());
assert_eq!(SpecType::Kotti, "kotti".parse().unwrap());
assert_eq!(SpecType::Sokol, "sokol".parse().unwrap()); assert_eq!(SpecType::Sokol, "sokol".parse().unwrap());
assert_eq!(SpecType::Sokol, "poasokol".parse().unwrap()); assert_eq!(SpecType::Sokol, "poasokol".parse().unwrap());
} }
@ -410,6 +426,9 @@ mod tests {
assert_eq!(format!("{}", SpecType::Morden), "morden"); assert_eq!(format!("{}", SpecType::Morden), "morden");
assert_eq!(format!("{}", SpecType::Ropsten), "ropsten"); assert_eq!(format!("{}", SpecType::Ropsten), "ropsten");
assert_eq!(format!("{}", SpecType::Kovan), "kovan"); assert_eq!(format!("{}", SpecType::Kovan), "kovan");
assert_eq!(format!("{}", SpecType::Rinkeby), "rinkeby");
assert_eq!(format!("{}", SpecType::Goerli), "goerli");
assert_eq!(format!("{}", SpecType::Kotti), "kotti");
assert_eq!(format!("{}", SpecType::Sokol), "sokol"); assert_eq!(format!("{}", SpecType::Sokol), "sokol");
assert_eq!(format!("{}", SpecType::Dev), "dev"); assert_eq!(format!("{}", SpecType::Dev), "dev");
assert_eq!(format!("{}", SpecType::Custom("foo/bar".into())), "foo/bar"); assert_eq!(format!("{}", SpecType::Custom("foo/bar".into())), "foo/bar");

View File

@ -19,6 +19,7 @@ track = "nightly"
foundation = { forkBlock = 7280000, critical = false } foundation = { forkBlock = 7280000, critical = false }
ropsten = { forkBlock = 4939394, critical = false } ropsten = { forkBlock = 4939394, critical = false }
kovan = { forkBlock = 10255201, critical = false } kovan = { forkBlock = 10255201, critical = false }
goerli = { forkBlock = 0, critical = false }
[dependencies] [dependencies]
parity-bytes = "0.1" parity-bytes = "0.1"