Merge branch 'lightcli' into light-filters
This commit is contained in:
commit
8c715e025a
@ -448,8 +448,8 @@ windows:
|
||||
- signtool sign /f %keyfile% /p %certpass% target\release\parity.exe
|
||||
- target\release\parity.exe tools hash target\release\parity.exe > parity.sha3
|
||||
- set /P SHA3=<parity.sha3
|
||||
- curl -sL --url "https://github.com/ethcore/win-build/raw/master/SimpleFC.dll" -o nsis\SimpleFC.dll
|
||||
- curl -sL --url "https://github.com/ethcore/win-build/raw/master/vc_redist.x64.exe" -o nsis\vc_redist.x64.exe
|
||||
- curl -sL --url "https://github.com/paritytech/win-build/raw/master/SimpleFC.dll" -o nsis\SimpleFC.dll
|
||||
- curl -sL --url "https://github.com/paritytech/win-build/raw/master/vc_redist.x64.exe" -o nsis\vc_redist.x64.exe
|
||||
- msbuild windows\ptray\ptray.vcxproj /p:Platform=x64 /p:Configuration=Release
|
||||
- signtool sign /f %keyfile% /p %certpass% windows\ptray\x64\release\ptray.exe
|
||||
- cd nsis
|
||||
|
445
Cargo.lock
generated
445
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -8,16 +8,16 @@ build = "build.rs"
|
||||
|
||||
[dependencies]
|
||||
log = "0.3"
|
||||
env_logger = "0.3"
|
||||
env_logger = "0.4"
|
||||
rustc-serialize = "0.3"
|
||||
docopt = "0.6"
|
||||
docopt = "0.7"
|
||||
time = "0.1"
|
||||
num_cpus = "1.2"
|
||||
number_prefix = "0.2"
|
||||
rpassword = "0.2.1"
|
||||
semver = "0.5"
|
||||
semver = "0.6"
|
||||
ansi_term = "0.9"
|
||||
regex = "0.1"
|
||||
regex = "0.2"
|
||||
isatty = "0.1"
|
||||
toml = "0.2"
|
||||
serde = "0.9"
|
||||
|
12
README.md
12
README.md
@ -13,14 +13,14 @@ parity.js [![Join the chat at https://gitter.im/ethcore/parity.js](https://badge
|
||||
|
||||
Be sure to check out [our wiki][wiki-url] for more information.
|
||||
|
||||
[coveralls-image]: https://coveralls.io/repos/github/ethcore/parity/badge.svg?branch=master
|
||||
[coveralls-url]: https://coveralls.io/github/ethcore/parity?branch=master
|
||||
[coveralls-image]: https://coveralls.io/repos/github/paritytech/parity/badge.svg?branch=master
|
||||
[coveralls-url]: https://coveralls.io/github/paritytech/parity?branch=master
|
||||
[gitter-image]: https://badges.gitter.im/Join%20Chat.svg
|
||||
[gitter-url]: https://gitter.im/ethcore/parity?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
|
||||
[license-image]: https://img.shields.io/badge/license-GPL%20v3-green.svg
|
||||
[license-url]: https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
[doc-url]: https://ethcore.github.io/parity/ethcore/index.html
|
||||
[wiki-url]: https://github.com/ethcore/parity/wiki
|
||||
[doc-url]: https://paritytech.github.io/parity/ethcore/index.html
|
||||
[wiki-url]: https://github.com/paritytech/parity/wiki
|
||||
|
||||
**Parity requires Rust version 1.15.0 to build**
|
||||
|
||||
@ -83,7 +83,7 @@ Once you have rustup, install parity or download and build from source
|
||||
## Quick install
|
||||
|
||||
```bash
|
||||
cargo install --git https://github.com/ethcore/parity.git parity
|
||||
cargo install --git https://github.com/paritytech/parity.git parity
|
||||
```
|
||||
|
||||
----
|
||||
@ -92,7 +92,7 @@ cargo install --git https://github.com/ethcore/parity.git parity
|
||||
|
||||
```bash
|
||||
# download Parity code
|
||||
$ git clone https://github.com/ethcore/parity
|
||||
$ git clone https://github.com/paritytech/parity
|
||||
$ cd parity
|
||||
|
||||
# build in release mode
|
||||
|
@ -9,7 +9,7 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
base32 = "0.3"
|
||||
env_logger = "0.3"
|
||||
env_logger = "0.4"
|
||||
futures = "0.1"
|
||||
linked-hash-map = "0.3"
|
||||
log = "0.3"
|
||||
|
@ -6,7 +6,7 @@ Code generator to simplify creating a built-in Parity Dapp
|
||||
1. Clone this repository.
|
||||
|
||||
```bash
|
||||
$ git clone https://github.com/ethcore/parity.git
|
||||
$ git clone https://github.com/paritytech/parity.git
|
||||
```
|
||||
|
||||
1. Create a new directory for your Dapp. (`./myapp`)
|
||||
@ -29,10 +29,10 @@ Code generator to simplify creating a built-in Parity Dapp
|
||||
|
||||
The `inject.js` script will create global `web3` instance with proper provider that should be used by your dapp.
|
||||
|
||||
1. Create `./parity/dapps/myapp/Cargo.toml` with you apps details. See example here: [parity-status Cargo.toml](https://github.com/ethcore/parity-ui/blob/master/status/Cargo.toml).
|
||||
1. Create `./parity/dapps/myapp/Cargo.toml` with you apps details. See example here: [parity-status Cargo.toml](https://github.com/paritytech/parity-ui/blob/master/status/Cargo.toml).
|
||||
|
||||
```bash
|
||||
$ git clone https://github.com/ethcore/parity-ui.git
|
||||
$ git clone https://github.com/paritytech/parity-ui.git
|
||||
$ cd ./parity-ui/
|
||||
$ cp ./home/Cargo.toml ../parity/dapps/myapp/Cargo.toml
|
||||
$ cp ./home/build.rs ../parity/dapps/myapp/build.rs
|
||||
|
@ -67,7 +67,7 @@ pub fn init_server<F, B>(process: F, io: MetaIoHandler<Metadata>, remote: Remote
|
||||
let mut dapps_path = env::temp_dir();
|
||||
dapps_path.push("non-existent-dir-to-prevent-fs-files-from-loading");
|
||||
|
||||
// TODO [ToDr] When https://github.com/ethcore/jsonrpc/issues/26 is resolved
|
||||
// TODO [ToDr] When https://github.com/paritytech/jsonrpc/issues/26 is resolved
|
||||
// this additional EventLoop wouldn't be needed, we should be able to re-use remote.
|
||||
let event_loop = EventLoop::spawn();
|
||||
let server = process(ServerBuilder::new(
|
||||
|
@ -12,7 +12,7 @@ rustc_version = "0.1"
|
||||
[dependencies]
|
||||
parity-ui-dev = { path = "../../js", optional = true }
|
||||
# This is managed by the js/scripts/release.sh script on CI - keep it in a single line
|
||||
parity-ui-precompiled = { git = "https://github.com/ethcore/js-precompiled.git", optional = true, branch = "master" }
|
||||
parity-ui-precompiled = { git = "https://github.com/paritytech/js-precompiled.git", optional = true, branch = "master" }
|
||||
|
||||
[features]
|
||||
no-precompiled-js = ["parity-ui-dev"]
|
||||
|
@ -18,7 +18,7 @@ cargo -V && \
|
||||
gcc -v &&\
|
||||
g++ -v
|
||||
# build parity
|
||||
RUN git clone https://github.com/ethcore/parity && \
|
||||
RUN git clone https://github.com/paritytech/parity && \
|
||||
cd parity&&\
|
||||
git checkout beta && \
|
||||
git pull && \
|
||||
|
@ -48,7 +48,7 @@ RUN apt-get update && \
|
||||
# show backtraces
|
||||
RUST_BACKTRACE=1 && \
|
||||
# build parity
|
||||
cd /build&&git clone https://github.com/ethcore/parity && \
|
||||
cd /build&&git clone https://github.com/paritytech/parity && \
|
||||
cd parity && \
|
||||
git pull&& \
|
||||
git checkout $BUILD_TAG && \
|
||||
|
@ -28,7 +28,7 @@ ENV RUST_BACKTRACE 1
|
||||
cargo -V
|
||||
|
||||
# build parity
|
||||
RUN git clone https://github.com/ethcore/parity && \
|
||||
RUN git clone https://github.com/paritytech/parity && \
|
||||
cd parity && \
|
||||
git checkout beta && \
|
||||
git pull && \
|
||||
|
@ -29,7 +29,7 @@ ENV RUST_BACKTRACE 1
|
||||
cargo -V
|
||||
|
||||
# build parity
|
||||
RUN git clone https://github.com/ethcore/parity && \
|
||||
RUN git clone https://github.com/paritytech/parity && \
|
||||
cd parity && \
|
||||
git checkout beta && \
|
||||
git pull && \
|
||||
|
@ -45,7 +45,7 @@ gcc -v &&\
|
||||
g++ -v
|
||||
|
||||
# build parity
|
||||
RUN git clone https://github.com/ethcore/parity && \
|
||||
RUN git clone https://github.com/paritytech/parity && \
|
||||
cd parity && \
|
||||
git checkout beta && \
|
||||
git pull && \
|
||||
|
@ -26,7 +26,7 @@ gcc -v &&\
|
||||
g++ -v
|
||||
|
||||
# build parity
|
||||
RUN git clone https://github.com/ethcore/parity && \
|
||||
RUN git clone https://github.com/paritytech/parity && \
|
||||
cd parity && \
|
||||
git checkout stable && \
|
||||
git pull && \
|
||||
|
@ -26,7 +26,7 @@ gcc -v &&\
|
||||
g++ -v
|
||||
|
||||
# build parity
|
||||
RUN git clone https://github.com/ethcore/parity && \
|
||||
RUN git clone https://github.com/paritytech/parity && \
|
||||
cd parity && \
|
||||
git checkout beta && \
|
||||
git pull && \
|
||||
|
@ -9,4 +9,4 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
||||
log = "0.3"
|
||||
sha3 = { path = "../util/sha3" }
|
||||
primal = "0.2.3"
|
||||
parking_lot = "0.3"
|
||||
parking_lot = "0.4"
|
||||
|
@ -12,14 +12,14 @@ build = "build.rs"
|
||||
|
||||
[dependencies]
|
||||
log = "0.3"
|
||||
env_logger = "0.3"
|
||||
env_logger = "0.4"
|
||||
rustc-serialize = "0.3"
|
||||
rust-crypto = "0.2.34"
|
||||
num_cpus = "1.2"
|
||||
crossbeam = "0.2.9"
|
||||
lazy_static = "0.2"
|
||||
bloomchain = "0.1"
|
||||
semver = "0.5"
|
||||
semver = "0.6"
|
||||
bit-set = "0.4"
|
||||
time = "0.1"
|
||||
rand = "0.3"
|
||||
@ -27,6 +27,7 @@ byteorder = "1.0"
|
||||
transient-hashmap = "0.4"
|
||||
linked-hash-map = "0.3.0"
|
||||
lru-cache = "0.1.0"
|
||||
itertools = "0.5"
|
||||
ethabi = "1.0.0"
|
||||
evmjit = { path = "../evmjit", optional = true }
|
||||
clippy = { version = "0.0.103", optional = true}
|
||||
@ -47,6 +48,7 @@ ethcore-logger = { path = "../logger" }
|
||||
stats = { path = "../util/stats" }
|
||||
hyper = { git = "https://github.com/paritytech/hyper", default-features = false }
|
||||
num = "0.1"
|
||||
bn = { git = "https://github.com/paritytech/bn" }
|
||||
|
||||
[features]
|
||||
jit = ["evmjit"]
|
||||
|
@ -109,7 +109,7 @@ impl Encodable for CostTable {
|
||||
fn append_cost(s: &mut RlpStream, cost: &U256, kind: request::Kind) {
|
||||
s.begin_list(2);
|
||||
|
||||
// hack around https://github.com/ethcore/parity/issues/4356
|
||||
// hack around https://github.com/paritytech/parity/issues/4356
|
||||
Encodable::rlp_append(&kind, s);
|
||||
s.append(cost);
|
||||
}
|
||||
|
@ -278,7 +278,7 @@ impl Encodable for Request {
|
||||
fn rlp_append(&self, s: &mut RlpStream) {
|
||||
s.begin_list(2);
|
||||
|
||||
// hack around https://github.com/ethcore/parity/issues/4356
|
||||
// hack around https://github.com/paritytech/parity/issues/4356
|
||||
Encodable::rlp_append(&self.kind(), s);
|
||||
|
||||
match *self {
|
||||
@ -470,7 +470,7 @@ impl Encodable for Response {
|
||||
fn rlp_append(&self, s: &mut RlpStream) {
|
||||
s.begin_list(2);
|
||||
|
||||
// hack around https://github.com/ethcore/parity/issues/4356
|
||||
// hack around https://github.com/paritytech/parity/issues/4356
|
||||
Encodable::rlp_append(&self.kind(), s);
|
||||
|
||||
match *self {
|
||||
|
@ -53,7 +53,9 @@
|
||||
"enode://5fbfb426fbb46f8b8c1bd3dd140f5b511da558cd37d60844b525909ab82e13a25ee722293c829e52cb65c2305b1637fa9a2ea4d6634a224d5f400bfe244ac0de@162.243.55.45:30303",
|
||||
"enode://42d8f29d1db5f4b2947cd5c3d76c6d0d3697e6b9b3430c3d41e46b4bb77655433aeedc25d4b4ea9d8214b6a43008ba67199374a9b53633301bca0cd20c6928ab@104.155.176.151:30303",
|
||||
"enode://814920f1ec9510aa9ea1c8f79d8b6e6a462045f09caa2ae4055b0f34f7416fca6facd3dd45f1cf1673c0209e0503f02776b8ff94020e98b6679a0dc561b4eba0@104.154.136.117:30303",
|
||||
"enode://72e445f4e89c0f476d404bc40478b0df83a5b500d2d2e850e08eb1af0cd464ab86db6160d0fde64bd77d5f0d33507ae19035671b3c74fec126d6e28787669740@104.198.71.200:30303"
|
||||
"enode://72e445f4e89c0f476d404bc40478b0df83a5b500d2d2e850e08eb1af0cd464ab86db6160d0fde64bd77d5f0d33507ae19035671b3c74fec126d6e28787669740@104.198.71.200:30303",
|
||||
"enode://39abab9d2a41f53298c0c9dc6bbca57b0840c3ba9dccf42aa27316addc1b7e56ade32a0a9f7f52d6c5db4fe74d8824bcedfeaecf1a4e533cacb71cf8100a9442@144.76.238.49:30303",
|
||||
"enode://f50e675a34f471af2438b921914b5f06499c7438f3146f6b8936f1faeb50b8a91d0d0c24fb05a66f05865cd58c24da3e664d0def806172ddd0d4c5bdbf37747e@144.76.238.49:30306"
|
||||
],
|
||||
"accounts": {
|
||||
"0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
||||
|
@ -190,6 +190,8 @@
|
||||
"0000000000000000000000000000000000000003": { "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } },
|
||||
"0000000000000000000000000000000000000004": { "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } },
|
||||
"0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": "0x7fffffffffffff", "pricing": { "modexp": { "divisor": 20 } } } },
|
||||
"0000000000000000000000000000000000000006": { "builtin": { "name": "bn128_add", "activate_at": "0x7fffffffffffff", "pricing": { "linear": { "base": 999999, "word": 0 } } } },
|
||||
"0000000000000000000000000000000000000007": { "builtin": { "name": "bn128_mul", "activate_at": "0x7fffffffffffff", "pricing": { "linear": { "base": 999999, "word": 0 } } } },
|
||||
"3282791d6fd713f1e94f4bfd565eaa78b3a0599d": {
|
||||
"balance": "1337000000000000000000"
|
||||
},
|
||||
|
@ -23,7 +23,9 @@
|
||||
|
||||
"0x00a0a24b9f0e5ec7aa4c7389b8302fd0123194de"
|
||||
]
|
||||
}
|
||||
},
|
||||
"validateScoreTransition": 1000000,
|
||||
"eip155Transition": 1000000
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -31,7 +33,7 @@
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x2A",
|
||||
"validateReceipts" : false
|
||||
"validateReceiptsTransition" : 1000000
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
|
@ -25,8 +25,8 @@
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x3",
|
||||
"forkBlock": 333922,
|
||||
"forkCanonHash": "0x8737eb141d4f05db57af63fc8d3b4d4d8f9cddb0c4e1ab855de8c288fdc1924f",
|
||||
"forkBlock": 641350,
|
||||
"forkCanonHash": "0x8033403e9fe5811a7b6d6b469905915de1c59207ce2172cbcf5d6ff14fa6a2eb",
|
||||
"eip98Transition": "0x7fffffffffffff"
|
||||
},
|
||||
"genesis": {
|
||||
@ -44,11 +44,8 @@
|
||||
"gasLimit": "0x1000000"
|
||||
},
|
||||
"nodes": [
|
||||
"enode://a22f0977ce02653bf95e38730106356342df48b5222e2c2a1a6f9ef34769bf593bae9ca0a888cf60839edd52efc1b6e393c63a57d76f4c4fe14e641f1f9e637e@128.199.55.137:30303",
|
||||
"enode://012239fccf3ff1d92b036983a430cb6705c6528c96c0354413f8854802138e5135c084ab36e7c54efb621c46728df8c3a6f4c1db9bb48a1330efe3f82f2dd7a6@52.169.94.142:30303",
|
||||
"enode://1462682e4b7ba2258346d55e25e5b9d264b0db40cee12bdfba4e72b1d7050350ea954c006e9106dd96a128e6e0bd6dffb17eed51f9f99bf7f9cdadfeaf8da4ff@51.15.61.253:30303",
|
||||
"enode://98fbb020c799ae39a828bd75dc2bd5d4721539faf317076b275f91182a5c8900b592e8abfdddceae674a7c3bb40ea00a6ca9ccb7805ab58c4b7b29c61c8f7239@51.15.62.44:30303",
|
||||
"enode://d801dd4e3d15a8bf785931add164bd9c313e3f6b5749d9302b311f2b48064cba5c86c32b1302c27cd983fc89ae07d4d306dd1197610835b8782e95dfb1b3f9ea@51.15.43.255:30303"
|
||||
"enode://20c9ad97c081d63397d7b685a412227a40e23c8bdc6688c6f37e97cfbc22d2b4d1db1510d8f61e6a8866ad7f0e17c02b14182d37ea7c3c8b9c2683aeb6b733a1@52.169.14.227:30303",
|
||||
"enode://6ce05930c72abc632c58e2e4324f7c7ea478cec0ed4fa2528982cf34483094e9cbc9216e7aa349691242576d552a2a56aaeae426c5303ded677ce455ba1acd9d@13.84.180.240:30303"
|
||||
],
|
||||
"accounts": {
|
||||
"0000000000000000000000000000000000000001": { "balance": "1", "nonce": "0", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
||||
|
@ -492,6 +492,16 @@ impl LockedBlock {
|
||||
_ => Ok(SealedBlock { block: s.block, uncle_bytes: s.uncle_bytes }),
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove state root from transaction receipts to make them EIP-98 compatible.
|
||||
pub fn strip_receipts(self) -> LockedBlock {
|
||||
let mut block = self;
|
||||
for receipt in &mut block.block.receipts {
|
||||
receipt.state_root = None;
|
||||
}
|
||||
block.block.header.set_receipts_root(ordered_trie_root(block.block.receipts.iter().map(|r| r.rlp_bytes().to_vec())));
|
||||
block
|
||||
}
|
||||
}
|
||||
|
||||
impl Drain for LockedBlock {
|
||||
|
@ -27,10 +27,19 @@ use util::{U256, H256, Uint, Hashable, BytesRef};
|
||||
use ethkey::{Signature, recover as ec_recover};
|
||||
use ethjson;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Error(pub &'static str);
|
||||
|
||||
impl From<&'static str> for Error {
|
||||
fn from(val: &'static str) -> Self {
|
||||
Error(val)
|
||||
}
|
||||
}
|
||||
|
||||
/// Native implementation of a built-in contract.
|
||||
pub trait Impl: Send + Sync {
|
||||
/// execute this built-in on the given input, writing to the given output.
|
||||
fn execute(&self, input: &[u8], output: &mut BytesRef);
|
||||
fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
/// A gas pricing scheme for built-in contracts.
|
||||
@ -102,7 +111,9 @@ impl Builtin {
|
||||
pub fn cost(&self, input: &[u8]) -> U256 { self.pricer.cost(input) }
|
||||
|
||||
/// Simple forwarder for execute.
|
||||
pub fn execute(&self, input: &[u8], output: &mut BytesRef) { self.native.execute(input, output) }
|
||||
pub fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> {
|
||||
self.native.execute(input, output)
|
||||
}
|
||||
|
||||
/// Whether the builtin is activated at the given block number.
|
||||
pub fn is_active(&self, at: u64) -> bool { at >= self.activate_at }
|
||||
@ -145,6 +156,8 @@ fn ethereum_builtin(name: &str) -> Box<Impl> {
|
||||
"sha256" => Box::new(Sha256) as Box<Impl>,
|
||||
"ripemd160" => Box::new(Ripemd160) as Box<Impl>,
|
||||
"modexp" => Box::new(ModexpImpl) as Box<Impl>,
|
||||
"bn128_add" => Box::new(Bn128AddImpl) as Box<Impl>,
|
||||
"bn128_mul" => Box::new(Bn128MulImpl) as Box<Impl>,
|
||||
_ => panic!("invalid builtin name: {}", name),
|
||||
}
|
||||
}
|
||||
@ -172,14 +185,21 @@ struct Ripemd160;
|
||||
#[derive(Debug)]
|
||||
struct ModexpImpl;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Bn128AddImpl;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Bn128MulImpl;
|
||||
|
||||
impl Impl for Identity {
|
||||
fn execute(&self, input: &[u8], output: &mut BytesRef) {
|
||||
fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> {
|
||||
output.write(0, input);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Impl for EcRecover {
|
||||
fn execute(&self, i: &[u8], output: &mut BytesRef) {
|
||||
fn execute(&self, i: &[u8], output: &mut BytesRef) -> Result<(), Error> {
|
||||
let len = min(i.len(), 128);
|
||||
|
||||
let mut input = [0; 128];
|
||||
@ -192,7 +212,7 @@ impl Impl for EcRecover {
|
||||
|
||||
let bit = match v[31] {
|
||||
27 | 28 if &v.0[..31] == &[0; 31] => v[31] - 27,
|
||||
_ => return,
|
||||
_ => { return Ok(()); },
|
||||
};
|
||||
|
||||
let s = Signature::from_rsv(&r, &s, bit);
|
||||
@ -203,11 +223,13 @@ impl Impl for EcRecover {
|
||||
output.write(12, &r[12..r.len()]);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Impl for Sha256 {
|
||||
fn execute(&self, input: &[u8], output: &mut BytesRef) {
|
||||
fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> {
|
||||
let mut sha = Sha256Digest::new();
|
||||
sha.input(input);
|
||||
|
||||
@ -215,11 +237,13 @@ impl Impl for Sha256 {
|
||||
sha.result(&mut out);
|
||||
|
||||
output.write(0, &out);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Impl for Ripemd160 {
|
||||
fn execute(&self, input: &[u8], output: &mut BytesRef) {
|
||||
fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> {
|
||||
let mut sha = Ripemd160Digest::new();
|
||||
sha.input(input);
|
||||
|
||||
@ -227,11 +251,13 @@ impl Impl for Ripemd160 {
|
||||
sha.result(&mut out[12..32]);
|
||||
|
||||
output.write(0, &out);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Impl for ModexpImpl {
|
||||
fn execute(&self, input: &[u8], output: &mut BytesRef) {
|
||||
fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> {
|
||||
let mut reader = input.chain(io::repeat(0));
|
||||
let mut buf = [0; 32];
|
||||
|
||||
@ -294,6 +320,76 @@ impl Impl for ModexpImpl {
|
||||
let res_start = mod_len - bytes.len();
|
||||
output.write(res_start, &bytes);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn read_fr(reader: &mut io::Chain<&[u8], io::Repeat>) -> Result<::bn::Fr, Error> {
|
||||
let mut buf = [0u8; 32];
|
||||
|
||||
reader.read_exact(&mut buf[..]).expect("reading from zero-extended memory cannot fail; qed");
|
||||
::bn::Fr::from_slice(&buf[0..32]).map_err(|_| Error::from("Invalid field element"))
|
||||
}
|
||||
|
||||
fn read_point(reader: &mut io::Chain<&[u8], io::Repeat>) -> Result<::bn::G1, Error> {
|
||||
use bn::{Fq, AffineG1, G1, Group};
|
||||
|
||||
let mut buf = [0u8; 32];
|
||||
|
||||
reader.read_exact(&mut buf[..]).expect("reading from zero-extended memory cannot fail; qed");
|
||||
let px = Fq::from_slice(&buf[0..32]).map_err(|_| Error::from("Invalid point x coordinate"))?;
|
||||
|
||||
reader.read_exact(&mut buf[..]).expect("reading from zero-extended memory cannot fail; qed");
|
||||
let py = Fq::from_slice(&buf[0..32]).map_err(|_| Error::from("Invalid point x coordinate"))?;
|
||||
|
||||
Ok(
|
||||
if px == Fq::zero() && py == Fq::zero() {
|
||||
G1::zero()
|
||||
} else {
|
||||
AffineG1::new(px, py).map_err(|_| Error::from("Invalid curve point"))?.into()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
impl Impl for Bn128AddImpl {
|
||||
// Can fail if any of the 2 points does not belong the bn128 curve
|
||||
fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> {
|
||||
use bn::AffineG1;
|
||||
|
||||
let mut padded_input = input.chain(io::repeat(0));
|
||||
let p1 = read_point(&mut padded_input)?;
|
||||
let p2 = read_point(&mut padded_input)?;
|
||||
|
||||
let mut write_buf = [0u8; 64];
|
||||
if let Some(sum) = AffineG1::from_jacobian(p1 + p2) {
|
||||
// point not at infinity
|
||||
sum.x().to_big_endian(&mut write_buf[0..32]).expect("Cannot fail since 0..32 is 32-byte length");
|
||||
sum.y().to_big_endian(&mut write_buf[32..64]).expect("Cannot fail since 32..64 is 32-byte length");;
|
||||
}
|
||||
output.write(0, &write_buf);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Impl for Bn128MulImpl {
|
||||
// Can fail if first paramter (bn128 curve point) does not actually belong to the curve
|
||||
fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> {
|
||||
use bn::AffineG1;
|
||||
|
||||
let mut padded_input = input.chain(io::repeat(0));
|
||||
let p = read_point(&mut padded_input)?;
|
||||
let fr = read_fr(&mut padded_input)?;
|
||||
|
||||
let mut write_buf = [0u8; 64];
|
||||
if let Some(sum) = AffineG1::from_jacobian(p * fr) {
|
||||
// point not at infinity
|
||||
sum.x().to_big_endian(&mut write_buf[0..32]).expect("Cannot fail since 0..32 is 32-byte length");
|
||||
sum.y().to_big_endian(&mut write_buf[32..64]).expect("Cannot fail since 32..64 is 32-byte length");;
|
||||
}
|
||||
output.write(0, &write_buf);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -310,15 +406,15 @@ mod tests {
|
||||
let i = [0u8, 1, 2, 3];
|
||||
|
||||
let mut o2 = [255u8; 2];
|
||||
f.execute(&i[..], &mut BytesRef::Fixed(&mut o2[..]));
|
||||
f.execute(&i[..], &mut BytesRef::Fixed(&mut o2[..])).expect("Builtin should not fail");
|
||||
assert_eq!(i[0..2], o2);
|
||||
|
||||
let mut o4 = [255u8; 4];
|
||||
f.execute(&i[..], &mut BytesRef::Fixed(&mut o4[..]));
|
||||
f.execute(&i[..], &mut BytesRef::Fixed(&mut o4[..])).expect("Builtin should not fail");
|
||||
assert_eq!(i, o4);
|
||||
|
||||
let mut o8 = [255u8; 8];
|
||||
f.execute(&i[..], &mut BytesRef::Fixed(&mut o8[..]));
|
||||
f.execute(&i[..], &mut BytesRef::Fixed(&mut o8[..])).expect("Builtin should not fail");
|
||||
assert_eq!(i, o8[..4]);
|
||||
assert_eq!([255u8; 4], o8[4..]);
|
||||
}
|
||||
@ -331,19 +427,19 @@ mod tests {
|
||||
let i = [0u8; 0];
|
||||
|
||||
let mut o = [255u8; 32];
|
||||
f.execute(&i[..], &mut BytesRef::Fixed(&mut o[..]));
|
||||
f.execute(&i[..], &mut BytesRef::Fixed(&mut o[..])).expect("Builtin should not fail");
|
||||
assert_eq!(&o[..], &(FromHex::from_hex("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855").unwrap())[..]);
|
||||
|
||||
let mut o8 = [255u8; 8];
|
||||
f.execute(&i[..], &mut BytesRef::Fixed(&mut o8[..]));
|
||||
f.execute(&i[..], &mut BytesRef::Fixed(&mut o8[..])).expect("Builtin should not fail");
|
||||
assert_eq!(&o8[..], &(FromHex::from_hex("e3b0c44298fc1c14").unwrap())[..]);
|
||||
|
||||
let mut o34 = [255u8; 34];
|
||||
f.execute(&i[..], &mut BytesRef::Fixed(&mut o34[..]));
|
||||
f.execute(&i[..], &mut BytesRef::Fixed(&mut o34[..])).expect("Builtin should not fail");
|
||||
assert_eq!(&o34[..], &(FromHex::from_hex("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855ffff").unwrap())[..]);
|
||||
|
||||
let mut ov = vec![];
|
||||
f.execute(&i[..], &mut BytesRef::Flexible(&mut ov));
|
||||
f.execute(&i[..], &mut BytesRef::Flexible(&mut ov)).expect("Builtin should not fail");
|
||||
assert_eq!(&ov[..], &(FromHex::from_hex("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855").unwrap())[..]);
|
||||
}
|
||||
|
||||
@ -355,15 +451,15 @@ mod tests {
|
||||
let i = [0u8; 0];
|
||||
|
||||
let mut o = [255u8; 32];
|
||||
f.execute(&i[..], &mut BytesRef::Fixed(&mut o[..]));
|
||||
f.execute(&i[..], &mut BytesRef::Fixed(&mut o[..])).expect("Builtin should not fail");
|
||||
assert_eq!(&o[..], &(FromHex::from_hex("0000000000000000000000009c1185a5c5e9fc54612808977ee8f548b2258d31").unwrap())[..]);
|
||||
|
||||
let mut o8 = [255u8; 8];
|
||||
f.execute(&i[..], &mut BytesRef::Fixed(&mut o8[..]));
|
||||
f.execute(&i[..], &mut BytesRef::Fixed(&mut o8[..])).expect("Builtin should not fail");
|
||||
assert_eq!(&o8[..], &(FromHex::from_hex("0000000000000000").unwrap())[..]);
|
||||
|
||||
let mut o34 = [255u8; 34];
|
||||
f.execute(&i[..], &mut BytesRef::Fixed(&mut o34[..]));
|
||||
f.execute(&i[..], &mut BytesRef::Fixed(&mut o34[..])).expect("Builtin should not fail");
|
||||
assert_eq!(&o34[..], &(FromHex::from_hex("0000000000000000000000009c1185a5c5e9fc54612808977ee8f548b2258d31ffff").unwrap())[..]);
|
||||
}
|
||||
|
||||
@ -383,40 +479,40 @@ mod tests {
|
||||
let i = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03").unwrap();
|
||||
|
||||
let mut o = [255u8; 32];
|
||||
f.execute(&i[..], &mut BytesRef::Fixed(&mut o[..]));
|
||||
f.execute(&i[..], &mut BytesRef::Fixed(&mut o[..])).expect("Builtin should not fail");
|
||||
assert_eq!(&o[..], &(FromHex::from_hex("000000000000000000000000c08b5542d177ac6686946920409741463a15dddb").unwrap())[..]);
|
||||
|
||||
let mut o8 = [255u8; 8];
|
||||
f.execute(&i[..], &mut BytesRef::Fixed(&mut o8[..]));
|
||||
f.execute(&i[..], &mut BytesRef::Fixed(&mut o8[..])).expect("Builtin should not fail");
|
||||
assert_eq!(&o8[..], &(FromHex::from_hex("0000000000000000").unwrap())[..]);
|
||||
|
||||
let mut o34 = [255u8; 34];
|
||||
f.execute(&i[..], &mut BytesRef::Fixed(&mut o34[..]));
|
||||
f.execute(&i[..], &mut BytesRef::Fixed(&mut o34[..])).expect("Builtin should not fail");
|
||||
assert_eq!(&o34[..], &(FromHex::from_hex("000000000000000000000000c08b5542d177ac6686946920409741463a15dddbffff").unwrap())[..]);
|
||||
|
||||
let i_bad = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001a650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03").unwrap();
|
||||
let mut o = [255u8; 32];
|
||||
f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..]));
|
||||
f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..])).expect("Builtin should not fail");
|
||||
assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]);
|
||||
|
||||
let i_bad = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b0000000000000000000000000000000000000000000000000000000000000000").unwrap();
|
||||
let mut o = [255u8; 32];
|
||||
f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..]));
|
||||
f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..])).expect("Builtin should not fail");
|
||||
assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]);
|
||||
|
||||
let i_bad = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b").unwrap();
|
||||
let mut o = [255u8; 32];
|
||||
f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..]));
|
||||
f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..])).expect("Builtin should not fail");
|
||||
assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]);
|
||||
|
||||
let i_bad = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001bffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000001b").unwrap();
|
||||
let mut o = [255u8; 32];
|
||||
f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..]));
|
||||
f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..])).expect("Builtin should not fail");
|
||||
assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]);
|
||||
|
||||
let i_bad = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001bffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap();
|
||||
let mut o = [255u8; 32];
|
||||
f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..]));
|
||||
f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..])).expect("Builtin should not fail");
|
||||
assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]);
|
||||
|
||||
// TODO: Should this (corrupted version of the above) fail rather than returning some address?
|
||||
@ -450,7 +546,7 @@ mod tests {
|
||||
let expected = FromHex::from_hex("0000000000000000000000000000000000000000000000000000000000000001").unwrap();
|
||||
let expected_cost = 1638;
|
||||
|
||||
f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..]));
|
||||
f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])).expect("Builtin should not fail");
|
||||
assert_eq!(output, expected);
|
||||
assert_eq!(f.cost(&input[..]), expected_cost.into());
|
||||
}
|
||||
@ -469,7 +565,7 @@ mod tests {
|
||||
let expected = FromHex::from_hex("0000000000000000000000000000000000000000000000000000000000000000").unwrap();
|
||||
let expected_cost = 1638;
|
||||
|
||||
f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..]));
|
||||
f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])).expect("Builtin should not fail");
|
||||
assert_eq!(output, expected);
|
||||
assert_eq!(f.cost(&input[..]), expected_cost.into());
|
||||
}
|
||||
@ -489,7 +585,7 @@ mod tests {
|
||||
let expected = FromHex::from_hex("3b01b01ac41f2d6e917c6d6a221ce793802469026d9ab7578fa2e79e4da6aaab").unwrap();
|
||||
let expected_cost = 102;
|
||||
|
||||
f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..]));
|
||||
f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])).expect("Builtin should not fail");
|
||||
assert_eq!(output, expected);
|
||||
assert_eq!(f.cost(&input[..]), expected_cost.into());
|
||||
}
|
||||
@ -507,12 +603,118 @@ mod tests {
|
||||
let mut output = vec![];
|
||||
let expected_cost = 0;
|
||||
|
||||
f.execute(&input[..], &mut BytesRef::Flexible(&mut output));
|
||||
f.execute(&input[..], &mut BytesRef::Flexible(&mut output)).expect("Builtin should not fail");
|
||||
assert_eq!(output.len(), 0); // shouldn't have written any output.
|
||||
assert_eq!(f.cost(&input[..]), expected_cost.into());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bn128_add() {
|
||||
use rustc_serialize::hex::FromHex;
|
||||
|
||||
let f = Builtin {
|
||||
pricer: Box::new(Linear { base: 0, word: 0 }),
|
||||
native: ethereum_builtin("bn128_add"),
|
||||
activate_at: 0,
|
||||
};
|
||||
|
||||
// zero-points additions
|
||||
{
|
||||
let input = FromHex::from_hex("\
|
||||
0000000000000000000000000000000000000000000000000000000000000000\
|
||||
0000000000000000000000000000000000000000000000000000000000000000\
|
||||
0000000000000000000000000000000000000000000000000000000000000000\
|
||||
0000000000000000000000000000000000000000000000000000000000000000"
|
||||
).unwrap();
|
||||
|
||||
let mut output = vec![0u8; 64];
|
||||
let expected = FromHex::from_hex("\
|
||||
0000000000000000000000000000000000000000000000000000000000000000\
|
||||
0000000000000000000000000000000000000000000000000000000000000000"
|
||||
).unwrap();
|
||||
|
||||
f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])).expect("Builtin should not fail");
|
||||
assert_eq!(output, expected);
|
||||
}
|
||||
|
||||
|
||||
// no input, should not fail
|
||||
{
|
||||
let mut empty = [0u8; 0];
|
||||
let input = BytesRef::Fixed(&mut empty);
|
||||
|
||||
let mut output = vec![0u8; 64];
|
||||
let expected = FromHex::from_hex("\
|
||||
0000000000000000000000000000000000000000000000000000000000000000\
|
||||
0000000000000000000000000000000000000000000000000000000000000000"
|
||||
).unwrap();
|
||||
|
||||
f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])).expect("Builtin should not fail");
|
||||
assert_eq!(output, expected);
|
||||
}
|
||||
|
||||
// should fail - point not on curve
|
||||
{
|
||||
let input = FromHex::from_hex("\
|
||||
1111111111111111111111111111111111111111111111111111111111111111\
|
||||
1111111111111111111111111111111111111111111111111111111111111111\
|
||||
1111111111111111111111111111111111111111111111111111111111111111\
|
||||
1111111111111111111111111111111111111111111111111111111111111111"
|
||||
).unwrap();
|
||||
|
||||
let mut output = vec![0u8; 64];
|
||||
|
||||
let res = f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..]));
|
||||
assert!(res.is_err(), "There should be built-in error here");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn bn128_mul() {
|
||||
use rustc_serialize::hex::FromHex;
|
||||
|
||||
let f = Builtin {
|
||||
pricer: Box::new(Linear { base: 0, word: 0 }),
|
||||
native: ethereum_builtin("bn128_mul"),
|
||||
activate_at: 0,
|
||||
};
|
||||
|
||||
// zero-point multiplication
|
||||
{
|
||||
let input = FromHex::from_hex("\
|
||||
0000000000000000000000000000000000000000000000000000000000000000\
|
||||
0000000000000000000000000000000000000000000000000000000000000000\
|
||||
0200000000000000000000000000000000000000000000000000000000000000"
|
||||
).unwrap();
|
||||
|
||||
let mut output = vec![0u8; 64];
|
||||
let expected = FromHex::from_hex("\
|
||||
0000000000000000000000000000000000000000000000000000000000000000\
|
||||
0000000000000000000000000000000000000000000000000000000000000000"
|
||||
).unwrap();
|
||||
|
||||
f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])).expect("Builtin should not fail");
|
||||
assert_eq!(output, expected);
|
||||
}
|
||||
|
||||
// should fail - point not on curve
|
||||
{
|
||||
let input = FromHex::from_hex("\
|
||||
1111111111111111111111111111111111111111111111111111111111111111\
|
||||
1111111111111111111111111111111111111111111111111111111111111111\
|
||||
0f00000000000000000000000000000000000000000000000000000000000000"
|
||||
).unwrap();
|
||||
|
||||
let mut output = vec![0u8; 64];
|
||||
|
||||
let res = f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..]));
|
||||
assert!(res.is_err(), "There should be built-in error here");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn from_unknown_linear() {
|
||||
@ -549,7 +751,7 @@ mod tests {
|
||||
|
||||
let i = [0u8, 1, 2, 3];
|
||||
let mut o = [255u8; 4];
|
||||
b.execute(&i[..], &mut BytesRef::Fixed(&mut o[..]));
|
||||
b.execute(&i[..], &mut BytesRef::Fixed(&mut o[..])).expect("Builtin should not fail");
|
||||
assert_eq!(i, o);
|
||||
}
|
||||
|
||||
@ -571,7 +773,7 @@ mod tests {
|
||||
|
||||
let i = [0u8, 1, 2, 3];
|
||||
let mut o = [255u8; 4];
|
||||
b.execute(&i[..], &mut BytesRef::Fixed(&mut o[..]));
|
||||
b.execute(&i[..], &mut BytesRef::Fixed(&mut o[..])).expect("Builtin should not fail");
|
||||
assert_eq!(i, o);
|
||||
}
|
||||
}
|
@ -388,12 +388,16 @@ impl Client {
|
||||
let db = self.state_db.lock().boxed_clone_canon(header.parent_hash());
|
||||
|
||||
let enact_result = enact_verified(block, engine, self.tracedb.read().tracing_enabled(), db, &parent, last_hashes, self.factories.clone());
|
||||
let locked_block = enact_result.map_err(|e| {
|
||||
let mut locked_block = enact_result.map_err(|e| {
|
||||
warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
|
||||
})?;
|
||||
|
||||
if header.number() < self.engine().params().validate_receipts_transition && header.receipts_root() != locked_block.block().header().receipts_root() {
|
||||
locked_block = locked_block.strip_receipts();
|
||||
}
|
||||
|
||||
// Final Verification
|
||||
if let Err(e) = self.verifier.verify_block_final(header, locked_block.block().header(), self.engine().params().validate_receipts) {
|
||||
if let Err(e) = self.verifier.verify_block_final(header, locked_block.block().header()) {
|
||||
warn!(target: "client", "Stage 4 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
|
||||
return Err(());
|
||||
}
|
||||
|
@ -27,12 +27,13 @@ use block::*;
|
||||
use spec::CommonParams;
|
||||
use engines::{Engine, Seal, EngineError};
|
||||
use header::Header;
|
||||
use error::{Error, BlockError};
|
||||
use error::{Error, TransactionError, BlockError};
|
||||
use evm::Schedule;
|
||||
use ethjson;
|
||||
use io::{IoContext, IoHandler, TimerToken, IoService};
|
||||
use env_info::EnvInfo;
|
||||
use builtin::Builtin;
|
||||
use transaction::UnverifiedTransaction;
|
||||
use client::{Client, EngineClient};
|
||||
use state::CleanupMode;
|
||||
use super::signer::EngineSigner;
|
||||
@ -53,6 +54,10 @@ pub struct AuthorityRoundParams {
|
||||
pub start_step: Option<u64>,
|
||||
/// Valid validators.
|
||||
pub validators: ethjson::spec::ValidatorSet,
|
||||
/// Chain score validation transition block.
|
||||
pub validate_score_transition: u64,
|
||||
/// Number of first block where EIP-155 rules are validated.
|
||||
pub eip155_transition: u64,
|
||||
}
|
||||
|
||||
impl From<ethjson::spec::AuthorityRoundParams> for AuthorityRoundParams {
|
||||
@ -64,6 +69,8 @@ impl From<ethjson::spec::AuthorityRoundParams> for AuthorityRoundParams {
|
||||
block_reward: p.block_reward.map_or_else(U256::zero, Into::into),
|
||||
registrar: p.registrar.map_or_else(Address::new, Into::into),
|
||||
start_step: p.start_step.map(Into::into),
|
||||
validate_score_transition: p.validate_score_transition.map_or(0, Into::into),
|
||||
eip155_transition: p.eip155_transition.map_or(0, Into::into),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -85,6 +92,8 @@ pub struct AuthorityRound {
|
||||
validators: Box<ValidatorSet>,
|
||||
/// Is this Engine just for testing (prevents step calibration).
|
||||
calibrate_step: bool,
|
||||
validate_score_transition: u64,
|
||||
eip155_transition: u64,
|
||||
}
|
||||
|
||||
fn header_step(header: &Header) -> Result<usize, ::rlp::DecoderError> {
|
||||
@ -125,6 +134,8 @@ impl AuthorityRound {
|
||||
signer: Default::default(),
|
||||
validators: new_validator_set(our_params.validators),
|
||||
calibrate_step: our_params.start_step.is_none(),
|
||||
validate_score_transition: our_params.validate_score_transition,
|
||||
eip155_transition: our_params.eip155_transition,
|
||||
});
|
||||
// Do not initialize timeouts for tests.
|
||||
if should_timeout {
|
||||
@ -295,6 +306,10 @@ impl Engine for AuthorityRound {
|
||||
Err(From::from(BlockError::InvalidSealArity(
|
||||
Mismatch { expected: self.seal_fields(), found: header.seal().len() }
|
||||
)))
|
||||
} else if header.number() >= self.validate_score_transition && *header.difficulty() >= U256::from(U128::max_value()) {
|
||||
Err(From::from(BlockError::DifficultyOutOfBounds(
|
||||
OutOfBounds { min: None, max: Some(U256::from(U128::max_value())), found: *header.difficulty() }
|
||||
)))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
@ -327,7 +342,8 @@ impl Engine for AuthorityRound {
|
||||
}
|
||||
|
||||
// Check if parent is from a previous step.
|
||||
if step == header_step(parent)? {
|
||||
let parent_step = header_step(parent)?;
|
||||
if step == parent_step {
|
||||
trace!(target: "engine", "Multiple blocks proposed for step {}.", step);
|
||||
self.validators.report_malicious(header.author());
|
||||
Err(EngineError::DoubleVote(header.author().clone()))?;
|
||||
@ -342,6 +358,18 @@ impl Engine for AuthorityRound {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn verify_transaction_basic(&self, t: &UnverifiedTransaction, header: &Header) -> result::Result<(), Error> {
|
||||
t.check_low_s()?;
|
||||
|
||||
if let Some(n) = t.network_id() {
|
||||
if header.number() >= self.eip155_transition && n != self.params().chain_id {
|
||||
return Err(TransactionError::InvalidNetworkId.into());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn register_client(&self, client: Weak<Client>) {
|
||||
*self.client.write() = Some(client.clone());
|
||||
self.validators.register_contract(client);
|
||||
|
@ -39,7 +39,7 @@ use account_provider::AccountProvider;
|
||||
use block::ExecutedBlock;
|
||||
use builtin::Builtin;
|
||||
use env_info::EnvInfo;
|
||||
use error::Error;
|
||||
use error::{Error, TransactionError};
|
||||
use spec::CommonParams;
|
||||
use evm::Schedule;
|
||||
use header::Header;
|
||||
@ -157,6 +157,13 @@ pub trait Engine : Sync + Send {
|
||||
// TODO: consider including State in the params.
|
||||
fn verify_transaction_basic(&self, t: &UnverifiedTransaction, _header: &Header) -> Result<(), Error> {
|
||||
t.check_low_s()?;
|
||||
|
||||
if let Some(n) = t.network_id() {
|
||||
if n != self.params().chain_id {
|
||||
return Err(TransactionError::InvalidNetworkId.into());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -43,6 +43,8 @@ pub struct EthashParams {
|
||||
pub difficulty_bound_divisor: U256,
|
||||
/// Difficulty increment divisor.
|
||||
pub difficulty_increment_divisor: u64,
|
||||
/// Metropolis difficulty increment divisor.
|
||||
pub metropolis_difficulty_increment_divisor: u64,
|
||||
/// Block duration.
|
||||
pub duration_limit: u64,
|
||||
/// Block reward.
|
||||
@ -63,6 +65,8 @@ pub struct EthashParams {
|
||||
pub difficulty_hardfork_bound_divisor: U256,
|
||||
/// Block on which there is no additional difficulty from the exponential bomb.
|
||||
pub bomb_defuse_transition: u64,
|
||||
/// Number of first block where EIP-100 rules begin.
|
||||
pub eip100b_transition: u64,
|
||||
/// Number of first block where EIP-150 rules begin.
|
||||
pub eip150_transition: u64,
|
||||
/// Number of first block where EIP-155 rules begin.
|
||||
@ -96,6 +100,7 @@ impl From<ethjson::spec::EthashParams> for EthashParams {
|
||||
minimum_difficulty: p.minimum_difficulty.into(),
|
||||
difficulty_bound_divisor: p.difficulty_bound_divisor.into(),
|
||||
difficulty_increment_divisor: p.difficulty_increment_divisor.map_or(10, Into::into),
|
||||
metropolis_difficulty_increment_divisor: p.metropolis_difficulty_increment_divisor.map_or(9, Into::into),
|
||||
duration_limit: p.duration_limit.into(),
|
||||
block_reward: p.block_reward.into(),
|
||||
registrar: p.registrar.map_or_else(Address::new, Into::into),
|
||||
@ -106,6 +111,7 @@ impl From<ethjson::spec::EthashParams> for EthashParams {
|
||||
difficulty_hardfork_transition: p.difficulty_hardfork_transition.map_or(u64::max_value(), Into::into),
|
||||
difficulty_hardfork_bound_divisor: p.difficulty_hardfork_bound_divisor.map_or(p.difficulty_bound_divisor.into(), Into::into),
|
||||
bomb_defuse_transition: p.bomb_defuse_transition.map_or(u64::max_value(), Into::into),
|
||||
eip100b_transition: p.eip100b_transition.map_or(u64::max_value(), Into::into),
|
||||
eip150_transition: p.eip150_transition.map_or(0, Into::into),
|
||||
eip155_transition: p.eip155_transition.map_or(0, Into::into),
|
||||
eip160_transition: p.eip160_transition.map_or(0, Into::into),
|
||||
@ -406,6 +412,8 @@ impl Ethash {
|
||||
panic!("Can't calculate genesis block difficulty");
|
||||
}
|
||||
|
||||
let parent_has_uncles = parent.uncles_hash() != &sha3::SHA3_EMPTY_LIST_RLP;
|
||||
|
||||
let min_difficulty = self.ethash_params.minimum_difficulty;
|
||||
let difficulty_hardfork = header.number() >= self.ethash_params.difficulty_hardfork_transition;
|
||||
let difficulty_bound_divisor = match difficulty_hardfork {
|
||||
@ -417,19 +425,27 @@ impl Ethash {
|
||||
|
||||
let mut target = if header.number() < frontier_limit {
|
||||
if header.timestamp() >= parent.timestamp() + duration_limit {
|
||||
parent.difficulty().clone() - (parent.difficulty().clone() / difficulty_bound_divisor)
|
||||
*parent.difficulty() - (*parent.difficulty() / difficulty_bound_divisor)
|
||||
} else {
|
||||
parent.difficulty().clone() + (parent.difficulty().clone() / difficulty_bound_divisor)
|
||||
*parent.difficulty() + (*parent.difficulty() / difficulty_bound_divisor)
|
||||
}
|
||||
}
|
||||
else {
|
||||
trace!(target: "ethash", "Calculating difficulty parent.difficulty={}, header.timestamp={}, parent.timestamp={}", parent.difficulty(), header.timestamp(), parent.timestamp());
|
||||
//block_diff = parent_diff + parent_diff // 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99)
|
||||
let diff_inc = (header.timestamp() - parent.timestamp()) / self.ethash_params.difficulty_increment_divisor;
|
||||
if diff_inc <= 1 {
|
||||
parent.difficulty().clone() + parent.difficulty().clone() / From::from(difficulty_bound_divisor) * From::from(1 - diff_inc)
|
||||
let (increment_divisor, threshold) = if header.number() < self.ethash_params.eip100b_transition {
|
||||
(self.ethash_params.difficulty_increment_divisor, 1)
|
||||
} else if parent_has_uncles {
|
||||
(self.ethash_params.metropolis_difficulty_increment_divisor, 2)
|
||||
} else {
|
||||
parent.difficulty().clone() - parent.difficulty().clone() / From::from(difficulty_bound_divisor) * From::from(min(diff_inc - 1, 99))
|
||||
(self.ethash_params.metropolis_difficulty_increment_divisor, 1)
|
||||
};
|
||||
|
||||
let diff_inc = (header.timestamp() - parent.timestamp()) / increment_divisor;
|
||||
if diff_inc <= threshold {
|
||||
*parent.difficulty() + *parent.difficulty() / difficulty_bound_divisor * (threshold - diff_inc).into()
|
||||
} else {
|
||||
*parent.difficulty() - *parent.difficulty() / difficulty_bound_divisor * min(diff_inc - threshold, 99).into()
|
||||
}
|
||||
};
|
||||
target = max(min_difficulty, target);
|
||||
|
@ -20,6 +20,7 @@ use std::{ops, cmp, fmt};
|
||||
use util::{U128, U256, U512, Uint, trie};
|
||||
use action_params::ActionParams;
|
||||
use evm::Ext;
|
||||
use builtin;
|
||||
|
||||
/// Evm errors.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
@ -59,6 +60,8 @@ pub enum Error {
|
||||
/// What was the stack limit
|
||||
limit: usize
|
||||
},
|
||||
/// Built-in contract failed on given input
|
||||
BuiltIn(&'static str),
|
||||
/// Returned on evm internal error. Should never be ignored during development.
|
||||
/// Likely to cause consensus issues.
|
||||
Internal(String),
|
||||
@ -70,6 +73,12 @@ impl From<Box<trie::TrieError>> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<builtin::Error> for Error {
|
||||
fn from(err: builtin::Error) -> Self {
|
||||
Error::BuiltIn(err.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
use self::Error::*;
|
||||
@ -79,6 +88,7 @@ impl fmt::Display for Error {
|
||||
BadInstruction { .. } => "Bad instruction",
|
||||
StackUnderflow { .. } => "Stack underflow",
|
||||
OutOfStack { .. } => "Out of stack",
|
||||
BuiltIn { .. } => "Built-in failed",
|
||||
Internal(ref msg) => msg,
|
||||
};
|
||||
message.fmt(f)
|
||||
|
@ -276,7 +276,12 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
|
||||
|
||||
let cost = builtin.cost(data);
|
||||
if cost <= params.gas {
|
||||
builtin.execute(data, &mut output);
|
||||
if let Err(e) = builtin.execute(data, &mut output) {
|
||||
self.state.revert_to_checkpoint();
|
||||
let evm_err: evm::evm::Error = e.into();
|
||||
tracer.trace_failed_call(trace_info, vec![], evm_err.clone().into());
|
||||
Err(evm_err)
|
||||
} else {
|
||||
self.state.discard_checkpoint();
|
||||
|
||||
// trace only top level calls to builtins to avoid DDoS attacks
|
||||
@ -295,6 +300,7 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
|
||||
}
|
||||
|
||||
Ok(params.gas - cost)
|
||||
}
|
||||
} else {
|
||||
// just drain the whole gas
|
||||
self.state.revert_to_checkpoint();
|
||||
@ -497,6 +503,7 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
|
||||
| Err(evm::Error::BadJumpDestination {..})
|
||||
| Err(evm::Error::BadInstruction {.. })
|
||||
| Err(evm::Error::StackUnderflow {..})
|
||||
| Err(evm::Error::BuiltIn {..})
|
||||
| Err(evm::Error::OutOfStack {..}) => {
|
||||
self.state.revert_to_checkpoint();
|
||||
},
|
||||
|
@ -56,7 +56,7 @@
|
||||
//! export LIBRARY_PATH=/usr/local/lib
|
||||
//!
|
||||
//! # download and build parity
|
||||
//! git clone https://github.com/ethcore/parity
|
||||
//! git clone https://github.com/paritytech/parity
|
||||
//! cd parity
|
||||
//! multirust override beta
|
||||
//! cargo build --release
|
||||
@ -73,7 +73,7 @@
|
||||
//! export LIBRARY_PATH=/usr/local/lib
|
||||
//!
|
||||
//! # download and build parity
|
||||
//! git clone https://github.com/ethcore/parity
|
||||
//! git clone https://github.com/paritytech/parity
|
||||
//! cd parity
|
||||
//! multirust override beta
|
||||
//! cargo build --release
|
||||
@ -108,6 +108,8 @@ extern crate hardware_wallet;
|
||||
extern crate stats;
|
||||
extern crate ethcore_logger;
|
||||
extern crate num;
|
||||
extern crate bn;
|
||||
extern crate itertools;
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
@ -17,7 +17,7 @@
|
||||
//! Local Transactions List.
|
||||
|
||||
use linked_hash_map::LinkedHashMap;
|
||||
use transaction::SignedTransaction;
|
||||
use transaction::{SignedTransaction, PendingTransaction};
|
||||
use error::TransactionError;
|
||||
use util::{U256, H256};
|
||||
|
||||
@ -40,6 +40,8 @@ pub enum Status {
|
||||
Rejected(SignedTransaction, TransactionError),
|
||||
/// Transaction is invalid.
|
||||
Invalid(SignedTransaction),
|
||||
/// Transaction was canceled.
|
||||
Canceled(PendingTransaction),
|
||||
}
|
||||
|
||||
impl Status {
|
||||
@ -99,6 +101,12 @@ impl LocalTransactionsList {
|
||||
self.clear_old();
|
||||
}
|
||||
|
||||
pub fn mark_canceled(&mut self, tx: PendingTransaction) {
|
||||
warn!(target: "own_tx", "Transaction canceled (hash {:?})", tx.hash());
|
||||
self.transactions.insert(tx.hash(), Status::Canceled(tx));
|
||||
self.clear_old();
|
||||
}
|
||||
|
||||
pub fn mark_dropped(&mut self, tx: SignedTransaction) {
|
||||
warn!(target: "own_tx", "Transaction dropped (hash {:?})", tx.hash());
|
||||
self.transactions.insert(tx.hash(), Status::Dropped(tx));
|
||||
|
@ -29,7 +29,7 @@ use transaction::{Action, UnverifiedTransaction, PendingTransaction, SignedTrans
|
||||
use receipt::{Receipt, RichReceipt};
|
||||
use spec::Spec;
|
||||
use engines::{Engine, Seal};
|
||||
use miner::{MinerService, MinerStatus, TransactionQueue, TransactionQueueDetailsProvider, PrioritizationStrategy,
|
||||
use miner::{MinerService, MinerStatus, TransactionQueue, RemovalReason, TransactionQueueDetailsProvider, PrioritizationStrategy,
|
||||
AccountDetails, TransactionOrigin};
|
||||
use miner::banning_queue::{BanningTransactionQueue, Threshold};
|
||||
use miner::work_notify::{WorkPoster, NotifyWork};
|
||||
@ -430,7 +430,7 @@ impl Miner {
|
||||
{
|
||||
let mut queue = self.transaction_queue.write();
|
||||
for hash in invalid_transactions {
|
||||
queue.remove_invalid(&hash, &fetch_nonce);
|
||||
queue.remove(&hash, &fetch_nonce, RemovalReason::Invalid);
|
||||
}
|
||||
for hash in transactions_to_penalize {
|
||||
queue.penalize(&hash);
|
||||
@ -1021,7 +1021,7 @@ impl MinerService for Miner {
|
||||
let tx = queue.find(hash);
|
||||
if tx.is_some() {
|
||||
let fetch_nonce = |a: &Address| chain.latest_nonce(a);
|
||||
queue.remove_invalid(hash, &fetch_nonce);
|
||||
queue.remove(hash, &fetch_nonce, RemovalReason::Canceled);
|
||||
}
|
||||
tx
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ mod stratum;
|
||||
pub use self::external::{ExternalMiner, ExternalMinerService};
|
||||
|
||||
pub use self::miner::{Miner, MinerOptions, Banning, PendingSet, GasPricer, GasPriceCalibratorOptions, GasLimit};
|
||||
pub use self::transaction_queue::{TransactionQueue, TransactionDetailsProvider as TransactionQueueDetailsProvider,
|
||||
pub use self::transaction_queue::{TransactionQueue, RemovalReason, TransactionDetailsProvider as TransactionQueueDetailsProvider,
|
||||
PrioritizationStrategy, AccountDetails, TransactionOrigin};
|
||||
pub use self::local_transactions::{Status as LocalTransactionStatus};
|
||||
pub use client::TransactionImportResult;
|
||||
|
@ -31,7 +31,7 @@
|
||||
//!
|
||||
//! use util::{Uint, U256, Address};
|
||||
//! use ethkey::{Random, Generator};
|
||||
//! use ethcore::miner::{TransactionQueue, TransactionQueueDetailsProvider, AccountDetails, TransactionOrigin};
|
||||
//! use ethcore::miner::{TransactionQueue, RemovalReason, TransactionQueueDetailsProvider, AccountDetails, TransactionOrigin};
|
||||
//! use ethcore::transaction::*;
|
||||
//! use rustc_serialize::hex::FromHex;
|
||||
//!
|
||||
@ -80,7 +80,7 @@
|
||||
//!
|
||||
//! // And when transaction is removed (but nonce haven't changed)
|
||||
//! // it will move subsequent transactions to future
|
||||
//! txq.remove_invalid(&st1.hash(), &|_| 10.into());
|
||||
//! txq.remove(&st1.hash(), &|_| 10.into(), RemovalReason::Invalid);
|
||||
//! assert_eq!(txq.status().pending, 0);
|
||||
//! assert_eq!(txq.status().future, 1);
|
||||
//! assert_eq!(txq.top_transactions().len(), 0);
|
||||
@ -510,6 +510,15 @@ pub enum PrioritizationStrategy {
|
||||
GasFactorAndGasPrice,
|
||||
}
|
||||
|
||||
/// Reason to remove single transaction from the queue.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum RemovalReason {
|
||||
/// Transaction is invalid
|
||||
Invalid,
|
||||
/// Transaction was canceled
|
||||
Canceled,
|
||||
}
|
||||
|
||||
/// Point in time when transaction was inserted.
|
||||
pub type QueuingInstant = BlockNumber;
|
||||
const DEFAULT_QUEUING_PERIOD: BlockNumber = 128;
|
||||
@ -897,7 +906,7 @@ impl TransactionQueue {
|
||||
.expect("We fetch details for all senders from both current and future")
|
||||
.nonce;
|
||||
for hash in invalid {
|
||||
self.remove_invalid(&hash, &fetch_nonce);
|
||||
self.remove(&hash, &fetch_nonce, RemovalReason::Invalid);
|
||||
}
|
||||
}
|
||||
|
||||
@ -945,7 +954,7 @@ impl TransactionQueue {
|
||||
/// so transactions left in queue are processed according to client nonce.
|
||||
///
|
||||
/// If gap is introduced marks subsequent transactions as future
|
||||
pub fn remove_invalid<F>(&mut self, transaction_hash: &H256, fetch_nonce: &F)
|
||||
pub fn remove<F>(&mut self, transaction_hash: &H256, fetch_nonce: &F, reason: RemovalReason)
|
||||
where F: Fn(&Address) -> U256 {
|
||||
|
||||
assert_eq!(self.future.by_priority.len() + self.current.by_priority.len(), self.by_hash.len());
|
||||
@ -964,7 +973,14 @@ impl TransactionQueue {
|
||||
|
||||
// Mark in locals
|
||||
if self.local_transactions.contains(transaction_hash) {
|
||||
self.local_transactions.mark_invalid(transaction.transaction.into());
|
||||
match reason {
|
||||
RemovalReason::Invalid => self.local_transactions.mark_invalid(
|
||||
transaction.transaction.into()
|
||||
),
|
||||
RemovalReason::Canceled => self.local_transactions.mark_canceled(
|
||||
PendingTransaction::new(transaction.transaction, transaction.condition)
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
// Remove from future
|
||||
@ -2277,7 +2293,7 @@ pub mod test {
|
||||
assert_eq!(txq.status().pending, 3);
|
||||
|
||||
// when
|
||||
txq.remove_invalid(&tx.hash(), &|_| default_nonce());
|
||||
txq.remove(&tx.hash(), &|_| default_nonce(), RemovalReason::Invalid);
|
||||
|
||||
// then
|
||||
let stats = txq.status();
|
||||
@ -2420,7 +2436,7 @@ pub mod test {
|
||||
assert_eq!(txq.status().pending, 2);
|
||||
|
||||
// when
|
||||
txq.remove_invalid(&tx1.hash(), &|_| default_nonce());
|
||||
txq.remove(&tx1.hash(), &|_| default_nonce(), RemovalReason::Invalid);
|
||||
assert_eq!(txq.status().pending, 0);
|
||||
assert_eq!(txq.status().future, 1);
|
||||
txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||
@ -2518,7 +2534,7 @@ pub mod test {
|
||||
assert_eq!(txq.status().future, 2);
|
||||
|
||||
// when
|
||||
txq.remove_invalid(&tx1.hash(), &|_| default_nonce() + 1.into());
|
||||
txq.remove(&tx1.hash(), &|_| default_nonce() + 1.into(), RemovalReason::Invalid);
|
||||
|
||||
// then
|
||||
let stats = txq.status();
|
||||
|
@ -23,6 +23,7 @@ use snapshot::Error;
|
||||
use util::{U256, H256, Bytes, HashDB, SHA3_EMPTY, SHA3_NULL_RLP};
|
||||
use util::trie::{TrieDB, Trie};
|
||||
use rlp::{RlpStream, UntrustedRlp};
|
||||
use itertools::Itertools;
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
@ -60,25 +61,23 @@ impl CodeState {
|
||||
}
|
||||
}
|
||||
|
||||
// walk the account's storage trie, returning an RLP item containing the
|
||||
// account properties and the storage.
|
||||
pub fn to_fat_rlp(acc: &BasicAccount, acct_db: &AccountDB, used_code: &mut HashSet<H256>) -> Result<Bytes, Error> {
|
||||
// walk the account's storage trie, returning a vector of RLP items containing the
|
||||
// account properties and the storage. Each item contains at most `max_storage_items`
|
||||
// storage records split according to snapshot format definition.
|
||||
pub fn to_fat_rlps(acc: &BasicAccount, acct_db: &AccountDB, used_code: &mut HashSet<H256>, max_storage_items: usize) -> Result<Vec<Bytes>, Error> {
|
||||
if acc == &ACC_EMPTY {
|
||||
return Ok(::rlp::NULL_RLP.to_vec());
|
||||
return Ok(vec![::rlp::NULL_RLP.to_vec()]);
|
||||
}
|
||||
|
||||
let db = TrieDB::new(acct_db, &acc.storage_root)?;
|
||||
|
||||
let mut pairs = Vec::new();
|
||||
|
||||
for item in db.iter()? {
|
||||
let (k, v) = item?;
|
||||
pairs.push((k, v));
|
||||
}
|
||||
|
||||
let chunks = db.iter()?.chunks(max_storage_items);
|
||||
let pair_chunks = chunks.into_iter().map(|chunk| chunk.collect());
|
||||
pair_chunks.pad_using(1, |_| Vec::new(), ).map(|pairs| {
|
||||
let mut stream = RlpStream::new_list(pairs.len());
|
||||
|
||||
for (k, v) in pairs {
|
||||
for r in pairs {
|
||||
let (k, v) = r?;
|
||||
stream.begin_list(2).append(&k).append(&&*v);
|
||||
}
|
||||
|
||||
@ -107,8 +106,8 @@ pub fn to_fat_rlp(acc: &BasicAccount, acct_db: &AccountDB, used_code: &mut HashS
|
||||
}
|
||||
|
||||
account_stream.append_raw(&pairs_rlp, 1);
|
||||
|
||||
Ok(account_stream.out())
|
||||
}).collect()
|
||||
}
|
||||
|
||||
// decode a fat rlp, and rebuild the storage trie as we go.
|
||||
@ -117,6 +116,7 @@ pub fn to_fat_rlp(acc: &BasicAccount, acct_db: &AccountDB, used_code: &mut HashS
|
||||
pub fn from_fat_rlp(
|
||||
acct_db: &mut AccountDBMut,
|
||||
rlp: UntrustedRlp,
|
||||
mut storage_root: H256,
|
||||
) -> Result<(BasicAccount, Option<Bytes>), Error> {
|
||||
use util::{TrieDBMut, TrieMut};
|
||||
|
||||
@ -148,10 +148,12 @@ pub fn from_fat_rlp(
|
||||
}
|
||||
};
|
||||
|
||||
let mut storage_root = H256::zero();
|
||||
|
||||
{
|
||||
let mut storage_trie = TrieDBMut::new(acct_db, &mut storage_root);
|
||||
let mut storage_trie = if storage_root.is_zero() {
|
||||
TrieDBMut::new(acct_db, &mut storage_root)
|
||||
} else {
|
||||
TrieDBMut::from_existing(acct_db, &mut storage_root)?
|
||||
};
|
||||
let pairs = rlp.at(4)?;
|
||||
for pair_rlp in pairs.iter() {
|
||||
let k: Bytes = pair_rlp.val_at(0)?;
|
||||
@ -184,7 +186,7 @@ mod tests {
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
use super::{ACC_EMPTY, to_fat_rlp, from_fat_rlp};
|
||||
use super::{ACC_EMPTY, to_fat_rlps, from_fat_rlp};
|
||||
|
||||
#[test]
|
||||
fn encoding_basic() {
|
||||
@ -201,9 +203,9 @@ mod tests {
|
||||
let thin_rlp = ::rlp::encode(&account);
|
||||
assert_eq!(::rlp::decode::<BasicAccount>(&thin_rlp), account);
|
||||
|
||||
let fat_rlp = to_fat_rlp(&account, &AccountDB::new(db.as_hashdb(), &addr), &mut Default::default()).unwrap();
|
||||
let fat_rlp = UntrustedRlp::new(&fat_rlp);
|
||||
assert_eq!(from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr), fat_rlp).unwrap().0, account);
|
||||
let fat_rlps = to_fat_rlps(&account, &AccountDB::new(db.as_hashdb(), &addr), &mut Default::default(), usize::max_value()).unwrap();
|
||||
let fat_rlp = UntrustedRlp::new(&fat_rlps[0]);
|
||||
assert_eq!(from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr), fat_rlp, H256::zero()).unwrap().0, account);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -226,9 +228,40 @@ mod tests {
|
||||
let thin_rlp = ::rlp::encode(&account);
|
||||
assert_eq!(::rlp::decode::<BasicAccount>(&thin_rlp), account);
|
||||
|
||||
let fat_rlp = to_fat_rlp(&account, &AccountDB::new(db.as_hashdb(), &addr), &mut Default::default()).unwrap();
|
||||
let fat_rlp = UntrustedRlp::new(&fat_rlp);
|
||||
assert_eq!(from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr), fat_rlp).unwrap().0, account);
|
||||
let fat_rlp = to_fat_rlps(&account, &AccountDB::new(db.as_hashdb(), &addr), &mut Default::default(), usize::max_value()).unwrap();
|
||||
let fat_rlp = UntrustedRlp::new(&fat_rlp[0]);
|
||||
assert_eq!(from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr), fat_rlp, H256::zero()).unwrap().0, account);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encoding_storage_split() {
|
||||
let mut db = get_temp_state_db();
|
||||
let addr = Address::random();
|
||||
|
||||
let account = {
|
||||
let acct_db = AccountDBMut::new(db.as_hashdb_mut(), &addr);
|
||||
let mut root = SHA3_NULL_RLP;
|
||||
fill_storage(acct_db, &mut root, &mut H256::zero());
|
||||
BasicAccount {
|
||||
nonce: 25.into(),
|
||||
balance: 987654321.into(),
|
||||
storage_root: root,
|
||||
code_hash: SHA3_EMPTY,
|
||||
}
|
||||
};
|
||||
|
||||
let thin_rlp = ::rlp::encode(&account);
|
||||
assert_eq!(::rlp::decode::<BasicAccount>(&thin_rlp), account);
|
||||
|
||||
let fat_rlps = to_fat_rlps(&account, &AccountDB::new(db.as_hashdb(), &addr), &mut Default::default(), 100).unwrap();
|
||||
let mut root = SHA3_NULL_RLP;
|
||||
let mut restored_account = None;
|
||||
for rlp in fat_rlps {
|
||||
let fat_rlp = UntrustedRlp::new(&rlp);
|
||||
restored_account = Some(from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr), fat_rlp, root).unwrap().0);
|
||||
root = restored_account.as_ref().unwrap().storage_root.clone();
|
||||
}
|
||||
assert_eq!(restored_account, Some(account));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -264,18 +297,18 @@ mod tests {
|
||||
|
||||
let mut used_code = HashSet::new();
|
||||
|
||||
let fat_rlp1 = to_fat_rlp(&account1, &AccountDB::new(db.as_hashdb(), &addr1), &mut used_code).unwrap();
|
||||
let fat_rlp2 = to_fat_rlp(&account2, &AccountDB::new(db.as_hashdb(), &addr2), &mut used_code).unwrap();
|
||||
let fat_rlp1 = to_fat_rlps(&account1, &AccountDB::new(db.as_hashdb(), &addr1), &mut used_code, usize::max_value()).unwrap();
|
||||
let fat_rlp2 = to_fat_rlps(&account2, &AccountDB::new(db.as_hashdb(), &addr2), &mut used_code, usize::max_value()).unwrap();
|
||||
assert_eq!(used_code.len(), 1);
|
||||
|
||||
let fat_rlp1 = UntrustedRlp::new(&fat_rlp1);
|
||||
let fat_rlp2 = UntrustedRlp::new(&fat_rlp2);
|
||||
let fat_rlp1 = UntrustedRlp::new(&fat_rlp1[0]);
|
||||
let fat_rlp2 = UntrustedRlp::new(&fat_rlp2[0]);
|
||||
|
||||
let (acc, maybe_code) = from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr2), fat_rlp2).unwrap();
|
||||
let (acc, maybe_code) = from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr2), fat_rlp2, H256::zero()).unwrap();
|
||||
assert!(maybe_code.is_none());
|
||||
assert_eq!(acc, account2);
|
||||
|
||||
let (acc, maybe_code) = from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr1), fat_rlp1).unwrap();
|
||||
let (acc, maybe_code) = from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr1), fat_rlp1, H256::zero()).unwrap();
|
||||
assert_eq!(maybe_code, Some(b"this is definitely code".to_vec()));
|
||||
assert_eq!(acc, account1);
|
||||
}
|
||||
@ -285,7 +318,7 @@ mod tests {
|
||||
let mut db = get_temp_state_db();
|
||||
let mut used_code = HashSet::new();
|
||||
|
||||
assert_eq!(to_fat_rlp(&ACC_EMPTY, &AccountDB::new(db.as_hashdb(), &Address::default()), &mut used_code).unwrap(), ::rlp::NULL_RLP.to_vec());
|
||||
assert_eq!(from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &Address::default()), UntrustedRlp::new(&::rlp::NULL_RLP)).unwrap(), (ACC_EMPTY, None));
|
||||
assert_eq!(to_fat_rlps(&ACC_EMPTY, &AccountDB::new(db.as_hashdb(), &Address::default()), &mut used_code, usize::max_value()).unwrap(), vec![::rlp::NULL_RLP.to_vec()]);
|
||||
assert_eq!(from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &Address::default()), UntrustedRlp::new(&::rlp::NULL_RLP), H256::zero()).unwrap(), (ACC_EMPTY, None));
|
||||
}
|
||||
}
|
||||
|
@ -53,6 +53,8 @@ pub enum Error {
|
||||
Decoder(DecoderError),
|
||||
/// Io error.
|
||||
Io(::std::io::Error),
|
||||
/// Snapshot version is not supported.
|
||||
VersionNotSupported(u64),
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
@ -73,6 +75,7 @@ impl fmt::Display for Error {
|
||||
Error::Io(ref err) => err.fmt(f),
|
||||
Error::Decoder(ref err) => err.fmt(f),
|
||||
Error::Trie(ref err) => err.fmt(f),
|
||||
Error::VersionNotSupported(ref ver) => write!(f, "Snapshot version {} is not supprted.", ver),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +31,8 @@ use rlp::{self, Encodable, RlpStream, UntrustedRlp};
|
||||
|
||||
use super::ManifestData;
|
||||
|
||||
const SNAPSHOT_VERSION: u64 = 2;
|
||||
|
||||
/// Something which can write snapshots.
|
||||
/// Writing the same chunk multiple times will lead to implementation-defined
|
||||
/// behavior, and is not advised.
|
||||
@ -118,8 +120,9 @@ impl SnapshotWriter for PackedWriter {
|
||||
fn finish(mut self, manifest: ManifestData) -> io::Result<()> {
|
||||
// we ignore the hashes fields of the manifest under the assumption that
|
||||
// they are consistent with ours.
|
||||
let mut stream = RlpStream::new_list(5);
|
||||
let mut stream = RlpStream::new_list(6);
|
||||
stream
|
||||
.append(&SNAPSHOT_VERSION)
|
||||
.append_list(&self.state_hashes)
|
||||
.append_list(&self.block_hashes)
|
||||
.append(&manifest.state_root)
|
||||
@ -221,7 +224,7 @@ impl PackedReader {
|
||||
/// Create a new `PackedReader` for the file at the given path.
|
||||
/// This will fail if any io errors are encountered or the file
|
||||
/// is not a valid packed snapshot.
|
||||
pub fn new(path: &Path) -> Result<Option<Self>, ::error::Error> {
|
||||
pub fn new(path: &Path) -> Result<Option<Self>, ::snapshot::error::Error> {
|
||||
let mut file = File::open(path)?;
|
||||
let file_len = file.metadata()?.len();
|
||||
if file_len < 8 {
|
||||
@ -255,15 +258,26 @@ impl PackedReader {
|
||||
|
||||
let rlp = UntrustedRlp::new(&manifest_buf);
|
||||
|
||||
let state: Vec<ChunkInfo> = rlp.list_at(0)?;
|
||||
let blocks: Vec<ChunkInfo> = rlp.list_at(1)?;
|
||||
let (start, version) = if rlp.item_count()? == 5 {
|
||||
(0, 1)
|
||||
} else {
|
||||
(1, rlp.val_at(0)?)
|
||||
};
|
||||
|
||||
if version > SNAPSHOT_VERSION {
|
||||
return Err(::snapshot::error::Error::VersionNotSupported(version));
|
||||
}
|
||||
|
||||
let state: Vec<ChunkInfo> = rlp.list_at(0 + start)?;
|
||||
let blocks: Vec<ChunkInfo> = rlp.list_at(1 + start)?;
|
||||
|
||||
let manifest = ManifestData {
|
||||
version: version,
|
||||
state_hashes: state.iter().map(|c| c.0).collect(),
|
||||
block_hashes: blocks.iter().map(|c| c.0).collect(),
|
||||
state_root: rlp.val_at(2)?,
|
||||
block_number: rlp.val_at(3)?,
|
||||
block_hash: rlp.val_at(4)?,
|
||||
state_root: rlp.val_at(2 + start)?,
|
||||
block_number: rlp.val_at(3 + start)?,
|
||||
block_hash: rlp.val_at(4 + start)?,
|
||||
};
|
||||
|
||||
Ok(Some(PackedReader {
|
||||
@ -346,7 +360,7 @@ mod tests {
|
||||
use util::sha3::Hashable;
|
||||
|
||||
use snapshot::ManifestData;
|
||||
use super::{SnapshotWriter, SnapshotReader, PackedWriter, PackedReader, LooseWriter, LooseReader};
|
||||
use super::{SnapshotWriter, SnapshotReader, PackedWriter, PackedReader, LooseWriter, LooseReader, SNAPSHOT_VERSION};
|
||||
|
||||
const STATE_CHUNKS: &'static [&'static [u8]] = &[b"dog", b"cat", b"hello world", b"hi", b"notarealchunk"];
|
||||
const BLOCK_CHUNKS: &'static [&'static [u8]] = &[b"hello!", b"goodbye!", b"abcdefg", b"hijklmnop", b"qrstuvwxy", b"and", b"z"];
|
||||
@ -372,6 +386,7 @@ mod tests {
|
||||
}
|
||||
|
||||
let manifest = ManifestData {
|
||||
version: SNAPSHOT_VERSION,
|
||||
state_hashes: state_hashes,
|
||||
block_hashes: block_hashes,
|
||||
state_root: b"notarealroot".sha3(),
|
||||
@ -410,6 +425,7 @@ mod tests {
|
||||
}
|
||||
|
||||
let manifest = ManifestData {
|
||||
version: SNAPSHOT_VERSION,
|
||||
state_hashes: state_hashes,
|
||||
block_hashes: block_hashes,
|
||||
state_root: b"notarealroot".sha3(),
|
||||
|
@ -17,7 +17,7 @@
|
||||
//! Snapshot creation, restoration, and network service.
|
||||
//!
|
||||
//! Documentation of the format can be found at
|
||||
//! https://github.com/ethcore/parity/wiki/%22PV64%22-Snapshot-Format
|
||||
//! https://github.com/paritytech/parity/wiki/%22PV64%22-Snapshot-Format
|
||||
|
||||
use std::collections::{HashMap, HashSet, VecDeque};
|
||||
use std::sync::Arc;
|
||||
@ -56,6 +56,7 @@ pub use self::traits::SnapshotService;
|
||||
pub use self::watcher::Watcher;
|
||||
pub use types::snapshot_manifest::ManifestData;
|
||||
pub use types::restoration_status::RestorationStatus;
|
||||
pub use types::basic_account::BasicAccount;
|
||||
|
||||
pub mod io;
|
||||
pub mod service;
|
||||
@ -82,6 +83,9 @@ mod traits {
|
||||
// Try to have chunks be around 4MB (before compression)
|
||||
const PREFERRED_CHUNK_SIZE: usize = 4 * 1024 * 1024;
|
||||
|
||||
// Try to have chunks be around 4MB (before compression)
|
||||
const MAX_STORAGE_ENTRIES_PER_ACCOUNT_RECORD: usize = 80_000;
|
||||
|
||||
// How many blocks to include in a snapshot, starting from the head of the chain.
|
||||
const SNAPSHOT_BLOCKS: u64 = 30000;
|
||||
|
||||
@ -147,6 +151,7 @@ pub fn take_snapshot<W: SnapshotWriter + Send>(
|
||||
info!("produced {} state chunks and {} block chunks.", state_hashes.len(), block_hashes.len());
|
||||
|
||||
let manifest_data = ManifestData {
|
||||
version: 2,
|
||||
state_hashes: state_hashes,
|
||||
block_hashes: block_hashes,
|
||||
state_root: *state_root,
|
||||
@ -300,14 +305,14 @@ impl<'a> StateChunker<'a> {
|
||||
//
|
||||
// If the buffer is greater than the desired chunk size,
|
||||
// this will write out the data to disk.
|
||||
fn push(&mut self, account_hash: Bytes, data: Bytes) -> Result<(), Error> {
|
||||
fn push(&mut self, account_hash: Bytes, data: Bytes, force_chunk: bool) -> Result<(), Error> {
|
||||
let pair = {
|
||||
let mut stream = RlpStream::new_list(2);
|
||||
stream.append(&account_hash).append_raw(&data, 1);
|
||||
stream.out()
|
||||
};
|
||||
|
||||
if self.cur_size + pair.len() >= PREFERRED_CHUNK_SIZE {
|
||||
if force_chunk || self.cur_size + pair.len() >= PREFERRED_CHUNK_SIZE {
|
||||
self.write_chunk()?;
|
||||
}
|
||||
|
||||
@ -372,8 +377,10 @@ pub fn chunk_state<'a>(db: &HashDB, root: &H256, writer: &Mutex<SnapshotWriter +
|
||||
|
||||
let account_db = AccountDB::from_hash(db, account_key_hash);
|
||||
|
||||
let fat_rlp = account::to_fat_rlp(&account, &account_db, &mut used_code)?;
|
||||
chunker.push(account_key, fat_rlp)?;
|
||||
let fat_rlps = account::to_fat_rlps(&account, &account_db, &mut used_code, MAX_STORAGE_ENTRIES_PER_ACCOUNT_RECORD)?;
|
||||
for (i, fat_rlp) in fat_rlps.into_iter().enumerate() {
|
||||
chunker.push(account_key.clone(), fat_rlp, i > 0)?;
|
||||
}
|
||||
}
|
||||
|
||||
if chunker.cur_size != 0 {
|
||||
@ -390,6 +397,7 @@ pub struct StateRebuilder {
|
||||
known_code: HashMap<H256, H256>, // code hashes mapped to first account with this code.
|
||||
missing_code: HashMap<H256, Vec<H256>>, // maps code hashes to lists of accounts missing that code.
|
||||
bloom: Bloom,
|
||||
known_storage_roots: HashMap<H256, H256>, // maps account hashes to last known storage root. Only filled for last account per chunk.
|
||||
}
|
||||
|
||||
impl StateRebuilder {
|
||||
@ -401,6 +409,7 @@ impl StateRebuilder {
|
||||
known_code: HashMap::new(),
|
||||
missing_code: HashMap::new(),
|
||||
bloom: StateDB::load_bloom(&*db),
|
||||
known_storage_roots: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -418,6 +427,7 @@ impl StateRebuilder {
|
||||
rlp,
|
||||
&mut pairs,
|
||||
&self.known_code,
|
||||
&mut self.known_storage_roots,
|
||||
flag
|
||||
)?;
|
||||
|
||||
@ -464,14 +474,18 @@ impl StateRebuilder {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check for accounts missing code. Once all chunks have been fed, there should
|
||||
/// be none.
|
||||
pub fn check_missing(self) -> Result<(), Error> {
|
||||
/// Finalize the restoration. Check for accounts missing code and make a dummy
|
||||
/// journal entry.
|
||||
/// Once all chunks have been fed, there should be nothing missing.
|
||||
pub fn finalize(mut self, era: u64, id: H256) -> Result<(), ::error::Error> {
|
||||
let missing = self.missing_code.keys().cloned().collect::<Vec<_>>();
|
||||
match missing.is_empty() {
|
||||
true => Ok(()),
|
||||
false => Err(Error::MissingCode(missing)),
|
||||
}
|
||||
if !missing.is_empty() { return Err(Error::MissingCode(missing).into()) }
|
||||
|
||||
let mut batch = self.db.backing().transaction();
|
||||
self.db.journal_under(&mut batch, era, &id)?;
|
||||
self.db.backing().write_buffered(batch);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the state root of the rebuilder.
|
||||
@ -492,10 +506,11 @@ fn rebuild_accounts(
|
||||
account_fat_rlps: UntrustedRlp,
|
||||
out_chunk: &mut [(H256, Bytes)],
|
||||
known_code: &HashMap<H256, H256>,
|
||||
known_storage_roots: &mut HashMap<H256, H256>,
|
||||
abort_flag: &AtomicBool,
|
||||
) -> Result<RebuiltStatus, ::error::Error> {
|
||||
let mut status = RebuiltStatus::default();
|
||||
for (account_rlp, out) in account_fat_rlps.into_iter().zip(out_chunk) {
|
||||
for (account_rlp, out) in account_fat_rlps.into_iter().zip(out_chunk.iter_mut()) {
|
||||
if !abort_flag.load(Ordering::SeqCst) { return Err(Error::RestorationAborted.into()) }
|
||||
|
||||
let hash: H256 = account_rlp.val_at(0)?;
|
||||
@ -506,7 +521,8 @@ fn rebuild_accounts(
|
||||
// fill out the storage trie and code while decoding.
|
||||
let (acc, maybe_code) = {
|
||||
let mut acct_db = AccountDBMut::from_hash(db, hash);
|
||||
account::from_fat_rlp(&mut acct_db, fat_rlp)?
|
||||
let storage_root = known_storage_roots.get(&hash).cloned().unwrap_or(H256::zero());
|
||||
account::from_fat_rlp(&mut acct_db, fat_rlp, storage_root)?
|
||||
};
|
||||
|
||||
let code_hash = acc.code_hash.clone();
|
||||
@ -538,6 +554,12 @@ fn rebuild_accounts(
|
||||
|
||||
*out = (hash, thin_rlp);
|
||||
}
|
||||
if let Some(&(ref hash, ref rlp)) = out_chunk.iter().last() {
|
||||
known_storage_roots.insert(*hash, ::rlp::decode::<BasicAccount>(rlp).storage_root);
|
||||
}
|
||||
if let Some(&(ref hash, ref rlp)) = out_chunk.iter().next() {
|
||||
known_storage_roots.insert(*hash, ::rlp::decode::<BasicAccount>(rlp).storage_root);
|
||||
}
|
||||
Ok(status)
|
||||
}
|
||||
|
||||
|
@ -166,7 +166,7 @@ impl Restoration {
|
||||
}
|
||||
|
||||
// check for missing code.
|
||||
self.state.check_missing()?;
|
||||
self.state.finalize(self.manifest.block_number, self.manifest.block_hash)?;
|
||||
|
||||
// connect out-of-order chunks and verify chain integrity.
|
||||
self.blocks.finalize(self.canonical_hashes)?;
|
||||
@ -656,6 +656,7 @@ mod tests {
|
||||
assert_eq!(service.status(), RestorationStatus::Inactive);
|
||||
|
||||
let manifest = ManifestData {
|
||||
version: 2,
|
||||
state_hashes: vec![],
|
||||
block_hashes: vec![],
|
||||
state_root: Default::default(),
|
||||
|
@ -63,6 +63,7 @@ fn chunk_and_restore(amount: u64) {
|
||||
let writer = Mutex::new(PackedWriter::new(&snapshot_path).unwrap());
|
||||
let block_hashes = chunk_blocks(&bc, best_hash, &writer, &Progress::default()).unwrap();
|
||||
let manifest = ::snapshot::ManifestData {
|
||||
version: 2,
|
||||
state_hashes: Vec::new(),
|
||||
block_hashes: block_hashes,
|
||||
state_root: ::util::sha3::SHA3_NULL_RLP,
|
||||
@ -125,6 +126,7 @@ fn checks_flag() {
|
||||
let chain = BlockChain::new(Default::default(), &genesis, db.clone());
|
||||
|
||||
let manifest = ::snapshot::ManifestData {
|
||||
version: 2,
|
||||
state_hashes: Vec::new(),
|
||||
block_hashes: Vec::new(),
|
||||
state_root: ::util::sha3::SHA3_NULL_RLP,
|
||||
|
@ -27,6 +27,7 @@ use super::ManifestData;
|
||||
#[test]
|
||||
fn manifest_rlp() {
|
||||
let manifest = ManifestData {
|
||||
version: 2,
|
||||
block_hashes: Vec::new(),
|
||||
state_hashes: Vec::new(),
|
||||
block_number: 1234567,
|
||||
|
@ -122,6 +122,7 @@ fn guards_delete_folders() {
|
||||
path.push("restoration");
|
||||
|
||||
let manifest = ManifestData {
|
||||
version: 2,
|
||||
state_hashes: vec![],
|
||||
block_hashes: vec![],
|
||||
block_number: 0,
|
||||
|
@ -58,10 +58,11 @@ fn snap_and_restore() {
|
||||
let state_hashes = chunk_state(&old_db, &state_root, &writer, &Progress::default()).unwrap();
|
||||
|
||||
writer.into_inner().finish(::snapshot::ManifestData {
|
||||
version: 2,
|
||||
state_hashes: state_hashes,
|
||||
block_hashes: Vec::new(),
|
||||
state_root: state_root,
|
||||
block_number: 0,
|
||||
block_number: 1000,
|
||||
block_hash: H256::default(),
|
||||
}).unwrap();
|
||||
|
||||
@ -69,7 +70,7 @@ fn snap_and_restore() {
|
||||
db_path.push("db");
|
||||
let db = {
|
||||
let new_db = Arc::new(Database::open(&db_cfg, &db_path.to_string_lossy()).unwrap());
|
||||
let mut rebuilder = StateRebuilder::new(new_db.clone(), Algorithm::Archive);
|
||||
let mut rebuilder = StateRebuilder::new(new_db.clone(), Algorithm::OverlayRecent);
|
||||
let reader = PackedReader::new(&snap_file).unwrap().unwrap();
|
||||
|
||||
let flag = AtomicBool::new(true);
|
||||
@ -82,12 +83,13 @@ fn snap_and_restore() {
|
||||
}
|
||||
|
||||
assert_eq!(rebuilder.state_root(), state_root);
|
||||
rebuilder.check_missing().unwrap();
|
||||
rebuilder.finalize(1000, H256::default()).unwrap();
|
||||
|
||||
new_db
|
||||
};
|
||||
|
||||
let new_db = journaldb::new(db, Algorithm::Archive, ::db::COL_STATE);
|
||||
let new_db = journaldb::new(db, Algorithm::OverlayRecent, ::db::COL_STATE);
|
||||
assert_eq!(new_db.earliest_era(), Some(1000));
|
||||
|
||||
compare_dbs(&old_db, new_db.as_hashdb());
|
||||
}
|
||||
@ -120,10 +122,10 @@ fn get_code_from_prev_chunk() {
|
||||
let mut db = MemoryDB::new();
|
||||
AccountDBMut::from_hash(&mut db, hash).insert(&code[..]);
|
||||
|
||||
let fat_rlp = account::to_fat_rlp(&acc, &AccountDB::from_hash(&db, hash), &mut used_code).unwrap();
|
||||
let fat_rlp = account::to_fat_rlps(&acc, &AccountDB::from_hash(&db, hash), &mut used_code, usize::max_value()).unwrap();
|
||||
|
||||
let mut stream = RlpStream::new_list(1);
|
||||
stream.begin_list(2).append(&hash).append_raw(&fat_rlp, 1);
|
||||
stream.begin_list(2).append(&hash).append_raw(&fat_rlp[0], 1);
|
||||
stream.out()
|
||||
};
|
||||
|
||||
@ -134,13 +136,18 @@ fn get_code_from_prev_chunk() {
|
||||
let db_cfg = DatabaseConfig::with_columns(::db::NUM_COLUMNS);
|
||||
let new_db = Arc::new(Database::open(&db_cfg, &db_path.to_string_lossy()).unwrap());
|
||||
|
||||
let mut rebuilder = StateRebuilder::new(new_db, Algorithm::Archive);
|
||||
{
|
||||
let mut rebuilder = StateRebuilder::new(new_db.clone(), Algorithm::OverlayRecent);
|
||||
let flag = AtomicBool::new(true);
|
||||
|
||||
rebuilder.feed(&chunk1, &flag).unwrap();
|
||||
rebuilder.feed(&chunk2, &flag).unwrap();
|
||||
|
||||
rebuilder.check_missing().unwrap();
|
||||
rebuilder.finalize(1000, H256::random()).unwrap();
|
||||
}
|
||||
|
||||
let state_db = journaldb::new(new_db, Algorithm::OverlayRecent, ::db::COL_STATE);
|
||||
assert_eq!(state_db.earliest_era(), Some(1000));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -164,6 +171,7 @@ fn checks_flag() {
|
||||
let state_hashes = chunk_state(&old_db, &state_root, &writer, &Progress::default()).unwrap();
|
||||
|
||||
writer.into_inner().finish(::snapshot::ManifestData {
|
||||
version: 2,
|
||||
state_hashes: state_hashes,
|
||||
block_hashes: Vec::new(),
|
||||
state_root: state_root,
|
||||
@ -175,7 +183,7 @@ fn checks_flag() {
|
||||
db_path.push("db");
|
||||
{
|
||||
let new_db = Arc::new(Database::open(&db_cfg, &db_path.to_string_lossy()).unwrap());
|
||||
let mut rebuilder = StateRebuilder::new(new_db.clone(), Algorithm::Archive);
|
||||
let mut rebuilder = StateRebuilder::new(new_db.clone(), Algorithm::OverlayRecent);
|
||||
let reader = PackedReader::new(&snap_file).unwrap().unwrap();
|
||||
|
||||
let flag = AtomicBool::new(false);
|
||||
|
@ -56,7 +56,7 @@ pub struct CommonParams {
|
||||
/// Number of first block where EIP-98 rules begin.
|
||||
pub eip98_transition: BlockNumber,
|
||||
/// Validate block receipts root.
|
||||
pub validate_receipts: bool,
|
||||
pub validate_receipts_transition: u64,
|
||||
}
|
||||
|
||||
impl From<ethjson::spec::Params> for CommonParams {
|
||||
@ -70,7 +70,7 @@ impl From<ethjson::spec::Params> for CommonParams {
|
||||
min_gas_limit: p.min_gas_limit.into(),
|
||||
fork_block: if let (Some(n), Some(h)) = (p.fork_block, p.fork_hash) { Some((n.into(), h.into())) } else { None },
|
||||
eip98_transition: p.eip98_transition.map_or(0, Into::into),
|
||||
validate_receipts: p.validate_receipts.unwrap_or(true),
|
||||
validate_receipts_transition: p.validate_receipts_transition.map_or(0, Into::into),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -350,7 +350,7 @@ impl Spec {
|
||||
/// Account "0".sha3() and "1".sha3() are a authorities.
|
||||
pub fn new_test_tendermint() -> Self { load_bundled!("tendermint") }
|
||||
|
||||
/// TestList.sol used in both specs: https://github.com/ethcore/contracts/pull/30/files
|
||||
/// TestList.sol used in both specs: https://github.com/paritytech/contracts/pull/30/files
|
||||
/// Accounts with secrets "0".sha3() and "1".sha3() are initially the validators.
|
||||
/// Create a new Spec with BasicAuthority which uses a contract at address 5 to determine the current validators using `getValidators`.
|
||||
/// Second validator can be removed with "0xbfc708a000000000000000000000000082a978b3f5962a5b0957d9ee9eef472ee55b42f1" and added back in using "0x4d238c8e00000000000000000000000082a978b3f5962a5b0957d9ee9eef472ee55b42f1".
|
||||
@ -374,7 +374,7 @@ mod tests {
|
||||
use state::State;
|
||||
use super::*;
|
||||
|
||||
// https://github.com/ethcore/parity/issues/1840
|
||||
// https://github.com/paritytech/parity/issues/1840
|
||||
#[test]
|
||||
fn test_load_empty() {
|
||||
assert!(Spec::load(&[] as &[u8]).is_err());
|
||||
|
@ -601,7 +601,7 @@ impl<B: Backend> State<B> {
|
||||
|
||||
let e = self.execute(env_info, engine, t, tracing)?;
|
||||
// trace!("Applied transaction. Diff:\n{}\n", state_diff::diff_pod(&old, &self.to_pod()));
|
||||
let state_root = if env_info.number < engine.params().eip98_transition {
|
||||
let state_root = if env_info.number < engine.params().eip98_transition || env_info.number < engine.params().validate_receipts_transition {
|
||||
self.commit()?;
|
||||
Some(self.root().clone())
|
||||
} else {
|
||||
|
@ -438,6 +438,7 @@ pub fn get_default_ethash_params() -> EthashParams{
|
||||
minimum_difficulty: U256::from(131072),
|
||||
difficulty_bound_divisor: U256::from(2048),
|
||||
difficulty_increment_divisor: 10,
|
||||
metropolis_difficulty_increment_divisor: 9,
|
||||
duration_limit: 13,
|
||||
block_reward: U256::from(0),
|
||||
registrar: "0000000000000000000000000000000000000001".into(),
|
||||
@ -448,6 +449,7 @@ pub fn get_default_ethash_params() -> EthashParams{
|
||||
difficulty_hardfork_transition: u64::max_value(),
|
||||
difficulty_hardfork_bound_divisor: U256::from(0),
|
||||
bomb_defuse_transition: u64::max_value(),
|
||||
eip100b_transition: u64::max_value(),
|
||||
eip150_transition: u64::max_value(),
|
||||
eip155_transition: u64::max_value(),
|
||||
eip160_transition: u64::max_value(),
|
||||
|
@ -49,7 +49,7 @@ pub struct BlockChainInfo {
|
||||
impl BlockChainInfo {
|
||||
/// Determine the security model for the current state.
|
||||
pub fn security_level(&self) -> SecurityLevel {
|
||||
// TODO: Detect SecurityLevel::FullState : https://github.com/ethcore/parity/issues/3834
|
||||
// TODO: Detect SecurityLevel::FullState : https://github.com/paritytech/parity/issues/3834
|
||||
if self.ancient_block_number.is_none() || self.first_block_number.is_none() {
|
||||
SecurityLevel::FullProofOfWork
|
||||
} else {
|
||||
|
@ -24,6 +24,8 @@ use util::Bytes;
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "ipc", binary)]
|
||||
pub struct ManifestData {
|
||||
/// Snapshot format version.
|
||||
pub version: u64,
|
||||
/// List of state chunk hashes.
|
||||
pub state_hashes: Vec<H256>,
|
||||
/// List of block chunk hashes.
|
||||
@ -39,7 +41,8 @@ pub struct ManifestData {
|
||||
impl ManifestData {
|
||||
/// Encode the manifest data to rlp.
|
||||
pub fn into_rlp(self) -> Bytes {
|
||||
let mut stream = RlpStream::new_list(5);
|
||||
let mut stream = RlpStream::new_list(6);
|
||||
stream.append(&self.version);
|
||||
stream.append_list(&self.state_hashes);
|
||||
stream.append_list(&self.block_hashes);
|
||||
stream.append(&self.state_root);
|
||||
@ -52,14 +55,20 @@ impl ManifestData {
|
||||
/// Try to restore manifest data from raw bytes, interpreted as RLP.
|
||||
pub fn from_rlp(raw: &[u8]) -> Result<Self, DecoderError> {
|
||||
let decoder = UntrustedRlp::new(raw);
|
||||
let (start, version) = if decoder.item_count()? == 5 {
|
||||
(0, 1)
|
||||
} else {
|
||||
(1, decoder.val_at(0)?)
|
||||
};
|
||||
|
||||
let state_hashes: Vec<H256> = decoder.list_at(0)?;
|
||||
let block_hashes: Vec<H256> = decoder.list_at(1)?;
|
||||
let state_root: H256 = decoder.val_at(2)?;
|
||||
let block_number: u64 = decoder.val_at(3)?;
|
||||
let block_hash: H256 = decoder.val_at(4)?;
|
||||
let state_hashes: Vec<H256> = decoder.list_at(start + 0)?;
|
||||
let block_hashes: Vec<H256> = decoder.list_at(start + 1)?;
|
||||
let state_root: H256 = decoder.val_at(start + 2)?;
|
||||
let block_number: u64 = decoder.val_at(start + 3)?;
|
||||
let block_hash: H256 = decoder.val_at(start + 4)?;
|
||||
|
||||
Ok(ManifestData {
|
||||
version: version,
|
||||
state_hashes: state_hashes,
|
||||
block_hashes: block_hashes,
|
||||
state_root: state_root,
|
||||
|
@ -35,6 +35,8 @@ pub enum Error {
|
||||
StackUnderflow,
|
||||
/// When execution would exceed defined Stack Limit
|
||||
OutOfStack,
|
||||
/// When builtin contract failed on input data
|
||||
BuiltIn,
|
||||
/// Returned on evm internal error. Should never be ignored during development.
|
||||
/// Likely to cause consensus issues.
|
||||
Internal,
|
||||
@ -48,6 +50,7 @@ impl<'a> From<&'a EvmError> for Error {
|
||||
EvmError::BadInstruction { .. } => Error::BadInstruction,
|
||||
EvmError::StackUnderflow { .. } => Error::StackUnderflow,
|
||||
EvmError::OutOfStack { .. } => Error::OutOfStack,
|
||||
EvmError::BuiltIn { .. } => Error::BuiltIn,
|
||||
EvmError::Internal(_) => Error::Internal,
|
||||
}
|
||||
}
|
||||
@ -68,6 +71,7 @@ impl fmt::Display for Error {
|
||||
BadInstruction => "Bad instruction",
|
||||
StackUnderflow => "Stack underflow",
|
||||
OutOfStack => "Out of stack",
|
||||
BuiltIn => "Built-in failed",
|
||||
Internal => "Internal error",
|
||||
};
|
||||
message.fmt(f)
|
||||
@ -84,6 +88,7 @@ impl Encodable for Error {
|
||||
StackUnderflow => 3,
|
||||
OutOfStack => 4,
|
||||
Internal => 5,
|
||||
BuiltIn => 6,
|
||||
};
|
||||
|
||||
s.append_internal(&value);
|
||||
@ -101,6 +106,7 @@ impl Decodable for Error {
|
||||
3 => Ok(StackUnderflow),
|
||||
4 => Ok(OutOfStack),
|
||||
5 => Ok(Internal),
|
||||
6 => Ok(BuiltIn),
|
||||
_ => Err(DecoderError::Custom("Invalid error type")),
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ impl Verifier for CanonVerifier {
|
||||
verification::verify_block_family(header, bytes, engine, bc)
|
||||
}
|
||||
|
||||
fn verify_block_final(&self, expected: &Header, got: &Header, receipts: bool) -> Result<(), Error> {
|
||||
verification::verify_block_final(expected, got, receipts)
|
||||
fn verify_block_final(&self, expected: &Header, got: &Header) -> Result<(), Error> {
|
||||
verification::verify_block_final(expected, got)
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ impl Verifier for NoopVerifier {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn verify_block_final(&self, _expected: &Header, _got: &Header, _receipts: bool) -> Result<(), Error> {
|
||||
fn verify_block_final(&self, _expected: &Header, _got: &Header) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -178,7 +178,7 @@ pub fn verify_block_family(header: &Header, bytes: &[u8], engine: &Engine, bc: &
|
||||
}
|
||||
|
||||
/// Phase 4 verification. Check block information against transaction enactment results,
|
||||
pub fn verify_block_final(expected: &Header, got: &Header, check_receipts: bool) -> Result<(), Error> {
|
||||
pub fn verify_block_final(expected: &Header, got: &Header) -> Result<(), Error> {
|
||||
if expected.gas_used() != got.gas_used() {
|
||||
return Err(From::from(BlockError::InvalidGasUsed(Mismatch { expected: expected.gas_used().clone(), found: got.gas_used().clone() })))
|
||||
}
|
||||
@ -188,7 +188,7 @@ pub fn verify_block_final(expected: &Header, got: &Header, check_receipts: bool)
|
||||
if expected.state_root() != got.state_root() {
|
||||
return Err(From::from(BlockError::InvalidStateRoot(Mismatch { expected: expected.state_root().clone(), found: got.state_root().clone() })))
|
||||
}
|
||||
if check_receipts && expected.receipts_root() != got.receipts_root() {
|
||||
if expected.receipts_root() != got.receipts_root() {
|
||||
return Err(From::from(BlockError::InvalidReceiptsRoot(Mismatch { expected: expected.receipts_root().clone(), found: got.receipts_root().clone() })))
|
||||
}
|
||||
Ok(())
|
||||
|
@ -26,5 +26,5 @@ pub trait Verifier: Send + Sync {
|
||||
/// Verify a block relative to its parent and uncles.
|
||||
fn verify_block_family(&self, header: &Header, bytes: &[u8], engine: &Engine, bc: &BlockProvider) -> Result<(), Error>;
|
||||
/// Do a final verification check for an enacted header vs its expected counterpart.
|
||||
fn verify_block_final(&self, expected: &Header, got: &Header, receipts: bool) -> Result<(), Error>;
|
||||
fn verify_block_final(&self, expected: &Header, got: &Header) -> Result<(), Error>;
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
||||
[dependencies]
|
||||
rust-crypto = "0.2.36"
|
||||
tiny-keccak = "1.0"
|
||||
eth-secp256k1 = { git = "https://github.com/ethcore/rust-secp256k1" }
|
||||
eth-secp256k1 = { git = "https://github.com/paritytech/rust-secp256k1" }
|
||||
ethkey = { path = "../ethkey" }
|
||||
ethcore-bigint = { path = "../util/bigint" }
|
||||
|
||||
|
@ -7,9 +7,9 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
||||
rand = "0.3.14"
|
||||
lazy_static = "0.2"
|
||||
tiny-keccak = "1.0"
|
||||
eth-secp256k1 = { git = "https://github.com/ethcore/rust-secp256k1" }
|
||||
eth-secp256k1 = { git = "https://github.com/paritytech/rust-secp256k1" }
|
||||
rustc-serialize = "0.3"
|
||||
docopt = { version = "0.6", optional = true }
|
||||
docopt = { version = "0.7", optional = true }
|
||||
ethcore-bigint = { path = "../util/bigint" }
|
||||
rust-crypto = "0.2"
|
||||
byteorder = "1.0"
|
||||
|
@ -2,12 +2,12 @@
|
||||
|
||||
[![Build Status][travis-image]][travis-url]
|
||||
|
||||
[travis-image]: https://travis-ci.org/ethcore/ethkey.svg?branch=master
|
||||
[travis-url]: https://travis-ci.org/ethcore/ethkey
|
||||
[travis-image]: https://travis-ci.org/paritytech/ethkey.svg?branch=master
|
||||
[travis-url]: https://travis-ci.org/paritytech/ethkey
|
||||
|
||||
Ethereum keys generator.
|
||||
|
||||
[Documentation](http://ethcore.github.io/ethkey/ethkey/index.html)
|
||||
[Documentation](http://paritytech.github.io/ethkey/ethkey/index.html)
|
||||
|
||||
### Usage
|
||||
|
||||
@ -163,6 +163,6 @@ true
|
||||
# Ethcore toolchain
|
||||
*this project is a part of the ethcore toolchain*
|
||||
|
||||
- [**ethkey**](https://github.com/ethcore/ethkey) - Ethereum keys generator and signer.
|
||||
- [**ethstore**](https://github.com/ethcore/ethstore) - Ethereum key management.
|
||||
- [**ethabi**](https://github.com/ethcore/ethabi) - Ethereum function calls encoding.
|
||||
- [**ethkey**](https://github.com/paritytech/ethkey) - Ethereum keys generator and signer.
|
||||
- [**ethstore**](https://github.com/paritytech/ethstore) - Ethereum key management.
|
||||
- [**ethabi**](https://github.com/paritytech/ethabi) - Ethereum function calls encoding.
|
||||
|
@ -29,7 +29,7 @@ impl Brain {
|
||||
impl Generator for Brain {
|
||||
fn generate(self) -> Result<KeyPair, Error> {
|
||||
let seed = self.0;
|
||||
let mut secret = seed.bytes().collect::<Vec<u8>>().keccak256();
|
||||
let mut secret = seed.into_bytes().keccak256();
|
||||
|
||||
let mut i = 0;
|
||||
loop {
|
||||
|
@ -5,8 +5,8 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
log = "0.3"
|
||||
libc = "0.2.11"
|
||||
rand = "0.3.14"
|
||||
libc = "0.2"
|
||||
rand = "0.3"
|
||||
ethkey = { path = "../ethkey" }
|
||||
serde = "0.9"
|
||||
serde_json = "0.9"
|
||||
@ -14,15 +14,15 @@ serde_derive = "0.9"
|
||||
rustc-serialize = "0.3"
|
||||
rust-crypto = "0.2.36"
|
||||
tiny-keccak = "1.0"
|
||||
docopt = { version = "0.6", optional = true }
|
||||
docopt = { version = "0.7", optional = true }
|
||||
time = "0.1.34"
|
||||
lazy_static = "0.2"
|
||||
itertools = "0.5"
|
||||
parking_lot = "0.3"
|
||||
parking_lot = "0.4"
|
||||
ethcrypto = { path = "../ethcrypto" }
|
||||
ethcore-util = { path = "../util" }
|
||||
smallvec = "0.3.1"
|
||||
ethcore-devtools = { path = "../devtools" }
|
||||
parity-wordlist = "1.0"
|
||||
|
||||
[features]
|
||||
cli = ["docopt"]
|
||||
|
@ -2,12 +2,12 @@
|
||||
|
||||
[![Build Status][travis-image]][travis-url]
|
||||
|
||||
[travis-image]: https://travis-ci.org/ethcore/ethstore.svg?branch=master
|
||||
[travis-url]: https://travis-ci.org/ethcore/ethstore
|
||||
[travis-image]: https://travis-ci.org/paritytech/ethstore.svg?branch=master
|
||||
[travis-url]: https://travis-ci.org/paritytech/ethstore
|
||||
|
||||
Ethereum key management.
|
||||
|
||||
[Documentation](http://ethcore.github.io/ethstore/ethstore/index.html)
|
||||
[Documentation](http://paritytech.github.io/ethstore/ethstore/index.html)
|
||||
|
||||
### Usage
|
||||
|
||||
@ -321,6 +321,6 @@ OK
|
||||
# Ethcore toolchain
|
||||
*this project is a part of the ethcore toolchain*
|
||||
|
||||
- [**ethkey**](https://github.com/ethcore/ethkey) - Ethereum keys generator and signer.
|
||||
- [**ethstore**](https://github.com/ethcore/ethstore) - Ethereum key management.
|
||||
- [**ethabi**](https://github.com/ethcore/ethabi) - Ethereum function calls encoding.
|
||||
- [**ethkey**](https://github.com/paritytech/ethkey) - Ethereum keys generator and signer.
|
||||
- [**ethstore**](https://github.com/paritytech/ethstore) - Ethereum key management.
|
||||
- [**ethabi**](https://github.com/paritytech/ethabi) - Ethereum function calls encoding.
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -16,32 +16,29 @@
|
||||
|
||||
//! Ethereum key-management.
|
||||
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
extern crate libc;
|
||||
extern crate crypto as rcrypto;
|
||||
extern crate itertools;
|
||||
extern crate smallvec;
|
||||
extern crate libc;
|
||||
extern crate parking_lot;
|
||||
extern crate rand;
|
||||
extern crate time;
|
||||
extern crate rustc_serialize;
|
||||
extern crate serde;
|
||||
extern crate serde_json;
|
||||
extern crate rustc_serialize;
|
||||
extern crate crypto as rcrypto;
|
||||
extern crate smallvec;
|
||||
extern crate time;
|
||||
extern crate tiny_keccak;
|
||||
extern crate parking_lot;
|
||||
extern crate ethcore_devtools as devtools;
|
||||
|
||||
// reexport it nicely
|
||||
extern crate ethkey as _ethkey;
|
||||
extern crate ethcrypto as crypto;
|
||||
extern crate ethcore_devtools as devtools;
|
||||
extern crate ethcore_util as util;
|
||||
extern crate ethcrypto as crypto;
|
||||
extern crate ethkey as _ethkey;
|
||||
extern crate parity_wordlist;
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
pub mod dir;
|
||||
@ -67,4 +64,5 @@ pub use self::secret_store::{
|
||||
SecretVaultRef, StoreAccountRef, SimpleSecretStore, SecretStore,
|
||||
Derivation, IndexDerivation,
|
||||
};
|
||||
pub use self::random::{random_phrase, random_string};
|
||||
pub use self::random::random_string;
|
||||
pub use self::parity_wordlist::random_phrase;
|
||||
|
@ -15,7 +15,6 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use rand::{Rng, OsRng};
|
||||
use itertools::Itertools;
|
||||
|
||||
pub trait Random {
|
||||
fn random() -> Self where Self: Sized;
|
||||
@ -39,41 +38,9 @@ impl Random for [u8; 32] {
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate a string which is a random phrase of a number of lowercase words.
|
||||
///
|
||||
/// `words` is the number of words, chosen from a dictionary of 7,530. An value of
|
||||
/// 12 gives 155 bits of entropy (almost saturating address space); 20 gives 258 bits
|
||||
/// which is enough to saturate 32-byte key space
|
||||
pub fn random_phrase(words: usize) -> String {
|
||||
lazy_static! {
|
||||
static ref WORDS: Vec<String> = String::from_utf8_lossy(include_bytes!("../res/wordlist.txt"))
|
||||
.lines()
|
||||
.map(|s| s.to_owned())
|
||||
.collect();
|
||||
}
|
||||
let mut rng = OsRng::new().expect("Not able to operate without random source.");
|
||||
(0..words).map(|_| rng.choose(&WORDS).unwrap()).join(" ")
|
||||
}
|
||||
|
||||
/// Generate a random string of given length.
|
||||
pub fn random_string(length: usize) -> String {
|
||||
let mut rng = OsRng::new().expect("Not able to operate without random source.");
|
||||
rng.gen_ascii_chars().take(length).collect()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::random_phrase;
|
||||
|
||||
#[test]
|
||||
fn should_produce_right_number_of_words() {
|
||||
let p = random_phrase(10);
|
||||
assert_eq!(p.split(" ").count(), 10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_not_include_carriage_return() {
|
||||
let p = random_phrase(10);
|
||||
assert!(!p.contains('\r'), "Carriage return should be trimmed.");
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ path = "./src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
rustc-serialize = "0.3"
|
||||
docopt = { version = "0.6" }
|
||||
docopt = { version = "0.7" }
|
||||
ethcore = { path = "../ethcore" }
|
||||
ethcore-util = { path = "../util" }
|
||||
|
||||
|
@ -8,9 +8,9 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
log = "0.3"
|
||||
parking_lot = "0.3"
|
||||
hidapi = { git = "https://github.com/ethcore/hidapi-rs" }
|
||||
libusb = { git = "https://github.com/ethcore/libusb-rs" }
|
||||
parking_lot = "0.4"
|
||||
hidapi = { git = "https://github.com/paritytech/hidapi-rs" }
|
||||
libusb = { git = "https://github.com/paritytech/libusb-rs" }
|
||||
ethkey = { path = "../ethkey" }
|
||||
ethcore-bigint = { path = "../util/bigint" }
|
||||
|
||||
|
@ -10,7 +10,7 @@ build = "build.rs"
|
||||
ethcore-ipc-codegen = { path = "../ipc/codegen" }
|
||||
|
||||
[dependencies]
|
||||
semver = "0.5"
|
||||
semver = "0.6"
|
||||
ethcore-ipc = { path = "../ipc/rpc" }
|
||||
ethcore-util = { path = "../util" }
|
||||
|
||||
|
@ -9,9 +9,9 @@ build = "build.rs"
|
||||
|
||||
[dependencies]
|
||||
ethcore-ipc = { path = "../rpc" }
|
||||
nanomsg = { git = "https://github.com/ethcore/nanomsg.rs.git", branch = "parity-1.7" }
|
||||
nanomsg = { git = "https://github.com/paritytech/nanomsg.rs.git", branch = "parity-1.7" }
|
||||
ethcore-ipc-nano = { path = "../nano" }
|
||||
semver = "0.5"
|
||||
semver = "0.6"
|
||||
log = "0.3"
|
||||
time = "0.1"
|
||||
|
||||
|
@ -8,6 +8,6 @@ license = "GPL-3.0"
|
||||
|
||||
[dependencies]
|
||||
ethcore-ipc = { path = "../rpc" }
|
||||
nanomsg = { git = "https://github.com/ethcore/nanomsg.rs.git", branch = "parity-1.7" }
|
||||
nanomsg = { git = "https://github.com/paritytech/nanomsg.rs.git", branch = "parity-1.7" }
|
||||
log = "0.3"
|
||||
lazy_static = "0.2"
|
||||
|
@ -8,6 +8,6 @@ license = "GPL-3.0"
|
||||
|
||||
[dependencies]
|
||||
ethcore-devtools = { path = "../../devtools" }
|
||||
nanomsg = { git = "https://github.com/ethcore/nanomsg.rs.git", branch = "parity-1.7" }
|
||||
nanomsg = { git = "https://github.com/paritytech/nanomsg.rs.git", branch = "parity-1.7" }
|
||||
ethcore-util = { path = "../../util" }
|
||||
semver = "0.5"
|
||||
semver = "0.6"
|
||||
|
@ -10,8 +10,8 @@ path = "run.rs"
|
||||
[dependencies]
|
||||
ethcore-ipc = { path = "../rpc" }
|
||||
ethcore-devtools = { path = "../../devtools" }
|
||||
semver = "0.5"
|
||||
nanomsg = { git = "https://github.com/ethcore/nanomsg.rs.git", branch = "parity-1.7" }
|
||||
semver = "0.6"
|
||||
nanomsg = { git = "https://github.com/paritytech/nanomsg.rs.git", branch = "parity-1.7" }
|
||||
ethcore-ipc-nano = { path = "../nano" }
|
||||
ethcore-util = { path = "../../util" }
|
||||
log = "0.3"
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
A thin, lightweight promise wrapper for the api.etherscan.io/apis service, exposing a common endpoint for use in JavaScript applications.
|
||||
|
||||
[https://github.com/ethcore/parity/tree/master/js/src/3rdparty/etherscan](https://github.com/ethcore/parity/tree/master/js/src/3rdparty/etherscan)
|
||||
[https://github.com/paritytech/parity/tree/master/js/src/3rdparty/etherscan](https://github.com/paritytech/parity/tree/master/js/src/3rdparty/etherscan)
|
||||
|
||||
## usage
|
||||
|
||||
|
@ -11,7 +11,7 @@
|
||||
"license": "GPL-3.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/ethcore/parity.git"
|
||||
"url": "git+https://github.com/paritytech/parity.git"
|
||||
},
|
||||
"keywords": [
|
||||
"Ethereum",
|
||||
|
@ -2,4 +2,4 @@
|
||||
|
||||
JSON and JS interface defintions for RPC calls.
|
||||
|
||||
[https://github.com/ethcore/parity/tree/master/js/src/jsonrpc](https://github.com/ethcore/parity/tree/master/js/src/jsonrpc)
|
||||
[https://github.com/paritytech/parity/tree/master/js/src/jsonrpc](https://github.com/paritytech/parity/tree/master/js/src/jsonrpc)
|
||||
|
@ -11,7 +11,7 @@
|
||||
"license": "GPL-3.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/ethcore/parity.git"
|
||||
"url": "git+https://github.com/paritytech/parity.git"
|
||||
},
|
||||
"keywords": [
|
||||
"Ethereum",
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
Parity.js is a thin, fast, Promise-based wrapper around the Ethereum APIs.
|
||||
|
||||
[https://github.com/ethcore/parity/tree/master/js/src/api](https://github.com/ethcore/parity/tree/master/js/src/api)
|
||||
[https://github.com/paritytech/parity/tree/master/js/src/api](https://github.com/paritytech/parity/tree/master/js/src/api)
|
||||
|
||||
## installation
|
||||
|
||||
@ -80,4 +80,4 @@ contract.instance
|
||||
|
||||
## apis
|
||||
|
||||
APIs implement the calls as exposed in the [Ethcore JSON Ethereum RPC](https://github.com/ethcore/ethereum-rpc-json/) definitions. Mapping follows the naming conventions of the originals, i.e. `eth_call` becomes `eth.call`, `personal_accounts` becomes `personal.accounts`, etc.
|
||||
APIs implement the calls as exposed in the [Ethcore JSON Ethereum RPC](https://github.com/paritytech/ethereum-rpc-json/) definitions. Mapping follows the naming conventions of the originals, i.e. `eth_call` becomes `eth.call`, `personal_accounts` becomes `personal.accounts`, etc.
|
||||
|
@ -11,7 +11,7 @@
|
||||
"license": "GPL-3.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/ethcore/parity.git"
|
||||
"url": "git+https://github.com/paritytech/parity.git"
|
||||
},
|
||||
"keywords": [
|
||||
"Ethereum",
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
A thin ES6 promise wrapper around the shapeshift.io APIs as documented at https://shapeshift.io/api
|
||||
|
||||
[https://github.com/ethcore/parity/tree/master/js/src/3rdparty/shapeshift](https://github.com/ethcore/parity/tree/master/js/src/3rdparty/shapeshift)
|
||||
[https://github.com/paritytech/parity/tree/master/js/src/3rdparty/shapeshift](https://github.com/paritytech/parity/tree/master/js/src/3rdparty/shapeshift)
|
||||
|
||||
## usage
|
||||
|
||||
|
@ -11,7 +11,7 @@
|
||||
"license": "GPL-3.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/ethcore/parity.git"
|
||||
"url": "git+https://github.com/paritytech/parity.git"
|
||||
},
|
||||
"keywords": [
|
||||
"Ethereum",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "parity.js",
|
||||
"version": "1.7.29",
|
||||
"version": "1.7.43",
|
||||
"main": "release/index.js",
|
||||
"jsnext:main": "src/index.js",
|
||||
"author": "Parity Team <admin@parity.io>",
|
||||
@ -14,7 +14,7 @@
|
||||
"license": "GPL-3.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/ethcore/parity.git"
|
||||
"url": "git+https://github.com/paritytech/parity.git"
|
||||
},
|
||||
"keywords": [
|
||||
"Ethereum",
|
||||
@ -26,6 +26,11 @@
|
||||
"Promise"
|
||||
],
|
||||
"scripts": {
|
||||
"install": "napa",
|
||||
"analize": "npm run analize:lib && npm run analize:dll && npm run analize:app",
|
||||
"analize:app": "WPANALIZE=1 webpack --config webpack/app --json > .build/analize.app.json && cat .build/analize.app.json | webpack-bundle-size-analyzer",
|
||||
"analize:lib": "WPANALIZE=1 webpack --config webpack/libraries --json > .build/analize.lib.json && cat .build/analize.lib.json | webpack-bundle-size-analyzer",
|
||||
"analize:dll": "WPANALIZE=1 webpack --config webpack/vendor --json > .build/analize.dll.json && cat .build/analize.dll.json | webpack-bundle-size-analyzer",
|
||||
"build": "npm run build:lib && npm run build:dll && npm run build:app && npm run build:embed",
|
||||
"build:app": "webpack --config webpack/app",
|
||||
"build:lib": "webpack --config webpack/libraries",
|
||||
@ -49,6 +54,7 @@
|
||||
"lint:cached": "npm run lint:css && npm run lint:js:cached",
|
||||
"lint:css": "stylelint ./src/**/*.css",
|
||||
"lint:fix": "npm run lint:js:fix",
|
||||
"lint:i18n": "babel-node ./scripts/lint-i18n.js",
|
||||
"lint:js": "eslint --ignore-path .gitignore ./src/",
|
||||
"lint:js:cached": "eslint --cache --ignore-path .gitignore ./src/",
|
||||
"lint:js:fix": "eslint --fix --ignore-path .gitignore ./src/",
|
||||
@ -58,6 +64,9 @@
|
||||
"test:npm": "(cd .npmjs && npm i) && node test/npmParity && node test/npmJsonRpc && (rm -rf .npmjs/node_modules)",
|
||||
"prepush": "npm run lint:cached"
|
||||
},
|
||||
"napa": {
|
||||
"qrcode-generator": "kazuhikoarase/qrcode-generator"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "6.23.0",
|
||||
"babel-core": "6.23.1",
|
||||
@ -141,6 +150,7 @@
|
||||
"to-source": "2.0.3",
|
||||
"url-loader": "0.5.7",
|
||||
"webpack": "2.2.1",
|
||||
"webpack-bundle-size-analyzer": "2.5.0",
|
||||
"webpack-dev-middleware": "1.10.1",
|
||||
"webpack-error-notification": "0.1.6",
|
||||
"webpack-hot-middleware": "2.17.1",
|
||||
@ -148,6 +158,7 @@
|
||||
"yargs": "6.6.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@parity/wordlist": "1.0.1",
|
||||
"base32.js": "0.1.0",
|
||||
"bignumber.js": "3.0.1",
|
||||
"blockies": "0.0.2",
|
||||
@ -165,6 +176,7 @@
|
||||
"geopattern": "1.2.3",
|
||||
"isomorphic-fetch": "2.2.1",
|
||||
"js-sha3": "0.5.5",
|
||||
"keythereum": "0.4.3",
|
||||
"lodash": "4.17.2",
|
||||
"loglevel": "1.4.1",
|
||||
"marked": "0.3.6",
|
||||
@ -174,22 +186,23 @@
|
||||
"mobx-react": "4.0.3",
|
||||
"mobx-react-devtools": "4.2.10",
|
||||
"moment": "2.17.0",
|
||||
"napa": "2.3.0",
|
||||
"phoneformat.js": "1.0.3",
|
||||
"promise-worker": "1.1.1",
|
||||
"push.js": "0.0.11",
|
||||
"qrcode-npm": "0.0.3",
|
||||
"qs": "6.3.0",
|
||||
"react": "15.4.1",
|
||||
"react": "15.4.2",
|
||||
"react-ace": "4.1.0",
|
||||
"react-addons-css-transition-group": "15.4.1",
|
||||
"react-addons-css-transition-group": "15.4.2",
|
||||
"react-copy-to-clipboard": "4.2.3",
|
||||
"react-dom": "15.4.1",
|
||||
"react-dom": "15.4.2",
|
||||
"react-dropzone": "3.7.3",
|
||||
"react-element-to-jsx-string": "6.0.0",
|
||||
"react-event-listener": "0.4.1",
|
||||
"react-intl": "2.1.5",
|
||||
"react-markdown": "2.4.4",
|
||||
"react-portal": "3.0.0",
|
||||
"react-qr-reader": "1.0.3",
|
||||
"react-redux": "4.4.6",
|
||||
"react-router": "3.0.0",
|
||||
"react-router-redux": "4.0.7",
|
||||
@ -201,12 +214,13 @@
|
||||
"redux-thunk": "2.1.0",
|
||||
"rlp": "2.0.0",
|
||||
"scryptsy": "2.0.0",
|
||||
"secp256k1": "3.2.5",
|
||||
"solc": "ngotchac/solc-js",
|
||||
"store": "1.3.20",
|
||||
"sw-toolbox": "^3.6.0",
|
||||
"u2f-api": "0.0.9",
|
||||
"u2f-api-polyfill": "0.4.3",
|
||||
"uglify-js": "2.8.2",
|
||||
"uglify-js": "2.8.16",
|
||||
"useragent.js": "0.5.6",
|
||||
"utf8": "2.1.2",
|
||||
"valid-url": "1.0.9",
|
||||
|
53
js/scripts/lint-i18n.js
Normal file
53
js/scripts/lint-i18n.js
Normal file
@ -0,0 +1,53 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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 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. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import flatten from 'flat';
|
||||
|
||||
import * as defaults from '../src/i18n/_default';
|
||||
import { LANGUAGES, MESSAGES } from '../src/i18n/store';
|
||||
|
||||
const SKIP_LANG = ['en'];
|
||||
const defaultKeys = Object.keys(flatten(Object.assign({}, defaults, LANGUAGES)));
|
||||
|
||||
Object
|
||||
.keys(MESSAGES)
|
||||
.filter((lang) => !SKIP_LANG.includes(lang))
|
||||
.forEach((lang) => {
|
||||
const messageKeys = Object.keys(MESSAGES[lang]);
|
||||
let extra = 0;
|
||||
let found = 0;
|
||||
let missing = 0;
|
||||
|
||||
console.log(`*** Checking translations for ${lang}`);
|
||||
|
||||
defaultKeys.forEach((key) => {
|
||||
if (messageKeys.includes(key)) {
|
||||
found++;
|
||||
} else {
|
||||
missing++;
|
||||
console.log(` Missing ${key}`);
|
||||
}
|
||||
});
|
||||
|
||||
messageKeys.forEach((key) => {
|
||||
if (!defaultKeys.includes(key)) {
|
||||
extra++;
|
||||
console.log(` Extra ${key}`);
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`Found ${found} keys, missing ${missing} keys, ${extra} extraneous keys\n`);
|
||||
});
|
@ -5,8 +5,8 @@ set -e
|
||||
UTCDATE=`date -u "+%Y%m%d-%H%M%S"`
|
||||
PACKAGES=( "parity" "etherscan" "shapeshift" "jsonrpc" )
|
||||
BRANCH=$CI_BUILD_REF_NAME
|
||||
GIT_JS_PRECOMPILED="https://${GITHUB_JS_PRECOMPILED}:@github.com/ethcore/js-precompiled.git"
|
||||
GIT_PARITY="https://${GITHUB_JS_PRECOMPILED}:@github.com/ethcore/parity.git"
|
||||
GIT_JS_PRECOMPILED="https://${GITHUB_JS_PRECOMPILED}:@github.com/paritytech/js-precompiled.git"
|
||||
GIT_PARITY="https://${GITHUB_JS_PRECOMPILED}:@github.com/paritytech/parity.git"
|
||||
|
||||
# setup the git user defaults for the current repo
|
||||
function setup_git_user {
|
||||
|
@ -1,2 +1,2 @@
|
||||
// test script 8
|
||||
// test script 9
|
||||
// trigger rebuild on master 15 Mar 2017, 11:19
|
||||
|
@ -1,6 +1,6 @@
|
||||
# ethabi-js
|
||||
|
||||
A very early, very POC-type port of [https://github.com/ethcore/ethabi](https://github.com/ethcore/ethabi) to JavaScript
|
||||
A very early, very POC-type port of [https://github.com/paritytech/ethabi](https://github.com/paritytech/ethabi) to JavaScript
|
||||
|
||||
[![Build Status](https://travis-ci.org/jacogr/ethabi-js.svg?branch=master)](https://travis-ci.org/jacogr/ethabi-js)
|
||||
[![Coverage Status](https://coveralls.io/repos/github/jacogr/ethabi-js/badge.svg?branch=master)](https://coveralls.io/github/jacogr/ethabi-js?branch=master)
|
||||
|
@ -131,16 +131,16 @@ contract
|
||||
|
||||
## apis
|
||||
|
||||
APIs implement the calls as exposed in the [Ethcore JSON Ethereum RPC](https://github.com/ethcore/ethereum-rpc-json/) definitions. Mapping follows the naming conventions of the originals, i.e. `eth_call` becomes `eth.call`, `personal_accounts` becomes `personal.accounts`, etc.
|
||||
APIs implement the calls as exposed in the [Ethcore JSON Ethereum RPC](https://github.com/paritytech/ethereum-rpc-json/) definitions. Mapping follows the naming conventions of the originals, i.e. `eth_call` becomes `eth.call`, `personal_accounts` becomes `personal.accounts`, etc.
|
||||
|
||||
- [ethapi.db](https://github.com/ethcore/ethereum-rpc-json/blob/master/interfaces.md#db)
|
||||
- [ethapi.eth](https://github.com/ethcore/ethereum-rpc-json/blob/master/interfaces.md#eth)
|
||||
- [ethapi.parity](https://github.com/ethcore/ethereum-rpc-json/blob/master/interfaces.md#parity)
|
||||
- [ethapi.net](https://github.com/ethcore/ethereum-rpc-json/blob/master/interfaces.md#net)
|
||||
- [ethapi.personal](https://github.com/ethcore/ethereum-rpc-json/blob/master/interfaces.md#personal)
|
||||
- [ethapi.shh](https://github.com/ethcore/ethereum-rpc-json/blob/master/interfaces.md#shh)
|
||||
- [ethapi.signer](https://github.com/ethcore/ethereum-rpc-json/blob/master/interfaces.md#signer)
|
||||
- [ethapi.trace](https://github.com/ethcore/ethereum-rpc-json/blob/master/interfaces.md#trace)
|
||||
- [ethapi.web3](https://github.com/ethcore/ethereum-rpc-json/blob/master/interfaces.md#web3)
|
||||
- [ethapi.db](https://github.com/paritytech/ethereum-rpc-json/blob/master/interfaces.md#db)
|
||||
- [ethapi.eth](https://github.com/paritytech/ethereum-rpc-json/blob/master/interfaces.md#eth)
|
||||
- [ethapi.parity](https://github.com/paritytech/ethereum-rpc-json/blob/master/interfaces.md#parity)
|
||||
- [ethapi.net](https://github.com/paritytech/ethereum-rpc-json/blob/master/interfaces.md#net)
|
||||
- [ethapi.personal](https://github.com/paritytech/ethereum-rpc-json/blob/master/interfaces.md#personal)
|
||||
- [ethapi.shh](https://github.com/paritytech/ethereum-rpc-json/blob/master/interfaces.md#shh)
|
||||
- [ethapi.signer](https://github.com/paritytech/ethereum-rpc-json/blob/master/interfaces.md#signer)
|
||||
- [ethapi.trace](https://github.com/paritytech/ethereum-rpc-json/blob/master/interfaces.md#trace)
|
||||
- [ethapi.web3](https://github.com/paritytech/ethereum-rpc-json/blob/master/interfaces.md#web3)
|
||||
|
||||
As a verification step, all exposed interfaces are tested for existing and pointing to the correct endpoints by using the generated interfaces from the above repo.
|
||||
|
@ -23,9 +23,10 @@ import { Db, Eth, Parity, Net, Personal, Shh, Signer, Trace, Web3 } from './rpc'
|
||||
import Subscriptions from './subscriptions';
|
||||
import util from './util';
|
||||
import { isFunction } from './util/types';
|
||||
import { LocalAccountsMiddleware } from './local';
|
||||
|
||||
export default class Api extends EventEmitter {
|
||||
constructor (transport) {
|
||||
constructor (transport, allowSubscriptions = true) {
|
||||
super();
|
||||
|
||||
if (!transport || !isFunction(transport.execute)) {
|
||||
@ -44,9 +45,26 @@ export default class Api extends EventEmitter {
|
||||
this._trace = new Trace(transport);
|
||||
this._web3 = new Web3(transport);
|
||||
|
||||
if (allowSubscriptions) {
|
||||
this._subscriptions = new Subscriptions(this);
|
||||
}
|
||||
|
||||
// Doing a request here in test env would cause an error
|
||||
if (process.env.NODE_ENV !== 'test') {
|
||||
const middleware = this.parity
|
||||
.nodeKind()
|
||||
.then((nodeKind) => {
|
||||
if (nodeKind.availability === 'public') {
|
||||
return new LocalAccountsMiddleware(transport);
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
transport.addMiddleware(middleware);
|
||||
}
|
||||
}
|
||||
|
||||
get db () {
|
||||
return this._db;
|
||||
}
|
||||
@ -96,10 +114,18 @@ export default class Api extends EventEmitter {
|
||||
}
|
||||
|
||||
subscribe (subscriptionName, callback) {
|
||||
if (!this._subscriptions) {
|
||||
return Promise.resolve(1);
|
||||
}
|
||||
|
||||
return this._subscriptions.subscribe(subscriptionName, callback);
|
||||
}
|
||||
|
||||
unsubscribe (subscriptionId) {
|
||||
if (!this._subscriptions) {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
return this._subscriptions.unsubscribe(subscriptionId);
|
||||
}
|
||||
|
||||
|
@ -139,15 +139,16 @@ export function inOptionsCondition (condition) {
|
||||
return condition;
|
||||
}
|
||||
|
||||
export function inOptions (options) {
|
||||
if (options) {
|
||||
export function inOptions (_options = {}) {
|
||||
const options = { ..._options };
|
||||
|
||||
Object.keys(options).forEach((key) => {
|
||||
switch (key) {
|
||||
case 'to':
|
||||
// Don't encode the `to` option if it's empty
|
||||
// (eg. contract deployments)
|
||||
if (options[key]) {
|
||||
options[key] = inAddress(options[key]);
|
||||
options.to = inAddress(options[key]);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -178,7 +179,6 @@ export function inOptions (options) {
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
@ -140,6 +140,10 @@ export function outHwAccountInfo (infos) {
|
||||
}, {});
|
||||
}
|
||||
|
||||
export function outNodeKind (info) {
|
||||
return info;
|
||||
}
|
||||
|
||||
export function outNumber (number) {
|
||||
return new BigNumber(number || 0);
|
||||
}
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
import BigNumber from 'bignumber.js';
|
||||
|
||||
import { outBlock, outAccountInfo, outAddress, outChainStatus, outDate, outHistogram, outHwAccountInfo, outNumber, outPeer, outPeers, outReceipt, outRecentDapps, outSyncing, outTransaction, outTrace, outVaultMeta } from './output';
|
||||
import { outBlock, outAccountInfo, outAddress, outChainStatus, outDate, outHistogram, outHwAccountInfo, outNodeKind, outNumber, outPeer, outPeers, outReceipt, outRecentDapps, outSyncing, outTransaction, outTrace, outVaultMeta } from './output';
|
||||
import { isAddress, isBigNumber, isInstanceOf } from '../../../test/types';
|
||||
|
||||
describe('api/format/output', () => {
|
||||
@ -173,6 +173,14 @@ describe('api/format/output', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('outNodeKind', () => {
|
||||
it('formats the input as received', () => {
|
||||
const kind = { availability: 'personal', capability: 'full' };
|
||||
|
||||
expect(outNodeKind(kind)).to.deep.equal(kind);
|
||||
});
|
||||
});
|
||||
|
||||
describe('outNumber', () => {
|
||||
it('returns a BigNumber equalling the value', () => {
|
||||
const bn = outNumber('0x123456');
|
||||
|
95
js/src/api/local/accounts/account.js
Normal file
95
js/src/api/local/accounts/account.js
Normal file
@ -0,0 +1,95 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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 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. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { keythereum } from '../ethkey';
|
||||
|
||||
export default class Account {
|
||||
constructor (persist, data) {
|
||||
const {
|
||||
keyObject,
|
||||
meta = {},
|
||||
name = ''
|
||||
} = data;
|
||||
|
||||
this._persist = persist;
|
||||
this._keyObject = keyObject;
|
||||
this._name = name;
|
||||
this._meta = meta;
|
||||
}
|
||||
|
||||
isValidPassword (password) {
|
||||
try {
|
||||
keythereum.recover(Buffer.from(password), this._keyObject);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
get address () {
|
||||
return `0x${this._keyObject.address.toLowerCase()}`;
|
||||
}
|
||||
|
||||
get name () {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
set name (name) {
|
||||
this._name = name;
|
||||
|
||||
this._persist();
|
||||
}
|
||||
|
||||
get meta () {
|
||||
return JSON.stringify(this._meta);
|
||||
}
|
||||
|
||||
set meta (meta) {
|
||||
this._meta = JSON.parse(meta);
|
||||
|
||||
this._persist();
|
||||
}
|
||||
|
||||
get uuid () {
|
||||
return this._keyObject.id;
|
||||
}
|
||||
|
||||
decryptPrivateKey (password) {
|
||||
return keythereum.recover(Buffer.from(password), this._keyObject);
|
||||
}
|
||||
|
||||
static fromPrivateKey (persist, key, password) {
|
||||
const iv = keythereum.crypto.randomBytes(16);
|
||||
const salt = keythereum.crypto.randomBytes(32);
|
||||
|
||||
// Keythereum will fail if `password` is an empty string
|
||||
password = Buffer.from(password);
|
||||
|
||||
const keyObject = keythereum.dump(password, key, salt, iv);
|
||||
|
||||
const account = new Account(persist, { keyObject });
|
||||
|
||||
return account;
|
||||
}
|
||||
|
||||
toJSON () {
|
||||
return {
|
||||
keyObject: this._keyObject,
|
||||
name: this._name,
|
||||
meta: this._meta
|
||||
};
|
||||
}
|
||||
}
|
120
js/src/api/local/accounts/accounts.js
Normal file
120
js/src/api/local/accounts/accounts.js
Normal file
@ -0,0 +1,120 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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 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. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import Account from './account';
|
||||
import localStore from 'store';
|
||||
import { debounce } from 'lodash';
|
||||
|
||||
const NULL_ADDRESS = '0x0000000000000000000000000000000000000000';
|
||||
const LS_STORE_KEY = '_parity::localAccounts';
|
||||
|
||||
export default class Accounts {
|
||||
constructor (data = localStore.get(LS_STORE_KEY) || {}) {
|
||||
const {
|
||||
last = NULL_ADDRESS,
|
||||
store = []
|
||||
} = data;
|
||||
|
||||
this.persist = debounce(() => {
|
||||
localStore.set(LS_STORE_KEY, this);
|
||||
}, 100);
|
||||
|
||||
this._last = last;
|
||||
this._store = store.map((data) => new Account(this.persist, data));
|
||||
}
|
||||
|
||||
create (secret, password) {
|
||||
const privateKey = Buffer.from(secret.slice(2), 'hex');
|
||||
const account = Account.fromPrivateKey(this.persist, privateKey, password);
|
||||
|
||||
this._store.push(account);
|
||||
this.lastAddress = account.address;
|
||||
|
||||
this.persist();
|
||||
|
||||
return account.address;
|
||||
}
|
||||
|
||||
set lastAddress (value) {
|
||||
this._last = value.toLowerCase();
|
||||
}
|
||||
|
||||
get lastAddress () {
|
||||
return this._last;
|
||||
}
|
||||
|
||||
get (address) {
|
||||
address = address.toLowerCase();
|
||||
|
||||
this.lastAddress = address;
|
||||
|
||||
const account = this._store.find((account) => account.address === address);
|
||||
|
||||
if (!account) {
|
||||
throw new Error(`Account not found: ${address}`);
|
||||
}
|
||||
|
||||
return account;
|
||||
}
|
||||
|
||||
remove (address, password) {
|
||||
address = address.toLowerCase();
|
||||
|
||||
const index = this._store.findIndex((account) => account.address === address);
|
||||
|
||||
if (index === -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const account = this._store[index];
|
||||
|
||||
if (!account.isValidPassword(password)) {
|
||||
console.log('invalid password');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (address === this.lastAddress) {
|
||||
this.lastAddress = NULL_ADDRESS;
|
||||
}
|
||||
|
||||
this._store.splice(index, 1);
|
||||
|
||||
this.persist();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
mapArray (mapper) {
|
||||
return this._store.map(mapper);
|
||||
}
|
||||
|
||||
mapObject (mapper) {
|
||||
const result = {};
|
||||
|
||||
this._store.forEach((account) => {
|
||||
result[account.address] = mapper(account);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
toJSON () {
|
||||
return {
|
||||
last: this._last,
|
||||
store: this._store
|
||||
};
|
||||
}
|
||||
}
|
@ -14,6 +14,8 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { createAction } from 'redux-actions';
|
||||
import Accounts from './accounts';
|
||||
|
||||
export const initAppAction = createAction('init app');
|
||||
const accounts = new Accounts();
|
||||
|
||||
export default accounts;
|
44
js/src/api/local/ethkey/index.js
Normal file
44
js/src/api/local/ethkey/index.js
Normal file
@ -0,0 +1,44 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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 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. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Allow a web worker in the browser, with a fallback for Node.js
|
||||
const hasWebWorkers = typeof Worker !== 'undefined';
|
||||
const KeyWorker = hasWebWorkers ? require('worker-loader!./worker')
|
||||
: require('./worker').KeyWorker;
|
||||
|
||||
// Local accounts should never be used outside of the browser
|
||||
export let keythereum = null;
|
||||
|
||||
if (hasWebWorkers) {
|
||||
require('keythereum/dist/keythereum');
|
||||
|
||||
keythereum = window.keythereum;
|
||||
}
|
||||
|
||||
export function phraseToAddress (phrase) {
|
||||
return phraseToWallet(phrase).then((wallet) => wallet.address);
|
||||
}
|
||||
|
||||
export function phraseToWallet (phrase) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const worker = new KeyWorker();
|
||||
|
||||
worker.postMessage(phrase);
|
||||
worker.onmessage = ({ data }) => {
|
||||
resolve(data);
|
||||
};
|
||||
});
|
||||
}
|
57
js/src/api/local/ethkey/index.spec.js
Normal file
57
js/src/api/local/ethkey/index.spec.js
Normal file
@ -0,0 +1,57 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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 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. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { randomPhrase } from '@parity/wordlist';
|
||||
import { phraseToAddress, phraseToWallet } from './';
|
||||
|
||||
describe('api/local/ethkey', () => {
|
||||
describe.skip('phraseToAddress', function () {
|
||||
this.timeout(10000);
|
||||
|
||||
it('generates a valid address', () => {
|
||||
const phrase = randomPhrase(12);
|
||||
|
||||
return phraseToAddress(phrase).then((address) => {
|
||||
expect(address.length).to.be.equal(42);
|
||||
expect(address.slice(0, 4)).to.be.equal('0x00');
|
||||
});
|
||||
});
|
||||
|
||||
it('generates valid address for empty phrase', () => {
|
||||
return phraseToAddress('').then((address) => {
|
||||
expect(address).to.be.equal('0x00a329c0648769a73afac7f9381e08fb43dbea72');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe.skip('phraseToWallet', function () {
|
||||
this.timeout(10000);
|
||||
|
||||
it('generates a valid wallet object', () => {
|
||||
const phrase = randomPhrase(12);
|
||||
|
||||
return phraseToWallet(phrase).then((wallet) => {
|
||||
expect(wallet.address.length).to.be.equal(42);
|
||||
expect(wallet.secret.length).to.be.equal(66);
|
||||
expect(wallet.public.length).to.be.equal(130);
|
||||
|
||||
expect(wallet.address.slice(0, 4)).to.be.equal('0x00');
|
||||
expect(wallet.secret.slice(0, 2)).to.be.equal('0x');
|
||||
expect(wallet.public.slice(0, 2)).to.be.equal('0x');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
89
js/src/api/local/ethkey/worker.js
Normal file
89
js/src/api/local/ethkey/worker.js
Normal file
@ -0,0 +1,89 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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 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. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { keccak_256 as keccak256 } from 'js-sha3';
|
||||
import secp256k1 from 'secp256k1/js';
|
||||
|
||||
// Stay compatible between environments
|
||||
if (typeof self !== 'object') {
|
||||
const scope = typeof global === 'undefined' ? window : global;
|
||||
|
||||
scope.self = scope;
|
||||
}
|
||||
|
||||
function bytesToHex (bytes) {
|
||||
return '0x' + Array.from(bytes).map(n => ('0' + n.toString(16)).slice(-2)).join('');
|
||||
}
|
||||
|
||||
// Logic ported from /ethkey/src/brain.rs
|
||||
function phraseToWallet (phrase) {
|
||||
let secret = keccak256.array(phrase);
|
||||
|
||||
for (let i = 0; i < 16384; i++) {
|
||||
secret = keccak256.array(secret);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
secret = keccak256.array(secret);
|
||||
|
||||
const secretBuf = Buffer.from(secret);
|
||||
|
||||
if (secp256k1.privateKeyVerify(secretBuf)) {
|
||||
// No compression, slice out last 64 bytes
|
||||
const publicBuf = secp256k1.publicKeyCreate(secretBuf, false).slice(-64);
|
||||
const address = keccak256.array(publicBuf).slice(12);
|
||||
|
||||
if (address[0] !== 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const wallet = {
|
||||
secret: bytesToHex(secretBuf),
|
||||
public: bytesToHex(publicBuf),
|
||||
address: bytesToHex(address)
|
||||
};
|
||||
|
||||
return wallet;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.onmessage = function ({ data }) {
|
||||
const wallet = phraseToWallet(data);
|
||||
|
||||
postMessage(wallet);
|
||||
close();
|
||||
};
|
||||
|
||||
// Emulate a web worker in Node.js
|
||||
class KeyWorker {
|
||||
postMessage (data) {
|
||||
// Force async
|
||||
setTimeout(() => {
|
||||
const wallet = phraseToWallet(data);
|
||||
|
||||
this.onmessage({ data: wallet });
|
||||
}, 0);
|
||||
}
|
||||
|
||||
onmessage (event) {
|
||||
// no-op to be overriden
|
||||
}
|
||||
}
|
||||
|
||||
if (exports != null) {
|
||||
exports.KeyWorker = KeyWorker;
|
||||
}
|
17
js/src/api/local/index.js
Normal file
17
js/src/api/local/index.js
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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 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. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export LocalAccountsMiddleware from './middleware';
|
174
js/src/api/local/middleware.js
Normal file
174
js/src/api/local/middleware.js
Normal file
@ -0,0 +1,174 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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 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. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import EthereumTx from 'ethereumjs-tx';
|
||||
import accounts from './accounts';
|
||||
import transactions from './transactions';
|
||||
import { Middleware } from '../transport';
|
||||
import { inNumber16 } from '../format/input';
|
||||
import { phraseToWallet, phraseToAddress } from './ethkey';
|
||||
import { randomPhrase } from '@parity/wordlist';
|
||||
|
||||
export default class LocalAccountsMiddleware extends Middleware {
|
||||
// Maps transaction requests to transaction hashes.
|
||||
// This allows the locally-signed transactions to emulate the signer.
|
||||
transactionHashes = {};
|
||||
transactions = {};
|
||||
|
||||
// Current transaction id. This doesn't need to be stored, as it's
|
||||
// only relevant for the current the session.
|
||||
transactionId = 1;
|
||||
|
||||
constructor (transport) {
|
||||
super(transport);
|
||||
|
||||
const register = this.register.bind(this);
|
||||
|
||||
register('eth_accounts', () => {
|
||||
return accounts.mapArray((account) => account.address);
|
||||
});
|
||||
|
||||
register('eth_coinbase', () => {
|
||||
return accounts.lastAddress;
|
||||
});
|
||||
|
||||
register('parity_accountsInfo', () => {
|
||||
return accounts.mapObject(({ name }) => {
|
||||
return { name };
|
||||
});
|
||||
});
|
||||
|
||||
register('parity_allAccountsInfo', () => {
|
||||
return accounts.mapObject(({ name, meta, uuid }) => {
|
||||
return { name, meta, uuid };
|
||||
});
|
||||
});
|
||||
|
||||
register('parity_checkRequest', ([id]) => {
|
||||
return transactions.hash(id) || Promise.resolve(null);
|
||||
});
|
||||
|
||||
register('parity_defaultAccount', () => {
|
||||
return accounts.lastAddress;
|
||||
});
|
||||
|
||||
register('parity_generateSecretPhrase', () => {
|
||||
return randomPhrase(12);
|
||||
});
|
||||
|
||||
register('parity_getNewDappsAddresses', () => {
|
||||
return [];
|
||||
});
|
||||
|
||||
register('parity_hardwareAccountsInfo', () => {
|
||||
return {};
|
||||
});
|
||||
|
||||
register('parity_newAccountFromPhrase', ([phrase, password]) => {
|
||||
return phraseToWallet(phrase)
|
||||
.then((wallet) => {
|
||||
return accounts.create(wallet.secret, password);
|
||||
});
|
||||
});
|
||||
|
||||
register('parity_setAccountMeta', ([address, meta]) => {
|
||||
accounts.get(address).meta = meta;
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
register('parity_setAccountName', ([address, name]) => {
|
||||
accounts.get(address).name = name;
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
register('parity_postTransaction', ([tx]) => {
|
||||
if (!tx.from) {
|
||||
tx.from = accounts.lastAddress;
|
||||
}
|
||||
|
||||
tx.nonce = null;
|
||||
tx.condition = null;
|
||||
|
||||
return transactions.add(tx);
|
||||
});
|
||||
|
||||
register('parity_phraseToAddress', ([phrase]) => {
|
||||
return phraseToAddress(phrase);
|
||||
});
|
||||
|
||||
register('parity_useLocalAccounts', () => {
|
||||
return true;
|
||||
});
|
||||
|
||||
register('parity_listGethAccounts', () => {
|
||||
return [];
|
||||
});
|
||||
|
||||
register('parity_listRecentDapps', () => {
|
||||
return {};
|
||||
});
|
||||
|
||||
register('parity_killAccount', ([address, password]) => {
|
||||
return accounts.remove(address, password);
|
||||
});
|
||||
|
||||
register('signer_confirmRequest', ([id, modify, password]) => {
|
||||
const {
|
||||
gasPrice,
|
||||
gas: gasLimit,
|
||||
from,
|
||||
to,
|
||||
value,
|
||||
data
|
||||
} = Object.assign(transactions.get(id), modify);
|
||||
|
||||
return this
|
||||
.rpcRequest('parity_nextNonce', [from])
|
||||
.then((nonce) => {
|
||||
const tx = new EthereumTx({
|
||||
nonce,
|
||||
to,
|
||||
data,
|
||||
gasLimit: inNumber16(gasLimit),
|
||||
gasPrice: inNumber16(gasPrice),
|
||||
value: inNumber16(value)
|
||||
});
|
||||
const account = accounts.get(from);
|
||||
|
||||
tx.sign(account.decryptPrivateKey(password));
|
||||
|
||||
const serializedTx = `0x${tx.serialize().toString('hex')}`;
|
||||
|
||||
return this.rpcRequest('eth_sendRawTransaction', [serializedTx]);
|
||||
})
|
||||
.then((hash) => {
|
||||
transactions.confirm(id, hash);
|
||||
|
||||
return {};
|
||||
});
|
||||
});
|
||||
|
||||
register('signer_rejectRequest', ([id]) => {
|
||||
return transactions.reject(id);
|
||||
});
|
||||
|
||||
register('signer_requestsToConfirm', () => {
|
||||
return transactions.requestsToConfirm();
|
||||
});
|
||||
}
|
||||
}
|
123
js/src/api/local/transactions.js
Normal file
123
js/src/api/local/transactions.js
Normal file
@ -0,0 +1,123 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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 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. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { toHex } from '../util/format';
|
||||
import { TransportError } from '../transport';
|
||||
|
||||
const AWAITING = Symbol('awaiting');
|
||||
const CONFIRMED = Symbol('confirmed');
|
||||
const REJECTED = Symbol('rejected');
|
||||
|
||||
class Transactions {
|
||||
constructor () {
|
||||
this.reset();
|
||||
}
|
||||
|
||||
// should only really be needed in the constructor and tests
|
||||
reset () {
|
||||
this._id = 1;
|
||||
this._states = {};
|
||||
}
|
||||
|
||||
nextId () {
|
||||
return toHex(this._id++);
|
||||
}
|
||||
|
||||
add (tx) {
|
||||
const id = this.nextId();
|
||||
|
||||
this._states[id] = {
|
||||
status: AWAITING,
|
||||
transaction: tx
|
||||
};
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
get (id) {
|
||||
const state = this._states[id];
|
||||
|
||||
if (!state || state.status !== AWAITING) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return state.transaction;
|
||||
}
|
||||
|
||||
hash (id) {
|
||||
const state = this._states[id];
|
||||
|
||||
if (!state) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (state.status) {
|
||||
case REJECTED:
|
||||
throw TransportError.requestRejected();
|
||||
case CONFIRMED:
|
||||
return state.hash;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
confirm (id, hash) {
|
||||
const state = this._states[id];
|
||||
|
||||
if (!state || state.status !== AWAITING) {
|
||||
throw new Error('Trying to confirm an invalid transaction');
|
||||
}
|
||||
|
||||
state.hash = hash;
|
||||
state.status = CONFIRMED;
|
||||
}
|
||||
|
||||
reject (id) {
|
||||
const state = this._states[id];
|
||||
|
||||
if (!state) {
|
||||
return false;
|
||||
}
|
||||
|
||||
state.status = REJECTED;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
requestsToConfirm () {
|
||||
const result = [];
|
||||
|
||||
Object.keys(this._states).forEach((id) => {
|
||||
const state = this._states[id];
|
||||
|
||||
if (state.status === AWAITING) {
|
||||
result.push({
|
||||
id,
|
||||
origin: {
|
||||
signer: '0x0'
|
||||
},
|
||||
payload: {
|
||||
sendTransaction: state.transaction
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
export default new Transactions();
|
68
js/src/api/local/transactions.spec.js
Normal file
68
js/src/api/local/transactions.spec.js
Normal file
@ -0,0 +1,68 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity 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 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. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import transactions from './transactions';
|
||||
import { TransportError } from '../transport/error';
|
||||
|
||||
const DUMMY_TX = 'dummy';
|
||||
|
||||
describe('api/local/transactions', () => {
|
||||
beforeEach(() => {
|
||||
transactions.reset();
|
||||
});
|
||||
|
||||
it('can store transactions', () => {
|
||||
const id1 = transactions.add(DUMMY_TX);
|
||||
const id2 = transactions.add(DUMMY_TX);
|
||||
const requests = transactions.requestsToConfirm();
|
||||
|
||||
expect(id1).to.be.equal('0x1');
|
||||
expect(id2).to.be.equal('0x2');
|
||||
expect(requests.length).to.be.equal(2);
|
||||
expect(requests[0].id).to.be.equal(id1);
|
||||
expect(requests[1].id).to.be.equal(id2);
|
||||
expect(requests[0].payload.sendTransaction).to.be.equal(DUMMY_TX);
|
||||
expect(requests[1].payload.sendTransaction).to.be.equal(DUMMY_TX);
|
||||
});
|
||||
|
||||
it('can confirm transactions', () => {
|
||||
const id1 = transactions.add(DUMMY_TX);
|
||||
const id2 = transactions.add(DUMMY_TX);
|
||||
|
||||
const hash1 = '0x1111111111111111111111111111111111111111';
|
||||
const hash2 = '0x2222222222222222222222222222222222222222';
|
||||
|
||||
transactions.confirm(id1, hash1);
|
||||
transactions.confirm(id2, hash2);
|
||||
|
||||
const requests = transactions.requestsToConfirm();
|
||||
|
||||
expect(requests.length).to.be.equal(0);
|
||||
expect(transactions.hash(id1)).to.be.equal(hash1);
|
||||
expect(transactions.hash(id2)).to.be.equal(hash2);
|
||||
});
|
||||
|
||||
it('can reject transactions', () => {
|
||||
const id = transactions.add(DUMMY_TX);
|
||||
|
||||
transactions.reject(id);
|
||||
|
||||
const requests = transactions.requestsToConfirm();
|
||||
|
||||
expect(requests.length).to.be.equal(0);
|
||||
expect(() => transactions.hash(id)).to.throw(TransportError);
|
||||
});
|
||||
});
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user