From c8809b3396ff3295ccd3e1934277a62c686fc9e8 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Wed, 26 Oct 2016 13:44:38 +0200 Subject: [PATCH 001/192] Add inject to "bundle everything" list (#2871) * Add inject to "bundle everything" list * Fixes the `build-server` script // Updates Webpack config (#2872) * New Webpack config file for libraries * Added `parity-utils` path * Removed parity in CommonChunks prod --- js/build-server.js | 25 +++++++++- js/package.json | 17 ++++--- js/src/dapps/basiccoin.html | 2 +- js/src/dapps/gavcoin.html | 2 +- js/src/dapps/githubhint.html | 2 +- js/src/dapps/registry.html | 2 +- js/src/dapps/signaturereg.html | 2 +- js/src/dapps/tokenreg.html | 2 +- js/src/dev.parity.html | 2 +- js/src/dev.web3.html | 2 +- js/src/environment/index.js | 8 ++- js/src/{inject.js => web3.js} | 0 js/webpack.config.js | 27 +++++------ js/webpack.libraries.js | 89 ++++++++++++++++++++++++++++++++++ 14 files changed, 151 insertions(+), 31 deletions(-) rename js/src/{inject.js => web3.js} (100%) create mode 100644 js/webpack.libraries.js diff --git a/js/build-server.js b/js/build-server.js index 797e89183..9153f5ed2 100644 --- a/js/build-server.js +++ b/js/build-server.js @@ -24,17 +24,38 @@ var express = require('express'); var proxy = require('http-proxy-middleware'); var app = express(); +var wsProxy = proxy('ws://127.0.0.1:8180', { changeOrigin: true }); -app.use(express.static('build')); +app.use(express.static('.build')); app.use('/api/*', proxy({ target: 'http://127.0.0.1:8080', changeOrigin: true })); +app.use('/app/*', proxy({ + target: 'http://127.0.0.1:8080', + changeOrigin: true, + pathRewrite: { + '^/app': '' + } +})); + +app.use('/parity-utils/*', proxy({ + target: 'http://127.0.0.1:3000', + changeOrigin: true, + pathRewrite: { + '^/parity-utils': '' + } +})); + app.use('/rpc/*', proxy({ target: 'http://127.0.0.1:8080', changeOrigin: true })); -app.listen(3000); +app.use(wsProxy); + +var server = app.listen(3000); + +server.on('upgrade', wsProxy.upgrade); diff --git a/js/package.json b/js/package.json index d84b21d25..8992c7fa6 100644 --- a/js/package.json +++ b/js/package.json @@ -23,17 +23,22 @@ "Promise" ], "scripts": { - "build": "npm run build:dll && npm run build:app", + "build": "npm run build:dll && npm run build:app && npm run build:lib", "build:app": "webpack --progress", - "build:dll": "webpack --config webpack.vendor.js --progress", - "ci:build": "npm run ci:build:dll && npm run ci:build:app", + "build:lib": "webpack --config webpack.libraries --progress", + "build:dll": "webpack --config webpack.vendor --progress", + + "ci:build": "npm run ci:build:dll && npm run ci:build:dll && npm run ci:build:app", "ci:build:app": "NODE_ENV=production webpack", - "ci:build:dll": "NODE_ENV=production webpack --config webpack.vendor.js", + "ci:build:lib": "NODE_ENV=production webpack --config webpack.libraries", + "ci:build:dll": "NODE_ENV=production webpack --config webpack.vendor", + + "start": "npm install && npm run build:dll && npm run start:app", + "start:app": "webpack-dev-server -d --history-api-fallback --open --hot --inline --progress --colors --port 3000", + "clean": "rm -rf ./build ./coverage", "coveralls": "npm run testCoverage && coveralls < coverage/lcov.info", "lint": "eslint --ignore-path .gitignore ./src/", - "start": "npm install && npm run build:dll && npm run start:app", - "start:app": "webpack-dev-server -d --history-api-fallback --open --hot --inline --progress --colors --port 3000", "test": "mocha 'src/**/*.spec.js'", "test:coverage": "istanbul cover _mocha -- 'src/**/*.spec.js'", "test:e2e": "mocha 'src/**/*.e2e.js'" diff --git a/js/src/dapps/basiccoin.html b/js/src/dapps/basiccoin.html index 9bcc368f3..7ac5cb3cb 100644 --- a/js/src/dapps/basiccoin.html +++ b/js/src/dapps/basiccoin.html @@ -10,7 +10,7 @@
- + diff --git a/js/src/dapps/gavcoin.html b/js/src/dapps/gavcoin.html index 928310a52..f777f2920 100644 --- a/js/src/dapps/gavcoin.html +++ b/js/src/dapps/gavcoin.html @@ -10,7 +10,7 @@
- + diff --git a/js/src/dapps/githubhint.html b/js/src/dapps/githubhint.html index 0084dd051..085b15953 100644 --- a/js/src/dapps/githubhint.html +++ b/js/src/dapps/githubhint.html @@ -10,7 +10,7 @@
- + diff --git a/js/src/dapps/registry.html b/js/src/dapps/registry.html index 21b09dc12..83c5e8c9b 100644 --- a/js/src/dapps/registry.html +++ b/js/src/dapps/registry.html @@ -10,7 +10,7 @@
- + diff --git a/js/src/dapps/signaturereg.html b/js/src/dapps/signaturereg.html index 3f74be28a..be62400d2 100644 --- a/js/src/dapps/signaturereg.html +++ b/js/src/dapps/signaturereg.html @@ -10,7 +10,7 @@
- + diff --git a/js/src/dapps/tokenreg.html b/js/src/dapps/tokenreg.html index ecb03d005..bcf04e298 100644 --- a/js/src/dapps/tokenreg.html +++ b/js/src/dapps/tokenreg.html @@ -10,7 +10,7 @@
- + diff --git a/js/src/dev.parity.html b/js/src/dev.parity.html index 20b8e965f..56811f7c2 100644 --- a/js/src/dev.parity.html +++ b/js/src/dev.parity.html @@ -7,6 +7,6 @@ dev::Parity.js - + diff --git a/js/src/dev.web3.html b/js/src/dev.web3.html index 97a47eb72..e55e0109b 100644 --- a/js/src/dev.web3.html +++ b/js/src/dev.web3.html @@ -7,6 +7,6 @@ dev::Web3 - + diff --git a/js/src/environment/index.js b/js/src/environment/index.js index 1dfda77aa..9b95bb0da 100644 --- a/js/src/environment/index.js +++ b/js/src/environment/index.js @@ -19,7 +19,13 @@ import './tests'; -const parityNode = process.env.NODE_ENV === 'production' ? 'http://127.0.0.1:8080' : ''; +const parityNode = ( + process.env.PARITY_URL && `http://${process.env.PARITY_URL}` + ) || ( + process.env.NODE_ENV === 'production' + ? 'http://127.0.0.1:8080' + : '' + ); export { parityNode diff --git a/js/src/inject.js b/js/src/web3.js similarity index 100% rename from js/src/inject.js rename to js/src/web3.js diff --git a/js/webpack.config.js b/js/webpack.config.js index 39415b1a1..c72ef8936 100644 --- a/js/webpack.config.js +++ b/js/webpack.config.js @@ -41,8 +41,9 @@ module.exports = { 'signaturereg': ['./dapps/signaturereg.js'], 'tokenreg': ['./dapps/tokenreg.js'], // library - 'inject': ['./inject.js'], 'parity': ['./parity.js'], + 'inject': ['./web3.js'], + 'web3': ['./web3.js'], // app 'index': ['./index.js'] }, @@ -136,10 +137,6 @@ module.exports = { 'babel?cacheDirectory=true' ] }), - new webpack.DllReferencePlugin({ - context: '.', - manifest: require(`./${DEST}/vendor-manifest.json`) - }), new CopyWebpackPlugin([{ from: './error_pages.css', to: 'styles.css' }], {}), new WebpackErrorNotificationPlugin(), new webpack.DefinePlugin({ @@ -149,6 +146,11 @@ module.exports = { PARITY_URL: JSON.stringify(process.env.PARITY_URL), LOGGING: JSON.stringify(!isProd) } + }), + + new webpack.DllReferencePlugin({ + context: '.', + manifest: require(`./${DEST}/vendor-manifest.json`) }) ]; @@ -164,16 +166,10 @@ module.exports = { if (isProd) { plugins.push( new webpack.optimize.CommonsChunkPlugin({ - chunks: [ 'index' ], + chunks: ['index'], name: 'commons' }) ); - plugins.push( - new webpack.optimize.CommonsChunkPlugin({ - chunks: [ 'parity' ], - name: 'parity' - }) - ); plugins.push(new webpack.optimize.OccurrenceOrderPlugin(false)); plugins.push(new webpack.optimize.DedupePlugin()); @@ -208,8 +204,11 @@ module.exports = { } }, '/parity-utils/*': { - target: 'http://127.0.0.1:8080', - changeOrigin: true + target: 'http://127.0.0.1:3000', + changeOrigin: true, + pathRewrite: { + '^/parity-utils': '' + } }, '/rpc/*': { target: 'http://localhost:8080', diff --git a/js/webpack.libraries.js b/js/webpack.libraries.js new file mode 100644 index 000000000..bf54a933f --- /dev/null +++ b/js/webpack.libraries.js @@ -0,0 +1,89 @@ +// Copyright 2015, 2016 Ethcore (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 . + +// Run with `webpack --config webpack.libraries.js --progress` + +const HappyPack = require('happypack'); +const path = require('path'); +const webpack = require('webpack'); + +const ENV = process.env.NODE_ENV || 'development'; +const isProd = ENV === 'production'; +const DEST = process.env.BUILD_DEST || '.build'; + +module.exports = { + context: path.join(__dirname, './src'), + entry: { + // library + 'inject': ['./web3.js'], + 'web3': ['./web3.js'], + 'parity': ['./parity.js'] + }, + output: { + path: path.join(__dirname, DEST), + filename: '[name].js' + }, + module: { + loaders: [ + { + test: /\.js$/, + exclude: /node_modules/, + loader: 'happypack/loader?id=js' + }, + { + test: /\.json$/, + loaders: ['json'] + }, + { + test: /\.html$/, + loader: 'file?name=[name].[ext]' + } + ] + }, + plugins: (function () { + const plugins = [ + new HappyPack({ + id: 'js', + threads: 4, + loaders: [ 'babel' ] + }), + new webpack.DefinePlugin({ + 'process.env': { + NODE_ENV: JSON.stringify(ENV), + RPC_ADDRESS: JSON.stringify(process.env.RPC_ADDRESS), + PARITY_URL: JSON.stringify(process.env.PARITY_URL), + LOGGING: JSON.stringify(!isProd) + } + }) + ]; + + if (isProd) { + plugins.push(new webpack.optimize.OccurrenceOrderPlugin(false)); + plugins.push(new webpack.optimize.DedupePlugin()); + plugins.push(new webpack.optimize.UglifyJsPlugin({ + screwIe8: true, + compress: { + warnings: false + }, + output: { + comments: false + } + })); + } + + return plugins; + }()) +}; From 436b7c213d50543c89db2bc2c9cfc4241b3be6f1 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Wed, 26 Oct 2016 11:46:34 +0000 Subject: [PATCH 002/192] [ci skip] js-precompiled 20161026-114541 --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 27ac800f5..51e255a24 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1208,7 +1208,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#6be42e2bcf15db125797097df7a2dcfbf7d1e1d2" +source = "git+https://github.com/ethcore/js-precompiled.git#1c5409c9102b62600f9e5e2894221827b24721f8" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] From 5b978be03445ef0d811fdd3cf1ef4193f21f2a70 Mon Sep 17 00:00:00 2001 From: Arkadiy Paronyan Date: Wed, 26 Oct 2016 13:53:47 +0200 Subject: [PATCH 003/192] Get rid of MemoryDB denote (#2881) --- Cargo.lock | 13 ++-- Cargo.toml | 1 + ethcore/src/account_db.rs | 34 +++++----- ethcore/src/migrations/state/v7.rs | 2 +- ethcore/src/snapshot/account.rs | 12 ++-- ethcore/src/snapshot/mod.rs | 6 +- ethcore/src/snapshot/tests/helpers.rs | 3 +- ethcore/src/state/account.rs | 6 +- ethcore/src/state/mod.rs | 6 +- util/Cargo.toml | 2 +- util/rlp/Cargo.toml | 4 +- util/src/hashdb.rs | 13 ++-- util/src/journaldb/archivedb.rs | 28 +++----- util/src/journaldb/earlymergedb.rs | 29 +++----- util/src/journaldb/overlayrecentdb.rs | 56 ++++++---------- util/src/journaldb/refcounteddb.rs | 6 +- util/src/kvdb.rs | 21 +++--- util/src/lib.rs | 2 +- util/src/memorydb.rs | 78 ++++++---------------- util/src/nibbleslice.rs | 19 +++--- util/src/overlaydb.rs | 81 +++++++++++----------- util/src/trie/fatdb.rs | 10 +-- util/src/trie/fatdbmut.rs | 6 +- util/src/trie/journal.rs | 8 +-- util/src/trie/mod.rs | 12 ++-- util/src/trie/node.rs | 84 +++++++++++++++-------- util/src/trie/sectriedb.rs | 6 +- util/src/trie/sectriedbmut.rs | 6 +- util/src/trie/triedb.rs | 85 +++++++++++++----------- util/src/trie/triedbmut.rs | 96 ++++++++++++++------------- 30 files changed, 359 insertions(+), 376 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 51e255a24..9df7200e8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -222,8 +222,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "elastic-array" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" +version = "0.6.0" +source = "git+https://github.com/ethcore/elastic-array#70e4012e691b732c7c4cb04e9232799e6aa268bc" +dependencies = [ + "heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "env_logger" @@ -540,7 +543,7 @@ dependencies = [ "ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", "clippy 0.0.90 (registry+https://github.com/rust-lang/crates.io-index)", - "elastic-array 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "elastic-array 0.6.0 (git+https://github.com/ethcore/elastic-array)", "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "eth-secp256k1 0.5.4 (git+https://github.com/ethcore/rust-secp256k1)", "ethcore-bigint 0.1.1", @@ -1421,7 +1424,7 @@ dependencies = [ name = "rlp" version = "0.1.0" dependencies = [ - "elastic-array 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "elastic-array 0.6.0 (git+https://github.com/ethcore/elastic-array)", "ethcore-bigint 0.1.1", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1954,7 +1957,7 @@ dependencies = [ "checksum deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1614659040e711785ed8ea24219140654da1729f3ec8a47a9719d041112fe7bf" "checksum docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4cc0acb4ce0828c6a5a11d47baa432fe885881c27428c3a4e473e454ffe57a76" "checksum dtoa 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0dd841b58510c9618291ffa448da2e4e0f699d984d436122372f446dae62263d" -"checksum elastic-array 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4bc9250a632e7c001b741eb0ec6cee93c9a5b6d5f1879696a4b94d62b012210a" +"checksum elastic-array 0.6.0 (git+https://github.com/ethcore/elastic-array)" = "" "checksum env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "aba65b63ffcc17ffacd6cf5aa843da7c5a25e3bd4bbe0b7def8b214e411250e5" "checksum eth-secp256k1 0.5.4 (git+https://github.com/ethcore/rust-secp256k1)" = "" "checksum ethabi 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f7b0c53453517f620847be51943db329276ae52f2e210cfc659e81182864be2f" diff --git a/Cargo.toml b/Cargo.toml index 0eec6ff7f..62039696c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -73,6 +73,7 @@ ipc = ["ethcore/ipc", "ethsync/ipc"] jit = ["ethcore/jit"] dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev", "ethcore-rpc/dev", "ethcore-dapps/dev", "ethcore-signer/dev"] json-tests = ["ethcore/json-tests"] +test-heavy = ["ethcore/test-heavy"] stratum = ["ipc"] ethkey-cli = ["ethcore/ethkey-cli"] ethstore-cli = ["ethcore/ethstore-cli"] diff --git a/ethcore/src/account_db.rs b/ethcore/src/account_db.rs index 2d00f8ed5..0761b7fba 100644 --- a/ethcore/src/account_db.rs +++ b/ethcore/src/account_db.rs @@ -96,9 +96,9 @@ impl<'db> HashDB for AccountDB<'db>{ unimplemented!() } - fn get(&self, key: &H256) -> Option<&[u8]> { + fn get(&self, key: &H256) -> Option { if key == &SHA3_NULL_RLP { - return Some(&NULL_RLP_STATIC); + return Some(DBValue::from_slice(&NULL_RLP_STATIC)); } self.db.get(&combine_key(&self.address_hash, key)) } @@ -114,7 +114,7 @@ impl<'db> HashDB for AccountDB<'db>{ unimplemented!() } - fn emplace(&mut self, _key: H256, _value: Bytes) { + fn emplace(&mut self, _key: H256, _value: DBValue) { unimplemented!() } @@ -122,7 +122,7 @@ impl<'db> HashDB for AccountDB<'db>{ unimplemented!() } - fn get_aux(&self, hash: &[u8]) -> Option> { + fn get_aux(&self, hash: &[u8]) -> Option { self.db.get_aux(hash) } } @@ -158,9 +158,9 @@ impl<'db> HashDB for AccountDBMut<'db>{ unimplemented!() } - fn get(&self, key: &H256) -> Option<&[u8]> { + fn get(&self, key: &H256) -> Option { if key == &SHA3_NULL_RLP { - return Some(&NULL_RLP_STATIC); + return Some(DBValue::from_slice(&NULL_RLP_STATIC)); } self.db.get(&combine_key(&self.address_hash, key)) } @@ -178,16 +178,16 @@ impl<'db> HashDB for AccountDBMut<'db>{ } let k = value.sha3(); let ak = combine_key(&self.address_hash, &k); - self.db.emplace(ak, value.to_vec()); + self.db.emplace(ak, DBValue::from_slice(value)); k } - fn emplace(&mut self, key: H256, value: Bytes) { + fn emplace(&mut self, key: H256, value: DBValue) { if key == SHA3_NULL_RLP { return; } let key = combine_key(&self.address_hash, &key); - self.db.emplace(key, value.to_vec()) + self.db.emplace(key, value) } fn remove(&mut self, key: &H256) { @@ -202,7 +202,7 @@ impl<'db> HashDB for AccountDBMut<'db>{ self.db.insert_aux(hash, value); } - fn get_aux(&self, hash: &[u8]) -> Option> { + fn get_aux(&self, hash: &[u8]) -> Option { self.db.get_aux(hash) } @@ -218,9 +218,9 @@ impl<'db> HashDB for Wrapping<'db> { unimplemented!() } - fn get(&self, key: &H256) -> Option<&[u8]> { + fn get(&self, key: &H256) -> Option { if key == &SHA3_NULL_RLP { - return Some(&NULL_RLP_STATIC); + return Some(DBValue::from_slice(&NULL_RLP_STATIC)); } self.0.get(key) } @@ -236,7 +236,7 @@ impl<'db> HashDB for Wrapping<'db> { unimplemented!() } - fn emplace(&mut self, _key: H256, _value: Bytes) { + fn emplace(&mut self, _key: H256, _value: DBValue) { unimplemented!() } @@ -252,9 +252,9 @@ impl<'db> HashDB for WrappingMut<'db>{ unimplemented!() } - fn get(&self, key: &H256) -> Option<&[u8]> { + fn get(&self, key: &H256) -> Option { if key == &SHA3_NULL_RLP { - return Some(&NULL_RLP_STATIC); + return Some(DBValue::from_slice(&NULL_RLP_STATIC)); } self.0.get(key) } @@ -273,7 +273,7 @@ impl<'db> HashDB for WrappingMut<'db>{ self.0.insert(value) } - fn emplace(&mut self, key: H256, value: Bytes) { + fn emplace(&mut self, key: H256, value: DBValue) { if key == SHA3_NULL_RLP { return; } @@ -286,4 +286,4 @@ impl<'db> HashDB for WrappingMut<'db>{ } self.0.remove(key) } -} \ No newline at end of file +} diff --git a/ethcore/src/migrations/state/v7.rs b/ethcore/src/migrations/state/v7.rs index 9af75a8ed..49df041eb 100644 --- a/ethcore/src/migrations/state/v7.rs +++ b/ethcore/src/migrations/state/v7.rs @@ -154,7 +154,7 @@ impl OverlayRecentV7 { // and commit the altered entries. fn migrate_journal(&self, source: Arc, mut batch: Batch, dest: &mut Database) -> Result<(), Error> { if let Some(val) = try!(source.get(None, V7_LATEST_ERA_KEY).map_err(Error::Custom)) { - try!(batch.insert(V7_LATEST_ERA_KEY.into(), val.to_owned(), dest)); + try!(batch.insert(V7_LATEST_ERA_KEY.into(), val.clone().to_vec(), dest)); let mut era = decode::(&val); loop { diff --git a/ethcore/src/snapshot/account.rs b/ethcore/src/snapshot/account.rs index 30f2cd956..7e4585365 100644 --- a/ethcore/src/snapshot/account.rs +++ b/ethcore/src/snapshot/account.rs @@ -19,7 +19,7 @@ use account_db::{AccountDB, AccountDBMut}; use snapshot::Error; -use util::{U256, FixedHash, H256, Bytes, HashDB, SHA3_EMPTY, SHA3_NULL_RLP}; +use util::{U256, FixedHash, H256, Bytes, HashDB, DBValue, SHA3_EMPTY, SHA3_NULL_RLP}; use util::trie::{TrieDB, Trie}; use rlp::{Rlp, RlpStream, Stream, UntrustedRlp, View}; @@ -112,7 +112,7 @@ impl Account { let mut stream = RlpStream::new_list(pairs.len()); for (k, v) in pairs { - stream.begin_list(2).append(&k).append(&v); + stream.begin_list(2).append(&k).append(&&*v); } let pairs_rlp = stream.out(); @@ -130,7 +130,7 @@ impl Account { match acct_db.get(&self.code_hash) { Some(c) => { used_code.insert(self.code_hash.clone()); - account_stream.append(&CodeState::Inline.raw()).append(&c); + account_stream.append(&CodeState::Inline.raw()).append(&&*c); } None => { warn!("code lookup failed during snapshot"); @@ -178,7 +178,7 @@ impl Account { CodeState::Hash => { let code_hash = try!(rlp.val_at(3)); if let Some(code) = code_map.get(&code_hash) { - acct_db.emplace(code_hash.clone(), code.clone()); + acct_db.emplace(code_hash.clone(), DBValue::from_slice(&code)); } (code_hash, None) @@ -226,7 +226,7 @@ mod tests { use snapshot::tests::helpers::fill_storage; use util::sha3::{SHA3_EMPTY, SHA3_NULL_RLP}; - use util::{Address, FixedHash, H256, HashDB}; + use util::{Address, FixedHash, H256, HashDB, DBValue}; use rlp::{UntrustedRlp, View}; use std::collections::{HashSet, HashMap}; @@ -292,7 +292,7 @@ mod tests { { let mut acct_db = AccountDBMut::new(db.as_hashdb_mut(), &addr2); - acct_db.emplace(code_hash.clone(), b"this is definitely code".to_vec()); + acct_db.emplace(code_hash.clone(), DBValue::from_slice(b"this is definitely code")); } let account1 = Account { diff --git a/ethcore/src/snapshot/mod.rs b/ethcore/src/snapshot/mod.rs index 8b032c0e1..223d769d2 100644 --- a/ethcore/src/snapshot/mod.rs +++ b/ethcore/src/snapshot/mod.rs @@ -29,7 +29,7 @@ use engines::Engine; use ids::BlockID; use views::BlockView; -use util::{Bytes, Hashable, HashDB, snappy, U256, Uint}; +use util::{Bytes, Hashable, HashDB, DBValue, snappy, U256, Uint}; use util::memorydb::MemoryDB; use util::Mutex; use util::hash::{FixedHash, H256}; @@ -369,7 +369,7 @@ pub fn chunk_state<'a>(db: &HashDB, root: &H256, writer: &Mutex x.map_or_else(U256::zero, decode), + Ok(x) => x.map_or_else(U256::zero, |v| decode(&*v)), Err(e) => panic!("Encountered potential DB corruption: {}", e), }; let value: H256 = item.into(); @@ -253,8 +253,8 @@ impl Account { self.is_cached() || match db.get(&self.code_hash) { Some(x) => { - self.code_cache = Arc::new(x.to_vec()); self.code_size = Some(x.len()); + self.code_cache = Arc::new(x.to_vec()); true }, _ => { @@ -351,7 +351,7 @@ impl Account { self.code_filth = Filth::Clean; }, (true, false) => { - db.emplace(self.code_hash.clone(), (*self.code_cache).clone()); + db.emplace(self.code_hash.clone(), DBValue::from_slice(&*self.code_cache)); self.code_size = Some(self.code_cache.len()); self.code_filth = Filth::Clean; }, diff --git a/ethcore/src/state/mod.rs b/ethcore/src/state/mod.rs index 2253ed89d..6befcad12 100644 --- a/ethcore/src/state/mod.rs +++ b/ethcore/src/state/mod.rs @@ -408,7 +408,7 @@ impl State { // account is not found in the global cache, get from the DB and insert into local let db = self.factories.trie.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR); let maybe_acc = match db.get(address) { - Ok(acc) => acc.map(Account::from_rlp), + Ok(acc) => acc.map(|v| Account::from_rlp(&v)), Err(e) => panic!("Potential DB corruption encountered: {}", e), }; let r = maybe_acc.as_ref().map_or(H256::new(), |a| { @@ -648,7 +648,7 @@ impl State { // not found in the global cache, get from the DB and insert into local let db = self.factories.trie.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR); let mut maybe_acc = match db.get(a) { - Ok(acc) => acc.map(Account::from_rlp), + Ok(acc) => acc.map(|v| Account::from_rlp(&v)), Err(e) => panic!("Potential DB corruption encountered: {}", e), }; if let Some(ref mut account) = maybe_acc.as_mut() { @@ -680,7 +680,7 @@ impl State { let maybe_acc = if self.db.check_account_bloom(a) { let db = self.factories.trie.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR); let maybe_acc = match db.get(a) { - Ok(Some(acc)) => AccountEntry::new_clean(Some(Account::from_rlp(acc))), + Ok(Some(acc)) => AccountEntry::new_clean(Some(Account::from_rlp(&acc))), Ok(None) => AccountEntry::new_clean(None), Err(e) => panic!("Potential DB corruption encountered: {}", e), }; diff --git a/util/Cargo.toml b/util/Cargo.toml index ce2992fe5..c560a6bb5 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -18,7 +18,7 @@ rocksdb = { git = "https://github.com/ethcore/rust-rocksdb" } lazy_static = "0.2" eth-secp256k1 = { git = "https://github.com/ethcore/rust-secp256k1" } rust-crypto = "0.2.34" -elastic-array = "0.5" +elastic-array = { git = "https://github.com/ethcore/elastic-array" } rlp = { path = "rlp" } heapsize = { version = "0.3", features = ["unstable"] } itertools = "0.4" diff --git a/util/rlp/Cargo.toml b/util/rlp/Cargo.toml index c24e4cc59..7095ddb04 100644 --- a/util/rlp/Cargo.toml +++ b/util/rlp/Cargo.toml @@ -6,7 +6,7 @@ version = "0.1.0" authors = ["Ethcore "] [dependencies] -elastic-array = "0.5" +elastic-array = { git = "https://github.com/ethcore/elastic-array" } ethcore-bigint = { path = "../bigint" } lazy_static = "0.2" -rustc-serialize = "0.3" \ No newline at end of file +rustc-serialize = "0.3" diff --git a/util/src/hashdb.rs b/util/src/hashdb.rs index 55cc2a89e..395a504de 100644 --- a/util/src/hashdb.rs +++ b/util/src/hashdb.rs @@ -16,8 +16,11 @@ //! Database of byte-slices keyed to their Keccak hash. use hash::*; -use bytes::*; use std::collections::HashMap; +use elastic_array::ElasticArray256; + +/// `HashDB` value type. +pub type DBValue = ElasticArray256; /// Trait modelling datastore keyed by a 32-byte Keccak hash. pub trait HashDB: AsHashDB + Send + Sync { @@ -39,7 +42,7 @@ pub trait HashDB: AsHashDB + Send + Sync { /// assert_eq!(m.get(&hash).unwrap(), hello_bytes); /// } /// ``` - fn get(&self, key: &H256) -> Option<&[u8]>; + fn get(&self, key: &H256) -> Option; /// Check for the existance of a hash-key. /// @@ -80,7 +83,7 @@ pub trait HashDB: AsHashDB + Send + Sync { fn insert(&mut self, value: &[u8]) -> H256; /// Like `insert()` , except you provide the key and the data is all moved. - fn emplace(&mut self, key: H256, value: Bytes); + fn emplace(&mut self, key: H256, value: DBValue); /// Remove a datum previously inserted. Insertions can be "owed" such that the same number of `insert()`s may /// happen without the data being eventually being inserted into the DB. @@ -111,7 +114,7 @@ pub trait HashDB: AsHashDB + Send + Sync { } /// Get auxiliary data from hashdb. - fn get_aux(&self, _hash: &[u8]) -> Option> { + fn get_aux(&self, _hash: &[u8]) -> Option { unimplemented!(); } @@ -136,4 +139,4 @@ impl AsHashDB for T { fn as_hashdb_mut(&mut self) -> &mut HashDB { self } -} \ No newline at end of file +} diff --git a/util/src/journaldb/archivedb.rs b/util/src/journaldb/archivedb.rs index efedfb766..940f92375 100644 --- a/util/src/journaldb/archivedb.rs +++ b/util/src/journaldb/archivedb.rs @@ -65,8 +65,8 @@ impl ArchiveDB { Self::new(backing, None) } - fn payload(&self, key: &H256) -> Option { - self.backing.get(self.column, key).expect("Low-level database error. Some issue with your hard disk?").map(|v| v.to_vec()) + fn payload(&self, key: &H256) -> Option { + self.backing.get(self.column, key).expect("Low-level database error. Some issue with your hard disk?") } } @@ -85,19 +85,12 @@ impl HashDB for ArchiveDB { ret } - fn get(&self, key: &H256) -> Option<&[u8]> { + fn get(&self, key: &H256) -> Option { let k = self.overlay.raw(key); - match k { - Some((d, rc)) if rc > 0 => Some(d), - _ => { - if let Some(x) = self.payload(key) { - Some(self.overlay.denote(key, x).0) - } - else { - None - } - } + if let Some((d, rc)) = k { + if rc > 0 { return Some(d); } } + self.payload(key) } fn contains(&self, key: &H256) -> bool { @@ -108,7 +101,7 @@ impl HashDB for ArchiveDB { self.overlay.insert(value) } - fn emplace(&mut self, key: H256, value: Bytes) { + fn emplace(&mut self, key: H256, value: DBValue) { self.overlay.emplace(key, value); } @@ -120,7 +113,7 @@ impl HashDB for ArchiveDB { self.overlay.insert_aux(hash, value); } - fn get_aux(&self, hash: &[u8]) -> Option> { + fn get_aux(&self, hash: &[u8]) -> Option { if let Some(res) = self.overlay.get_aux(hash) { return Some(res) } @@ -130,7 +123,6 @@ impl HashDB for ArchiveDB { self.backing.get(self.column, &db_hash) .expect("Low-level database error. Some issue with your hard disk?") - .map(|v| v.to_vec()) } fn remove_aux(&mut self, hash: &[u8]) { @@ -396,7 +388,7 @@ mod tests { let mut jdb = new_db(&dir); // history is 1 let foo = jdb.insert(b"foo"); - jdb.emplace(bar.clone(), b"bar".to_vec()); + jdb.emplace(bar.clone(), DBValue::from_slice(b"bar")); jdb.commit_batch(0, &b"0".sha3(), None).unwrap(); foo }; @@ -497,7 +489,7 @@ mod tests { let key = jdb.insert(b"dog"); jdb.inject_batch().unwrap(); - assert_eq!(jdb.get(&key).unwrap(), b"dog"); + assert_eq!(jdb.get(&key).unwrap(), DBValue::from_slice(b"dog")); jdb.remove(&key); jdb.inject_batch().unwrap(); diff --git a/util/src/journaldb/earlymergedb.rs b/util/src/journaldb/earlymergedb.rs index 0f7c097b8..1e782c580 100644 --- a/util/src/journaldb/earlymergedb.rs +++ b/util/src/journaldb/earlymergedb.rs @@ -150,7 +150,7 @@ impl EarlyMergeDB { backing.get(col, &Self::morph_key(key, 0)).expect("Low-level database error. Some issue with your hard disk?").is_some() } - fn insert_keys(inserts: &[(H256, Bytes)], backing: &Database, col: Option, refs: &mut HashMap, batch: &mut DBTransaction, trace: bool) { + fn insert_keys(inserts: &[(H256, DBValue)], backing: &Database, col: Option, refs: &mut HashMap, batch: &mut DBTransaction, trace: bool) { for &(ref h, ref d) in inserts { if let Some(c) = refs.get_mut(h) { // already counting. increment. @@ -268,8 +268,8 @@ impl EarlyMergeDB { } } - fn payload(&self, key: &H256) -> Option { - self.backing.get(self.column, key).expect("Low-level database error. Some issue with your hard disk?").map(|v| v.to_vec()) + fn payload(&self, key: &H256) -> Option { + self.backing.get(self.column, key).expect("Low-level database error. Some issue with your hard disk?") } fn read_refs(db: &Database, col: Option) -> (Option, HashMap) { @@ -317,19 +317,12 @@ impl HashDB for EarlyMergeDB { ret } - fn get(&self, key: &H256) -> Option<&[u8]> { + fn get(&self, key: &H256) -> Option { let k = self.overlay.raw(key); - match k { - Some((d, rc)) if rc > 0 => Some(d), - _ => { - if let Some(x) = self.payload(key) { - Some(self.overlay.denote(key, x).0) - } - else { - None - } - } + if let Some((d, rc)) = k { + if rc > 0 { return Some(d) } } + self.payload(key) } fn contains(&self, key: &H256) -> bool { @@ -339,7 +332,7 @@ impl HashDB for EarlyMergeDB { fn insert(&mut self, value: &[u8]) -> H256 { self.overlay.insert(value) } - fn emplace(&mut self, key: H256, value: Bytes) { + fn emplace(&mut self, key: H256, value: DBValue) { self.overlay.emplace(key, value); } fn remove(&mut self, key: &H256) { @@ -413,7 +406,7 @@ impl JournalDB for EarlyMergeDB { .iter() .filter_map(|(k, &(_, c))| if c < 0 {Some(k.clone())} else {None}) .collect(); - let inserts: Vec<(H256, Bytes)> = drained + let inserts: Vec<(H256, _)> = drained .into_iter() .filter_map(|(k, (v, r))| if r > 0 { assert!(r == 1); Some((k, v)) } else { assert!(r >= -1); None }) .collect(); @@ -832,7 +825,7 @@ mod tests { let mut jdb = new_db(&dir); // history is 1 let foo = jdb.insert(b"foo"); - jdb.emplace(bar.clone(), b"bar".to_vec()); + jdb.emplace(bar.clone(), DBValue::from_slice(b"bar")); jdb.commit_batch(0, &b"0".sha3(), None).unwrap(); assert!(jdb.can_reconstruct_refs()); foo @@ -1088,7 +1081,7 @@ mod tests { let key = jdb.insert(b"dog"); jdb.inject_batch().unwrap(); - assert_eq!(jdb.get(&key).unwrap(), b"dog"); + assert_eq!(jdb.get(&key).unwrap(), DBValue::from_slice(b"dog")); jdb.remove(&key); jdb.inject_batch().unwrap(); diff --git a/util/src/journaldb/overlayrecentdb.rs b/util/src/journaldb/overlayrecentdb.rs index e58fe2522..83868d06b 100644 --- a/util/src/journaldb/overlayrecentdb.rs +++ b/util/src/journaldb/overlayrecentdb.rs @@ -67,7 +67,7 @@ pub struct OverlayRecentDB { #[derive(PartialEq)] struct JournalOverlay { backing_overlay: MemoryDB, // Nodes added in the history period - pending_overlay: H256FastMap, // Nodes being transfered from backing_overlay to backing db + pending_overlay: H256FastMap, // Nodes being transfered from backing_overlay to backing db journal: HashMap>, latest_era: Option, earliest_era: Option, @@ -130,7 +130,7 @@ impl OverlayRecentDB { journal_overlay.latest_era == reconstructed.latest_era } - fn payload(&self, key: &H256) -> Option { + fn payload(&self, key: &H256) -> Option { self.backing.get(self.column, key).expect("Low-level database error. Some issue with your hard disk?") } @@ -160,8 +160,8 @@ impl OverlayRecentDB { let mut inserted_keys = Vec::new(); for r in insertions.iter() { let k: H256 = r.val_at(0); - let v: Bytes = r.val_at(1); - overlay.emplace(to_short_key(&k), v); + let v = r.at(1).data(); + overlay.emplace(to_short_key(&k), DBValue::from_slice(v)); inserted_keys.push(k); count += 1; } @@ -229,7 +229,7 @@ impl JournalDB for OverlayRecentDB { let journal_overlay = self.journal_overlay.read(); let key = to_short_key(key); journal_overlay.backing_overlay.get(&key).map(|v| v.to_vec()) - .or_else(|| journal_overlay.pending_overlay.get(&key).cloned()) + .or_else(|| journal_overlay.pending_overlay.get(&key).map(|d| d.clone().to_vec())) .or_else(|| self.backing.get_by_prefix(self.column, &key[0..DB_PREFIX_LEN]).map(|b| b.to_vec())) } @@ -255,7 +255,7 @@ impl JournalDB for OverlayRecentDB { for (k, v) in insertions { r.begin_list(2); r.append(&k); - r.append(&v); + r.append(&&*v); journal_overlay.backing_overlay.emplace(to_short_key(&k), v); } r.append(&removed_keys); @@ -284,7 +284,7 @@ impl JournalDB for OverlayRecentDB { let mut ops = 0; // apply old commits' details if let Some(ref mut records) = journal_overlay.journal.get_mut(&end_era) { - let mut canon_insertions: Vec<(H256, Bytes)> = Vec::new(); + let mut canon_insertions: Vec<(H256, DBValue)> = Vec::new(); let mut canon_deletions: Vec = Vec::new(); let mut overlay_deletions: Vec = Vec::new(); let mut index = 0usize; @@ -301,7 +301,7 @@ impl JournalDB for OverlayRecentDB { for h in &journal.insertions { if let Some((d, rc)) = journal_overlay.backing_overlay.raw(&to_short_key(h)) { if rc > 0 { - canon_insertions.push((h.clone(), d.to_owned())); //TODO: optimize this to avoid data copy + canon_insertions.push((h.clone(), d)); //TODO: optimize this to avoid data copy } } } @@ -386,32 +386,18 @@ impl HashDB for OverlayRecentDB { ret } - fn get(&self, key: &H256) -> Option<&[u8]> { + fn get(&self, key: &H256) -> Option { let k = self.transaction_overlay.raw(key); - match k { - Some((d, rc)) if rc > 0 => Some(d), - _ => { - let v = { - let journal_overlay = self.journal_overlay.read(); - let key = to_short_key(key); - journal_overlay.backing_overlay.get(&key).map(|v| v.to_vec()) - .or_else(|| journal_overlay.pending_overlay.get(&key).cloned()) - }; - match v { - Some(x) => { - Some(self.transaction_overlay.denote(key, x).0) - } - _ => { - if let Some(x) = self.payload(key) { - Some(self.transaction_overlay.denote(key, x).0) - } - else { - None - } - } - } - } + if let Some((d, rc)) = k { + if rc > 0 { return Some(d) } } + let v = { + let journal_overlay = self.journal_overlay.read(); + let key = to_short_key(key); + journal_overlay.backing_overlay.get(&key) + .or_else(|| journal_overlay.pending_overlay.get(&key).cloned()) + }; + v.or_else(|| self.payload(key)) } fn contains(&self, key: &H256) -> bool { @@ -421,7 +407,7 @@ impl HashDB for OverlayRecentDB { fn insert(&mut self, value: &[u8]) -> H256 { self.transaction_overlay.insert(value) } - fn emplace(&mut self, key: H256, value: Bytes) { + fn emplace(&mut self, key: H256, value: DBValue) { self.transaction_overlay.emplace(key, value); } fn remove(&mut self, key: &H256) { @@ -692,7 +678,7 @@ mod tests { let mut jdb = new_db(&dir); // history is 1 let foo = jdb.insert(b"foo"); - jdb.emplace(bar.clone(), b"bar".to_vec()); + jdb.emplace(bar.clone(), DBValue::from_slice(b"bar")); jdb.commit_batch(0, &b"0".sha3(), None).unwrap(); assert!(jdb.can_reconstruct_refs()); foo @@ -965,7 +951,7 @@ mod tests { let key = jdb.insert(b"dog"); jdb.inject_batch().unwrap(); - assert_eq!(jdb.get(&key).unwrap(), b"dog"); + assert_eq!(jdb.get(&key).unwrap(), DBValue::from_slice(b"dog")); jdb.remove(&key); jdb.inject_batch().unwrap(); diff --git a/util/src/journaldb/refcounteddb.rs b/util/src/journaldb/refcounteddb.rs index e6a0f5dcc..57621f321 100644 --- a/util/src/journaldb/refcounteddb.rs +++ b/util/src/journaldb/refcounteddb.rs @@ -83,10 +83,10 @@ impl RefCountedDB { impl HashDB for RefCountedDB { fn keys(&self) -> HashMap { self.forward.keys() } - fn get(&self, key: &H256) -> Option<&[u8]> { self.forward.get(key) } + fn get(&self, key: &H256) -> Option { self.forward.get(key) } fn contains(&self, key: &H256) -> bool { self.forward.contains(key) } fn insert(&mut self, value: &[u8]) -> H256 { let r = self.forward.insert(value); self.inserts.push(r.clone()); r } - fn emplace(&mut self, key: H256, value: Bytes) { self.inserts.push(key.clone()); self.forward.emplace(key, value); } + fn emplace(&mut self, key: H256, value: DBValue) { self.inserts.push(key.clone()); self.forward.emplace(key, value); } fn remove(&mut self, key: &H256) { self.removes.push(key.clone()); } } @@ -326,7 +326,7 @@ mod tests { let key = jdb.insert(b"dog"); jdb.inject_batch().unwrap(); - assert_eq!(jdb.get(&key).unwrap(), b"dog"); + assert_eq!(jdb.get(&key).unwrap(), DBValue::from_slice(b"dog")); jdb.remove(&key); jdb.inject_batch().unwrap(); diff --git a/util/src/kvdb.rs b/util/src/kvdb.rs index b37af4dc9..08e856117 100644 --- a/util/src/kvdb.rs +++ b/util/src/kvdb.rs @@ -21,6 +21,7 @@ use common::*; use elastic_array::*; use std::default::Default; use std::path::PathBuf; +use hashdb::DBValue; use rlp::{UntrustedRlp, RlpType, View, Compressible}; use rocksdb::{DB, Writable, WriteBatch, WriteOptions, IteratorMode, DBIterator, Options, DBCompactionStyle, BlockBasedOptions, Direction, Cache, Column, ReadOptions}; @@ -43,12 +44,12 @@ enum DBOp { Insert { col: Option, key: ElasticArray32, - value: Bytes, + value: DBValue, }, InsertCompressed { col: Option, key: ElasticArray32, - value: Bytes, + value: DBValue, }, Delete { col: Option, @@ -71,7 +72,7 @@ impl DBTransaction { self.ops.push(DBOp::Insert { col: col, key: ekey, - value: value.to_vec(), + value: DBValue::from_slice(value), }); } @@ -82,7 +83,7 @@ impl DBTransaction { self.ops.push(DBOp::Insert { col: col, key: ekey, - value: value, + value: DBValue::from_vec(value), }); } @@ -94,7 +95,7 @@ impl DBTransaction { self.ops.push(DBOp::InsertCompressed { col: col, key: ekey, - value: value, + value: DBValue::from_vec(value), }); } @@ -110,8 +111,8 @@ impl DBTransaction { } enum KeyState { - Insert(Bytes), - InsertCompressed(Bytes), + Insert(DBValue), + InsertCompressed(DBValue), Delete, } @@ -507,7 +508,7 @@ impl Database { } /// Get value by key. - pub fn get(&self, col: Option, key: &[u8]) -> Result, String> { + pub fn get(&self, col: Option, key: &[u8]) -> Result, String> { match *self.db.read() { Some(DBAndColumns { ref db, ref cfs }) => { let overlay = &self.overlay.read()[Self::to_overlay_column(col)]; @@ -521,8 +522,8 @@ impl Database { Some(&KeyState::Delete) => Ok(None), None => { col.map_or_else( - || db.get_opt(key, &self.read_opts).map(|r| r.map(|v| v.to_vec())), - |c| db.get_cf_opt(cfs[c as usize], key, &self.read_opts).map(|r| r.map(|v| v.to_vec()))) + || db.get_opt(key, &self.read_opts).map(|r| r.map(|v| DBValue::from_slice(&v))), + |c| db.get_cf_opt(cfs[c as usize], key, &self.read_opts).map(|r| r.map(|v| DBValue::from_slice(&v)))) }, } }, diff --git a/util/src/lib.rs b/util/src/lib.rs index bebb2819a..e362459a6 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -148,7 +148,7 @@ mod timer; pub use common::*; pub use misc::*; pub use hashdb::*; -pub use memorydb::*; +pub use memorydb::MemoryDB; pub use overlaydb::*; pub use journaldb::JournalDB; pub use triehash::*; diff --git a/util/src/memorydb.rs b/util/src/memorydb.rs index d0ecb73d9..338f12b1e 100644 --- a/util/src/memorydb.rs +++ b/util/src/memorydb.rs @@ -24,8 +24,6 @@ use hashdb::*; use heapsize::*; use std::mem; use std::collections::HashMap; - -const STATIC_NULL_RLP: (&'static [u8], i32) = (&[0x80; 1], 1); use std::collections::hash_map::Entry; /// Reference-counted memory-based `HashDB` implementation. @@ -73,8 +71,8 @@ use std::collections::hash_map::Entry; /// ``` #[derive(Default, Clone, PartialEq)] pub struct MemoryDB { - data: H256FastMap<(Bytes, i32)>, - aux: HashMap, + data: H256FastMap<(DBValue, i32)>, + aux: HashMap, } impl MemoryDB { @@ -116,12 +114,12 @@ impl MemoryDB { } /// Return the internal map of hashes to data, clearing the current state. - pub fn drain(&mut self) -> H256FastMap<(Bytes, i32)> { + pub fn drain(&mut self) -> H256FastMap<(DBValue, i32)> { mem::replace(&mut self.data, H256FastMap::default()) } /// Return the internal map of auxiliary data, clearing the current state. - pub fn drain_aux(&mut self) -> HashMap { + pub fn drain_aux(&mut self) -> HashMap { mem::replace(&mut self.aux, HashMap::new()) } @@ -130,25 +128,11 @@ impl MemoryDB { /// /// Even when Some is returned, the data is only guaranteed to be useful /// when the refs > 0. - pub fn raw(&self, key: &H256) -> Option<(&[u8], i32)> { + pub fn raw(&self, key: &H256) -> Option<(DBValue, i32)> { if key == &SHA3_NULL_RLP { - return Some(STATIC_NULL_RLP.clone()); + return Some((DBValue::from_slice(&NULL_RLP_STATIC), 1)); } - self.data.get(key).map(|&(ref val, rc)| (&val[..], rc)) - } - - /// Denote than an existing value has the given key. Used when a key gets removed without - /// a prior insert and thus has a negative reference with no value. - /// - /// May safely be called even if the key's value is known, in which case it will be a no-op. - pub fn denote(&self, key: &H256, value: Bytes) -> (&[u8], i32) { - if self.raw(key) == None { - unsafe { - let p = &self.data as *const H256FastMap<(Bytes, i32)> as *mut H256FastMap<(Bytes, i32)>; - (*p).insert(key.clone(), (value, 0)); - } - } - self.raw(key).expect("entry just inserted into data; qed") + self.data.get(key).cloned() } /// Returns the size of allocated heap memory @@ -170,7 +154,7 @@ impl MemoryDB { entry.get_mut().1 -= 1; }, Entry::Vacant(entry) => { - entry.insert((Bytes::new(), -1)); + entry.insert((DBValue::new(), -1)); } } } @@ -197,13 +181,13 @@ impl MemoryDB { static NULL_RLP_STATIC: [u8; 1] = [0x80; 1]; impl HashDB for MemoryDB { - fn get(&self, key: &H256) -> Option<&[u8]> { + fn get(&self, key: &H256) -> Option { if key == &SHA3_NULL_RLP { - return Some(&NULL_RLP_STATIC); + return Some(DBValue::from_slice(&NULL_RLP_STATIC)); } match self.data.get(key) { - Some(&(ref d, rc)) if rc > 0 => Some(d), + Some(&(ref d, rc)) if rc > 0 => Some(d.clone()), _ => None } } @@ -230,20 +214,20 @@ impl HashDB for MemoryDB { let key = value.sha3(); if match self.data.get_mut(&key) { Some(&mut (ref mut old_value, ref mut rc @ -0x80000000i32 ... 0)) => { - *old_value = value.into(); + *old_value = DBValue::from_slice(value); *rc += 1; false }, Some(&mut (_, ref mut x)) => { *x += 1; false } , None => true, }{ // ... None falls through into... - self.data.insert(key.clone(), (value.into(), 1)); + self.data.insert(key.clone(), (DBValue::from_slice(value), 1)); } key } - fn emplace(&mut self, key: H256, value: Bytes) { - if value == &NULL_RLP { + fn emplace(&mut self, key: H256, value: DBValue) { + if &*value == &NULL_RLP { return; } @@ -269,15 +253,15 @@ impl HashDB for MemoryDB { Some(&mut (_, ref mut x)) => { *x -= 1; false } None => true }{ // ... None falls through into... - self.data.insert(key.clone(), (Bytes::new(), -1)); + self.data.insert(key.clone(), (DBValue::new(), -1)); } } fn insert_aux(&mut self, hash: Vec, value: Vec) { - self.aux.insert(hash, value); + self.aux.insert(hash, DBValue::from_vec(value)); } - fn get_aux(&self, hash: &[u8]) -> Option> { + fn get_aux(&self, hash: &[u8]) -> Option { self.aux.get(hash).cloned() } @@ -286,24 +270,6 @@ impl HashDB for MemoryDB { } } -#[test] -fn memorydb_denote() { - let mut m = MemoryDB::new(); - let hello_bytes = b"Hello world!"; - let hash = m.insert(hello_bytes); - assert_eq!(m.get(&hash).unwrap(), b"Hello world!"); - - for _ in 0..1000 { - let r = H256::random(); - let k = r.sha3(); - let (v, rc) = m.denote(&k, r.to_vec()); - assert_eq!(v, &*r); - assert_eq!(rc, 0); - } - - assert_eq!(m.get(&hash).unwrap(), b"Hello world!"); -} - #[test] fn memorydb_remove_and_purge() { let hello_bytes = b"Hello world!"; @@ -337,12 +303,12 @@ fn consolidate() { main.remove(&remove_key); let insert_key = other.insert(b"arf"); - main.emplace(insert_key, b"arf".to_vec()); + main.emplace(insert_key, DBValue::from_slice(b"arf")); main.consolidate(other); let overlay = main.drain(); - assert_eq!(overlay.get(&remove_key).unwrap(), &(b"doggo".to_vec(), 0)); - assert_eq!(overlay.get(&insert_key).unwrap(), &(b"arf".to_vec(), 2)); -} \ No newline at end of file + assert_eq!(overlay.get(&remove_key).unwrap(), &(DBValue::from_slice(b"doggo"), 0)); + assert_eq!(overlay.get(&insert_key).unwrap(), &(DBValue::from_slice(b"arf"), 2)); +} diff --git a/util/src/nibbleslice.rs b/util/src/nibbleslice.rs index e10def40a..7daec55ca 100644 --- a/util/src/nibbleslice.rs +++ b/util/src/nibbleslice.rs @@ -17,7 +17,7 @@ //! Nibble-orientated view onto byte-slice, allowing nibble-precision offsets. use std::cmp::*; use std::fmt; -use bytes::*; +use elastic_array::ElasticArray36; /// Nibble-orientated view onto byte-slice, allowing nibble-precision offsets. /// @@ -149,9 +149,9 @@ impl<'a, 'view> NibbleSlice<'a> where 'a: 'view { } /// Encode while nibble slice in prefixed hex notation, noting whether it `is_leaf`. - pub fn encoded(&self, is_leaf: bool) -> Bytes { + pub fn encoded(&self, is_leaf: bool) -> ElasticArray36 { let l = self.len(); - let mut r = Bytes::with_capacity(l / 2 + 1); + let mut r = ElasticArray36::new(); let mut i = l % 2; r.push(if i == 1 {0x10 + self.at(0)} else {0} + if is_leaf {0x20} else {0}); while i < l { @@ -163,9 +163,9 @@ impl<'a, 'view> NibbleSlice<'a> where 'a: 'view { /// Encode only the leftmost `n` bytes of the nibble slice in prefixed hex notation, /// noting whether it `is_leaf`. - pub fn encoded_leftmost(&self, n: usize, is_leaf: bool) -> Bytes { + pub fn encoded_leftmost(&self, n: usize, is_leaf: bool) -> ElasticArray36 { let l = min(self.len(), n); - let mut r = Bytes::with_capacity(l / 2 + 1); + let mut r = ElasticArray36::new(); let mut i = l % 2; r.push(if i == 1 {0x10 + self.at(0)} else {0} + if is_leaf {0x20} else {0}); while i < l { @@ -212,6 +212,7 @@ impl<'a> fmt::Debug for NibbleSlice<'a> { #[cfg(test)] mod tests { use super::NibbleSlice; + use elastic_array::ElasticArray36; static D: &'static [u8;3] = &[0x01u8, 0x23, 0x45]; #[test] @@ -254,10 +255,10 @@ mod tests { #[test] fn encoded() { let n = NibbleSlice::new(D); - assert_eq!(n.encoded(false), &[0x00, 0x01, 0x23, 0x45]); - assert_eq!(n.encoded(true), &[0x20, 0x01, 0x23, 0x45]); - assert_eq!(n.mid(1).encoded(false), &[0x11, 0x23, 0x45]); - assert_eq!(n.mid(1).encoded(true), &[0x31, 0x23, 0x45]); + assert_eq!(n.encoded(false), ElasticArray36::from_slice(&[0x00, 0x01, 0x23, 0x45])); + assert_eq!(n.encoded(true), ElasticArray36::from_slice(&[0x20, 0x01, 0x23, 0x45])); + assert_eq!(n.mid(1).encoded(false), ElasticArray36::from_slice(&[0x11, 0x23, 0x45])); + assert_eq!(n.mid(1).encoded(true), ElasticArray36::from_slice(&[0x31, 0x23, 0x45])); } #[test] diff --git a/util/src/overlaydb.rs b/util/src/overlaydb.rs index 4a11961b6..9ebc7d1d4 100644 --- a/util/src/overlaydb.rs +++ b/util/src/overlaydb.rs @@ -18,7 +18,6 @@ use error::*; use hash::*; -use bytes::*; use rlp::*; use hashdb::*; use memorydb::*; @@ -101,21 +100,21 @@ impl OverlayDB { pub fn commit_refs(&self, key: &H256) -> i32 { self.overlay.raw(key).map_or(0, |(_, refs)| refs) } /// Get the refs and value of the given key. - fn payload(&self, key: &H256) -> Option<(Bytes, u32)> { + fn payload(&self, key: &H256) -> Option<(DBValue, u32)> { self.backing.get(self.column, key) .expect("Low-level database error. Some issue with your hard disk?") .map(|d| { let r = Rlp::new(&d); - (r.at(1).as_val(), r.at(0).as_val()) + (DBValue::from_slice(r.at(1).data()), r.at(0).as_val()) }) } /// Put the refs and value of the given key, possibly deleting it from the db. - fn put_payload_in_batch(&self, batch: &mut DBTransaction, key: &H256, payload: (Bytes, u32)) -> bool { + fn put_payload_in_batch(&self, batch: &mut DBTransaction, key: &H256, payload: (DBValue, u32)) -> bool { if payload.1 > 0 { let mut s = RlpStream::new_list(2); s.append(&payload.1); - s.append(&payload.0); + s.append(&&*payload.0); batch.put(self.column, key, s.as_raw()); false } else { @@ -140,29 +139,31 @@ impl HashDB for OverlayDB { } ret } - fn get(&self, key: &H256) -> Option<&[u8]> { + fn get(&self, key: &H256) -> Option { // return ok if positive; if negative, check backing - might be enough references there to make // it positive again. let k = self.overlay.raw(key); - match k { - Some((d, rc)) if rc > 0 => Some(d), - _ => { - let memrc = k.map_or(0, |(_, rc)| rc); - match self.payload(key) { - Some(x) => { - let (d, rc) = x; - if rc as i32 + memrc > 0 { - Some(self.overlay.denote(key, d).0) - } - else { - None - } - } - // Replace above match arm with this once https://github.com/rust-lang/rust/issues/15287 is done. - //Some((d, rc)) if rc + memrc > 0 => Some(d), - _ => None, + let memrc = { + if let Some((d, rc)) = k { + if rc > 0 { return Some(d); } + rc + } else { + 0 + } + }; + match self.payload(key) { + Some(x) => { + let (d, rc) = x; + if rc as i32 + memrc > 0 { + Some(d) + } + else { + None } } + // Replace above match arm with this once https://github.com/rust-lang/rust/issues/15287 is done. + //Some((d, rc)) if rc + memrc > 0 => Some(d), + _ => None, } } fn contains(&self, key: &H256) -> bool { @@ -186,7 +187,7 @@ impl HashDB for OverlayDB { } } fn insert(&mut self, value: &[u8]) -> H256 { self.overlay.insert(value) } - fn emplace(&mut self, key: H256, value: Bytes) { self.overlay.emplace(key, value); } + fn emplace(&mut self, key: H256, value: DBValue) { self.overlay.emplace(key, value); } fn remove(&mut self, key: &H256) { self.overlay.remove(key); } } @@ -211,7 +212,7 @@ fn overlaydb_revert() { fn overlaydb_overlay_insert_and_remove() { let mut trie = OverlayDB::new_temp(); let h = trie.insert(b"hello world"); - assert_eq!(trie.get(&h).unwrap(), b"hello world"); + assert_eq!(trie.get(&h).unwrap(), DBValue::from_slice(b"hello world")); trie.remove(&h); assert_eq!(trie.get(&h), None); } @@ -220,11 +221,11 @@ fn overlaydb_overlay_insert_and_remove() { fn overlaydb_backing_insert_revert() { let mut trie = OverlayDB::new_temp(); let h = trie.insert(b"hello world"); - assert_eq!(trie.get(&h).unwrap(), b"hello world"); + assert_eq!(trie.get(&h).unwrap(), DBValue::from_slice(b"hello world")); trie.commit().unwrap(); - assert_eq!(trie.get(&h).unwrap(), b"hello world"); + assert_eq!(trie.get(&h).unwrap(), DBValue::from_slice(b"hello world")); trie.revert(); - assert_eq!(trie.get(&h).unwrap(), b"hello world"); + assert_eq!(trie.get(&h).unwrap(), DBValue::from_slice(b"hello world")); } #[test] @@ -248,7 +249,7 @@ fn overlaydb_backing_remove_revert() { trie.remove(&h); assert_eq!(trie.get(&h), None); trie.revert(); - assert_eq!(trie.get(&h).unwrap(), b"hello world"); + assert_eq!(trie.get(&h).unwrap(), DBValue::from_slice(b"hello world")); } #[test] @@ -266,29 +267,29 @@ fn overlaydb_negative() { fn overlaydb_complex() { let mut trie = OverlayDB::new_temp(); let hfoo = trie.insert(b"foo"); - assert_eq!(trie.get(&hfoo).unwrap(), b"foo"); + assert_eq!(trie.get(&hfoo).unwrap(), DBValue::from_slice(b"foo")); let hbar = trie.insert(b"bar"); - assert_eq!(trie.get(&hbar).unwrap(), b"bar"); + assert_eq!(trie.get(&hbar).unwrap(), DBValue::from_slice(b"bar")); trie.commit().unwrap(); - assert_eq!(trie.get(&hfoo).unwrap(), b"foo"); - assert_eq!(trie.get(&hbar).unwrap(), b"bar"); + assert_eq!(trie.get(&hfoo).unwrap(), DBValue::from_slice(b"foo")); + assert_eq!(trie.get(&hbar).unwrap(), DBValue::from_slice(b"bar")); trie.insert(b"foo"); // two refs - assert_eq!(trie.get(&hfoo).unwrap(), b"foo"); + assert_eq!(trie.get(&hfoo).unwrap(), DBValue::from_slice(b"foo")); trie.commit().unwrap(); - assert_eq!(trie.get(&hfoo).unwrap(), b"foo"); - assert_eq!(trie.get(&hbar).unwrap(), b"bar"); + assert_eq!(trie.get(&hfoo).unwrap(), DBValue::from_slice(b"foo")); + assert_eq!(trie.get(&hbar).unwrap(), DBValue::from_slice(b"bar")); trie.remove(&hbar); // zero refs - delete assert_eq!(trie.get(&hbar), None); trie.remove(&hfoo); // one ref - keep - assert_eq!(trie.get(&hfoo).unwrap(), b"foo"); + assert_eq!(trie.get(&hfoo).unwrap(), DBValue::from_slice(b"foo")); trie.commit().unwrap(); - assert_eq!(trie.get(&hfoo).unwrap(), b"foo"); + assert_eq!(trie.get(&hfoo).unwrap(), DBValue::from_slice(b"foo")); trie.remove(&hfoo); // zero ref - would delete, but... assert_eq!(trie.get(&hfoo), None); trie.insert(b"foo"); // one ref - keep after all. - assert_eq!(trie.get(&hfoo).unwrap(), b"foo"); + assert_eq!(trie.get(&hfoo).unwrap(), DBValue::from_slice(b"foo")); trie.commit().unwrap(); - assert_eq!(trie.get(&hfoo).unwrap(), b"foo"); + assert_eq!(trie.get(&hfoo).unwrap(), DBValue::from_slice(b"foo")); trie.remove(&hfoo); // zero ref - delete assert_eq!(trie.get(&hfoo), None); trie.commit().unwrap(); // diff --git a/util/src/trie/fatdb.rs b/util/src/trie/fatdb.rs index f4c65a84b..700156429 100644 --- a/util/src/trie/fatdb.rs +++ b/util/src/trie/fatdb.rs @@ -16,7 +16,7 @@ use hash::H256; use sha3::Hashable; -use hashdb::HashDB; +use hashdb::{HashDB, DBValue}; use super::{TrieDB, Trie, TrieDBIterator, TrieItem, Recorder}; /// A `Trie` implementation which hashes keys and uses a generic `HashDB` backing database. @@ -58,7 +58,7 @@ impl<'db> Trie for FatDB<'db> { self.raw.contains(&key.sha3()) } - fn get_recorded<'a, 'b, R: 'b>(&'a self, key: &'b [u8], rec: &'b mut R) -> super::Result> + fn get_recorded<'a, 'b, R: 'b>(&'a self, key: &'b [u8], rec: &'b mut R) -> super::Result> where 'a: 'b, R: Recorder { self.raw.get_recorded(&key.sha3(), rec) @@ -88,7 +88,7 @@ impl<'db> Iterator for FatDBIterator<'db> { self.trie_iterator.next() .map(|res| res.map(|(hash, value)| { - (self.trie.db().get_aux(&hash).expect("Missing fatdb hash"), value) + (self.trie.db().get_aux(&hash).expect("Missing fatdb hash").to_vec(), value) }) ) } @@ -106,6 +106,6 @@ fn fatdb_to_trie() { t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); } let t = FatDB::new(&memdb, &root).unwrap(); - assert_eq!(t.get(&[0x01u8, 0x23]).unwrap().unwrap(), &[0x01u8, 0x23]); - assert_eq!(t.iter().unwrap().map(Result::unwrap).collect::>(), vec![(vec![0x01u8, 0x23], &[0x01u8, 0x23] as &[u8])]); + assert_eq!(t.get(&[0x01u8, 0x23]).unwrap().unwrap(), DBValue::from_slice(&[0x01u8, 0x23])); + assert_eq!(t.iter().unwrap().map(Result::unwrap).collect::>(), vec![(vec![0x01u8, 0x23], DBValue::from_slice(&[0x01u8, 0x23] as &[u8]))]); } diff --git a/util/src/trie/fatdbmut.rs b/util/src/trie/fatdbmut.rs index 3298541b7..fa1c168e8 100644 --- a/util/src/trie/fatdbmut.rs +++ b/util/src/trie/fatdbmut.rs @@ -16,7 +16,7 @@ use hash::H256; use sha3::Hashable; -use hashdb::HashDB; +use hashdb::{HashDB, DBValue}; use super::{TrieDBMut, TrieMut}; /// A mutable `Trie` implementation which hashes keys and uses a generic `HashDB` backing database. @@ -66,7 +66,7 @@ impl<'db> TrieMut for FatDBMut<'db> { self.raw.contains(&key.sha3()) } - fn get<'a, 'key>(&'a self, key: &'key [u8]) -> super::Result> + fn get<'a, 'key>(&'a self, key: &'key [u8]) -> super::Result> where 'a: 'key { self.raw.get(&key.sha3()) @@ -98,5 +98,5 @@ fn fatdb_to_trie() { t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); } let t = TrieDB::new(&memdb, &root).unwrap(); - assert_eq!(t.get(&(&[0x01u8, 0x23]).sha3()).unwrap().unwrap(), &[0x01u8, 0x23]); + assert_eq!(t.get(&(&[0x01u8, 0x23]).sha3()).unwrap().unwrap(), DBValue::from_slice(&[0x01u8, 0x23])); } diff --git a/util/src/trie/journal.rs b/util/src/trie/journal.rs index 4ffd7cf5c..49bd1bf0f 100644 --- a/util/src/trie/journal.rs +++ b/util/src/trie/journal.rs @@ -24,7 +24,7 @@ use hashdb::*; /// Type of operation for the backing database - either a new node or a node deletion. #[derive(Debug)] enum Operation { - New(H256, Bytes), + New(H256, DBValue), Delete(H256), } @@ -52,16 +52,16 @@ impl Journal { /// Given the RLP that encodes a node, append a reference to that node `out` and leave `journal` /// such that the reference is valid, once applied. - pub fn new_node(&mut self, rlp: Bytes, out: &mut RlpStream) { + pub fn new_node(&mut self, rlp: DBValue, out: &mut RlpStream) { if rlp.len() >= 32 { let rlp_sha3 = rlp.sha3(); - trace!("new_node: reference node {:?} => {:?}", rlp_sha3, rlp.pretty()); + trace!("new_node: reference node {:?} => {:?}", rlp_sha3, &*rlp); out.append(&rlp_sha3); self.0.push(Operation::New(rlp_sha3, rlp)); } else { - trace!("new_node: inline node {:?}", rlp.pretty()); + trace!("new_node: inline node {:?}", &*rlp); out.append_raw(&rlp, 1); } } diff --git a/util/src/trie/mod.rs b/util/src/trie/mod.rs index 952eb8894..d4cc04962 100644 --- a/util/src/trie/mod.rs +++ b/util/src/trie/mod.rs @@ -18,7 +18,7 @@ use std::fmt; use hash::H256; -use hashdb::HashDB; +use hashdb::{HashDB, DBValue}; /// Export the standardmap module. pub mod standardmap; @@ -76,7 +76,7 @@ impl fmt::Display for TrieError { pub type Result = ::std::result::Result>; /// Trie-Item type. -pub type TrieItem<'a> = Result<(Vec, &'a [u8])>; +pub type TrieItem<'a> = Result<(Vec, DBValue)>; /// A key-value datastore implemented as a database-backed modified Merkle tree. pub trait Trie { @@ -92,13 +92,13 @@ pub trait Trie { } /// What is the value of the given key in this trie? - fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Result> where 'a: 'key { + fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Result> where 'a: 'key { self.get_recorded(key, &mut recorder::NoOp) } /// Query the value of the given key in this trie while recording visited nodes /// to the given recorder. If the query fails, the nodes passed to the recorder are unspecified. - fn get_recorded<'a, 'b, R: 'b>(&'a self, key: &'b [u8], rec: &'b mut R) -> Result> + fn get_recorded<'a, 'b, R: 'b>(&'a self, key: &'b [u8], rec: &'b mut R) -> Result> where 'a: 'b, R: Recorder; /// Returns an iterator over elements of trie. @@ -119,7 +119,7 @@ pub trait TrieMut { } /// What is the value of the given key in this trie? - fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Result> where 'a: 'key; + fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Result> where 'a: 'key; /// Insert a `key`/`value` pair into the trie. An `empty` value is equivalent to removing /// `key` from the trie. @@ -188,7 +188,7 @@ impl<'db> Trie for TrieKinds<'db> { wrapper!(self, contains, key) } - fn get_recorded<'a, 'b, R: 'b>(&'a self, key: &'b [u8], r: &'b mut R) -> Result> + fn get_recorded<'a, 'b, R: 'b>(&'a self, key: &'b [u8], r: &'b mut R) -> Result> where 'a: 'b, R: Recorder { wrapper!(self, get_recorded, key, r) } diff --git a/util/src/trie/node.rs b/util/src/trie/node.rs index 8e1c55c73..e1f71fdc0 100644 --- a/util/src/trie/node.rs +++ b/util/src/trie/node.rs @@ -14,27 +14,51 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +use elastic_array::ElasticArray36; use nibbleslice::*; use bytes::*; use rlp::*; use super::journal::*; +use hashdb::DBValue; + +/// Partial node key type. +pub type NodeKey = ElasticArray36; /// Type of node in the trie and essential information thereof. -#[derive(Clone, Eq, PartialEq, Debug)] -pub enum Node<'a> { +#[derive(Eq, PartialEq, Debug)] +pub enum Node { /// Null trie node; could be an empty root or an empty branch entry. Empty, /// Leaf node; has key slice and value. Value may not be empty. - Leaf(NibbleSlice<'a>, &'a [u8]), + Leaf(NodeKey, DBValue), /// Extension node; has key slice and node data. Data may not be null. - Extension(NibbleSlice<'a>, &'a [u8]), + Extension(NodeKey, DBValue), /// Branch node; has array of 16 child nodes (each possibly null) and an optional immediate node data. - Branch([&'a [u8]; 16], Option<&'a [u8]>) + Branch([NodeKey; 16], Option) } -impl<'a> Node<'a> { +impl Clone for Node { + fn clone(&self) -> Node { + match *self { + Node::Empty => Node::Empty, + Node::Leaf(ref k, ref v) => Node::Leaf(k.clone(), v.clone()), + Node::Extension(ref k, ref v) => Node::Extension(k.clone(), v.clone()), + Node::Branch(ref k, ref v) => { + let mut branch = [NodeKey::new(), NodeKey::new(), NodeKey::new(), NodeKey::new(), NodeKey::new(), + NodeKey::new(), NodeKey::new(), NodeKey::new(), NodeKey::new(), NodeKey::new(), NodeKey::new(), + NodeKey::new(), NodeKey::new(), NodeKey::new(), NodeKey::new(), NodeKey::new()]; + for i in 0 .. 16 { + branch[i] = k[i].clone(); + } + Node::Branch(branch, v.clone()) + } + } + } +} + +impl Node { /// Decode the `node_rlp` and return the Node. - pub fn decoded(node_rlp: &'a [u8]) -> Node<'a> { + pub fn decoded(node_rlp: &[u8]) -> Node { let r = Rlp::new(node_rlp); match r.prototype() { // either leaf or extension - decode first item with NibbleSlice::??? @@ -43,16 +67,18 @@ impl<'a> Node<'a> { // if extension, second item is a node (either SHA3 to be looked up and // fed back into this function or inline RLP which can be fed back into this function). Prototype::List(2) => match NibbleSlice::from_encoded(r.at(0).data()) { - (slice, true) => Node::Leaf(slice, r.at(1).data()), - (slice, false) => Node::Extension(slice, r.at(1).as_raw()), + (slice, true) => Node::Leaf(slice.encoded(true), DBValue::from_slice(r.at(1).data())), + (slice, false) => Node::Extension(slice.encoded(false), DBValue::from_slice(r.at(1).as_raw())), }, // branch - first 16 are nodes, 17th is a value (or empty). Prototype::List(17) => { - let mut nodes: [&'a [u8]; 16] = [&[], &[], &[], &[], &[], &[], &[], &[], &[], &[], &[], &[], &[], &[], &[], &[]]; + let mut nodes: [NodeKey; 16] = [NodeKey::new(), NodeKey::new(), NodeKey::new(), NodeKey::new(), NodeKey::new(), + NodeKey::new(), NodeKey::new(), NodeKey::new(), NodeKey::new(), NodeKey::new(), NodeKey::new(), + NodeKey::new(), NodeKey::new(), NodeKey::new(), NodeKey::new(), NodeKey::new()]; for i in 0..16 { - nodes[i] = r.at(i).as_raw(); + nodes[i] = NodeKey::from_slice(r.at(i).as_raw()); } - Node::Branch(nodes, if r.at(16).is_empty() { None } else { Some(r.at(16).data()) }) + Node::Branch(nodes, if r.at(16).is_empty() { None } else { Some(DBValue::from_slice(r.at(16).data())) }) }, // an empty branch index. Prototype::Data(0) => Node::Empty, @@ -69,23 +95,23 @@ impl<'a> Node<'a> { match *self { Node::Leaf(ref slice, ref value) => { let mut stream = RlpStream::new_list(2); - stream.append(&slice.encoded(true)); - stream.append(value); + stream.append(&&**slice); + stream.append(&&**value); stream.out() }, - Node::Extension(ref slice, raw_rlp) => { + Node::Extension(ref slice, ref raw_rlp) => { let mut stream = RlpStream::new_list(2); - stream.append(&slice.encoded(false)); - stream.append_raw(raw_rlp, 1); + stream.append(&&**slice); + stream.append_raw(&&*raw_rlp, 1); stream.out() }, Node::Branch(ref nodes, ref value) => { let mut stream = RlpStream::new_list(17); for i in 0..16 { - stream.append_raw(nodes[i], 1); + stream.append_raw(&*nodes[i], 1); } match *value { - Some(n) => { stream.append(&n); }, + Some(ref n) => { stream.append(&&**n); }, None => { stream.append_empty_data(); }, } stream.out() @@ -100,26 +126,26 @@ impl<'a> Node<'a> { /// Encode the node, adding it to `journal` if necessary and return the RLP valid for /// insertion into a parent node. - pub fn encoded_and_added(&self, journal: &mut Journal) -> Bytes { + pub fn encoded_and_added(&self, journal: &mut Journal) -> DBValue { let mut stream = RlpStream::new(); match *self { Node::Leaf(ref slice, ref value) => { stream.begin_list(2); - stream.append(&slice.encoded(true)); - stream.append(value); + stream.append(&&**slice); + stream.append(&&**value); }, - Node::Extension(ref slice, raw_rlp) => { + Node::Extension(ref slice, ref raw_rlp) => { stream.begin_list(2); - stream.append(&slice.encoded(false)); - stream.append_raw(raw_rlp, 1); + stream.append(&&**slice); + stream.append_raw(&&**raw_rlp, 1); }, Node::Branch(ref nodes, ref value) => { stream.begin_list(17); for i in 0..16 { - stream.append_raw(nodes[i], 1); + stream.append_raw(&*nodes[i], 1); } match *value { - Some(n) => { stream.append(&n); }, + Some(ref n) => { stream.append(&&**n); }, None => { stream.append_empty_data(); }, } }, @@ -127,13 +153,13 @@ impl<'a> Node<'a> { stream.append_empty_data(); } } - let node = stream.out(); + let node = DBValue::from_slice(stream.as_raw()); match node.len() { 0 ... 31 => node, _ => { let mut stream = RlpStream::new(); journal.new_node(node, &mut stream); - stream.out() + DBValue::from_slice(stream.as_raw()) } } } diff --git a/util/src/trie/sectriedb.rs b/util/src/trie/sectriedb.rs index d7108dc3e..b1d7bbc0c 100644 --- a/util/src/trie/sectriedb.rs +++ b/util/src/trie/sectriedb.rs @@ -16,7 +16,7 @@ use hash::H256; use sha3::Hashable; -use hashdb::HashDB; +use hashdb::{HashDB, DBValue}; use super::triedb::TrieDB; use super::{Trie, TrieItem, Recorder}; @@ -59,7 +59,7 @@ impl<'db> Trie for SecTrieDB<'db> { self.raw.contains(&key.sha3()) } - fn get_recorded<'a, 'b, R: 'b>(&'a self, key: &'b [u8], rec: &'b mut R) -> super::Result> + fn get_recorded<'a, 'b, R: 'b>(&'a self, key: &'b [u8], rec: &'b mut R) -> super::Result> where 'a: 'b, R: Recorder { self.raw.get_recorded(&key.sha3(), rec) @@ -79,5 +79,5 @@ fn trie_to_sectrie() { t.insert(&(&[0x01u8, 0x23]).sha3(), &[0x01u8, 0x23]).unwrap(); } let t = SecTrieDB::new(&memdb, &root).unwrap(); - assert_eq!(t.get(&[0x01u8, 0x23]).unwrap().unwrap(), &[0x01u8, 0x23]); + assert_eq!(t.get(&[0x01u8, 0x23]).unwrap().unwrap(), DBValue::from_slice(&[0x01u8, 0x23])); } diff --git a/util/src/trie/sectriedbmut.rs b/util/src/trie/sectriedbmut.rs index d6980d48b..16cc376fe 100644 --- a/util/src/trie/sectriedbmut.rs +++ b/util/src/trie/sectriedbmut.rs @@ -16,7 +16,7 @@ use hash::H256; use sha3::Hashable; -use hashdb::HashDB; +use hashdb::{HashDB, DBValue}; use super::triedbmut::TrieDBMut; use super::TrieMut; @@ -62,7 +62,7 @@ impl<'db> TrieMut for SecTrieDBMut<'db> { self.raw.contains(&key.sha3()) } - fn get<'a, 'key>(&'a self, key: &'key [u8]) -> super::Result> + fn get<'a, 'key>(&'a self, key: &'key [u8]) -> super::Result> where 'a: 'key { self.raw.get(&key.sha3()) @@ -90,5 +90,5 @@ fn sectrie_to_trie() { t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); } let t = TrieDB::new(&memdb, &root).unwrap(); - assert_eq!(t.get(&(&[0x01u8, 0x23]).sha3()).unwrap().unwrap(), &[0x01u8, 0x23]); + assert_eq!(t.get(&(&[0x01u8, 0x23]).sha3()).unwrap().unwrap(), DBValue::from_slice(&[0x01u8, 0x23])); } diff --git a/util/src/trie/triedb.rs b/util/src/trie/triedb.rs index 72604892d..ad1e509a0 100644 --- a/util/src/trie/triedb.rs +++ b/util/src/trie/triedb.rs @@ -44,7 +44,7 @@ use super::{Trie, TrieItem, TrieError}; /// TrieDBMut::new(&mut memdb, &mut root).insert(b"foo", b"bar").unwrap(); /// let t = TrieDB::new(&memdb, &root).unwrap(); /// assert!(t.contains(b"foo").unwrap()); -/// assert_eq!(t.get(b"foo").unwrap().unwrap(), b"bar"); +/// assert_eq!(t.get(b"foo").unwrap().unwrap(), DBValue::from_slice(b"bar")); /// assert!(t.db_items_remaining().unwrap().is_empty()); /// } /// ``` @@ -119,8 +119,8 @@ impl<'db> TrieDB<'db> { }; match node { - Node::Extension(_, payload) => try!(handle_payload(payload)), - Node::Branch(payloads, _) => for payload in &payloads { try!(handle_payload(payload)) }, + Node::Extension(_, ref payload) => try!(handle_payload(payload)), + Node::Branch(ref payloads, _) => for payload in payloads { try!(handle_payload(payload)) }, _ => {}, } @@ -129,18 +129,18 @@ impl<'db> TrieDB<'db> { /// Get the root node's RLP. fn root_node(&self, r: &mut R) -> super::Result { - self.root_data(r).map(Node::decoded) + self.root_data(r).map(|d| Node::decoded(&d)) } /// Get the data of the root node. - fn root_data<'a, R: 'a + Recorder>(&self, r: &'a mut R) -> super::Result<&[u8]> { + fn root_data<'a, R: 'a + Recorder>(&self, r: &'a mut R) -> super::Result { self.db.get(self.root).ok_or_else(|| Box::new(TrieError::InvalidStateRoot(*self.root))) - .map(|node| { r.record(self.root, node, 0); node }) + .map(|node| { r.record(self.root, &*node, 0); node }) } /// Get the root node as a `Node`. fn get_node<'a, R: 'a + Recorder>(&'db self, node: &'db [u8], r: &'a mut R, depth: u32) -> super::Result { - self.get_raw_or_lookup(node, r, depth).map(Node::decoded) + self.get_raw_or_lookup(node, r, depth).map(|n| Node::decoded(&n)) } /// Indentation helper for `formal_all`. @@ -155,20 +155,20 @@ impl<'db> TrieDB<'db> { fn fmt_all(&self, node: Node, f: &mut fmt::Formatter, deepness: usize) -> fmt::Result { match node { Node::Leaf(slice, value) => try!(writeln!(f, "'{:?}: {:?}.", slice, value.pretty())), - Node::Extension(ref slice, item) => { + Node::Extension(ref slice, ref item) => { try!(write!(f, "'{:?} ", slice)); - if let Ok(node) = self.get_node(item, &mut NoOp, 0) { + if let Ok(node) = self.get_node(&*item, &mut NoOp, 0) { try!(self.fmt_all(node, f, deepness)); } }, Node::Branch(ref nodes, ref value) => { try!(writeln!(f, "")); - if let Some(v) = *value { + if let Some(ref v) = *value { try!(self.fmt_indent(f, deepness + 1)); try!(writeln!(f, "=: {:?}", v.pretty())) } for i in 0..16 { - match self.get_node(nodes[i], &mut NoOp, 0) { + match self.get_node(&*nodes[i], &mut NoOp, 0) { Ok(Node::Empty) => {}, Ok(n) => { try!(self.fmt_indent(f, deepness + 1)); @@ -190,11 +190,11 @@ impl<'db> TrieDB<'db> { } /// Return optional data for a key given as a `NibbleSlice`. Returns `None` if no data exists. - fn do_lookup<'key, R: 'key>(&'db self, key: &NibbleSlice<'key>, r: &'key mut R) -> super::Result> + fn do_lookup<'key, R: 'key>(&'db self, key: &NibbleSlice<'key>, r: &'key mut R) -> super::Result> where 'db: 'key, R: Recorder { let root_rlp = try!(self.root_data(r)); - self.get_from_node(root_rlp, key, r, 1) + self.get_from_node(&root_rlp, key, r, 1) } /// Recursible function to retrieve the value given a `node` and a partial `key`. `None` if no @@ -207,18 +207,23 @@ impl<'db> TrieDB<'db> { key: &NibbleSlice<'key>, r: &'key mut R, d: u32 - ) -> super::Result> where 'db: 'key, R: Recorder { + ) -> super::Result> where 'db: 'key, R: Recorder { match Node::decoded(node) { - Node::Leaf(ref slice, value) if key == slice => Ok(Some(value)), - Node::Extension(ref slice, item) if key.starts_with(slice) => { - let data = try!(self.get_raw_or_lookup(item, r, d)); - self.get_from_node(data, &key.mid(slice.len()), r, d + 1) + Node::Leaf(ref slice, ref value) if NibbleSlice::from_encoded(slice).0 == *key => Ok(Some(value.clone())), + Node::Extension(ref slice, ref item) => { + let slice = &NibbleSlice::from_encoded(slice).0; + if key.starts_with(slice) { + let data = try!(self.get_raw_or_lookup(&*item, r, d)); + self.get_from_node(&data, &key.mid(slice.len()), r, d + 1) + } else { + Ok(None) + } }, - Node::Branch(ref nodes, value) => match key.is_empty() { - true => Ok(value), + Node::Branch(ref nodes, ref value) => match key.is_empty() { + true => Ok(value.clone()), false => { - let node = try!(self.get_raw_or_lookup(nodes[key.at(0) as usize], r, d)); - self.get_from_node(node, &key.mid(1), r, d + 1) + let node = try!(self.get_raw_or_lookup(&*nodes[key.at(0) as usize], r, d)); + self.get_from_node(&node, &key.mid(1), r, d + 1) } }, _ => Ok(None) @@ -228,16 +233,16 @@ impl<'db> TrieDB<'db> { /// Given some node-describing data `node`, return the actual node RLP. /// This could be a simple identity operation in the case that the node is sufficiently small, but /// may require a database lookup. - fn get_raw_or_lookup(&'db self, node: &'db [u8], rec: &mut R, d: u32) -> super::Result<&'db [u8]> { + fn get_raw_or_lookup(&'db self, node: &'db [u8], rec: &mut R, d: u32) -> super::Result { // check if its sha3 + len let r = Rlp::new(node); match r.is_data() && r.size() == 32 { true => { let key = r.as_val::(); self.db.get(&key).ok_or_else(|| Box::new(TrieError::IncompleteDatabase(key))) - .map(|raw| { rec.record(&key, raw, d); raw }) + .map(|raw| { rec.record(&key, &raw, d); raw }) } - false => Ok(node) + false => Ok(DBValue::from_slice(node)) } } } @@ -251,12 +256,12 @@ enum Status { } #[derive(Clone, Eq, PartialEq)] -struct Crumb<'a> { - node: Node<'a>, +struct Crumb { + node: Node, status: Status, } -impl<'a> Crumb<'a> { +impl Crumb { /// Move on to next status in the node's sequence. fn increment(&mut self) { self.status = match (&self.status, &self.node) { @@ -273,7 +278,7 @@ impl<'a> Crumb<'a> { #[derive(Clone)] pub struct TrieDBIterator<'a> { db: &'a TrieDB<'a>, - trail: Vec>, + trail: Vec, key_nibbles: Bytes, } @@ -286,18 +291,18 @@ impl<'a> TrieDBIterator<'a> { key_nibbles: Vec::new(), }; - try!(db.root_data(&mut NoOp).and_then(|root| r.descend(root))); + try!(db.root_data(&mut NoOp).and_then(|root| r.descend(&root))); Ok(r) } /// Descend into a payload. - fn descend(&mut self, d: &'a [u8]) -> super::Result<()> { + fn descend(&mut self, d: &[u8]) -> super::Result<()> { self.trail.push(Crumb { status: Status::Entering, node: try!(self.db.get_node(d, &mut NoOp, 0)), }); match self.trail.last().expect("just pushed item; qed").node { - Node::Leaf(n, _) | Node::Extension(n, _) => { self.key_nibbles.extend(n.iter()); }, + Node::Leaf(ref n, _) | Node::Extension(ref n, _) => { self.key_nibbles.extend(NibbleSlice::from_encoded(n).0.iter()); }, _ => {} } @@ -325,7 +330,7 @@ impl<'a> Iterator for TrieDBIterator<'a> { match n { Node::Leaf(n, _) | Node::Extension(n, _) => { let l = self.key_nibbles.len(); - self.key_nibbles.truncate(l - n.len()); + self.key_nibbles.truncate(l - NibbleSlice::from_encoded(&*n).0.len()); }, Node::Branch(_, _) => { self.key_nibbles.pop(); }, _ => {} @@ -337,19 +342,19 @@ impl<'a> Iterator for TrieDBIterator<'a> { return Some(Ok((self.key(), v))); }, (Status::At, Node::Extension(_, d)) => { - if let Err(e) = self.descend(d) { + if let Err(e) = self.descend(&*d) { return Some(Err(e)); } // continue }, (Status::At, Node::Branch(_, _)) => {}, - (Status::AtChild(i), Node::Branch(children, _)) if children[i].len() > 0 => { + (Status::AtChild(i), Node::Branch(ref children, _)) if children[i].len() > 0 => { match i { 0 => self.key_nibbles.push(0), i => *self.key_nibbles.last_mut() .expect("pushed as 0; moves sequentially; removed afterwards; qed") = i as u8, } - if let Err(e) = self.descend(children[i]) { + if let Err(e) = self.descend(&*children[i]) { return Some(Err(e)); } // continue @@ -373,7 +378,7 @@ impl<'db> Trie for TrieDB<'db> { fn root(&self) -> &H256 { self.root } - fn get_recorded<'a, 'b, R: 'b>(&'a self, key: &'b [u8], rec: &'b mut R) -> super::Result> + fn get_recorded<'a, 'b, R: 'b>(&'a self, key: &'b [u8], rec: &'b mut R) -> super::Result> where 'a: 'b, R: Recorder { self.do_lookup(&NibbleSlice::new(key), rec) @@ -384,7 +389,7 @@ impl<'db> fmt::Debug for TrieDB<'db> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { try!(writeln!(f, "c={:?} [", self.hash_count)); let root_rlp = self.db.get(self.root).expect("Trie root not found!"); - try!(self.fmt_all(Node::decoded(root_rlp), f, 0)); + try!(self.fmt_all(Node::decoded(&root_rlp), f, 0)); writeln!(f, "]") } } @@ -395,7 +400,7 @@ fn iterator() { use super::TrieMut; use super::triedbmut::*; - let d = vec![ &b"A"[..], &b"AA"[..], &b"AB"[..], &b"B"[..] ]; + let d = vec![ DBValue::from_slice(b"A"), DBValue::from_slice(b"AA"), DBValue::from_slice(b"AB"), DBValue::from_slice(b"B") ]; let mut memdb = MemoryDB::new(); let mut root = H256::new(); @@ -407,6 +412,6 @@ fn iterator() { } let t = TrieDB::new(&memdb, &root).unwrap(); - assert_eq!(d.iter().map(|i|i.to_vec()).collect::>(), t.iter().unwrap().map(|x| x.unwrap().0).collect::>()); + assert_eq!(d.iter().map(|i| i.clone().to_vec()).collect::>(), t.iter().unwrap().map(|x| x.unwrap().0).collect::>()); assert_eq!(d, t.iter().unwrap().map(|x| x.unwrap().1).collect::>()); } diff --git a/util/src/trie/triedbmut.rs b/util/src/trie/triedbmut.rs index e99179e7a..f5940dd7b 100644 --- a/util/src/trie/triedbmut.rs +++ b/util/src/trie/triedbmut.rs @@ -18,12 +18,14 @@ use super::{TrieError, TrieMut}; use super::node::Node as RlpNode; +use super::node::NodeKey; -use ::{Bytes, HashDB, H256}; +use ::{HashDB, H256}; use ::bytes::ToPretty; use ::nibbleslice::NibbleSlice; use ::rlp::{Rlp, RlpStream, View, Stream}; use ::sha3::SHA3_NULL_RLP; +use hashdb::DBValue; use elastic_array::ElasticArray1024; @@ -72,14 +74,14 @@ enum Node { /// A leaf node contains the end of a key and a value. /// This key is encoded from a `NibbleSlice`, meaning it contains /// a flag indicating it is a leaf. - Leaf(Bytes, Bytes), + Leaf(NodeKey, DBValue), /// An extension contains a shared portion of a key and a child node. /// The shared portion is encoded from a `NibbleSlice` meaning it contains /// a flag indicating it is an extension. /// The child node is always a branch. - Extension(Bytes, NodeHandle), + Extension(NodeKey, NodeHandle), /// A branch has up to 16 children and an optional value. - Branch(Box<[Option; 16]>, Option) + Branch(Box<[Option; 16]>, Option) } impl Node { @@ -98,21 +100,18 @@ impl Node { fn from_rlp(rlp: &[u8], db: &HashDB, storage: &mut NodeStorage) -> Self { match RlpNode::decoded(rlp) { RlpNode::Empty => Node::Empty, - RlpNode::Leaf(k, v) => Node::Leaf(k.encoded(true), v.to_owned()), - RlpNode::Extension(partial, cb) => { - let key = partial.encoded(false); - - Node::Extension(key, Self::inline_or_hash(cb, db, storage)) + RlpNode::Leaf(k, v) => Node::Leaf(k, v), + RlpNode::Extension(key, cb) => { + Node::Extension(key, Self::inline_or_hash(&*cb, db, storage)) } - RlpNode::Branch(children_rlp, v) => { - let val = v.map(|x| x.to_owned()); + RlpNode::Branch(children_rlp, val) => { let mut children = empty_children(); for i in 0..16 { - let raw = children_rlp[i]; - let child_rlp = Rlp::new(raw); + let raw = &children_rlp[i]; + let child_rlp = Rlp::new(&*raw); if !child_rlp.is_empty() { - children[i] = Some(Self::inline_or_hash(raw, db, storage)); + children[i] = Some(Self::inline_or_hash(&*raw, db, storage)); } } @@ -134,13 +133,13 @@ impl Node { } Node::Leaf(partial, value) => { let mut stream = RlpStream::new_list(2); - stream.append(&partial); - stream.append(&value); + stream.append(&&*partial); + stream.append(&&*value); stream.drain() } Node::Extension(partial, child) => { let mut stream = RlpStream::new_list(2); - stream.append(&partial); + stream.append(&&*partial); child_cb(child, &mut stream); stream.drain() } @@ -154,7 +153,7 @@ impl Node { } } if let Some(value) = value { - stream.append(&value); + stream.append(&&*value); } else { stream.append_empty_data(); } @@ -276,7 +275,7 @@ impl<'a> Index<&'a StorageHandle> for NodeStorage { /// assert_eq!(*t.root(), ::util::sha3::SHA3_NULL_RLP); /// t.insert(b"foo", b"bar").unwrap(); /// assert!(t.contains(b"foo").unwrap()); -/// assert_eq!(t.get(b"foo").unwrap().unwrap(), b"bar"); +/// assert_eq!(t.get(b"foo").unwrap().unwrap(), DBValue::from_slice(b"bar")); /// t.remove(b"foo").unwrap(); /// assert!(!t.contains(b"foo").unwrap()); /// } @@ -338,7 +337,7 @@ impl<'a> TrieDBMut<'a> { // cache a node by hash fn cache(&mut self, hash: H256) -> super::Result { let node_rlp = try!(self.db.get(&hash).ok_or_else(|| Box::new(TrieError::IncompleteDatabase(hash)))); - let node = Node::from_rlp(node_rlp, &*self.db, &mut self.storage); + let node = Node::from_rlp(&node_rlp, &*self.db, &mut self.storage); Ok(self.storage.alloc(Stored::Cached(node, hash))) } @@ -367,7 +366,7 @@ impl<'a> TrieDBMut<'a> { } // walk the trie, attempting to find the key's node. - fn lookup<'x, 'key>(&'x self, partial: NibbleSlice<'key>, handle: &NodeHandle) -> super::Result> + fn lookup<'x, 'key>(&'x self, partial: NibbleSlice<'key>, handle: &NodeHandle) -> super::Result> where 'x: 'key { match *handle { @@ -376,7 +375,7 @@ impl<'a> TrieDBMut<'a> { Node::Empty => Ok(None), Node::Leaf(ref key, ref value) => { if NibbleSlice::from_encoded(key).0 == partial { - Ok(Some(value)) + Ok(Some(DBValue::from_slice(value))) } else { Ok(None) } @@ -391,7 +390,7 @@ impl<'a> TrieDBMut<'a> { } Node::Branch(ref children, ref value) => { if partial.is_empty() { - Ok(value.as_ref().map(|v| &v[..])) + Ok(value.as_ref().map(|v| DBValue::from_slice(v))) } else { let idx = partial.at(0); match children[idx as usize].as_ref() { @@ -405,28 +404,33 @@ impl<'a> TrieDBMut<'a> { } /// Return optional data for a key given as a `NibbleSlice`. Returns `None` if no data exists. - fn do_db_lookup<'x, 'key>(&'x self, hash: &H256, key: NibbleSlice<'key>) -> super::Result> + fn do_db_lookup<'x, 'key>(&'x self, hash: &H256, key: NibbleSlice<'key>) -> super::Result> where 'x: 'key { self.db.get(hash).ok_or_else(|| Box::new(TrieError::IncompleteDatabase(*hash))) - .and_then(|node_rlp| self.get_from_db_node(node_rlp, key)) + .and_then(|node_rlp| self.get_from_db_node(&node_rlp, key)) } /// Recursible function to retrieve the value given a `node` and a partial `key`. `None` if no /// value exists for the key. /// /// Note: Not a public API; use Trie trait functions. - fn get_from_db_node<'x, 'key>(&'x self, node: &'x [u8], key: NibbleSlice<'key>) -> super::Result> + fn get_from_db_node<'x, 'key>(&'x self, node: &'x [u8], key: NibbleSlice<'key>) -> super::Result> where 'x: 'key { match RlpNode::decoded(node) { - RlpNode::Leaf(ref slice, value) if &key == slice => Ok(Some(value)), - RlpNode::Extension(ref slice, item) if key.starts_with(slice) => { - self.get_from_db_node(try!(self.get_raw_or_lookup(item)), key.mid(slice.len())) + RlpNode::Leaf(ref slice, ref value) if NibbleSlice::from_encoded(slice).0 == key => Ok(Some(value.clone())), + RlpNode::Extension(ref slice, ref item) => { + let slice = &NibbleSlice::from_encoded(slice).0; + if key.starts_with(slice) { + self.get_from_db_node(&try!(self.get_raw_or_lookup(&*item)), key.mid(slice.len())) + } else { + Ok(None) + } }, - RlpNode::Branch(ref nodes, value) => match key.is_empty() { - true => Ok(value), - false => self.get_from_db_node(try!(self.get_raw_or_lookup(nodes[key.at(0) as usize])), key.mid(1)) + RlpNode::Branch(ref nodes, ref value) => match key.is_empty() { + true => Ok(value.clone()), + false => self.get_from_db_node(&try!(self.get_raw_or_lookup(&*nodes[key.at(0) as usize])), key.mid(1)) }, _ => Ok(None), } @@ -435,7 +439,7 @@ impl<'a> TrieDBMut<'a> { /// Given some node-describing data `node`, return the actual node RLP. /// This could be a simple identity operation in the case that the node is sufficiently small, but /// may require a database lookup. - fn get_raw_or_lookup<'x>(&'x self, node: &'x [u8]) -> super::Result<&'x [u8]> { + fn get_raw_or_lookup<'x>(&'x self, node: &'x [u8]) -> super::Result { // check if its sha3 + len let r = Rlp::new(node); match r.is_data() && r.size() == 32 { @@ -443,12 +447,12 @@ impl<'a> TrieDBMut<'a> { let key = r.as_val::(); self.db.get(&key).ok_or_else(|| Box::new(TrieError::IncompleteDatabase(key))) } - false => Ok(node) + false => Ok(DBValue::from_slice(node)) } } /// insert a key, value pair into the trie, creating new nodes if necessary. - fn insert_at(&mut self, handle: NodeHandle, partial: NibbleSlice, value: Bytes) -> super::Result<(StorageHandle, bool)> { + fn insert_at(&mut self, handle: NodeHandle, partial: NibbleSlice, value: DBValue) -> super::Result<(StorageHandle, bool)> { let h = match handle { NodeHandle::InMemory(h) => h, NodeHandle::Hash(h) => try!(self.cache(h)), @@ -463,7 +467,7 @@ impl<'a> TrieDBMut<'a> { /// the insertion inspector. #[cfg_attr(feature = "dev", allow(cyclomatic_complexity))] - fn insert_inspector(&mut self, node: Node, partial: NibbleSlice, value: Bytes) -> super::Result { + fn insert_inspector(&mut self, node: Node, partial: NibbleSlice, value: DBValue) -> super::Result { trace!(target: "trie", "augmented (partial: {:?}, value: {:?})", partial, value.pretty()); Ok(match node { @@ -898,7 +902,7 @@ impl<'a> TrieMut for TrieDBMut<'a> { } } - fn get<'x, 'key>(&'x self, key: &'key [u8]) -> super::Result> where 'x: 'key { + fn get<'x, 'key>(&'x self, key: &'key [u8]) -> super::Result> where 'x: 'key { self.lookup(NibbleSlice::new(key), &self.root_handle) } @@ -911,7 +915,7 @@ impl<'a> TrieMut for TrieDBMut<'a> { trace!(target: "trie", "insert: key={:?}, value={:?}", key.pretty(), value.pretty()); let root_handle = self.root_handle(); - let (new_handle, changed) = try!(self.insert_at(root_handle, NibbleSlice::new(key), value.to_owned())); + let (new_handle, changed) = try!(self.insert_at(root_handle, NibbleSlice::new(key), DBValue::from_slice(value))); trace!(target: "trie", "insert: altered trie={}", changed); self.root_handle = NodeHandle::InMemory(new_handle); @@ -1180,9 +1184,9 @@ mod tests { let mut root = H256::new(); let mut t = TrieDBMut::new(&mut memdb, &mut root); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); - assert_eq!(t.get(&[0x1, 0x23]).unwrap().unwrap(), &[0x1u8, 0x23]); + assert_eq!(t.get(&[0x1, 0x23]).unwrap().unwrap(), DBValue::from_slice(&[0x1u8, 0x23])); t.commit(); - assert_eq!(t.get(&[0x1, 0x23]).unwrap().unwrap(), &[0x1u8, 0x23]); + assert_eq!(t.get(&[0x1, 0x23]).unwrap().unwrap(), DBValue::from_slice(&[0x1u8, 0x23])); } #[test] @@ -1193,14 +1197,14 @@ mod tests { t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); t.insert(&[0xf1u8, 0x23], &[0xf1u8, 0x23]).unwrap(); t.insert(&[0x81u8, 0x23], &[0x81u8, 0x23]).unwrap(); - assert_eq!(t.get(&[0x01, 0x23]).unwrap().unwrap(), &[0x01u8, 0x23]); - assert_eq!(t.get(&[0xf1, 0x23]).unwrap().unwrap(), &[0xf1u8, 0x23]); - assert_eq!(t.get(&[0x81, 0x23]).unwrap().unwrap(), &[0x81u8, 0x23]); + assert_eq!(t.get(&[0x01, 0x23]).unwrap().unwrap(), DBValue::from_slice(&[0x01u8, 0x23])); + assert_eq!(t.get(&[0xf1, 0x23]).unwrap().unwrap(), DBValue::from_slice(&[0xf1u8, 0x23])); + assert_eq!(t.get(&[0x81, 0x23]).unwrap().unwrap(), DBValue::from_slice(&[0x81u8, 0x23])); assert_eq!(t.get(&[0x82, 0x23]), Ok(None)); t.commit(); - assert_eq!(t.get(&[0x01, 0x23]).unwrap().unwrap(), &[0x01u8, 0x23]); - assert_eq!(t.get(&[0xf1, 0x23]).unwrap().unwrap(), &[0xf1u8, 0x23]); - assert_eq!(t.get(&[0x81, 0x23]).unwrap().unwrap(), &[0x81u8, 0x23]); + assert_eq!(t.get(&[0x01, 0x23]).unwrap().unwrap(), DBValue::from_slice(&[0x01u8, 0x23])); + assert_eq!(t.get(&[0xf1, 0x23]).unwrap().unwrap(), DBValue::from_slice(&[0xf1u8, 0x23])); + assert_eq!(t.get(&[0x81, 0x23]).unwrap().unwrap(), DBValue::from_slice(&[0x81u8, 0x23])); assert_eq!(t.get(&[0x82, 0x23]), Ok(None)); } From 0441babb5f665f8691babb69536e0f81f73cdea6 Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac Date: Wed, 26 Oct 2016 13:55:41 +0200 Subject: [PATCH 004/192] Fix empty tags modif. (#2882) --- js/src/modals/EditMeta/editMeta.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/modals/EditMeta/editMeta.js b/js/src/modals/EditMeta/editMeta.js index aef0232a6..afcf9b127 100644 --- a/js/src/modals/EditMeta/editMeta.js +++ b/js/src/modals/EditMeta/editMeta.js @@ -137,7 +137,7 @@ export default class EditMeta extends Component { onTagsInputChange = (value) => { const { meta } = this.state; - const { tags } = meta || []; + const { tags = [] } = meta; const tokens = value.split(/[\s,;]+/); From c9298981f802c0d91290cb5443364dbe17ae9ec6 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 26 Oct 2016 13:57:54 +0200 Subject: [PATCH 005/192] Fix up informant. (#2865) Just ensure as many transactions/blocks are reported as possible. --- parity/informant.rs | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/parity/informant.rs b/parity/informant.rs index 33ad54b3d..9b0b6c754 100644 --- a/parity/informant.rs +++ b/parity/informant.rs @@ -43,6 +43,7 @@ pub struct Informant { net: Option>, last_import: Mutex, skipped: AtomicUsize, + skipped_txs: AtomicUsize, } /// Format byte counts to standard denominations. @@ -80,6 +81,7 @@ impl Informant { net: net, last_import: Mutex::new(Instant::now()), skipped: AtomicUsize::new(0), + skipped_txs: AtomicUsize::new(0), } } @@ -178,13 +180,21 @@ impl ChainNotify for Informant { let mut last_import = self.last_import.lock(); let sync_state = self.sync.as_ref().map(|s| s.status().state); let importing = is_major_importing(sync_state, self.client.queue_info()); - if Instant::now() > *last_import + Duration::from_secs(1) && !importing { + + let ripe = Instant::now() > *last_import + Duration::from_secs(1) && !importing; + let txs_imported = imported.iter() + .take(imported.len() - if ripe {1} else {0}) + .filter_map(|h| self.client.block(BlockID::Hash(h.clone()))) + .map(|b| BlockView::new(&b).transactions_count()) + .sum(); + + if ripe { if let Some(block) = imported.last().and_then(|h| self.client.block(BlockID::Hash(*h))) { let view = BlockView::new(&block); let header = view.header(); let tx_count = view.transactions_count(); let size = block.len(); - let skipped = self.skipped.load(AtomicOrdering::Relaxed); + let (skipped, skipped_txs) = (self.skipped.load(AtomicOrdering::Relaxed) + imported.len() - 1, self.skipped.load(AtomicOrdering::Relaxed) + txs_imported); info!(target: "import", "Imported {} {} ({} txs, {} Mgas, {} ms, {} KiB){}", Colour::White.bold().paint(format!("#{}", header.number())), Colour::White.bold().paint(format!("{}", header.hash())), @@ -192,13 +202,22 @@ impl ChainNotify for Informant { Colour::Yellow.bold().paint(format!("{:.2}", header.gas_used().low_u64() as f32 / 1000000f32)), Colour::Purple.bold().paint(format!("{:.2}", duration as f32 / 1000000f32)), Colour::Blue.bold().paint(format!("{:.2}", size as f32 / 1024f32)), - if skipped > 0 { format!(" + another {} block(s)", Colour::Red.bold().paint(format!("{}", skipped))) } else { String::new() } + if skipped > 0 { + format!(" + another {} block(s) containing {} tx(s)", + Colour::Red.bold().paint(format!("{}", skipped)), + Colour::Red.bold().paint(format!("{}", skipped_txs)) + ) + } else { + String::new() + } ); + self.skipped.store(0, AtomicOrdering::Relaxed); + self.skipped_txs.store(0, AtomicOrdering::Relaxed); *last_import = Instant::now(); } - self.skipped.store(0, AtomicOrdering::Relaxed); } else { self.skipped.fetch_add(imported.len(), AtomicOrdering::Relaxed); + self.skipped_txs.fetch_add(txs_imported, AtomicOrdering::Relaxed); } } } From cdc348d9554b7948834a56d8f635deeebf615bc9 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Wed, 26 Oct 2016 12:03:52 +0000 Subject: [PATCH 006/192] [ci skip] js-precompiled 20161026-120247 --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 9df7200e8..6592df2e2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1211,7 +1211,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#1c5409c9102b62600f9e5e2894221827b24721f8" +source = "git+https://github.com/ethcore/js-precompiled.git#10abc6955255a004351239cd6bd0a917dfe5f443" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] From 909fb1d54e880edbf108065009cccbedf9d256ca Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Wed, 26 Oct 2016 12:15:18 +0000 Subject: [PATCH 007/192] [ci skip] js-precompiled 20161026-121420 --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 6592df2e2..ae87fc1dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1211,7 +1211,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#10abc6955255a004351239cd6bd0a917dfe5f443" +source = "git+https://github.com/ethcore/js-precompiled.git#a9f4d6f0ff2a084cb5425b75430dd0ad45c21700" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] From af27bfe868e1eb123bdb372387fa511aa7f044c0 Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac Date: Wed, 26 Oct 2016 14:59:45 +0200 Subject: [PATCH 008/192] Fixes CI JS precompiled build (#2886) * Add inject to "bundle everything" list * Fixes the `build-server` script // Updates Webpack config (#2872) * New Webpack config file for libraries * Added `parity-utils` path * Removed parity in CommonChunks prod * Fixes CI build --- js/package.json | 2 +- js/webpack.config.js | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/js/package.json b/js/package.json index 8992c7fa6..bafb3efcd 100644 --- a/js/package.json +++ b/js/package.json @@ -28,7 +28,7 @@ "build:lib": "webpack --config webpack.libraries --progress", "build:dll": "webpack --config webpack.vendor --progress", - "ci:build": "npm run ci:build:dll && npm run ci:build:dll && npm run ci:build:app", + "ci:build": "npm run ci:build:dll && npm run ci:build:app && npm run ci:build:lib", "ci:build:app": "NODE_ENV=production webpack", "ci:build:lib": "NODE_ENV=production webpack --config webpack.libraries", "ci:build:dll": "NODE_ENV=production webpack --config webpack.vendor", diff --git a/js/webpack.config.js b/js/webpack.config.js index c72ef8936..51337b9ef 100644 --- a/js/webpack.config.js +++ b/js/webpack.config.js @@ -40,10 +40,6 @@ module.exports = { 'registry': ['./dapps/registry.js'], 'signaturereg': ['./dapps/signaturereg.js'], 'tokenreg': ['./dapps/tokenreg.js'], - // library - 'parity': ['./parity.js'], - 'inject': ['./web3.js'], - 'web3': ['./web3.js'], // app 'index': ['./index.js'] }, From c36202fcf50ed03a79f9f9c280d46afa33be73d3 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Wed, 26 Oct 2016 13:19:04 +0000 Subject: [PATCH 009/192] [ci skip] js-precompiled 20161026-131806 --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index ae87fc1dd..2ffb29a7b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1211,7 +1211,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#a9f4d6f0ff2a084cb5425b75430dd0ad45c21700" +source = "git+https://github.com/ethcore/js-precompiled.git#7e761cb85def0ade260e60cc05f697a0bf845ac9" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] From f024acd3295555458a8aa26c3b3dc124498a1f88 Mon Sep 17 00:00:00 2001 From: Arkadiy Paronyan Date: Wed, 26 Oct 2016 16:14:13 +0200 Subject: [PATCH 010/192] More snapshot fixes and optimizations (#2883) * More snapshot fixes and optimizations * db drop --- ethcore/res/ethereum/frontier.json | 3 +-- ethcore/src/snapshot/mod.rs | 41 ++++++------------------------ ethcore/src/snapshot/service.rs | 3 ++- 3 files changed, 11 insertions(+), 36 deletions(-) diff --git a/ethcore/res/ethereum/frontier.json b/ethcore/res/ethereum/frontier.json index 2ad0f0bec..d5f57defd 100644 --- a/ethcore/res/ethereum/frontier.json +++ b/ethcore/res/ethereum/frontier.json @@ -164,8 +164,7 @@ "enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303", "enode://de471bccee3d042261d52e9bff31458daecc406142b401d4cd848f677479f73104b9fdeb090af9583d3391b7f10cb2ba9e26865dd5fca4fcdc0fb1e3b723c786@54.94.239.50:30303", "enode://1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082@52.74.57.123:30303", - "enode://4cd540b2c3292e17cff39922e864094bf8b0741fcc8c5dcea14957e389d7944c70278d872902e3d0345927f621547efa659013c400865485ab4bfa0c6596936f@zero.parity.io:30303", - "enode://cc92c4c40d612a10c877ca023ef0496c843fbc92b6c6c0d55ce0b863d51d821c4bd70daebb54324a6086374e6dc05708fed39862b275f169cb678e655da9d07d@136.243.154.246:30303" + "enode://4cd540b2c3292e17cff39922e864094bf8b0741fcc8c5dcea14957e389d7944c70278d872902e3d0345927f621547efa659013c400865485ab4bfa0c6596936f@138.201.144.135:30303" ], "accounts": { "0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, diff --git a/ethcore/src/snapshot/mod.rs b/ethcore/src/snapshot/mod.rs index 223d769d2..4fa00f771 100644 --- a/ethcore/src/snapshot/mod.rs +++ b/ethcore/src/snapshot/mod.rs @@ -30,7 +30,6 @@ use ids::BlockID; use views::BlockView; use util::{Bytes, Hashable, HashDB, DBValue, snappy, U256, Uint}; -use util::memorydb::MemoryDB; use util::Mutex; use util::hash::{FixedHash, H256}; use util::journaldb::{self, Algorithm, JournalDB}; @@ -47,7 +46,7 @@ use self::io::SnapshotWriter; use super::state_db::StateDB; use super::state::Account as StateAccount; -use crossbeam::{scope, ScopedJoinHandle}; +use crossbeam::scope; use rand::{Rng, OsRng}; pub use self::error::Error; @@ -421,38 +420,14 @@ impl StateRebuilder { // new code contained within this chunk. let mut chunk_code = HashMap::new(); - // build account tries in parallel. - // Todo [rob] keep a thread pool around so we don't do this per-chunk. - try!(scope(|scope| { - let mut handles = Vec::new(); - for (account_chunk, out_pairs_chunk) in account_fat_rlps.chunks(chunk_size).zip(pairs.chunks_mut(chunk_size)) { - let code_map = &self.code_map; - let handle: ScopedJoinHandle> = scope.spawn(move || { - let mut db = MemoryDB::new(); - let status = try!(rebuild_accounts(&mut db, account_chunk, out_pairs_chunk, code_map)); - - trace!(target: "snapshot", "thread rebuilt {} account tries", account_chunk.len()); - Ok((db, status)) - }); - - handles.push(handle); + for (account_chunk, out_pairs_chunk) in account_fat_rlps.chunks(chunk_size).zip(pairs.chunks_mut(chunk_size)) { + let code_map = &self.code_map; + let status = try!(rebuild_accounts(self.db.as_hashdb_mut(), account_chunk, out_pairs_chunk, code_map)); + chunk_code.extend(status.new_code); + for (addr_hash, code_hash) in status.missing_code { + self.missing_code.entry(code_hash).or_insert_with(Vec::new).push(addr_hash); } - - // consolidate all edits into the main overlay. - for handle in handles { - let (thread_db, status): (MemoryDB, _) = try!(handle.join()); - self.db.consolidate(thread_db); - - chunk_code.extend(status.new_code); - - for (addr_hash, code_hash) in status.missing_code { - self.missing_code.entry(code_hash).or_insert_with(Vec::new).push(addr_hash); - } - } - - Ok::<_, ::error::Error>(()) - })); - + } // patch up all missing code. must be done after collecting all new missing code entries. for (code_hash, code) in chunk_code { for addr_hash in self.missing_code.remove(&code_hash).unwrap_or_else(Vec::new) { diff --git a/ethcore/src/snapshot/service.rs b/ethcore/src/snapshot/service.rs index b3aaa017e..9b66a5cdc 100644 --- a/ethcore/src/snapshot/service.rs +++ b/ethcore/src/snapshot/service.rs @@ -497,7 +497,8 @@ impl Service { match is_done { true => { try!(db.flush().map_err(::util::UtilError::SimpleString)); - self.finalize_restoration(&mut *restoration) + drop(db); + return self.finalize_restoration(&mut *restoration); }, false => Ok(()) } From 6901d087dd0f8d496d1880f993ad043fca5f1227 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Wed, 26 Oct 2016 14:16:03 +0000 Subject: [PATCH 011/192] [ci skip] js-precompiled 20161026-141507 --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 2ffb29a7b..fab068c80 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1211,7 +1211,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#7e761cb85def0ade260e60cc05f697a0bf845ac9" +source = "git+https://github.com/ethcore/js-precompiled.git#49ee3f2dbcccca5f5147d8db45be7e74ba5d0735" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] From a1745624ce046dab9924c0876dc93830650e6096 Mon Sep 17 00:00:00 2001 From: keorn Date: Wed, 26 Oct 2016 15:27:38 +0100 Subject: [PATCH 012/192] enable suicide test (#2893) --- ethcore/src/json_tests/homestead_chain.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ethcore/src/json_tests/homestead_chain.rs b/ethcore/src/json_tests/homestead_chain.rs index d92b8a7f1..37a9d0a21 100644 --- a/ethcore/src/json_tests/homestead_chain.rs +++ b/ethcore/src/json_tests/homestead_chain.rs @@ -37,6 +37,5 @@ declare_test!{BlockchainTests_Homestead_bcUncleTest, "BlockchainTests/Homestead/ declare_test!{BlockchainTests_Homestead_bcValidBlockTest, "BlockchainTests/Homestead/bcValidBlockTest"} declare_test!{BlockchainTests_Homestead_bcWalletTest, "BlockchainTests/Homestead/bcWalletTest"} declare_test!{BlockchainTests_Homestead_bcShanghaiLove, "BlockchainTests/Homestead/bcShanghaiLove"} -// TODO [ToDr] uncomment as soon as eip150 tests are merged to develop branch of ethereum/tests -// declare_test!{BlockchainTests_Homestead_bcSuicideIssue, "BlockchainTests/Homestead/bcSuicideIssue"} +declare_test!{BlockchainTests_Homestead_bcSuicideIssue, "BlockchainTests/Homestead/bcSuicideIssue"} declare_test!{BlockchainTests_Homestead_bcExploitTest, "BlockchainTests/Homestead/bcExploitTest"} From 99fab5cdb9ae4de53e3b9fae83407aa34952850d Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac Date: Wed, 26 Oct 2016 16:27:53 +0200 Subject: [PATCH 013/192] Fix Webpack, again (#2895) * Add inject to "bundle everything" list * Fixes the `build-server` script // Updates Webpack config (#2872) * New Webpack config file for libraries * Added `parity-utils` path * Removed parity in CommonChunks prod * Fixes CI build * Re-Add Libs in Webpack Config --- js/webpack.config.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/js/webpack.config.js b/js/webpack.config.js index 51337b9ef..d253b6592 100644 --- a/js/webpack.config.js +++ b/js/webpack.config.js @@ -40,6 +40,10 @@ module.exports = { 'registry': ['./dapps/registry.js'], 'signaturereg': ['./dapps/signaturereg.js'], 'tokenreg': ['./dapps/tokenreg.js'], + // library + 'inject': ['./web3.js'], + 'web3': ['./web3.js'], + 'parity': ['./parity.js'], // app 'index': ['./index.js'] }, From 215c82d744f772d2170ed3256b4382bb087dea2d Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Wed, 26 Oct 2016 14:43:01 +0000 Subject: [PATCH 014/192] [ci skip] js-precompiled 20161026-144207 --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index fab068c80..fdb80f602 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1211,7 +1211,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#49ee3f2dbcccca5f5147d8db45be7e74ba5d0735" +source = "git+https://github.com/ethcore/js-precompiled.git#9f8baa9d0e54056c41a842b351597d0565beda98" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] From e449477f23000c1a66fa8baa40ae295231b38230 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Wed, 26 Oct 2016 17:04:48 +0200 Subject: [PATCH 015/192] Add mising imgages for local dapps (#2890) --- js/src/views/Dapps/Summary/summary.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/src/views/Dapps/Summary/summary.js b/js/src/views/Dapps/Summary/summary.js index 1989386ea..9cf6a046b 100644 --- a/js/src/views/Dapps/Summary/summary.js +++ b/js/src/views/Dapps/Summary/summary.js @@ -39,8 +39,8 @@ export default class Summary extends Component { } const url = `/app/${app.builtin ? 'global' : 'local'}/${app.url || app.id}`; - const image = app.image - ? + const image = app.image || app.iconUrl + ? :
 
; return ( From f58ccfb4357b394fb3d1f4ca8f229791b09b4045 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Wed, 26 Oct 2016 16:57:31 +0000 Subject: [PATCH 016/192] [ci skip] js-precompiled 20161026-165632 --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index fdb80f602..82edd3623 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1211,7 +1211,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#9f8baa9d0e54056c41a842b351597d0565beda98" +source = "git+https://github.com/ethcore/js-precompiled.git#51b61fdce036bc8c01923fc73206cfa9fc5ec473" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] From 0e14738147643f5dc926c6203a05e222a91997c2 Mon Sep 17 00:00:00 2001 From: "Denis S. Soldatov aka General-Beck" Date: Thu, 27 Oct 2016 02:12:05 +0700 Subject: [PATCH 017/192] Update gitlab-ci enable arm* in master branch --- .gitlab-ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0cc444846..fcee8333b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -136,6 +136,7 @@ linux-armv7: stage: build image: ethcore/rust-armv7:latest only: + - master - beta - tags - stable @@ -171,6 +172,7 @@ linux-arm: stage: build image: ethcore/rust-arm:latest only: + - master - beta - tags - stable @@ -206,6 +208,7 @@ linux-armv6: stage: build image: ethcore/rust-armv6:latest only: + - master - beta - tags - stable @@ -234,6 +237,7 @@ linux-aarch64: stage: build image: ethcore/rust-aarch64:latest only: + - master - beta - tags - stable From f7259c26d167e782f384edc73addfc2fad51e3eb Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac Date: Wed, 26 Oct 2016 21:25:03 +0200 Subject: [PATCH 018/192] Fixes too long desc and Token Balance Value (#2891) (#2902) --- js/src/ui/Balance/balance.css | 18 ++++++++++++------ js/src/ui/Balance/balance.js | 5 ++++- js/src/ui/Container/Title/title.css | 3 +++ js/src/ui/Container/Title/title.js | 2 +- 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/js/src/ui/Balance/balance.css b/js/src/ui/Balance/balance.css index 6f9b7bb21..6fe1b2a51 100644 --- a/js/src/ui/Balance/balance.css +++ b/js/src/ui/Balance/balance.css @@ -32,19 +32,25 @@ border-radius: 16px; margin: 0.75em 0.5em 0 0; max-height: 24px; + max-width: 100%; + display: flex; + align-items: center; } .balance img { - display: inline-block; height: 32px; margin: -4px 1em 0 0; width: 32px; } -.balance div { - display: inline-block; - /*font-family: 'Roboto Mono', monospace;*/ - line-height: 24px; +.balanceValue { margin: 0 1em 0 0; - vertical-align: top; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; +} + +.balanceTag { + font-size: 0.85em; + padding-right: 0.75rem; } diff --git a/js/src/ui/Balance/balance.js b/js/src/ui/Balance/balance.js index b9df3e9fb..b6f786648 100644 --- a/js/src/ui/Balance/balance.js +++ b/js/src/ui/Balance/balance.js @@ -56,7 +56,10 @@ class Balance extends Component { { -
{ value } { token.tag }
+
+ { value } +
+
{ token.tag }
); }); diff --git a/js/src/ui/Container/Title/title.css b/js/src/ui/Container/Title/title.css index c3bf7a3b1..ee5cc58cd 100644 --- a/js/src/ui/Container/Title/title.css +++ b/js/src/ui/Container/Title/title.css @@ -16,6 +16,9 @@ */ .byline { color: #aaa; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; } .title { diff --git a/js/src/ui/Container/Title/title.js b/js/src/ui/Container/Title/title.js index 2acfc30bb..0c0d0e6b3 100644 --- a/js/src/ui/Container/Title/title.js +++ b/js/src/ui/Container/Title/title.js @@ -40,7 +40,7 @@ export default class Title extends Component { { this.props.title }
- { this.props.byline } + { this.props.byline }
); From 58b963b93d20d7528f7aaa8d31a6ec15937add2e Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Wed, 26 Oct 2016 19:44:08 +0000 Subject: [PATCH 019/192] [ci skip] js-precompiled 20161026-194241 --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 82edd3623..768096cd2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1211,7 +1211,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#51b61fdce036bc8c01923fc73206cfa9fc5ec473" +source = "git+https://github.com/ethcore/js-precompiled.git#95fdde8fabd4eb15450b49263124b8659d114167" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] From d72ea19e54b8e12619657a811105fee0cc6af697 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Wed, 26 Oct 2016 22:25:51 +0200 Subject: [PATCH 020/192] iconomi token images (#2906) --- js/assets/images/contracts/iconomi-64x64.png | Bin 0 -> 2305 bytes js/assets/images/contracts/iconomi.png | Bin 0 -> 6501 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 js/assets/images/contracts/iconomi-64x64.png create mode 100644 js/assets/images/contracts/iconomi.png diff --git a/js/assets/images/contracts/iconomi-64x64.png b/js/assets/images/contracts/iconomi-64x64.png new file mode 100644 index 0000000000000000000000000000000000000000..b3ef6b95b9d1554f94d4526b4071e46132e3aa1b GIT binary patch literal 2305 zcmV+c3I6tpP)%uqT_5JV8c$w|)doZtCf-gkfG(&S(geaON2JkR;(_xru? z_x*m}@AZ8T5Jnhbgb_v_9y#{z=#UJ8sqkR;r4ss(Ny9I_NVy^DeemwXcyn=T=CUQSppF#2bTbF0tgX`eeZpf1wJ~wSGnZ%Bm_)D zApjQ$rH`eipUr61-XBiJVcZ8U&^K%Ds487s08Zio&O|E6v3BR2e~f;8?yb!Cr~l+d zZ#i*zc%l1J9|CmOOe4{&Yl6u+A|)9Siv6-?8VBMPN5K>AQg*!c)QF?aM!?T=)^^-| z=J?7BUeA-1REy*QWJ+=IwPlT7!_WS@Mu?DY4}E&BXijOBdw|c-Y3+PB0tbgIh={W} zi@LUKyJSFzE*oCz_UP3+ngtBYlnIDEn0ddcH)=fhYh>>JPafECZ0}5u9vH9c8Ym&S znex?g2n@@@Mb(pX;Z)`FgBQOYB8Ga9>X12g@Mk538v=~kYIeMV z@{CuTI3dIm2nkRSt}_!N6`VdpM=e+6>!J*da}>MFCzeoQT1XSnbJd$ei>t1zHW;&! z>-e@!lr)*3HIP0Vu0SKhJ@?_v}%F0f= zC|My(z|c*ZJr0(gU2HKP9jrVh1ks8!FG5!SFt9_$o_YO-e|cSZ;N1TBx&_@;U3Z+2o`BLSx6QDuxZr{(MsZ;LBTwJ^1e|o2p-R_S zgVe`?SepaiT{Zi*tTm+4AR*NaOE64k;m}X#v3uSEGF)v*jN+i2^9E&axzCILmX*YX zv<0l*v#a$v&lMZvi~|JyJ9qK)NltS&Wsa71r?(I&3vOitmWlcK+q(kT9g-H1wJ&$P zs{40FDz@BcncX3yP!_SEhjM-O&v$~-FvjjNCa@iYr6f+zt_c`gqLSr^bv^ZVdP zBS3osBoQEqF_v2FzSOsgF+gYFUq#6_G^_*(N-Eu>IOjA(W59KrD$Y1-vWvBHM4gbS zc(T6!1VE*(xt`ygIpr=3f=xdKdw;;_!-62}2~GzDKz`||*S8)mNcbizI$e62d|CVz z3E5tty(_UQTCwLiB)KA(KHV}~&is7qmd{E~+)_qJfA!t}YnoI9pNP_dr?6wr{^XYfS%j3sX7g^7ZD)K3Wjp zXU*J)Gv1py@toI_Q|hglU@->9Efjk$zxLr51w+JuY5c(AyiN>f9I`0(y6UZL58!xn zk+9>1sd_eoP0gi4-Q(4JcaHS<)Y#fDY?55n zwQbVQzR3@sznvuHz4XTN^ySl8g|21#O z!?NtE5dZ}Nf-X@w=MbrYc)N4Ic!S_w7k{z}cJ=XS{)M%FZUR6|X6Z#`=idP^`*vHx zv0uzz@%Y-`Rh+53vdpdd;)7e|TE$c&00I(a)vVlLR^2gpaBCeQ$%wN#7j|utn0fsy zP|xwadZ4q#mVol3TYf2I%-{2lH_r>WO9HQL2Q;h4+lu&Sf?OAbftOypy zYR0s%JM+_BZ3aKvvCEuyo*7dr3Y5ZR?CSv>V2l|ivrGW6ONy+UWh7cJZH`@5r0U(^roovJOpZW>##V?NK;d)im%I`wD~rl~-WgXl-(b}Y5L$EPRog$+ zU5nK`WB_DAM4~gYAVzV_j}YZgw~v@oK?IRkFH(}hjzO! literal 0 HcmV?d00001 diff --git a/js/assets/images/contracts/iconomi.png b/js/assets/images/contracts/iconomi.png new file mode 100644 index 0000000000000000000000000000000000000000..cc7b69afec2f35a264da954b875db3d5381c2201 GIT binary patch literal 6501 zcmcgxXEO_kk5j9x7M~H6KMG&kWy#|SBVTs;}-lB!*qAgYl z(R;f`{`d2}&;4@GKIb{-opatZ@65b2^PAZ)byY+$Kl5hVBjc#KL=eq=4vgoMT; z6FMX#R%$yA?Uc-0)IYj-SFsg7#gB#uPg!s5oqINNXi~<(Z2K+oNq9}~uoV`Oe;Ut} zGmI2v+s;4p+40oAxcJb(`%pA^mDnvjK!w~WQ5n{b2YQK*i!tw?)Oi;}@)8V{;5^-s zQ*Y00+6XNMhIE>SQ4!qrMrlpWy{0W?NHN+pC=9u5-Iy|;K|1@vsR}vyv%`NlJBix( zqQjqw86QkRfr|=-!sKp7<}Kv^J(2@M|BZM}Lql<7fzgJvFD2!|ZlT>BK$IO4CDqtU z!*9}ur>0w66T^is#{S~?J7L7I z)p!HTj$LpIufqrh6{T3uqNI| zzPB~w7Dm2mR?#G#Pt9($nI2iN!k4bDLks`O?b zLX*sr_Tat&YiqZqFEhb;t zyA6xIkl^YEb39lodmJq?MmLM$HXY$|{CU)&lzzw9=F^3QUrZk+W;9$1DIcdFiYym} zG-=eiFBH*rt+vcKGbWcA&TBh^flUu8i?TsQW7pR{aoVFmy!D z-7KJoYo2&2jy&P`RU^mG4Tbd!Dl<0>O8K>>rcs#D7}cZ(oTRe;lF6K}CRmZJ5-$OM zND#eaHlh;9dp_=*lI~8+7h?4sFjuwx$|Li^>+krY-_>^1%%f>f4r%M9Ou}vLFqc1u zV!SP-7GB{562j7Cs97^e>Bn%t(p!>zC%N`HH>!Q?Yxx|lf&cPu8CHTT&A^~iR8PX8 zC{3KMFMn`kR`k`|5=y_PjSODZfmcMWmZW{p>dYn$3!D@lk+ndw^UcqC!~{%86LBE& zE66Ssm2`vPTQ#4TPd59E$2&!{Ywy$8OEgMd=gKm)&ABo$%jU9}@&9^yD?0L1;nbJE ztj1}>S^5Rr#vu*_lRQsdDMn8TJY?S;-fKb22pKSTcgc?pvC*p7XAFpK6qdCc6M7vk zVLalm2rCq3sYVM?IFLv8XSoM(S*HVEj|CjZl$qg?Thw8hBdy@q>B?lAEhD&MF-E$K znT_XJpKp^4Js+uhr5d7JYai7!S$gZ^+Yi#!59J1m)$`lC>;mkn8IL^)HBG#T`sRF% zMg>Lia8X~0%ZZ6QVydR<$&!u=?@z_~H$q-N_g!JPq@i{dwS>ZQ#0*HcX-+O>V!a!0 zyVyMlT)~%)2W|mMS)H9@ejo}*wq9dm;jSL73Hkoj9&2E3g*1dV1kZb}>uc3en2Wbt z$iH_A^fgT`J?>^m(YdJw%TNoHIg3m$bWGOhFuyq-Ck+PUqQ1)z2~0TSlopNJznY=Jf438Sle z+$L!_P3^f?V6QA4`Qn8FQpQ9V5&;kX=$ zNG(AlYXcV1(j!Na+tC>9SizHO_hgJ^i@My8siv{(N;a^V>&$ptu%G8;xxwQF)jv*= zwKn9MX9x0cHOq5ABGZY-?t=H>w`Yg&)2n z&It|o0ZdKS;LHt&vlBaJ_!ERgbtW88JXd*20O#E?Yo*w2e{Pip1!*-Fx?qf0pim2J48M{ zpGlBiZ<>O2%k}U>bf=)uZdVpr*}9zGPxSrHrLID6E5AP@X#jI*^!ExOlifmz>9TvC zXOPLZM+B!mgiAuiT0(9ls=GhBx6f&!>&sKTJZ4(&&!3}aCY>ID<%3ABYZNz2;YYf) zY{pbCB8dAOGC(U~V-YO&eqTp(EZIHIe3ZP*4>9SxWG8-^gLZo7(OP8H{hkM?`7R<< z+Z=noELiX3tc6jsZI(dLU>OJbFe zxHzFi-iYgqw=?gCn8;{6Amxhl#OLM7xn<)AU#DET)rTkP4Z@1v1%9sm`1J5>>=(_G zzdgrg@3Ts)>+DK&OINq(3yc$}Y1{|(3#Yz?EHeINYW$_cYPIP}5(A!}mdX||k=t!= zCe)Gvv|<>Jc80r{VY zSen{kWi_#mPlek;aAeJTv0tg~{gDyF%I_BTYzW_xj~3@yZ@OHK%y3bmb#;p*6Z2w! z!o}$WxwBYI(JoI5h7ySZcGb}G;Ip(azFQKmq0|ee!3VFmZXY!TsvkG`?Ch`DdE$fR z8#KMOtqvp?p`1O8uiJ=x4+ITkU3X+sqtc!!^+FShw-S$q6gzcb~-0_Z%? z?KXYGpY#8G5L-?rEBvShjX9iZ*6r7M(-NcFh%6Ej7*mcQ{#b!^^Q;x)HDznts!Sxz z?WBsch7hAlqksHMXpHR0z+rXd$m!D$!OHXqM1ysfZXKI=L>3Qi8X)HA&yDd5D7&+goi#9bSia_n*oW--+5pO`C5Ar3-r=WvTsB<3Q8bAJ z5O_qvPX`9*n5Pq6P66P$R`>*F#D^|LBtG~*kI|;V0M;AfF!dzv@@MjW5fx>@0y7O| zP8beCs3cF40&}zF5JgzE*t>AZKmG~i*_Vv9>XFo}_~76yN$Rb0(+A)1{|Wp2PgrRA zaFFaE^54)}ecSTck|fkSyY1iNav$Y`+VW4G?LO$YjsvS5JgDlh^&{`~e;Zt*1Te}7 z@U`6HgvQ@{l+Q6W2@pOq^|Hr}HxTtGKuATuTUh}6AeyxoS5Qhp;}UMSMsH~jaWygu ze3hUD$0Y2Fi_dy8$Hm12v9GhZ)^v8IybIjv&E22oo?-O4!P9yyT~6ne_%D~)fCwD* z$>&bR@a_0kP<+-O11H1U&9)sjsqH*rH%68 z4}fGk=`n(;h4Q~d{T0sYHGO6ndGGPyCwYz6!4CB34@z`}UWz4l0%pk>bWPxtJ9}5V zvY`=#S9xa6yjDK?-jp|ignrt&!@o5m3n?HqsPRUkyB%iAoR8eK7P&QMC$P9PF?Ncy zRtB;~QrS4ZPO8HT z&?6!fTH^#YNC#S<5PH@-@zZ7idvkB|WIK3SNC|j#>CceH+Y)oCcEIaWGI^OEa{k8g zw^M4#4yK)2yO|HzR&#tnR*=D!#%WY9TCj7wBHXMc##mf*o!}W->|uAD#Zsy3wbEBv!ns#Wt+fU01 zygDidv%bYk@I4^vi2(}r9ZWfw8J{9PIk&)WP4_5H=>HBbCuI9A?tCm8f%qZW6T-{n z%4DI`((yh~WW_ZUSfOF_F$a*3%bL%iswOVCKz}PqyBnR4-M`*M_$2NA!szN%9x2^B zQ+9+lJz%|KTU0-ld?FRbv#uqI z6+E?LFgsLle1kWUt7HHGV=|y*q*d{|<}&~;J-_#u-KT%t#zirYV2iw%yuXar1TWIWoEpd_~`ozl4WWU%V3|Lz=^~+k5@~&f2n-E7vOUrD+ zR0H_>#`;LPhY3xpfpH_NwU~>6*R}zV@$t3alF^GTZH(>818(p7>K~H>O1`_Z0XjLX zk1Dbw>$m)@uG;1a$lFNgt0Nu@k=}q*0QAcJJvi0ymZZ_v3H?On@GNtS(NcMJW@^rd z@CF?(U#@Aa*WiP9#jUn-Im69- zw%5hp!~Y^FrdOhzA$wf%@!a`34~0rbGqBxuT^Fq^oAwE=x72CGMqgLpd38%Ucq~1W z{rr~w;>*Pk(%{+a@Ce@Q@9UJ~4<;chv$=)2O$NE5ep4q=S5REg<|*d+Phm>_dtjg{c9`sYIZDxqNRoCIK3sbf z!$qk3u9SRlqgy^Fb3(MChN#Gp(IqZ<^CV zTb(V|{oeO_CvLWLPLt?vw95-|yw#HJ$~q<^eyoBozqSfZOx=onr2cvOq>eGA`>H1p zdQwiumCbvm?W{G+3?KU=I3?dUU`e95ra$n*XjN(_M?27g@6Gq48OYE>EC!S}IzhNM zkpi_6;5W|>mecbzy%T%5pZt?>{7Br7LD|mGTC7LK>SEX5kBmc=>T_sZTEdgWX>+c6AhLrv} zNVac3d!1dd#UI?r70KG@Tb_<18*6i8{FR+|yp9uHr9`PMop{-V1I1iDVj3v%8R?dM zY_$Hl;g!+lFBsoz@~#|Gc^4mMK23a&jrElOlzIPdAIv(i@y=IB__z)XOYN z)~zhh-|9f)1Mq@aY*Ygrfce@$rIO!$rRgDH2U@TWgecOcw}kOsW3X)m7k^i8@ly9# z5#x6~W>V$DU{4-`TBfl0wLvsA5?rOi9R6_mu0PF(&1IILTl??Zo|u-5f2qduo3fd& zy!^lan?WI=ABhRMT000a(0#Ik)^0ldPd z*WLr=qnVQ=Jqlq-3PgG@YsX5s5P?+PbJ$89jwo$Kss$ zp49$8HP_wGI;*c1O$M4d3zB?!InfJp{cI$u&+ClBt->1!XYzR7HgvUpf6oqNJHN9{ zi&0^tx5VM!d`<&5#D(7NF-;gpVP<5d)Utz`MTqSY{tx^wxgnxm*<=WTqbp3G(5ul!b;My{Py3pp8CnpbV?h6sY{4D zXM?ok^V`2~zAj*SMJE!M?272SeFAssrCC z&mB4Cgux`k#$FOQhF~nr%Mjz5cO~{3N2dtWicFu^jP+y7n=tE_Gr(SBnw93$e2=F9 z8UM`>QRZievszf;ZXyG)AScYqLS^^d+@@Ex1-&6@eM@_`u=iJ)&&CrQ_PvI~cR(6! z%1tG^l(0Us$9)+1Esqu`pVTG%En86M@4~zPo_();;COqbsa?p_Jc?I*UyMnC?{os1 zIrD zxC_IcCthj4n}3z0TGMeeb$wY6tJ@qWtS_{_$$mgwj@|hqoMTTW8J=3OewE8;U`WA_ z#{@HC-bqac(?)!mQj_+=O68{8AacJMe#{_Bakg2HsH!*B`a~uhckZ@cWl_8yr!!OV zRg&RM7gJwpCks^R32`wwHf#i$F0ML2im&uOnBM5Z^!{@7J{NeidDo`9edxsxp6goA z)qcPBwyYW!N!D?{MdWWYDiQktpyv6SV`&+k{vK1;7htlYe6z-Kg-F6Fmq(NfCX`Gt zThSC6X8`9ZZ+n&YNksy&r<&nZU=2cVR7`)tjL=VpBjG@}nG>lMW_YH4oZwQ0gxMZs ztPPqZYS@0di8az(D^wm39hFn2B(;oAH`i%V#lB4w^LcpUqyZe)d;d{O{w@4hGxc?8 zbALhpaQlYf3VjSOKNoJQ4U3cFbSeG3a>LdF;G<>RIV1suD5E;3TYh6eZ@tFT`C#SW?>@I(ArKWqNeIs$@VW!dK;H( z?s;lo1MjWE3$Wtg*q8bO1Vj-RJtLJOiT_J+u9XTDxD2FE?i%ERokItn2S3o`6ETVk z6-*h%>@T>W2|z#p6oyNPa#MsQi2yYQi>M?;lX<^{apM2dH2xnu%Kz7g8f=$R_X3}c VH0|qJ0)L2rl;l Date: Wed, 26 Oct 2016 20:39:01 +0000 Subject: [PATCH 021/192] [ci skip] js-precompiled 20161026-203802 --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 768096cd2..cfd995446 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1211,7 +1211,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#95fdde8fabd4eb15450b49263124b8659d114167" +source = "git+https://github.com/ethcore/js-precompiled.git#f1b734cb0df5796f2d73db6268fa0613c1526e84" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] From e668fc4c55f13c04b189a3fd6b8b17408d6209a9 Mon Sep 17 00:00:00 2001 From: "Denis S. Soldatov aka General-Beck" Date: Thu, 27 Oct 2016 09:53:51 +0700 Subject: [PATCH 022/192] Update gitlab-ci fix arm* build ENV --- .gitlab-ci.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index fcee8333b..17907b49a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -6,6 +6,8 @@ variables: SIMPLECOV: "true" RUST_BACKTRACE: "1" RUSTFLAGS: "-D warnings" + HOST_CC: "gcc" + HOST_CXX: "g++" cache: key: "$CI_BUILD_NAME/$CI_BUILD_REF_NAME" untracked: true @@ -141,6 +143,8 @@ linux-armv7: - tags - stable script: + - export CC=arm-linux-gnueabihf-gcc + - export CXX=arm-linux-gnueabihf-g++ - rm -rf .cargo - mkdir -p .cargo - echo "[target.armv7-unknown-linux-gnueabihf]" >> .cargo/config @@ -177,6 +181,8 @@ linux-arm: - tags - stable script: + - export CC=arm-linux-gnueabihf-gcc + - export CXX=arm-linux-gnueabihf-g++ - rm -rf .cargo - mkdir -p .cargo - echo "[target.arm-unknown-linux-gnueabihf]" >> .cargo/config @@ -213,6 +219,8 @@ linux-armv6: - tags - stable script: + - export CC=arm-linux-gnueabi-gcc + - export CXX=arm-linux-gnueabi-g++ - rm -rf .cargo - mkdir -p .cargo - echo "[target.arm-unknown-linux-gnueabi]" >> .cargo/config @@ -242,6 +250,8 @@ linux-aarch64: - tags - stable script: + - export CC=aarch64-linux-gnu-gcc + - export CXX=aarch64-linux-gnu-g++ - rm -rf .cargo - mkdir -p .cargo - echo "[target.aarch64-unknown-linux-gnu]" >> .cargo/config From 9bfb8094cc8ed58dc28b8c950c1802c5ff7c5c46 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Thu, 27 Oct 2016 02:56:17 +0000 Subject: [PATCH 023/192] [ci skip] js-precompiled 20161027-025520 --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index cfd995446..ced9524ee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1211,7 +1211,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#f1b734cb0df5796f2d73db6268fa0613c1526e84" +source = "git+https://github.com/ethcore/js-precompiled.git#9d77b0117c2fa0b17dd381853b0020b1fdc96855" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] From 88997801d0f5fdafbf583271be077116f99ce8d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 27 Oct 2016 08:28:12 +0200 Subject: [PATCH 024/192] Clippy bump (#2877) * Bumping clippy * Fixing warnings * Fix the "fix" --- Cargo.lock | 24 +++++++------- Cargo.toml | 2 +- dapps/Cargo.toml | 2 +- db/Cargo.toml | 2 +- ethcore/Cargo.toml | 2 +- ethcore/src/account_provider.rs | 8 ++--- ethcore/src/block.rs | 2 +- ethcore/src/blockchain/blockchain.rs | 3 +- ethcore/src/cache_manager.rs | 2 +- ethcore/src/client/client.rs | 2 +- ethcore/src/db.rs | 6 ++-- ethcore/src/evm/interpreter/mod.rs | 4 +-- ethcore/src/migrations/v10.rs | 2 +- ethcore/src/miner/miner.rs | 8 +++-- ethcore/src/miner/transaction_queue.rs | 1 + ethcore/src/service.rs | 2 +- ethcore/src/snapshot/mod.rs | 2 +- ethcore/src/state/account.rs | 2 +- ethcore/src/state/mod.rs | 46 +++++++++++--------------- ethcore/src/state_db.rs | 15 ++++----- ethcore/src/trace/db.rs | 2 +- ethcore/src/trace/executive_tracer.rs | 6 ++-- json/Cargo.toml | 2 +- rpc/Cargo.toml | 2 +- signer/Cargo.toml | 2 +- signer/src/authcode_store.rs | 1 + sync/Cargo.toml | 2 +- sync/src/blocks.rs | 6 ++-- sync/src/chain.rs | 27 +++++++-------- util/Cargo.toml | 2 +- util/network/Cargo.toml | 2 +- util/src/journaldb/archivedb.rs | 10 +++--- util/src/journaldb/earlymergedb.rs | 10 ++++-- util/src/journaldb/overlayrecentdb.rs | 2 +- util/src/journaldb/refcounteddb.rs | 4 ++- util/src/migration/mod.rs | 2 +- util/src/overlaydb.rs | 4 +-- util/src/trie/journal.rs | 2 +- util/src/trie/triedb.rs | 4 +-- 39 files changed, 117 insertions(+), 112 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ced9524ee..a58a426a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,7 +3,7 @@ name = "parity" version = "1.4.0" dependencies = [ "ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "clippy 0.0.90 (registry+https://github.com/rust-lang/crates.io-index)", + "clippy 0.0.96 (registry+https://github.com/rust-lang/crates.io-index)", "ctrlc 1.1.1 (git+https://github.com/ethcore/rust-ctrlc.git)", "daemonize 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)", @@ -145,15 +145,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "clippy" -version = "0.0.90" +version = "0.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "clippy_lints 0.0.90 (registry+https://github.com/rust-lang/crates.io-index)", + "clippy_lints 0.0.96 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "clippy_lints" -version = "0.0.90" +version = "0.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -279,7 +279,7 @@ dependencies = [ "bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "bloomchain 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "clippy 0.0.90 (registry+https://github.com/rust-lang/crates.io-index)", + "clippy 0.0.96 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "ethash 1.4.0", @@ -330,7 +330,7 @@ dependencies = [ name = "ethcore-dapps" version = "1.4.0" dependencies = [ - "clippy 0.0.90 (registry+https://github.com/rust-lang/crates.io-index)", + "clippy 0.0.96 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "ethabi 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "ethcore-devtools 1.4.0", @@ -473,7 +473,7 @@ dependencies = [ name = "ethcore-rpc" version = "1.4.0" dependencies = [ - "clippy 0.0.90 (registry+https://github.com/rust-lang/crates.io-index)", + "clippy 0.0.96 (registry+https://github.com/rust-lang/crates.io-index)", "ethash 1.4.0", "ethcore 1.4.0", "ethcore-devtools 1.4.0", @@ -503,7 +503,7 @@ dependencies = [ name = "ethcore-signer" version = "1.4.0" dependencies = [ - "clippy 0.0.90 (registry+https://github.com/rust-lang/crates.io-index)", + "clippy 0.0.96 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "ethcore-devtools 1.4.0", "ethcore-io 1.4.0", @@ -542,7 +542,7 @@ version = "1.4.0" dependencies = [ "ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", - "clippy 0.0.90 (registry+https://github.com/rust-lang/crates.io-index)", + "clippy 0.0.96 (registry+https://github.com/rust-lang/crates.io-index)", "elastic-array 0.6.0 (git+https://github.com/ethcore/elastic-array)", "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "eth-secp256k1 0.5.4 (git+https://github.com/ethcore/rust-secp256k1)", @@ -631,7 +631,7 @@ dependencies = [ name = "ethsync" version = "1.4.0" dependencies = [ - "clippy 0.0.90 (registry+https://github.com/rust-lang/crates.io-index)", + "clippy 0.0.96 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "ethcore 1.4.0", "ethcore-io 1.4.0", @@ -1948,8 +1948,8 @@ dependencies = [ "checksum bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c129aff112dcc562970abb69e2508b40850dd24c274761bb50fb8a0067ba6c27" "checksum bytes 0.4.0-dev (git+https://github.com/carllerche/bytes)" = "" "checksum cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de1e760d7b6535af4241fca8bd8adf68e2e7edacc6b29f5d399050c5e48cf88c" -"checksum clippy 0.0.90 (registry+https://github.com/rust-lang/crates.io-index)" = "d19bda68c3db98e3a780342f6101b44312fef20a5f13ce756d1202a35922b01b" -"checksum clippy_lints 0.0.90 (registry+https://github.com/rust-lang/crates.io-index)" = "3d4ed67c69b9bb35169be2538691d290a3aa0cbfd4b9f0bfb7c221fc1d399a96" +"checksum clippy 0.0.96 (registry+https://github.com/rust-lang/crates.io-index)" = "6eacf01b0aad84a0817703498f72d252df7c0faf6a5b86d0be4265f1829e459f" +"checksum clippy_lints 0.0.96 (registry+https://github.com/rust-lang/crates.io-index)" = "a49960c9aab544ce86b004dcb61620e8b898fea5fc0f697a028f460f48221ed6" "checksum cookie 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "90266f45846f14a1e986c77d1e9c2626b8c342ed806fe60241ec38cc8697b245" "checksum crossbeam 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "fb974f835e90390c5f9dfac00f05b06dc117299f5ea4e85fbc7bb443af4911cc" "checksum ctrlc 1.1.1 (git+https://github.com/ethcore/rust-ctrlc.git)" = "" diff --git a/Cargo.toml b/Cargo.toml index 62039696c..dc802a0fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,7 +46,7 @@ ethcore-logger = { path = "logger" } rlp = { path = "util/rlp" } ethcore-stratum = { path = "stratum" } ethcore-dapps = { path = "dapps", optional = true } -clippy = { version = "0.0.90", optional = true} +clippy = { version = "0.0.96", optional = true} [target.'cfg(windows)'.dependencies] winapi = "0.2" diff --git a/dapps/Cargo.toml b/dapps/Cargo.toml index 9c49c7e28..ddc23c87c 100644 --- a/dapps/Cargo.toml +++ b/dapps/Cargo.toml @@ -33,7 +33,7 @@ fetch = { path = "../util/fetch" } parity-ui = { path = "./ui" } mime_guess = { version = "1.6.1" } -clippy = { version = "0.0.90", optional = true} +clippy = { version = "0.0.96", optional = true} [build-dependencies] serde_codegen = { version = "0.8", optional = true } diff --git a/db/Cargo.toml b/db/Cargo.toml index 15ceb9b3b..27eadef4a 100644 --- a/db/Cargo.toml +++ b/db/Cargo.toml @@ -11,7 +11,7 @@ build = "build.rs" ethcore-ipc-codegen = { path = "../ipc/codegen" } [dependencies] -clippy = { version = "0.0.90", optional = true} +clippy = { version = "0.0.96", optional = true} ethcore-devtools = { path = "../devtools" } ethcore-ipc = { path = "../ipc/rpc" } rocksdb = { git = "https://github.com/ethcore/rust-rocksdb" } diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index ad9e545ce..a60eccddd 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -25,7 +25,7 @@ semver = "0.2" bit-set = "0.4" time = "0.1" evmjit = { path = "../evmjit", optional = true } -clippy = { version = "0.0.90", optional = true} +clippy = { version = "0.0.96", optional = true} ethash = { path = "../ethash" } ethcore-util = { path = "../util" } ethcore-io = { path = "../util/io" } diff --git a/ethcore/src/account_provider.rs b/ethcore/src/account_provider.rs index 7a4958ddc..fed1a05fc 100644 --- a/ethcore/src/account_provider.rs +++ b/ethcore/src/account_provider.rs @@ -267,17 +267,17 @@ impl AccountProvider { /// Returns `true` if the password for `account` is `password`. `false` if not. pub fn test_password(&self, account: &Address, password: String) -> Result { - match self.sstore.sign(&account, &password, &Default::default()) { + match self.sstore.sign(account, &password, &Default::default()) { Ok(_) => Ok(true), Err(SSError::InvalidPassword) => Ok(false), Err(e) => Err(Error::SStore(e)), } - } + } /// Changes the password of `account` from `password` to `new_password`. Fails if incorrect `password` given. pub fn change_password(&self, account: &Address, password: String, new_password: String) -> Result<(), Error> { - self.sstore.change_password(&account, &password, &new_password).map_err(Error::SStore) - } + self.sstore.change_password(account, &password, &new_password).map_err(Error::SStore) + } /// Helper method used for unlocking accounts. fn unlock_account(&self, account: Address, password: String, unlock: Unlock) -> Result<(), Error> { diff --git a/ethcore/src/block.rs b/ethcore/src/block.rs index 5d7305b91..54c2a7a02 100644 --- a/ethcore/src/block.rs +++ b/ethcore/src/block.rs @@ -542,7 +542,7 @@ pub fn enact( Ok(b.close_and_lock()) } -#[inline(always)] +#[inline] #[cfg(not(feature = "slow-blocks"))] fn push_transactions(block: &mut OpenBlock, transactions: &[SignedTransaction]) -> Result<(), Error> { for t in transactions { diff --git a/ethcore/src/blockchain/blockchain.rs b/ethcore/src/blockchain/blockchain.rs index de0c72a38..32d42fe31 100644 --- a/ethcore/src/blockchain/blockchain.rs +++ b/ethcore/src/blockchain/blockchain.rs @@ -414,6 +414,7 @@ impl<'a> Iterator for AncestryIter<'a> { } impl BlockChain { + #[cfg_attr(feature="dev", allow(useless_let_if_seq))] /// Create new instance of blockchain from given Genesis pub fn new(config: Config, genesis: &[u8], db: Arc) -> BlockChain { // 400 is the avarage size of the key @@ -565,7 +566,7 @@ impl BlockChain { let range = extras.number as bc::Number .. extras.number as bc::Number; let chain = bc::group::BloomGroupChain::new(self.blooms_config, self); let changes = chain.replace(&range, vec![]); - for (k, v) in changes.into_iter() { + for (k, v) in changes { batch.write(db::COL_EXTRA, &LogGroupPosition::from(k), &BloomGroup::from(v)); } batch.put(db::COL_EXTRA, b"best", &hash); diff --git a/ethcore/src/cache_manager.rs b/ethcore/src/cache_manager.rs index 02c8f08a1..6ad01b453 100644 --- a/ethcore/src/cache_manager.rs +++ b/ethcore/src/cache_manager.rs @@ -66,7 +66,7 @@ impl CacheManager where T: Eq + Hash { } fn rotate_cache_if_needed(&mut self) { - if self.cache_usage.len() == 0 { return } + if self.cache_usage.is_empty() { return } if self.cache_usage[0].len() * self.bytes_per_cache_entry > self.pref_cache_size / COLLECTION_QUEUE_SIZE { if let Some(cache) = self.cache_usage.pop_back() { diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 49195d952..cb963dadc 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -314,7 +314,7 @@ impl Client { if let Some(parent) = chain_has_parent { // Enact Verified Block let last_hashes = self.build_last_hashes(header.parent_hash().clone()); - let db = self.state_db.lock().boxed_clone_canon(&header.parent_hash()); + 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 = try!(enact_result.map_err(|e| { diff --git a/ethcore/src/db.rs b/ethcore/src/db.rs index 10672d730..92c0f1b39 100644 --- a/ethcore/src/db.rs +++ b/ethcore/src/db.rs @@ -114,7 +114,7 @@ pub trait Writable { R: Deref { match policy { CacheUpdatePolicy::Overwrite => { - for (key, value) in values.into_iter() { + for (key, value) in values { self.write(col, &key, &value); cache.insert(key, value); } @@ -135,7 +135,7 @@ pub trait Writable { R: Deref { match policy { CacheUpdatePolicy::Overwrite => { - for (key, value) in values.into_iter() { + for (key, value) in values { match value { Some(ref v) => self.write(col, &key, v), None => self.delete(col, &key), @@ -144,7 +144,7 @@ pub trait Writable { } }, CacheUpdatePolicy::Remove => { - for (key, value) in values.into_iter() { + for (key, value) in values { match value { Some(v) => self.write(col, &key, &v), None => self.delete(col, &key), diff --git a/ethcore/src/evm/interpreter/mod.rs b/ethcore/src/evm/interpreter/mod.rs index a39e09e79..8ded2e1f1 100644 --- a/ethcore/src/evm/interpreter/mod.rs +++ b/ethcore/src/evm/interpreter/mod.rs @@ -54,14 +54,14 @@ const TWO_POW_248: U256 = U256([0, 0, 0, 0x100000000000000]); //0x1 00000000 000 /// Abstraction over raw vector of Bytes. Easier state management of PC. struct CodeReader<'a> { position: ProgramCounter, - code: &'a Bytes + code: &'a [u8] } #[cfg_attr(feature="dev", allow(len_without_is_empty))] impl<'a> CodeReader<'a> { /// Create new code reader - starting at position 0. - fn new(code: &'a Bytes) -> Self { + fn new(code: &'a [u8]) -> Self { CodeReader { position: 0, code: code, diff --git a/ethcore/src/migrations/v10.rs b/ethcore/src/migrations/v10.rs index 88884fb26..77531eb08 100644 --- a/ethcore/src/migrations/v10.rs +++ b/ethcore/src/migrations/v10.rs @@ -61,7 +61,7 @@ pub fn generate_bloom(source: Arc, dest: &mut Database) -> Result<(), let account_trie = try!(TrieDB::new(state_db.as_hashdb(), &state_root).map_err(|e| Error::Custom(format!("Cannot open trie: {:?}", e)))); for item in try!(account_trie.iter().map_err(|_| Error::MigrationImpossible)) { let (ref account_key, _) = try!(item.map_err(|_| Error::MigrationImpossible)); - let account_key_hash = H256::from_slice(&account_key); + let account_key_hash = H256::from_slice(account_key); bloom.set(&*account_key_hash); } diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index 43b6ca721..270a39e48 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -362,7 +362,7 @@ impl Miner { { let mut queue = self.transaction_queue.lock(); - for hash in invalid_transactions.into_iter() { + for hash in invalid_transactions { queue.remove_invalid(&hash, &fetch_account); } for hash in transactions_to_penalize { @@ -522,6 +522,8 @@ impl Miner { /// Are we allowed to do a non-mandatory reseal? fn tx_reseal_allowed(&self) -> bool { Instant::now() > *self.next_allowed_reseal.lock() } + #[cfg_attr(feature="dev", allow(wrong_self_convention))] + #[cfg_attr(feature="dev", allow(redundant_closure))] fn from_pending_block(&self, latest_block_number: BlockNumber, from_chain: F, map_block: G) -> H where F: Fn() -> H, G: Fn(&ClosedBlock) -> H { let sealing_work = self.sealing_work.lock(); @@ -885,7 +887,7 @@ impl MinerService for Miner { fn pending_receipts(&self, best_block: BlockNumber) -> BTreeMap { self.from_pending_block( best_block, - || BTreeMap::new(), + BTreeMap::new, |pending| { let hashes = pending.transactions() .iter() @@ -1019,7 +1021,7 @@ impl MinerService for Miner { tx.sender().expect("Transaction is in block, so sender has to be defined.") }) .collect::>(); - for sender in to_remove.into_iter() { + for sender in to_remove { transaction_queue.remove_all(sender, chain.latest_nonce(&sender)); } }); diff --git a/ethcore/src/miner/transaction_queue.rs b/ethcore/src/miner/transaction_queue.rs index fa0cce1e6..40150b78d 100644 --- a/ethcore/src/miner/transaction_queue.rs +++ b/ethcore/src/miner/transaction_queue.rs @@ -446,6 +446,7 @@ pub struct AccountDetails { const GAS_LIMIT_HYSTERESIS: usize = 10; // (100/GAS_LIMIT_HYSTERESIS) % /// Describes the strategy used to prioritize transactions in the queue. +#[cfg_attr(feature="dev", allow(enum_variant_names))] #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum PrioritizationStrategy { /// Use only gas price. Disregards the actual computation cost of the transaction. diff --git a/ethcore/src/service.rs b/ethcore/src/service.rs index 95cbe745e..1180c1b3c 100644 --- a/ethcore/src/service.rs +++ b/ethcore/src/service.rs @@ -87,7 +87,7 @@ impl ClientService { db_config.set_cache(::db::COL_STATE, size); } - db_config.compaction = config.db_compaction.compaction_profile(&client_path); + db_config.compaction = config.db_compaction.compaction_profile(client_path); db_config.wal = config.db_wal; let pruning = config.pruning; diff --git a/ethcore/src/snapshot/mod.rs b/ethcore/src/snapshot/mod.rs index 4fa00f771..86f921cf0 100644 --- a/ethcore/src/snapshot/mod.rs +++ b/ethcore/src/snapshot/mod.rs @@ -202,7 +202,7 @@ impl<'a> BlockChunker<'a> { // cut off the chunk if too large. - if new_loaded_size > PREFERRED_CHUNK_SIZE && self.rlps.len() > 0 { + if new_loaded_size > PREFERRED_CHUNK_SIZE && !self.rlps.is_empty() { try!(self.write_chunk(last)); loaded_size = pair.len(); } else { diff --git a/ethcore/src/state/account.rs b/ethcore/src/state/account.rs index b26c79cba..2bd8a2d15 100644 --- a/ethcore/src/state/account.rs +++ b/ethcore/src/state/account.rs @@ -413,7 +413,7 @@ impl Account { self.code_size = other.code_size; self.address_hash = other.address_hash; let mut cache = self.storage_cache.borrow_mut(); - for (k, v) in other.storage_cache.into_inner().into_iter() { + for (k, v) in other.storage_cache.into_inner() { cache.insert(k.clone() , v.clone()); //TODO: cloning should not be required here } self.storage_changes = other.storage_changes; diff --git a/ethcore/src/state/mod.rs b/ethcore/src/state/mod.rs index 6befcad12..bef20d257 100644 --- a/ethcore/src/state/mod.rs +++ b/ethcore/src/state/mod.rs @@ -127,11 +127,10 @@ impl AccountEntry { fn overwrite_with(&mut self, other: AccountEntry) { self.state = other.state; match other.account { - Some(acc) => match self.account { - Some(ref mut ours) => { + Some(acc) => { + if let Some(ref mut ours) = self.account { ours.overwrite_with(acc); - }, - None => {}, + } }, None => self.account = None, } @@ -281,13 +280,10 @@ impl State { } }, None => { - match self.cache.get_mut().entry(k) { - Entry::Occupied(e) => { - if e.get().is_dirty() { - e.remove(); - } - }, - _ => {} + if let Entry::Occupied(e) = self.cache.get_mut().entry(k) { + if e.get().is_dirty() { + e.remove(); + } } } } @@ -501,6 +497,7 @@ impl State { /// Commit accounts to SecTrieDBMut. This is similar to cpp-ethereum's dev::eth::commit. /// `accounts` is mutable because we may need to commit the code or storage and record that. #[cfg_attr(feature="dev", allow(match_ref_pats))] + #[cfg_attr(feature="dev", allow(needless_borrow))] fn commit_into( factories: &Factories, db: &mut StateDB, @@ -509,17 +506,14 @@ impl State { ) -> Result<(), Error> { // first, commit the sub trees. for (address, ref mut a) in accounts.iter_mut().filter(|&(_, ref a)| a.is_dirty()) { - match a.account { - Some(ref mut account) => { - if !account.is_empty() { - db.note_account_bloom(&address); - } - let addr_hash = account.address_hash(address); - let mut account_db = factories.accountdb.create(db.as_hashdb_mut(), addr_hash); - account.commit_storage(&factories.trie, account_db.as_hashdb_mut()); - account.commit_code(account_db.as_hashdb_mut()); + if let Some(ref mut account) = a.account { + if !account.is_empty() { + db.note_account_bloom(address); } - _ => {} + let addr_hash = account.address_hash(address); + let mut account_db = factories.accountdb.create(db.as_hashdb_mut(), addr_hash); + account.commit_storage(&factories.trie, account_db.as_hashdb_mut()); + account.commit_code(account_db.as_hashdb_mut()); } } @@ -586,7 +580,7 @@ impl State { fn query_pod(&mut self, query: &PodState) { for (address, pod_account) in query.get().into_iter() - .filter(|&(ref a, _)| self.ensure_cached(a, RequireCache::Code, true, |a| a.is_some())) + .filter(|&(a, _)| self.ensure_cached(a, RequireCache::Code, true, |a| a.is_some())) { // needs to be split into two parts for the refcell code here // to work. @@ -679,14 +673,12 @@ impl State { None => { let maybe_acc = if self.db.check_account_bloom(a) { let db = self.factories.trie.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR); - let maybe_acc = match db.get(a) { + match db.get(a) { Ok(Some(acc)) => AccountEntry::new_clean(Some(Account::from_rlp(&acc))), Ok(None) => AccountEntry::new_clean(None), Err(e) => panic!("Potential DB corruption encountered: {}", e), - }; - maybe_acc - } - else { + } + } else { AccountEntry::new_clean(None) }; self.insert_cache(a, maybe_acc); diff --git a/ethcore/src/state_db.rs b/ethcore/src/state_db.rs index 2823b9b1b..dfa65ab1d 100644 --- a/ethcore/src/state_db.rs +++ b/ethcore/src/state_db.rs @@ -170,7 +170,7 @@ impl StateDB { pub fn commit_bloom(batch: &mut DBTransaction, journal: BloomJournal) -> Result<(), UtilError> { assert!(journal.hash_functions <= 255); - batch.put(COL_ACCOUNT_BLOOM, ACCOUNT_BLOOM_HASHCOUNT_KEY, &vec![journal.hash_functions as u8]); + batch.put(COL_ACCOUNT_BLOOM, ACCOUNT_BLOOM_HASHCOUNT_KEY, &[journal.hash_functions as u8]); let mut key = [0u8; 8]; let mut val = [0u8; 8]; @@ -216,7 +216,7 @@ impl StateDB { let mut clear = false; for block in enacted.iter().filter(|h| self.commit_hash.as_ref().map_or(true, |p| *h != p)) { clear = clear || { - if let Some(ref mut m) = cache.modifications.iter_mut().find(|ref m| &m.hash == block) { + if let Some(ref mut m) = cache.modifications.iter_mut().find(|m| &m.hash == block) { trace!("Reverting enacted block {:?}", block); m.is_canon = true; for a in &m.accounts { @@ -232,7 +232,7 @@ impl StateDB { for block in retracted { clear = clear || { - if let Some(ref mut m) = cache.modifications.iter_mut().find(|ref m| &m.hash == block) { + if let Some(ref mut m) = cache.modifications.iter_mut().find(|m| &m.hash == block) { trace!("Retracting block {:?}", block); m.is_canon = false; for a in &m.accounts { @@ -286,7 +286,7 @@ impl StateDB { is_canon: is_best, parent: parent.clone(), }; - let insert_at = cache.modifications.iter().enumerate().find(|&(_, ref m)| m.number < *number).map(|(i, _)| i); + let insert_at = cache.modifications.iter().enumerate().find(|&(_, m)| m.number < *number).map(|(i, _)| i); trace!("inserting modifications at {:?}", insert_at); if let Some(insert_at) = insert_at { cache.modifications.insert(insert_at, block_changes); @@ -369,7 +369,7 @@ impl StateDB { if !Self::is_allowed(addr, &self.parent_hash, &cache.modifications) { return None; } - cache.accounts.get_mut(&addr).map(|a| a.as_ref().map(|a| a.clone_basic())) + cache.accounts.get_mut(addr).map(|a| a.as_ref().map(|a| a.clone_basic())) } /// Get value from a cached account. @@ -406,8 +406,7 @@ impl StateDB { // We search for our parent in that list first and then for // all its parent until we hit the canonical block, // checking against all the intermediate modifications. - let mut iter = modifications.iter(); - while let Some(ref m) = iter.next() { + for m in modifications { if &m.hash == parent { if m.is_canon { return true; @@ -420,7 +419,7 @@ impl StateDB { } } trace!("Cache lookup skipped for {:?}: parent hash is unknown", addr); - return false; + false } } diff --git a/ethcore/src/trace/db.rs b/ethcore/src/trace/db.rs index bc867983d..6a1b55a1b 100644 --- a/ethcore/src/trace/db.rs +++ b/ethcore/src/trace/db.rs @@ -285,7 +285,7 @@ impl TraceDatabase for TraceDB where T: DatabaseExtras { let mut blooms = self.blooms.write(); batch.extend_with_cache(db::COL_TRACE, &mut *blooms, blooms_to_insert, CacheUpdatePolicy::Remove); // note_used must be called after locking blooms to avoid cache/traces deadlock on garbage collection - for key in blooms_keys.into_iter() { + for key in blooms_keys { self.note_used(CacheID::Bloom(key)); } } diff --git a/ethcore/src/trace/executive_tracer.rs b/ethcore/src/trace/executive_tracer.rs index ca9bc30b5..bb18c61a8 100644 --- a/ethcore/src/trace/executive_tracer.rs +++ b/ethcore/src/trace/executive_tracer.rs @@ -50,12 +50,12 @@ fn prefix_subtrace_addresses(mut traces: Vec) -> Vec { // [1, 0] let mut current_subtrace_index = 0; let mut first = true; - for trace in traces.iter_mut() { + for trace in &mut traces { match (first, trace.trace_address.is_empty()) { (true, _) => first = false, (_, true) => current_subtrace_index += 1, _ => {} - } + } trace.trace_address.push_front(current_subtrace_index); } traces @@ -78,7 +78,7 @@ fn should_prefix_address_properly() { let t = vec![vec![], vec![0], vec![0, 0], vec![0], vec![], vec![], vec![0], vec![]].into_iter().map(&f).collect(); let t = prefix_subtrace_addresses(t); assert_eq!(t, vec![vec![0], vec![0, 0], vec![0, 0, 0], vec![0, 0], vec![1], vec![2], vec![2, 0], vec![3]].into_iter().map(&f).collect::>()); -} +} impl Tracer for ExecutiveTracer { fn prepare_trace_call(&self, params: &ActionParams) -> Option { diff --git a/json/Cargo.toml b/json/Cargo.toml index 90c36cedc..8f7b0c227 100644 --- a/json/Cargo.toml +++ b/json/Cargo.toml @@ -10,7 +10,7 @@ rustc-serialize = "0.3" serde = "0.8" serde_json = "0.8" serde_macros = { version = "0.8", optional = true } -clippy = { version = "0.0.90", optional = true} +clippy = { version = "0.0.96", optional = true} [build-dependencies] serde_codegen = { version = "0.8", optional = true } diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml index 34b68fb81..9ce638ea6 100644 --- a/rpc/Cargo.toml +++ b/rpc/Cargo.toml @@ -29,7 +29,7 @@ fetch = { path = "../util/fetch" } rustc-serialize = "0.3" transient-hashmap = "0.1" serde_macros = { version = "0.8.0", optional = true } -clippy = { version = "0.0.90", optional = true} +clippy = { version = "0.0.96", optional = true} json-ipc-server = { git = "https://github.com/ethcore/json-ipc-server.git" } ethcore-ipc = { path = "../ipc/rpc" } time = "0.1" diff --git a/signer/Cargo.toml b/signer/Cargo.toml index b8a7c5ce4..651d96cb3 100644 --- a/signer/Cargo.toml +++ b/signer/Cargo.toml @@ -23,7 +23,7 @@ ethcore-rpc = { path = "../rpc" } ethcore-devtools = { path = "../devtools" } parity-ui = { path = "../dapps/ui", version = "1.4", optional = true } -clippy = { version = "0.0.90", optional = true} +clippy = { version = "0.0.96", optional = true} [features] dev = ["clippy"] diff --git a/signer/src/authcode_store.rs b/signer/src/authcode_store.rs index d8068fc88..d8474441c 100644 --- a/signer/src/authcode_store.rs +++ b/signer/src/authcode_store.rs @@ -99,6 +99,7 @@ impl AuthCodes { } /// Checks if given hash is correct identifier of `SignerUI` + #[cfg_attr(feature="dev", allow(wrong_self_convention))] pub fn is_valid(&mut self, hash: &H256, time: u64) -> bool { let now = self.now.now(); // check time diff --git a/sync/Cargo.toml b/sync/Cargo.toml index d27929186..95d738eb4 100644 --- a/sync/Cargo.toml +++ b/sync/Cargo.toml @@ -17,7 +17,7 @@ ethcore-network = { path = "../util/network" } ethcore-io = { path = "../util/io" } ethcore = { path = "../ethcore" } rlp = { path = "../util/rlp" } -clippy = { version = "0.0.90", optional = true} +clippy = { version = "0.0.96", optional = true} log = "0.3" env_logger = "0.3" time = "0.1.34" diff --git a/sync/src/blocks.rs b/sync/src/blocks.rs index db385b9d5..ed608d9c1 100644 --- a/sync/src/blocks.rs +++ b/sync/src/blocks.rs @@ -114,7 +114,7 @@ impl BlockCollection { /// Insert a set of headers into collection and advance subchain head pointers. pub fn insert_headers(&mut self, headers: Vec) { - for h in headers.into_iter() { + for h in headers { if let Err(e) = self.insert_header(h) { trace!(target: "sync", "Ignored invalid header: {:?}", e); } @@ -125,7 +125,7 @@ impl BlockCollection { /// Insert a collection of block bodies for previously downloaded headers. pub fn insert_bodies(&mut self, bodies: Vec) -> usize { let mut inserted = 0; - for b in bodies.into_iter() { + for b in bodies { if let Err(e) = self.insert_body(b) { trace!(target: "sync", "Ignored invalid body: {:?}", e); } else { @@ -141,7 +141,7 @@ impl BlockCollection { return 0; } let mut inserted = 0; - for r in receipts.into_iter() { + for r in receipts { if let Err(e) = self.insert_receipt(r) { trace!(target: "sync", "Ignored invalid receipt: {:?}", e); } else { diff --git a/sync/src/chain.rs b/sync/src/chain.rs index 916e7424e..764eccdac 100644 --- a/sync/src/chain.rs +++ b/sync/src/chain.rs @@ -209,8 +209,8 @@ pub struct SyncStatus { impl SyncStatus { /// Indicates if snapshot download is in progress pub fn is_snapshot_syncing(&self) -> bool { - self.state == SyncState::SnapshotManifest - || self.state == SyncState::SnapshotData + self.state == SyncState::SnapshotManifest + || self.state == SyncState::SnapshotData || self.state == SyncState::SnapshotWaiting } @@ -381,7 +381,7 @@ impl ChainSync { /// Returns information on peers connections pub fn peers(&self, io: &SyncIo) -> Vec { self.peers.iter() - .filter_map(|(&peer_id, ref peer_data)| + .filter_map(|(&peer_id, peer_data)| io.peer_session_info(peer_id).map(|session_info| PeerInfoDigest { id: session_info.id.map(|id| id.hex()), @@ -453,7 +453,7 @@ impl ChainSync { self.init_downloaders(io.chain()); self.reset_and_continue(io); } - + /// Restart sync after bad block has been detected. May end up re-downloading up to QUEUE_SIZE blocks fn init_downloaders(&mut self, chain: &BlockChainClient) { // Do not assume that the block queue/chain still has our last_imported_block @@ -1017,7 +1017,7 @@ impl ChainSync { return; } let (peer_latest, peer_difficulty, peer_snapshot_number, peer_snapshot_hash) = { - if let Some(ref peer) = self.peers.get_mut(&peer_id) { + if let Some(peer) = self.peers.get_mut(&peer_id) { if peer.asking != PeerAsking::Nothing || !peer.can_sync() { return; } @@ -1142,6 +1142,7 @@ impl ChainSync { } /// Checks if there are blocks fully downloaded that can be imported into the blockchain and does the import. + #[cfg_attr(feature="dev", allow(block_in_if_condition_stmt))] fn collect_blocks(&mut self, io: &mut SyncIo, block_set: BlockSet) { match block_set { BlockSet::NewBlocks => { @@ -1150,9 +1151,9 @@ impl ChainSync { } }, BlockSet::OldBlocks => { - if self.old_blocks.as_mut().map_or(false, |downloader| { downloader.collect_blocks(io, false) == Err(DownloaderImportError::Invalid) }) { - self.restart(io); - } else if self.old_blocks.as_ref().map_or(false, |downloader| { downloader.is_complete() }) { + if self.old_blocks.as_mut().map_or(false, |downloader| { downloader.collect_blocks(io, false) == Err(DownloaderImportError::Invalid) }) { + self.restart(io); + } else if self.old_blocks.as_ref().map_or(false, |downloader| { downloader.is_complete() }) { trace!(target: "sync", "Background block download is complete"); self.old_blocks = None; } @@ -1242,7 +1243,7 @@ impl ChainSync { return true; } } - return false; + false } /// Generic request sender @@ -1370,7 +1371,7 @@ impl ChainSync { while number <= last && count < max_count { if let Some(hdr) = overlay.get(&number) { trace!(target: "sync", "{}: Returning cached fork header", peer_id); - data.extend(hdr); + data.extend_from_slice(hdr); count += 1; } else if let Some(mut hdr) = io.chain().block_header(BlockID::Number(number)) { data.append(&mut hdr); @@ -1707,7 +1708,7 @@ impl ChainSync { self.send_packet(io, *peer_id, NEW_BLOCK_PACKET, rlp); } } - if let Some(ref mut peer) = self.peers.get_mut(&peer_id) { + if let Some(ref mut peer) = self.peers.get_mut(peer_id) { peer.latest_hash = chain_info.best_block_hash.clone(); } sent += 1; @@ -1725,7 +1726,7 @@ impl ChainSync { sent += match ChainSync::create_new_hashes_rlp(io.chain(), &last_parent, &chain_info.best_block_hash) { Some(rlp) => { { - if let Some(ref mut peer) = self.peers.get_mut(&peer_id) { + if let Some(ref mut peer) = self.peers.get_mut(peer_id) { peer.latest_hash = chain_info.best_block_hash.clone(); } } @@ -1793,7 +1794,7 @@ impl ChainSync { // Send RLPs let sent = lucky_peers.len(); if sent > 0 { - for (peer_id, rlp) in lucky_peers.into_iter() { + for (peer_id, rlp) in lucky_peers { self.send_packet(io, peer_id, TRANSACTIONS_PACKET, rlp); } diff --git a/util/Cargo.toml b/util/Cargo.toml index c560a6bb5..1b6939595 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -23,7 +23,7 @@ rlp = { path = "rlp" } heapsize = { version = "0.3", features = ["unstable"] } itertools = "0.4" sha3 = { path = "sha3" } -clippy = { version = "0.0.90", optional = true} +clippy = { version = "0.0.96", optional = true} ethcore-devtools = { path = "../devtools" } libc = "0.2.7" vergen = "0.1" diff --git a/util/network/Cargo.toml b/util/network/Cargo.toml index b5292db60..8b566fcf2 100644 --- a/util/network/Cargo.toml +++ b/util/network/Cargo.toml @@ -14,7 +14,7 @@ time = "0.1.34" tiny-keccak = "1.0" rust-crypto = "0.2.34" slab = "0.2" -clippy = { version = "0.0.90", optional = true} +clippy = { version = "0.0.96", optional = true} igd = "0.5.0" libc = "0.2.7" parking_lot = "0.3" diff --git a/util/src/journaldb/archivedb.rs b/util/src/journaldb/archivedb.rs index 940f92375..a8800045b 100644 --- a/util/src/journaldb/archivedb.rs +++ b/util/src/journaldb/archivedb.rs @@ -78,7 +78,7 @@ impl HashDB for ArchiveDB { ret.insert(h, 1); } - for (key, refs) in self.overlay.keys().into_iter() { + for (key, refs) in self.overlay.keys() { let refs = *ret.get(&key).unwrap_or(&0) + refs; ret.insert(key, refs); } @@ -152,7 +152,7 @@ impl JournalDB for ArchiveDB { let mut inserts = 0usize; let mut deletes = 0usize; - for i in self.overlay.drain().into_iter() { + for i in self.overlay.drain() { let (key, (value, rc)) = i; if rc > 0 { batch.put(self.column, &key, &value); @@ -164,7 +164,7 @@ impl JournalDB for ArchiveDB { } } - for (mut key, value) in self.overlay.drain_aux().into_iter() { + for (mut key, value) in self.overlay.drain_aux() { key.push(AUX_FLAG); batch.put(self.column, &key, &value); } @@ -185,7 +185,7 @@ impl JournalDB for ArchiveDB { let mut inserts = 0usize; let mut deletes = 0usize; - for i in self.overlay.drain().into_iter() { + for i in self.overlay.drain() { let (key, (value, rc)) = i; if rc > 0 { if try!(self.backing.get(self.column, &key)).is_some() { @@ -204,7 +204,7 @@ impl JournalDB for ArchiveDB { } } - for (mut key, value) in self.overlay.drain_aux().into_iter() { + for (mut key, value) in self.overlay.drain_aux() { key.push(AUX_FLAG); batch.put(self.column, &key, &value); } diff --git a/util/src/journaldb/earlymergedb.rs b/util/src/journaldb/earlymergedb.rs index 1e782c580..d17c0ef1e 100644 --- a/util/src/journaldb/earlymergedb.rs +++ b/util/src/journaldb/earlymergedb.rs @@ -63,9 +63,11 @@ enum RemoveFrom { /// the removals actually take effect. /// /// journal format: +/// ``` /// [era, 0] => [ id, [insert_0, ...], [remove_0, ...] ] /// [era, 1] => [ id, [insert_0, ...], [remove_0, ...] ] /// [era, n] => [ ... ] +/// ``` /// /// When we make a new commit, we make a journal of all blocks in the recent history and record /// all keys that were inserted and deleted. The journal is ordered by era; multiple commits can @@ -80,6 +82,7 @@ enum RemoveFrom { /// which includes an original key, if any. /// /// The semantics of the `counter` are: +/// ``` /// insert key k: /// counter already contains k: count += 1 /// counter doesn't contain k: @@ -91,9 +94,11 @@ enum RemoveFrom { /// count == 1: remove counter /// count == 0: remove key from backing db /// counter doesn't contain k: remove key from backing db +/// ``` /// /// Practically, this means that for each commit block turning from recent to ancient we do the /// following: +/// ``` /// is_canonical: /// inserts: Ignored (left alone in the backing database). /// deletes: Enacted; however, recent history queue is checked for ongoing references. This is @@ -102,8 +107,9 @@ enum RemoveFrom { /// inserts: Reverted; however, recent history queue is checked for ongoing references. This is /// reduced as a preference to deletion from the backing database. /// deletes: Ignored (they were never inserted). +/// ``` /// -/// TODO: store_reclaim_period +/// TODO: `store_reclaim_period` pub struct EarlyMergeDB { overlay: MemoryDB, backing: Arc, @@ -310,7 +316,7 @@ impl HashDB for EarlyMergeDB { ret.insert(h, 1); } - for (key, refs) in self.overlay.keys().into_iter() { + for (key, refs) in self.overlay.keys() { let refs = *ret.get(&key).unwrap_or(&0) + refs; ret.insert(key, refs); } diff --git a/util/src/journaldb/overlayrecentdb.rs b/util/src/journaldb/overlayrecentdb.rs index 83868d06b..42fe84557 100644 --- a/util/src/journaldb/overlayrecentdb.rs +++ b/util/src/journaldb/overlayrecentdb.rs @@ -379,7 +379,7 @@ impl HashDB for OverlayRecentDB { ret.insert(h, 1); } - for (key, refs) in self.transaction_overlay.keys().into_iter() { + for (key, refs) in self.transaction_overlay.keys() { let refs = *ret.get(&key).unwrap_or(&0) + refs; ret.insert(key, refs); } diff --git a/util/src/journaldb/refcounteddb.rs b/util/src/journaldb/refcounteddb.rs index 57621f321..d63f8837d 100644 --- a/util/src/journaldb/refcounteddb.rs +++ b/util/src/journaldb/refcounteddb.rs @@ -36,12 +36,14 @@ use std::env; /// the removals actually take effect. /// /// journal format: +/// ``` /// [era, 0] => [ id, [insert_0, ...], [remove_0, ...] ] /// [era, 1] => [ id, [insert_0, ...], [remove_0, ...] ] /// [era, n] => [ ... ] +/// ``` /// /// when we make a new commit, we journal the inserts and removes. -/// for each end_era that we journaled that we are no passing by, +/// for each `end_era` that we journaled that we are no passing by, /// we remove all of its removes assuming it is canonical and all /// of its inserts otherwise. // TODO: store last_era, reclaim_period. diff --git a/util/src/migration/mod.rs b/util/src/migration/mod.rs index 80cfa29b6..9c1776699 100644 --- a/util/src/migration/mod.rs +++ b/util/src/migration/mod.rs @@ -231,7 +231,7 @@ impl Manager { trace!(target: "migration", "Total migrations to execute for version {}: {}", version, migrations.len()); if migrations.is_empty() { return Err(Error::MigrationImpossible) }; - let columns = migrations.iter().nth(0).and_then(|m| m.pre_columns()); + let columns = migrations.get(0).and_then(|m| m.pre_columns()); trace!(target: "migration", "Expecting database to contain {:?} columns", columns); let mut db_config = DatabaseConfig { diff --git a/util/src/overlaydb.rs b/util/src/overlaydb.rs index 9ebc7d1d4..009ef151e 100644 --- a/util/src/overlaydb.rs +++ b/util/src/overlaydb.rs @@ -66,7 +66,7 @@ impl OverlayDB { pub fn commit_to_batch(&mut self, batch: &mut DBTransaction) -> Result { let mut ret = 0u32; let mut deletes = 0usize; - for i in self.overlay.drain().into_iter() { + for i in self.overlay.drain() { let (key, (value, rc)) = i; if rc != 0 { match self.payload(&key) { @@ -133,7 +133,7 @@ impl HashDB for OverlayDB { ret.insert(h, r as i32); } - for (key, refs) in self.overlay.keys().into_iter() { + for (key, refs) in self.overlay.keys() { let refs = *ret.get(&key).unwrap_or(&0) + refs; ret.insert(key, refs); } diff --git a/util/src/trie/journal.rs b/util/src/trie/journal.rs index 49bd1bf0f..55aed70ac 100644 --- a/util/src/trie/journal.rs +++ b/util/src/trie/journal.rs @@ -84,7 +84,7 @@ impl Journal { pub fn apply(self, db: &mut HashDB) -> Score { trace!("applying {:?} changes", self.0.len()); let mut ret = Score{inserts: 0, removes: 0}; - for d in self.0.into_iter() { + for d in self.0 { match d { Operation::Delete(h) => { trace!("TrieDBMut::apply --- {:?}", &h); diff --git a/util/src/trie/triedb.rs b/util/src/trie/triedb.rs index ad1e509a0..cd8c9939a 100644 --- a/util/src/trie/triedb.rs +++ b/util/src/trie/triedb.rs @@ -87,7 +87,7 @@ impl<'db> TrieDB<'db> { /// Convert a vector of hashes to a hashmap of hash to occurrences. pub fn to_map(hashes: Vec) -> HashMap { let mut r: HashMap = HashMap::new(); - for h in hashes.into_iter() { + for h in hashes { *r.entry(h).or_insert(0) += 1; } r @@ -97,7 +97,7 @@ impl<'db> TrieDB<'db> { /// trie. pub fn db_items_remaining(&self) -> super::Result> { let mut ret = self.db.keys(); - for (k, v) in Self::to_map(try!(self.keys())).into_iter() { + for (k, v) in Self::to_map(try!(self.keys())) { let keycount = *ret.get(&k).unwrap_or(&0); match keycount <= v as i32 { true => ret.remove(&k), From 6c79decddaca40ee577421415c58dc1a3c02f264 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Thu, 27 Oct 2016 06:30:05 +0000 Subject: [PATCH 025/192] [ci skip] js-precompiled 20161027-062905 --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index a58a426a4..aea550503 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1211,7 +1211,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#9d77b0117c2fa0b17dd381853b0020b1fdc96855" +source = "git+https://github.com/ethcore/js-precompiled.git#a27ea0e6a88c8f0e6a1556819447f9e946549552" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] From ae46361b3302f35d3e11579a9707841435f07440 Mon Sep 17 00:00:00 2001 From: "Denis S. Soldatov aka General-Beck" Date: Thu, 27 Oct 2016 13:42:09 +0700 Subject: [PATCH 026/192] Update gitlab-ci fix windows HOST_CC HOST_CXX -> x86_64-pc-windows-msvc --- .gitlab-ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 17907b49a..46fddb579 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -311,6 +311,8 @@ windows: - set LIB=C:\vs2015\VC\lib;C:\Program Files (x86)\Windows Kits\10\Lib\10.0.10240.0\ucrt\x64 - set RUST_BACKTRACE=1 - set RUSTFLAGS=%RUSTFLAGS% -Zorbit=off -D warnings + - set HOST_CC=x86_64-pc-windows-msvc + - set HOST_CXX=x86_64-pc-windows-msvc - rustup default stable-x86_64-pc-windows-msvc - cargo build --release --verbose - curl -sL --url "https://github.com/ethcore/win-build/raw/master/SimpleFC.dll" -o nsis\SimpleFC.dll From 8e6ca7fc613c019817a8bf79bc7e547f3e421f7d Mon Sep 17 00:00:00 2001 From: "Denis S. Soldatov aka General-Beck" Date: Thu, 27 Oct 2016 13:45:16 +0700 Subject: [PATCH 027/192] Update gitlab-ci remove armv6 from master [ci skip] --- .gitlab-ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 46fddb579..17d6fe985 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -214,7 +214,6 @@ linux-armv6: stage: build image: ethcore/rust-armv6:latest only: - - master - beta - tags - stable From 005bdd5d0737a5550771926558a339c439668d4c Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Thu, 27 Oct 2016 06:55:53 +0000 Subject: [PATCH 028/192] [ci skip] js-precompiled 20161027-065456 --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index aea550503..7cbf8df18 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1211,7 +1211,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#a27ea0e6a88c8f0e6a1556819447f9e946549552" +source = "git+https://github.com/ethcore/js-precompiled.git#62f407081dfa20ad534b9ee555494978b76211b5" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] From 3347d590427346728a5d3593935c58fb937d04d6 Mon Sep 17 00:00:00 2001 From: "Denis S. Soldatov aka General-Beck" Date: Thu, 27 Oct 2016 14:00:05 +0700 Subject: [PATCH 029/192] Update gitlab-ci add to arm* export HOST_CC=gcc export HOST_CXX=g++ --- .gitlab-ci.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 17d6fe985..2a456e9eb 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -6,8 +6,6 @@ variables: SIMPLECOV: "true" RUST_BACKTRACE: "1" RUSTFLAGS: "-D warnings" - HOST_CC: "gcc" - HOST_CXX: "g++" cache: key: "$CI_BUILD_NAME/$CI_BUILD_REF_NAME" untracked: true @@ -145,6 +143,8 @@ linux-armv7: script: - export CC=arm-linux-gnueabihf-gcc - export CXX=arm-linux-gnueabihf-g++ + - export HOST_CC=gcc + - export HOST_CXX=g++ - rm -rf .cargo - mkdir -p .cargo - echo "[target.armv7-unknown-linux-gnueabihf]" >> .cargo/config @@ -183,6 +183,8 @@ linux-arm: script: - export CC=arm-linux-gnueabihf-gcc - export CXX=arm-linux-gnueabihf-g++ + - export HOST_CC=gcc + - export HOST_CXX=g++ - rm -rf .cargo - mkdir -p .cargo - echo "[target.arm-unknown-linux-gnueabihf]" >> .cargo/config @@ -220,6 +222,8 @@ linux-armv6: script: - export CC=arm-linux-gnueabi-gcc - export CXX=arm-linux-gnueabi-g++ + - export HOST_CC=gcc + - export HOST_CXX=g++ - rm -rf .cargo - mkdir -p .cargo - echo "[target.arm-unknown-linux-gnueabi]" >> .cargo/config @@ -251,6 +255,8 @@ linux-aarch64: script: - export CC=aarch64-linux-gnu-gcc - export CXX=aarch64-linux-gnu-g++ + - export HOST_CC=gcc + - export HOST_CXX=g++ - rm -rf .cargo - mkdir -p .cargo - echo "[target.aarch64-unknown-linux-gnu]" >> .cargo/config @@ -310,8 +316,6 @@ windows: - set LIB=C:\vs2015\VC\lib;C:\Program Files (x86)\Windows Kits\10\Lib\10.0.10240.0\ucrt\x64 - set RUST_BACKTRACE=1 - set RUSTFLAGS=%RUSTFLAGS% -Zorbit=off -D warnings - - set HOST_CC=x86_64-pc-windows-msvc - - set HOST_CXX=x86_64-pc-windows-msvc - rustup default stable-x86_64-pc-windows-msvc - cargo build --release --verbose - curl -sL --url "https://github.com/ethcore/win-build/raw/master/SimpleFC.dll" -o nsis\SimpleFC.dll From 02b6d3943dc3f20e2646d30640ac2b84dfd2888d Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Thu, 27 Oct 2016 07:09:06 +0000 Subject: [PATCH 030/192] [ci skip] js-precompiled 20161027-070811 --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 7cbf8df18..4fbed7ddb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1211,7 +1211,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#62f407081dfa20ad534b9ee555494978b76211b5" +source = "git+https://github.com/ethcore/js-precompiled.git#4fdd06b96f86061ee57bc4ca81ddf314ba2931c4" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] From bdc372462afc63f88711e77f5bc40d51f8fc4cbb Mon Sep 17 00:00:00 2001 From: Arkadiy Paronyan Date: Thu, 27 Oct 2016 13:49:44 +0200 Subject: [PATCH 031/192] Change sync protocol ID (#2912) --- sync/src/api.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sync/src/api.rs b/sync/src/api.rs index b227dcd60..67a81237a 100644 --- a/sync/src/api.rs +++ b/sync/src/api.rs @@ -34,7 +34,7 @@ use std::str::FromStr; use parking_lot::RwLock; use chain::{ETH_PACKET_COUNT, SNAPSHOT_SYNC_PACKET_COUNT}; -pub const WARP_SYNC_PROTOCOL_ID: ProtocolId = *b"bam"; +pub const WARP_SYNC_PROTOCOL_ID: ProtocolId = *b"par"; /// Sync configuration #[derive(Debug, Clone, Copy)] From 176dc0e945696eeeeeb33ee997de33f64c4fd312 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Thu, 27 Oct 2016 11:51:52 +0000 Subject: [PATCH 032/192] [ci skip] js-precompiled 20161027-115054 --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 4fbed7ddb..cad330266 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1211,7 +1211,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#4fdd06b96f86061ee57bc4ca81ddf314ba2931c4" +source = "git+https://github.com/ethcore/js-precompiled.git#c1dc23ab7a8a01aee8587915895fd0125695f775" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] From 3edd9e4bee49af6ab72c995f3d900266862ea2c9 Mon Sep 17 00:00:00 2001 From: Arkadiy Paronyan Date: Thu, 27 Oct 2016 15:25:54 +0200 Subject: [PATCH 033/192] Fixed GetNodeData output (#2892) --- sync/src/chain.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/sync/src/chain.rs b/sync/src/chain.rs index 764eccdac..d18adb6ea 100644 --- a/sync/src/chain.rs +++ b/sync/src/chain.rs @@ -1428,16 +1428,18 @@ impl ChainSync { } count = min(count, MAX_NODE_DATA_TO_SEND); let mut added = 0usize; - let mut data = Bytes::new(); + let mut data = Vec::new(); for i in 0..count { - if let Some(mut hdr) = io.chain().state_data(&try!(r.val_at::(i))) { - data.append(&mut hdr); + if let Some(hdr) = io.chain().state_data(&try!(r.val_at::(i))) { + data.push(hdr); added += 1; } } trace!(target: "sync", "{} -> GetNodeData: return {} entries", peer_id, added); let mut rlp = RlpStream::new_list(added); - rlp.append_raw(&data, added); + for d in data.into_iter() { + rlp.append(&d); + } Ok(Some((NODE_DATA_PACKET, rlp))) } @@ -2026,7 +2028,9 @@ mod tests { assert!(rlp_result.is_some()); // the length of one rlp-encoded hashe - assert_eq!(34, rlp_result.unwrap().1.out().len()); + let rlp = rlp_result.unwrap().1.out(); + let rlp = Rlp::new(&rlp); + assert_eq!(1, rlp.item_count()); io.sender = Some(2usize); From d315ec29e190dc14d628aaa0882d39533f0bb13f Mon Sep 17 00:00:00 2001 From: Nikolay Volf Date: Thu, 27 Oct 2016 16:26:29 +0300 Subject: [PATCH 034/192] Apply pending block details on commit (#2254) * failing test * Cache pending details * [ci skip] updated comment --- ethcore/src/blockchain/blockchain.rs | 30 +++++++++++++++++----------- ethcore/src/client/client.rs | 30 ++++++++++++++++++++++++++++ ethcore/src/tests/helpers.rs | 9 +++++++-- 3 files changed, 55 insertions(+), 14 deletions(-) diff --git a/ethcore/src/blockchain/blockchain.rs b/ethcore/src/blockchain/blockchain.rs index 32d42fe31..282039c16 100644 --- a/ethcore/src/blockchain/blockchain.rs +++ b/ethcore/src/blockchain/blockchain.rs @@ -196,6 +196,7 @@ pub struct BlockChain { pending_best_block: RwLock>, pending_block_hashes: RwLock>, + pending_block_details: RwLock>, pending_transaction_addresses: RwLock>>, } @@ -439,6 +440,7 @@ impl BlockChain { cache_man: Mutex::new(cache_man), pending_best_block: RwLock::new(None), pending_block_hashes: RwLock::new(HashMap::new()), + pending_block_details: RwLock::new(HashMap::new()), pending_transaction_addresses: RwLock::new(HashMap::new()), }; @@ -895,17 +897,6 @@ impl BlockChain { /// Prepares extras update. fn prepare_update(&self, batch: &mut DBTransaction, update: ExtrasUpdate, is_best: bool) { - { - let block_hashes: Vec<_> = update.block_details.keys().cloned().collect(); - - let mut write_details = self.block_details.write(); - batch.extend_with_cache(db::COL_EXTRA, &mut *write_details, update.block_details, CacheUpdatePolicy::Overwrite); - - let mut cache_man = self.cache_man.lock(); - for hash in block_hashes { - cache_man.note_used(CacheID::BlockDetails(hash)); - } - } { let mut write_receipts = self.block_receipts.write(); @@ -917,7 +908,7 @@ impl BlockChain { batch.extend_with_cache(db::COL_EXTRA, &mut *write_blocks_blooms, update.blocks_blooms, CacheUpdatePolicy::Remove); } - // These cached values must be updated last with all three locks taken to avoid + // These cached values must be updated last with all four locks taken to avoid // cache decoherence { let mut best_block = self.pending_best_block.write(); @@ -935,8 +926,10 @@ impl BlockChain { }, } let mut write_hashes = self.pending_block_hashes.write(); + let mut write_details = self.pending_block_details.write(); let mut write_txs = self.pending_transaction_addresses.write(); + batch.extend_with_cache(db::COL_EXTRA, &mut *write_details, update.block_details, CacheUpdatePolicy::Overwrite); batch.extend_with_cache(db::COL_EXTRA, &mut *write_hashes, update.block_hashes, CacheUpdatePolicy::Overwrite); batch.extend_with_option_cache(db::COL_EXTRA, &mut *write_txs, update.transactions_addresses, CacheUpdatePolicy::Overwrite); } @@ -946,9 +939,11 @@ impl BlockChain { pub fn commit(&self) { let mut pending_best_block = self.pending_best_block.write(); let mut pending_write_hashes = self.pending_block_hashes.write(); + let mut pending_block_details = self.pending_block_details.write(); let mut pending_write_txs = self.pending_transaction_addresses.write(); let mut best_block = self.best_block.write(); + let mut write_block_details = self.block_details.write(); let mut write_hashes = self.block_hashes.write(); let mut write_txs = self.transaction_addresses.write(); // update best block @@ -961,9 +956,11 @@ impl BlockChain { let pending_hashes_keys: Vec<_> = pending_write_hashes.keys().cloned().collect(); let enacted_txs_keys: Vec<_> = enacted_txs.keys().cloned().collect(); + let pending_block_hashes: Vec<_> = pending_block_details.keys().cloned().collect(); write_hashes.extend(mem::replace(&mut *pending_write_hashes, HashMap::new())); write_txs.extend(enacted_txs.into_iter().map(|(k, v)| (k, v.expect("Transactions were partitioned; qed")))); + write_block_details.extend(mem::replace(&mut *pending_block_details, HashMap::new())); for hash in retracted_txs.keys() { write_txs.remove(hash); @@ -977,6 +974,10 @@ impl BlockChain { for hash in enacted_txs_keys { cache_man.note_used(CacheID::TransactionAddresses(hash)); } + + for hash in pending_block_hashes { + cache_man.note_used(CacheID::BlockDetails(hash)); + } } /// Iterator that lists `first` and then all of `first`'s ancestors, by hash. @@ -1297,6 +1298,11 @@ impl BlockChain { ancient_block_number: best_ancient_block.as_ref().map(|b| b.number), } } + + #[cfg(test)] + pub fn db(&self) -> &Arc { + &self.db + } } #[cfg(test)] diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index cb963dadc..b59d892c5 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -1216,3 +1216,33 @@ impl MayPanic for Client { self.panic_handler.on_panic(closure); } } + + +#[test] +fn should_not_cache_details_before_commit() { + use tests::helpers::*; + use std::thread; + use std::time::Duration; + use std::sync::atomic::{AtomicBool, Ordering}; + + let client = generate_dummy_client(0); + let genesis = client.chain_info().best_block_hash; + let (new_hash, new_block) = get_good_dummy_block_hash(); + + let go = { + // Separate thread uncommited transaction + let go = Arc::new(AtomicBool::new(false)); + let go_thread = go.clone(); + let another_client = client.reference().clone(); + thread::spawn(move || { + let mut batch = DBTransaction::new(&*another_client.chain.read().db().clone()); + another_client.chain.read().insert_block(&mut batch, &new_block, Vec::new()); + go_thread.store(true, Ordering::SeqCst); + }); + go + }; + + while !go.load(Ordering::SeqCst) { thread::park_timeout(Duration::from_millis(5)); } + + assert!(client.tree_route(&genesis, &new_hash).is_none()); +} diff --git a/ethcore/src/tests/helpers.rs b/ethcore/src/tests/helpers.rs index 7b8264720..7b952b30c 100644 --- a/ethcore/src/tests/helpers.rs +++ b/ethcore/src/tests/helpers.rs @@ -388,7 +388,7 @@ pub fn get_good_dummy_block_fork_seq(start_number: usize, count: usize, parent_h r } -pub fn get_good_dummy_block() -> Bytes { +pub fn get_good_dummy_block_hash() -> (H256, Bytes) { let mut block_header = Header::new(); let test_spec = get_test_spec(); let test_engine = &test_spec.engine; @@ -399,7 +399,12 @@ pub fn get_good_dummy_block() -> Bytes { block_header.set_parent_hash(test_spec.genesis_header().hash()); block_header.set_state_root(test_spec.genesis_header().state_root().clone()); - create_test_block(&block_header) + (block_header.hash(), create_test_block(&block_header)) +} + +pub fn get_good_dummy_block() -> Bytes { + let (_, bytes) = get_good_dummy_block_hash(); + bytes } pub fn get_bad_state_dummy_block() -> Bytes { From 123b75179c2b047a08093ce9947ddd45547fd880 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Thu, 27 Oct 2016 13:40:42 +0000 Subject: [PATCH 035/192] [ci skip] js-precompiled 20161027-133941 --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index cad330266..9bf28cca9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1211,7 +1211,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#c1dc23ab7a8a01aee8587915895fd0125695f775" +source = "git+https://github.com/ethcore/js-precompiled.git#3fd329340b2753da0f9762d45e65960878c5d008" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] From 6978042c16e26112214f89aa97b6db7ae59fa479 Mon Sep 17 00:00:00 2001 From: arkpar Date: Thu, 27 Oct 2016 16:20:11 +0200 Subject: [PATCH 036/192] Packet id tracing --- util/network/src/session.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/util/network/src/session.rs b/util/network/src/session.rs index 8d5578e83..6d6009535 100644 --- a/util/network/src/session.rs +++ b/util/network/src/session.rs @@ -395,7 +395,7 @@ impl Session { PACKET_PEERS => Ok(SessionData::None), PACKET_USER ... PACKET_LAST => { let mut i = 0usize; - while packet_id > self.info.capabilities[i].id_offset + self.info.capabilities[i].packet_count { + while packet_id >= self.info.capabilities[i].id_offset + self.info.capabilities[i].packet_count { i += 1; if i == self.info.capabilities.len() { debug!(target: "network", "Unknown packet: {:?}", packet_id); @@ -406,6 +406,7 @@ impl Session { // map to protocol let protocol = self.info.capabilities[i].protocol; let pid = packet_id - self.info.capabilities[i].id_offset; + trace!(target: "network", "Packet {} mapped to {:?}:{}, i={}, capabilities={:?}", packet_id, protocol, pid, i, self.info.capabilities); Ok(SessionData::Packet { data: packet.data, protocol: protocol, packet_id: pid } ) }, _ => { From 7c6112e9f091fb525f5960249b2b66f587fe9c02 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Thu, 27 Oct 2016 14:33:50 +0000 Subject: [PATCH 037/192] [ci skip] js-precompiled 20161027-143256 --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 9bf28cca9..14a8e1f01 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1211,7 +1211,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#3fd329340b2753da0f9762d45e65960878c5d008" +source = "git+https://github.com/ethcore/js-precompiled.git#10c0948cd0986df03ba9b2e5fae928f1a5618c9a" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] From 8cef5177885a94d044734a09352708b91ed3cca1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 27 Oct 2016 16:56:18 +0200 Subject: [PATCH 038/192] Fixing config values for pruning_history (#2918) --- parity/cli/mod.rs | 14 ++++++++++++++ parity/cli/usage.txt | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/parity/cli/mod.rs b/parity/cli/mod.rs index 27e0cb4dc..9290ab0f9 100644 --- a/parity/cli/mod.rs +++ b/parity/cli/mod.rs @@ -445,6 +445,20 @@ mod tests { assert_eq!(args.flag_chain, "xyz".to_owned()); } + #[test] + fn should_use_config_if_cli_is_missing() { + let mut config = Config::default(); + let mut footprint = Footprint::default(); + footprint.pruning_history = Some(128); + config.footprint = Some(footprint); + + // when + let args = Args::parse_with_config(&["parity"], config).unwrap(); + + // then + assert_eq!(args.flag_pruning_history, 128); + } + #[test] fn should_parse_full_config() { // given diff --git a/parity/cli/usage.txt b/parity/cli/usage.txt index af8e83f0e..bebdafd97 100644 --- a/parity/cli/usage.txt +++ b/parity/cli/usage.txt @@ -225,7 +225,7 @@ Footprint Options: auto - use the method most recently synced or default to fast if none synced (default: {flag_pruning}). --pruning-history NUM Set a number of recent states to keep when pruning - is active. [default: {flag_pruning_history}]. + is active. (default: {flag_pruning_history}). --cache-size-db MB Override database cache size (default: {flag_cache_size_db}). --cache-size-blocks MB Specify the prefered size of the blockchain cache in megabytes (default: {flag_cache_size_blocks}). From bb14eea66cda190b49eaab2f4bc58217089e9a3a Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Thu, 27 Oct 2016 15:09:11 +0000 Subject: [PATCH 039/192] [ci skip] js-precompiled 20161027-150816 --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 14a8e1f01..df75941da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1211,7 +1211,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#10c0948cd0986df03ba9b2e5fae928f1a5618c9a" +source = "git+https://github.com/ethcore/js-precompiled.git#0668ff1d25ddd1db11f44a1b1e6d050e41b3841b" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] From 90aa2fefc2382d8a34fb643072f524dca7a47825 Mon Sep 17 00:00:00 2001 From: "Denis S. Soldatov aka General-Beck" Date: Thu, 27 Oct 2016 23:13:51 +0700 Subject: [PATCH 040/192] Update gitlab-ci fix darwin md5 --- .gitlab-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2a456e9eb..3d822a9d5 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -293,6 +293,7 @@ darwin: - stable script: - cargo build --release --verbose + - rm -rf parity.md5 - md5sum target/release/parity >> parity.md5 - aws configure set aws_access_key_id $s3_key - aws configure set aws_secret_access_key $s3_secret From abb1da5f4b8b02ce164a6dd5459bb7c5883ce8f6 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Thu, 27 Oct 2016 16:49:18 +0000 Subject: [PATCH 041/192] [ci skip] js-precompiled 20161027-164817 --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index df75941da..71684d5c4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1211,7 +1211,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#0668ff1d25ddd1db11f44a1b1e6d050e41b3841b" +source = "git+https://github.com/ethcore/js-precompiled.git#0053be493c742cbd7332ba647b3c84d35327abd7" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] From 8dff4012a68c35fe50450364b617d7c1a7045c17 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Thu, 27 Oct 2016 19:26:34 +0200 Subject: [PATCH 042/192] Personal split (#2879) * Split personal namespace into Safe and Unsafe part * Re-add api.personal.accountsInfo() calls to dapps * Removing listGethAccounts from safe personal --- .../basiccoin/Application/application.js | 4 +- .../dapps/gavcoin/Application/application.js | 2 +- js/src/dapps/githubhint/services.js | 2 +- js/src/dapps/registry/addresses/actions.js | 12 +- js/src/dapps/signaturereg/services.js | 2 +- js/src/dapps/tokenreg/Accounts/actions.js | 2 +- parity/cli/config.full.toml | 4 +- parity/cli/mod.rs | 8 +- parity/cli/usage.txt | 2 +- parity/rpc_apis.rs | 27 ++- rpc/src/v1/impls/mod.rs | 2 + rpc/src/v1/impls/personal.rs | 154 +------------- rpc/src/v1/impls/personal_accounts.rs | 194 ++++++++++++++++++ rpc/src/v1/mod.rs | 2 +- rpc/src/v1/tests/mocked/personal.rs | 6 +- rpc/src/v1/traits/mod.rs | 2 +- rpc/src/v1/traits/personal.rs | 50 +++-- 17 files changed, 276 insertions(+), 199 deletions(-) create mode 100644 rpc/src/v1/impls/personal_accounts.rs diff --git a/js/src/dapps/basiccoin/Application/application.js b/js/src/dapps/basiccoin/Application/application.js index d84085c98..4ab97ab6c 100644 --- a/js/src/dapps/basiccoin/Application/application.js +++ b/js/src/dapps/basiccoin/Application/application.js @@ -16,7 +16,7 @@ import React, { Component, PropTypes } from 'react'; -// import { api } from '../parity'; +import { api } from '../parity'; import { attachInstances } from '../services'; import Header from './Header'; @@ -83,7 +83,7 @@ export default class Application extends Component { Promise .all([ attachInstances(), - null // api.personal.accountsInfo() + api.personal.accountsInfo() ]) .then(([{ managerInstance, registryInstance, tokenregInstance }, accountsInfo]) => { accountsInfo = accountsInfo || {}; diff --git a/js/src/dapps/gavcoin/Application/application.js b/js/src/dapps/gavcoin/Application/application.js index 29c86c78d..d5d26bc3e 100644 --- a/js/src/dapps/gavcoin/Application/application.js +++ b/js/src/dapps/gavcoin/Application/application.js @@ -206,7 +206,7 @@ export default class Application extends Component { .all([ registry.getAddress.call({}, [api.util.sha3('gavcoin'), 'A']), api.eth.accounts(), - null // api.personal.accountsInfo() + api.personal.accountsInfo() ]); }) .then(([address, addresses, infos]) => { diff --git a/js/src/dapps/githubhint/services.js b/js/src/dapps/githubhint/services.js index 1904be2d7..b7676d5f5 100644 --- a/js/src/dapps/githubhint/services.js +++ b/js/src/dapps/githubhint/services.js @@ -29,7 +29,7 @@ export function attachInterface () { .all([ registry.getAddress.call({}, [api.util.sha3('githubhint'), 'A']), api.eth.accounts(), - null // api.personal.accountsInfo() + api.personal.accountsInfo() ]); }) .then(([address, addresses, accountsInfo]) => { diff --git a/js/src/dapps/registry/addresses/actions.js b/js/src/dapps/registry/addresses/actions.js index dfd7d16a3..17975f9e6 100644 --- a/js/src/dapps/registry/addresses/actions.js +++ b/js/src/dapps/registry/addresses/actions.js @@ -22,12 +22,16 @@ export const fetch = () => (dispatch) => { return Promise .all([ api.eth.accounts(), - null // api.personal.accountsInfo() + api.personal.accountsInfo() ]) .then(([ accounts, data ]) => { - const addresses = accounts.map((address) => { - return { address, isAccount: true }; - }); + data = data || {}; + const addresses = Object.keys(data) + .filter((address) => data[address] && !data[address].meta.deleted) + .map((address) => ({ + ...data[address], address, + isAccount: accounts.includes(address) + })); dispatch(set(addresses)); }) .catch((error) => { diff --git a/js/src/dapps/signaturereg/services.js b/js/src/dapps/signaturereg/services.js index 7219ddff1..cab324f7e 100644 --- a/js/src/dapps/signaturereg/services.js +++ b/js/src/dapps/signaturereg/services.js @@ -50,7 +50,7 @@ export function attachInterface (callback) { .all([ registry.getAddress.call({}, [api.util.sha3('signaturereg'), 'A']), api.eth.accounts(), - null // api.personal.accountsInfo() + api.personal.accountsInfo() ]); }) .then(([address, addresses, accountsInfo]) => { diff --git a/js/src/dapps/tokenreg/Accounts/actions.js b/js/src/dapps/tokenreg/Accounts/actions.js index f093b5300..f501399c2 100644 --- a/js/src/dapps/tokenreg/Accounts/actions.js +++ b/js/src/dapps/tokenreg/Accounts/actions.js @@ -38,7 +38,7 @@ export const loadAccounts = () => (dispatch) => { Promise .all([ api.eth.accounts(), - null // api.personal.accountsInfo() + api.personal.accountsInfo() ]) .then(([ accounts, accountsInfo ]) => { accountsInfo = accountsInfo || {}; diff --git a/parity/cli/config.full.toml b/parity/cli/config.full.toml index fd2e11f98..4d1d48025 100644 --- a/parity/cli/config.full.toml +++ b/parity/cli/config.full.toml @@ -41,13 +41,13 @@ disable = false port = 8545 interface = "local" cors = "null" -apis = ["web3", "eth", "net", "ethcore", "traces", "rpc"] +apis = ["web3", "eth", "net", "ethcore", "traces", "rpc", "personal_safe"] hosts = ["none"] [ipc] disable = false path = "$HOME/.parity/jsonrpc.ipc" -apis = ["web3", "eth", "net", "ethcore", "traces", "rpc"] +apis = ["web3", "eth", "net", "ethcore", "traces", "rpc", "personal_safe"] [dapps] disable = false diff --git a/parity/cli/mod.rs b/parity/cli/mod.rs index 9290ab0f9..68411414f 100644 --- a/parity/cli/mod.rs +++ b/parity/cli/mod.rs @@ -145,7 +145,7 @@ usage! { or |c: &Config| otry!(c.rpc).interface.clone(), flag_jsonrpc_cors: Option = None, or |c: &Config| otry!(c.rpc).cors.clone().map(Some), - flag_jsonrpc_apis: String = "web3,eth,net,ethcore,traces,rpc", + flag_jsonrpc_apis: String = "web3,eth,net,ethcore,traces,rpc,personal_safe", or |c: &Config| otry!(c.rpc).apis.clone().map(|vec| vec.join(",")), flag_jsonrpc_hosts: String = "none", or |c: &Config| otry!(c.rpc).hosts.clone().map(|vec| vec.join(",")), @@ -155,7 +155,7 @@ usage! { or |c: &Config| otry!(c.ipc).disable.clone(), flag_ipc_path: String = "$HOME/.parity/jsonrpc.ipc", or |c: &Config| otry!(c.ipc).path.clone(), - flag_ipc_apis: String = "web3,eth,net,ethcore,traces,rpc", + flag_ipc_apis: String = "web3,eth,net,ethcore,traces,rpc,personal_safe", or |c: &Config| otry!(c.ipc).apis.clone().map(|vec| vec.join(",")), // DAPPS @@ -534,13 +534,13 @@ mod tests { flag_jsonrpc_port: 8545u16, flag_jsonrpc_interface: "local".into(), flag_jsonrpc_cors: Some("null".into()), - flag_jsonrpc_apis: "web3,eth,net,ethcore,traces,rpc".into(), + flag_jsonrpc_apis: "web3,eth,net,ethcore,traces,rpc,personal_safe".into(), flag_jsonrpc_hosts: "none".into(), // IPC flag_no_ipc: false, flag_ipc_path: "$HOME/.parity/jsonrpc.ipc".into(), - flag_ipc_apis: "web3,eth,net,ethcore,traces,rpc".into(), + flag_ipc_apis: "web3,eth,net,ethcore,traces,rpc,personal_safe".into(), // DAPPS flag_no_dapps: false, diff --git a/parity/cli/usage.txt b/parity/cli/usage.txt index bebdafd97..d96d875cf 100644 --- a/parity/cli/usage.txt +++ b/parity/cli/usage.txt @@ -107,7 +107,7 @@ API and Console Options: --jsonrpc-apis APIS Specify the APIs available through the JSONRPC interface. APIS is a comma-delimited list of API name. Possible name are web3, eth, net, personal, - ethcore, ethcore_set, traces, rpc. + ethcore, ethcore_set, traces, rpc, personal_safe. (default: {flag_jsonrpc_apis}). --jsonrpc-hosts HOSTS List of allowed Host header values. This option will validate the Host header sent by the browser, it diff --git a/parity/rpc_apis.rs b/parity/rpc_apis.rs index f6ccf16a3..491f58a1b 100644 --- a/parity/rpc_apis.rs +++ b/parity/rpc_apis.rs @@ -33,7 +33,8 @@ pub enum Api { Web3, Net, Eth, - Personal, + PersonalSafe, + PersonalAccounts, Signer, Ethcore, EthcoreSet, @@ -51,7 +52,8 @@ impl FromStr for Api { "web3" => Ok(Web3), "net" => Ok(Net), "eth" => Ok(Eth), - "personal" => Ok(Personal), + "personal" => Ok(PersonalAccounts), + "personal_safe" => Ok(PersonalSafe), "signer" => Ok(Signer), "ethcore" => Ok(Ethcore), "ethcore_set" => Ok(EthcoreSet), @@ -114,7 +116,8 @@ fn to_modules(apis: &[Api]) -> BTreeMap { Api::Web3 => ("web3", "1.0"), Api::Net => ("net", "1.0"), Api::Eth => ("eth", "1.0"), - Api::Personal => ("personal", "1.0"), + Api::PersonalSafe => ("personal_safe", "1.0"), + Api::PersonalAccounts => ("personal", "1.0"), Api::Signer => ("signer", "1.0"), Api::Ethcore => ("ethcore", "1.0"), Api::EthcoreSet => ("ethcore_set", "1.0"), @@ -131,11 +134,11 @@ impl ApiSet { match *self { ApiSet::List(ref apis) => apis.clone(), ApiSet::UnsafeContext => { - vec![Api::Web3, Api::Net, Api::Eth, Api::Ethcore, Api::Traces, Api::Rpc] + vec![Api::Web3, Api::Net, Api::Eth, Api::Ethcore, Api::Traces, Api::Rpc, Api::PersonalSafe] .into_iter().collect() }, ApiSet::SafeContext => { - vec![Api::Web3, Api::Net, Api::Eth, Api::Personal, Api::Signer, Api::Ethcore, Api::EthcoreSet, Api::Traces, Api::Rpc] + vec![Api::Web3, Api::Net, Api::Eth, Api::PersonalAccounts, Api::PersonalSafe, Api::Signer, Api::Ethcore, Api::EthcoreSet, Api::Traces, Api::Rpc] .into_iter().collect() }, } @@ -178,8 +181,11 @@ pub fn setup_rpc(server: T, deps: Arc, apis: ApiSet server.add_delegate(EthSigningUnsafeClient::new(&deps.client, &deps.secret_store, &deps.miner).to_delegate()); } }, - Api::Personal => { - server.add_delegate(PersonalClient::new(&deps.secret_store, &deps.client, &deps.miner, deps.geth_compatibility).to_delegate()); + Api::PersonalAccounts => { + server.add_delegate(PersonalAccountsClient::new(&deps.secret_store, &deps.client, &deps.miner, deps.geth_compatibility).to_delegate()); + }, + Api::PersonalSafe => { + server.add_delegate(PersonalClient::new(&deps.secret_store, &deps.client).to_delegate()); }, Api::Signer => { server.add_delegate(SignerClient::new(&deps.secret_store, &deps.client, &deps.miner, &deps.signer_service).to_delegate()); @@ -224,7 +230,8 @@ mod test { assert_eq!(Api::Web3, "web3".parse().unwrap()); assert_eq!(Api::Net, "net".parse().unwrap()); assert_eq!(Api::Eth, "eth".parse().unwrap()); - assert_eq!(Api::Personal, "personal".parse().unwrap()); + assert_eq!(Api::PersonalAccounts, "personal".parse().unwrap()); + assert_eq!(Api::PersonalSafe, "personal_safe".parse().unwrap()); assert_eq!(Api::Signer, "signer".parse().unwrap()); assert_eq!(Api::Ethcore, "ethcore".parse().unwrap()); assert_eq!(Api::EthcoreSet, "ethcore_set".parse().unwrap()); @@ -245,14 +252,14 @@ mod test { #[test] fn test_api_set_unsafe_context() { - let expected = vec![Api::Web3, Api::Net, Api::Eth, Api::Ethcore, Api::Traces, Api::Rpc] + let expected = vec![Api::Web3, Api::Net, Api::Eth, Api::Ethcore, Api::Traces, Api::Rpc, Api::PersonalSafe] .into_iter().collect(); assert_eq!(ApiSet::UnsafeContext.list_apis(), expected); } #[test] fn test_api_set_safe_context() { - let expected = vec![Api::Web3, Api::Net, Api::Eth, Api::Personal, Api::Signer, Api::Ethcore, Api::EthcoreSet, Api::Traces, Api::Rpc] + let expected = vec![Api::Web3, Api::Net, Api::Eth, Api::PersonalAccounts, Api::PersonalSafe, Api::Signer, Api::Ethcore, Api::EthcoreSet, Api::Traces, Api::Rpc] .into_iter().collect(); assert_eq!(ApiSet::SafeContext.list_apis(), expected); } diff --git a/rpc/src/v1/impls/mod.rs b/rpc/src/v1/impls/mod.rs index bf2013d99..c108f0b6b 100644 --- a/rpc/src/v1/impls/mod.rs +++ b/rpc/src/v1/impls/mod.rs @@ -32,6 +32,7 @@ mod ethcore; mod ethcore_set; mod net; mod personal; +mod personal_accounts; mod personal_signer; mod rpc; mod traces; @@ -43,6 +44,7 @@ pub use self::eth_filter::EthFilterClient; pub use self::eth_signing::{EthSigningUnsafeClient, EthSigningQueueClient}; pub use self::net::NetClient; pub use self::personal::PersonalClient; +pub use self::personal_accounts::PersonalAccountsClient; pub use self::personal_signer::SignerClient; pub use self::ethcore::EthcoreClient; pub use self::ethcore_set::EthcoreSetClient; diff --git a/rpc/src/v1/impls/personal.rs b/rpc/src/v1/impls/personal.rs index 0d6b63240..aacf90c91 100644 --- a/rpc/src/v1/impls/personal.rs +++ b/rpc/src/v1/impls/personal.rs @@ -17,34 +17,26 @@ //! Account management (personal) rpc implementation use std::sync::{Arc, Weak}; use std::collections::{BTreeMap}; -use util::{Address}; use jsonrpc_core::*; -use ethkey::{Brain, Generator}; use v1::traits::Personal; -use v1::types::{H160 as RpcH160, TransactionRequest}; +use v1::types::{H160 as RpcH160}; use v1::helpers::errors; use v1::helpers::params::expect_no_params; -use v1::helpers::dispatch::sign_and_dispatch; use ethcore::account_provider::AccountProvider; use ethcore::client::MiningBlockChainClient; -use ethcore::miner::MinerService; /// Account management (personal) rpc implementation. -pub struct PersonalClient where C: MiningBlockChainClient, M: MinerService { +pub struct PersonalClient where C: MiningBlockChainClient { accounts: Weak, client: Weak, - miner: Weak, - allow_perm_unlock: bool, } -impl PersonalClient where C: MiningBlockChainClient, M: MinerService { +impl PersonalClient where C: MiningBlockChainClient { /// Creates new PersonalClient - pub fn new(store: &Arc, client: &Arc, miner: &Arc, allow_perm_unlock: bool) -> Self { + pub fn new(store: &Arc, client: &Arc) -> Self { PersonalClient { accounts: Arc::downgrade(store), client: Arc::downgrade(client), - miner: Arc::downgrade(miner), - allow_perm_unlock: allow_perm_unlock, } } @@ -55,7 +47,7 @@ impl PersonalClient where C: MiningBlockChainClient, M: MinerService } } -impl Personal for PersonalClient where C: MiningBlockChainClient, M: MinerService { +impl Personal for PersonalClient where C: MiningBlockChainClient { fn accounts(&self, params: Params) -> Result { try!(self.active()); @@ -66,125 +58,6 @@ impl Personal for PersonalClient where C: MiningBl Ok(to_value(&accounts.into_iter().map(Into::into).collect::>())) } - fn new_account(&self, params: Params) -> Result { - try!(self.active()); - from_params::<(String, )>(params).and_then( - |(pass, )| { - let store = take_weak!(self.accounts); - match store.new_account(&pass) { - Ok(address) => Ok(to_value(&RpcH160::from(address))), - Err(e) => Err(errors::account("Could not create account.", e)), - } - } - ) - } - - fn new_account_from_phrase(&self, params: Params) -> Result { - try!(self.active()); - from_params::<(String, String, )>(params).and_then( - |(phrase, pass, )| { - let store = take_weak!(self.accounts); - match store.insert_account(*Brain::new(phrase).generate().unwrap().secret(), &pass) { - Ok(address) => Ok(to_value(&RpcH160::from(address))), - Err(e) => Err(errors::account("Could not create account.", e)), - } - } - ) - } - - fn new_account_from_wallet(&self, params: Params) -> Result { - try!(self.active()); - from_params::<(String, String, )>(params).and_then( - |(json, pass, )| { - let store = take_weak!(self.accounts); - match store.import_presale(json.as_bytes(), &pass).or_else(|_| store.import_wallet(json.as_bytes(), &pass)) { - Ok(address) => Ok(to_value(&RpcH160::from(address))), - Err(e) => Err(errors::account("Could not create account.", e)), - } - } - ) - } - - fn unlock_account(&self, params: Params) -> Result { - try!(self.active()); - from_params::<(RpcH160, String, Option)>(params).and_then( - |(account, account_pass, duration)| { - let account: Address = account.into(); - let store = take_weak!(self.accounts); - let r = match (self.allow_perm_unlock, duration) { - (false, _) => store.unlock_account_temporarily(account, account_pass), - (true, Some(0)) => store.unlock_account_permanently(account, account_pass), - (true, Some(d)) => store.unlock_account_timed(account, account_pass, d as u32 * 1000), - (true, None) => store.unlock_account_timed(account, account_pass, 300_000), - }; - match r { - Ok(_) => Ok(Value::Bool(true)), - Err(_) => Ok(Value::Bool(false)), - } - } - ) - } - - fn test_password(&self, params: Params) -> Result { - try!(self.active()); - from_params::<(RpcH160, String)>(params).and_then( - |(account, password)| { - let account: Address = account.into(); - take_weak!(self.accounts) - .test_password(&account, password) - .map(|b| Value::Bool(b)) - .map_err(|e| errors::account("Could not fetch account info.", e)) - } - ) - } - - fn change_password(&self, params: Params) -> Result { - try!(self.active()); - from_params::<(RpcH160, String, String)>(params).and_then( - |(account, password, new_password)| { - let account: Address = account.into(); - take_weak!(self.accounts) - .change_password(&account, password, new_password) - .map(|_| Value::Null) - .map_err(|e| errors::account("Could not fetch account info.", e)) - } - ) - } - - fn sign_and_send_transaction(&self, params: Params) -> Result { - try!(self.active()); - from_params::<(TransactionRequest, String)>(params) - .and_then(|(request, password)| { - sign_and_dispatch( - &*take_weak!(self.client), - &*take_weak!(self.miner), - &*take_weak!(self.accounts), - request.into(), - Some(password) - ) - }) - } - - fn set_account_name(&self, params: Params) -> Result { - try!(self.active()); - let store = take_weak!(self.accounts); - from_params::<(RpcH160, String)>(params).and_then(|(addr, name)| { - let addr: Address = addr.into(); - store.set_account_name(addr.clone(), name.clone()).or_else(|_| store.set_address_name(addr, name)).expect("set_address_name always returns Ok; qed"); - Ok(Value::Null) - }) - } - - fn set_account_meta(&self, params: Params) -> Result { - try!(self.active()); - let store = take_weak!(self.accounts); - from_params::<(RpcH160, String)>(params).and_then(|(addr, meta)| { - let addr: Address = addr.into(); - store.set_account_meta(addr.clone(), meta.clone()).or_else(|_| store.set_address_meta(addr, meta)).expect("set_address_meta always returns Ok; qed"); - Ok(Value::Null) - }) - } - fn accounts_info(&self, params: Params) -> Result { try!(self.active()); try!(expect_no_params(params)); @@ -204,21 +77,4 @@ impl Personal for PersonalClient where C: MiningBl (format!("0x{}", a.hex()), Value::Object(m)) }).collect::>())) } - - fn geth_accounts(&self, params: Params) -> Result { - try!(self.active()); - try!(expect_no_params(params)); - let store = take_weak!(self.accounts); - Ok(to_value(&store.list_geth_accounts(false).into_iter().map(Into::into).collect::>())) - } - - fn import_geth_accounts(&self, params: Params) -> Result { - from_params::<(Vec,)>(params).and_then(|(addresses,)| { - let store = take_weak!(self.accounts); - Ok(to_value(&try!(store - .import_geth_accounts(addresses.into_iter().map(Into::into).collect(), false) - .map_err(|e| errors::account("Couldn't import Geth accounts", e)) - ).into_iter().map(Into::into).collect::>())) - }) - } } diff --git a/rpc/src/v1/impls/personal_accounts.rs b/rpc/src/v1/impls/personal_accounts.rs new file mode 100644 index 000000000..0fa236845 --- /dev/null +++ b/rpc/src/v1/impls/personal_accounts.rs @@ -0,0 +1,194 @@ +// Copyright 2015, 2016 Ethcore (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 . + +//! Account management (personal) rpc implementation +use std::sync::{Arc, Weak}; +use util::{Address}; +use jsonrpc_core::*; +use ethkey::{Brain, Generator}; +use v1::traits::PersonalAccounts; +use v1::types::{H160 as RpcH160, TransactionRequest}; +use v1::helpers::errors; +use v1::helpers::params::expect_no_params; +use v1::helpers::dispatch::sign_and_dispatch; +use ethcore::account_provider::AccountProvider; +use ethcore::client::MiningBlockChainClient; +use ethcore::miner::MinerService; + +/// Account management (personal) rpc implementation. +pub struct PersonalAccountsClient where C: MiningBlockChainClient, M: MinerService { + accounts: Weak, + client: Weak, + miner: Weak, + allow_perm_unlock: bool, +} + +impl PersonalAccountsClient where C: MiningBlockChainClient, M: MinerService { + /// Creates new PersonalClient + pub fn new(store: &Arc, client: &Arc, miner: &Arc, allow_perm_unlock: bool) -> Self { + PersonalAccountsClient { + accounts: Arc::downgrade(store), + client: Arc::downgrade(client), + miner: Arc::downgrade(miner), + allow_perm_unlock: allow_perm_unlock, + } + } + + fn active(&self) -> Result<(), Error> { + // TODO: only call every 30s at most. + take_weak!(self.client).keep_alive(); + Ok(()) + } +} + +impl PersonalAccounts for PersonalAccountsClient where C: MiningBlockChainClient, M: MinerService { + + fn new_account(&self, params: Params) -> Result { + try!(self.active()); + from_params::<(String, )>(params).and_then( + |(pass, )| { + let store = take_weak!(self.accounts); + match store.new_account(&pass) { + Ok(address) => Ok(to_value(&RpcH160::from(address))), + Err(e) => Err(errors::account("Could not create account.", e)), + } + } + ) + } + + fn new_account_from_phrase(&self, params: Params) -> Result { + try!(self.active()); + from_params::<(String, String, )>(params).and_then( + |(phrase, pass, )| { + let store = take_weak!(self.accounts); + match store.insert_account(*Brain::new(phrase).generate().unwrap().secret(), &pass) { + Ok(address) => Ok(to_value(&RpcH160::from(address))), + Err(e) => Err(errors::account("Could not create account.", e)), + } + } + ) + } + + fn new_account_from_wallet(&self, params: Params) -> Result { + try!(self.active()); + from_params::<(String, String, )>(params).and_then( + |(json, pass, )| { + let store = take_weak!(self.accounts); + match store.import_presale(json.as_bytes(), &pass).or_else(|_| store.import_wallet(json.as_bytes(), &pass)) { + Ok(address) => Ok(to_value(&RpcH160::from(address))), + Err(e) => Err(errors::account("Could not create account.", e)), + } + } + ) + } + + fn unlock_account(&self, params: Params) -> Result { + try!(self.active()); + from_params::<(RpcH160, String, Option)>(params).and_then( + |(account, account_pass, duration)| { + let account: Address = account.into(); + let store = take_weak!(self.accounts); + let r = match (self.allow_perm_unlock, duration) { + (false, _) => store.unlock_account_temporarily(account, account_pass), + (true, Some(0)) => store.unlock_account_permanently(account, account_pass), + (true, Some(d)) => store.unlock_account_timed(account, account_pass, d as u32 * 1000), + (true, None) => store.unlock_account_timed(account, account_pass, 300_000), + }; + match r { + Ok(_) => Ok(Value::Bool(true)), + Err(_) => Ok(Value::Bool(false)), + } + } + ) + } + + fn test_password(&self, params: Params) -> Result { + try!(self.active()); + from_params::<(RpcH160, String)>(params).and_then( + |(account, password)| { + let account: Address = account.into(); + take_weak!(self.accounts) + .test_password(&account, password) + .map(|b| Value::Bool(b)) + .map_err(|e| errors::account("Could not fetch account info.", e)) + } + ) + } + + fn change_password(&self, params: Params) -> Result { + try!(self.active()); + from_params::<(RpcH160, String, String)>(params).and_then( + |(account, password, new_password)| { + let account: Address = account.into(); + take_weak!(self.accounts) + .change_password(&account, password, new_password) + .map(|_| Value::Null) + .map_err(|e| errors::account("Could not fetch account info.", e)) + } + ) + } + + fn sign_and_send_transaction(&self, params: Params) -> Result { + try!(self.active()); + from_params::<(TransactionRequest, String)>(params) + .and_then(|(request, password)| { + sign_and_dispatch( + &*take_weak!(self.client), + &*take_weak!(self.miner), + &*take_weak!(self.accounts), + request.into(), + Some(password) + ) + }) + } + + fn set_account_name(&self, params: Params) -> Result { + try!(self.active()); + let store = take_weak!(self.accounts); + from_params::<(RpcH160, String)>(params).and_then(|(addr, name)| { + let addr: Address = addr.into(); + store.set_account_name(addr.clone(), name.clone()).or_else(|_| store.set_address_name(addr, name)).expect("set_address_name always returns Ok; qed"); + Ok(Value::Null) + }) + } + + fn set_account_meta(&self, params: Params) -> Result { + try!(self.active()); + let store = take_weak!(self.accounts); + from_params::<(RpcH160, String)>(params).and_then(|(addr, meta)| { + let addr: Address = addr.into(); + store.set_account_meta(addr.clone(), meta.clone()).or_else(|_| store.set_address_meta(addr, meta)).expect("set_address_meta always returns Ok; qed"); + Ok(Value::Null) + }) + } + + fn import_geth_accounts(&self, params: Params) -> Result { + from_params::<(Vec,)>(params).and_then(|(addresses,)| { + let store = take_weak!(self.accounts); + Ok(to_value(&try!(store + .import_geth_accounts(addresses.into_iter().map(Into::into).collect(), false) + .map_err(|e| errors::account("Couldn't import Geth accounts", e)) + ).into_iter().map(Into::into).collect::>())) + }) + } + + fn geth_accounts(&self, params: Params) -> Result { + try!(self.active()); + try!(expect_no_params(params)); + let store = take_weak!(self.accounts); + Ok(to_value(&store.list_geth_accounts(false).into_iter().map(Into::into).collect::>())) + } +} diff --git a/rpc/src/v1/mod.rs b/rpc/src/v1/mod.rs index 5ba302cea..24560160c 100644 --- a/rpc/src/v1/mod.rs +++ b/rpc/src/v1/mod.rs @@ -26,6 +26,6 @@ pub mod traits; pub mod tests; pub mod types; -pub use self::traits::{Web3, Eth, EthFilter, EthSigning, Personal, PersonalSigner, Net, Ethcore, EthcoreSet, Traces, Rpc}; +pub use self::traits::{Web3, Eth, EthFilter, EthSigning, Personal, PersonalAccounts, PersonalSigner, Net, Ethcore, EthcoreSet, Traces, Rpc}; pub use self::impls::*; pub use self::helpers::{SigningQueue, SignerService, ConfirmationsQueue, NetworkSettings, block_import}; diff --git a/rpc/src/v1/tests/mocked/personal.rs b/rpc/src/v1/tests/mocked/personal.rs index 91e1ef0f5..2dd186cca 100644 --- a/rpc/src/v1/tests/mocked/personal.rs +++ b/rpc/src/v1/tests/mocked/personal.rs @@ -19,7 +19,7 @@ use std::str::FromStr; use jsonrpc_core::IoHandler; use util::{U256, Uint, Address}; use ethcore::account_provider::AccountProvider; -use v1::{PersonalClient, Personal}; +use v1::{PersonalClient, PersonalAccountsClient, PersonalAccounts, Personal}; use v1::tests::helpers::TestMinerService; use ethcore::client::TestBlockChainClient; use ethcore::transaction::{Action, Transaction}; @@ -50,10 +50,12 @@ fn setup() -> PersonalTester { let accounts = accounts_provider(); let client = blockchain_client(); let miner = miner_service(); - let personal = PersonalClient::new(&accounts, &client, &miner, false); + let personal = PersonalClient::new(&accounts, &client); + let personal_accounts = PersonalAccountsClient::new(&accounts, &client, &miner, false); let io = IoHandler::new(); io.add_delegate(personal.to_delegate()); + io.add_delegate(personal_accounts.to_delegate()); let tester = PersonalTester { accounts: accounts, diff --git a/rpc/src/v1/traits/mod.rs b/rpc/src/v1/traits/mod.rs index e804c5553..ea0834463 100644 --- a/rpc/src/v1/traits/mod.rs +++ b/rpc/src/v1/traits/mod.rs @@ -30,7 +30,7 @@ pub use self::web3::Web3; pub use self::eth::{Eth, EthFilter}; pub use self::eth_signing::EthSigning; pub use self::net::Net; -pub use self::personal::{Personal, PersonalSigner}; +pub use self::personal::{Personal, PersonalAccounts, PersonalSigner}; pub use self::ethcore::Ethcore; pub use self::ethcore_set::EthcoreSet; pub use self::traces::Traces; diff --git a/rpc/src/v1/traits/personal.rs b/rpc/src/v1/traits/personal.rs index 2114131d4..92a9df6eb 100644 --- a/rpc/src/v1/traits/personal.rs +++ b/rpc/src/v1/traits/personal.rs @@ -18,12 +18,28 @@ use std::sync::Arc; use jsonrpc_core::*; -/// Personal rpc interface. +/// Personal rpc interface. Safe (read-only) functions. pub trait Personal: Sized + Send + Sync + 'static { /// Lists all stored accounts fn accounts(&self, _: Params) -> Result; + /// Returns accounts information. + fn accounts_info(&self, _: Params) -> Result; + + /// Should be used to convert object to io delegate. + fn to_delegate(self) -> IoDelegate { + let mut delegate = IoDelegate::new(Arc::new(self)); + delegate.add_method("personal_listAccounts", Personal::accounts); + delegate.add_method("personal_accountsInfo", Personal::accounts_info); + + delegate + } +} + +/// Personal rpc methods altering stored accounts or their settings. +pub trait PersonalAccounts: Sized + Send + Sync + 'static { + /// Creates new account (it becomes new current unlocked account) /// Param is the password for the account. fn new_account(&self, _: Params) -> Result; @@ -56,31 +72,26 @@ pub trait Personal: Sized + Send + Sync + 'static { /// Set an account's metadata string. fn set_account_meta(&self, _: Params) -> Result; - /// Returns accounts information. - fn accounts_info(&self, _: Params) -> Result; + /// Imports a number of Geth accounts, with the list provided as the argument. + fn import_geth_accounts(&self, _: Params) -> Result; /// Returns the accounts available for importing from Geth. fn geth_accounts(&self, _: Params) -> Result; - /// Imports a number of Geth accounts, with the list provided as the argument. - fn import_geth_accounts(&self, _: Params) -> Result; - /// Should be used to convert object to io delegate. fn to_delegate(self) -> IoDelegate { let mut delegate = IoDelegate::new(Arc::new(self)); - delegate.add_method("personal_listAccounts", Personal::accounts); - delegate.add_method("personal_newAccount", Personal::new_account); - delegate.add_method("personal_newAccountFromPhrase", Personal::new_account_from_phrase); - delegate.add_method("personal_newAccountFromWallet", Personal::new_account_from_wallet); - delegate.add_method("personal_unlockAccount", Personal::unlock_account); - delegate.add_method("personal_testPassword", Personal::test_password); - delegate.add_method("personal_changePassword", Personal::change_password); - delegate.add_method("personal_signAndSendTransaction", Personal::sign_and_send_transaction); - delegate.add_method("personal_setAccountName", Personal::set_account_name); - delegate.add_method("personal_setAccountMeta", Personal::set_account_meta); - delegate.add_method("personal_accountsInfo", Personal::accounts_info); - delegate.add_method("personal_listGethAccounts", Personal::geth_accounts); - delegate.add_method("personal_importGethAccounts", Personal::import_geth_accounts); + delegate.add_method("personal_newAccount", PersonalAccounts::new_account); + delegate.add_method("personal_newAccountFromPhrase", PersonalAccounts::new_account_from_phrase); + delegate.add_method("personal_newAccountFromWallet", PersonalAccounts::new_account_from_wallet); + delegate.add_method("personal_unlockAccount", PersonalAccounts::unlock_account); + delegate.add_method("personal_testPassword", PersonalAccounts::test_password); + delegate.add_method("personal_changePassword", PersonalAccounts::change_password); + delegate.add_method("personal_signAndSendTransaction", PersonalAccounts::sign_and_send_transaction); + delegate.add_method("personal_setAccountName", PersonalAccounts::set_account_name); + delegate.add_method("personal_setAccountMeta", PersonalAccounts::set_account_meta); + delegate.add_method("personal_importGethAccounts", PersonalAccounts::import_geth_accounts); + delegate.add_method("personal_listGethAccounts", PersonalAccounts::geth_accounts); delegate } @@ -108,6 +119,7 @@ pub trait PersonalSigner: Sized + Send + Sync + 'static { delegate.add_method("personal_confirmRequest", PersonalSigner::confirm_request); delegate.add_method("personal_rejectRequest", PersonalSigner::reject_request); delegate.add_method("personal_generateAuthorizationToken", PersonalSigner::generate_token); + delegate } } From ce37b6dcb90c23b17b296f34e5e73b17d31a46e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 27 Oct 2016 19:27:08 +0200 Subject: [PATCH 043/192] Revert to gas price ordering (#2919) --- ethcore/src/miner/miner.rs | 2 +- parity/cli/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index 270a39e48..0a00b79eb 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -98,7 +98,7 @@ impl Default for MinerOptions { tx_gas_limit: !U256::zero(), tx_queue_size: 1024, tx_queue_gas_limit: GasLimit::Auto, - tx_queue_strategy: PrioritizationStrategy::GasFactorAndGasPrice, + tx_queue_strategy: PrioritizationStrategy::GasPriceOnly, pending_set: PendingSet::AlwaysQueue, reseal_min_period: Duration::from_secs(2), work_queue_size: 20, diff --git a/parity/cli/mod.rs b/parity/cli/mod.rs index 68411414f..98e815bd0 100644 --- a/parity/cli/mod.rs +++ b/parity/cli/mod.rs @@ -205,7 +205,7 @@ usage! { or |c: &Config| otry!(c.mining).tx_queue_size.clone(), flag_tx_queue_gas: String = "auto", or |c: &Config| otry!(c.mining).tx_queue_gas.clone(), - flag_tx_queue_strategy: String = "gas_factor", + flag_tx_queue_strategy: String = "gas_price", or |c: &Config| otry!(c.mining).tx_queue_strategy.clone(), flag_remove_solved: bool = false, or |c: &Config| otry!(c.mining).remove_solved.clone(), From 152a551e8b5526a2fb728a1dc98bee23ee9d30e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 27 Oct 2016 19:28:34 +0200 Subject: [PATCH 044/192] Transaction Queue banning (#2524) * Blacklisting transaction queue * Using blacklisting queue in miner * Restoring todo [ci:skip] * Blacklisting recipients and code * Renaming blacklisting->banning * CLI option for banning. * Fixing submodule commit [ci:skip] * Fixing RPC tests * Additional logging when dropping transactions * whitespace [ci:skip] * Configurable ban duration * Reverting fix for pruning history from config file --- Cargo.lock | 1 + ethcore/Cargo.toml | 7 +- ethcore/src/error.rs | 9 + ethcore/src/lib.rs | 1 + ethcore/src/miner/banning_queue.rs | 331 +++++++++++++++++++++++++++++ ethcore/src/miner/miner.rs | 74 ++++++- ethcore/src/miner/mod.rs | 3 +- parity/cli/config.full.toml | 3 + parity/cli/mod.rs | 15 ++ parity/cli/usage.txt | 12 ++ parity/configuration.rs | 10 +- rpc/src/v1/helpers/errors.rs | 3 + rpc/src/v1/tests/eth.rs | 3 +- 13 files changed, 457 insertions(+), 15 deletions(-) create mode 100644 ethcore/src/miner/banning_queue.rs diff --git a/Cargo.lock b/Cargo.lock index 71684d5c4..6fac719d4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -307,6 +307,7 @@ dependencies = [ "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", + "transient-hashmap 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index a60eccddd..d48e76e94 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -24,6 +24,10 @@ rayon = "0.4.2" semver = "0.2" bit-set = "0.4" time = "0.1" +rand = "0.3" +byteorder = "0.5" +transient-hashmap = "0.1" +lru-cache = { git = "https://github.com/contain-rs/lru-cache" } evmjit = { path = "../evmjit", optional = true } clippy = { version = "0.0.96", optional = true} ethash = { path = "../ethash" } @@ -36,10 +40,7 @@ ethstore = { path = "../ethstore" } ethkey = { path = "../ethkey" } ethcore-ipc-nano = { path = "../ipc/nano" } rlp = { path = "../util/rlp" } -rand = "0.3" -lru-cache = { git = "https://github.com/contain-rs/lru-cache" } ethcore-bloom-journal = { path = "../util/bloom" } -byteorder = "0.5" [dependencies.hyper] git = "https://github.com/ethcore/hyper" diff --git a/ethcore/src/error.rs b/ethcore/src/error.rs index e87aa231f..40f4c6d28 100644 --- a/ethcore/src/error.rs +++ b/ethcore/src/error.rs @@ -63,6 +63,12 @@ pub enum TransactionError { }, /// Transaction's gas limit (aka gas) is invalid. InvalidGasLimit(OutOfBounds), + /// Transaction sender is banned. + SenderBanned, + /// Transaction receipient is banned. + RecipientBanned, + /// Contract creation code is banned. + CodeBanned, } impl fmt::Display for TransactionError { @@ -81,6 +87,9 @@ impl fmt::Display for TransactionError { GasLimitExceeded { limit, got } => format!("Gas limit exceeded. Limit={}, Given={}", limit, got), InvalidGasLimit(ref err) => format!("Invalid gas limit. {}", err), + SenderBanned => "Sender is temporarily banned.".into(), + RecipientBanned => "Recipient is temporarily banned.".into(), + CodeBanned => "Contract code is temporarily banned.".into(), }; f.write_fmt(format_args!("Transaction error ({})", msg)) diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index 9985dc58e..c7f40418c 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -101,6 +101,7 @@ extern crate bit_set; extern crate rlp; extern crate ethcore_bloom_journal as bloom_journal; extern crate byteorder; +extern crate transient_hashmap; #[macro_use] extern crate log; diff --git a/ethcore/src/miner/banning_queue.rs b/ethcore/src/miner/banning_queue.rs new file mode 100644 index 000000000..0ad99b008 --- /dev/null +++ b/ethcore/src/miner/banning_queue.rs @@ -0,0 +1,331 @@ +// Copyright 2015, 2016 Ethcore (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 . + +//! Banning Queue +//! Transacton Queue wrapper maintaining additional list of banned senders and contract hashes. + +use std::time::Duration; +use std::ops::{Deref, DerefMut}; +use std::cell::Cell; +use transaction::{SignedTransaction, Action}; +use transient_hashmap::TransientHashMap; +use miner::{TransactionQueue, TransactionImportResult, TransactionOrigin, AccountDetails}; +use error::{Error, TransactionError}; +use util::{Uint, U256, H256, Address, Hashable}; + +type Count = u16; + +/// Auto-Banning threshold +pub enum Threshold { + /// Should ban after given number of misbehaves reported. + BanAfter(Count), + /// Should never ban anything + NeverBan +} + +impl Default for Threshold { + fn default() -> Self { + Threshold::NeverBan + } +} + +/// Transaction queue with banlist. +pub struct BanningTransactionQueue { + queue: TransactionQueue, + ban_threshold: Threshold, + senders_bans: TransientHashMap>, + recipients_bans: TransientHashMap>, + codes_bans: TransientHashMap>, +} + +impl BanningTransactionQueue { + /// Creates new banlisting transaction queue + pub fn new(queue: TransactionQueue, ban_threshold: Threshold, ban_lifetime: Duration) -> Self { + let ban_lifetime_sec = ban_lifetime.as_secs(); + assert!(ban_lifetime_sec > 0, "Lifetime has to be specified in seconds."); + BanningTransactionQueue { + queue: queue, + ban_threshold: ban_threshold, + senders_bans: TransientHashMap::new(ban_lifetime_sec), + recipients_bans: TransientHashMap::new(ban_lifetime_sec), + codes_bans: TransientHashMap::new(ban_lifetime_sec), + } + } + + /// Borrows internal queue. + /// NOTE: you can insert transactions to the queue even + /// if they would be rejected because of ban otherwise. + /// But probably you shouldn't. + pub fn queue(&mut self) -> &mut TransactionQueue { + &mut self.queue + } + + /// Add to the queue taking bans into consideration. + /// May reject transaction because of the banlist. + pub fn add_with_banlist( + &mut self, + transaction: SignedTransaction, + account_details: &F, + ) -> Result where F: Fn(&Address) -> AccountDetails { + if let Threshold::BanAfter(threshold) = self.ban_threshold { + // NOTE In all checks use direct query to avoid increasing ban timeout. + + // Check sender + if let Ok(sender) = transaction.sender() { + let count = self.senders_bans.direct().get(&sender).map(|v| v.get()).unwrap_or(0); + if count > threshold { + debug!(target: "txqueue", "Ignoring transaction {:?} because sender is banned.", transaction.hash()); + return Err(Error::Transaction(TransactionError::SenderBanned)); + } + } + + // Check recipient + if let Action::Call(recipient) = transaction.action { + let count = self.recipients_bans.direct().get(&recipient).map(|v| v.get()).unwrap_or(0); + if count > threshold { + debug!(target: "txqueue", "Ignoring transaction {:?} because recipient is banned.", transaction.hash()); + return Err(Error::Transaction(TransactionError::RecipientBanned)); + } + } + + // Check code + if let Action::Create = transaction.action { + let code_hash = transaction.data.sha3(); + let count = self.codes_bans.direct().get(&code_hash).map(|v| v.get()).unwrap_or(0); + if count > threshold { + debug!(target: "txqueue", "Ignoring transaction {:?} because code is banned.", transaction.hash()); + return Err(Error::Transaction(TransactionError::CodeBanned)); + } + } + } + self.queue.add(transaction, account_details, TransactionOrigin::External) + } + + /// Ban transaction with given hash. + /// Transaction has to be in the queue. + /// + /// Bans sender and recipient/code and returns `true` when any ban has reached threshold. + pub fn ban_transaction(&mut self, hash: &H256) -> bool { + let transaction = self.queue.find(hash); + match transaction { + Some(transaction) => { + let sender = transaction.sender().expect("Transaction is in queue, so the sender is already validated; qed"); + // Ban sender + let sender_banned = self.ban_sender(sender); + // Ban recipient and codehash + let is_banned = sender_banned || match transaction.action { + Action::Call(recipient) => { + self.ban_recipient(recipient) + }, + Action::Create => { + self.ban_codehash(transaction.data.sha3()) + }, + }; + is_banned + }, + None => false, + } + } + + /// Ban given sender. + /// If bans threshold is reached all subsequent transactions from this sender will be rejected. + /// Reaching bans threshold also removes all existsing transaction from this sender that are already in the + /// queue. + fn ban_sender(&mut self, address: Address) -> bool { + let count = { + let mut count = self.senders_bans.entry(address).or_insert_with(|| Cell::new(0)); + *count.get_mut() = count.get().saturating_add(1); + count.get() + }; + match self.ban_threshold { + Threshold::BanAfter(threshold) if count > threshold => { + // Banlist the sender. + // Remove all transactions from the queue. + self.remove_all(address, !U256::zero()); + true + }, + _ => false + } + } + + /// Ban given recipient. + /// If bans threshold is reached all subsequent transactions to this address will be rejected. + /// Returns true if bans threshold has been reached. + fn ban_recipient(&mut self, address: Address) -> bool { + let count = { + let mut count = self.recipients_bans.entry(address).or_insert_with(|| Cell::new(0)); + *count.get_mut() = count.get().saturating_add(1); + count.get() + }; + match self.ban_threshold { + // TODO [ToDr] Consider removing other transactions to the same recipient from the queue? + Threshold::BanAfter(threshold) if count > threshold => true, + _ => false + } + } + + + /// Ban given codehash. + /// If bans threshold is reached all subsequent transactions to contracts with this codehash will be rejected. + /// Returns true if bans threshold has been reached. + fn ban_codehash(&mut self, code_hash: H256) -> bool { + let mut count = self.codes_bans.entry(code_hash).or_insert_with(|| Cell::new(0)); + *count.get_mut() = count.get().saturating_add(1); + + match self.ban_threshold { + // TODO [ToDr] Consider removing other transactions with the same code from the queue? + Threshold::BanAfter(threshold) if count.get() > threshold => true, + _ => false, + } + } +} + +impl Deref for BanningTransactionQueue { + type Target = TransactionQueue; + + fn deref(&self) -> &Self::Target { + &self.queue + } +} +impl DerefMut for BanningTransactionQueue { + fn deref_mut(&mut self) -> &mut Self::Target { + self.queue() + } +} + +#[cfg(test)] +mod tests { + use std::time::Duration; + use super::{BanningTransactionQueue, Threshold}; + use ethkey::{Random, Generator}; + use transaction::{Transaction, SignedTransaction, Action}; + use error::{Error, TransactionError}; + use client::TransactionImportResult; + use miner::{TransactionQueue, TransactionOrigin, AccountDetails}; + use util::{Uint, U256, Address, FromHex, Hashable}; + + fn queue() -> BanningTransactionQueue { + BanningTransactionQueue::new(TransactionQueue::default(), Threshold::BanAfter(1), Duration::from_secs(180)) + } + + fn default_account_details(_address: &Address) -> AccountDetails { + AccountDetails { + nonce: U256::zero(), + balance: !U256::zero(), + } + } + + fn transaction(action: Action) -> SignedTransaction { + let keypair = Random.generate().unwrap(); + Transaction { + action: action, + value: U256::from(100), + data: "3331600055".from_hex().unwrap(), + gas: U256::from(100_000), + gas_price: U256::from(10), + nonce: U256::from(0), + }.sign(keypair.secret()) + } + + fn unwrap_err(res: Result) -> TransactionError { + match res { + Err(Error::Transaction(e)) => e, + Ok(x) => panic!("Expected error, got: Ok({:?})", x), + Err(e) => panic!("Unexpected error type returned by queue: {:?}", e), + } + } + + #[test] + fn should_allow_to_borrow_the_queue() { + // given + let tx = transaction(Action::Create); + let mut txq = queue(); + + // when + txq.queue().add(tx, &default_account_details, TransactionOrigin::External).unwrap(); + + // then + // should also deref to queue + assert_eq!(txq.status().pending, 1); + } + + #[test] + fn should_not_accept_transactions_from_banned_sender() { + // given + let tx = transaction(Action::Create); + let mut txq = queue(); + // Banlist once (threshold not reached) + let banlist1 = txq.ban_sender(tx.sender().unwrap()); + assert!(!banlist1, "Threshold not reached yet."); + // Insert once + let import1 = txq.add_with_banlist(tx.clone(), &default_account_details).unwrap(); + assert_eq!(import1, TransactionImportResult::Current); + + // when + let banlist2 = txq.ban_sender(tx.sender().unwrap()); + let import2 = txq.add_with_banlist(tx.clone(), &default_account_details); + + // then + assert!(banlist2, "Threshold should be reached - banned."); + assert_eq!(unwrap_err(import2), TransactionError::SenderBanned); + // Should also remove transacion from the queue + assert_eq!(txq.find(&tx.hash()), None); + } + + #[test] + fn should_not_accept_transactions_to_banned_recipient() { + // given + let recipient = Address::default(); + let tx = transaction(Action::Call(recipient)); + let mut txq = queue(); + // Banlist once (threshold not reached) + let banlist1 = txq.ban_recipient(recipient); + assert!(!banlist1, "Threshold not reached yet."); + // Insert once + let import1 = txq.add_with_banlist(tx.clone(), &default_account_details).unwrap(); + assert_eq!(import1, TransactionImportResult::Current); + + // when + let banlist2 = txq.ban_recipient(recipient); + let import2 = txq.add_with_banlist(tx.clone(), &default_account_details); + + // then + assert!(banlist2, "Threshold should be reached - banned."); + assert_eq!(unwrap_err(import2), TransactionError::RecipientBanned); + } + + #[test] + fn should_not_accept_transactions_with_banned_code() { + // given + let tx = transaction(Action::Create); + let codehash = tx.data.sha3(); + let mut txq = queue(); + // Banlist once (threshold not reached) + let banlist1 = txq.ban_codehash(codehash); + assert!(!banlist1, "Threshold not reached yet."); + // Insert once + let import1 = txq.add_with_banlist(tx.clone(), &default_account_details).unwrap(); + assert_eq!(import1, TransactionImportResult::Current); + + // when + let banlist2 = txq.ban_codehash(codehash); + let import2 = txq.add_with_banlist(tx.clone(), &default_account_details); + + // then + assert!(banlist2, "Threshold should be reached - banned."); + assert_eq!(unwrap_err(import2), TransactionError::CodeBanned); + } +} diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index 0a00b79eb..47464b36f 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -31,6 +31,7 @@ use receipt::{Receipt, RichReceipt}; use spec::Spec; use engines::Engine; use miner::{MinerService, MinerStatus, TransactionQueue, PrioritizationStrategy, AccountDetails, TransactionOrigin}; +use miner::banning_queue::{BanningTransactionQueue, Threshold}; use miner::work_notify::WorkPoster; use client::TransactionImportResult; use miner::price_info::PriceInfo; @@ -59,6 +60,22 @@ pub enum GasLimit { Fixed(U256), } +/// Transaction queue banning settings. +#[derive(Debug, PartialEq, Clone)] +pub enum Banning { + /// Banning in transaction queue is disabled + Disabled, + /// Banning in transaction queue is enabled + Enabled { + /// Upper limit of transaction processing time before banning. + offend_threshold: Duration, + /// Number of similar offending transactions before banning. + min_offends: u16, + /// Number of seconds the offender is banned for. + ban_duration: Duration, + }, +} + /// Configures the behaviour of the miner. #[derive(Debug, PartialEq)] pub struct MinerOptions { @@ -86,6 +103,8 @@ pub struct MinerOptions { pub enable_resubmission: bool, /// Global gas limit for all transaction in the queue except for local and retracted. pub tx_queue_gas_limit: GasLimit, + /// Banning settings + pub tx_queue_banning: Banning, } impl Default for MinerOptions { @@ -103,6 +122,7 @@ impl Default for MinerOptions { reseal_min_period: Duration::from_secs(2), work_queue_size: 20, enable_resubmission: true, + tx_queue_banning: Banning::Disabled, } } } @@ -186,7 +206,7 @@ struct SealingWork { /// Handles preparing work for "work sealing" or seals "internally" if Engine does not require work. pub struct Miner { // NOTE [ToDr] When locking always lock in this order! - transaction_queue: Arc>, + transaction_queue: Arc>, sealing_work: Mutex, next_allowed_reseal: Mutex, sealing_block_last_request: Mutex, @@ -215,11 +235,18 @@ impl Miner { GasLimit::Fixed(ref limit) => *limit, _ => !U256::zero(), }; - let txq = Arc::new(Mutex::new(TransactionQueue::with_limits( - options.tx_queue_strategy, options.tx_queue_size, gas_limit, options.tx_gas_limit - ))); + + let txq = TransactionQueue::with_limits(options.tx_queue_strategy, options.tx_queue_size, gas_limit, options.tx_gas_limit); + let txq = match options.tx_queue_banning { + Banning::Disabled => BanningTransactionQueue::new(txq, Threshold::NeverBan, Duration::from_secs(180)), + Banning::Enabled { ban_duration, min_offends, .. } => BanningTransactionQueue::new( + txq, + Threshold::BanAfter(min_offends), + ban_duration, + ), + }; Miner { - transaction_queue: txq, + transaction_queue: Arc::new(Mutex::new(txq)), next_allowed_reseal: Mutex::new(Instant::now()), sealing_block_last_request: Mutex::new(0), sealing_work: Mutex::new(SealingWork{ @@ -318,10 +345,31 @@ impl Miner { let mut invalid_transactions = HashSet::new(); let mut transactions_to_penalize = HashSet::new(); let block_number = open_block.block().fields().header.number(); - // TODO: push new uncles, too. + + // TODO Push new uncles too. for tx in transactions { let hash = tx.hash(); - match open_block.push_transaction(tx, None) { + let start = Instant::now(); + let result = open_block.push_transaction(tx, None); + let took = start.elapsed(); + + // Check for heavy transactions + match self.options.tx_queue_banning { + Banning::Enabled { ref offend_threshold, .. } if &took > offend_threshold => { + match self.transaction_queue.lock().ban_transaction(&hash) { + true => { + warn!(target: "miner", "Detected heavy transaction. Banning the sender and recipient/code."); + }, + false => { + transactions_to_penalize.insert(hash); + debug!(target: "miner", "Detected heavy transaction. Penalizing sender.") + } + } + }, + _ => {}, + } + + match result { Err(Error::Execution(ExecutionError::BlockGasLimitReached { gas_limit, gas_used, gas })) => { debug!(target: "miner", "Skipping adding transaction to block because of gas limit: {:?} (limit: {:?}, used: {:?}, gas: {:?})", hash, gas_limit, gas_used, gas); @@ -506,7 +554,7 @@ impl Miner { prepare_new } - fn add_transactions_to_queue(&self, chain: &MiningBlockChainClient, transactions: Vec, origin: TransactionOrigin, transaction_queue: &mut TransactionQueue) -> + fn add_transactions_to_queue(&self, chain: &MiningBlockChainClient, transactions: Vec, origin: TransactionOrigin, transaction_queue: &mut BanningTransactionQueue) -> Vec> { let fetch_account = |a: &Address| AccountDetails { @@ -515,7 +563,14 @@ impl Miner { }; transactions.into_iter() - .map(|tx| transaction_queue.add(tx, &fetch_account, origin)) + .map(|tx| match origin { + TransactionOrigin::Local | TransactionOrigin::RetractedBlock => { + transaction_queue.add(tx, &fetch_account, origin) + }, + TransactionOrigin::External => { + transaction_queue.add_with_banlist(tx, &fetch_account) + } + }) .collect() } @@ -1099,6 +1154,7 @@ mod tests { pending_set: PendingSet::AlwaysSealing, work_queue_size: 5, enable_resubmission: true, + tx_queue_banning: Banning::Disabled, }, GasPricer::new_fixed(0u64.into()), &Spec::new_test(), diff --git a/ethcore/src/miner/mod.rs b/ethcore/src/miner/mod.rs index 5fe8dbf44..145d790dd 100644 --- a/ethcore/src/miner/mod.rs +++ b/ethcore/src/miner/mod.rs @@ -44,11 +44,12 @@ mod miner; mod external; mod transaction_queue; +mod banning_queue; mod work_notify; mod price_info; pub use self::transaction_queue::{TransactionQueue, PrioritizationStrategy, AccountDetails, TransactionOrigin}; -pub use self::miner::{Miner, MinerOptions, PendingSet, GasPricer, GasPriceCalibratorOptions, GasLimit}; +pub use self::miner::{Miner, MinerOptions, Banning, PendingSet, GasPricer, GasPriceCalibratorOptions, GasLimit}; pub use self::external::{ExternalMiner, ExternalMinerService}; pub use client::TransactionImportResult; diff --git a/parity/cli/config.full.toml b/parity/cli/config.full.toml index 4d1d48025..84e44ee77 100644 --- a/parity/cli/config.full.toml +++ b/parity/cli/config.full.toml @@ -74,7 +74,10 @@ gas_cap = "6283184" tx_queue_size = 1024 tx_queue_gas = "auto" tx_queue_strategy = "gas_factor" +tx_queue_ban_count = 1 +tx_queue_ban_time = 180 #s tx_gas_limit = "6283184" +tx_time_limit = 100 #ms extra_data = "Parity" remove_solved = false notify_work = ["http://localhost:3001"] diff --git a/parity/cli/mod.rs b/parity/cli/mod.rs index 98e815bd0..d13d791ad 100644 --- a/parity/cli/mod.rs +++ b/parity/cli/mod.rs @@ -187,6 +187,8 @@ usage! { or |c: &Config| otry!(c.mining).work_queue_size.clone(), flag_tx_gas_limit: Option = None, or |c: &Config| otry!(c.mining).tx_gas_limit.clone().map(Some), + flag_tx_time_limit: Option = None, + or |c: &Config| otry!(c.mining).tx_time_limit.clone().map(Some), flag_relay_set: String = "cheap", or |c: &Config| otry!(c.mining).relay_set.clone(), flag_usd_per_tx: String = "0", @@ -207,6 +209,10 @@ usage! { or |c: &Config| otry!(c.mining).tx_queue_gas.clone(), flag_tx_queue_strategy: String = "gas_price", or |c: &Config| otry!(c.mining).tx_queue_strategy.clone(), + flag_tx_queue_ban_count: u16 = 1u16, + or |c: &Config| otry!(c.mining).tx_queue_ban_count.clone(), + flag_tx_queue_ban_time: u16 = 180u16, + or |c: &Config| otry!(c.mining).tx_queue_ban_time.clone(), flag_remove_solved: bool = false, or |c: &Config| otry!(c.mining).remove_solved.clone(), flag_notify_work: Option = None, @@ -361,6 +367,7 @@ struct Mining { reseal_min_period: Option, work_queue_size: Option, tx_gas_limit: Option, + tx_time_limit: Option, relay_set: Option, usd_per_tx: Option, usd_per_eth: Option, @@ -371,6 +378,8 @@ struct Mining { tx_queue_size: Option, tx_queue_gas: Option, tx_queue_strategy: Option, + tx_queue_ban_count: Option, + tx_queue_ban_time: Option, remove_solved: Option, notify_work: Option>, } @@ -558,6 +567,7 @@ mod tests { flag_reseal_min_period: 4000u64, flag_work_queue_size: 20usize, flag_tx_gas_limit: Some("6283184".into()), + flag_tx_time_limit: Some(100u64), flag_relay_set: "cheap".into(), flag_usd_per_tx: "0".into(), flag_usd_per_eth: "auto".into(), @@ -568,6 +578,8 @@ mod tests { flag_tx_queue_size: 1024usize, flag_tx_queue_gas: "auto".into(), flag_tx_queue_strategy: "gas_factor".into(), + flag_tx_queue_ban_count: 1u16, + flag_tx_queue_ban_time: 180u16, flag_remove_solved: false, flag_notify_work: Some("http://localhost:3001".into()), @@ -727,7 +739,10 @@ mod tests { tx_queue_size: Some(1024), tx_queue_gas: Some("auto".into()), tx_queue_strategy: None, + tx_queue_ban_count: None, + tx_queue_ban_time: None, tx_gas_limit: None, + tx_time_limit: None, extra_data: None, remove_solved: None, notify_work: None, diff --git a/parity/cli/usage.txt b/parity/cli/usage.txt index d96d875cf..bf7e82561 100644 --- a/parity/cli/usage.txt +++ b/parity/cli/usage.txt @@ -166,6 +166,11 @@ Sealing/Mining Options: --tx-gas-limit GAS Apply a limit of GAS as the maximum amount of gas a single transaction may have for it to be mined. (default: {flag_tx_gas_limit:?}) + --tx-time-limit MS Maximal time for processing single transaction. + If enabled senders/recipients/code of transactions + offending the limit will be banned from being included + in transaction queue for 180 seconds. + (default: {flag_tx_time_limit:?}) --relay-set SET Set of transactions to relay. SET may be: cheap - Relay any transaction in the queue (this may include invalid transactions); @@ -203,6 +208,13 @@ Sealing/Mining Options: gas_price - Prioritize txs with high gas price; gas_factor - Prioritize txs using gas price and gas limit ratio (default: {flag_tx_queue_strategy}). + --tx-queue-ban-count C Number of times maximal time for execution (--tx-time-limit) + can be exceeded before banning sender/recipient/code. + (default: {flag_tx_queue_ban_count}) + --tx-queue-ban-time SEC Banning time (in seconds) for offenders of specified + execution time limit. Also number of offending actions + have to reach the threshold within that time. + (default: {flag_tx_queue_ban_time} seconds) --remove-solved Move solved blocks from the work package queue instead of cloning them. This gives a slightly faster import speed, but means that extra solutions diff --git a/parity/configuration.rs b/parity/configuration.rs index 5680e6110..1972b9f1e 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -24,7 +24,7 @@ use util::{Hashable, U256, Uint, Bytes, version_data, Secret, Address}; use util::log::Colour; use ethsync::{NetworkConfiguration, is_valid_node_url, AllowIP}; use ethcore::client::{VMType, Mode}; -use ethcore::miner::MinerOptions; +use ethcore::miner::{MinerOptions, Banning}; use rpc::{IpcConfiguration, HttpConfiguration}; use ethcore_rpc::NetworkSettings; @@ -387,6 +387,14 @@ impl Configuration { reseal_min_period: Duration::from_millis(self.args.flag_reseal_min_period), work_queue_size: self.args.flag_work_queue_size, enable_resubmission: !self.args.flag_remove_solved, + tx_queue_banning: match self.args.flag_tx_time_limit { + Some(limit) => Banning::Enabled { + min_offends: self.args.flag_tx_queue_ban_count, + offend_threshold: Duration::from_millis(limit), + ban_duration: Duration::from_secs(self.args.flag_tx_queue_ban_time as u64), + }, + None => Banning::Disabled, + } }; Ok(options) diff --git a/rpc/src/v1/helpers/errors.rs b/rpc/src/v1/helpers/errors.rs index 572adca3a..3f5dda2d3 100644 --- a/rpc/src/v1/helpers/errors.rs +++ b/rpc/src/v1/helpers/errors.rs @@ -231,6 +231,9 @@ pub fn from_transaction_error(error: EthcoreError) -> Error { format!("Transaction cost exceeds current gas limit. Limit: {}, got: {}. Try decreasing supplied gas.", limit, got) }, InvalidGasLimit(_) => "Supplied gas is beyond limit.".into(), + SenderBanned => "Sender is banned in local queue.".into(), + RecipientBanned => "Recipient is banned in local queue.".into(), + CodeBanned => "Code is banned in local queue.".into(), }; Error { code: ErrorCode::ServerError(codes::TRANSACTION_ERROR), diff --git a/rpc/src/v1/tests/eth.rs b/rpc/src/v1/tests/eth.rs index 5d7481551..d556d11ef 100644 --- a/rpc/src/v1/tests/eth.rs +++ b/rpc/src/v1/tests/eth.rs @@ -24,7 +24,7 @@ use ethcore::spec::{Genesis, Spec}; use ethcore::block::Block; use ethcore::views::BlockView; use ethcore::ethereum; -use ethcore::miner::{MinerOptions, GasPricer, MinerService, ExternalMiner, Miner, PendingSet, PrioritizationStrategy, GasLimit}; +use ethcore::miner::{MinerOptions, Banning, GasPricer, MinerService, ExternalMiner, Miner, PendingSet, PrioritizationStrategy, GasLimit}; use ethcore::account_provider::AccountProvider; use devtools::RandomTempPath; use util::Hashable; @@ -61,6 +61,7 @@ fn miner_service(spec: &Spec, accounts: Arc) -> Arc { tx_gas_limit: !U256::zero(), tx_queue_strategy: PrioritizationStrategy::GasPriceOnly, tx_queue_gas_limit: GasLimit::None, + tx_queue_banning: Banning::Disabled, pending_set: PendingSet::SealingOrElseQueue, reseal_min_period: Duration::from_secs(0), work_queue_size: 50, From dedc4d5cfc03c84de78686e8cd5d3ef2d1042ba5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 27 Oct 2016 19:29:23 +0200 Subject: [PATCH 045/192] Get rid of duplicated code (#2915) --- ethcore/src/evm/instructions.rs | 8 +- ethcore/src/evm/interpreter/gasometer.rs | 141 ++++++++++++++--------- ethcore/src/evm/interpreter/mod.rs | 14 +-- evmbin/Cargo.lock | 38 +++++- evmbin/src/ext.rs | 2 +- 5 files changed, 130 insertions(+), 73 deletions(-) diff --git a/ethcore/src/evm/instructions.rs b/ethcore/src/evm/instructions.rs index 62cfe77d6..e609bf542 100644 --- a/ethcore/src/evm/instructions.rs +++ b/ethcore/src/evm/instructions.rs @@ -173,7 +173,7 @@ lazy_static! { arr[SIGNEXTEND as usize] = InstructionInfo::new("SIGNEXTEND", 0, 2, 1, false, GasPriceTier::Low); arr[SHA3 as usize] = InstructionInfo::new("SHA3", 0, 2, 1, false, GasPriceTier::Special); arr[ADDRESS as usize] = InstructionInfo::new("ADDRESS", 0, 0, 1, false, GasPriceTier::Base); - arr[BALANCE as usize] = InstructionInfo::new("BALANCE", 0, 1, 1, false, GasPriceTier::Ext); + arr[BALANCE as usize] = InstructionInfo::new("BALANCE", 0, 1, 1, false, GasPriceTier::Special); arr[ORIGIN as usize] = InstructionInfo::new("ORIGIN", 0, 0, 1, false, GasPriceTier::Base); arr[CALLER as usize] = InstructionInfo::new("CALLER", 0, 0, 1, false, GasPriceTier::Base); arr[CALLVALUE as usize] = InstructionInfo::new("CALLVALUE", 0, 0, 1, false, GasPriceTier::Base); @@ -183,8 +183,8 @@ lazy_static! { arr[CODESIZE as usize] = InstructionInfo::new("CODESIZE", 0, 0, 1, false, GasPriceTier::Base); arr[CODECOPY as usize] = InstructionInfo::new("CODECOPY", 0, 3, 0, true, GasPriceTier::VeryLow); arr[GASPRICE as usize] = InstructionInfo::new("GASPRICE", 0, 0, 1, false, GasPriceTier::Base); - arr[EXTCODESIZE as usize] = InstructionInfo::new("EXTCODESIZE", 0, 1, 1, false, GasPriceTier::Ext); - arr[EXTCODECOPY as usize] = InstructionInfo::new("EXTCODECOPY", 0, 4, 0, true, GasPriceTier::Ext); + arr[EXTCODESIZE as usize] = InstructionInfo::new("EXTCODESIZE", 0, 1, 1, false, GasPriceTier::Special); + arr[EXTCODECOPY as usize] = InstructionInfo::new("EXTCODECOPY", 0, 4, 0, true, GasPriceTier::Special); arr[BLOCKHASH as usize] = InstructionInfo::new("BLOCKHASH", 0, 1, 1, false, GasPriceTier::Ext); arr[COINBASE as usize] = InstructionInfo::new("COINBASE", 0, 0, 1, false, GasPriceTier::Base); arr[TIMESTAMP as usize] = InstructionInfo::new("TIMESTAMP", 0, 0, 1, false, GasPriceTier::Base); @@ -277,7 +277,7 @@ lazy_static! { arr[CALLCODE as usize] = InstructionInfo::new("CALLCODE", 0, 7, 1, true, GasPriceTier::Special); arr[RETURN as usize] = InstructionInfo::new("RETURN", 0, 2, 0, true, GasPriceTier::Zero); arr[DELEGATECALL as usize] = InstructionInfo::new("DELEGATECALL", 0, 6, 1, true, GasPriceTier::Special); - arr[SUICIDE as usize] = InstructionInfo::new("SUICIDE", 0, 1, 0, true, GasPriceTier::Zero); + arr[SUICIDE as usize] = InstructionInfo::new("SUICIDE", 0, 1, 0, true, GasPriceTier::Special); arr }; } diff --git a/ethcore/src/evm/interpreter/gasometer.rs b/ethcore/src/evm/interpreter/gasometer.rs index 3fde3f664..a2b940655 100644 --- a/ethcore/src/evm/interpreter/gasometer.rs +++ b/ethcore/src/evm/interpreter/gasometer.rs @@ -30,12 +30,20 @@ macro_rules! overflowing { } #[cfg_attr(feature="dev", allow(enum_variant_names))] -enum InstructionCost { +enum Request { Gas(Cost), - GasMem(Cost, Cost, Option), + GasMem(Cost, Cost), + GasMemProvide(Cost, Cost, Option), GasMemCopy(Cost, Cost, Cost) } +pub struct InstructionRequirements { + pub gas_cost: Cost, + pub provide_gas: Option, + pub memory_total_gas: Cost, + pub memory_required_size: usize, +} + pub struct Gasometer { pub current_gas: Gas, pub current_mem_gas: Gas, @@ -59,11 +67,19 @@ impl Gasometer { /// How much gas is provided to a CALL/CREATE, given that we need to deduct `needed` for this operation /// and that we `requested` some. - pub fn gas_provided(&self, schedule: &Schedule, needed: Gas, requested: Option>) -> evm::Result { + pub fn gas_provided(&self, schedule: &Schedule, needed: Gas, requested: Option) -> evm::Result { + // Try converting requested gas to `Gas` (`U256/u64`) + // but in EIP150 even if we request more we should never fail from OOG + let requested = requested.map(Gas::from_u256); + match schedule.sub_gas_cap_divisor { Some(cap_divisor) if self.current_gas >= needed => { let gas_remaining = self.current_gas - needed; - let max_gas_provided = gas_remaining - gas_remaining / Gas::from(cap_divisor); + let max_gas_provided = match cap_divisor { + 64 => gas_remaining - (gas_remaining >> 6), + cap_divisor => gas_remaining - gas_remaining / Gas::from(cap_divisor), + }; + if let Some(Ok(r)) = requested { Ok(min(r, max_gas_provided)) } else { @@ -78,7 +94,7 @@ impl Gasometer { } else { Ok(0.into()) } - } + }, } } @@ -88,21 +104,21 @@ impl Gasometer { /// We guarantee that the final element of the returned tuple (`provided`) will be `Some` /// iff the `instruction` is one of `CREATE`, or any of the `CALL` variants. In this case, /// it will be the amount of gas that the current context provides to the child context. - pub fn get_gas_cost_mem( + pub fn requirements( &mut self, ext: &evm::Ext, instruction: Instruction, info: &InstructionInfo, stack: &Stack, current_mem_size: usize, - ) -> evm::Result<(Gas, Gas, usize, Option)> { + ) -> evm::Result> { let schedule = ext.schedule(); let tier = instructions::get_tier_idx(info.tier); let default_gas = Gas::from(schedule.tier_step_gas[tier]); let cost = match instruction { instructions::JUMPDEST => { - InstructionCost::Gas(Gas::from(1)) + Request::Gas(Gas::from(1)) }, instructions::SSTORE => { let address = H256::from(stack.peek(0)); @@ -116,16 +132,16 @@ impl Gasometer { // !is_zero(&val) && is_zero(newval) schedule.sstore_reset_gas }; - InstructionCost::Gas(Gas::from(gas)) + Request::Gas(Gas::from(gas)) }, instructions::SLOAD => { - InstructionCost::Gas(Gas::from(schedule.sload_gas)) + Request::Gas(Gas::from(schedule.sload_gas)) }, instructions::BALANCE => { - InstructionCost::Gas(Gas::from(schedule.balance_gas)) + Request::Gas(Gas::from(schedule.balance_gas)) }, instructions::EXTCODESIZE => { - InstructionCost::Gas(Gas::from(schedule.extcodesize_gas)) + Request::Gas(Gas::from(schedule.extcodesize_gas)) }, instructions::SUICIDE => { let mut gas = Gas::from(schedule.suicide_gas); @@ -135,28 +151,28 @@ impl Gasometer { gas = overflowing!(gas.overflow_add(schedule.suicide_to_new_account_cost.into())); } - InstructionCost::Gas(gas) + Request::Gas(gas) }, instructions::MSTORE | instructions::MLOAD => { - InstructionCost::GasMem(default_gas, try!(mem_needed_const(stack.peek(0), 32)), None) + Request::GasMem(default_gas, try!(mem_needed_const(stack.peek(0), 32))) }, instructions::MSTORE8 => { - InstructionCost::GasMem(default_gas, try!(mem_needed_const(stack.peek(0), 1)), None) + Request::GasMem(default_gas, try!(mem_needed_const(stack.peek(0), 1))) }, instructions::RETURN => { - InstructionCost::GasMem(default_gas, try!(mem_needed(stack.peek(0), stack.peek(1))), None) + Request::GasMem(default_gas, try!(mem_needed(stack.peek(0), stack.peek(1)))) }, instructions::SHA3 => { let w = overflowing!(add_gas_usize(try!(Gas::from_u256(*stack.peek(1))), 31)); let words = w >> 5; let gas = Gas::from(schedule.sha3_gas) + (Gas::from(schedule.sha3_word_gas) * words); - InstructionCost::GasMem(gas, try!(mem_needed(stack.peek(0), stack.peek(1))), None) + Request::GasMem(gas, try!(mem_needed(stack.peek(0), stack.peek(1)))) }, instructions::CALLDATACOPY | instructions::CODECOPY => { - InstructionCost::GasMemCopy(default_gas, try!(mem_needed(stack.peek(0), stack.peek(2))), try!(Gas::from_u256(*stack.peek(2)))) + Request::GasMemCopy(default_gas, try!(mem_needed(stack.peek(0), stack.peek(2))), try!(Gas::from_u256(*stack.peek(2)))) }, instructions::EXTCODECOPY => { - InstructionCost::GasMemCopy(schedule.extcodecopy_base_gas.into(), try!(mem_needed(stack.peek(1), stack.peek(3))), try!(Gas::from_u256(*stack.peek(3)))) + Request::GasMemCopy(schedule.extcodecopy_base_gas.into(), try!(mem_needed(stack.peek(1), stack.peek(3))), try!(Gas::from_u256(*stack.peek(3)))) }, instructions::LOG0...instructions::LOG4 => { let no_of_topics = instructions::get_log_topics(instruction); @@ -164,7 +180,7 @@ impl Gasometer { let data_gas = overflowing!(try!(Gas::from_u256(*stack.peek(1))).overflow_mul(Gas::from(schedule.log_data_gas))); let gas = overflowing!(data_gas.overflow_add(Gas::from(log_gas))); - InstructionCost::GasMem(gas, try!(mem_needed(stack.peek(0), stack.peek(1))), None) + Request::GasMem(gas, try!(mem_needed(stack.peek(0), stack.peek(1)))) }, instructions::CALL | instructions::CALLCODE => { let mut gas = Gas::from(schedule.call_gas); @@ -183,70 +199,82 @@ impl Gasometer { gas = overflowing!(gas.overflow_add(schedule.call_value_transfer_gas.into())); }; - // TODO: refactor to avoid duplicate calculation here and later on. - let (mem_gas_cost, _, _) = try!(self.mem_gas_cost(schedule, current_mem_size, &mem)); - let cost_so_far = overflowing!(gas.overflow_add(mem_gas_cost.into())); - let requested = Gas::from_u256(*stack.peek(0)); - let provided = try!(self.gas_provided(schedule, cost_so_far, Some(requested))); - gas = overflowing!(gas.overflow_add(provided)); + let requested = *stack.peek(0); - InstructionCost::GasMem(gas, mem, Some(provided)) + Request::GasMemProvide(gas, mem, Some(requested)) }, instructions::DELEGATECALL => { - let mut gas = Gas::from(schedule.call_gas); + let gas = Gas::from(schedule.call_gas); let mem = cmp::max( try!(mem_needed(stack.peek(4), stack.peek(5))), try!(mem_needed(stack.peek(2), stack.peek(3))) ); + let requested = *stack.peek(0); - // TODO: refactor to avoid duplicate calculation here and later on. - let (mem_gas_cost, _, _) = try!(self.mem_gas_cost(schedule, current_mem_size, &mem)); - let cost_so_far = overflowing!(gas.overflow_add(mem_gas_cost.into())); - let requested = Gas::from_u256(*stack.peek(0)); - let provided = try!(self.gas_provided(schedule, cost_so_far, Some(requested))); - gas = overflowing!(gas.overflow_add(provided)); - - InstructionCost::GasMem(gas, mem, Some(provided)) + Request::GasMemProvide(gas, mem, Some(requested)) }, instructions::CREATE => { - let mut gas = Gas::from(schedule.create_gas); + let gas = Gas::from(schedule.create_gas); let mem = try!(mem_needed(stack.peek(1), stack.peek(2))); - // TODO: refactor to avoid duplicate calculation here and later on. - let (mem_gas_cost, _, _) = try!(self.mem_gas_cost(schedule, current_mem_size, &mem)); - let cost_so_far = overflowing!(gas.overflow_add(mem_gas_cost.into())); - let provided = try!(self.gas_provided(schedule, cost_so_far, None)); - gas = overflowing!(gas.overflow_add(provided)); - - InstructionCost::GasMem(gas, mem, Some(provided)) + Request::GasMemProvide(gas, mem, None) }, instructions::EXP => { let expon = stack.peek(1); let bytes = ((expon.bits() + 7) / 8) as usize; let gas = Gas::from(schedule.exp_gas + schedule.exp_byte_gas * bytes); - InstructionCost::Gas(gas) + Request::Gas(gas) }, - _ => InstructionCost::Gas(default_gas), + _ => Request::Gas(default_gas), }; - match cost { - InstructionCost::Gas(gas) => { - Ok((gas, self.current_mem_gas, 0, None)) + Ok(match cost { + Request::Gas(gas) => { + InstructionRequirements { + gas_cost: gas, + provide_gas: None, + memory_required_size: 0, + memory_total_gas: self.current_mem_gas, + } }, - InstructionCost::GasMem(gas, mem_size, provided) => { + Request::GasMem(gas, mem_size) => { let (mem_gas_cost, new_mem_gas, new_mem_size) = try!(self.mem_gas_cost(schedule, current_mem_size, &mem_size)); let gas = overflowing!(gas.overflow_add(mem_gas_cost)); - Ok((gas, new_mem_gas, new_mem_size, provided)) + InstructionRequirements { + gas_cost: gas, + provide_gas: None, + memory_required_size: new_mem_size, + memory_total_gas: new_mem_gas, + } }, - InstructionCost::GasMemCopy(gas, mem_size, copy) => { + Request::GasMemProvide(gas, mem_size, requested) => { + let (mem_gas_cost, new_mem_gas, new_mem_size) = try!(self.mem_gas_cost(schedule, current_mem_size, &mem_size)); + let gas = overflowing!(gas.overflow_add(mem_gas_cost)); + let provided = try!(self.gas_provided(schedule, gas, requested)); + let total_gas = overflowing!(gas.overflow_add(provided)); + + InstructionRequirements { + gas_cost: total_gas, + provide_gas: Some(provided), + memory_required_size: new_mem_size, + memory_total_gas: new_mem_gas, + } + }, + Request::GasMemCopy(gas, mem_size, copy) => { let (mem_gas_cost, new_mem_gas, new_mem_size) = try!(self.mem_gas_cost(schedule, current_mem_size, &mem_size)); let copy = overflowing!(add_gas_usize(copy, 31)) >> 5; let copy_gas = Gas::from(schedule.copy_gas) * copy; let gas = overflowing!(gas.overflow_add(copy_gas)); let gas = overflowing!(gas.overflow_add(mem_gas_cost)); - Ok((gas, new_mem_gas, new_mem_size, None)) - } - } + + InstructionRequirements { + gas_cost: gas, + provide_gas: None, + memory_required_size: new_mem_size, + memory_total_gas: new_mem_gas, + } + }, + }) } fn mem_gas_cost(&self, schedule: &evm::Schedule, current_mem_size: usize, mem_size: &Gas) -> evm::Result<(Gas, Gas, usize)> { @@ -256,7 +284,7 @@ impl Gasometer { let a = overflowing!(s.overflow_mul(Gas::from(schedule.memory_gas))); // Calculate s*s/quad_coeff_div - debug_assert_eq!(schedule.quad_coeff_div, 512); + assert_eq!(schedule.quad_coeff_div, 512); let b = overflowing!(s.overflow_mul_shr(s, 9)); Ok(overflowing!(a.overflow_add(b))) }; @@ -328,3 +356,4 @@ fn test_calculate_mem_cost() { assert_eq!(new_mem_gas, 3); assert_eq!(mem_size, 32); } + diff --git a/ethcore/src/evm/interpreter/mod.rs b/ethcore/src/evm/interpreter/mod.rs index 8ded2e1f1..bb9791abe 100644 --- a/ethcore/src/evm/interpreter/mod.rs +++ b/ethcore/src/evm/interpreter/mod.rs @@ -120,14 +120,14 @@ impl evm::Evm for Interpreter { try!(self.verify_instruction(ext, instruction, info, &stack)); // Calculate gas cost - let (gas_cost, mem_gas, mem_size, provided) = try!(gasometer.get_gas_cost_mem(ext, instruction, info, &stack, self.mem.size())); + let requirements = try!(gasometer.requirements(ext, instruction, info, &stack, self.mem.size())); // TODO: make compile-time removable if too much of a performance hit. - let trace_executed = ext.trace_prepare_execute(reader.position - 1, instruction, &gas_cost.as_u256()); + let trace_executed = ext.trace_prepare_execute(reader.position - 1, instruction, &requirements.gas_cost.as_u256()); - try!(gasometer.verify_gas(&gas_cost)); - self.mem.expand(mem_size); - gasometer.current_mem_gas = mem_gas; - gasometer.current_gas = gasometer.current_gas - gas_cost; + try!(gasometer.verify_gas(&requirements.gas_cost)); + self.mem.expand(requirements.memory_required_size); + gasometer.current_mem_gas = requirements.memory_total_gas; + gasometer.current_gas = gasometer.current_gas - requirements.gas_cost; evm_debug!({ informant.before_instruction(reader.position, instruction, info, &gasometer.current_gas, &stack) }); @@ -138,7 +138,7 @@ impl evm::Evm for Interpreter { // Execute instruction let result = try!(self.exec_instruction( - gasometer.current_gas, ¶ms, ext, instruction, &mut reader, &mut stack, provided + gasometer.current_gas, ¶ms, ext, instruction, &mut reader, &mut stack, requirements.provide_gas )); evm_debug!({ informant.after_instruction(instruction) }); diff --git a/evmbin/Cargo.lock b/evmbin/Cargo.lock index 600af473b..fde83d1bf 100644 --- a/evmbin/Cargo.lock +++ b/evmbin/Cargo.lock @@ -155,7 +155,7 @@ name = "ethash" version = "1.4.0" dependencies = [ "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "primal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "sha3 0.1.0", ] @@ -208,6 +208,9 @@ dependencies = [ [[package]] name = "ethcore-bloom-journal" version = "0.1.0" +dependencies = [ + "siphasher 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "ethcore-devtools" @@ -223,7 +226,7 @@ dependencies = [ "crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.5.1 (git+https://github.com/ethcore/mio?branch=v0.5.x)", - "parking_lot 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -275,8 +278,9 @@ dependencies = [ "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.1.77 (registry+https://github.com/rust-lang/crates.io-index)", "rlp 0.1.0", "rocksdb 0.4.5 (git+https://github.com/ethcore/rust-rocksdb)", "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", @@ -334,6 +338,7 @@ dependencies = [ "itertools 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", @@ -632,13 +637,28 @@ name = "odds" version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "owning_ref" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "parking_lot" -version = "0.2.8" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "owning_ref 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot_core 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parking_lot_core" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -867,6 +887,11 @@ dependencies = [ "gcc 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "siphasher" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "slab" version = "0.1.3" @@ -1179,7 +1204,9 @@ dependencies = [ "checksum num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)" = "a16a42856a256b39c6d3484f097f6713e14feacd9bfb02290917904fae46c81c" "checksum num_cpus 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "cee7e88156f3f9e19bdd598f8d6c9db7bf4078f99f8381f43a55b09648d1a6e3" "checksum odds 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)" = "e04630a62b3f1cc8c58b4d8f2555a40136f02b420e158242936ef286a72d33a0" -"checksum parking_lot 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "968f685642555d2f7e202c48b8b11de80569e9bfea817f7f12d7c61aac62d4e6" +"checksum owning_ref 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8d91377085359426407a287ab16884a0111ba473aa6844ff01d4ec20ce3d75e7" +"checksum parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "e1435e7a2a00dfebededd6c6bdbd54008001e94b4a2aadd6aef0dc4c56317621" +"checksum parking_lot_core 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fb1b97670a2ffadce7c397fb80a3d687c4f3060140b885621ef1653d0e5d5068" "checksum primal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0e31b86efadeaeb1235452171a66689682783149a6249ff334a2c5d8218d00a4" "checksum primal-bit 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "464a91febc06166783d4f5ba3577b5ed8dda8e421012df80bfe48a971ed7be8f" "checksum primal-check 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "647c81b67bb9551a7b88d0bcd785ac35b7d0bf4b2f358683d7c2375d04daec51" @@ -1205,6 +1232,7 @@ dependencies = [ "checksum serde_codegen 0.8.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e575e583f7d162e163af117fb9791fbd2bd203c31023b3219617e12c5997a738" "checksum serde_codegen_internals 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "318f7e77aa5187391d74aaf4553d2189f56b0ce25e963414c951b97877ffdcec" "checksum serde_json 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1cb6b19e74d9f65b9d03343730b643d729a446b29376785cd65efdff4675e2fc" +"checksum siphasher 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5c44e42fa187b5a8782489cf7740cc27c3125806be2bf33563cf5e02e9533fcd" "checksum slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d807fd58c4181bbabed77cb3b891ba9748241a552bcc5be698faaebefc54f46e" "checksum slab 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6dbdd334bd28d328dad1c41b0ea662517883d8880d8533895ef96c8003dec9c4" "checksum smallvec 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "fcc8d19212aacecf95e4a7a2179b26f7aeb9732a915cf01f05b0d3e044865410" diff --git a/evmbin/src/ext.rs b/evmbin/src/ext.rs index 79e6eb3bd..11fb3a876 100644 --- a/evmbin/src/ext.rs +++ b/evmbin/src/ext.rs @@ -31,7 +31,7 @@ pub struct FakeExt { impl Default for FakeExt { fn default() -> Self { FakeExt { - schedule: Schedule::new_homestead(), + schedule: Schedule::new_homestead_gas_fix(), store: HashMap::new(), depth: 1, } From 07fca24b446cc0d36bf8fe45856c61af26820929 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 27 Oct 2016 19:29:55 +0200 Subject: [PATCH 046/192] Next nonce RPC (#2917) --- rpc/src/v1/impls/ethcore.rs | 13 +++++++++++++ rpc/src/v1/tests/mocked/ethcore.rs | 24 +++++++++++++++++++++++- rpc/src/v1/traits/ethcore.rs | 4 ++++ 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/rpc/src/v1/impls/ethcore.rs b/rpc/src/v1/impls/ethcore.rs index 1a1410ebd..2619b84da 100644 --- a/rpc/src/v1/impls/ethcore.rs +++ b/rpc/src/v1/impls/ethcore.rs @@ -334,4 +334,17 @@ impl Ethcore for EthcoreClient where self.dapps_port .ok_or_else(|| errors::dapps_disabled()) } + + fn next_nonce(&self, address: H160) -> Result { + try!(self.active()); + let address: Address = address.into(); + let miner = take_weak!(self.miner); + let client = take_weak!(self.client); + + Ok(miner.last_nonce(&address) + .map(|n| n + 1.into()) + .unwrap_or_else(|| client.latest_nonce(&address)) + .into() + ) + } } diff --git a/rpc/src/v1/tests/mocked/ethcore.rs b/rpc/src/v1/tests/mocked/ethcore.rs index ea4112c19..e33f1a8f7 100644 --- a/rpc/src/v1/tests/mocked/ethcore.rs +++ b/rpc/src/v1/tests/mocked/ethcore.rs @@ -16,7 +16,7 @@ use std::sync::Arc; use util::log::RotatingLogger; -use util::U256; +use util::{U256, Address}; use ethsync::ManageNetwork; use ethcore::client::{TestBlockChainClient}; use ethstore::ethkey::{Generator, Random}; @@ -320,3 +320,25 @@ fn rpc_ethcore_dapps_port() { assert_eq!(io1.handle_request_sync(request), Some(response1.to_owned())); assert_eq!(io2.handle_request_sync(request), Some(response2.to_owned())); } + +#[test] +fn rpc_ethcore_next_nonce() { + let deps = Dependencies::new(); + let address = Address::default(); + let io1 = deps.default_client(); + let deps = Dependencies::new(); + deps.miner.last_nonces.write().insert(address.clone(), 2.into()); + let io2 = deps.default_client(); + + let request = r#"{ + "jsonrpc": "2.0", + "method": "ethcore_nextNonce", + "params": [""#.to_owned() + &format!("0x{:?}", address) + r#""], + "id": 1 + }"#; + let response1 = r#"{"jsonrpc":"2.0","result":"0x0","id":1}"#; + let response2 = r#"{"jsonrpc":"2.0","result":"0x3","id":1}"#; + + assert_eq!(io1.handle_request_sync(&request), Some(response1.to_owned())); + assert_eq!(io2.handle_request_sync(&request), Some(response2.to_owned())); +} diff --git a/rpc/src/v1/traits/ethcore.rs b/rpc/src/v1/traits/ethcore.rs index ea5f0b13d..e787ce5ac 100644 --- a/rpc/src/v1/traits/ethcore.rs +++ b/rpc/src/v1/traits/ethcore.rs @@ -125,5 +125,9 @@ build_rpc_trait! { /// Returns current Dapps Server port or an error if dapps server is disabled. #[rpc(name = "ethcore_dappsPort")] fn dapps_port(&self) -> Result; + + /// Returns next nonce for particular sender. Should include all transactions in the queue. + #[rpc(name = "ethcore_nextNonce")] + fn next_nonce(&self, H160) -> Result; } } From a4e84c375e05f5dbef26b730094fd653a61803ca Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Thu, 27 Oct 2016 19:30:13 +0200 Subject: [PATCH 047/192] Pass the js-precompiled commit hash to cargo update (#2920) --- js/scripts/release.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/js/scripts/release.sh b/js/scripts/release.sh index 392bdd3b8..8e90713cb 100755 --- a/js/scripts/release.sh +++ b/js/scripts/release.sh @@ -28,9 +28,10 @@ git remote add origin https://${GITHUB_JS_PRECOMPILED}:@github.com/ethcore/js-pr git fetch origin 2>$GITLOG git checkout -b $CI_BUILD_REF_NAME git add . -git commit -m "$UTCDATE [compiled]" +git commit -m "$UTCDATE" git merge origin/$CI_BUILD_REF_NAME -X ours --commit -m "$UTCDATE [release]" git push origin HEAD:refs/heads/$CI_BUILD_REF_NAME 2>$GITLOG +PRECOMPILED_HASH=$(git rev-parse HEAD) # back to root popd @@ -43,7 +44,7 @@ git remote set-url origin https://${GITHUB_JS_PRECOMPILED}:@github.com/ethcore/p git reset --hard origin/$CI_BUILD_REF_NAME 2>$GITLOG # bump js-precompiled, add, commit & push -cargo update -p parity-ui-precompiled +cargo update -p parity-ui-precompiled --precise $PRECOMPILED_HASH git add . || true git commit -m "[ci skip] js-precompiled $UTCDATE" git push origin HEAD:refs/heads/$CI_BUILD_REF_NAME 2>$GITLOG From 22af04e81d6be8bec6b4b1f164aeb84420c7f9c4 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Thu, 27 Oct 2016 17:53:23 +0000 Subject: [PATCH 048/192] [ci skip] js-precompiled 20161027-175225 --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 6fac719d4..dff3416e5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1212,7 +1212,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#0053be493c742cbd7332ba647b3c84d35327abd7" +source = "git+https://github.com/ethcore/js-precompiled.git#0b2b13109f3cc4daa33511bddcb1c7edefc25dd1" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] From 9fd8ac6a155825f150b0c0c78116e42ed4db4e67 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Thu, 27 Oct 2016 21:05:47 +0200 Subject: [PATCH 049/192] additional release.sh debugging info (#2922) --- js/scripts/release.sh | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/js/scripts/release.sh b/js/scripts/release.sh index 8e90713cb..c08d92ca0 100755 --- a/js/scripts/release.sh +++ b/js/scripts/release.sh @@ -23,28 +23,37 @@ rm -rf ./.git git init # add local files and send it up +echo "Setting up GitHub config for js-precompiled" setup_git_user + +echo "Checking out $CI_BUILD_REF_NAME branch" git remote add origin https://${GITHUB_JS_PRECOMPILED}:@github.com/ethcore/js-precompiled.git git fetch origin 2>$GITLOG git checkout -b $CI_BUILD_REF_NAME + +echo "Committing compiled files for $UTCDATE" git add . git commit -m "$UTCDATE" + +echo "Merging remote" git merge origin/$CI_BUILD_REF_NAME -X ours --commit -m "$UTCDATE [release]" git push origin HEAD:refs/heads/$CI_BUILD_REF_NAME 2>$GITLOG PRECOMPILED_HASH=$(git rev-parse HEAD) +echo "Remote updated with [$PRECOMPILED_HASH]" + # back to root popd -# inti git with right origin +echo "Setting up GitHub config for parity" setup_git_user git remote set-url origin https://${GITHUB_JS_PRECOMPILED}:@github.com/ethcore/parity.git - -# at this point we have a detached head on GitLab, reset git reset --hard origin/$CI_BUILD_REF_NAME 2>$GITLOG -# bump js-precompiled, add, commit & push +echo "Updating cargo package parity-ui-precompiled" cargo update -p parity-ui-precompiled --precise $PRECOMPILED_HASH + +echo "Committing updated files" git add . || true git commit -m "[ci skip] js-precompiled $UTCDATE" git push origin HEAD:refs/heads/$CI_BUILD_REF_NAME 2>$GITLOG From a9e7f592034038c6ca71837faa37f89d3e36098c Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Thu, 27 Oct 2016 21:15:41 +0200 Subject: [PATCH 050/192] Merge branch 'master' into jg-js-release-fix (#2924) # Conflicts: # js/scripts/release.sh --- js/scripts/release.sh | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/js/scripts/release.sh b/js/scripts/release.sh index c08d92ca0..4f5bf421d 100755 --- a/js/scripts/release.sh +++ b/js/scripts/release.sh @@ -40,8 +40,6 @@ git merge origin/$CI_BUILD_REF_NAME -X ours --commit -m "$UTCDATE [release]" git push origin HEAD:refs/heads/$CI_BUILD_REF_NAME 2>$GITLOG PRECOMPILED_HASH=$(git rev-parse HEAD) -echo "Remote updated with [$PRECOMPILED_HASH]" - # back to root popd @@ -50,11 +48,11 @@ setup_git_user git remote set-url origin https://${GITHUB_JS_PRECOMPILED}:@github.com/ethcore/parity.git git reset --hard origin/$CI_BUILD_REF_NAME 2>$GITLOG -echo "Updating cargo package parity-ui-precompiled" -cargo update -p parity-ui-precompiled --precise $PRECOMPILED_HASH +echo "Updating cargo package parity-ui-precompiled#$PRECOMPILED_HASH" +cargo update -p parity-ui-precompiled --precise "$PRECOMPILED_HASH" echo "Committing updated files" -git add . || true +git add . git commit -m "[ci skip] js-precompiled $UTCDATE" git push origin HEAD:refs/heads/$CI_BUILD_REF_NAME 2>$GITLOG From 908e563f1884c45b2fbac534fb5dc801bf0a37e4 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Thu, 27 Oct 2016 22:26:47 +0200 Subject: [PATCH 051/192] Revert hash updates until testable (#2925) * Revert hash updates until testable * Re-add hash to echo (debug info) --- js/scripts/release.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/js/scripts/release.sh b/js/scripts/release.sh index 4f5bf421d..95e561b77 100755 --- a/js/scripts/release.sh +++ b/js/scripts/release.sh @@ -38,7 +38,7 @@ git commit -m "$UTCDATE" echo "Merging remote" git merge origin/$CI_BUILD_REF_NAME -X ours --commit -m "$UTCDATE [release]" git push origin HEAD:refs/heads/$CI_BUILD_REF_NAME 2>$GITLOG -PRECOMPILED_HASH=$(git rev-parse HEAD) +PRECOMPILED_HASH=`git rev-parse HEAD` # back to root popd @@ -49,7 +49,8 @@ git remote set-url origin https://${GITHUB_JS_PRECOMPILED}:@github.com/ethcore/p git reset --hard origin/$CI_BUILD_REF_NAME 2>$GITLOG echo "Updating cargo package parity-ui-precompiled#$PRECOMPILED_HASH" -cargo update -p parity-ui-precompiled --precise "$PRECOMPILED_HASH" +cargo update -p parity-ui-precompiled +# --precise "$PRECOMPILED_HASH" echo "Committing updated files" git add . From 151ec869a6ac0caf64dc0f6c46f181a7d7b3f826 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Thu, 27 Oct 2016 22:41:22 +0200 Subject: [PATCH 052/192] [ci skip] js-precompiled 20161027-204107 --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index dff3416e5..f7c08953e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1212,7 +1212,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#0b2b13109f3cc4daa33511bddcb1c7edefc25dd1" +source = "git+https://github.com/ethcore/js-precompiled.git#3f31aca3dd0b71871f6439407c3e258c3ed91abf" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] From 55efa16e4248d48375f4bb5d8de96e1fd3387422 Mon Sep 17 00:00:00 2001 From: Arkadiy Paronyan Date: Fri, 28 Oct 2016 12:00:01 +0200 Subject: [PATCH 053/192] More bootnodes (#2926) --- ethcore/res/ethereum/frontier.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ethcore/res/ethereum/frontier.json b/ethcore/res/ethereum/frontier.json index d5f57defd..111dff30e 100644 --- a/ethcore/res/ethereum/frontier.json +++ b/ethcore/res/ethereum/frontier.json @@ -158,9 +158,17 @@ "stateRoot": "0xd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544" }, "nodes": [ - "enode://cd6611461840543d5b9c56fbf088736154c699c43973b3a1a32390cf27106f87e58a818a606ccb05f3866de95a4fe860786fea71bf891ea95f234480d3022aa3@136.243.154.245:30303", + "enode://efe4f2493f4aff2d641b1db8366b96ddacfe13e7a6e9c8f8f8cf49f9cdba0fdf3258d8c8f8d0c5db529f8123c8f1d95f36d54d590ca1bb366a5818b9a4ba521c@163.172.187.252:30303", + "enode://cd6611461840543d5b9c56fbf088736154c699c43973b3a1a32390cf27106f87e58a818a606ccb05f3866de95a4fe860786fea71bf891ea95f234480d3022aa3@163.172.157.114:30303", "enode://bcc7240543fe2cf86f5e9093d05753dd83343f8fda7bf0e833f65985c73afccf8f981301e13ef49c4804491eab043647374df1c4adf85766af88a624ecc3330e@136.243.154.244:30303", "enode://ed4227681ca8c70beb2277b9e870353a9693f12e7c548c35df6bca6a956934d6f659999c2decb31f75ce217822eefca149ace914f1cbe461ed5a2ebaf9501455@88.212.206.70:30303", + "enode://cadc6e573b6bc2a9128f2f635ac0db3353e360b56deef239e9be7e7fce039502e0ec670b595f6288c0d2116812516ad6b6ff8d5728ff45eba176989e40dead1e@37.128.191.230:30303", + "enode://595a9a06f8b9bc9835c8723b6a82105aea5d55c66b029b6d44f229d6d135ac3ecdd3e9309360a961ea39d7bee7bac5d03564077a4e08823acc723370aace65ec@46.20.235.22:30303", + "enode://029178d6d6f9f8026fc0bc17d5d1401aac76ec9d86633bba2320b5eed7b312980c0a210b74b20c4f9a8b0b2bf884b111fa9ea5c5f916bb9bbc0e0c8640a0f56c@216.158.85.185:30303", + "enode://84f5d5957b4880a8b0545e32e05472318898ad9fc8ebe1d56c90c12334a98e12351eccfdf3a2bf72432ac38b57e9d348400d17caa083879ade3822390f89773f@10.1.52.78:30303", + "enode://f90dc9b9bf7b8db97726b7849e175f1eb2707f3d8f281c929336e398dd89b0409fc6aeceb89e846278e9d3ecc3857cebfbe6758ff352ece6fe5d42921ee761db@10.1.173.87:30303", + "enode://6a868ced2dec399c53f730261173638a93a40214cf299ccf4d42a76e3fa54701db410669e8006347a4b3a74fa090bb35af0320e4bc8d04cf5b7f582b1db285f5@10.3.149.199:30303", + "enode://fdd1b9bb613cfbc200bba17ce199a9490edc752a833f88d4134bf52bb0d858aa5524cb3ec9366c7a4ef4637754b8b15b5dc913e4ed9fdb6022f7512d7b63f181@212.47.247.103:30303", "enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303", "enode://de471bccee3d042261d52e9bff31458daecc406142b401d4cd848f677479f73104b9fdeb090af9583d3391b7f10cb2ba9e26865dd5fca4fcdc0fb1e3b723c786@54.94.239.50:30303", "enode://1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082@52.74.57.123:30303", From 956a059a06806cf01c9dce39ec9b42e2a9f6e576 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Fri, 28 Oct 2016 16:02:23 +0200 Subject: [PATCH 054/192] Updating bootnodes for ETC (#2938) --- ethcore/res/ethereum/classic.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ethcore/res/ethereum/classic.json b/ethcore/res/ethereum/classic.json index 5d951752f..5be7b1caf 100644 --- a/ethcore/res/ethereum/classic.json +++ b/ethcore/res/ethereum/classic.json @@ -39,10 +39,18 @@ "stateRoot": "0xd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544" }, "nodes": [ + "enode://08c7ee6a4f861ff0664a49532bcc86de1363acd608999d1b76609bb9bc278649906f069057630fd9493924a368b5d1dc9b8f8bf13ac26df72512f6d1fabd8c95@45.32.7.81:30303", "enode://e809c4a2fec7daed400e5e28564e23693b23b2cc5a019b612505631bbe7b9ccf709c1796d2a3d29ef2b045f210caf51e3c4f5b6d3587d43ad5d6397526fa6179@174.112.32.157:30303", "enode://687be94c3a7beaa3d2fde82fa5046cdeb3e8198354e05b29d6e0d4e276713e3707ac10f784a7904938b06b46c764875c241b0337dd853385a4d8bfcbf8190647@95.183.51.229:30303", "enode://6e538e7c1280f0a31ff08b382db5302480f775480b8e68f8febca0ceff81e4b19153c6f8bf60313b93bef2cc34d34e1df41317de0ce613a201d1660a788a03e2@52.206.67.235:30303", - "enode://217ebe27e89bf4fec8ce06509323ff095b1014378deb75ab2e5f6759a4e8750a3bd8254b8c6833136e4d5e58230d65ee8ab34a5db5abf0640408c4288af3c8a7@188.138.1.237:30303" + "enode://ca5ae4eca09ba6787e29cf6d86f7634d07aae6b9e6317a59aff675851c0bf445068173208cf8ef7f5cd783d8e29b85b2fa3fa358124cf0546823149724f9bde1@138.68.1.16:30303", + "enode://217ebe27e89bf4fec8ce06509323ff095b1014378deb75ab2e5f6759a4e8750a3bd8254b8c6833136e4d5e58230d65ee8ab34a5db5abf0640408c4288af3c8a7@188.138.1.237:30303", + "enode://fa20444ef991596ce99b81652ac4e61de1eddc4ff21d3cd42762abd7ed47e7cf044d3c9ccddaf6035d39725e4eb372806787829ccb9a08ec7cb71883cb8c3abd@50.149.116.182:30303", + "enode://4bd6a4df3612c718333eb5ea7f817923a8cdf1bed89cee70d1710b45a0b6b77b2819846440555e451a9b602ad2efa2d2facd4620650249d8468008946887820a@71.178.232.20:30304", + "enode://921cf8e4c345fe8db913c53964f9cadc667644e7f20195a0b7d877bd689a5934e146ff2c2259f1bae6817b6585153a007ceb67d260b720fa3e6fc4350df25c7f@51.255.49.170:30303", + "enode://ffea3b01c000cdd89e1e9229fea3e80e95b646f9b2aa55071fc865e2f19543c9b06045cc2e69453e6b78100a119e66be1b5ad50b36f2ffd27293caa28efdd1b2@128.199.93.177:3030", + "enode://ee3da491ce6a155eb132708eb0e8d04b0637926ec0ae1b79e63fc97cb9fc3818f49250a0ae0d7f79ed62b66ec677f408c4e01741504dc7a051e274f1e803d454@91.121.65.179:40404", + "enode://48e063a6cf5f335b1ef2ed98126bf522cf254396f850c7d442fe2edbbc23398787e14cd4de7968a00175a82762de9cbe9e1407d8ccbcaeca5004d65f8398d759@159.203.255.59:30303" ], "accounts": { "0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, From 29ab4ecac1e78d90980a56f39116c46f40f9a473 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Fri, 28 Oct 2016 16:04:44 +0200 Subject: [PATCH 055/192] Shared code cache (#2921) * add a global code cache * extract memory-limited lru cache to util * use memory-limited code cache * account for code cache size in mem_used --- Cargo.lock | 9 +-- ethcore/Cargo.toml | 2 +- ethcore/src/evm/interpreter/shared_cache.rs | 53 +++++--------- ethcore/src/state/account.rs | 21 ++++-- ethcore/src/state/mod.rs | 40 +++++++---- ethcore/src/state_db.rs | 34 ++++++++- util/Cargo.toml | 1 + util/src/cache.rs | 79 +++++++++++++++++++++ util/src/lib.rs | 2 + 9 files changed, 180 insertions(+), 61 deletions(-) create mode 100644 util/src/cache.rs diff --git a/Cargo.lock b/Cargo.lock index f7c08953e..26fab3d49 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -298,7 +298,7 @@ dependencies = [ "hyper 0.9.4 (git+https://github.com/ethcore/hyper)", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "lru-cache 0.0.7 (git+https://github.com/contain-rs/lru-cache)", + "lru-cache 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -555,6 +555,7 @@ dependencies = [ "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "lru-cache 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.1.68 (registry+https://github.com/rust-lang/crates.io-index)", @@ -907,8 +908,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "lru-cache" -version = "0.0.7" -source = "git+https://github.com/contain-rs/lru-cache#13255e33c45ceb69a4b143f235a4322df5fb580e" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "linked-hash-map 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1987,7 +1988,7 @@ dependencies = [ "checksum linked-hash-map 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bda158e0dabeb97ee8a401f4d17e479d6b891a14de0bba79d5cc2d4d325b5e48" "checksum linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6d262045c5b87c0861b3f004610afd0e2c851e2908d08b6c870cbb9d5f494ecd" "checksum log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ab83497bf8bf4ed2a74259c1c802351fcd67a65baa86394b6ba73c36f4838054" -"checksum lru-cache 0.0.7 (git+https://github.com/contain-rs/lru-cache)" = "" +"checksum lru-cache 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "656fa4dfcb02bcf1063c592ba3ff6a5303ee1f2afe98c8a889e8b1a77c6dfdb7" "checksum matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "15305656809ce5a4805b1ff2946892810992197ce1270ff79baded852187942e" "checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20" "checksum mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a74cc2587bf97c49f3f5bab62860d6abf3902ca73b66b51d9b049fbdcd727bd2" diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index d48e76e94..1f8413339 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -27,7 +27,6 @@ time = "0.1" rand = "0.3" byteorder = "0.5" transient-hashmap = "0.1" -lru-cache = { git = "https://github.com/contain-rs/lru-cache" } evmjit = { path = "../evmjit", optional = true } clippy = { version = "0.0.96", optional = true} ethash = { path = "../ethash" } @@ -40,6 +39,7 @@ ethstore = { path = "../ethstore" } ethkey = { path = "../ethkey" } ethcore-ipc-nano = { path = "../ipc/nano" } rlp = { path = "../util/rlp" } +lru-cache = "0.1.0" ethcore-bloom-journal = { path = "../util/bloom" } [dependencies.hyper] diff --git a/ethcore/src/evm/interpreter/shared_cache.rs b/ethcore/src/evm/interpreter/shared_cache.rs index dee557522..cacc4dde3 100644 --- a/ethcore/src/evm/interpreter/shared_cache.rs +++ b/ethcore/src/evm/interpreter/shared_cache.rs @@ -15,20 +15,27 @@ // along with Parity. If not, see . use std::sync::Arc; -use lru_cache::LruCache; -use util::{H256, Mutex}; +use util::{H256, HeapSizeOf, Mutex}; use util::sha3::*; +use util::cache::MemoryLruCache; use bit_set::BitSet; use super::super::instructions; -const INITIAL_CAPACITY: usize = 32; const DEFAULT_CACHE_SIZE: usize = 4 * 1024 * 1024; +// stub for a HeapSizeOf implementation. +struct Bits(Arc); + +impl HeapSizeOf for Bits { + fn heap_size_of_children(&self) -> usize { + // dealing in bits here + self.0.capacity() * 8 + } +} + /// Global cache for EVM interpreter pub struct SharedCache { - jump_destinations: Mutex>>, - max_size: usize, - cur_size: Mutex, + jump_destinations: Mutex>, } impl SharedCache { @@ -36,9 +43,7 @@ impl SharedCache { /// to cache. pub fn new(max_size: usize) -> Self { SharedCache { - jump_destinations: Mutex::new(LruCache::new(INITIAL_CAPACITY)), - max_size: max_size * 8, // dealing with bits here. - cur_size: Mutex::new(0), + jump_destinations: Mutex::new(MemoryLruCache::new(max_size)), } } @@ -49,37 +54,11 @@ impl SharedCache { } if let Some(d) = self.jump_destinations.lock().get_mut(code_hash) { - return d.clone(); + return d.0.clone(); } let d = Self::find_jump_destinations(code); - - { - let mut cur_size = self.cur_size.lock(); - *cur_size += d.capacity(); - - let mut jump_dests = self.jump_destinations.lock(); - let cap = jump_dests.capacity(); - - // grow the cache as necessary; it operates on amount of items - // but we're working based on memory usage. - if jump_dests.len() == cap && *cur_size < self.max_size { - jump_dests.set_capacity(cap * 2); - } - - // account for any element displaced from the cache. - if let Some(lru) = jump_dests.insert(code_hash.clone(), d.clone()) { - *cur_size -= lru.capacity(); - } - - // remove elements until we are below the memory target. - while *cur_size > self.max_size { - match jump_dests.remove_lru() { - Some((_, v)) => *cur_size -= v.capacity(), - _ => break, - } - } - } + self.jump_destinations.lock().insert(code_hash.clone(), Bits(d.clone())); d } diff --git a/ethcore/src/state/account.rs b/ethcore/src/state/account.rs index 2bd8a2d15..d8d281b17 100644 --- a/ethcore/src/state/account.rs +++ b/ethcore/src/state/account.rs @@ -247,23 +247,34 @@ impl Account { } /// Provide a database to get `code_hash`. Should not be called if it is a contract without code. - pub fn cache_code(&mut self, db: &HashDB) -> bool { + pub fn cache_code(&mut self, db: &HashDB) -> Option> { // TODO: fill out self.code_cache; trace!("Account::cache_code: ic={}; self.code_hash={:?}, self.code_cache={}", self.is_cached(), self.code_hash, self.code_cache.pretty()); - self.is_cached() || + + if self.is_cached() { return Some(self.code_cache.clone()) } + match db.get(&self.code_hash) { Some(x) => { self.code_size = Some(x.len()); self.code_cache = Arc::new(x.to_vec()); - true + Some(self.code_cache.clone()) }, _ => { warn!("Failed reverse get of {}", self.code_hash); - false + None }, } } + /// Provide code to cache. For correctness, should be the correct code for the + /// account. + pub fn cache_given_code(&mut self, code: Arc) { + trace!("Account::cache_given_code: ic={}; self.code_hash={:?}, self.code_cache={}", self.is_cached(), self.code_hash, self.code_cache.pretty()); + + self.code_size = Some(code.len()); + self.code_cache = code; + } + /// Provide a database to get `code_size`. Should not be called if it is a contract without code. pub fn cache_code_size(&mut self, db: &HashDB) -> bool { // TODO: fill out self.code_cache; @@ -476,7 +487,7 @@ mod tests { }; let mut a = Account::from_rlp(&rlp); - assert!(a.cache_code(&db.immutable())); + assert!(a.cache_code(&db.immutable()).is_some()); let mut a = Account::from_rlp(&rlp); assert_eq!(a.note_code(vec![0x55, 0x44, 0xffu8]), Ok(())); diff --git a/ethcore/src/state/mod.rs b/ethcore/src/state/mod.rs index bef20d257..7c0f43d97 100644 --- a/ethcore/src/state/mod.rs +++ b/ethcore/src/state/mod.rs @@ -599,14 +599,30 @@ impl State { pod_state::diff_pod(&state_pre.to_pod(), &pod_state_post) } - fn update_account_cache(require: RequireCache, account: &mut Account, db: &HashDB) { - match require { - RequireCache::None => {}, - RequireCache::Code => { - account.cache_code(db); - } - RequireCache::CodeSize => { - account.cache_code_size(db); + // load required account data from the databases. + fn update_account_cache(require: RequireCache, account: &mut Account, state_db: &StateDB, db: &HashDB) { + match (account.is_cached(), require) { + (true, _) | (false, RequireCache::None) => {} + (false, require) => { + // if there's already code in the global cache, always cache it + // locally. + let hash = account.code_hash(); + match state_db.get_cached_code(&hash) { + Some(code) => account.cache_given_code(code), + None => match require { + RequireCache::None => {}, + RequireCache::Code => { + if let Some(code) = account.cache_code(db) { + // propagate code loaded from the database to + // the global code cache. + state_db.cache_code(hash, code) + } + } + RequireCache::CodeSize => { + account.cache_code_size(db); + } + } + } } } } @@ -620,7 +636,7 @@ impl State { if let Some(ref mut maybe_acc) = self.cache.borrow_mut().get_mut(a) { if let Some(ref mut account) = maybe_acc.account { let accountdb = self.factories.accountdb.readonly(self.db.as_hashdb(), account.address_hash(a)); - Self::update_account_cache(require, account, accountdb.as_hashdb()); + Self::update_account_cache(require, account, &self.db, accountdb.as_hashdb()); return f(Some(account)); } return f(None); @@ -629,7 +645,7 @@ impl State { let result = self.db.get_cached(a, |mut acc| { if let Some(ref mut account) = acc { let accountdb = self.factories.accountdb.readonly(self.db.as_hashdb(), account.address_hash(a)); - Self::update_account_cache(require, account, accountdb.as_hashdb()); + Self::update_account_cache(require, account, &self.db, accountdb.as_hashdb()); } f(acc.map(|a| &*a)) }); @@ -647,7 +663,7 @@ impl State { }; if let Some(ref mut account) = maybe_acc.as_mut() { let accountdb = self.factories.accountdb.readonly(self.db.as_hashdb(), account.address_hash(a)); - Self::update_account_cache(require, account, accountdb.as_hashdb()); + Self::update_account_cache(require, account, &self.db, accountdb.as_hashdb()); } let r = f(maybe_acc.as_ref()); self.insert_cache(a, AccountEntry::new_clean(maybe_acc)); @@ -703,7 +719,7 @@ impl State { if require_code { let addr_hash = account.address_hash(a); let accountdb = self.factories.accountdb.readonly(self.db.as_hashdb(), addr_hash); - account.cache_code(accountdb.as_hashdb()); + Self::update_account_cache(RequireCache::Code, account, &self.db, accountdb.as_hashdb()); } account }, diff --git a/ethcore/src/state_db.rs b/ethcore/src/state_db.rs index dfa65ab1d..affc0b405 100644 --- a/ethcore/src/state_db.rs +++ b/ethcore/src/state_db.rs @@ -16,6 +16,7 @@ use std::collections::{VecDeque, HashSet}; use lru_cache::LruCache; +use util::cache::MemoryLruCache; use util::journaldb::JournalDB; use util::hash::{H256}; use util::hashdb::HashDB; @@ -33,12 +34,17 @@ pub const ACCOUNT_BLOOM_HASHCOUNT_KEY: &'static [u8] = b"account_hash_count"; const STATE_CACHE_BLOCKS: usize = 12; +// The percentage of supplied cache size to go to accounts. +const ACCOUNT_CACHE_RATIO: usize = 90; + /// Shared canonical state cache. struct AccountCache { /// DB Account cache. `None` indicates that account is known to be missing. // When changing the type of the values here, be sure to update `mem_used` and // `new`. accounts: LruCache>, + /// DB Code cache. Maps code hashes to shared bytes. + code: MemoryLruCache>>, /// Information on the modifications in recently committed blocks; specifically which addresses /// changed in which block. Ordered by block number. modifications: VecDeque, @@ -111,12 +117,15 @@ impl StateDB { // into the `AccountCache` structure as its own `LruCache<(Address, H256), H256>`. pub fn new(db: Box, cache_size: usize) -> StateDB { let bloom = Self::load_bloom(db.backing()); - let cache_items = cache_size / ::std::mem::size_of::>(); + let acc_cache_size = cache_size * ACCOUNT_CACHE_RATIO / 100; + let code_cache_size = cache_size - acc_cache_size; + let cache_items = acc_cache_size / ::std::mem::size_of::>(); StateDB { db: db, account_cache: Arc::new(Mutex::new(AccountCache { accounts: LruCache::new(cache_items), + code: MemoryLruCache::new(code_cache_size), modifications: VecDeque::new(), })), local_cache: Vec::new(), @@ -342,7 +351,12 @@ impl StateDB { /// Heap size used. pub fn mem_used(&self) -> usize { // TODO: account for LRU-cache overhead; this is a close approximation. - self.db.mem_used() + self.account_cache.lock().accounts.len() * ::std::mem::size_of::>() + self.db.mem_used() + { + let cache = self.account_cache.lock(); + + cache.code.current_size() + + cache.accounts.len() * ::std::mem::size_of::>() + } } /// Returns underlying `JournalDB`. @@ -362,6 +376,15 @@ impl StateDB { }) } + /// Add a global code cache entry. This doesn't need to worry about canonicality because + /// it simply maps hashes to raw code and will always be correct in the absence of + /// hash collisions. + pub fn cache_code(&self, hash: H256, code: Arc>) { + let mut cache = self.account_cache.lock(); + + cache.code.insert(hash, code); + } + /// Get basic copy of the cached account. Does not include storage. /// Returns 'None' if cache is disabled or if the account is not cached. pub fn get_cached_account(&self, addr: &Address) -> Option> { @@ -372,6 +395,13 @@ impl StateDB { cache.accounts.get_mut(addr).map(|a| a.as_ref().map(|a| a.clone_basic())) } + /// Get cached code based on hash. + pub fn get_cached_code(&self, hash: &H256) -> Option>> { + let mut cache = self.account_cache.lock(); + + cache.code.get_mut(hash).map(|code| code.clone()) + } + /// Get value from a cached account. /// Returns 'None' if cache is disabled or if the account is not cached. pub fn get_cached(&self, a: &Address, f: F) -> Option diff --git a/util/Cargo.toml b/util/Cargo.toml index 1b6939595..78cca92e0 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -36,6 +36,7 @@ ansi_term = "0.7" tiny-keccak= "1.0" ethcore-bloom-journal = { path = "bloom" } regex = "0.1" +lru-cache = "0.1.0" [features] default = [] diff --git a/util/src/cache.rs b/util/src/cache.rs new file mode 100644 index 000000000..2b2c50c8b --- /dev/null +++ b/util/src/cache.rs @@ -0,0 +1,79 @@ +// Copyright 2015, 2016 Ethcore (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 . + +//! Lru-cache related utilities as quick-and-dirty wrappers around the lru-cache +//! crate. +// TODO: push changes upstream in a clean way. + +use heapsize::HeapSizeOf; +use lru_cache::LruCache; + +use std::hash::Hash; + +const INITIAL_CAPACITY: usize = 4; + +/// An LRU-cache which operates on memory used. +pub struct MemoryLruCache { + inner: LruCache, + cur_size: usize, + max_size: usize, +} + +impl MemoryLruCache { + /// Create a new cache with a maximum size in bytes. + pub fn new(max_size: usize) -> Self { + MemoryLruCache { + inner: LruCache::new(INITIAL_CAPACITY), + max_size: max_size, + cur_size: 0, + } + } + + /// Insert an item. + pub fn insert(&mut self, key: K, val: V) { + let cap = self.inner.capacity(); + + // grow the cache as necessary; it operates on amount of items + // but we're working based on memory usage. + if self.inner.len() == cap && self.cur_size < self.max_size { + self.inner.set_capacity(cap * 2); + } + + // account for any element displaced from the cache. + if let Some(lru) = self.inner.insert(key, val) { + self.cur_size -= lru.heap_size_of_children(); + } + + // remove elements until we are below the memory target. + while self.cur_size > self.max_size { + match self.inner.remove_lru() { + Some((_, v)) => self.cur_size -= v.heap_size_of_children(), + _ => break, + } + } + } + + /// Get a reference to an item in the cache. It is a logic error for its + /// heap size to be altered while borrowed. + pub fn get_mut(&mut self, key: &K) -> Option<&mut V> { + self.inner.get_mut(key) + } + + /// Currently-used size of values in bytes. + pub fn current_size(&self) -> usize { + self.cur_size + } +} \ No newline at end of file diff --git a/util/src/lib.rs b/util/src/lib.rs index e362459a6..f5558bcfc 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -105,6 +105,7 @@ extern crate ansi_term; extern crate tiny_keccak; extern crate rlp; extern crate regex; +extern crate lru_cache; #[macro_use] extern crate heapsize; @@ -143,6 +144,7 @@ pub mod semantic_version; pub mod log; pub mod path; pub mod snappy; +pub mod cache; mod timer; pub use common::*; From 2806f1d4c938c87386d0ee0f91e5a6d4446b16d4 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Fri, 28 Oct 2016 16:10:30 +0200 Subject: [PATCH 056/192] Even more snapshot validity checks (#2935) * clarify "cancelled periodic snapshot" message * more rigorous checks for snapshot validity * verify ancient blocks on import * limit number of fed blocks * make it possible to feed snapshot service canonical hashes * fix failing test build * swap ethash DAG only when more recent --- ethash/src/lib.rs | 21 +++-- ethcore/src/blockchain/blockchain.rs | 5 +- ethcore/src/client/client.rs | 23 +++++- ethcore/src/snapshot/error.rs | 12 ++- ethcore/src/snapshot/mod.rs | 80 ++++++++++++++++--- ethcore/src/snapshot/service.rs | 28 +++++-- .../src/snapshot/snapshot_service_trait.rs | 4 + ethcore/src/snapshot/tests/blocks.rs | 13 +-- sync/src/tests/snapshot.rs | 7 ++ 9 files changed, 154 insertions(+), 39 deletions(-) diff --git a/ethash/src/lib.rs b/ethash/src/lib.rs index 130882318..a04e2486b 100644 --- a/ethash/src/lib.rs +++ b/ethash/src/lib.rs @@ -69,14 +69,19 @@ impl EthashManager { Some(ref e) if *e == epoch => lights.recent.clone(), _ => match lights.prev_epoch.clone() { Some(e) if e == epoch => { - // swap - let t = lights.prev_epoch; - lights.prev_epoch = lights.recent_epoch; - lights.recent_epoch = t; - let t = lights.prev.clone(); - lights.prev = lights.recent.clone(); - lights.recent = t; - lights.recent.clone() + // don't swap if recent is newer. + if lights.recent_epoch > lights.prev_epoch { + None + } else { + // swap + let t = lights.prev_epoch; + lights.prev_epoch = lights.recent_epoch; + lights.recent_epoch = t; + let t = lights.prev.clone(); + lights.prev = lights.recent.clone(); + lights.recent = t; + lights.recent.clone() + } } _ => None, }, diff --git a/ethcore/src/blockchain/blockchain.rs b/ethcore/src/blockchain/blockchain.rs index 282039c16..d95c199ed 100644 --- a/ethcore/src/blockchain/blockchain.rs +++ b/ethcore/src/blockchain/blockchain.rs @@ -792,11 +792,10 @@ impl BlockChain { /// the chain and the child's parent is this block. /// /// Used in snapshots to glue the chunks together at the end. - pub fn add_child(&self, block_hash: H256, child_hash: H256) { + pub fn add_child(&self, batch: &mut DBTransaction, block_hash: H256, child_hash: H256) { let mut parent_details = self.block_details(&block_hash) .unwrap_or_else(|| panic!("Invalid block hash: {:?}", block_hash)); - let mut batch = self.db.transaction(); parent_details.children.push(child_hash); let mut update = HashMap::new(); @@ -807,8 +806,6 @@ impl BlockChain { batch.extend_with_cache(db::COL_EXTRA, &mut *write_details, update, CacheUpdatePolicy::Overwrite); self.cache_man.lock().note_used(CacheID::BlockDetails(block_hash)); - - self.db.write(batch).unwrap(); } #[cfg_attr(feature="dev", allow(similar_names))] diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index b59d892c5..388ea97e8 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -66,6 +66,7 @@ use snapshot::{self, io as snapshot_io}; use factory::Factories; use rlp::{View, UntrustedRlp}; use state_db::StateDB; +use rand::OsRng; // re-export pub use types::blockchain_info::BlockChainInfo; @@ -144,6 +145,7 @@ pub struct Client { last_hashes: RwLock>, factories: Factories, history: u64, + rng: Mutex, } impl Client { @@ -239,6 +241,7 @@ impl Client { last_hashes: RwLock::new(VecDeque::new()), factories: factories, history: history, + rng: Mutex::new(try!(OsRng::new().map_err(::util::UtilError::StdIo))), }; Ok(Arc::new(client)) } @@ -434,14 +437,26 @@ impl Client { /// Import a block with transaction receipts. /// The block is guaranteed to be the next best blocks in the first block sequence. /// Does no sealing or transaction validation. - fn import_old_block(&self, block_bytes: Bytes, receipts_bytes: Bytes) -> H256 { + fn import_old_block(&self, block_bytes: Bytes, receipts_bytes: Bytes) -> Result { let block = BlockView::new(&block_bytes); - let hash = block.header().hash(); + let header = block.header(); + let hash = header.hash(); let _import_lock = self.import_lock.lock(); { let _timer = PerfTimer::new("import_old_block"); + let mut rng = self.rng.lock(); let chain = self.chain.read(); + // verify block. + try!(::snapshot::verify_old_block( + &mut *rng, + &header, + &*self.engine, + &*chain, + Some(&block_bytes), + false, + )); + // Commit results let receipts = ::rlp::decode(&receipts_bytes); let mut batch = DBTransaction::new(&self.db.read()); @@ -451,7 +466,7 @@ impl Client { chain.commit(); } self.db.read().flush().expect("DB flush failed."); - hash + Ok(hash) } fn commit_block(&self, block: B, hash: &H256, block_data: &[u8]) -> ImportRoute where B: IsBlock + Drain { @@ -1036,7 +1051,7 @@ impl BlockChainClient for Client { return Err(BlockImportError::Block(BlockError::UnknownParent(header.parent_hash()))); } } - Ok(self.import_old_block(block_bytes, receipts_bytes)) + self.import_old_block(block_bytes, receipts_bytes).map_err(Into::into) } fn queue_info(&self) -> BlockQueueInfo { diff --git a/ethcore/src/snapshot/error.rs b/ethcore/src/snapshot/error.rs index acd9409f7..d634057dc 100644 --- a/ethcore/src/snapshot/error.rs +++ b/ethcore/src/snapshot/error.rs @@ -33,6 +33,12 @@ pub enum Error { BlockNotFound(H256), /// Incomplete chain. IncompleteChain, + /// Best block has wrong state root. + WrongStateRoot(H256, H256), + /// Wrong block hash. + WrongBlockHash(u64, H256, H256), + /// Too many blocks contained within the snapshot. + TooManyBlocks(u64, u64), /// Old starting block in a pruned database. OldBlockPrunedDB, /// Missing code. @@ -52,7 +58,11 @@ impl fmt::Display for Error { match *self { Error::InvalidStartingBlock(ref id) => write!(f, "Invalid starting block: {:?}", id), Error::BlockNotFound(ref hash) => write!(f, "Block not found in chain: {}", hash), - Error::IncompleteChain => write!(f, "Cannot create snapshot due to incomplete chain."), + Error::IncompleteChain => write!(f, "Incomplete blockchain."), + Error::WrongStateRoot(ref expected, ref found) => write!(f, "Final block has wrong state root. Expected {:?}, got {:?}", expected, found), + Error::WrongBlockHash(ref num, ref expected, ref found) => + write!(f, "Block {} had wrong hash. expected {:?}, got {:?}", num, expected, found), + Error::TooManyBlocks(ref expected, ref found) => write!(f, "Snapshot contained too many blocks. Expected {}, got {}", expected, found), Error::OldBlockPrunedDB => write!(f, "Attempted to create a snapshot at an old block while using \ a pruned database. Please re-run with the --pruning archive flag."), Error::MissingCode(ref missing) => write!(f, "Incomplete snapshot: {} contract codes not found.", missing.len()), diff --git a/ethcore/src/snapshot/mod.rs b/ethcore/src/snapshot/mod.rs index 86f921cf0..22c44ba3b 100644 --- a/ethcore/src/snapshot/mod.rs +++ b/ethcore/src/snapshot/mod.rs @@ -26,6 +26,7 @@ use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use account_db::{AccountDB, AccountDBMut}; use blockchain::{BlockChain, BlockProvider}; use engines::Engine; +use header::Header; use ids::BlockID; use views::BlockView; @@ -528,6 +529,20 @@ fn rebuild_accounts( /// Proportion of blocks which we will verify `PoW` for. const POW_VERIFY_RATE: f32 = 0.02; +/// Verify an old block with the given header, engine, blockchain, body. If `always` is set, it will perform +/// the fullest verification possible. If not, it will take a random sample to determine whether it will +/// do heavy or light verification. +pub fn verify_old_block(rng: &mut OsRng, header: &Header, engine: &Engine, chain: &BlockChain, body: Option<&[u8]>, always: bool) -> Result<(), ::error::Error> { + if always || rng.gen::() <= POW_VERIFY_RATE { + match chain.block_header(header.parent_hash()) { + Some(parent) => engine.verify_block_family(&header, &parent, body), + None => engine.verify_block_seal(&header), + } + } else { + engine.verify_block_basic(&header, body) + } +} + /// Rebuilds the blockchain from chunks. /// /// Does basic verification for all blocks, but `PoW` verification for some. @@ -543,17 +558,23 @@ pub struct BlockRebuilder { rng: OsRng, disconnected: Vec<(u64, H256)>, best_number: u64, + best_hash: H256, + best_root: H256, + fed_blocks: u64, } impl BlockRebuilder { /// Create a new BlockRebuilder. - pub fn new(chain: BlockChain, db: Arc, best_number: u64) -> Result { + pub fn new(chain: BlockChain, db: Arc, manifest: &ManifestData) -> Result { Ok(BlockRebuilder { chain: chain, db: db, rng: try!(OsRng::new()), disconnected: Vec::new(), - best_number: best_number, + best_number: manifest.block_number, + best_hash: manifest.block_hash, + best_root: manifest.state_root, + fed_blocks: 0, }) } @@ -566,9 +587,14 @@ impl BlockRebuilder { let rlp = UntrustedRlp::new(chunk); let item_count = rlp.item_count(); + let num_blocks = (item_count - 3) as u64; trace!(target: "snapshot", "restoring block chunk with {} blocks.", item_count - 3); + if self.fed_blocks + num_blocks > SNAPSHOT_BLOCKS { + return Err(Error::TooManyBlocks(SNAPSHOT_BLOCKS, self.fed_blocks).into()) + } + // todo: assert here that these values are consistent with chunks being in order. let mut cur_number = try!(rlp.val_at::(0)) + 1; let mut parent_hash = try!(rlp.val_at::(1)); @@ -585,14 +611,27 @@ impl BlockRebuilder { let block = try!(abridged_block.to_block(parent_hash, cur_number, receipts_root)); let block_bytes = block.rlp_bytes(With); + let is_best = cur_number == self.best_number; - if self.rng.gen::() <= POW_VERIFY_RATE { - try!(engine.verify_block_seal(&block.header)) - } else { - try!(engine.verify_block_basic(&block.header, Some(&block_bytes))); + if is_best { + if block.header.hash() != self.best_hash { + return Err(Error::WrongBlockHash(cur_number, self.best_hash, block.header.hash()).into()) + } + + if block.header.state_root() != &self.best_root { + return Err(Error::WrongStateRoot(self.best_root, *block.header.state_root()).into()) + } } - let is_best = cur_number == self.best_number; + try!(verify_old_block( + &mut self.rng, + &block.header, + engine, + &self.chain, + Some(&block_bytes), + is_best + )); + let mut batch = self.db.transaction(); // special-case the first block in each chunk. @@ -610,11 +649,15 @@ impl BlockRebuilder { cur_number += 1; } - Ok(item_count as u64 - 3) + self.fed_blocks += num_blocks; + + Ok(num_blocks) } - /// Glue together any disconnected chunks. To be called at the end. - pub fn glue_chunks(self) { + /// Glue together any disconnected chunks and check that the chain is complete. + pub fn finalize(self, canonical: HashMap) -> Result<(), Error> { + let mut batch = self.db.transaction(); + for (first_num, first_hash) in self.disconnected { let parent_num = first_num - 1; @@ -623,8 +666,23 @@ impl BlockRebuilder { // the first block of the first chunks has nothing to connect to. if let Some(parent_hash) = self.chain.block_hash(parent_num) { // if so, add the child to it. - self.chain.add_child(parent_hash, first_hash); + self.chain.add_child(&mut batch, parent_hash, first_hash); } } + self.db.write_buffered(batch); + + let best_number = self.best_number; + for num in (0..self.fed_blocks).map(|x| best_number - x) { + + let hash = try!(self.chain.block_hash(num).ok_or(Error::IncompleteChain)); + + if let Some(canon_hash) = canonical.get(&num).cloned() { + if canon_hash != hash { + return Err(Error::WrongBlockHash(num, canon_hash, hash)); + } + } + } + + Ok(()) } } diff --git a/ethcore/src/snapshot/service.rs b/ethcore/src/snapshot/service.rs index 9b66a5cdc..cc30a5c26 100644 --- a/ethcore/src/snapshot/service.rs +++ b/ethcore/src/snapshot/service.rs @@ -16,7 +16,7 @@ //! Snapshot network service implementation. -use std::collections::HashSet; +use std::collections::{HashMap, HashSet}; use std::io::ErrorKind; use std::fs; use std::path::PathBuf; @@ -74,6 +74,7 @@ struct Restoration { snappy_buffer: Bytes, final_state_root: H256, guard: Guard, + canonical_hashes: HashMap, db: Arc, } @@ -99,7 +100,7 @@ impl Restoration { .map_err(UtilError::SimpleString))); let chain = BlockChain::new(Default::default(), params.genesis, raw_db.clone()); - let blocks = try!(BlockRebuilder::new(chain, raw_db.clone(), manifest.block_number)); + let blocks = try!(BlockRebuilder::new(chain, raw_db.clone(), &manifest)); let root = manifest.state_root.clone(); Ok(Restoration { @@ -112,6 +113,7 @@ impl Restoration { snappy_buffer: Vec::new(), final_state_root: root, guard: params.guard, + canonical_hashes: HashMap::new(), db: raw_db, }) } @@ -138,13 +140,18 @@ impl Restoration { try!(self.blocks.feed(&self.snappy_buffer[..len], engine)); if let Some(ref mut writer) = self.writer.as_mut() { - try!(writer.write_block_chunk(hash, chunk)); + try!(writer.write_block_chunk(hash, chunk)); } } Ok(()) } + // note canonical hashes. + fn note_canonical(&mut self, hashes: &[(u64, H256)]) { + self.canonical_hashes.extend(hashes.iter().cloned()); + } + // finish up restoration. fn finalize(self) -> Result<(), Error> { use util::trie::TrieError; @@ -161,8 +168,8 @@ impl Restoration { // check for missing code. try!(self.state.check_missing()); - // connect out-of-order chunks. - self.blocks.glue_chunks(); + // connect out-of-order chunks and verify chain integrity. + try!(self.blocks.finalize(self.canonical_hashes)); if let Some(writer) = self.writer { try!(writer.finish(self.manifest)); @@ -352,7 +359,8 @@ impl Service { // "Cancelled" is mincing words a bit -- what really happened // is that the state we were snapshotting got pruned out // before we could finish. - info!("Cancelled prematurely-started periodic snapshot."); + info!("Periodic snapshot failed: block state pruned.\ + Run with a longer `--pruning-history` or with `--no-periodic-snapshot`"); return Ok(()) } else { return Err(e); @@ -580,6 +588,14 @@ impl SnapshotService for Service { trace!("Error sending snapshot service message: {:?}", e); } } + + fn provide_canon_hashes(&self, canonical: &[(u64, H256)]) { + let mut rest = self.restoration.lock(); + + if let Some(ref mut rest) = rest.as_mut() { + rest.note_canonical(canonical); + } + } } impl Drop for Service { diff --git a/ethcore/src/snapshot/snapshot_service_trait.rs b/ethcore/src/snapshot/snapshot_service_trait.rs index 65448090f..42223f878 100644 --- a/ethcore/src/snapshot/snapshot_service_trait.rs +++ b/ethcore/src/snapshot/snapshot_service_trait.rs @@ -48,6 +48,10 @@ pub trait SnapshotService : Sync + Send { /// Feed a raw block chunk to the service to be processed asynchronously. /// no-op if currently restoring. fn restore_block_chunk(&self, hash: H256, chunk: Bytes); + + /// Give the restoration in-progress some canonical block hashes for + /// extra verification (performed at the end) + fn provide_canon_hashes(&self, canonical: &[(u64, H256)]); } impl IpcConfig for SnapshotService { } diff --git a/ethcore/src/snapshot/tests/blocks.rs b/ethcore/src/snapshot/tests/blocks.rs index 62c6ea2fe..12efcda77 100644 --- a/ethcore/src/snapshot/tests/blocks.rs +++ b/ethcore/src/snapshot/tests/blocks.rs @@ -26,6 +26,7 @@ use snapshot::io::{PackedReader, PackedWriter, SnapshotReader, SnapshotWriter}; use util::{Mutex, snappy}; use util::kvdb::{Database, DatabaseConfig}; +use std::collections::HashMap; use std::sync::Arc; fn chunk_and_restore(amount: u64) { @@ -58,18 +59,20 @@ fn chunk_and_restore(amount: u64) { // snapshot it. let writer = Mutex::new(PackedWriter::new(&snapshot_path).unwrap()); let block_hashes = chunk_blocks(&bc, best_hash, &writer, &Progress::default()).unwrap(); - writer.into_inner().finish(::snapshot::ManifestData { + let manifest = ::snapshot::ManifestData { state_hashes: Vec::new(), block_hashes: block_hashes, - state_root: Default::default(), + state_root: ::util::sha3::SHA3_NULL_RLP, block_number: amount, block_hash: best_hash, - }).unwrap(); + }; + + writer.into_inner().finish(manifest.clone()).unwrap(); // restore it. let new_db = Arc::new(Database::open(&db_cfg, new_path.as_str()).unwrap()); let new_chain = BlockChain::new(Default::default(), &genesis, new_db.clone()); - let mut rebuilder = BlockRebuilder::new(new_chain, new_db.clone(), amount).unwrap(); + let mut rebuilder = BlockRebuilder::new(new_chain, new_db.clone(), &manifest).unwrap(); let reader = PackedReader::new(&snapshot_path).unwrap().unwrap(); let engine = ::engines::NullEngine::new(Default::default(), Default::default()); for chunk_hash in &reader.manifest().block_hashes { @@ -78,7 +81,7 @@ fn chunk_and_restore(amount: u64) { rebuilder.feed(&chunk, &engine).unwrap(); } - rebuilder.glue_chunks(); + rebuilder.finalize(HashMap::new()).unwrap(); // and test it. let new_chain = BlockChain::new(Default::default(), &genesis, new_db); diff --git a/sync/src/tests/snapshot.rs b/sync/src/tests/snapshot.rs index 58b7ec786..813513e84 100644 --- a/sync/src/tests/snapshot.rs +++ b/sync/src/tests/snapshot.rs @@ -23,6 +23,7 @@ use super::helpers::*; pub struct TestSnapshotService { manifest: Option, chunks: HashMap, + canon_hashes: Mutex>, restoration_manifest: Mutex>, state_restoration_chunks: Mutex>, @@ -34,6 +35,7 @@ impl TestSnapshotService { TestSnapshotService { manifest: None, chunks: HashMap::new(), + canon_hashes: Mutex::new(HashMap::new()), restoration_manifest: Mutex::new(None), state_restoration_chunks: Mutex::new(HashMap::new()), block_restoration_chunks: Mutex::new(HashMap::new()), @@ -57,6 +59,7 @@ impl TestSnapshotService { TestSnapshotService { manifest: Some(manifest), chunks: chunks, + canon_hashes: Mutex::new(HashMap::new()), restoration_manifest: Mutex::new(None), state_restoration_chunks: Mutex::new(HashMap::new()), block_restoration_chunks: Mutex::new(HashMap::new()), @@ -110,6 +113,10 @@ impl SnapshotService for TestSnapshotService { self.block_restoration_chunks.lock().insert(hash, chunk); } } + + fn provide_canon_hashes(&self, hashes: &[(u64, H256)]) { + self.canon_hashes.lock().extend(hashes.iter().cloned()); + } } #[test] From 0f0334275eded44ae1646501d5fc73f3d1404889 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Fri, 28 Oct 2016 16:42:24 +0200 Subject: [PATCH 057/192] Validating minimal required gas for a transaction (#2937) * Validating minimal required gas for a transaction * Adding RPC case and note * Fixing whitespace [ci skip] --- ethcore/src/client/client.rs | 19 +- ethcore/src/client/test_client.rs | 8 +- ethcore/src/client/traits.rs | 5 +- ethcore/src/error.rs | 9 + ethcore/src/miner/banning_queue.rs | 28 ++- ethcore/src/miner/miner.rs | 6 +- ethcore/src/miner/transaction_queue.rs | 270 ++++++++++++++----------- rpc/src/v1/helpers/errors.rs | 3 + 8 files changed, 218 insertions(+), 130 deletions(-) diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 388ea97e8..4709955d1 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -60,7 +60,7 @@ use receipt::LocalizedReceipt; use trace::{TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Database as TraceDatabase}; use trace; use trace::FlatTransactionTraces; -use evm::Factory as EvmFactory; +use evm::{Factory as EvmFactory, Schedule}; use miner::{Miner, MinerService}; use snapshot::{self, io as snapshot_io}; use factory::Factories; @@ -1156,6 +1156,23 @@ impl BlockChainClient for Client { } impl MiningBlockChainClient for Client { + + fn latest_schedule(&self) -> Schedule { + let header_data = self.best_block_header(); + let view = HeaderView::new(&header_data); + + let env_info = EnvInfo { + number: view.number(), + author: view.author(), + timestamp: view.timestamp(), + difficulty: view.difficulty(), + last_hashes: self.build_last_hashes(view.hash()), + gas_used: U256::default(), + gas_limit: view.gas_limit(), + }; + self.engine.schedule(&env_info) + } + fn prepare_open_block(&self, author: Address, gas_range_target: (U256, U256), extra_data: Bytes) -> OpenBlock { let engine = &*self.engine; let chain = self.chain.read(); diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index e32a074bb..11a9286da 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -34,7 +34,7 @@ use log_entry::LocalizedLogEntry; use receipt::{Receipt, LocalizedReceipt}; use blockchain::extras::BlockReceipts; use error::{ImportResult}; -use evm::{Factory as EvmFactory, VMType}; +use evm::{Factory as EvmFactory, VMType, Schedule}; use miner::{Miner, MinerService, TransactionImportResult}; use spec::Spec; @@ -137,7 +137,7 @@ impl TestBlockChainClient { client.genesis_hash = client.last_hash.read().clone(); client } - + /// Set the transaction receipt result pub fn set_transaction_receipt(&self, id: TransactionID, receipt: LocalizedReceipt) { self.receipts.write().insert(id, receipt); @@ -306,6 +306,10 @@ pub fn get_temp_state_db() -> GuardedTempResult { } impl MiningBlockChainClient for TestBlockChainClient { + fn latest_schedule(&self) -> Schedule { + Schedule::new_homestead_gas_fix() + } + fn prepare_open_block(&self, author: Address, gas_range_target: (U256, U256), extra_data: Bytes) -> OpenBlock { let engine = &*self.spec.engine; let genesis_header = self.spec.genesis_header(); diff --git a/ethcore/src/client/traits.rs b/ethcore/src/client/traits.rs index 0bc9e70fa..700b88f8b 100644 --- a/ethcore/src/client/traits.rs +++ b/ethcore/src/client/traits.rs @@ -27,7 +27,7 @@ use views::{BlockView}; use error::{ImportResult, CallError}; use receipt::LocalizedReceipt; use trace::LocalizedTrace; -use evm::Factory as EvmFactory; +use evm::{Factory as EvmFactory, Schedule}; use types::ids::*; use types::trace_filter::Filter as TraceFilter; use executive::Executed; @@ -236,6 +236,9 @@ pub trait MiningBlockChainClient : BlockChainClient { /// Import sealed block. Skips all verifications. fn import_sealed_block(&self, block: SealedBlock) -> ImportResult; + + /// Returns latest schedule. + fn latest_schedule(&self) -> Schedule; } impl IpcConfig for BlockChainClient { } diff --git a/ethcore/src/error.rs b/ethcore/src/error.rs index 40f4c6d28..04a0920fa 100644 --- a/ethcore/src/error.rs +++ b/ethcore/src/error.rs @@ -47,6 +47,13 @@ pub enum TransactionError { /// Transaction gas price got: U256, }, + /// Transaction's gas is below currently set minimal gas requirement. + InsufficientGas { + /// Minimal expected gas + minimal: U256, + /// Transaction gas + got: U256, + }, /// Sender doesn't have enough funds to pay for this transaction InsufficientBalance { /// Senders balance @@ -81,6 +88,8 @@ impl fmt::Display for TransactionError { LimitReached => "Transaction limit reached".into(), InsufficientGasPrice { minimal, got } => format!("Insufficient gas price. Min={}, Given={}", minimal, got), + InsufficientGas { minimal, got } => + format!("Insufficient gas. Min={}, Given={}", minimal, got), InsufficientBalance { balance, cost } => format!("Insufficient balance for transaction. Balance={}, Cost={}", balance, cost), diff --git a/ethcore/src/miner/banning_queue.rs b/ethcore/src/miner/banning_queue.rs index 0ad99b008..0329503bf 100644 --- a/ethcore/src/miner/banning_queue.rs +++ b/ethcore/src/miner/banning_queue.rs @@ -75,11 +75,15 @@ impl BanningTransactionQueue { /// Add to the queue taking bans into consideration. /// May reject transaction because of the banlist. - pub fn add_with_banlist( + pub fn add_with_banlist( &mut self, transaction: SignedTransaction, account_details: &F, - ) -> Result where F: Fn(&Address) -> AccountDetails { + gas_estimator: &G, + ) -> Result where + F: Fn(&Address) -> AccountDetails, + G: Fn(&SignedTransaction) -> U256, + { if let Threshold::BanAfter(threshold) = self.ban_threshold { // NOTE In all checks use direct query to avoid increasing ban timeout. @@ -111,7 +115,7 @@ impl BanningTransactionQueue { } } } - self.queue.add(transaction, account_details, TransactionOrigin::External) + self.queue.add(transaction, TransactionOrigin::External, account_details, gas_estimator) } /// Ban transaction with given hash. @@ -228,6 +232,10 @@ mod tests { } } + fn gas_required(_tx: &SignedTransaction) -> U256 { + 0.into() + } + fn transaction(action: Action) -> SignedTransaction { let keypair = Random.generate().unwrap(); Transaction { @@ -255,7 +263,7 @@ mod tests { let mut txq = queue(); // when - txq.queue().add(tx, &default_account_details, TransactionOrigin::External).unwrap(); + txq.queue().add(tx, TransactionOrigin::External, &default_account_details, &gas_required).unwrap(); // then // should also deref to queue @@ -271,12 +279,12 @@ mod tests { let banlist1 = txq.ban_sender(tx.sender().unwrap()); assert!(!banlist1, "Threshold not reached yet."); // Insert once - let import1 = txq.add_with_banlist(tx.clone(), &default_account_details).unwrap(); + let import1 = txq.add_with_banlist(tx.clone(), &default_account_details, &gas_required).unwrap(); assert_eq!(import1, TransactionImportResult::Current); // when let banlist2 = txq.ban_sender(tx.sender().unwrap()); - let import2 = txq.add_with_banlist(tx.clone(), &default_account_details); + let import2 = txq.add_with_banlist(tx.clone(), &default_account_details, &gas_required); // then assert!(banlist2, "Threshold should be reached - banned."); @@ -295,12 +303,12 @@ mod tests { let banlist1 = txq.ban_recipient(recipient); assert!(!banlist1, "Threshold not reached yet."); // Insert once - let import1 = txq.add_with_banlist(tx.clone(), &default_account_details).unwrap(); + let import1 = txq.add_with_banlist(tx.clone(), &default_account_details, &gas_required).unwrap(); assert_eq!(import1, TransactionImportResult::Current); // when let banlist2 = txq.ban_recipient(recipient); - let import2 = txq.add_with_banlist(tx.clone(), &default_account_details); + let import2 = txq.add_with_banlist(tx.clone(), &default_account_details, &gas_required); // then assert!(banlist2, "Threshold should be reached - banned."); @@ -317,12 +325,12 @@ mod tests { let banlist1 = txq.ban_codehash(codehash); assert!(!banlist1, "Threshold not reached yet."); // Insert once - let import1 = txq.add_with_banlist(tx.clone(), &default_account_details).unwrap(); + let import1 = txq.add_with_banlist(tx.clone(), &default_account_details, &gas_required).unwrap(); assert_eq!(import1, TransactionImportResult::Current); // when let banlist2 = txq.ban_codehash(codehash); - let import2 = txq.add_with_banlist(tx.clone(), &default_account_details); + let import2 = txq.add_with_banlist(tx.clone(), &default_account_details, &gas_required); // then assert!(banlist2, "Threshold should be reached - banned."); diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index 47464b36f..d36869a74 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -562,13 +562,15 @@ impl Miner { balance: chain.latest_balance(a), }; + let schedule = chain.latest_schedule(); + let gas_required = |tx: &SignedTransaction| tx.gas_required(&schedule).into(); transactions.into_iter() .map(|tx| match origin { TransactionOrigin::Local | TransactionOrigin::RetractedBlock => { - transaction_queue.add(tx, &fetch_account, origin) + transaction_queue.add(tx, origin, &fetch_account, &gas_required) }, TransactionOrigin::External => { - transaction_queue.add_with_banlist(tx, &fetch_account) + transaction_queue.add_with_banlist(tx, &fetch_account, &gas_required) } }) .collect() diff --git a/ethcore/src/miner/transaction_queue.rs b/ethcore/src/miner/transaction_queue.rs index 40150b78d..f8baf8989 100644 --- a/ethcore/src/miner/transaction_queue.rs +++ b/ethcore/src/miner/transaction_queue.rs @@ -48,10 +48,11 @@ //! nonce: U256::from(10), //! balance: U256::from(1_000_000), //! }; +//! let gas_estimator = |_tx: &SignedTransaction| 2.into(); //! //! let mut txq = TransactionQueue::default(); -//! txq.add(st2.clone(), &default_account_details, TransactionOrigin::External).unwrap(); -//! txq.add(st1.clone(), &default_account_details, TransactionOrigin::External).unwrap(); +//! txq.add(st2.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); +//! txq.add(st1.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); //! //! // Check status //! assert_eq!(txq.status().pending, 2); @@ -593,9 +594,20 @@ impl TransactionQueue { } } - /// Add signed transaction to queue to be verified and imported - pub fn add(&mut self, tx: SignedTransaction, fetch_account: &T, origin: TransactionOrigin) -> Result - where T: Fn(&Address) -> AccountDetails { + /// Add signed transaction to queue to be verified and imported. + /// + /// NOTE fetch_account and gas_estimator should be cheap to compute + /// otherwise it might open up an attack vector. + pub fn add( + &mut self, + tx: SignedTransaction, + origin: TransactionOrigin, + fetch_account: &F, + gas_estimator: &G, + ) -> Result where + F: Fn(&Address) -> AccountDetails, + G: Fn(&SignedTransaction) -> U256, + { if tx.gas_price < self.minimal_gas_price && origin != TransactionOrigin::Local { trace!(target: "txqueue", @@ -626,8 +638,6 @@ impl TransactionQueue { })); } - try!(tx.check_low_s()); - if tx.gas > self.gas_limit || tx.gas > self.tx_gas_limit { trace!(target: "txqueue", "Dropping transaction above gas limit: {:?} ({} > min({}, {}))", @@ -643,6 +653,24 @@ impl TransactionQueue { })); } + let minimal_gas = gas_estimator(&tx); + if tx.gas < minimal_gas { + trace!(target: "txqueue", + "Dropping transaction with insufficient gas: {:?} ({} > {})", + tx.hash(), + tx.gas, + minimal_gas, + ); + + return Err(Error::Transaction(TransactionError::InsufficientGas { + minimal: minimal_gas, + got: tx.gas, + })); + } + + // Verify signature + try!(tx.check_low_s()); + let vtx = try!(VerifiedTransaction::new(tx, origin)); let client_account = fetch_account(&vtx.sender()); @@ -905,16 +933,6 @@ impl TransactionQueue { let nonce = tx.nonce(); let hash = tx.hash(); - { - // Rough size sanity check - let gas = &tx.transaction.gas; - if U256::from(tx.transaction.data.len()) > *gas { - // Droping transaction - trace!(target: "txqueue", "Dropping oversized transaction: {:?} (gas: {} < size {})", hash, gas, tx.transaction.data.len()); - return Err(TransactionError::LimitReached); - } - } - // The transaction might be old, let's check that. // This has to be the first test, otherwise calculating // nonce height would result in overflow. @@ -1104,6 +1122,10 @@ mod test { } } + fn gas_estimator(_tx: &SignedTransaction) -> U256 { + U256::zero() + } + fn new_tx_pair(nonce: U256, gas_price: U256, nonce_increment: U256, gas_price_increment: U256) -> (SignedTransaction, SignedTransaction) { let tx1 = new_unsigned_tx(nonce, default_gas_val(), gas_price); let tx2 = new_unsigned_tx(nonce + nonce_increment, default_gas_val(), gas_price + gas_price_increment); @@ -1155,14 +1177,14 @@ mod test { let (tx1, tx2) = new_tx_pair(123.into(), 1.into(), 1.into(), 0.into()); let sender = tx1.sender().unwrap(); let nonce = tx1.nonce; - txq.add(tx1.clone(), &default_account_details, TransactionOrigin::External).unwrap(); - txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap(); + txq.add(tx1.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); assert_eq!(txq.status().pending, 2); assert_eq!(txq.last_nonce(&sender), Some(nonce + 1.into())); // when let tx = new_tx(123.into(), 1.into()); - let res = txq.add(tx.clone(), &default_account_details, TransactionOrigin::External); + let res = txq.add(tx.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator); // then // No longer the case as we don't even consider a transaction that isn't above a full @@ -1318,12 +1340,12 @@ mod test { !U256::zero() }; // First insert one transaction to future - let res = txq.add(tx, &prev_nonce, TransactionOrigin::External); + let res = txq.add(tx, TransactionOrigin::External, &prev_nonce, &gas_estimator); assert_eq!(res.unwrap(), TransactionImportResult::Future); assert_eq!(txq.status().future, 1); // now import second transaction to current - let res = txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External); + let res = txq.add(tx2.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator); // and then there should be only one transaction in current (the one with higher gas_price) assert_eq!(res.unwrap(), TransactionImportResult::Current); @@ -1343,12 +1365,12 @@ mod test { !U256::zero() }; // First insert one transaction to future - let res = txq.add(tx.clone(), &prev_nonce, TransactionOrigin::External); + let res = txq.add(tx.clone(), TransactionOrigin::External, &prev_nonce, &gas_estimator); assert_eq!(res.unwrap(), TransactionImportResult::Future); assert_eq!(txq.status().future, 1); // now import second transaction to current - let res = txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External); + let res = txq.add(tx2.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator); // then assert_eq!(res.unwrap(), TransactionImportResult::Current); @@ -1367,7 +1389,7 @@ mod test { let tx = new_tx_default(); // when - let res = txq.add(tx, &default_account_details, TransactionOrigin::External); + let res = txq.add(tx, TransactionOrigin::External, &default_account_details, &gas_estimator); // then assert_eq!(res.unwrap(), TransactionImportResult::Current); @@ -1386,10 +1408,10 @@ mod test { txq.set_minimal_gas_price(15.into()); // when - let res1 = txq.add(tx1, &default_account_details, TransactionOrigin::External); - let res2 = txq.add(tx2, &default_account_details, TransactionOrigin::External); - let res3 = txq.add(tx3, &default_account_details, TransactionOrigin::External); - let res4 = txq.add(tx4, &default_account_details, TransactionOrigin::External); + let res1 = txq.add(tx1, TransactionOrigin::External, &default_account_details, &gas_estimator); + let res2 = txq.add(tx2, TransactionOrigin::External, &default_account_details, &gas_estimator); + let res3 = txq.add(tx3, TransactionOrigin::External, &default_account_details, &gas_estimator); + let res4 = txq.add(tx4, TransactionOrigin::External, &default_account_details, &gas_estimator); // then assert_eq!(res1.unwrap(), TransactionImportResult::Current); @@ -1420,10 +1442,10 @@ mod test { txq.set_minimal_gas_price(15.into()); // when - let res1 = txq.add(tx1, &default_account_details, TransactionOrigin::External); - let res2 = txq.add(tx2, &default_account_details, TransactionOrigin::External); - let res3 = txq.add(tx3, &default_account_details, TransactionOrigin::External); - let res4 = txq.add(tx4, &default_account_details, TransactionOrigin::External); + let res1 = txq.add(tx1, TransactionOrigin::External, &default_account_details, &gas_estimator); + let res2 = txq.add(tx2, TransactionOrigin::External, &default_account_details, &gas_estimator); + let res3 = txq.add(tx3, TransactionOrigin::External, &default_account_details, &gas_estimator); + let res4 = txq.add(tx4, TransactionOrigin::External, &default_account_details, &gas_estimator); // then assert_eq!(res1.unwrap(), TransactionImportResult::Current); @@ -1466,7 +1488,7 @@ mod test { txq.set_gas_limit(limit); // when - let res = txq.add(tx, &default_account_details, TransactionOrigin::External); + let res = txq.add(tx, TransactionOrigin::External, &default_account_details, &gas_estimator); // then assert_eq!(unwrap_tx_err(res), TransactionError::GasLimitExceeded { @@ -1490,7 +1512,7 @@ mod test { }; // when - let res = txq.add(tx, &account, TransactionOrigin::External); + let res = txq.add(tx, TransactionOrigin::External, &account, &gas_estimator); // then assert_eq!(unwrap_tx_err(res), TransactionError::InsufficientBalance { @@ -1510,7 +1532,7 @@ mod test { txq.set_minimal_gas_price(tx.gas_price + U256::one()); // when - let res = txq.add(tx, &default_account_details, TransactionOrigin::External); + let res = txq.add(tx, TransactionOrigin::External, &default_account_details, &gas_estimator); // then assert_eq!(unwrap_tx_err(res), TransactionError::InsufficientGasPrice { @@ -1530,7 +1552,7 @@ mod test { txq.set_minimal_gas_price(tx.gas_price + U256::one()); // when - let res = txq.add(tx, &default_account_details, TransactionOrigin::Local); + let res = txq.add(tx, TransactionOrigin::Local, &default_account_details, &gas_estimator); // then assert_eq!(res.unwrap(), TransactionImportResult::Current); @@ -1560,7 +1582,7 @@ mod test { rlp::decode(s.as_raw()) }; // when - let res = txq.add(stx, &default_account_details, TransactionOrigin::External); + let res = txq.add(stx, TransactionOrigin::External, &default_account_details, &gas_estimator); // then assert!(res.is_err()); @@ -1574,8 +1596,8 @@ mod test { let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); // when - txq.add(tx.clone(), &default_account_details, TransactionOrigin::External).unwrap(); - txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap(); + txq.add(tx.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); // then let top = txq.top_transactions(); @@ -1594,9 +1616,9 @@ mod test { // when // first insert the one with higher gas price - txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); // then the one with lower gas price, but local - txq.add(tx.clone(), &default_account_details, TransactionOrigin::Local).unwrap(); + txq.add(tx.clone(), TransactionOrigin::Local, &default_account_details, &gas_estimator).unwrap(); // then let top = txq.top_transactions(); @@ -1615,9 +1637,9 @@ mod test { // when // first insert local one with higher gas price - txq.add(tx2.clone(), &default_account_details, TransactionOrigin::Local).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::Local, &default_account_details, &gas_estimator).unwrap(); // then the one with lower gas price, but from retracted block - txq.add(tx.clone(), &default_account_details, TransactionOrigin::RetractedBlock).unwrap(); + txq.add(tx.clone(), TransactionOrigin::RetractedBlock, &default_account_details, &gas_estimator).unwrap(); // then let top = txq.top_transactions(); @@ -1633,8 +1655,8 @@ mod test { let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); // when - txq.add(tx.clone(), &default_account_details, TransactionOrigin::External).unwrap(); - txq.add(tx2.clone(), &default_account_details, TransactionOrigin::Local).unwrap(); + txq.add(tx.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::Local, &default_account_details, &gas_estimator).unwrap(); // then let top = txq.top_transactions(); @@ -1653,10 +1675,10 @@ mod test { let (tx1, tx2) = new_tx_pair_with_gas_price_increment(3.into()); // insert everything - txq.add(txa.clone(), &prev_nonce, TransactionOrigin::External).unwrap(); - txq.add(txb.clone(), &prev_nonce, TransactionOrigin::External).unwrap(); - txq.add(tx1.clone(), &prev_nonce, TransactionOrigin::External).unwrap(); - txq.add(tx2.clone(), &prev_nonce, TransactionOrigin::External).unwrap(); + txq.add(txa.clone(), TransactionOrigin::External, &prev_nonce, &gas_estimator).unwrap(); + txq.add(txb.clone(), TransactionOrigin::External, &prev_nonce, &gas_estimator).unwrap(); + txq.add(tx1.clone(), TransactionOrigin::External, &prev_nonce, &gas_estimator).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, &prev_nonce, &gas_estimator).unwrap(); assert_eq!(txq.status().future, 4); @@ -1682,10 +1704,10 @@ mod test { let (tx1, tx2) = new_tx_pair_with_gas_price_increment(3.into()); // insert everything - txq.add(txa.clone(), &default_account_details, TransactionOrigin::External).unwrap(); - txq.add(txb.clone(), &default_account_details, TransactionOrigin::External).unwrap(); - txq.add(tx1.clone(), &default_account_details, TransactionOrigin::External).unwrap(); - txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap(); + txq.add(txa.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); + txq.add(txb.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx1.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); let top = txq.top_transactions(); assert_eq!(top[0], tx1); @@ -1714,8 +1736,8 @@ mod test { let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); // when - txq.add(tx.clone(), &default_account_details, TransactionOrigin::External).unwrap(); - txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap(); + txq.add(tx.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); // then let top = txq.pending_hashes(); @@ -1732,8 +1754,8 @@ mod test { let (tx, tx2) = new_tx_pair_default(2.into(), 0.into()); // when - let res1 = txq.add(tx.clone(), &default_account_details, TransactionOrigin::External).unwrap(); - let res2 = txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap(); + let res1 = txq.add(tx.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); + let res2 = txq.add(tx2.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); // then assert_eq!(res1, TransactionImportResult::Current); @@ -1756,8 +1778,8 @@ mod test { let mut txq = TransactionQueue::default(); let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); - txq.add(tx.clone(), &prev_nonce, TransactionOrigin::External).unwrap(); - txq.add(tx2.clone(), &prev_nonce, TransactionOrigin::External).unwrap(); + txq.add(tx.clone(), TransactionOrigin::External, &prev_nonce, &gas_estimator).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, &prev_nonce, &gas_estimator).unwrap(); assert_eq!(txq.status().future, 2); // when @@ -1779,13 +1801,13 @@ mod test { let tx1 = new_unsigned_tx(124.into(), default_gas_val(), 1.into()).sign(secret); let tx2 = new_unsigned_tx(125.into(), default_gas_val(), 1.into()).sign(secret); - txq.add(tx, &default_account_details, TransactionOrigin::External).unwrap(); + txq.add(tx, TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); assert_eq!(txq.status().pending, 1); - txq.add(tx2, &default_account_details, TransactionOrigin::External).unwrap(); + txq.add(tx2, TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); assert_eq!(txq.status().future, 1); // when - txq.add(tx1, &default_account_details, TransactionOrigin::External).unwrap(); + txq.add(tx1, TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); // then let stats = txq.status(); @@ -1801,8 +1823,8 @@ mod test { // given let mut txq2 = TransactionQueue::default(); let (tx, tx2) = new_tx_pair_default(3.into(), 0.into()); - txq2.add(tx.clone(), &default_account_details, TransactionOrigin::External).unwrap(); - txq2.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap(); + txq2.add(tx.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); + txq2.add(tx2.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); assert_eq!(txq2.status().pending, 1); assert_eq!(txq2.status().future, 1); @@ -1823,10 +1845,10 @@ mod test { let mut txq = TransactionQueue::default(); let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); let tx3 = new_tx_default(); - txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); assert_eq!(txq.status().future, 1); - txq.add(tx3.clone(), &default_account_details, TransactionOrigin::External).unwrap(); - txq.add(tx.clone(), &default_account_details, TransactionOrigin::External).unwrap(); + txq.add(tx3.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); assert_eq!(txq.status().pending, 3); // when @@ -1845,8 +1867,8 @@ mod test { let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); // add - txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap(); - txq.add(tx.clone(), &default_account_details, TransactionOrigin::External).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); let stats = txq.status(); assert_eq!(stats.pending, 2); @@ -1865,11 +1887,11 @@ mod test { let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); let sender = tx.sender().unwrap(); let nonce = tx.nonce; - txq.add(tx.clone(), &default_account_details, TransactionOrigin::External).unwrap(); + txq.add(tx.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); assert_eq!(txq.status().pending, 1); // when - let res = txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External); + let res = txq.add(tx2.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator); // then let t = txq.top_transactions(); @@ -1886,14 +1908,14 @@ mod test { txq.current.set_limit(10); let (tx1, tx2) = new_tx_pair_default(4.into(), 1.into()); let (tx3, tx4) = new_tx_pair_default(4.into(), 2.into()); - txq.add(tx1.clone(), &default_account_details, TransactionOrigin::External).unwrap(); - txq.add(tx3.clone(), &default_account_details, TransactionOrigin::External).unwrap(); + txq.add(tx1.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx3.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); assert_eq!(txq.status().pending, 2); // when - txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); assert_eq!(txq.status().future, 1); - txq.add(tx4.clone(), &default_account_details, TransactionOrigin::External).unwrap(); + txq.add(tx4.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); // then assert_eq!(txq.status().future, 1); @@ -1904,11 +1926,11 @@ mod test { let mut txq = TransactionQueue::with_limits(PrioritizationStrategy::GasPriceOnly, 100, default_gas_val() * U256::from(2), !U256::zero()); let (tx1, tx2) = new_tx_pair_default(U256::from(1), U256::from(1)); let (tx3, tx4) = new_tx_pair_default(U256::from(1), U256::from(2)); - txq.add(tx1.clone(), &default_account_details, TransactionOrigin::External).unwrap(); - txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap(); - txq.add(tx3.clone(), &default_account_details, TransactionOrigin::External).unwrap(); + txq.add(tx1.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx3.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); // limited by gas - txq.add(tx4.clone(), &default_account_details, TransactionOrigin::External).unwrap_err(); + txq.add(tx4.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap_err(); assert_eq!(txq.status().pending, 2); } @@ -1918,13 +1940,13 @@ mod test { let (tx1, tx2) = new_tx_pair_default(U256::from(1), U256::from(1)); let (tx3, tx4) = new_tx_pair_default(U256::from(1), U256::from(2)); let (tx5, tx6) = new_tx_pair_default(U256::from(1), U256::from(2)); - txq.add(tx1.clone(), &default_account_details, TransactionOrigin::Local).unwrap(); - txq.add(tx2.clone(), &default_account_details, TransactionOrigin::Local).unwrap(); - txq.add(tx5.clone(), &default_account_details, TransactionOrigin::External).unwrap(); + txq.add(tx1.clone(), TransactionOrigin::Local, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::Local, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx5.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); // Not accepted because of limit - txq.add(tx6.clone(), &default_account_details, TransactionOrigin::External).unwrap_err(); - txq.add(tx3.clone(), &default_account_details, TransactionOrigin::Local).unwrap(); - txq.add(tx4.clone(), &default_account_details, TransactionOrigin::Local).unwrap(); + txq.add(tx6.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap_err(); + txq.add(tx3.clone(), TransactionOrigin::Local, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx4.clone(), TransactionOrigin::Local, &default_account_details, &gas_estimator).unwrap(); assert_eq!(txq.status().pending, 4); } @@ -1936,7 +1958,7 @@ mod test { let fetch_last_nonce = |_a: &Address| AccountDetails { nonce: last_nonce, balance: !U256::zero() }; // when - let res = txq.add(tx, &fetch_last_nonce, TransactionOrigin::External); + let res = txq.add(tx, TransactionOrigin::External, &fetch_last_nonce, &gas_estimator); // then assert_eq!(unwrap_tx_err(res), TransactionError::Old); @@ -1952,12 +1974,12 @@ mod test { balance: !U256::zero() }; let mut txq = TransactionQueue::default(); let (_tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); - txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); assert_eq!(txq.status().future, 1); assert_eq!(txq.status().pending, 0); // when - let res = txq.add(tx2.clone(), &nonce, TransactionOrigin::External); + let res = txq.add(tx2.clone(), TransactionOrigin::External, &nonce, &gas_estimator); // then assert_eq!(unwrap_tx_err(res), TransactionError::AlreadyImported); @@ -1971,15 +1993,15 @@ mod test { // given let mut txq = TransactionQueue::default(); let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); - txq.add(tx1.clone(), &default_account_details, TransactionOrigin::External).unwrap(); - txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap(); + txq.add(tx1.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); assert_eq!(txq.status().pending, 2); // when txq.remove_invalid(&tx1.hash(), &default_account_details); assert_eq!(txq.status().pending, 0); assert_eq!(txq.status().future, 1); - txq.add(tx1.clone(), &default_account_details, TransactionOrigin::External).unwrap(); + txq.add(tx1.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); // then let stats = txq.status(); @@ -1993,10 +2015,10 @@ mod test { let mut txq = TransactionQueue::default(); let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); let tx3 = new_tx_default(); - txq.add(tx2.clone(), &default_account_details, TransactionOrigin::External).unwrap(); + txq.add(tx2.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); assert_eq!(txq.status().future, 1); - txq.add(tx3.clone(), &default_account_details, TransactionOrigin::External).unwrap(); - txq.add(tx.clone(), &default_account_details, TransactionOrigin::External).unwrap(); + txq.add(tx3.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx.clone(), TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); assert_eq!(txq.status().pending, 3); // when @@ -2023,8 +2045,8 @@ mod test { }; // when - txq.add(tx, &default_account_details, TransactionOrigin::External).unwrap(); - txq.add(tx2, &default_account_details, TransactionOrigin::External).unwrap(); + txq.add(tx, TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2, TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); // then let stats = txq.status(); @@ -2051,10 +2073,10 @@ mod test { }; // when - txq.add(tx1, &default_account_details, TransactionOrigin::External).unwrap(); - txq.add(tx2, &default_account_details, TransactionOrigin::External).unwrap(); + txq.add(tx1, TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2, TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); assert_eq!(txq.status().future, 1); - txq.add(tx0, &default_account_details, TransactionOrigin::External).unwrap(); + txq.add(tx0, TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); // then let stats = txq.status(); @@ -2072,8 +2094,8 @@ mod test { !U256::zero() }; let mut txq = TransactionQueue::default(); let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); - txq.add(tx1.clone(), &previous_nonce, TransactionOrigin::External).unwrap(); - txq.add(tx2, &previous_nonce, TransactionOrigin::External).unwrap(); + txq.add(tx1.clone(), TransactionOrigin::External, &previous_nonce, &gas_estimator).unwrap(); + txq.add(tx2, TransactionOrigin::External, &previous_nonce, &gas_estimator).unwrap(); assert_eq!(txq.status().future, 2); // when @@ -2104,7 +2126,7 @@ mod test { let details = |_a: &Address| AccountDetails { nonce: nonce, balance: !U256::zero() }; // when - txq.add(tx, &details, TransactionOrigin::External).unwrap(); + txq.add(tx, TransactionOrigin::External, &details, &gas_estimator).unwrap(); // then assert_eq!(txq.last_nonce(&from), Some(nonce)); @@ -2119,7 +2141,7 @@ mod test { let details1 = |_a: &Address| AccountDetails { nonce: nonce1, balance: !U256::zero() }; // Insert first transaction - txq.add(tx1, &details1, TransactionOrigin::External).unwrap(); + txq.add(tx1, TransactionOrigin::External, &details1, &gas_estimator).unwrap(); // when txq.remove_all(tx2.sender().unwrap(), nonce2 + U256::one()); @@ -2139,9 +2161,9 @@ mod test { // when // Insert first transaction - assert_eq!(txq.add(tx1, &details1, TransactionOrigin::External).unwrap(), TransactionImportResult::Current); + assert_eq!(txq.add(tx1, TransactionOrigin::External, &details1, &gas_estimator).unwrap(), TransactionImportResult::Current); // Second should go to future - assert_eq!(txq.add(tx2, &details1, TransactionOrigin::External).unwrap(), TransactionImportResult::Future); + assert_eq!(txq.add(tx2, TransactionOrigin::External, &details1, &gas_estimator).unwrap(), TransactionImportResult::Future); // Now block is imported txq.remove_all(sender, nonce2 - U256::from(1)); // tx2 should be not be promoted to current @@ -2160,9 +2182,9 @@ mod test { assert_eq!(txq.has_local_pending_transactions(), false); // when - assert_eq!(txq.add(tx1, &default_account_details, TransactionOrigin::External).unwrap(), TransactionImportResult::Current); + assert_eq!(txq.add(tx1, TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(), TransactionImportResult::Current); assert_eq!(txq.has_local_pending_transactions(), false); - assert_eq!(txq.add(tx2, &default_account_details, TransactionOrigin::Local).unwrap(), TransactionImportResult::Current); + assert_eq!(txq.add(tx2, TransactionOrigin::Local, &default_account_details, &gas_estimator).unwrap(), TransactionImportResult::Current); // then assert_eq!(txq.has_local_pending_transactions(), true); @@ -2177,8 +2199,8 @@ mod test { default_account_details(a).balance }; // when - assert_eq!(txq.add(tx2, &prev_nonce, TransactionOrigin::External).unwrap(), TransactionImportResult::Future); - assert_eq!(txq.add(tx1.clone(), &prev_nonce, TransactionOrigin::External).unwrap(), TransactionImportResult::Future); + assert_eq!(txq.add(tx2, TransactionOrigin::External, &prev_nonce, &gas_estimator).unwrap(), TransactionImportResult::Future); + assert_eq!(txq.add(tx1.clone(), TransactionOrigin::External, &prev_nonce, &gas_estimator).unwrap(), TransactionImportResult::Future); // then assert_eq!(txq.future.by_priority.len(), 1); @@ -2203,14 +2225,14 @@ mod test { (tx.sign(secret), tx2.sign(secret), tx2_2.sign(secret), tx3.sign(secret)) }; let sender = tx1.sender().unwrap(); - txq.add(tx1, &default_account_details, TransactionOrigin::Local).unwrap(); - txq.add(tx2, &default_account_details, TransactionOrigin::Local).unwrap(); - txq.add(tx3, &default_account_details, TransactionOrigin::Local).unwrap(); + txq.add(tx1, TransactionOrigin::Local, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2, TransactionOrigin::Local, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx3, TransactionOrigin::Local, &default_account_details, &gas_estimator).unwrap(); assert_eq!(txq.future.by_priority.len(), 0); assert_eq!(txq.current.by_priority.len(), 3); // when - let res = txq.add(tx2_2, &default_account_details, TransactionOrigin::Local); + let res = txq.add(tx2_2, TransactionOrigin::Local, &default_account_details, &gas_estimator); // then assert_eq!(txq.last_nonce(&sender).unwrap(), 125.into()); @@ -2218,4 +2240,24 @@ mod test { assert_eq!(txq.current.by_priority.len(), 3); } + #[test] + fn should_reject_transactions_below_bas_gas() { + // given + let mut txq = TransactionQueue::default(); + let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); + let high_gas = |_: &SignedTransaction| 100_001.into(); + + // when + let res1 = txq.add(tx1, TransactionOrigin::Local, &default_account_details, &gas_estimator); + let res2 = txq.add(tx2, TransactionOrigin::Local, &default_account_details, &high_gas); + + // then + assert_eq!(res1.unwrap(), TransactionImportResult::Current); + assert_eq!(unwrap_tx_err(res2), TransactionError::InsufficientGas { + minimal: 100_001.into(), + got: 100_000.into(), + }); + + } + } diff --git a/rpc/src/v1/helpers/errors.rs b/rpc/src/v1/helpers/errors.rs index 3f5dda2d3..475063832 100644 --- a/rpc/src/v1/helpers/errors.rs +++ b/rpc/src/v1/helpers/errors.rs @@ -221,6 +221,9 @@ pub fn from_transaction_error(error: EthcoreError) -> Error { LimitReached => { "There are too many transactions in the queue. Your transaction was dropped due to limit. Try increasing the fee.".into() }, + InsufficientGas { minimal, got } => { + format!("Transaction gas is too low. There is not enough gas to cover minimal cost of the transaction (minimal: {}, got: {}). Try increasing supplied gas.", minimal, got) + }, InsufficientGasPrice { minimal, got } => { format!("Transaction gas price is too low. It does not satisfy your node's minimal gas price (minimal: {}, got: {}). Try increasing the gas price.", minimal, got) }, From 6abd08f5b2843779a56ae29d98d4b7a4832ecf20 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Fri, 28 Oct 2016 16:46:11 +0200 Subject: [PATCH 058/192] Bring in styling queues from original Gavcoin (#2936) * Bring in styling queues from original * NewTranch background colour update --- js/assets/images/dapps/gavcoin-bg.jpg | Bin 0 -> 79447 bytes js/src/dapps/gavcoin/Accounts/accounts.css | 2 +- .../Actions/ActionBuyIn/actionBuyIn.js | 3 +++ .../Actions/ActionRefund/actionRefund.js | 3 +++ .../Actions/ActionTransfer/actionTransfer.js | 3 +++ js/src/dapps/gavcoin/Actions/actions.css | 19 ++++++++++++---- .../dapps/gavcoin/Application/application.css | 21 ++++++++++++++++++ .../dapps/gavcoin/Application/application.js | 9 +++++++- js/src/dapps/gavcoin/Events/events.css | 2 +- js/src/dapps/gavcoin/Status/status.css | 10 +++++---- 10 files changed, 61 insertions(+), 11 deletions(-) create mode 100644 js/assets/images/dapps/gavcoin-bg.jpg create mode 100644 js/src/dapps/gavcoin/Application/application.css diff --git a/js/assets/images/dapps/gavcoin-bg.jpg b/js/assets/images/dapps/gavcoin-bg.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c344f5979f655729fa9418be631ac9d9d32c36d1 GIT binary patch literal 79447 zcmeFZX;@R&)&P7G5@sx5K)@+X3K)hY1V|7Y!aRpCND!X00y0km zG6fK26cBA4KoJ505k#rADj;CB73g5^z5R9&kq-C1?|uJ#&+~mZJZGPE)?RC!wWqb$ zUV9&Yd+^&6NZ!%b!4`tSV30ldgMRz=BXyhOVN3{eaL|J!AqbL&K7zp^aR8-)OCG@T z;GO|4MVJ@_19#D-21Bmh%K*H69j*be&N>Yoz*xX53+~O}!T}fs?%m+p3V$a@L=*No zVolC4fbWPwkW~ODlJhP_+qik4u|`Hl)*?mMsNUc099*bqoPn7E)(FBGVU0;frX(W* z8cQG79Ng@RIU%m;zvp(;v#PcOAY1q(j*f5CYtca5$`3_B&l20Xb?)?q0CORv+X0h-iY2}lxD zXfA-WK?14(!r>q&XA_*e35F`y^#m0ItNc(Fc;&l#6cs{j9nJtSavg32g11z^)3XG? zAFadH0G0=ds7-+i5Ul1M3_u9J4vX@{Ho@YXXhd{!;F1QHD!4?nphd8tIY6VJLDLu$ zNXEp!#6gw6{kFz&L{uvx&Ub0Y#?u<=|My9PhdIO+b&SltkR50wIH)VQsG%Vm(F{Nz zdW5K|#38ZuF(?|t@5iY!Ab&qjcL8|wIEBIA^_@Kc4_}9uMGXpm??>GG`)`|Vh^Vn3R;1tckKmPsp6{H{u-GZ9MU>Xoy z0VbvZ`|TFA8|aAy7EfgK)_-6KBaq?}l2Xz#fS^+zg2Tkb;0Q4!5&RLi$*F3+ zE~Sa5eXkVA9g|kpGX7Tk2T{@1O7_1iSknKgWE%zhP_BoNtQhFu3StV-$Iy@YwA&T= zdoAsx&1#nXY@Z5occ8E8?>tc1>Eg-M)S8F$)p5F|JmeDf@@}04T4Di)rD`^)RIQz* z`CQ_HTq^a%PpDN@viUQ@){q>E{DT3Wb3p4C7g;67w^bWvRge5lx@HyDZ@TB(pM|d7 z*LTYKq@xps9>M->T8%u8WkJ#C^7YiCq_Y_{-9FaCNH-eu71N}8-?mYri@ zna2wSiK~cz#?(BaN~jvkW`4eRC#&YBiu)9?sZnZZ*FW@n{C^U=$7OYCrE|t_UwYcq z`^JuSMej+_{S-SbJ2>AcG(ooEwlA;l=@#N^55x_gFsA;tvgMvaBcH~xHX127C+*r; za9~wvH%b?o7T}mIhdF0_+``6nAy1;a;qHAuZ{3_z0!@9_LFWPQjvg(mtWb{8N!{7c zd^3AJ;I90VPyKIq*$@uQ>5i=;j`EMVh~Y@xX6VV8^Z`5H+cwuP9KRDv{nZ<NH|Q8V-xewNlZ){lvzGfZFZ3iQUYlNYyd*LB8w9IsOsMhBlx)7a zhww^etJ}lv-;{(s^*&IpcgrD}-#K76J6E<_Qgntk6Y4 z_i05r-o1fO4OVbD-Er#Zf&6-9<*%Vp$KlgaaxB~00KZARzKk&N{F5CX3(}{kRI05F z$^&+XTQ7`u$PlCK!0yf@e8UJkDTQ_~HvQX)9bQ5|eUIeSLb1+IcIsZ2%G6Bb=stH( zYm&^EkaM&#PAf<~eY(zw-|E6jO>=G-JWEwt)LS^rquwd+mpWxFUr2zFJtP? zm$7HwN`E?x32jBqOa{lNE@#{qp1euWzgC%}ZahG&C2CaW|0wJ-#^ma4aq-6UEo{7t ziyuL`td3Svw_Hj0_1LXBwfO;)c`(n(JvKC{(OP=C+1y!gHD#}1XNx8hcKK=N?pJD~ zQfCfyQsboxo^JO%G1;(P(`!Y^d+g(Qe1&+tR~?a%S3YeYp?V%gQ|WNGK}&~M=kNDu z&@_fw6T>gXBc4d#-;p+YJPn&;XVLGUa@kfcLflFfb0Ea`LNq(`qk;nzlET5oy{dIn zM{GU47o_Fw16lXm5+7HL&DEwt2ZG$2f*zC}dE{x}-Qo6if z(<+Bo9^xHJ@~9m&W?JA*a&=lxxJoQ_jKW_ZU-QvudIs&OQ?S8iD z4q^IuXfvOpu zR_vzK=YJ>E&*Q|znWxo;WfN@sQjO~3RIE$LQ@$Iw|3?^Y_M~|BRIJBY_t!(r^Ep~0 z_QsD7o^?>4=~7X!Bp^5{iIX+V{(Z>7BglEGgw z1ZiJK9hfTb&XSARe(95h{4;keSjn0fRhGOMU4u`U&8n2$=g_}SsKt!>ewN^UD0-n^ zD?!JMA6yh7^UIcw9N+!TyWG1zwh~UbHEpZ>RmUOH;@XQ}zw~W$b*K$AQnE$Gz)MRC zUYR4c5fd^YiS~i*@gC%wdY!!sWXSIv>uB_NQk0R7v`;Ynv!nzA7x67Cf48A**Oa*M zNrQHR>2Mz*f^^coj4_rSSYoTowF*d;(^X2WyrDC=Yvm=43g_&0^bJEnc2>?3?k+Qr z2hPT=R7&9=ywN=sy1)9Lo~8e2Shi?@lfR0AP9lwRPfW;E+$ixWf#T2}P7~3#NCXhVbA zL+f8NRN~e8CvuHx&grPU3;Dm^wNVvs*KrK19}g^&y;GwVcrMWvxeunKav*;%yzrtU zW}D{2uSKJoaK-(QR>3XPsOmJ`1a})0^!|!%cqsX(@uQn*IyK`b61N{hKUdzJc`yfF zAyu6=Ut)VYqlDYQ>2Rl}VYJ(J)iwqof_~<$R;N9dQtjFgkz{FOHZ1%1vcMho+w^bW z?u0^PVxmZfh8&(jKyYLr!yqU!!ti)Nlp)r@$Pn89@$smDpm0VEI*<_(#c%VL04|Aw&N6-<<~Rx-ejM(-Crl3W8~!j3Ym z!$KImm;-ndGZSNsu@TY4Xf4HJ@TOSYyA-#Ed6(h=o`8KvV~i1TnXaW~7(Ct>4=^AF z7-KVn(OQaIO9^YK3E;(>f?SXSdXN%DsTrUIspwsJGe8eg(Od9lKpIE^FG$yTi6$6h ztTD(Hr9fMdzQdTV!DgbA2=oN$8V!*EU{jn3Z;B&|QasQcO9V1Ssfd?oCgLTUiD-ys zA{rubjfRL_gGJJaYto2B(Hn_tGKnHhz?#?uV}t<|7y`xwV~R1u5HX-?uviQhhr!}8 zSYr&9fB{5UQw-J&g9EfU3=WUM8Dnq+49*0DGsOVq@J8l1JjKWci#N5kGy=7Yv#}=N zD8?q1L}MIi5v(nKzu})-Y@;1r0>T*w)`BeSWNU~I*^$8v_PhKK;g|Q6Yk3=_Zrm^i z=x3rHb-*(+N}tVO#;j9r&f1{&p)q)o?ASGWQ=9>a>lm$*Z_e2ur~gax%{d$7AE>=J zVKE0BI6!x!)&tIK>X*nlD`op z_nk`$2;QxHJQ&fUiD&(Z9d>|@whBaBanbtw4MnRffLWtmdsrvl33VECwUkJAfU_IG~R+CYYKS znh*$hll6xFGtmaWWfX%O1IqA@*97d285#YK_xA)FeD4*agaz>4@fvNE>d$!_Y%UC- zELelMfJNN_!n%6DLvE1TMg|42of({vn9u`Qvk%fY<9|;~We0E=o0l1QGcz-=-ZL~a zeP0u6%zvW$3o)D5AUFaMClW(2d%wv2AZ9b&Ux@j`s^$G!=mY;B2sT7m#qwezBWMgx zFoO$v>HD7b2lNK{`zl|n*Y~~acgPLWgP~yUM+pFRwP|?&4!J?<5yapytOBA$9mF#( zim{=_pE5RB{=4Y<|Hh)r!NYCMrqIB&#tGrQo2x|g)}Q7zFf{(h8SO8my_?C_rXaLP zZd3r5x7Nul_KLHRjEfo6vyvy?&y37gXE3y4e&5{Wr1E zsQ1C3zYdUYWN(U+Zs163GxVBnz)JlUTBO?tyn~e$m}Hn?Y{mf|&5nY0u(2}15=~6> zaR$JC{3YG*1=xoH3&o8?8~%rdj0t1@ivX}FXH(HckEG2@cA(dva3Y=7kbhdl0$cmn z85=B{rwUV%g$I+2C|zgVoV~%b+1~#J&*tn6oco6$G5(R}poY)l5nu?x5^c%#GMNxH}xh0lj1!B}TCPZ6fYg0>8 z3dIU*YfCXRCRh=zY>jN!>-Kk={bz#&>|Xr!@OZy5@^1S8kdeS>2xG>Hyul4O4{PN4 zeogbC?{&k=E3S`&$d`8FvA9LuAOU|Viv#J`+S z0QLGd6fcIx_!nmAeLv!fTpq*slMx^n?Q4nVn>Aii*dLU)F*7zcC1Q{R5W+BIMKMD58-Bq4bw_+R9fGYzu*qn+u@wm(Ha0VX%WJWhE8433 z?WwE}n5I6V(%ftvoNd7AVF;47cMju3!ok_#2u=)FG;x8`ZfJ=Sa2`<#oI%7wP(Tnb z%EiOhQ*<&7OloM+?6!XVZvFP-|KpQFP!u-?oYkWON_;Tb-UM(m zfZ4}lqD1)b0gMU^7s236BY?R;K>(|W;E;DPW{u_@Ocuex5u9K^Bhoo4I3idC-vaRI zqp=JCi#Y>0^=KI57=VQU)?&v-gaPhYBC8?DY!%c^_6J;G zF$7&sfgqKKA8=Zg;4pGB1a*yWDvz?Lziis)4r2rvh$?jB_dgPBQ2uLR)B6lW@7wUY z(AErQKrA~3E$Y=lAOsW3Me{^K7_|N$JMsUSag$n`^uPe)29}w?5~ICAF9QxgXl_pM z+J+%IjPpC+aFc}pBikkkBJf?m2Ef*ue?q8F3?TVY6eRZDFof7D1Bv-nfE?IHyE#jF zL!yy)aQFPXeh*-fzn1XAveEVKlD5B&)J3eLz%z~o?CVXCm*Fm2dA7!GCzI{>qRIm2i$ zI_wB61jd2I!ajwaf~CW9VI{CC7$4RO>w#T|4Z_A?)39%03$Qn^w{S7IEF1;j3Eu-Z zgqy)F;ZAT*xIa7u9u1F&C&RPgMeu5PGrS9a9Xk$wAQ2;xDp4%aEb*npxWuf) z&yq5dyCrdwwvxV*9LXff0?9_nYm(!V^OCXjOmnw9!h zT0wfRw1u>%G)p>Bx=6Z3x?g%mdRazBMni@u<1P~>lOR(hb4lio%(pVX$STU}$y&+! z$;QfN$=1t$B|9yLaO-v_G=^=Tm?Yg#WVfWVE2X=?;&fERv?nSf` z+7iu17ol&VzXzw`?bTz|tJFu-f7RHl;iZwJaZ%%IO=(RtO{Qj^<_*p7wRUT{Xnm^H zr1el+O5033RJ&07w)RhZ_U<{j=kq<6_q^Dvve#j6{NAR$GdglQAL~TxRO<+J#dXbe z!*$Db@9Dwy2zsG$g!w>QK?Z+qDj&D(cg2OxOF@j&!5*Fg96UfaI8*jO6<2s4;(E$`tvdGW9`Qk zj&qI=$LYnLiJOnNjjug{I1zN>#;59^CVu+uNz0Q}pTRy0{OrcxH2;?Tx7h@{goZ@v z#PCExl2KA_(#olWr+SljCMP66OR-C7O5KvmO`ZDO{PT0C5vRjWkDS4uDL(VhwBWS6 z=|<@V>3`1%%D9_}%`D3NEsL2o@`cG471`q1ob1V*k8>Jw6>{TqpPh9%+m(mTOUqly z|0I8~z_{RCp>$zv;gcfgqV8g?;_TwTmxPwwFQt@TEZb3brtGKk;PUYbaz)Fzo#)ce zy{!zZd{AXq)m6Qxy5PL{`J?COYP@T1*P7Mx>(uJf>;6&CsejTyYq-TX6f5Ov6mKGkF-v1)Z?e)}^1%hj)-mxoM-x`qkEodSZObHrq% zYt(eK`yTOL@7VsaYvUh}U!Sm^7!W!Lhwi)HADi@=d@$uV_2fbDgBR26=^q{*efaC6 zgc;;a+Sl@57d+naxcVF2Z!SJDd2;nz>u-mjdOUsfEa=(NbKdj6&nC~w%@xe=p69>7 zz35x8UAVV+XmNHaYU$UPsoyDlSN2NhRmW@c>*4QxzMuVp`@?TsO3dZmd#Q zAO9Hf}ciHSZ@ zK_DcgB#;tPGT#m3P0jPi!Q>=Qk>U%mMxn_^jx+^$%F& z#pD6v9~W;ws$dtK!d@ZWS0@{3Jak0ppGli%vKT`i1@${^0~SeIXeel2&sVOV=bVmVXn4EGiL=w zjSTJBkxs>t*l)IVaooHK-)svHOakYgvvsTQp?4a@j1pVuk0rUnbbBW0;2|AICB>`q zw5Q%)bK-)g)DXK~-;y71XW^Wi(|Lv;w-|{DTbykUyBXJi^k!C3yKSvn=8j`VxtB|O zY|oq66gUd4lpU3Q@&qiBBbIiD<5%29Gu>XGMliq(I!}-L^qwt`F%P2LK2KT+TrKE_ z3-chzM6Xh;R!}UMB3_FF=a+|L+H0u@SyV^TnE-c;fY+X1wiJDLcuL>3r^}CJmDN7! z=auzKPmAwqL9F1r(ygOCmYLWP=dTB9`^#3b-Xy2nao=V7JX~%!30^QbKicCw9oWK_ zAM5jCR=PN4ItyKkC!e-p7E06cd`5nA3ZkX>Dy}K0E3#hgyHZC;R~ix@IwEc7l)2|% zYJ6N{QC2}rBczbf(S>eA51uVA>u5!m?GsEGuPjeu4(`MIYlvMV?8|C1TZ|RFF7czM zQarY0dvUw2-mOEy;vQdy+!B@}%uP$mTH4@uIx(|uTyR&Q3!_;M` z%!!jhgob(kZ4YIZIrC{PSJ>Klcgus`!@gxNQ!|Ge`U9^I@pKpGn;(rUq?^5R94s}C zCoTfnsr44l^zp;p*Eq%V-nYEYl#FRQ;!1o{1lb3jJ2BcUEHj)tA^!AK3tOpg+-r4t z>eI)$)k%q-Wcj+6HR+cAdt!OZdwq|#CBTw~r1jKNtW)=u7c~#+4Q(x$>CBlj_L;3C zU}Q@P22Y1FU60C6eyjG$f#s528SZ_xm*)9H9?EjNvf_7YKjvVTSCSud&VTMus?KKz z-aT)^(zbH1Wt8+6Cl`FxrfTS^w)I7hN zIB_*8LDr^u$Wg~9&v{2Damu;xSC;GawUe&qMeW>IhxY6%s72J27Sdk%1i34o;q&P2 zg1%S&25-i?)0=xZaX*#am-sY1njb%R(!B?&_O2n29OZew|5iC$c0RFPU+C7W6ZRO2HxK5V_%LzU_JLj{-aP z%=C~esYv)b{Wi_pk%gzXNw)?mov#aH6gx6I72@sm?ITUAzs38W@y9dh<{{L1eqZm< z()>lHp5H@D88W3GX_6j5;a+N#9F&u<%oP??A2c_k+jE60%r{l(W{Vzm{&-%!IdM@V z?CzNf)BB@-o#yj?v1Q)ud1k@sdA{RSZd)2dJ6Z??KhDXo%PPK#x3jR1eoPklB;;f6 zG(nn94G99Kz^O{HJV#%^o&Q;wr zlh2d`hbJ-fl?#OqN5p5pE4@jpJ2*OF!3*RydJr&!((c>+@`N5r>6oh;inXVkx_7*s zxirmp?}^>9s<&vdI_~uU2^0?TF@i~dY7Pd!R->Rv>o7pb*EHkfc z$s|5*F}L}s;Pai%^t5|;f1G!F=;55_kB@F4IT`pbJ7u~s#{CCebAO2sQ4YMU?@ID; zFG z^*bT?8c+Lq7w2#b?c%;zTxnrJ{kgcA-olp1(z=r>U5De#I`M)2J7T52Ax-)S!GOG< z8Ez_=*dJ?5mabVUp;@KQ`?W{vGq)FZMVgDBujv(xiDoeiXYca_U=7Y2YdFfQow4FR z7^wCQ9Q4hco1h$yPZ}Ut_^mP!^R-|oSbVy=N({Fqwva19>}|pC<*jYrD9MzX)4^RK z9_8h{I0Forbp+#q!&6Na)p5OUZWgp%hkIHvZEuA2&X0TgIu-W0FAiL)_$=E|7$|Tm zRi8337&p|nOfNg+TFjy|m!tjP=q$h9-%&sr*k@a5>0=`aYz}u(JGc%b;EY9xahOolx`qh6RJ#{?fv% z8s`P+NE13gR;8u8*NhWA$zXP>R&^)T?bB*Yx>Yq{qI$uW046irTH$$<9w7n6kOLVDC<}#kX2~`7SQd$vSa)& zk)snj-<96x#c?N09Os!Cr>^Py(e-xXk6~PWPjzwh=s^|5?RA{lXU(sCdmOpK)I^}u z1v>}3!aA9eyHuWz5@mSk-& zAl{ccNcFW|D9t3z-4ghWJyQF&?-iKAItjZn!UcT|k;cU>@g;>O^jO6z7rUKzWa&~? z?m@=tdUQq&{>V&6LVxiEJNmIw=OSTFoBZmsu*KV>g{^bo8<`#+Cqg9y1yNxHNYR&4 zbehw+Pnp+#t*Afu#qyNg+>Vz&zIn`^_m{IP<U_iyn=Swq(?+upXsrAm&)~VqpEGj-2bZ})Qre5h# zS8T&PZFurcy#?slK^2utrE)sI`fc}X)|Pe3hM=)>NsR{ysX<|ldDswrHydm;#%y}X zqntT<{`RM_ICdKt$9vh2<4+ONB^-_w)DG0OH96A{)^XT7d0`{NMZ(V=;#kxGk`tre zVnl7EcBZ@SR+IE>1DZIk6KRrL&8%_~Ba~tyqH_6Zb!{slz8O>Iz^!-#rq55qD{9)g zOByR*95%cx#ik$GF`EEMyEu~DbY%w`^1Rvo-1hs-Xud&EMH!*Ox4ix2@u_`i-MJYd z%z0|N(j(<^JY%cLt<>2WmM;s@T(*j5*0v7O^5*@ug@L07-W+Z{Wetm)nXC2K!%i)X zK4oXF?Nf`Tx9#Lb({6=0sTHpwl*4qad-wl3vL#0OZ|+GFLPaWoqd6^{$gHwggi{_Tl^y|7mAE7jN!-jmr=y!Lx$O zg%TnwS~$k%hW2~tmeA9L%vQ=kauAhFXPhecu*K9lMw&V`s5bA(918S)^Bp0%*1tb% z`b#T0>n>_S`sqQkt}!_oyZu7C?pC$vi#Fw3jyKv7kAwcw5=oC?_1q%T=S14Pxbww< zr-HeAEdLz@l2bZq?nm*!g5gFIE6{H!`Qfc+AvW{OaCdv|%sg{8^?XG_QDdM$m3Ut% z$UQAT*dQy$S?FS*K-Zlxjnca{UspXet{c=6tLR-w4|Q6JdYyM!tI~aI*(Fq`BsEZc zv9@Y!L0R*Up+mSkgM(2OhtIy?PQD!O8g?{WjJtg9Z~BY4OUti~mrA82dBO$x?@Gh; zFR||Qq`~g$y&7R;#WS>h>h@*LW|H$^qqG46vaq05-DSWzEcj4!ZQvbdBA!xkI48&{ zZ=mFKtN{4sHpv%eD^En9EDpXq$A{&oY9tj@!A49y@8>1E`UZJ?&BO<2-|EX!yR#bP zPr#NA*ZaPizRmk)h_1CIc3*U$FmcGG-&`fl_r5&y5T)p@T2)bhU|Ujg%hABg(l&U% zm-Qqy?2&{q+50$_HnW<^&!tP#<1J%V+O{jy&a{%4s{%pr3~kq|W7BzfHsmWcK-FoG z9Y|;BOBS{GmSa{H@X^TG^nF9kKNfbx2$!Gvy?n*~;h=?Eq(04)pmM>9hA|Eh7lakn zwtI>P=nc^YiU!4Zoywi*%5~v;Ei4=A?0G^kh`qAnvun|4ZI-z4nOsks9v7*2Yk~ng zHC@f$8Yodn=&SV+UTeNuWK;0`OjVe;_E)-k#rU0y_bb)!j`&tInTKgsgvA9udB$HT zJVr8e;hpV?_#vElAcHqj!)n(RUm2lwS$Wzk*H+mu_Z)O&YE^nD-qh7oZ6|E;E!P;z zbOLk8O%soNCrGV;4zuP>m{<`?b>%Rw#U0ATh3;0qZRXsnAdkd+I`YexZmKQ}9$USJ zst~QJ(-27Tb)m`+rek})d(7@@b@IUdQrmWHyvEN?H{RyrmgR?^M3l9*gs=uYJK4FJ zKKdRSx?$e8f{VK@oQa!Dj^tHv81jOmOUzbgcpV2rL*j!E`=+HhmlSASa7471J7MXU z+FV*(O7iGnbf=GNYc^kPWmwgP0*6Y|ok!Pgg5>W$)oGW}Jj!Z7obg*4o7%}9;4p@L zM-J03)W>%PnMlP=yAh6lLHQwZ@>b1cu9n_?efv(0X_k$WhNJ!|)!LCm6WHP-xUyB# zQsXl>qkIG`bqA~b+%|Vcfug`kI`Dc)e{ofbcG{H!ZNKW?x$5EzvyVu1naq`}*i~Y4 z&t;^(JiIyQu6uY{OrvK9l)BG%)CGJrABs(%J=_&*+Ppl_rad3B)3o5(g=;}I$69N( zq?9id)F{{J(10C53rKSGr+t~(;exxbN>2nR_-f__API|Wl)4VlH%LSbhlSEsgu>WD#e;F%FaEU zO&>f#cX_@4hi2QQGxIIoR~0YftG?lCi)-I&_X;TZHC z$eDiI!zQiA#tP+}Ch)1z4Y1EMTiU-OWL7)2^23}A2vQT+kOwPHCrg; z3#7=@Vt0?cZqhyPY)IUrpqyY%b8cyVmKkWyV#NE+`8l!Q*m~c-^i1z?!O?|cwF#ol z{d59_fszcvb@8=w2LmoRAP4lT8N5<<>u`VQOkjJ+PFJr4fhoF9egSpX9_=)N1qcOmm)_^QRY+ zlM9vQu1A%v5?`d#ozGeq^S$v_(YYdP*;;=|H5z4CS!f_wxGhDWb?WPN znzSg+iDvU*_VDWJH)oHISxebhCC}GK%wGsG^*&_AeY~o5nLc?|r*HXFlX$#n=~FCR z;7`PL;7c+&G_q&LkQ0NiTq8S#ritO^O}&aB*&-6=G_TzxFabDsLgt;&emSy!rdr0N zgI{NbZ?+(1?8r8%LnK@8IsXx9sZ`2$sQK!$1vjAiksdjrQhAW0NdX~Nc23$X|HVQ@ z&Zy~S=fSwZdx3}lfzR-B`baCL(BW?H+)De2a)Q3Sr(m+S`FSH#<6cQ~KOgQ?q?~-l zfSd0rCE3dxa$*m6yT)e=^0`I#7}_j9x~`h9k1!%S81&k#3h!HGo z&)YKnrJag_T7RQ81nJUqAHvDh)*Zu~qmSbi4>4DXK{SG2>xq>S#&FyIoROwfFhLKEHvwSs)k%bw})li|c_2}Ww`lkJ_(;>H5CE>#Yr zm;|4@EtZmHOQa-E4Q9LVX`ZldIYN`_>mgNY#^JRk^t8LzCStB?FO|wZIGn?eXPjT) zl@PxUu&$v&Fdb`Up#s^*lQ@0tY_)IBRad{2E=xjip=!_m+j65v*@C6O-X-&y4u#@a zJ}%jlqQeiBW+Do)+8tUjd;4@;ul=x13a+@>r}-2Cb62^zeb1~~n$3&{A)UzL36ph8 z+F}Y(E<+QhH9gun$F>WEp>IE(^*fZWm<_=w2pM~18nfB)dMW8uOuO$X)xIB`t~$u) z{xWE=b;75AiGRTrR6Qy+$V;OrU%pZro~z{)9Ad1;2-St#GI}*^840N#B`a&XB<4znche$QwnZNX zM^RG{ZMjfoo{oC5Af7aTRCcKBUi0h)v*l~CFN^a+zw28O_LepUYqb&RX@S1>ozf5< znR_Uh;-q0y6>6`V>6uu}(;37htDjfqH81+)2N6nIyOA@Jj^O|4h`fK#8kVk$lW67b zWLbLP+`i5m*D1N`p^&B05weapLg{RI`CHdQY;4GW)^u@j?9B4=Q~_z|R1K$hWn`7W zY@C+Tu@>ifrc-OH9js4F=_H@gZysonk{^?jAMUsnSmmfqzv*ET+V7K}d}ub5wRd&$b|%YjO5D4{Y1RJo-zRDCD7zuc5G&6*xkyI(ZY zk#_5v-`94e?+RU9uapx?YxQ$A644IinkuRkMqXWcz}nG~ETi#2s|}suv`raSE3AmP zBg6DNnnXcH}*Hwy-{{h`A6A5IKO(G_eYJ;nA>QILk0 zws?4XsczkGKi_Wh-JTGa>D?;)plyr`ELbkiQeGUY3 zU^x|8<1u**y&LuV_RMxhAwhv=?A2;1xlc!%*&Vr`pch=Zpl9yX{J3q=b6@nMZI2WJ zN^;1TAdT!Q$}LHEs+BVHs(*BGER!+7F7%EPwB|oAzUX|FK#DXM_IgTrCThU5Az!$4 zo}>mcNnPWfWWIx)b^WPADzs*BoJk9;nWHp zLD=c!&$%-#;o9;po<7p#B;g?tvv;1veAl;_LVV^^s4LszYHFeVo#Dy(y4iMLd2KJP zU=$?Y0nTgj9WAUGs5YUq zIm)a33#H@!68R{%V0_pIa%>5NLNZK8fmR=@4$5;D2Uc$6mNI?@o{Act~f%f zaP6M0oMHLf7VP|fsXV5TE*;-`lE#Q;SIJjrH_*X9c7y3vQNbG*#*g(FFrJNk8^yr- z#Y*@+Zhbkk(D$%9hb^xM_Bs#>QWzbymZWw9Dj`RESeXpjr>EmMhvReb6veire6}) zuo%-`H@`pJ`}i-BCA{&nmt%|-#?n&xqHm@0jb>}4RwYIcN0s7!<@$y-+@U&<-A zsX{Yx-R&l*+#DxWKHZosC#H=gPCs8b_p$TygU(;u`Hk^U%q{5;x$Jf;8}hlV)kub5 z8u!2(Q^;q0fF<%1l?UQ zihLzpncM*(vvt&c@cze2X=ZcFhjyKuZ;73sjGt@jh`($eb3MSuQ^8$Bk*eN=L?a3m zAw@5Qq?FVV1<7i1Y7k*$*t5B&Ne$Cc4GE|)>(FHFxcHF43DsJkh0;fAGgcFsJfZMf z+2J?wxb%?+(vIL?R9fdggrEjQzV#sc8;7zU7wcQx3x+#GuIXL57%_j@IRR45m`kBq zYb>A=YRC@OP-TK-?sIaYJ8YX`V2^0}YDc5mUI^)-h$d7`rcxl~pxpfJ2{WNG2*rih z`5x}+Y0s)ZZ~EfRRoqBtu4f7*$)>?t%|{y2LnUbY;A%YEOYVFw=R8K5_piC}_SiW) zzprFxiyg_pb+|4MxqBujKpkj|O^ya!4{hM)aA{u-HANlmE6?`^p}eJv_)T^CQHSGJKao{Z$^baK2$i7$-?wns9c+Q-rYNhB$MrH z8>~D}YowtPDwPzFJ$NSO;hW6q=Z&$Mn4HLZ`s*oz#=I$3xwbK7AT*W7Qz^_0yxuU= zQJ1Ntkxrb$~7Mn<-e>f?i3x~~-8AGNBv@&#v4;l2_9 z4WiT>I)X}~f>CAPq3nD?JY09wW2|SCbSu!b?}x&%9Zls&haNpryC+;SDlode|N+jM21H_x(sT67T-qp=t<;&Vv#fDW?&> ziL%opf_uYl?aj3>g1=vk$A4Dn$GSkVRwxc;`USoz6!gqLKF$jAAZU`ZTYKW|z>;<3 zEjE70g+-O|zA>+>5N!xHXqc@bAa(ql{rExe7rXtwpBWq{&At6)Njz5lpu5~at*D0f z>W}!IGKrdK@z?&5J6XFY{KeC-W&<4vMMmhrsW8O^9dN{SlIGcN>qdy*@_EYsUn_>g zU#twQ&>a-q&l3B7xOgB^4%Ca>6~^Lx%l6?<&OO@kDqg^`*uN@uNbtJ!g<$%sll6Vy zoT3qSq2dpdag*$MpLSAF)6Vu2Pgev7&+)SAp4CO*sbhR{YhWHq9`v61;AVOzi7{n5 zHFPqo_}EB9ckgu_%CvGNDgl+`ZjX>A(@+r7KL2c1ZTp!;hfmC@@z{&C6BYzzszZP# zu`h0EROijgXp`wwi0)^fID_c<_ETdcKaE^1i#fa#FE=!`Wqf7k>k=W*>hwr#S5Z(8Du+LbVaJ;#*T%ppv>F($Mv z4HlRHL4s&7ux^sV^$qJQuKL6jP8H`1dbE*w37B-M5~S<{llGCX0UI(2_TJ&SAs&N0 zH$ARJe){(PMmRk96VnlZzOP;@zTf$8*YXsGAZHNqq1qV|g1k>abh-|?FWK#@>u zu#R6I=$U&%oF4Tcl-%`jKL|`4%uh}{LCWjrm^0rJmxM>N`lj#Scus5F zNAyk0y_ceH$&i;s0abN$5b9|+;x6yMiVvE|JSte?FMXHJ(3R94w(oRi&K-_xpkYfT z1KXIx_sy9rnTwIUqmevsnrb0fSHwN@dzMvifoJMzHEk~h)>SR-Qog)nt7G*olqG~c z{8{nj;lu59{=5^<{M%2|*TYJiRT~}oXqyh04qBH1w?RV`KFpDl4Oi@`mvh_g_uAmB zf9r`|*Z#iBs6-b)2qtmfr+>IRV^Wct$|^85cosY1(XxD1KxbKl_k$)_7R>ssV)>^` z^oAOQVX?}P4&O7S)iF4w;f$yAL0|^vcUtU!?f1Ax6u4mIr}M~;VCrJ1TS`l^sPa$( zJV9DgnxfbmvVH%RvQ7}if9!wzd<*HdNkW=}w55FRGt&wVFUnp%KOOIFOh0-|_}zrs zgtu=_adc_f<2ax}Su?P33wd?3La$aQRHKtVRGTz6NL5yMrr+dcEi+c0f)2K$z{`n8 ztGooiRii5ni!CnkD-oa|Nzs4Sg~4Q})4t_DTR0b@9}z#WYT%A6nbnQ6|*Fs-e)$_ZJU#?pIGo7N#;V5O0y>pWE& zR}gMGR=N_WHyh2mqkE(Vp##e6@BG}I7eEGQ9>9TJN&;E7sJ);@_yn9}Fm$r8Xk~Y} zBP40Ser61nO{*4S^J$damY3g6>@+1fHi+}0OF@Z?)9FzJ+?WaJNPvDFYrJo>)&_mVeK&iDL7(fNK68ztAW*e`KK0C7#_;$-b@K=Wz zGB7mNK6egp5a^`g1S_^Q9;d z&3#~1o1+cF8$n(dT(5xP?rj5!GX^-kXanv@Bx&9qiKvlN_LZa+$Y7GS6(N?d#$-7C zoRutLUw&vFL`7556|-$BLA*2<0aZdU%iIK*y`+apU|(;kg~K;3u}+X<4W;;Wo0bDl z(OpB#>#A$1pvk^V^b!NcaEK}H>EWY+cRkRkDmE2DAW#}smTa2h73K+SRCn`z6gb;_ zN!wH2i4DP;?^U6J*;G@fUaEm=)nS%k3!7km@L~(W(+iy_1;gMKH3qFuvxW|}`{s2h zQ#B?T^QC3&20WgDc7L`YK@wIy1V&VHO+G_1!AmH~9Qh^w2VQKtHYI_km}8%!e7#5? zoabl;ekxg-2Ctz)m4o(Fn(9JDNK;)bqt-LYM@k{sJ}prS>blDs@@@6(u#}bs{Y|w; zFx*|J%uJRK@Bf>D#A4w^@7PppMme0x++mk*eSY9L1&M4D1f!ghf?yx16=>DHox zPA*MT71Af_j`15Ivb~~@CN5D*IlGZHT%GyR+TT3UQlS+;>CaaUBsglx?!DBEo1XpeCneeDv4uup% z%WEAtBqu}g7ErzHX{g+1m=GVqNFbo+&ZR2Oi8i|@fcyu{WFdy88L?l$| zL+aK#8p@|pV`=jYi@(2i;4*6^rO6PoT|0&HRRIMNyjPs!E$fB1S2csASrUpz?@ji4fu*eXT^L2X5e#E2a+8noIbsG>@#(wQPg zY&D8%lUOm@iqWFlr}kE}51nW!t+x8mrQ`YE{r-OEbBdYa4r$@yM*)S)B%Vo`K9jFhms?qz_jn68>`hqp5FfGMtL1pz$n$ z3KgaL88{gmC?EhKh;S+?my2qjKWzzw-jyIu8YoSS!m5Pn2I$fzAu~v)7{yPw;yasf z#n}eov>~M|7y_mOK@(_-pP|+{I;~?NO#ogoy}ng=0i&v9Pg-G_iEh@zN|@&npf17QCxsw_-K&RG-%g0sVz&-{xuowX*w!xmL zT6JK~%SNsX72lbLRMBPmKw=M6Eos4pUbK+fPS#!irHQEq|xY<*1B010nbx!%VZvGe|N2Oee5dF z1tmNRSY*i5LqxKzYbo8X@O{r@*-(sg8W2q3BJ$9sf+oB!3*0%&&LhH9R0x391_lBG zAG3IKtmf2w*dz?#E?`LtLbRWnH5A|xD7{l}vQ7a$2T*Vq!p?FbEDQX@OfW<+mX|!C zx@PFycR>#*xeJ%KgRwyn9}1w>bXml3v#Q*DmewDx!)gnU3P2u! zgKwXw5;pc$OPd9CN$WppeQsISu%yn5wmGvb$kNTS2lVp8!U8TEr(=4-+G3`yVU^N| z2H6s6s-6h4FC2a_u%lp@D~gmtOCAh>8G#I6VgdDHkX?Dq_`aF;kL`7LXcK<6_JCej2bixpwvL_VpIg#4*4x&% zTprUzFY@Dz!s0j^9{;D>0k zgR{?!*p5m86|2Rmy8Os>{%`%7;kD>U@d+vIg^5N1V2P_G=54`Ou02TWjqLEm7!i}T z#1Oe?bul6WjKGl&sD|$k_b=r-`}@^#5nyDVMs6KL>$qH6M-ylKq}4%}!R4S^KoOD# zoe#NNV0s63kZ!S3;SdILAzZLNb80!-Pe4SKc)ED1bH!+&J8+ufaw^|Y;~ir-I06h( z*FqNnUI!OI1N@px?z{Qb`Dkm;yHOEfh+H~>6A}>2Gm1tX3JWkQ8Y)k(s+*_{nhLhZMeLAbE2pIdKQH5{vPgiL zU~!R^1m{W!dWRQunszeA2T|1OgQ0lZA{{hxwbC8(X9DBfi>iGc5sM8##?oRO0S>Ng zrp?s{O#!8wLYOTz-_5#4;qk$ML_d&W3nbT5OofK5YN)=v>(s-us#1*8x=$l*yPNS%x5l-yhb`tJ$rnTQZFbRue$1{ zSEZ7=pAu<80LpA*LN&-*+J2uqx8E|}VS_2EEJJ!L2!^8|f-z3AbXs{8D>?g47*7lY z01R*_4e%1!UXCaf?>;H=KZCP_Oc@c<6*n7Jo|^a$Y9yz%r^{JgFdC?QcDnC<+0;SB z&j!aVwY&4j<6@>wrnUXfu6!L!HnJ-mf8?t_^D6o@XN&~N1sJgvDz1PFwd3|4jRn5| zAI;M|p#^2L?6kn4e{>tznJjX^lr~?l8#Wf^kG=ycHn^I~ZR1>#;7z{_ z3n0@%92ytamZp~ib;r1=gZaZi;^*)Gl;C7o-&S~O(bsN7MJML#))tb6!5YY+It^%c z7+&iM_$ajW=7bXv4N>t#K%2rqSiZRKzB#jyRx_S1H9)*fpHl@L}XjvK^fv77Q@kSRR! z9$RNxc`yikRVf0Zg#?_yddQzbT~AvG;titcxJh-Z2~aYjhT43Sps%nmEN-m+iCHi@ zv!nndRr;#7$E&D1Lf)j57I_HJ>izk~v115_@cGog$}`4aoCaJ{K%$215hVfY0W{YJ zL$EXK%or&d0Pj2akkt9FtGggWuU^{ z8z-pQ?=F9JMjim986nRddPV61@0kvk#_62;?va?27Ri4cU{Y=+mWHfU?;P~0vUNJN zp(~)lWi9-;;)z%c1?r#YM5F?2g04Xl3h%qgc-h&90u%TqCirSwf^L>wUC{gD>E*DMXL15CxI%@krG;c``y;E>#Be7>ondhg z>u@AYz9ua>13_s~fsr19DF8oul1tP~>dk8FRZM&0(I@Y;k$_ zG%+xcG$Pm)0>c4;Fcj(1rQz7-v-v)y_4JW_9W$+Us)m3M9-7;dWVqDT9vu>n4H~aL z4x)-uAVvGu-uA5h(;ki~Q)xvdvvgGvLcEF?O*Ld5>5N5qk{s*y_31WN!VF7_<4zz9 z!4$wX|Ig=uJaIbXr!B(7e22+$EwdcROyV-Yes(f+ ztn+3%jNc2z5w$>Q7AVzF7v<@_L*4W&?b@<4ADirOBA;9p)=O-J7jT@pK>PyiT;;Gi zgrV_TovLfgdEud4d@WecPp(+Bw2w1H=wo_80iogF(uo#~(Z`&dbk@IrK7d^sBHB0N zKrj(oA`U^mD#obPrIIx)G*9L48wty;1PA4&I!{moo1ic_2t0EDL;z>tB2B=c03LscQu6HhD=oQuAgqr`Z2oWQbcRn3104$Y(K z=3Pa%juNqzoy_v9RUv@L_gPSX;1ahvA`Rf6DbF0lL{$Yqkfz&X8l(zX9B{Bgl( zSf5i&u~E?*0ECQbHa7H%uC*uJGo+P}rZLi$7qaE+$DjFyA+?EIx{5!AhmlJQYdli# ziRBS%k@NSoo~USz`(Xh)I12eNtc4x~5y3>TYHo{`7!{q%6)jzf6v~?fsq^idCOy-J zfL#G0&3?`0p)>a~XOlz6=!F>;F49H0+XrvoyWZ6K4(l%(jI}8GTI2Kh2og%7QVGe7DyE`frMk(P&5o@D`uCfo%grb-U58VDF51Wdg*Y? zly8K5CVK|ZY7u?nVKIE4u{`THH_n#6@oSj1v-Q3lq)p`W5nvgq{01#5l^9fGHybqc zHVsT+&=Nr~)xqJ&+PXX-4W$bQL=*;vVc=9yKX)r{?rp+ zE;FEwpU%KFn|y8y{QmgFn;;E=A5ldeO+rk8;S4&u0L?|REi?oq{c2k@M0{l!*zRmZ zFbT;BLZvfM!(1d6U^5sE&7!8M1|N->44V(%S5c&E!Z##KY0C-&#wLpV#z&7!n`Efm zuIbk?6h?KsjDm!Fqkj+$VNo=vlFg`%!4lUQ7~YNo^b1UI8r z)ftH4SO}u1H;C=cY9xu7`Olz?@W~vKHf&#s6jumT7c|VfpJfc56<)lnAcI|Mf+4{O zIE0U=6?cK73%HPpyj|&aNaW}P!5v!~s4~-;KcovL^^V$4Mc%Z902+z(ANo*#mQcG3 zUg954^>;%1ql^e?s`x!xsP-_%_}LPTppUzmDL^e1CLb~fVxe&MT#|Y*2&6;B@o2cI zF%Su5q+dDF2nIXJ(E%s~l$=Jc@+Si8j0D^|uy>kn6Ye;&6?rZ6-W6{n`Kf{whO@f; zY)Rr~Gd4s>pXm-spE}&y<6n&3G$I0^E6B+X#t%98n2`wbw*%_&t5@M92j{rbsn9z_ zB2)$dso>zNDQ^orAwEPEz$YLgIQf?m1i9N9Wn<4mTkCZxEk=aQVQq4DC6JttnZ`24 z2Id!UP16s`2?$aV91xHh6~m?BS&;5zm{#R;FkG#4{Z6>WOsQEdm|e?<>l+sQu{CnW zp)GJa4Gst(0E^LCo}kt)^S@nRr1_KVjVNz?F~FV)YI`$KhA{uGZ~Odhr>Mu`Ge8_L z2M!`tVwZ3ra2TA#@;csuL@F*%9rrzHDq~hv420vLCL91q(Q@SsB}(xE8afwHBp?vI zkOUw|>hWLql09AUZv+M*o5%4X=SZ0-|kiwhDd4T!vsKJ3O)+hVK%KGwe*p7 zwYF>%noTc|y*2sv&4u`Th19V&9Eb~7ow7wk4-R3`|B3xlu2fOs>`}&{kEZP*Pqr`K zGGD%}zpG_Da%@inRSt%8A^>k1q_E^XD<{JsxgZ(<=>@1Dz@SPkTQ|pj0vD;9AqtsO z7WSy+U?BI*W+k@dIYFjXowa2TKCBX`kXTwfoH{DOx*>rH{x53wUa;}dyN|Q_eersr zlHEwKiGQ;O4ooao*UAUdC7BJP7502M8USLsfXQkFRf4!xW4(8V6lSU>Q3xDLj{;|d z;2^#&1l6pop`r!CYSh^(xcNZQq|G6$@POCacEagrXyr(S&4le_wbPX}4M;CHudout z;wR&d1fl0ca|tlO%hmwc3UF{?>I~|wv`G*VF3yI7v{YF`V2xxT@IqElqW0Rjxy%(URc3Ithy}4Dw@6Vd!=OejD7r1ySgq1;q4~k@Xqggaa9T->=E+ErR zQQ&)c4Hd8|4WWXiIXCUt;{zZFe_r*b-M>Bw0u7}f7q4-9RP}cG@k#&@qHzkC^c%Ng zg8X7j>yIEoV2wdMgi8afz=cpVe>8}c{s)QyBWx+?p%UWr;R&`X{4z0dm?T$(L_*}X z@N@{0n=!m{p*?Z6RF@UK~ZEdavVSPYgHMzfYM*iL>$ZPfqO$0VV4zkfI86-Fa zHAP`f)(LzrBP#z)2FRHR*gc{a#MTEXel@W>m!L%zvqe@r6TObtfk^WjqrtIPF;A;6 z=F}8r5CN05C+4XG=Wf=Uns zJ%oV(@+^vAB|>q|-m`cBwSYmjK#)AJE6Wy{@&`JHgSl`jTs+B<>`O|7X5OXs~1%@T-~n}v$bEb1%@O3p$CGG zCd9`sj#S_yO}#@wtjiQ=E6^Qa^dP{m+5reh!5~x%>{`GDj0a%;z+ei^t`1Hrb(80+ zfaD1Nd8Mg$Cm+o|MgNBy@B?7g&nugs$7umd2ov;WIH*!vTLlIMLRJ{gHJ8gBgaS(d z!-0T&3mn052Kp$ajjYb%3vd(w5Kz+$Ectpi;iDhm0R8us?xkzesia#nv9T>}Kw zphBJV0azFTr(r9BC4d972cXI*6){4cGf)x(1r|vaG0C6P0+tIW@ZQ(k{Brfp+PgBq zNBkcbMAJIppM>py`s>tq9f^ZfQ6dPZQK{z;998KD|Vnn-BJ-i*(1^5qd}^yppNUezHNQ( zj(&BLlZ4d%cstb4@5SEI) zm<6FE1kRKkIj ze=hA0GlF2GbP7=4rvhRV3R=~4uCBGe{rKpvYaQ!isqy44^(;H2gK?{m5|^P zf&ae~1n#2DUmZ+%SG+%_zk!B38bJhEP%zu+bk591H-4K~Ac75E31%P^*^@b!+nyl zkXu6|d5jX5x0)d-I#p9aPm2%P<;){#rnr?4*^$5UV(-I4LmAp})ido%1|iA4-W@L3 z{X^2yK>J%jUsN4_VH8E(UN}F34g_(4Qp;n<*rtKm4!2$Qt<~QwjQvpJBK?k1>`_L! zUwCS6JPtV=k&>3E*SA_bw%W&AXeckudvYXww>{$v^8MH}h7>dFQl+VWMSb^XoL9)j z^0^e98GYuKliJr1uOI`%<6j_IXwNLAm&)`$;Yu-(h|J+ zg%1rW$=<3BZLI`>`{`5GAq$Cd)>2v!M$DR~l zdMtk$@&4rgzavE~qkPU%-*1=L_#9gJb|8>*urM#~j*_81xrJ&} zd(Y2}R{skUXe)gqIlj%`Inq2|G4U^G(&u|qm$kEsRVf{VjBbH3wBF+j{Dh>CBhXh?XlXwqvHQACNV{AjWzJewE z3tA4WNXnpc1&QqUIU@G6`5kJQ5S@z8A;|{k6Q2wY}fIAi_u`KLm5x& z=P8`_Uo@X99V%o@_3(r}*EfzH9prth>6{q9q2W`o-Xqy7PAl8DW)$y+-Lw{vcAMxN zI1^&EXUs2qFuui-$4g^0X)B8@bowH;3iL1dT#@Mv-(BU=l!w_Z&}pVy>hLzh8d4hL zE-yUxIFZ3k3XZH!UU^8;50{_DJx9Nm%tIMYv2nETJ`Qnmojg? z!+Us+=JvT88G0FA^9|I0&UpY0dCOHTU$7C=bm3W({r4?>Rt4mR89b&~-|Wt|U#}0(`%SM`Y24ZLGt{Wsp)8)>BsW~W z-8q$FaKidZ*TTe|-99G>m||XZ%clT~u#ZXxX~$N7-ThcvSSQnanSbVb&l5%*xv8W3 zy6FqCV4o!~?;D*`HEeg?A0EF3T8BmsrM3+OZ}^Js-#-x2ch`QR--fh!${=TI`jpqd zpomHV<NZ4wN-3YQ0N!&pr@(1nQoo& ztOn|p*eZEC<3#ECBj4q^ZGsPPb}vU=Y;T&sFcANQ@#I|Chru{ftL4avf}oTGu|5ym zYM$1n9lASl$gHhlBNKnY-!7lJ@xAI?)7)X$()O;8 z8o~K3P992iVVO#Azu>YzM?2emuYPy*d`O4naWRb`JbyTOg$>e<81$c6`D+5uump9cx)mYxrP zxbhZyqb~jn@<_(bBSzj>?tgQoBd)ALP~Thffoi~LbFDAB`2+djtkQM;gzQn*Ysln* zl(G+AC!gMH)qj4l6Zs%R`A1h_gpR7?*>l&>?aflAd6%YRj?1#QN_MQ?^ZCnh|FBYt z%#|USMiQ?i-Y8Sg%6C!+zON)a7sOA8`EXM_1=I)9$Zrb{H-!HbNDO(5<~t0j zmpQ3lcg%a8plp}#WJw67tCs1g^;S5!kF3wojEeexAFmXU`aTS)zc`y2@rR(pmzooW zM{?P*w8~dY56rc`%yCLc>V6I*$Nod)y4{tTxXbIryd_)JxB%C9)$1ok%55b4a#AWQ z(~!UJ{`OJc$a4Q?d?Q87q5Yn}Ms68l zj?{Nb;rx{DGhI_v&Z{X;ZNfE`T6iX+OW5N`ikp~e_RWqyKAqNGUkI_ zUtdhiFIVeQJJ9QXwLTx3v!nklZ~q;u33f=T?YBN#S%+6jD_aFmO5xucE!0nOW(o=l zoA_5e3Y!M=w&nA)3=w($m!CjsY4Y+NPnmtW+V<{-9!`s6d6WlUs=HwkzLhm0J6F?D znI@rfP*Jpw0cS9)dVl&bTKXH+H-}mqY&h(^k&DxdCt@84{d%QlqyyWuk;lw0hR#T>a?A3U6V58%3c-|$ksxP^J zv8Vn8ZAe(}{BmH!ef#`{x=ZYeoR$YhAC0{R=8x^heEl_Mb)j|R`OCPp8%-Y`sO>)I zTxQcUd{Iqy^TG-Jw%e^QqCqE&6jf7J9_y46M^`~8Q(>$75m)i;!%cAXbtKf6|@aPOQ~64vYN z@qe^x1_0`$ZxUJ0o1)6x-du9bemZm>{Uh?9gk5|m^$W1(Di6+k_n)6%x_uDNxNj}p ziQVdCzgw)icKW&)|EuhJnw19%u9Eo%`l;-ntYCSP36QdX_ZDp62L-0PuvLjLIapdTBK(YX$;R|fMVZ_AB+$5S6zeE;ou z_1cz#l4G9wf-u3pq@rTC#`%E|qux*Zd6s}6CMW|>@)LIPW}HkDO}TU>7jdvvo){d3JiU-3xw4NZG%oyK2z3b#+3msPuOzQ5e!sNYZHyT#pS z+()lhUiWJVnfa&fTgJ}217p`J%S`|D>2*S+yFA*%Z(&>a{7OTUpD^F)!-hljl~!~@ z#{tgn9+$T*_FR_}SyG|TugU!jx?c8Ia`9XwB5++Q`AD*ZLA+LAWa)XC9+&l2)qwZ@ z(A!X{jIK)+E*@Ry&EE|SlsU{GPW2av|J~wn}2g*WzkLZhtYjUR5seU!pg2NK$_kA;28`_m; z6_oE})pTVr?^;2q1qbh~&r|=RxnQC;CHNQQ4QNL|mAYMN$v34u3bruu_N=Zwyvvj^ zmTvLjtJOmp_km6!34W~&m= zp0CQi-BG&Vo?qWQ>~&46W{{twR9c&_zkf}$ZM%6U>uwnS5C2Bi1%+9o^F2mxq&nQl zEr@JYtF~6p_vp(%JK1a&H$AmFYj@;EPyWS@fM=y=J*Vp~Z+HGo*fZrZmbHESbOBZH zcGe?z=BSOue{4qnxyk!|f~h?GyA~e@>0jar=;Mx&b?RaIHUqXwh}h@$#&zdzI{C=% z@%V&vv=wT4x)*oVr7P)Olm6{@&#OYixbC8cznw|)uWxEKs?(>@YjqbbX+G5^GI=() z%@Qr@iiTq=mOH$(r@KA$YoJI+Kia(ejYqN7`^|wH>2iIqT55rP7de${uJx3(&g;H8 z3xyIHZu+-TK9v^!K$8b8Pcfnw2CA=am8$M_z0p(~;UDCHQwrttcrQdM zJmA#4BPc2axtAEl%2nY${7-ZY1kg6%`%;4n-xV+al{a|!k;^;;)Q?pUNL&K+Vjb^=z)po0mXB-feBHU z0x)l2I{sh=|Ai`mL4PoVy+rB}RMZY>103r=GXnWDBbxZb`yVcN$o`#UJ+&_}@rHLC z8GX>?I-Qwb=HN(=(?kzW5#)9lA}7kPmMmpAE-H{7zRMiOh}+_7)f}Wqmz|nCmne^+ z)W+jG#UJm=oC}KS%~H+f$T*8EbPR@$B^q}RcIZJ*uXH^x5j)Xt!NfU{NZ(0a9mXM~ z3Z98wau$U2UBGL?^KRnlicXIa&KN{0G(rhkV%ksPMr3DFW^_xTIf$4s^%=e7vprOY zd0SD+5;J=T_QZ^4?YIXQRBP`)Z(LH69H*ItLZ?H6s1M?=6iEqqYO3so z94FBRZWdU1ZYId`ic>>`NSa!;Nfv8Lagx$zvB7~)Nv(_@5e%wP)_eQ*(KPTt;c|j&iRlp& zMJC|)b7bU9_|kg@W4ELqcCzio_S|rq$mLCsW){2OPPz$4&Fbr?Y`q*87es9Vy9envQ%J>d^IhQ64uc-=pfac!#6dn9{QJ zU`vAT7!_nF2c8nmjQhKMVljI?N*L+5>kN}^DeNwvTl9D+`R9umyK`^su?4AGS&jSkkv)BB z|MXXv9XfBAkQAI(wX!z7cxALq|Ju3DmmT}RuLraT0aN34S?c4^yWJ=omh#W*zty^- zXY9Vdu6Violg;_{^ozWW(j*!BKj9LlZjP1i4|l%oNN6cN;aIh1-6qOPCh$`eW7npK zzU1toc29GZ*P%PS(EA5>vBH*8?}8^Jg1Xb*L-knUK^C}sII$PFN%lSXkuHvMYr)>d zKFEG=+yqH=2t|7QN#jjR4P+=U;@6jdLDo`dM2|ewloX#+A@5KBX)~tCGFP35naa)-JFXie`Dbb*C;XA07WxWLMXixbWh0KZ;rKauliCPlW5eBL|L`~4cSwlZ zj*R-flx9jY4Rot6ZSl$99J*^I7b2bukCUdgzlwX2nI5YLcFZ09vD1x{%1A<`?wGls z5gHcj(_+AroC{0Uz$B45@|BCjmyN1Z*z>{NOnIXXuq4&6ibpK$+n7z%ZNlMSAV~ux$NGwP@ev~UvI88XQz^mY5&-UPwY{V zp3}A}t-Nw3(-*foKQ<|i>UTBPSxCagQ$kNBt@-OMWx_8=U?jiLCJJynRyCYcaJ9iR zI04gFEF@c6d01-0U%uBo3b)pGbhN*I@VZm_d^ol>D=Y1yNWVqJA>1#OT72Z4OY|=~ z0l`m={{=b7e#5QLxL{Lnrl&m~AfPVb`z68@9U5kfBqDsIQ}A<>-qk+6_tPfN%Jg5% zPZ7AT&K&d$7TdGOoPZO>s`Nckc!rUqb_XwSGZVslVhQmYc}@K0NRjo4lb3HNX-xPA ztj>Myl1#jy{k+Y0c}sZH_BqD&*G@;CkB}-(!ab~ene7^x5 zH%C_9ugp!wz<3|rr7b+WPKc3^VVIPLL(N#+?tFRu0!g6PI zx(0&Ld+CD6qBCEfLaqmJq4j}I1P1#HeR<5MD|<~+-fG) z*`~(R+t}oWSeK1hJmH!IV>kEg;kmQk1j6J`OPLSISCvU$3qo~I@3`YQM5bhmq$ zjz5BRmMQmZsIKg}b7KC7grlhsUSZB;s&_tY3;FIItMd+Q1oK5$a{ zx->%9x;}{7^0NkxJnoIPMVpwg2gI_|w4$dP_kPK!IIV7?^)F~mY<}KDK>@qETHxy@ zQJx|DT&__>a_HvFGsLG z4(Bx~bFd~PnVmZ(*x#gOuU@^DTeyHtkt6;KIyM=4sm*nfFY2)#>-=fMJ>r~B_uQBL z;*%27@~uV1+kxxT_5s$&{lrew+$nhrl%l&^aow@EShNN_I7g;pN&e`EJ-iWl)QyR6 zUB5FKQJ(hpE@&UowAI|m{_{z|322)3X*`Ztjmx}e2R?i-irT#qd)C6+Vq9eI-dtgm zzl*JhNxnlhIDw|$(KTGH4`b50fn)fe=zIG} z2n+<~bQ8&)2-%cW4FVI}pIRMp$&r_4&xVcc2sBN!tCqZU$1=Gh~I2;th2-gi`yFNhDFS zqL&WzQ`O?h9eEcp_6|?8QV$-=yClhaJU&tpWG*n{=_Ddazbs_%&Js#RJV;Ywa$?F; zskt9$Z`J;Wh4NkL#?)nVK5tn#L>B^oZQ9I-R>TI zda9B`p24m(o5qh+OS7CR)^3n&hXIb!NwL%Oj)LAjjg+X;5o5S$V zK#f&bf7mu~s`DBPRC1D%=BYF0q5NcDr^!iPT7bPcB`Wz7{+vUmhi5;oE1O!Vzbh+2 z5$8!(kPU#JwSE?-qLJ)G=GEJJRnWIXUBk)VJ^~+{BTp>^zxenxRj7K4auk6NNXOkI zVXxjv;l;?yz7CD2J~KWQ8YaI>M_Mj=Q_M&Fw3zGjHm1%_$=>TuM2M!f9k0^NYUV8E-xhuLQj zuWIg`JT7k1O<8~XQ*xoh`i5hiMsu@(pUshuTEtwD$J>+q$i29riHXV-b?QIf29gz6 zXuknmfKP~?zvPVVpJyrtS_CcP2#VH8s9!ma6%&UNa}sHXCNolxF)Y~f!V~n|789{X z9-qgHmo(+181bnkWRhYFE+^WL$9qeChF`dILALi0T(Bm1CEz{41UNjFUHKF?+z?>O zz=|K2Bu`n}2kiEaNMZR}<_NwzZY+eW_BN9QOn>|#IkLTTp@y#W6??R?iG-x<93?ms z$>VeS(rTTe8YARL8S>Vo$YFE529TKYMpU;XQ!9!2C$_{2hn$m zN2$&2&SF>-Z=*h@J2xAlYsXm0Ky@WW8 zU(v>^W0ErX)(VP*gUVkFaxfX6gvFte+Id@=Fdkt)yTyo{6u&*Ml~nQVZ=;tC8L8V%X$_HJ)uCuY5^^i6T` zSH~T!(Yn_h>0S763+3c(eP=zYb27IGAzSgmrGFz{kDP4KQ~Cmx!V4yo>3(E(izjtT zsb{sYRAQBxJMikZPD<)~Yg@H7UKdl%9#L>An}KJf;J#fKr~X7J-ihT2V!qo1GkD!c z$zIW3=aWeG5^1H^W^4j^*{Z`9JX7ne52g|ojT523(cADqnUzzI5=RzN%o$9MFde!t zDl1hCt&P%w^lFMxT%y>*;iBH@XEv6fin3BJ^47{_JotmVE7obEjEm>GQ~&&@=TYg)`T`dzh|^B z^$(ju!)0~^pbz%45eKP@yi);rjaQTVb5EMYtzkL)Y3beze%ot?fNMmb^R3+F9;T&kHj9F znTX=;X4+y6r9G#avg2nzAk-F~X7GOx2tu#7u(wBE-2ZVRtBC%*ug_yI57)pk;CSOi zF+$Y>I+pi?5E^z&#FXiq@+H*RJGsT?Vd2)lAm}FHuZMGU^UUDlvOWp4ueLMe7!0jB z^*-mkbwm|e;mfsRxAL-s^>VZ6lFgJ4xt(erO2yxF6l8@>G?OO1@z5eMdF z0+b{Q9{-eiYoe%Vof!FB;({f!)N#T;0QI~eDL6Q*C=lxxT;vzL{blcxjpTR3#dy^w zhM2ayw(zZS6l2;rbs_$?ZC_64bBQ`m&-=8w0~KsBg8b5mo{lAZy|0O`tZ@P};s4syV9_^1E=~6#ix$+i~(3GMe(?5cAUKYm!si^m#Mv!MR1!2>ZKY z(@4tYBH2?Bgf^jQItS?*xYG#zJmXyO+;j1g7?L+nn4|M~Zmt73Fb;W+&TGD%>i^BN zX{_8=q(2VrZ5|r@aP9;YP2oice|u&i6ms%)wPm}uJlGh0DHb{w5G*ZT@_PtJ_r zCx)$A!)72huB}oz0(ot=tM5X-4eGOB*Zy6}H@$RxBlbT2o%%rg8h}AjgVE+dg zt?m{}=uPu|(fx#|{(kM!a8vncE72AU6N6wtYza)+{(&}6mABeT2yIxUA?G2gFe6Ex zxA8+08M~`vcN_DQ@N5BfovPrAQLv$WmHcZxZs6TwGf7y3=(=6=B2>w+lcbQ9S4%qK zc;qVJz423ovi$~imMr&VgGh(mMFa=$(rwV@JIsTkCcgxu4Sp17*`9|#S4tV<9T$C~ z_^Ow_vKVSGJX~Dsz=o~+JM(n(lJPvi%HmHMWL7YyB&gTu-4es3fA*PeETq!&b;-<& zE?-C*@xf;GZhwq(Qm`S-O>TBk)_CjLG+t}EIE*UIQ zxwj{g!(eB~4zLEa(n!HY2lc|C2U2E*D2H(|dme1Akjxz7h1Xo*jCBKhs7Qa2!yaS= znS7U>m?bk3U!B1uG8l|O0GrBW7K-)I)on!Aij;^g`)YR+)6CWaF>j6axEI-Fn6z^y z38nIbE2)WCUb=foSJ624*`Hk?W&W-3lVixK96<7*%4*$OK8&S4U3%i;AU9nC0+M7=o z_9)ws!?;p=4a;q@>zI~v69`=*XY^MmsT{$Ofc+hU5x%( zo`Jct@iP6aYduTfFaGKPf%#SOLJzb5=QX{b#q4;!-N5XxMoG zv(TyYC{Vk|_#z*B60;cSU~F!s4_h-Bs_WT(lg8PcdzrG_Whu51DIOLmvqpdRJ8@Rn zwDA|r?c2n&zvMUfi!heN$CxI+`XUsclu|3d~&=7KUA7ksb4&mt8 zlcP*UWtkoEsz3Fy24>RPIevuOmMOa8KURw+hbyG#N-j2y^MKXIni?^C=Q^*Z^be$j z$s;$&8*TjR)RfOMSwLHj{4GM$hEUklKOi!q*GOn5P5gg+y$4hiUDPf-2>}vXLg+;Z z5SoM%x=INFq*oP*C>=osDYj4zA@trslqLu&zETuWkzNg5R1l?0v4(0uz&r2v-Fv_F z|KGhgD~rWi^UUPTnSJ)z=gfYd9UBkpEfc^^ZSX62nVGK2<~#H%Gx_nzx0VvUs#A|LL0bkeD0F-dkUxTSEPPz)bKca5a>MtIkd+Zf$L7Ojt7>l_I6 z2i?CuoD|f%B$k4G;nbo#If zUx7K(`4osJ(=57@asFdvVq)U6552g1tYVY1qAb_ddft|MM%_rvbDqcNX>q6XHCLA9 z3Od;LDB)~5&a1C_-Ok77FQXq*k>H*-_uRyVgcz^&nX@rRg=g5*AE*X!J9jxd7bL%#r_=EGPVv)%j?%ft z!f%9oaVVp-Bj{>Xxz>bdUh&J-)xlH#o_UD|$Pv>hMJ05u!bRf7=#ilbKJURE_S93V3pfIS`%O?+SB>kqIrm4yq@=q5J zzdhAd082+r8N;pS5A^VD=pK)g^YwjS{4T4GxMFo;#XEFOmxAjq%2PMy9kX$kUs8T> zSPJ5ZqCB|E%899Ifnsm!tIJ-v>iiJ8=Ryl2`t&3O*N!laU zGzJD_2yx-5y4@m<#r|4)(reYH2`L6WX?FaGk?tLMasXe*MbTP)15AWC`hHjYXUx2l)<5ec;Ds7$nxUR-Izfv%-4v)!Go%B4%c^b50 zLl(y5eBJjNRkm|{u)2%$<(;>welNASd`1g{I(@oSw8V&PFeUJK@ zFlD#cl)%n?Yq^_Z-EZn}QL*_&qI_kB9nL3&>t5dxSDR4qBC7ZMYkTp(@q$;Jw$C(< z&FQT)RwQ2L3UnwF)LHPglv?g%+cO=}{HL)Bnp*Zd$MKI* zW89^e?I-uQWAk(tgA}~z_dg`1A!g??M>AjY5{*YQ?deHq5%*C>61PX)D;2Ti(bQ_y zATKY*3vhTMduwD>+sDo<2H9a>%Pu?;i@l`Gb6m?l<4toQtej?V-i*(|KUaD&>9viE z((n{Sc8xI$J1jaSne>maGVc{s;w}!9Z2tAYNO=sxmy2{y) z&9-ZvC*6u2N(lnpgmXXPlNhS^cBX(-YM$;=OL0JtsioIV>8kQGx%bxf@@x>J_YT=t zT`wefB9=dmg2DV)St^%ps$XT9msix;7(QXL+ug7ei2U3%3FOhZG{nLI>!M%Ii4U+U zzf+#V^PS>~$V&UkQLnOtj9$9Lt(bv2=2gD51e}Xl-BGlHwH;eejF}#fh#3sm+;;7N z@1?;%a4?YfY%j2`t9vY3t?k8yWePf1EBFTvk+q$FFA|9)=g)~|YKl&QuG&d_Wt_jy z?kJ48YmJ_bEhw#x3GZ>#6t=l1xt!w#db8EZ*+N*IFWYxVamPGb_r7ZHa6W2?ElCkMQMSIvE+R2(H&)E(EAFh+b?#c9vu&wd@D3>JYxSw z;brPn)W_Qr(Cf;3%0sEU;uF`+9h{p?lggUISBcChkGJ-tRK7O8EpZaZ%!1|CRUsWJ zL;L)7V!hY(>y~l4I{S&|T!}Lav{i83_Df4sYgG%8QQmejNolAoj$YJT-%u`&l})Al zkUDz}eLJpL(Ou(i7x>vGtAwgtR#>@HUUNT|rXc?lS5wy!#i7*I*-b+;P9WT`@Mo>u zd%QMUl@WBgr!J~R^{rp|S1Vj|{eg=Z4xBtU#z;FH4DIV>sfjuj4y~^>UzZ7kZ_h9V zO+U0W9ZUtd>vUv0I8`}SJA8V;PH6BoK`Wi;X&}&w9~m#2j@G!|So;tSZc8QM>Ng?m ztod^_eotAuP@e^*GfgiJ=W<<0n~lC=^`Z9Z<>NuF0$#31J3D+&w>fw{? zY3f-%Und<$=hKo3-%`D#w{_)hk^rY2V>|nlv8lkf`k`h_K6Sb5rNqbv?9Ok8(RUqR zRbHbGEPI#=QBR7f{Ac6lO?x+qd7WpNu-nyr(bs&e_N~@yYNky2&t_F^arzqTO2%B? zqBlyqCz~crIX~q#4bP8d`qvNjXPS})1(eg5l@3O_s>&XFt;YM5*}mEQ5d9`fsVI59 znM9Xv+>?C!-fRj-$LL}FRgX94Q(%e$E3sLR>+x~?Xde{a|kNZyb2Y&>pd2D2Me z-G}HSgFXwIPN4i>3N5%KRU12mv^5e9b}(Z1PU{9c@Zf~Epi5fN1v)vzl$ z_((Cz3rq>%0+Sq|VB!EsAyzP$r*pGDlUgNZa`|2fJ22!?o99isvqiz^BogqaE(TGU5w<#5TQt*j4gX5&$-Dz2sVr% zHf&=Gc{wN8s?!;(%S)FM>5p>L3wut zX+C^ar=F@O$&AL=0JtfPZsko&+t_`fLWyvid4`pGt!CEjtb&}9K&Q{wZL7vrxNK42 z6Ms9Ra+7FeP`1tdIP5U{%!Ws-;A)R|+ zU&n-5w0-Jv26`(`jvf_TJSXOosFoRX2`ifiZ@Q9ro|S%T`Y)gv#whetD^>Z`nzGO> z4|Q$#F;%UU+I6W>4y?x_2jC_4tkDpbT1(#H9;z;Y{d9l{`oM+izY*R)Dym?={K5UE z84_N1FZ&7K7b(GI3YVvOT$0rYd!NtSWNFW}yYSl;E;9~dxx8n6bkDO`h`#MOe^lp@ zjs)&lF8QC=&nN)XyOH8iB{y_HT-f4&drHvkmq1Ls(Y1#{3gSOJMq(szt+?E9n?_FQ}0aY!C4V(j^DJ@L-69L-@D9Y^pnqu}71yRe3 zEfw#napYTct*l46Z!^_s)ZcgcwxY`~&p|@fofLnzhGX67)kSg=f8GUjLfFa^paS$0 zf8@EF4qTOZ{>bc}xuIb8PlsWp=&g3Ixz28D{2OZGrHHqoGgwDm0>B2vsQs)9=iIH! zuE^bDWWof`lW)y_fUfg#aJ58;ik5?Qzh-V1>AoQf7r z3v!{}_EQe$N%b|~%-pzux+7VdoR_KybCv{62$p0oMmgvAHOwuL`e0*(paW#A9dU2z z^V{fyJVPVS2wIImKh*!Qbqm&CI)^0-N@~NMugWe)r&K|~G&)7mfZ&aCkK=5I-}*L$ zD*0GXB2GR^6=a~RU9~)0Cf@i~aGbqYV!HoRjQ(-76ER9053Lb_&EaJpU2>{O6o1;w zY5va#&dIH~%Dy`ng)1QJn2d8Oo`!4ZR|4*(@l9E0sI*L&7oZyH&7@CQ9fKU_WZgJ+ zi8_8ZzFs*wp5kjH*-oDvedWjErL>1B-@6cfr&3?1!dY5G$!&yXwxiUqWZ4k&xd|;= zZ8#wJjp57#foeT?$$EHNP}bhPPh4HiD8W8#2n&^J_B3+Nfx%`9VlkpD&z$PuEpnG*aP397BaF8Q!TrbL#meK{YDM03hIE zOplZ^cYobLo2y+?fZUDAeG+Z)$obAyb-J2`xr*Y?3PbXQ>odMFa~@vcyaQkoec2sh zk#Ke5Mc(#qBFwg`Qv8apsnNu0XW0N$5&%@pqDg1@TF3+w_~`YGHQ;Q_llwLI6?-Eu zYOwN=J1mZO*)fy>Oo|Li8;*_ZSFxPJGOpAOW=_0H6T$KLL|n+p_pg*#g>C-Sz;Pll<24ZpQ3`cO0N zr|ygC%f1zSTr7q>aB9{_I;;RHO;+dfrWqT>p)j&8-}@KRP_@0o_m9m|3+x*eVS7fH z>?cj#@FnLIdEn+>Agm0T4N3*TX>fqDkHryCHkVlr{cl&P<0RAiyP*!F3Etv_(1uUm ztdzSA32++qj1q)3@61F6${Xg-7m{+x_3-Rgd-)e-%^fPt2)^TWWFucaK|0;Y(mo`EtDg{KGowXzh4Z$z-0>Te z9{PvZD+ygk-bLe*vbgUiZZCS7`&*O3cO z>NWa9z(<1eX)0DSHhPNh-D|BTHcHbe02Di>l{;VxuW6KtYsiiqfx2_2<$t3E8t35W*xRR_zd8lAuw81miH_g1C9)34p{%U8 zSXpx)k}pe{-0Hpj9KowS4rPV3k7?W%A9zBnymH1=E6XRYlWX=yu{4W$oOzbD3{Z)R zUtem@Zj0T24&$d>pgLYhsEiuaVA#S*wWB?7+~5zK}zp)tr*V4QDz*2 z=EY=Ss7^Hi^u_|eHDn}Kk71G_@(06nl6u1yRQ`8@T7dL@-7rP{zkoO)p$LV*W+YH( z{Gjrt1CfP?t3A4E-vU#}BPYbc_GQJp)1QyKr+Tlq7ib^N|Rthbk@|8BSaOO@l zFzNnKlv6Fc#kpDwcLJOz0}QoF9$ol6m(z(6PTd$T1cZBRC>iltJ{+W|4WjK6suc5r zoc;T+-eQ+Wp#;DmfL(o0zJgDf{B)9_o7v%#7X!B#_hEODX3HvKPpQ#wCs=7o&9&Xm zaC`;U0ta{i4DGXG@bKsXg~Dm|jAt4Bh#yNa7cCi$6>v}So7>>Hgcme^aw|Cb1XVLb zN*PRbjmxzzPkBa~ER{^MxvT-M%xwMwbLPEH?IdJ<*%EK3t%($&j+O;4lyky5hRUMg z7{Caf;eLab2>d9xdY|FQ#S=F+z)lXnaOAOaM@?iKwp*Npc2awKZ4mE_@Z|7~J@!ICbBfH-rlrwrnBnzk!a4gpA~HmG)E%|8 ziKu34Z_H1r3nZcBXeq^tD`(z(nu2DkX-^Z-9Ml2qGrr*q57*xvDg{TK-TAtLQecbI zmoYFo@yBHakK&Go0X?v%FY>rdD;!NSEXpa~84#nR&w2#D*ba9dAFOE!imC_7Uh9?l zvk57SW%?j1D~d&%)i?%*D9z0;^M z=J4HC0s&Ry9u0%SU;qICwMDXHWxu}LcQJFj_yzJ^!Km;3Qj+!d70a4(e|V#1-K7hi z)FY!=5DeTy?G6HY{i{M{N#cpt^^pLSm9YBM2FoBQ;j##dm57DCBgn9(I0?q; z!sSvS+)&QRdIB`sIgvrZSi)dgMtC*?3{A*0XEA{XqH-w(_CNskFF>VUI8cPcGR%QH z9<~HfrODE%+z3MiyPv>fVuZ;Ors`r#dyFXsb$1BhvhD(H)GGRzIp*XPY?RXzu!p@a z#eoCf8HUY5NE0mpDxzSA0EKf<3Fg!?BOAq((hL{G3kX$^B9~~9k^!-&I{Md!a(4re zJOmaf3ep_ahXPPI0^N?ZZzlqj=Sz;9QW%p`C;`gnhC>nfAEVSLC`_1z6b8(lALoyo zd}0hh6_A`-(kM%3b09?U891g01OP5bv63DoNOyWUJdNfj69AM27EOS+=Tt>J%T1zk zTJt)gk?*Kpu7F^2EsJ{&9BSErJcESMR`5G%&XaHqOF>|$5JagtWunMCE8gO!jB5u6 z5praQP^PKbjzx1zpKD1)qcG*GmlG-64>PeS#4{8jQ6Z6~%-&zHKgWn580{DeHF=ST z5$(<_`n8(R*C!6I9S% zr*yYF7D~9DTj)nUsY;r1Pk<6|eim8}mME}$)v%n&^QtMQpHX03{1dTMBbZT6kkb-? zK$MHEnM%Co>ACLCf-=I#i|tuSTtBBLWPAt3tGSY16vL_J6r%>*h8V0>Zz08U$V(}!1J4Fw3Q>=J*y09)E020YjpE!Lu%I#(RN6c3JZLGnA0oWxChm^kt>-LlE<)rM zW!4BLXlYpYI-9OFp-^w2T+&&X6pFCuBq5I#28!^tp2SEP?f1!s97ST2t=TzXHnnh6 z46K}=3(YiDq;CMDjO$-7k<=)$2_p|5#?Lw&I+%VM#_i(ELFyCsHoeNO7nbl}@Q-E0F$NFpxTU%g6Q9mB=jDwLv-24dO zN{zg&E`-b%YJI-?`&ajmiYT`x_!n)f)1xRQDMB7hvN)2<{7;HycjN4UE{`QpaL(Uf&%fzMgtsNnn4zT z*8It&v5b$1Y)h0l41#lRjVxzdQIO80@r-){rGQ4{Lp4WlR005SD$*>#Go^NnIbS-W$(k_rikyI22 zikLfa!C8nDp-}yys3y7p573v$6m zodUs92^flLdXl17zZCVoz>wzAg5;i9{bfj^B8Ki$OA!Or(Pc3kml`AUJS~7MZy4zk zCKDjyy~1j%Dj@b6?uygB@D|3?^E6}H1C|Aa2+koLb;OGEQAkpSW(1XFOZCBGy z^MV3~h5VPwQ4pZq80fcUeb(X5KW05+M5Fk_V!-E)0PZCI{tF0myXEkY`>5a-&%a9q zfP0M;3%uZ@+&CWi?s55qp&7u4sF2p zb*->_sRg?LLQ(uqpe{=V8vug>m|1i7u1mx(;jw!!PQc;Lv+yMrMGLvnW1y-Zl*0ZO zh>oLpBf190CAsh2jdrZ~fu8b*h)Lj;2$04?8Wrx)bA^nqk$={HOuL(gl8sq|G`<4@ zvck$929MX@w`%HuWk8OITvL5`6!yRwOHB}xd~Pbtmy646Q{U614nUy@wuw{9J5p|# zkA2EEb)GmHTSwx9pv))MGUE><%$Y-rF9RE80detaqU3xvfzMicIE)HGMS zRG3P@`{`wH@}$~E`$6Yn=5Lj~-(kgI6evKYK-5u)QV0L%t3E`v$;3ydmD-F_6c%P3 z4?s>DorGWkR*av+rKO()f>A6K4xn(FojDEBtlCDNu(y8xM@=&;hD_Ah`Gu4T0GGDp zDh$d2bDywr3?>z5c~Jpy+Rn=IsD)l)BT^#d5)k9K#?D|&tO7G1bP5n6Z9cX9BpB*O z^Rp_=ornS(4-REZ0F5@mCM%>oOXQZD$T~@3YaeS>1n~er#9B(7z%J(=TS|DM^_%!} zlDmv%^6x1Z_~76Lf>YsS2{Ae0-OvZ#<% zRKl8;LVch_8m?Kp{Fgpi1Vy*9RcCwRmY1ZZrmHsnZ-M0_~J%11fQ2ev~RdLy9cwkxBL(EX#cP z@qePh6O>tkp)_s|Y4y z0-*}lAW_K61Z9Tv_${t>8VP+(5`9KDik9LnM*zI$;M

g$M7M0L0-57(kpq!#?35 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

    )eAW;LcbG7n*(e<>l zV)Ehpyt^Gpey$k>ewXX>(L&EuR#ZDzn*V}v9BzK=f4?nC(py7;Id@KIZa|XI#CE>) z6=_gHlvnehI>!()uzv1!}Q(C1rj4iu%~FgW-kjQ9e=J#2MyiOCzRGw>;^WZ^4~ z3o7c)*x^F#t{izRXT4#81Rg3AYRIy~F~P>m30Z#WYG-hcu>aG*FK;Y9bUM0b?q>LC zUm#uVx7UP>R80|fO3syO(p85+KE>3I2Tdo98xpOb3IFmP_tsmg`th)!HmdYT)Y9R# z;z@%$fjh^JQSqUWUEQFlCjLkW6DWCkaz`pUn|wujw^%z@5YFgyD0;muQTZS~sB^KV zY2e;*#&8nGLV!VgZ+RCjjrx?c-i}4lH{%T$=*04y_jP%V4b9{CZPa$et@ zU6B3YCT?jb5f|Ps33`YRmqWX~IJIW-iqgs#Mo#FAnm)Z*g&|@KBan}#NQnQ*u=DN* z+48c9FSQ?KM{G*HJRI4PL!H9MRZ02=Y!3}GoJ{G*##-=|ZJvWeF>E){`PL?6m)F<4BZu?YpukwG%U*yr6C-0U=3~kD=7ZZsuH)`Cg{k@!`$8dcgNYt0c z<9r|kFxUQr-0a!UH#9m;d7ZaLDQ~|(v*uF`SSpwV`C7arGiOvn*IQWh>U|YD0V7s3 z+>U;z&wtNs(~|r;=5}F)_PKP9>;K&VM~75JFmbIUp*g3%r#~yMOP90N;bb;7e=X7p zSLnq43!evz`t$Bq8(FX#gbxTh6s(PvoKX8DE!Sy~*vsheL+NgIeb4dH)8|^2Y-U<} z!sHUWE+bDsSk=14GLz_mutAbE|p5E^}a4 zuYIS^^B1I4qpjDXrN;NaQZzrB_mn&=j5-iU4;(6?q+)%`;n6xVhBT7G3e&VXVg^xx zk{BTw7!eW|AzWkwRbyd%CR3(oC$R{ytBzd}n`XSrxmh~1XaxAsi{eVhkAVkQmrz4i z@sTe)$|<82V@?iTn>gg1t#w|XWiDP&+kz7n9rOkgM=YQk|0CvVa)lt6?N zv*BfsGw!AgPFy=)Qr4>HGrGT_JRm7U4mR1{|M`TJ;T3Vy8!mx2`PycE?^xmnYw0hD zKH?;MYY0)&V*ZdRyKf;o1WM@^2##0@sneGoqqo40r3sE(Bk1>vG*~2xpn1gZ&ejyQ zS_$nYP>RB&<{e#7+V7_i*#(E-ZF4h@p7k2UPzNL5sorcxgk;Foer3I<7?u3BBA*kM zL^Q8JNhJr*55p-rs`*~lr+Hex(S;g$`d)B8r=FP2ZixN+16M#>8~UPg)aa&$GO#M@ z+;=R#^A1iieBSER2Y%WYl3?p43-2N&3Rv)Rq-6-#nOGpTKrVkHv5;AM>!7yYZjBOp zPx|}Yi~LJYqWD|8&il0&szRyzV&z}Xebs((bJCaiDRCh$O(JL4==*TlmF?~Y-bRFW zmnu=~g-+2a_;AbHPEJ(8X?O3$N+?s|Srh!ON${)JMRY{S!h$o@C2E8S6raj|P1dUa zOqZlSmBi{R<_V(_)!p7I(OqiGRg))G3+G8A_(TfE_<>U~-xTu3!~QOjSB9Pw%;`a| ze+*K=eyA5D`r;;KEuMW#$G~J944JFFgh;2Yeu!EO#nIJA>n*H_F9%FsAka+RlI$`E zRUj0r7NDiy^CUq*Ffy-Uj>v_luKfpKq^+4z5a0R0Fn48HQ`lpxyk@!!rY58^E5{&hCZ7Hc{AlM$4ozw-(paEq} z_8Blus)~ncfzkXH(-!kDh@1>Mv`aEnT4M|sV#(P^h%9)Un65TD6Ml-_mVG165{z=u z44-0h#JtVyaOmw~=xkc-dWV8mTwD{^kA>B&I+N7aS#@z#Jafkpq^70imgqhc=3sdx z6x>`63$IAK753+?wa_)5%4?%+r2@9>(sn7d+B6ALB8a8`jXW#23s?d<;9#nfY)c`; zCSSk6o_6kd*U2_$fcf)vEuGrm#DK=zS^##fK>#DP<*AAL{ zZ@Bsd5NA<4oa&nroD^P2Sz#D-6FhoJyd88_BD!e6XToky+V^$3rIhcW^cWh4xk^Ef2X)V8#(o%d{CKQ7QmHufOEDuna}etR zYqwkInS*4=npFWgOI79zi$K_R2(4$7X=$2H^As~|u`|5oXZ|HUw)wDphxW2Jj)`-th0{3WE&L1z;FYfhR z6%Oveca5t}ZJ=v%cl^i#2nIJ5)4I!tVZdG$!eh-bjG zkbXfo`93`U?x~p^kS4rBUv9s2F0Co9$;zP8y1Ms2=|nIi!kmnCP~DU81;*L#iBwk@ z&m39)ZG%C2v*|y@YQ}5SC!?RFUh%Heb;0doYV!x9opXp&8`UbQa%Cuog6=W->P8_8 z%fxCM<6TDuj`sG}ec3g)Yg-|*dzts(^)5Bh7%Rg@++b9J70qUk*i&B*ofDWyq`X6C z*?>y5pSi2d^S()LV=P)^uc?eN)y`~A#c)9i zHfCuvcSl<)`C!2k2!>`SFfrSxJqM#M2-Wm=qKIU_un?C+Kxtq#RGu+EnRqs!-@>(I z&CDbQqZ27l;EH8@&J%Ls{CCNTr^>lxtyAooFB)ER%FOwEmbErXBW$OR8irM{YeOjb zvh34BMobst$Hw*0R)>T$gQ;F6L4h5g^KZd^iUnX`TH2cdEVB-qfMcp^Xln}$@3PqL zI;(oCHRYa|-!j1RfMx1+9R--PY)LiRqIy z^Fp{kgwbw2caL9ghDSnV_a4DUa$NkerW4EF7zocQCwI)qB0^5qgLYz$>wG%Sn!t*t>vbv zt_G)LY@m2%D2W-%f<3}(LCMADb?-g%)S_C!%GX753a63?Gw=+%@E+Zk!gn||#HS+K znwWi0TeiFM%zrMv;4Js@#w0lImNLBZ<%yikEtxZ+qiT-b;JT%jcZcR*Ig|c}w-v|8 z;T!nqZyWXV1Sc7HgTinI@H*b-RRWp~95#i{SO*)2{(a2v1cojmNLVZjhSMxRjq~{O zn9=voobrd`q=7F}y_r2$x^hF~NuJPIfcPVUhU&^Y#qxBlmp!yRn>DkZ9$!K1u3c&7 z#1+tBJ#krObn*tPgdI4c9#$1!%A(O*^)~KNk3#M_xxYT>N&`aJc*M-WP-_eySEMyb zH^^(&{Bvl7L`_~ql;=DgpwEM~oYY}BUf!_Gyrn-@1OfD#Zg(~~QPqaeN<#oPG|WqN z!y5g}cOTmT4&~?6{uWUoC@M!Qma|6>U0O8V8kJet%I5SezC@vHKHbh=iru?p`}Ka> z1K$lDsiUZ@9mI z5r6Mn!Qz~=RF8krx`$2=qjj(+c=Y_Y9S8?C`FEwLa#HLNN>AJeoLF8dsC{n8tgLOg zs_oq7G!R1`mE7~lfboZEDwZdTG`K>~g}QT%v+cqL?o^w}_ji>RSHQ9np3m-SOZUt? z{T4b&($7UKaPM5PYO+__jyN|p6BuDzv@dPe|G(qLIsxti_dS6fJAqgx%|!q`Yzhu4 zVTyN7yb_tnOcqaLY^%?>3NAb8&OIrQ*t~1%@UR&H`OkYV_6KEfL->hdin+{h-6YcKwK*7Rkjr!71b^Y~b@i{4Dv?~bWU zXT$A9S=ZMk4A`$rcd?Ry6H4lF%;invdLvXySz6h;2urcxoE()QO|vkCIq$ z5M8jL+ji`RW(UebPncDilHcojt^xT#39Pra)clXznI1EugeZES(A(9_tYbNU$U4@s zNP^N?X3piT=+sRS^JM<&mDwVAAA;{B7sU^2TEv10W39fw4>9odGspb3{o-qz8ZSwW z3ssud5(fPE$!YPDO%ZqNFkETxFXW_Rnk;Fx=PoZAz+>Q?+|aur>rCxag*kvHZ;gq? zo*QDwKL@AJS*qkr!Yb7j4N7M&Lx76szW(EODUhLSY%|fWqSy56U!h+LLHeT#yj;D^ z=5U5VydduBD)W#%H~U`~ogP2honPs(8|u*>Lzc<4_U0_vQJb7cO5a+KD-(y~c`=CO zJ(~NEcg`mKrx-F+;H+nyGx$wg*$5Ak#u!H*QAY}U%QrI4C{u2-KP@5IJIZ)9LhgsX zevQkplYoz-k^r}v%YcoqAMl%lg<;UHQ`J(6xBg#p{o$X|`)c~%EJwA|XyRVba^P9C zK@p`!WiY5KA3eIqQKJJGfRSYaJOjRjouznxVK(d~o8_KPW!CCEIq{31bBwml08(re z`nj>bSOVBsxf~k_XM)Uku$%XX^>ms%1(#p9vef?S6dB%WI?}A zJtMgtkW|fFWRAWF{{yY%5yI()Z~9E+=PRiRN{pzjeu*TMw#xQ$)sR<`oJH#|0IP3w z&+o$77Z;D?FB~&lvVo%EQC$E>4eKB)-*)lr8Q#!hfjQ7kTYaogsTb_?3&Y%`V4=ii z?xb0+89_#d8?5;)!!lGo4Q~p(Bz0;`NTod>yO=vh0~pntI>F3Gq+JCtey~~4^!nNmjP%8nm|9k=}mOVb(YmqTBWmi)Bya0dFZju{og2&DHf$_3XuX{=3YFe zz{q$HPb(X14;92p&I9S;6SQ60k(is$o@7i3xRhNgZbq=nhL;=qJ7@O`byW^aWVS=2 zS4&hlM=|D`QgPa^-D7=k5#TW-R(Ow=O*RWi&+u5!f%AikO`Ea7y!AT&DSWtII5U9E zb#53a?Ql%NqjX){EX`NsM(8#+_TjcHw=<`f?DwQ%Ms+T#KAdN~wt-~)6gwlopdXrt zp1$K?P8tNbT!iE%`8h=2WKHFKAts%>T$U6X25y(nO=;m~dX7E-fc zA!eB{hLiZt>MN)Yb7ta*&ALCurg^$!EjdMdaoKc4<>Z!63&BG_LwVAnc;UZSROyR< zdR)(!G8uGHbC+YYsA`^wR95M%r^1`?m&Q2w5bIq*MLCPdVPf$d8@ui@cPWY9+)BQT z9YF>vS~Sl@wvGUn0AnP$@+4(ImExp3_Cxn>uau?io{a+$S~GmdqJAEGQDZ*TW^mYe zp{te$FOmdu0wZR72~@fii@Egjr;>0OMeb-_qGa z&AM(AVY*3hG!w1_F(XJQ6x>}nJ0Z8+IVvTU4bwt2Zqy@2!i&u-v>l*@#i?wFJ7N2o zB{yJ%WF0hMGZtpIP_)#;vheE0_kGVrEJlEiA86a`4op*`grPKis&ts6;wTU=jwB+D zJF8|$cFQbyE6N(M)%%iVUzmBq`qt8r{5+_?>zrq`v}&^KpP}6J!yx zlbl&Y+ax)6nI;$ZX2g_SUGlc8p<^V=iu*SoEdWE9$7Rh(W?|9(882t$wj3Zym`Khx zJBI65u=g1cr7p_8)H6^Sw@|zDm5DnQtlOB8NDepUyfJ3UNYAh1XV4oPq(J5b3uaz) zSj5cj9PP?6Vl{h21OnESC#g2bBjb`^o7(m)I&+Y)X6y}iD`ZU1yjVYTN9p|%#!}NB zGUPa1oUmwV9&rbclbV+=yX5N_Lzxm7AO3BHlQZS7FA0bIGaby!Lq=`jMr4a=?@_?` zHwLr%pU3l2d*{ZYeQ&ij5nrMp!7*kw_&E!L9a2{l;Q85v zaq*(Iwkm9%iY>6HCaSTZeH?J#Sn_i9g6_=rBIQj<%b&0|kD8Tv`zU4~s{#K(kdg^& zzOxX@73~o`Y^D|FD-x@cs_QIe+}oWvfqmE{B1QxP^9BT|R z0={T8YB#C}6xb6DGiI>K2b#s=gY4*}MXa2?c1f&fa+OgRdIETcuJN~2Q^V>=&~x_E zZbUR;IGM6iWdmPkbJDl^$z^b5^XEY?o?rYx)Mpuy40~E+|L5pJ@pivc z&wyiKhEZGI*3sHW~^P>80!qGq!e}vW81yj6{@)kP@9D;~+ z4+>0l89ykf(m7n?r=d9rL7NnmlS6V(wrC7vW+Tg)d3p1&hx7cMhy<#3&9LjP4&BII z_`4Ax7953Aplc_=beK>)v>Fi1EU#hoh0e@lLl@=;lnP3S$FLaXb$P@cd7(Z4f`R=m zx9b@F$>2eV=E>C3r@YR`YfCOopDo%x-?2eOs8L&w?}-FecByk&%-oE?`VKi;D)Uih zaiE(hVh(9$ZfHIOWkkpz6U)|MIkY%}PX3HdF>ur-1HQ1lsYpUu$7&aDEO~Ej@13xq zwR!|<^^0=ak?=xul2)u%>>vU5S?o%G9XR%|=M^U!AowFecLr84s?#^@$E#w0NkoRy zmoUt@0%EiESSx*C3yz1NHYm#-Q#^chBDvX?a$MTB4NY&h>AI*;dy7C&+>#ndVq{pS zSM^Q*f%~MXS%wlfB^Ro_sT(}(N0|Z74wu23dAhms#@#{2>W?TECDCfW0SrK}D`z<1 zNRs0df>&DRl^kuD$d`Gz1x|DAkfC@KiE5sinEj!R2=t;p6x--f7}J{(71#!d0QQyrF$hV^$M>;97GRtA;^628{NXQIjC3-;whuleWe$Xu)g`e7}r-aWm$4Norv zBCB5-<#Ua2bwFnM!s9LpSZ4S`8dS-*b#%K!mcTlr!d-t@b6AAeX`BwGTRkxBurGf! z!1Kh7x`NT6B-IT1ovdSLjwX=fHZc*S=XZ6ow2y(!*^*%4-)ME6JIL6`27O)4wW32( z6&K4qA^4!s9QZ;Rz_YCU+ZI09ZJ9l}qu@RDHhY?TvlsCV4U_UiO%k~hdQIoN-|-0Ew4Y+U?~Hm2W|lJrAh=ElDtZ&L$c$A@%J8l^ zX7b4hk1N#oyMJ}p=2_n2=(b3-ekd0PWg3&TfF5|+#A-&wR{RRr-endHx9zeG&%knI zuvi8*nS2KKj_@_C75Kf}l)^^ZHis5oQh8eVkb2%+bPdFA!v}bKqQa#DF7q0J72(jT zdsqMqu~}Hqn;+|aTO*bg&O8d-M|Z|>X?!b4S0_ci6=VOPb=QJ<>dUCx(qIYG+9&u` zxrZXUsOv46XZTyfMF?mw;(D2Pmu@+F63|d2o|jN@`wJNF4{k8kX^ATHQ4N};68Z8Wipp9czG!4BUA|9!5E%)RJWMCIWGCEPh)2ph7FFtW60yHg z0(}g<^^)G9MBM)AnV@lw(*}3@FU|{5KV(OTi2^v-JG!GI#U;MVy+wxmpQEiYBQcI% zlSl@5PQmf*yKY=T9$&!u$DSL*N`q103-h&&vv1iKqRtCf`Lfr!hK8lJ^~(`~J`oS` z&{s^JzXdy{+d(4`MerF3J=@bSi868PES^Z;nNuig)vX^hS}GVvy{n;V@>l;Vp_r`r zpNXa(OjLIzIPIJ!yEg+|FKC^b@8H=nevq-peV(Oyfa+nT!0obh^-{71kLa^-+C7!o z_)M&gVo0DbaKs@+l!`EsrIxbX`C3so-}8qqM??CMW#Jxi-7pO6Ki7767bdn3WBte$ zCJKV85QzUJ6QB%5CDy$*;lNsToJN#(p?h-Skn(3p@l92cTsbhhu< znmehqNXw&QroV)Bk|w_pQzV+-E6l#dIRX1uW|P)Y?(y(m6Kldk8>$X$MRR;LCxd`M zahU+nqmm!3QtM2r+*4gG(oTQNmqmrw3G?zvyyVH8uYNu4A^@rK3~d@shJ&1@ySw+XE1SF$2b zq6#$GOvPmNxT%)syN1hOeCT?+k;I{m$=wL1f@doPUTg@9qvE%#pw(vivzedEbbPzn z%K4Ka?{KmkM=`BCYBFhD@0RX91bXgQ?ebd>?q!MFOtaNK+GgqA!a{Vtl~m%*br3#^ ze%YzpuSy?d!OzDS?s4)vc(0DM*fG=0rUdgt2I+kvZd|U$OK>~~BDF&5>?kh0Uc*N$ zHJ2okK=CCET3;G9c8kbvWkqnnsG7>izFjJ#@5p#I;tIUI##qH1ojGBX5>=E2OBBPsf^d zbsq$nFIt*rZVUL}hU~&c@iw#G|2)dpnFk95yAeJwtmGc;>IZJX%lSX9j36O&0mIBv z-@t<2Zs*c){}7Hnhs(2@uR6(JOJ00CjB)8uOF|ySIKROum@_zw5sBGabgVsO2hBFF zGnx)QhA$m6mww04oQBTDO?FV10N~)?Wb{U@M|EEjd;1nYj$XxBTuJNDZGC&P84pRrfF99D)U4fPuljO)4Yq*LMZMR?juX7_~lM zRxntk;?+l7K9d*M|5FTCtNq5mU)QGm)kiOAsr3od7ui$` zf8BR)qWP6f9}#0HCQ@8Ro?S5YrH?rc|C5ldtmYH(G@H67_Df5d~$Pi9@-MB>0@{Y*~37m;{ zd(o>bC@0vcZ5zpxXHGU1Q^K4aA051Cd`noX?GV}`aYlF@@^Twh?R?Je01|y|%v_Uk zGPGZ`*vwke##u2J5Agd@ca3VR0Eu}SKDwV73w?C`Ykbe6l>>^y-wVw1?0e?-6Hz`O&8`#q5F6L`yBpamA)_)K4*^ z?^*l8jk{rEz+clON01t54}e_05jl~Nn3wPKYn3ev;Bykdj32xCdA>T5OoFEcqyP%G zt0KuS7vU^T7~#<8u=Z7vSJU!Rxg~jV5LIhd#zgW3XPu<}4p6PgfET{yo5v zIK6d(9_gU-qG^Mb05q~KgGZev`!t+dy-liE%Z~F8waTfpsMaFD46A=1Oi-O_W#QOM z7K}Dy{Poap7bq}HNvsWY6Bj$&Jm!UjFHF{c&39x@Zh>HOY+$gi#PrckQ#vf1x;psY*Dsf~!a5^W1NagRsWD`U_||@-F(34m0T?1+3ek z;x7%$VeyLz9E){i)K_dG#a$NCfElVrv?L(%HIhd=J}d$xD&f?i?qS|*MuV(+IgJ@p zusMkAYAjE-Qrs+LxN)Q`!+dPZ(&V|Us0#8VU*L`ZDQ4*3>&IDasLB3L2idVEuLMiv zNcIFB))|{UwSuL~QeGBS-?$EHvLH-fs`l0J=o9Jsf?$(XSZwgtR#jisIJ4?pB{a*P zD(kVCp-s#HNvuEpezjug)7-p`T5!ww1os324`siaW%UFxNdSkk5zyTH`)chS<-Eh% z`f5tMzTkpDT+dtKF%fLXk4e|#R_>Y6C+M)%kBs;tq*4(38 zr4`H@8g|D(r#eDMmAuTqo@W^9X9(`}cr0;L%!;B@wCzQ;4KwG6>9x9xFKaz&7uJCs zrz2f=aX7I}USw(h=!>B;?zNNJb5~{wX#x9-hRjb(v;)oySn8x}bPuTdl8u z?j-9`D^8MbBDxm?!I<7aby<(~Q9dc9g*SGbhjJvtFBU!9QUl5S8^E_SV{xk5wOf#?6v3g$Bmp^@Wvu}}p%L+0Au)mpf9}n=8>7$po1hc;PB2V5 z$;2Qhosd=Gvs&Kg6aW0af3|II-$3z9U>?uKk;y>HM)c#eDCab+XXmR3!HSS^g(sun zSTJB+jug;>%P>Jhx{~LS@I_N^N-L|zy$n%m-UV^jnQ_&_CG^|$t0woLc&^`dg0;NZ z|FBrZX3w>FHs~=O-CH ztu6B6d=g)c29(RpPHN>UfDYlv5Pm`4g`z_(aC zHr>Opuot3HdzZiEjo^T~0$Jp{pK|5xkuc@E2Me>2la`Wf8Fltq=L@SQ+=F0-aB{gC z*}ss=h*gn zdh#yqyT9As%VD;mnR_7vawbQYOIEUM>$sky`nq*sly?)Nz}X9z!Vc{5o5Qc#7*epE zOJv(->=#Hf>y{8?ux*)axyG!0OfPKSqo(bGE#cJrYVrXE?Yu5XAY63Awpk<@J@pUC z>cVB2ZuLDwvk0Ays;Tij26TPEIc@JT!e4+A{q(S74M&PG_0d&E!qcl8JBEIMZL z3D1(@dP&^E-Im%Vkn|k~+hj+0zI)wHO zlONfML_0YQdu%PYX=G~zOM7I!UgJp4m}EP{!oc991nTn5fFT5RZyCca9ARJb-0C7o zI=n{<##=8wcRXdb?dz4jIX&muLbQF=h(5RUh<9};lN5qvcMNTj_IPZ&3C1IZpHLm$ zrQM4?eY#JtW%m8Et_u^~UvDE^m#A&oh8ZOxB##*+T0IeuZFn{bk4!VEcv$Mf*%;gC z#!QIn$~(5`F3jQWfxx$GFIz7;SZuO8&b#1$dplK5{7A1rg<*?9%!a_@)B zdXG8TVlvB|eI)}XTiz3As5{y2T)SFW!Fw~*zh^hW#64Pe+jF^bjWK3Lox$CcgZppl z-H=#(LmkJG%wEq{ZynRA;IXcD39}97IXW%;0>aD2W0GQBzj^OudcMG47eZz23Gt7j z!u>OQHv4Nc>9<|kWPbPjYg8%MwjeetnF-)u?V!V{;jwMDCdel0^Nr!MNgNro-%w=m z!N*y2-4c-R07q8aY#V;9*j-z6djr?o>N_#+4R5IjoEw~JJzm=ucccfqEe@}o7H$4b z-a~3hHZkNQxZ7hQdzY-b%j|i^Y#m7q#j~+8>~%aHPjP2|%R7cWEH=Zp>jK#LT^_~`uD#krthqSg_PpM9cQIQn^7n?z z$=))_VJ{`OjOKSLFzIcXWOUC>v)sd%mL8M*kyuA2VSqcGVG-;D2}k)A^t`7Jcun@+ zRI+j){{Ur<<>>hi_ioSf?BMl#4o6-`PM}YC$tTVOrTt&z&71A&(sS9s=yxsd+exa!^qWs>S<-lcro2XF3l^&4L6r&GX-__kv+yF3%mQ`ObW&PS- zkY$!_<-2EBepr`ooNs+dun>g)mcuJX2$vJo{Sn+h!s&iy%%@=y7M zd%g1Wfv{)bn;;x8m3GYIFJ?FQEn6h2rTpf*_~dB|_!lKw%QZt$BE7qGA=kYt-I zME2X;%WRv{XO*@$kZq7dF5A6ZmhF+%w#Rl!-kdY-pQwfpa_gyK)DsEg1}UgvYdik{ z78}p*!XEwL{LJsEyNqYPnM12CnP;9~1(|1a)E00%#&1vwhpSh~?#?{Df(|=+gS#?6 zP#-SB5ym*nIP;A;cqj17INl5FHftZI?dp26)wXlxyD>blS#7p5K`FcAr;_J)q`57! zOCj4O$amWgqXoHQ;vzjcZM!a8E#VsAJ;>U6lk+A~{{Y31kJCAWsQ&=uiPn6#JDv}> zm%J`HCmkg3W(9|l*O7-WD`Z0?lV-zad3umO-Mopg?bBy)StsF_mRoGWvTzb)PqxnN z#wVTNy5ambS)Y=&o;RymZj%Z2F3@8pK|Mn`mI1oMysl(7tdSFEs}CdTIC_@7@Kox6 zgQ@=j$S9|=Kh>td_DS31YqI?<331tdM`qvcuaW0`5YHLi+U|K{@D3RzjaiZ>Sy)Hy zggS!KTYBN+IA!6sI^cMCbMbiDO{PpsV3WYjv#tKaF(D>T)NV}b+#=g@d%Uw`CvPQQ z_%X95fy-wl$vRK=Fp;-!hO;M8`h~%P{{WLY6OsOsI(x8ujii{`zvY8S10Q0JtlqEX zliw|)(*%vPE#6BclVX2XW!%9sZJZrEg#{lrU?&YM7i&vlhi|gwv2C)&zpG{6Ep4`K zjt5>_UJU#ABonenAGO)DFB$LKa!vBwWPDge%(mI*%L`Z%Sufu#W2^!H02(?!oWJ>U zK{3B_%3~OiW7U^UkB~ckpZ@Wh*)~g%?45CzCke6m`DY%<9q<-TqA`n|A$fh=Fu{;O z*on5=!0m57Ot8q2EQ0Gh;H)-noM$<(ZJEMJ9BdXzJ&+c@FWU(d$>o^DyX2Yeo8`Ie z?4})EW46!V{{Vru+qf?JXM2aNW(jwEk)LnuHusZk{-YgFT}r~+Wc9#rIl~fJ7VONj z_6a1i(X$B*cCrvSZ8cPygB;H=^o>`gr2aj(p>d(Wu zct|oCyNhkKBRO6Ryf{Q#W!`*)W_Et|k#RZSL|*3p+#GuU z0O-r8{ zPi2R>?doRM$1cV*EepoR3s07xcry;n_l9M*?VDu5c`owzWuL3gAMws;Ieqa5L(lzJ zuy^~vZ!V;FXHoSk>eN6wl4GAN<0QPE{{RHpKXMTw z4#n2W5LMABZSoTLE%F!Ha3e85E~l#@)wXTY+>ZEfIR_0oGUt{zVmiKw{9tfld;b8e z$ezyTx3LxE%{rWU#J}DVKJmA_`D~Nd;+oh&CkeJ0z=_6K-J~qrPh43iSLl#rKTxb} zZEPcc*^j|9<&k6))w>;Wmz-qV0>JxWIr7=Zwhm8p>h63004{`N*&q7nxST`%yAJnE zJK<7MzHE;qCf%f-+%4ahDZL-IQN46kv-DUGcNCMi^vI#7t5y0a^2-P%Pir7ODDS| z;W*Acv9$`z**HD^Wd8u--ZBaH%Rg3i8w$3Tdb~y#h;RD`#kc$a0Jwby-@Z;?DOu^N3)u z4J9o$(fiuIT8W+CyeZsZ&H)xu>nHq6^*6W|V6lH|&wu=X`+t{wdFal&OZS)C+v%HM ziENEa<@@YFkqF3aD+PM9$Mu!_hNlIK)zPpE3EX&z&e<*Ue?nVFdl+`i1Y$OoT}UjG36>l|U1^q1(D^h7*Gg!v=#>+<~w{TJv* Z>y&m*dx<^WB&kY|Jjxa4SfIr literal 0 HcmV?d00001 diff --git a/js/src/dapps/gavcoin/Accounts/accounts.css b/js/src/dapps/gavcoin/Accounts/accounts.css index 7c4511710..a7fdcee73 100644 --- a/js/src/dapps/gavcoin/Accounts/accounts.css +++ b/js/src/dapps/gavcoin/Accounts/accounts.css @@ -22,7 +22,7 @@ .account { margin: 0.5em !important; - background: rgb(50, 100, 150) !important; + background: #430 !important; display: inline-block !important; } diff --git a/js/src/dapps/gavcoin/Actions/ActionBuyIn/actionBuyIn.js b/js/src/dapps/gavcoin/Actions/ActionBuyIn/actionBuyIn.js index f97e20a6c..42de205c1 100644 --- a/js/src/dapps/gavcoin/Actions/ActionBuyIn/actionBuyIn.js +++ b/js/src/dapps/gavcoin/Actions/ActionBuyIn/actionBuyIn.js @@ -73,6 +73,7 @@ export default class ActionBuyIn extends Component { if (complete) { return ( @@ -84,10 +85,12 @@ export default class ActionBuyIn extends Component { return ([ , @@ -82,10 +83,12 @@ export default class ActionRefund extends Component { return ([ , @@ -85,10 +86,12 @@ export default class ActionTransfer extends Component { return ([ , . +*/ + +.body { + background-size: cover; + background-repeat: no-repeat; +} diff --git a/js/src/dapps/gavcoin/Application/application.js b/js/src/dapps/gavcoin/Application/application.js index d5d26bc3e..1ae5d870a 100644 --- a/js/src/dapps/gavcoin/Application/application.js +++ b/js/src/dapps/gavcoin/Application/application.js @@ -32,6 +32,13 @@ import Events from '../Events'; import Loading from '../Loading'; import Status from '../Status'; +import styles from './application.css'; +import bgimage from '../../../../assets/images/dapps/gavcoin-bg.jpg'; + +const bgstyle = { + backgroundImage: `url(${bgimage})` +}; + const DIVISOR = 10 ** 6; export default class Application extends Component { @@ -70,7 +77,7 @@ export default class Application extends Component { } return ( -
    +
    { this.renderModals() } . */ .status { - background: rgba(25, 75, 125, 1); - color: rgba(255, 255, 255, 1); + background: rgba(255, 175, 0, 0.25); + color: #430; padding: 4em 0 2em 0; display: flex; flex-wrap: wrap; @@ -38,14 +38,16 @@ .byline { font-size: 1.25em; - color: rgba(255, 255, 255, 0.7); + color: #430; + opacity: 0.75; } .heading { text-transform: uppercase; letter-spacing: 0.25em; font-size: 1.5em; - color: rgba(255, 255, 255, 0.7); + color: #430; + opacity: 0.75; } .hero { From 8d66fc50e235a8cb88e9e5c2d90ff67171b18854 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Fri, 28 Oct 2016 16:46:25 +0200 Subject: [PATCH 059/192] Add import of raw private key RPCs (#2942) * Importing an account from raw secret * Add jsapi & jsonrpc for personal_newAccountFromSecret --- js/src/api/rpc/personal/personal.js | 8 +++++++- js/src/jsonrpc/interfaces/personal.js | 20 +++++++++++++++++++- rpc/src/v1/impls/personal_accounts.rs | 15 ++++++++++++++- rpc/src/v1/traits/personal.rs | 5 +++++ 4 files changed, 45 insertions(+), 3 deletions(-) diff --git a/js/src/api/rpc/personal/personal.js b/js/src/api/rpc/personal/personal.js index e35333102..ca7dbce9b 100644 --- a/js/src/api/rpc/personal/personal.js +++ b/js/src/api/rpc/personal/personal.js @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -import { inAddress, inNumber10, inNumber16, inOptions } from '../../format/input'; +import { inAddress, inHex, inNumber10, inNumber16, inOptions } from '../../format/input'; import { outAccountInfo, outAddress, outSignerRequest } from '../../format/output'; export default class Personal { @@ -73,6 +73,12 @@ export default class Personal { .then(outAddress); } + newAccountFromSecret (secret, password) { + return this._transport + .execute('personal_newAccountFromSecret', inHex(secret), password) + .then(outAddress); + } + newAccountFromWallet (json, password) { return this._transport .execute('personal_newAccountFromWallet', json, password) diff --git a/js/src/jsonrpc/interfaces/personal.js b/js/src/jsonrpc/interfaces/personal.js index 748c8799f..2a9ce7c19 100644 --- a/js/src/jsonrpc/interfaces/personal.js +++ b/js/src/jsonrpc/interfaces/personal.js @@ -141,7 +141,7 @@ export default { }, newAccountFromPhrase: { - desc: 'Creates a new account from a brainwallet passphrase', + desc: 'Creates a new account from a recovery passphrase', params: [ { type: String, @@ -158,6 +158,24 @@ export default { } }, + newAccountFromSecret: { + desc: 'Creates a new account from a private ethstore secret key', + params: [ + { + type: Data, + desc: 'Secret, 32-byte hex' + }, + { + type: String, + desc: 'Password' + } + ], + returns: { + type: Address, + desc: 'The created address' + } + }, + newAccountFromWallet: { desc: 'Creates a new account from a JSON import', params: [ diff --git a/rpc/src/v1/impls/personal_accounts.rs b/rpc/src/v1/impls/personal_accounts.rs index 0fa236845..6e777f537 100644 --- a/rpc/src/v1/impls/personal_accounts.rs +++ b/rpc/src/v1/impls/personal_accounts.rs @@ -20,7 +20,7 @@ use util::{Address}; use jsonrpc_core::*; use ethkey::{Brain, Generator}; use v1::traits::PersonalAccounts; -use v1::types::{H160 as RpcH160, TransactionRequest}; +use v1::types::{H160 as RpcH160, H256 as RpcH256, TransactionRequest}; use v1::helpers::errors; use v1::helpers::params::expect_no_params; use v1::helpers::dispatch::sign_and_dispatch; @@ -95,6 +95,19 @@ impl PersonalAccounts for PersonalAccountsClient w ) } + fn new_account_from_secret(&self, params: Params) -> Result { + try!(self.active()); + from_params::<(RpcH256, String, )>(params).and_then( + |(secret, pass, )| { + let store = take_weak!(self.accounts); + match store.insert_account(secret.into(), &pass) { + Ok(address) => Ok(to_value(&RpcH160::from(address))), + Err(e) => Err(errors::account("Could not create account.", e)), + } + } + ) + } + fn unlock_account(&self, params: Params) -> Result { try!(self.active()); from_params::<(RpcH160, String, Option)>(params).and_then( diff --git a/rpc/src/v1/traits/personal.rs b/rpc/src/v1/traits/personal.rs index 92a9df6eb..8ad1b7ac6 100644 --- a/rpc/src/v1/traits/personal.rs +++ b/rpc/src/v1/traits/personal.rs @@ -52,6 +52,10 @@ pub trait PersonalAccounts: Sized + Send + Sync + 'static { /// Second parameter is password for the wallet and the new account. fn new_account_from_wallet(&self, params: Params) -> Result; + /// Creates new account from the given raw secret. + /// Second parameter is password for the new account. + fn new_account_from_secret(&self, params: Params) -> Result; + /// Unlocks specified account for use (can only be one unlocked account at one moment) fn unlock_account(&self, _: Params) -> Result; @@ -84,6 +88,7 @@ pub trait PersonalAccounts: Sized + Send + Sync + 'static { delegate.add_method("personal_newAccount", PersonalAccounts::new_account); delegate.add_method("personal_newAccountFromPhrase", PersonalAccounts::new_account_from_phrase); delegate.add_method("personal_newAccountFromWallet", PersonalAccounts::new_account_from_wallet); + delegate.add_method("personal_newAccountFromSecret", PersonalAccounts::new_account_from_secret); delegate.add_method("personal_unlockAccount", PersonalAccounts::unlock_account); delegate.add_method("personal_testPassword", PersonalAccounts::test_password); delegate.add_method("personal_changePassword", PersonalAccounts::change_password); From ef0ed5833af75127acc1065e4f3b9b114ad91151 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Fri, 28 Oct 2016 15:51:18 +0000 Subject: [PATCH 060/192] [ci skip] js-precompiled 20161028-155009 --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 26fab3d49..7804269c8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1213,7 +1213,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#3f31aca3dd0b71871f6439407c3e258c3ed91abf" +source = "git+https://github.com/ethcore/js-precompiled.git#72b966a36c0a063e96de823f92c07201ad7ebad5" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] From 1094dfbe6bd62c3fec7d4626a52b8fbe1f9ff9a6 Mon Sep 17 00:00:00 2001 From: "Denis S. Soldatov aka General-Beck" Date: Fri, 28 Oct 2016 23:04:00 +0700 Subject: [PATCH 061/192] Update gitlab-ci add darwin and windows tests --- .gitlab-ci.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3d822a9d5..e16350325 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -364,6 +364,28 @@ test-linux: - rust-test dependencies: - linux-stable +test-darwin: + stage: test + before_script: + - git submodule update --init --recursive + script: + - export RUST_BACKTRACE=1 + - ./test.sh --verbose + tags: + - osx + dependencies: + - darwin +test-windows: + stage: test + before_script: + - git submodule update --init --recursive + script: + - export RUST_BACKTRACE=1 + - ./test.sh --verbose + tags: + - rust-windows + dependencies: + - windows js-release: stage: build image: ethcore/javascript:latest From 8cd66f14d5e44471fba87e322644245b964bf952 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Fri, 28 Oct 2016 16:06:51 +0000 Subject: [PATCH 062/192] [ci skip] js-precompiled 20161028-160553 --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 7804269c8..b9d989fbd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1213,7 +1213,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#72b966a36c0a063e96de823f92c07201ad7ebad5" +source = "git+https://github.com/ethcore/js-precompiled.git#ba726039185238d6fd604f092b089a7d52c0f436" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] From 40d794ddfd736d7c8343a7a4b6a3e353cc1069ed Mon Sep 17 00:00:00 2001 From: "Denis S. Soldatov aka General-Beck" Date: Sat, 29 Oct 2016 01:47:05 +0700 Subject: [PATCH 063/192] Update gitlab-ci --- .gitlab-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e16350325..424f933fe 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -380,8 +380,8 @@ test-windows: before_script: - git submodule update --init --recursive script: - - export RUST_BACKTRACE=1 - - ./test.sh --verbose + - set RUST_BACKTRACE=1 + - PowerShell ./test.sh --verbose tags: - rust-windows dependencies: From 3e3230aa34d52311a047a6f0e6d744b57d20706d Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Fri, 28 Oct 2016 18:50:17 +0000 Subject: [PATCH 064/192] [ci skip] js-precompiled 20161028-184917 --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index b9d989fbd..ed9b0330b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1213,7 +1213,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#ba726039185238d6fd604f092b089a7d52c0f436" +source = "git+https://github.com/ethcore/js-precompiled.git#ed68f673cf7bb576c1a7d1c4328e44f28dea7713" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] From c61526f8d51efe6a02d4196a1da5872986ec686e Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Sat, 29 Oct 2016 09:55:55 +0200 Subject: [PATCH 065/192] Eslint fixes (package upgrade) (#2957) --- js/src/dapps/githubhint/Application/application.js | 2 +- js/src/modals/FirstRun/firstRun.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/js/src/dapps/githubhint/Application/application.js b/js/src/dapps/githubhint/Application/application.js index ea7e760c5..8eb3902d3 100644 --- a/js/src/dapps/githubhint/Application/application.js +++ b/js/src/dapps/githubhint/Application/application.js @@ -151,7 +151,7 @@ export default class Application extends Component { let urlError = null; if (url && url.length) { - var re = /^https?:\/\/(?:www\.|(?!www))[^\s\.]+\.[^\s]{2,}/g; + const re = /^https?:\/\/(?:www\.|(?!www))[^\s\.]+\.[^\s]{2,}/g; // eslint-disable-line urlError = re.test(url) ? null : 'not matching rexex'; diff --git a/js/src/modals/FirstRun/firstRun.js b/js/src/modals/FirstRun/firstRun.js index e5033f9e6..2ba8cbbe8 100644 --- a/js/src/modals/FirstRun/firstRun.js +++ b/js/src/modals/FirstRun/firstRun.js @@ -122,7 +122,7 @@ export default class FirstRun extends Component { icon={ } label='Close' onClick={ this.onClose } /> - ); + ); } } From e1cf6f7dd072e24d36297e6fcc1691d8ed0d0a11 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Sat, 29 Oct 2016 07:58:45 +0000 Subject: [PATCH 066/192] [ci skip] js-precompiled 20161029-075744 --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index ed9b0330b..8d040eade 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1213,7 +1213,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#ed68f673cf7bb576c1a7d1c4328e44f28dea7713" +source = "git+https://github.com/ethcore/js-precompiled.git#93753cb4d50f85eca91e024196235c6f0deb3bd4" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] From 65c985bef3a6d2ffa1523f3699f941940b79fafa Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Sat, 29 Oct 2016 13:04:47 +0200 Subject: [PATCH 067/192] Gavcoin event display updates (#2956) * Mapp all known addresses * Display timestamp in event log * Shorten addresses * Display "Pending" timestamps as well * Pending/Loading with empty timestamp --- .../dapps/gavcoin/Application/application.js | 12 ++--- js/src/dapps/gavcoin/Events/Event/event.js | 44 +++++++++++++++---- js/src/dapps/gavcoin/Events/events.css | 9 ++-- js/src/dapps/gavcoin/Events/events.js | 10 ++--- js/src/dapps/gavcoin/format/index.js | 4 ++ 5 files changed, 55 insertions(+), 24 deletions(-) diff --git a/js/src/dapps/gavcoin/Application/application.js b/js/src/dapps/gavcoin/Application/application.js index 1ae5d870a..c5eda9db8 100644 --- a/js/src/dapps/gavcoin/Application/application.js +++ b/js/src/dapps/gavcoin/Application/application.js @@ -53,6 +53,7 @@ export default class Application extends Component { action: null, address: null, accounts: [], + accountsInfo: {}, blockNumber: new BigNumber(-1), ethBalance: new BigNumber(0), gavBalance: new BigNumber(0), @@ -68,7 +69,7 @@ export default class Application extends Component { } render () { - const { accounts, address, blockNumber, gavBalance, loading, price, remaining, totalSupply } = this.state; + const { accounts, accountsInfo, address, blockNumber, gavBalance, loading, price, remaining, totalSupply } = this.state; if (loading) { return ( @@ -93,7 +94,7 @@ export default class Application extends Component { gavBalance={ gavBalance } onAction={ this.onAction } /> + accountsInfo={ accountsInfo } />
    ); } @@ -216,8 +217,8 @@ export default class Application extends Component { api.personal.accountsInfo() ]); }) - .then(([address, addresses, infos]) => { - infos = infos || {}; + .then(([address, addresses, accountsInfo]) => { + accountsInfo = accountsInfo || {}; console.log(`gavcoin was found at ${address}`); const contract = api.newContract(abis.gavcoin, address); @@ -226,9 +227,10 @@ export default class Application extends Component { loading: false, address, contract, + accountsInfo, instance: contract.instance, accounts: addresses.map((address) => { - const info = infos[address] || {}; + const info = accountsInfo[address] || {}; return { address, diff --git a/js/src/dapps/gavcoin/Events/Event/event.js b/js/src/dapps/gavcoin/Events/Event/event.js index 0b4094ac0..c068454f0 100644 --- a/js/src/dapps/gavcoin/Events/Event/event.js +++ b/js/src/dapps/gavcoin/Events/Event/event.js @@ -14,10 +14,11 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +import moment from 'moment'; import React, { Component, PropTypes } from 'react'; import IdentityIcon from '../../IdentityIcon'; -import { formatBlockNumber, formatCoins, formatEth } from '../../format'; +import { formatCoins, formatEth, formatHash } from '../../format'; import styles from '../events.css'; @@ -27,7 +28,8 @@ const EMPTY_COLUMN = ( export default class Event extends Component { static contextTypes = { - accounts: PropTypes.array.isRequired + accountsInfo: PropTypes.object.isRequired, + api: PropTypes.object.isRequired } static propTypes = { @@ -38,14 +40,23 @@ export default class Event extends Component { toAddress: PropTypes.string } + state = { + block: null + } + + componentDidMount () { + this.loadBlock(); + } + render () { const { event, fromAddress, toAddress, price, value } = this.props; - const { blockNumber, state, type } = event; + const { block } = this.state; + const { state, type } = event; const cls = `${styles.event} ${styles[state]} ${styles[type.toLowerCase()]}`; return ( - { this.renderBlockNumber(blockNumber) } + { this.renderTimestamp(block) } { this.renderType(type) } { this.renderValue(value) } { this.renderPrice(price) } @@ -55,10 +66,10 @@ export default class Event extends Component { ); } - renderBlockNumber (blockNumber) { + renderTimestamp (block) { return ( - { formatBlockNumber(blockNumber) } + { !block ? ' ' : moment(block.timestamp).fromNow() } ); } @@ -77,8 +88,8 @@ export default class Event extends Component { } renderAddressName (address) { - const { accounts } = this.context; - const account = accounts.find((_account) => _account.address === address); + const { accountsInfo } = this.context; + const account = accountsInfo[address]; if (account && account.name) { return ( @@ -90,7 +101,7 @@ export default class Event extends Component { return (
    - { address } + { formatHash(address) }
    ); } @@ -126,4 +137,19 @@ export default class Event extends Component { ); } + + loadBlock () { + const { api } = this.context; + const { event } = this.props; + + if (!event || !event.blockNumber || event.blockNumber.eq(0)) { + return; + } + + api.eth + .getBlockByNumber(event.blockNumber) + .then((block) => { + this.setState({ block }); + }); + } } diff --git a/js/src/dapps/gavcoin/Events/events.css b/js/src/dapps/gavcoin/Events/events.css index 9be9b37af..cf9184744 100644 --- a/js/src/dapps/gavcoin/Events/events.css +++ b/js/src/dapps/gavcoin/Events/events.css @@ -16,18 +16,20 @@ */ .events { padding: 4em 2em; + text-align: center; } .list { - width: 100%; + margin: 0 auto; border: none; border-spacing: 0; + text-align: left; } .list td { vertical-align: top; - padding: 4px 0.5em; - max-height: 32px; + padding: 0.25em 1em; + max-height: 1.5em; } .event { @@ -38,7 +40,6 @@ .blocknumber, .ethvalue, .gavvalue { - font-family: 'Roboto Mono', monospace; } .blocknumber, diff --git a/js/src/dapps/gavcoin/Events/events.js b/js/src/dapps/gavcoin/Events/events.js index ba71d6541..97dac30bb 100644 --- a/js/src/dapps/gavcoin/Events/events.js +++ b/js/src/dapps/gavcoin/Events/events.js @@ -27,7 +27,7 @@ import styles from './events.css'; export default class Events extends Component { static childContextTypes = { - accounts: PropTypes.array + accountsInfo: PropTypes.object } static contextTypes = { @@ -36,7 +36,7 @@ export default class Events extends Component { } static propTypes = { - accounts: PropTypes.array + accountsInfo: PropTypes.object.isRequired } state = { @@ -84,11 +84,9 @@ export default class Events extends Component { } getChildContext () { - const { accounts } = this.props; + const { accountsInfo } = this.props; - return { - accounts - }; + return { accountsInfo }; } setupFilters () { diff --git a/js/src/dapps/gavcoin/format/index.js b/js/src/dapps/gavcoin/format/index.js index 5e32012d0..ee554bb60 100644 --- a/js/src/dapps/gavcoin/format/index.js +++ b/js/src/dapps/gavcoin/format/index.js @@ -50,3 +50,7 @@ export function formatCoins (amount, decimals = 6) { export function formatEth (eth, decimals = 3) { return api.util.fromWei(eth).toFormat(decimals); } + +export function formatHash (hash) { + return `${hash.substr(0, 10)}...${hash.substr(-8)}`; +} From 290ed3343f52e252b91d49edfdc94b10765c3776 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Sat, 29 Oct 2016 11:06:59 +0000 Subject: [PATCH 068/192] [ci skip] js-precompiled 20161029-110604 --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 8d040eade..870d39fa1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1213,7 +1213,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#93753cb4d50f85eca91e024196235c6f0deb3bd4" +source = "git+https://github.com/ethcore/js-precompiled.git#d35381a1442215c9b21f472c2a644d1ecbe43336" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] From 9e82eeccfeb05fc99bdd6d3ba332c0562b10e586 Mon Sep 17 00:00:00 2001 From: Arkadiy Paronyan Date: Sat, 29 Oct 2016 13:07:06 +0200 Subject: [PATCH 069/192] Snapshot sync improvements (#2960) * Status packet timeout * Snapshot collection state * Pause snapshot download * Updated tests --- sync/src/api.rs | 2 +- sync/src/chain.rs | 186 ++++++++++++++++++++++++++++--------- sync/src/snapshot.rs | 5 +- sync/src/tests/chain.rs | 5 + sync/src/tests/helpers.rs | 14 ++- sync/src/tests/snapshot.rs | 18 ++-- 6 files changed, 174 insertions(+), 56 deletions(-) diff --git a/sync/src/api.rs b/sync/src/api.rs index 67a81237a..abe18c3cd 100644 --- a/sync/src/api.rs +++ b/sync/src/api.rs @@ -58,7 +58,7 @@ impl Default for SyncConfig { network_id: U256::from(1), subprotocol_name: *b"eth", fork_block: None, - warp_sync: true, + warp_sync: false, } } } diff --git a/sync/src/chain.rs b/sync/src/chain.rs index d18adb6ea..06949682b 100644 --- a/sync/src/chain.rs +++ b/sync/src/chain.rs @@ -123,6 +123,7 @@ const MAX_NEW_BLOCK_AGE: BlockNumber = 20; const MAX_TRANSACTION_SIZE: usize = 300*1024; // Min number of blocks to be behind for a snapshot sync const SNAPSHOT_RESTORE_THRESHOLD: BlockNumber = 100000; +const SNAPSHOT_MIN_PEERS: usize = 3; const STATUS_PACKET: u8 = 0x00; const NEW_BLOCK_HASHES_PACKET: u8 = 0x01; @@ -147,21 +148,27 @@ const SNAPSHOT_DATA_PACKET: u8 = 0x14; pub const SNAPSHOT_SYNC_PACKET_COUNT: u8 = 0x15; -const HEADERS_TIMEOUT_SEC: f64 = 15f64; -const BODIES_TIMEOUT_SEC: f64 = 10f64; -const RECEIPTS_TIMEOUT_SEC: f64 = 10f64; -const FORK_HEADER_TIMEOUT_SEC: f64 = 3f64; -const SNAPSHOT_MANIFEST_TIMEOUT_SEC: f64 = 3f64; -const SNAPSHOT_DATA_TIMEOUT_SEC: f64 = 60f64; +const MAX_SNAPSHOT_CHUNKS_DOWNLOAD_AHEAD: usize = 3; + +const WAIT_PEERS_TIMEOUT_SEC: u64 = 5; +const STATUS_TIMEOUT_SEC: u64 = 5; +const HEADERS_TIMEOUT_SEC: u64 = 15; +const BODIES_TIMEOUT_SEC: u64 = 10; +const RECEIPTS_TIMEOUT_SEC: u64 = 10; +const FORK_HEADER_TIMEOUT_SEC: u64 = 3; +const SNAPSHOT_MANIFEST_TIMEOUT_SEC: u64 = 3; +const SNAPSHOT_DATA_TIMEOUT_SEC: u64 = 60; #[derive(Copy, Clone, Eq, PartialEq, Debug)] /// Sync state pub enum SyncState { - /// Waiting for pv64 peers to start snapshot syncing + /// Collecting enough peers to start syncing. + WaitingPeers, + /// Waiting for snapshot manifest download SnapshotManifest, /// Downloading snapshot data SnapshotData, - /// Waiting for snapshot restoration to complete + /// Waiting for snapshot restoration progress. SnapshotWaiting, /// Downloading new blocks Blocks, @@ -276,7 +283,7 @@ struct PeerInfo { /// Holds requested snapshot chunk hash if any. asking_snapshot_data: Option, /// Request timestamp - ask_time: f64, + ask_time: u64, /// Holds a set of transactions recently sent to this peer to avoid spamming. last_sent_transactions: HashSet, /// Pending request is expired and result should be ignored @@ -324,10 +331,13 @@ pub struct ChainSync { network_id: U256, /// Optional fork block to check fork_block: Option<(BlockNumber, H256)>, - /// Snapshot sync allowed. - snapshot_sync_enabled: bool, /// Snapshot downloader. snapshot: Snapshot, + /// Connected peers pending Status message. + /// Value is request timestamp. + handshaking_peers: HashMap, + /// Sync start timestamp. Measured when first peer is connected + sync_start_time: Option, } type RlpResponseResult = Result, PacketDecodeError>; @@ -337,20 +347,21 @@ impl ChainSync { pub fn new(config: SyncConfig, chain: &BlockChainClient) -> ChainSync { let chain_info = chain.chain_info(); let mut sync = ChainSync { - state: SyncState::Idle, + state: if config.warp_sync { SyncState::WaitingPeers } else { SyncState::Idle }, starting_block: chain.chain_info().best_block_number, highest_block: None, peers: HashMap::new(), + handshaking_peers: HashMap::new(), active_peers: HashSet::new(), new_blocks: BlockDownloader::new(false, &chain_info.best_block_hash, chain_info.best_block_number), old_blocks: None, last_sent_block_number: 0, network_id: config.network_id, fork_block: config.fork_block, - snapshot_sync_enabled: config.warp_sync, snapshot: Snapshot::new(), + sync_start_time: None, }; - sync.init_downloaders(chain); + sync.update_targets(chain); sync } @@ -442,20 +453,67 @@ impl ChainSync { self.active_peers.remove(&peer_id); } - fn start_snapshot_sync(&mut self, io: &mut SyncIo, peer_id: PeerId) { + fn maybe_start_snapshot_sync(&mut self, io: &mut SyncIo) { + if self.state != SyncState::WaitingPeers { + return; + } + let best_block = io.chain().chain_info().best_block_number; + + let (best_hash, max_peers, snapshot_peers) = { + //collect snapshot infos from peers + let snapshots = self.peers.iter() + .filter(|&(_, p)| p.is_allowed() && p.snapshot_number.map_or(false, |sn| best_block < sn && (sn - best_block) > SNAPSHOT_RESTORE_THRESHOLD)) + .filter_map(|(p, peer)| peer.snapshot_hash.map(|hash| (p, hash.clone()))); + + let mut snapshot_peers = HashMap::new(); + let mut max_peers: usize = 0; + let mut best_hash = None; + for (p, hash) in snapshots { + let peers = snapshot_peers.entry(hash).or_insert_with(Vec::new); + peers.push(*p); + if peers.len() > max_peers { + max_peers = peers.len(); + best_hash = Some(hash); + } + } + (best_hash, max_peers, snapshot_peers) + }; + + let timeout = self.sync_start_time.map_or(false, |t| ((time::precise_time_ns() - t) / 1_000_000_000) > WAIT_PEERS_TIMEOUT_SEC); + + if let (Some(hash), Some(peers)) = (best_hash, best_hash.map_or(None, |h| snapshot_peers.get(&h))) { + if max_peers >= SNAPSHOT_MIN_PEERS { + trace!(target: "sync", "Starting confirmed snapshot sync {:?} with {:?}", hash, peers); + self.start_snapshot_sync(io, peers); + } else if timeout { + trace!(target: "sync", "Starting unconfirmed snapshot sync {:?} with {:?}", hash, peers); + self.start_snapshot_sync(io, peers); + } + } else if timeout { + trace!(target: "sync", "No snapshots found, starting full sync"); + self.state = SyncState::Idle; + self.continue_sync(io); + } + } + + fn start_snapshot_sync(&mut self, io: &mut SyncIo, peers: &[PeerId]) { self.snapshot.clear(); - self.request_snapshot_manifest(io, peer_id); + for p in peers { + if self.peers.get(p).map_or(false, |p| p.asking == PeerAsking::Nothing) { + self.request_snapshot_manifest(io, *p); + } + } self.state = SyncState::SnapshotManifest; } /// Restart sync disregarding the block queue status. May end up re-downloading up to QUEUE_SIZE blocks pub fn restart(&mut self, io: &mut SyncIo) { - self.init_downloaders(io.chain()); + self.update_targets(io.chain()); self.reset_and_continue(io); } - /// Restart sync after bad block has been detected. May end up re-downloading up to QUEUE_SIZE blocks - fn init_downloaders(&mut self, chain: &BlockChainClient) { + /// Update sync after the blockchain has been changed externally. + pub fn update_targets(&mut self, chain: &BlockChainClient) { // Do not assume that the block queue/chain still has our last_imported_block let chain = chain.chain_info(); self.new_blocks = BlockDownloader::new(false, &chain.best_block_hash, chain.best_block_number); @@ -475,6 +533,7 @@ impl ChainSync { /// Called by peer to report status fn on_peer_status(&mut self, io: &mut SyncIo, peer_id: PeerId, r: &UntrustedRlp) -> Result<(), PacketDecodeError> { + self.handshaking_peers.remove(&peer_id); let protocol_version: u8 = try!(r.val_at(0)); let warp_protocol = io.protocol_version(&WARP_SYNC_PROTOCOL_ID, peer_id) != 0; let peer = PeerInfo { @@ -486,7 +545,7 @@ impl ChainSync { asking: PeerAsking::Nothing, asking_blocks: Vec::new(), asking_hash: None, - ask_time: 0f64, + ask_time: 0, last_sent_transactions: HashSet::new(), expired: false, confirmation: if self.fork_block.is_none() { ForkConfirmation::Confirmed } else { ForkConfirmation::Unconfirmed }, @@ -496,7 +555,12 @@ impl ChainSync { block_set: None, }; - trace!(target: "sync", "New peer {} (protocol: {}, network: {:?}, difficulty: {:?}, latest:{}, genesis:{})", peer_id, peer.protocol_version, peer.network_id, peer.difficulty, peer.latest_hash, peer.genesis); + if self.sync_start_time.is_none() { + self.sync_start_time = Some(time::precise_time_ns()); + } + + trace!(target: "sync", "New peer {} (protocol: {}, network: {:?}, difficulty: {:?}, latest:{}, genesis:{}, snapshot:{:?})", + peer_id, peer.protocol_version, peer.network_id, peer.difficulty, peer.latest_hash, peer.genesis, peer.snapshot_number); if io.is_expired() { trace!(target: "sync", "Status packet from expired session {}:{}", peer_id, io.peer_info(peer_id)); return Ok(()); @@ -578,7 +642,7 @@ impl ChainSync { } let item_count = r.item_count(); trace!(target: "sync", "{} -> BlockHeaders ({} entries), state = {:?}, set = {:?}", peer_id, item_count, self.state, block_set); - if self.state == SyncState::Idle && self.old_blocks.is_none() { + if (self.state == SyncState::Idle || self.state == SyncState::WaitingPeers) && self.old_blocks.is_none() { trace!(target: "sync", "Ignored unexpected block headers"); self.continue_sync(io); return Ok(()); @@ -875,7 +939,7 @@ impl ChainSync { } self.clear_peer_download(peer_id); if !self.reset_peer_asking(peer_id, PeerAsking::SnapshotManifest) || self.state != SyncState::SnapshotManifest { - trace!(target: "sync", "{}: Ignored unexpected manifest", peer_id); + trace!(target: "sync", "{}: Ignored unexpected/expired manifest", peer_id); self.continue_sync(io); return Ok(()); } @@ -918,7 +982,7 @@ impl ChainSync { match io.snapshot_service().status() { RestorationStatus::Inactive | RestorationStatus::Failed => { trace!(target: "sync", "{}: Snapshot restoration aborted", peer_id); - self.state = SyncState::Idle; + self.state = SyncState::WaitingPeers; self.snapshot.clear(); self.continue_sync(io); return Ok(()); @@ -960,6 +1024,7 @@ impl ChainSync { /// Called by peer when it is disconnecting pub fn on_peer_aborting(&mut self, io: &mut SyncIo, peer: PeerId) { trace!(target: "sync", "== Disconnecting {}: {}", peer, io.peer_info(peer)); + self.handshaking_peers.remove(&peer); if self.peers.contains_key(&peer) { debug!(target: "sync", "Disconnected {}", peer); self.clear_peer_download(peer); @@ -975,12 +1040,14 @@ impl ChainSync { if let Err(e) = self.send_status(io, peer) { debug!(target:"sync", "Error sending status request: {:?}", e); io.disable_peer(peer); + } else { + self.handshaking_peers.insert(peer, time::precise_time_ns()); } } /// Resume downloading fn continue_sync(&mut self, io: &mut SyncIo) { - if self.state != SyncState::Waiting && self.state != SyncState::SnapshotWaiting + if (self.state == SyncState::Blocks || self.state == SyncState::NewBlocks || self.state == SyncState::Idle) && !self.peers.values().any(|p| p.asking != PeerAsking::Nothing && p.block_set != Some(BlockSet::OldBlocks) && p.can_sync()) { self.complete_sync(io); } @@ -1040,11 +1107,9 @@ impl ChainSync { let higher_difficulty = peer_difficulty.map_or(true, |pd| pd > syncing_difficulty); if force || self.state == SyncState::NewBlocks || higher_difficulty || self.old_blocks.is_some() { match self.state { - SyncState::Idle if self.snapshot_sync_enabled - && chain_info.best_block_number < peer_snapshot_number - && (peer_snapshot_number - chain_info.best_block_number) > SNAPSHOT_RESTORE_THRESHOLD => { - trace!(target: "sync", "Starting snapshot sync: {} vs {}", peer_snapshot_number, chain_info.best_block_number); - self.start_snapshot_sync(io, peer_id); + SyncState::WaitingPeers => { + trace!(target: "sync", "Checking snapshot sync: {} vs {}", peer_snapshot_number, chain_info.best_block_number); + self.maybe_start_snapshot_sync(io); }, SyncState::Idle | SyncState::Blocks | SyncState::NewBlocks => { if io.chain().queue_info().is_full() { @@ -1070,6 +1135,13 @@ impl ChainSync { } }, SyncState::SnapshotData => { + if let RestorationStatus::Ongoing { state_chunks: _, block_chunks: _, state_chunks_done, block_chunks_done, } = io.snapshot_service().status() { + if self.snapshot.done_chunks() - (state_chunks_done + block_chunks_done) as usize > MAX_SNAPSHOT_CHUNKS_DOWNLOAD_AHEAD { + trace!(target: "sync", "Snapshot queue full, pausing sync"); + self.state = SyncState::SnapshotWaiting; + return; + } + } if peer_snapshot_hash.is_some() && peer_snapshot_hash == self.snapshot.snapshot_hash() { self.request_snapshot_data(io, peer_id); } @@ -1253,7 +1325,7 @@ impl ChainSync { warn!(target:"sync", "Asking {:?} while requesting {:?}", peer.asking, asking); } peer.asking = asking; - peer.ask_time = time::precise_time_s(); + peer.ask_time = time::precise_time_ns(); let result = if packet_id >= ETH_PACKET_COUNT { sync.send_protocol(WARP_SYNC_PROTOCOL_ID, peer_id, packet_id, packet) } else { @@ -1590,17 +1662,18 @@ impl ChainSync { #[cfg_attr(feature="dev", allow(match_same_arms))] pub fn maintain_peers(&mut self, io: &mut SyncIo) { - let tick = time::precise_time_s(); + let tick = time::precise_time_ns(); let mut aborting = Vec::new(); for (peer_id, peer) in &self.peers { + let elapsed = (tick - peer.ask_time) / 1_000_000_000; let timeout = match peer.asking { - PeerAsking::BlockHeaders => (tick - peer.ask_time) > HEADERS_TIMEOUT_SEC, - PeerAsking::BlockBodies => (tick - peer.ask_time) > BODIES_TIMEOUT_SEC, - PeerAsking::BlockReceipts => (tick - peer.ask_time) > RECEIPTS_TIMEOUT_SEC, + PeerAsking::BlockHeaders => elapsed > HEADERS_TIMEOUT_SEC, + PeerAsking::BlockBodies => elapsed > BODIES_TIMEOUT_SEC, + PeerAsking::BlockReceipts => elapsed > RECEIPTS_TIMEOUT_SEC, PeerAsking::Nothing => false, - PeerAsking::ForkHeader => (tick - peer.ask_time) > FORK_HEADER_TIMEOUT_SEC, - PeerAsking::SnapshotManifest => (tick - peer.ask_time) > SNAPSHOT_MANIFEST_TIMEOUT_SEC, - PeerAsking::SnapshotData => (tick - peer.ask_time) > SNAPSHOT_DATA_TIMEOUT_SEC, + PeerAsking::ForkHeader => elapsed > FORK_HEADER_TIMEOUT_SEC, + PeerAsking::SnapshotManifest => elapsed > SNAPSHOT_MANIFEST_TIMEOUT_SEC, + PeerAsking::SnapshotData => elapsed > SNAPSHOT_DATA_TIMEOUT_SEC, }; if timeout { trace!(target:"sync", "Timeout {}", peer_id); @@ -1611,16 +1684,42 @@ impl ChainSync { for p in aborting { self.on_peer_aborting(io, p); } + + // Check for handshake timeouts + for (peer, ask_time) in &self.handshaking_peers { + let elapsed = (tick - ask_time) / 1_000_000_000; + if elapsed > STATUS_TIMEOUT_SEC { + trace!(target:"sync", "Status timeout {}", peer); + io.disconnect_peer(*peer); + } + } } fn check_resume(&mut self, io: &mut SyncIo) { if self.state == SyncState::Waiting && !io.chain().queue_info().is_full() && self.state == SyncState::Waiting { self.state = SyncState::Blocks; self.continue_sync(io); - } else if self.state == SyncState::SnapshotWaiting && io.snapshot_service().status() == RestorationStatus::Inactive { - trace!(target:"sync", "Snapshot restoration is complete"); - self.restart(io); - self.continue_sync(io); + } else if self.state == SyncState::SnapshotWaiting { + match io.snapshot_service().status() { + RestorationStatus::Inactive => { + trace!(target:"sync", "Snapshot restoration is complete"); + self.restart(io); + self.continue_sync(io); + }, + RestorationStatus::Ongoing { state_chunks: _, block_chunks: _, state_chunks_done, block_chunks_done, } => { + if !self.snapshot.is_complete() && self.snapshot.done_chunks() - (state_chunks_done + block_chunks_done) as usize <= MAX_SNAPSHOT_CHUNKS_DOWNLOAD_AHEAD { + trace!(target:"sync", "Resuming snapshot sync"); + self.state = SyncState::SnapshotData; + self.continue_sync(io); + } + }, + RestorationStatus::Failed => { + trace!(target: "sync", "Snapshot restoration aborted"); + self.state = SyncState::WaitingPeers; + self.snapshot.clear(); + self.continue_sync(io); + }, + } } } @@ -1828,6 +1927,7 @@ impl ChainSync { /// Maintain other peers. Send out any new blocks and transactions pub fn maintain_sync(&mut self, io: &mut SyncIo) { + self.maybe_start_snapshot_sync(io); self.check_resume(io); } @@ -2050,7 +2150,7 @@ mod tests { asking: PeerAsking::Nothing, asking_blocks: Vec::new(), asking_hash: None, - ask_time: 0f64, + ask_time: 0, last_sent_transactions: HashSet::new(), expired: false, confirmation: super::ForkConfirmation::Confirmed, diff --git a/sync/src/snapshot.rs b/sync/src/snapshot.rs index ca9adf220..9f4262105 100644 --- a/sync/src/snapshot.rs +++ b/sync/src/snapshot.rs @@ -113,7 +113,7 @@ impl Snapshot { } pub fn done_chunks(&self) -> usize { - self.total_chunks() - self.completed_chunks.len() + self.completed_chunks.len() } pub fn is_complete(&self) -> bool { @@ -165,6 +165,7 @@ mod test { let mut snapshot = Snapshot::new(); let (manifest, mhash, state_chunks, block_chunks) = test_manifest(); snapshot.reset_to(&manifest, &mhash); + assert_eq!(snapshot.done_chunks(), 0); assert!(snapshot.validate_chunk(&H256::random().to_vec()).is_err()); let requested: Vec = (0..40).map(|_| snapshot.needed_chunk().unwrap()).collect(); @@ -194,6 +195,8 @@ mod test { } assert!(snapshot.is_complete()); + assert_eq!(snapshot.done_chunks(), 40); + assert_eq!(snapshot.done_chunks(), snapshot.total_chunks()); assert_eq!(snapshot.snapshot_hash(), Some(manifest.into_rlp().sha3())); } } diff --git a/sync/src/tests/chain.rs b/sync/src/tests/chain.rs index c54529beb..17c051162 100644 --- a/sync/src/tests/chain.rs +++ b/sync/src/tests/chain.rs @@ -18,6 +18,7 @@ use util::*; use ethcore::client::{TestBlockChainClient, BlockChainClient, BlockID, EachBlockWith}; use chain::{SyncState}; use super::helpers::*; +use SyncConfig; #[test] fn two_peers() { @@ -156,6 +157,10 @@ fn restart() { fn status_empty() { let net = TestNet::new(2); assert_eq!(net.peer(0).sync.read().status().state, SyncState::Idle); + let mut config = SyncConfig::default(); + config.warp_sync = true; + let net = TestNet::new_with_config(2, config); + assert_eq!(net.peer(0).sync.read().status().state, SyncState::WaitingPeers); } #[test] diff --git a/sync/src/tests/helpers.rs b/sync/src/tests/helpers.rs index 202ab4f17..d5e07a936 100644 --- a/sync/src/tests/helpers.rs +++ b/sync/src/tests/helpers.rs @@ -127,20 +127,24 @@ pub struct TestNet { impl TestNet { pub fn new(n: usize) -> TestNet { - Self::new_with_fork(n, None) + Self::new_with_config(n, SyncConfig::default()) } pub fn new_with_fork(n: usize, fork: Option<(BlockNumber, H256)>) -> TestNet { + let mut config = SyncConfig::default(); + config.fork_block = fork; + Self::new_with_config(n, config) + } + + pub fn new_with_config(n: usize, config: SyncConfig) -> TestNet { let mut net = TestNet { peers: Vec::new(), started: false, }; for _ in 0..n { let chain = TestBlockChainClient::new(); - let mut config = SyncConfig::default(); - config.fork_block = fork; let ss = Arc::new(TestSnapshotService::new()); - let sync = ChainSync::new(config, &chain); + let sync = ChainSync::new(config.clone(), &chain); net.peers.push(TestPeer { sync: RwLock::new(sync), snapshot_service: ss, @@ -164,7 +168,7 @@ impl TestNet { for client in 0..self.peers.len() { if peer != client { let mut p = self.peers.get_mut(peer).unwrap(); - p.sync.write().restart(&mut TestIo::new(&mut p.chain, &p.snapshot_service, &mut p.queue, Some(client as PeerId))); + p.sync.write().update_targets(&mut p.chain); p.sync.write().on_peer_connected(&mut TestIo::new(&mut p.chain, &p.snapshot_service, &mut p.queue, Some(client as PeerId)), client as PeerId); } } diff --git a/sync/src/tests/snapshot.rs b/sync/src/tests/snapshot.rs index 813513e84..5d0b21b47 100644 --- a/sync/src/tests/snapshot.rs +++ b/sync/src/tests/snapshot.rs @@ -19,6 +19,7 @@ use ethcore::snapshot::{SnapshotService, ManifestData, RestorationStatus}; use ethcore::header::BlockNumber; use ethcore::client::{EachBlockWith}; use super::helpers::*; +use SyncConfig; pub struct TestSnapshotService { manifest: Option, @@ -122,11 +123,16 @@ impl SnapshotService for TestSnapshotService { #[test] fn snapshot_sync() { ::env_logger::init().ok(); - let mut net = TestNet::new(2); - net.peer_mut(0).snapshot_service = Arc::new(TestSnapshotService::new_with_snapshot(16, H256::new(), 500000)); - net.peer_mut(0).chain.add_blocks(1, EachBlockWith::Nothing); - net.sync_steps(19); // status + manifest + chunks - assert_eq!(net.peer(1).snapshot_service.state_restoration_chunks.lock().len(), net.peer(0).snapshot_service.manifest.as_ref().unwrap().state_hashes.len()); - assert_eq!(net.peer(1).snapshot_service.block_restoration_chunks.lock().len(), net.peer(0).snapshot_service.manifest.as_ref().unwrap().block_hashes.len()); + let mut config = SyncConfig::default(); + config.warp_sync = true; + let mut net = TestNet::new_with_config(5, config); + let snapshot_service = Arc::new(TestSnapshotService::new_with_snapshot(16, H256::new(), 500000)); + for i in 0..4 { + net.peer_mut(i).snapshot_service = snapshot_service.clone(); + net.peer_mut(i).chain.add_blocks(1, EachBlockWith::Nothing); + } + net.sync_steps(50); + assert_eq!(net.peer(4).snapshot_service.state_restoration_chunks.lock().len(), net.peer(0).snapshot_service.manifest.as_ref().unwrap().state_hashes.len()); + assert_eq!(net.peer(4).snapshot_service.block_restoration_chunks.lock().len(), net.peer(0).snapshot_service.manifest.as_ref().unwrap().block_hashes.len()); } From f17cad9dff83a8d9a19af0b294ecb5ec8f456144 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Sat, 29 Oct 2016 13:08:49 +0200 Subject: [PATCH 070/192] Fixing clippy warnings 2 (#2961) --- ethcore/src/miner/banning_queue.rs | 4 ++-- ethcore/src/miner/transaction_queue.rs | 1 + ethcore/src/snapshot/account.rs | 2 +- parity/informant.rs | 2 +- sync/src/chain.rs | 2 +- util/src/trie/triedb.rs | 2 +- 6 files changed, 7 insertions(+), 6 deletions(-) diff --git a/ethcore/src/miner/banning_queue.rs b/ethcore/src/miner/banning_queue.rs index 0329503bf..f127dc7e8 100644 --- a/ethcore/src/miner/banning_queue.rs +++ b/ethcore/src/miner/banning_queue.rs @@ -130,7 +130,7 @@ impl BanningTransactionQueue { // Ban sender let sender_banned = self.ban_sender(sender); // Ban recipient and codehash - let is_banned = sender_banned || match transaction.action { + let recipient_or_code_banned = match transaction.action { Action::Call(recipient) => { self.ban_recipient(recipient) }, @@ -138,7 +138,7 @@ impl BanningTransactionQueue { self.ban_codehash(transaction.data.sha3()) }, }; - is_banned + sender_banned || recipient_or_code_banned }, None => false, } diff --git a/ethcore/src/miner/transaction_queue.rs b/ethcore/src/miner/transaction_queue.rs index f8baf8989..51c1863f6 100644 --- a/ethcore/src/miner/transaction_queue.rs +++ b/ethcore/src/miner/transaction_queue.rs @@ -110,6 +110,7 @@ impl PartialOrd for TransactionOrigin { } impl Ord for TransactionOrigin { + #[cfg_attr(feature="dev", allow(match_same_arms))] fn cmp(&self, other: &TransactionOrigin) -> Ordering { if *other == *self { return Ordering::Equal; diff --git a/ethcore/src/snapshot/account.rs b/ethcore/src/snapshot/account.rs index 7e4585365..38a4028e1 100644 --- a/ethcore/src/snapshot/account.rs +++ b/ethcore/src/snapshot/account.rs @@ -178,7 +178,7 @@ impl Account { CodeState::Hash => { let code_hash = try!(rlp.val_at(3)); if let Some(code) = code_map.get(&code_hash) { - acct_db.emplace(code_hash.clone(), DBValue::from_slice(&code)); + acct_db.emplace(code_hash.clone(), DBValue::from_slice(code)); } (code_hash, None) diff --git a/parity/informant.rs b/parity/informant.rs index 9b0b6c754..ae59cb9d0 100644 --- a/parity/informant.rs +++ b/parity/informant.rs @@ -184,7 +184,7 @@ impl ChainNotify for Informant { let ripe = Instant::now() > *last_import + Duration::from_secs(1) && !importing; let txs_imported = imported.iter() .take(imported.len() - if ripe {1} else {0}) - .filter_map(|h| self.client.block(BlockID::Hash(h.clone()))) + .filter_map(|h| self.client.block(BlockID::Hash(*h))) .map(|b| BlockView::new(&b).transactions_count()) .sum(); diff --git a/sync/src/chain.rs b/sync/src/chain.rs index 06949682b..72df47c07 100644 --- a/sync/src/chain.rs +++ b/sync/src/chain.rs @@ -1509,7 +1509,7 @@ impl ChainSync { } trace!(target: "sync", "{} -> GetNodeData: return {} entries", peer_id, added); let mut rlp = RlpStream::new_list(added); - for d in data.into_iter() { + for d in data { rlp.append(&d); } Ok(Some((NODE_DATA_PACKET, rlp))) diff --git a/util/src/trie/triedb.rs b/util/src/trie/triedb.rs index cd8c9939a..d929c9d68 100644 --- a/util/src/trie/triedb.rs +++ b/util/src/trie/triedb.rs @@ -133,7 +133,7 @@ impl<'db> TrieDB<'db> { } /// Get the data of the root node. - fn root_data<'a, R: 'a + Recorder>(&self, r: &'a mut R) -> super::Result { + fn root_data(&self, r: &mut R) -> super::Result { self.db.get(self.root).ok_or_else(|| Box::new(TrieError::InvalidStateRoot(*self.root))) .map(|node| { r.record(self.root, &*node, 0); node }) } From 227bf4b8c5c69d56a2a37b1404f98f1f0787e513 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Sat, 29 Oct 2016 13:09:16 +0200 Subject: [PATCH 071/192] Run cargo with verbose flag when testing (#2943) * Run cargo with verbose flag when testing * use full verbose flag --- test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test.sh b/test.sh index 327d83deb..44bffa7d9 100755 --- a/test.sh +++ b/test.sh @@ -2,7 +2,7 @@ # Running Parity Full Test Sute FEATURES="json-tests" -OPTIONS="--release" +OPTIONS="--verbose --release" case $1 in --no-json) From 1a258fc0a732c780c8fb7f2344f1eb2f5a236330 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Sat, 29 Oct 2016 11:21:07 +0000 Subject: [PATCH 072/192] [ci skip] js-precompiled 20161029-112006 --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 870d39fa1..b680687e3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1213,7 +1213,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#d35381a1442215c9b21f472c2a644d1ecbe43336" +source = "git+https://github.com/ethcore/js-precompiled.git#18430becb29cdde5ee3f62ca81c5910e553b5b9b" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] From 598679000c3cee386e4023001e2fa02828419d70 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Sat, 29 Oct 2016 11:29:09 +0000 Subject: [PATCH 073/192] [ci skip] js-precompiled 20161029-112814 --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index b680687e3..6dba19a11 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1213,7 +1213,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#18430becb29cdde5ee3f62ca81c5910e553b5b9b" +source = "git+https://github.com/ethcore/js-precompiled.git#897a9afd0a4aa500e2e29e5d978618446a6b6086" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] From fe02f93e4cf6b63ab2a64ba8ffa6a128276dfee9 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Sat, 29 Oct 2016 11:42:16 +0000 Subject: [PATCH 074/192] [ci skip] js-precompiled 20161029-114119 --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 6dba19a11..3df83fb84 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1213,7 +1213,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#897a9afd0a4aa500e2e29e5d978618446a6b6086" +source = "git+https://github.com/ethcore/js-precompiled.git#b65eb2375043a03d425a559910c1a4e5ab71d7d6" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] From 144930f12cf3541d6d3bf04792eaf16dbfc88152 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Sat, 29 Oct 2016 15:31:13 +0200 Subject: [PATCH 075/192] Updating ws-rs (#2962) --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3df83fb84..845935e56 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -516,7 +516,7 @@ dependencies = [ "parity-ui 1.4.0", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "ws 0.5.2 (git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable)", + "ws 0.5.3 (git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable)", ] [[package]] @@ -1884,8 +1884,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "ws" -version = "0.5.2" -source = "git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable#00bd2134b07b4bc8ea47b7f6c7afce16bbe34c8f" +version = "0.5.3" +source = "git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable#b4538aec81e4c19519469127cece5fe62c88bb04" dependencies = [ "bytes 0.4.0-dev (git+https://github.com/carllerche/bytes)", "httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2099,7 +2099,7 @@ dependencies = [ "checksum webpki 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "813503a5985585e0812d430cd1328ee322f47f66629c8ed4ecab939cf9e92f91" "checksum winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4dfaaa8fbdaa618fa6914b59b2769d690dd7521920a18d84b42d254678dd5fd4" "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" -"checksum ws 0.5.2 (git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable)" = "" +"checksum ws 0.5.3 (git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable)" = "" "checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" "checksum xml-rs 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "65e74b96bd3179209dc70a980da6df843dff09e46eee103a0376c0949257e3ef" "checksum xmltree 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "472a9d37c7c53ab2391161df5b89b1f3bf76dab6ab150d7941ecbdd832282082" From 49a7b84f14d713a8fd1756cfe9f16d786641ad64 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Sat, 29 Oct 2016 15:32:10 +0200 Subject: [PATCH 076/192] Pass gas & gasPrice to token transfers (#2964) --- js/src/dapps/gavcoin/Actions/ActionBuyIn/actionBuyIn.js | 2 +- js/src/modals/Transfer/transfer.js | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/js/src/dapps/gavcoin/Actions/ActionBuyIn/actionBuyIn.js b/js/src/dapps/gavcoin/Actions/ActionBuyIn/actionBuyIn.js index 42de205c1..fe67c3739 100644 --- a/js/src/dapps/gavcoin/Actions/ActionBuyIn/actionBuyIn.js +++ b/js/src/dapps/gavcoin/Actions/ActionBuyIn/actionBuyIn.js @@ -43,7 +43,7 @@ export default class ActionBuyIn extends Component { accountError: ERRORS.invalidAccount, amount: 0, amountError: ERRORS.invalidAmount, - maxPrice: api.util.fromWei(this.props.price.mul(1.2)).toString(), + maxPrice: api.util.fromWei(this.props.price.mul(1.2)).toFixed(0), maxPriceError: null, sending: false, complete: false diff --git a/js/src/modals/Transfer/transfer.js b/js/src/modals/Transfer/transfer.js index 2217d0e9c..0ffad525c 100644 --- a/js/src/modals/Transfer/transfer.js +++ b/js/src/modals/Transfer/transfer.js @@ -420,13 +420,15 @@ export default class Transfer extends Component { _sendToken () { const { account, balance } = this.props; - const { recipient, value, tag } = this.state; + const { gas, gasPrice, recipient, value, tag } = this.state; const token = balance.tokens.find((balance) => balance.token.tag === tag).token; return token.contract.instance.transfer .postTransaction({ from: account.address, - to: token.address + to: token.address, + gas, + gasPrice }, [ recipient, new BigNumber(value).mul(token.format).toFixed(0) @@ -483,7 +485,7 @@ export default class Transfer extends Component { to: token.address }, [ recipient, - new BigNumber(value || 0).mul(token.format).toString() + new BigNumber(value || 0).mul(token.format).toFixed(0) ]); } From 3a6c1c377c9a57d11d890cfecdff0f41e6b8f849 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Sat, 29 Oct 2016 13:34:58 +0000 Subject: [PATCH 077/192] [ci skip] js-precompiled 20161029-133354 --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 845935e56..62fa3d984 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1213,7 +1213,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#b65eb2375043a03d425a559910c1a4e5ab71d7d6" +source = "git+https://github.com/ethcore/js-precompiled.git#39fa4ad1e1775e8f4efed7a94fa21318f8053091" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] From 99d6d87e78eb960fe9bb17052f95ec521b46cd58 Mon Sep 17 00:00:00 2001 From: Arkadiy Paronyan Date: Sat, 29 Oct 2016 17:38:34 +0200 Subject: [PATCH 078/192] Discovery performance optimization (#2972) --- util/network/ | 0 util/network/src/discovery.rs | 20 +++++++++++++------- util/network/src/host.rs | 2 +- 3 files changed, 14 insertions(+), 8 deletions(-) create mode 100644 util/network/ diff --git a/util/network/ b/util/network/ new file mode 100644 index 000000000..e69de29bb diff --git a/util/network/src/discovery.rs b/util/network/src/discovery.rs index 595ac7605..d9cd7a4d7 100644 --- a/util/network/src/discovery.rs +++ b/util/network/src/discovery.rs @@ -57,6 +57,7 @@ pub struct NodeEntry { pub struct BucketEntry { pub address: NodeEntry, + pub id_hash: H256, pub timeout: Option, } @@ -85,6 +86,7 @@ struct Datagramm { pub struct Discovery { id: NodeId, + id_hash: H256, secret: Secret, public_endpoint: NodeEndpoint, udp_socket: UdpSocket, @@ -109,6 +111,7 @@ impl Discovery { let socket = UdpSocket::bound(&listen).expect("Error binding UDP socket"); Discovery { id: key.public().clone(), + id_hash: key.public().sha3(), secret: key.secret().clone(), public_endpoint: public, token: token, @@ -150,8 +153,9 @@ impl Discovery { fn update_node(&mut self, e: NodeEntry) { trace!(target: "discovery", "Inserting {:?}", &e); + let id_hash = e.id.sha3(); let ping = { - let mut bucket = self.node_buckets.get_mut(Discovery::distance(&self.id, &e.id) as usize).unwrap(); + let mut bucket = self.node_buckets.get_mut(Discovery::distance(&self.id_hash, &id_hash) as usize).unwrap(); let updated = if let Some(node) = bucket.nodes.iter_mut().find(|n| n.address.id == e.id) { node.address = e.clone(); node.timeout = None; @@ -159,7 +163,7 @@ impl Discovery { } else { false }; if !updated { - bucket.nodes.push_front(BucketEntry { address: e, timeout: None }); + bucket.nodes.push_front(BucketEntry { address: e, timeout: None, id_hash: id_hash, }); } if bucket.nodes.len() > BUCKET_SIZE { @@ -174,7 +178,7 @@ impl Discovery { } fn clear_ping(&mut self, id: &NodeId) { - let mut bucket = self.node_buckets.get_mut(Discovery::distance(&self.id, id) as usize).unwrap(); + let mut bucket = self.node_buckets.get_mut(Discovery::distance(&self.id_hash, &id.sha3()) as usize).unwrap(); if let Some(node) = bucket.nodes.iter_mut().find(|n| &n.address.id == id) { node.timeout = None; } @@ -224,8 +228,8 @@ impl Discovery { self.discovery_round += 1; } - fn distance(a: &NodeId, b: &NodeId) -> u32 { - let d = a.sha3() ^ b.sha3(); + fn distance(a: &H256, b: &H256) -> u32 { + let d = *a ^ *b; let mut ret:u32 = 0; for i in 0..32 { let mut v: u8 = d[i]; @@ -279,11 +283,12 @@ impl Discovery { fn nearest_node_entries(target: &NodeId, buckets: &[NodeBucket]) -> Vec { let mut found: BTreeMap> = BTreeMap::new(); let mut count = 0; + let target_hash = target.sha3(); // Sort nodes by distance to target for bucket in buckets { for node in &bucket.nodes { - let distance = Discovery::distance(target, &node.address.id); + let distance = Discovery::distance(&target_hash, &node.id_hash); found.entry(distance).or_insert_with(Vec::new).push(&node.address); if count == BUCKET_SIZE { // delete the most distant element @@ -626,7 +631,8 @@ mod tests { for _ in 0..(16 + 10) { buckets[0].nodes.push_back(BucketEntry { address: NodeEntry { id: NodeId::new(), endpoint: ep.clone() }, - timeout: None + timeout: None, + id_hash: NodeId::new().sha3(), }); } let nearest = Discovery::nearest_node_entries(&NodeId::new(), &buckets); diff --git a/util/network/src/host.rs b/util/network/src/host.rs index 177a44843..d6e530d6f 100644 --- a/util/network/src/host.rs +++ b/util/network/src/host.rs @@ -61,7 +61,7 @@ const SYS_TIMER: usize = LAST_SESSION + 1; // Timeouts const MAINTENANCE_TIMEOUT: u64 = 1000; -const DISCOVERY_REFRESH_TIMEOUT: u64 = 7200; +const DISCOVERY_REFRESH_TIMEOUT: u64 = 60_000; const DISCOVERY_ROUND_TIMEOUT: u64 = 300; const NODE_TABLE_TIMEOUT: u64 = 300_000; From c342fa3035c729d8c9826e5a4ff27a6c4b612d14 Mon Sep 17 00:00:00 2001 From: Arkadiy Paronyan Date: Sat, 29 Oct 2016 17:38:50 +0200 Subject: [PATCH 079/192] WS bump (#2973) --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 62fa3d984..296c17c31 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -990,7 +990,7 @@ dependencies = [ [[package]] name = "mio" version = "0.6.0-dev" -source = "git+https://github.com/carllerche/mio?rev=62ec763c9cc34d8a452ed0392c575c50ddd5fc8d#62ec763c9cc34d8a452ed0392c575c50ddd5fc8d" +source = "git+https://github.com/ethcore/mio?branch=timer-fix#31eccc40ece3d47abaefaf23bb2114033175b972" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1885,12 +1885,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "ws" version = "0.5.3" -source = "git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable#b4538aec81e4c19519469127cece5fe62c88bb04" +source = "git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable#0cd6c5e3e9d5e61a37d53eb8dcbad523dcc69314" dependencies = [ "bytes 0.4.0-dev (git+https://github.com/carllerche/bytes)", "httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.0-dev (git+https://github.com/carllerche/mio?rev=62ec763c9cc34d8a452ed0392c575c50ddd5fc8d)", + "mio 0.6.0-dev (git+https://github.com/ethcore/mio?branch=timer-fix)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.2.0 (git+https://github.com/carllerche/slab?rev=5476efcafb)", @@ -1996,7 +1996,7 @@ dependencies = [ "checksum miniz-sys 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "9d1f4d337a01c32e1f2122510fed46393d53ca35a7f429cb0450abaedfa3ed54" "checksum mio 0.5.1 (git+https://github.com/ethcore/mio?branch=v0.5.x)" = "" "checksum mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a637d1ca14eacae06296a008fa7ad955347e34efcb5891cfd8ba05491a37907e" -"checksum mio 0.6.0-dev (git+https://github.com/carllerche/mio?rev=62ec763c9cc34d8a452ed0392c575c50ddd5fc8d)" = "" +"checksum mio 0.6.0-dev (git+https://github.com/ethcore/mio?branch=timer-fix)" = "" "checksum miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d5bfc6782530ac8ace97af10a540054a37126b63b0702ddaaa243b73b5745b9a" "checksum msdos_time 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c04b68cc63a8480fb2550343695f7be72effdec953a9d4508161c3e69041c7d8" "checksum nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)" = "" From 4d1cfd67068d696533aece48c594d438d1a6fdde Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Sat, 29 Oct 2016 17:39:08 +0200 Subject: [PATCH 080/192] Align password change dialog with create (#2970) --- .../PasswordManager/passwordManager.css | 10 +++++ .../modals/PasswordManager/passwordManager.js | 44 ++++++++++--------- 2 files changed, 34 insertions(+), 20 deletions(-) diff --git a/js/src/modals/PasswordManager/passwordManager.css b/js/src/modals/PasswordManager/passwordManager.css index aa0f72cb3..f538ef546 100644 --- a/js/src/modals/PasswordManager/passwordManager.css +++ b/js/src/modals/PasswordManager/passwordManager.css @@ -46,6 +46,16 @@ font-size: 1.1rem; } +.passwords { + display: flex; + flex-wrap: wrap; +} + +.password { + flex: 0 1 50%; + width: 50%; +} + .passwordHint { font-size: 0.9rem; color: lightgrey; diff --git a/js/src/modals/PasswordManager/passwordManager.js b/js/src/modals/PasswordManager/passwordManager.js index 800fba929..368664a71 100644 --- a/js/src/modals/PasswordManager/passwordManager.js +++ b/js/src/modals/PasswordManager/passwordManager.js @@ -181,33 +181,37 @@ export default class PasswordManager extends Component { disabled={ disabled } onSubmit={ this.handleChangePassword } onChange={ this.onEditCurrent } /> - - - - +
    +
    + +
    +
    + +
    +
    From e2b82e1e377fe5c7ade90bb5f989fd725ed87587 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Sat, 29 Oct 2016 15:44:04 +0000 Subject: [PATCH 081/192] [ci skip] js-precompiled 20161029-154241 --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 296c17c31..25343f4f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1213,7 +1213,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#39fa4ad1e1775e8f4efed7a94fa21318f8053091" +source = "git+https://github.com/ethcore/js-precompiled.git#293affdf3aa7735862d5a83a4b1f1edc7edd5936" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] From fca545ce9822cb24a2b51845cebb292bc6842540 Mon Sep 17 00:00:00 2001 From: arkpar Date: Sat, 29 Oct 2016 17:59:27 +0200 Subject: [PATCH 082/192] Removed junk file --- util/network/ | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 util/network/ diff --git a/util/network/ b/util/network/ deleted file mode 100644 index e69de29bb..000000000 From c24ac577fb2c5feaaac5e7f4223874f1afd01fca Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Sat, 29 Oct 2016 16:01:29 +0000 Subject: [PATCH 083/192] [ci skip] js-precompiled 20161029-160033 --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 25343f4f9..b4477e976 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1213,7 +1213,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#293affdf3aa7735862d5a83a4b1f1edc7edd5936" +source = "git+https://github.com/ethcore/js-precompiled.git#5c2278c4456a82229069f13b16ebf5bdb630e34c" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] From e304a2f3fe95b4bc0352299182e7c640630a65a9 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Sat, 29 Oct 2016 19:06:33 +0200 Subject: [PATCH 084/192] Add favicon (#2974) * Add favicon.ico * Add favicon to all html pages * Cleanup favicon background --- js/assets/images/ethcore-block-black.png | Bin 0 -> 11783 bytes js/assets/images/parity.ico | Bin 0 -> 1150 bytes js/src/dapps/basiccoin.html | 1 + js/src/dapps/gavcoin.html | 1 + js/src/dapps/githubhint.html | 1 + js/src/dapps/registry.html | 1 + js/src/dapps/signaturereg.html | 1 + js/src/dapps/tokenreg.html | 1 + js/src/dev.parity.html | 1 + js/src/dev.web3.html | 1 + js/src/index.html | 1 + js/src/index.js | 2 ++ js/webpack.config.js | 6 +++++- 13 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 js/assets/images/ethcore-block-black.png create mode 100644 js/assets/images/parity.ico diff --git a/js/assets/images/ethcore-block-black.png b/js/assets/images/ethcore-block-black.png new file mode 100644 index 0000000000000000000000000000000000000000..780a5be5c6d2de859db903bb565e659e5dd1e2e4 GIT binary patch literal 11783 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 literal 0 HcmV?d00001 diff --git a/js/assets/images/parity.ico b/js/assets/images/parity.ico new file mode 100644 index 0000000000000000000000000000000000000000..4551b46bede66e0130b4c248e9c21cc0b5150153 GIT binary patch literal 1150 zcmZuwOHUeM6unKFxU5N&uG?L^?yu+{X_}C@Gj7}%SO_a=y6Hj|7!^fLjD&}Y0Rluo z5~D^z;|rpYpdiRYK@2JRXnk?e6X(7!2y#PteeHIrNadma2YvUoStE;%VxdC-AEiHkZMx*h2pKInvUG;h$)oK+h zD=WIcx3{Ny&d<+bv)Mp>tU<5W)4X^-*W|OVR;v}Kr>EK<`c6ztXl~rQy}iZ4!h&ii z@3}Eo>ZVTif?CGM$6>eI(Q36+3;kKg?d@&-CdaH%I-N!!5Kx@6!XA)IoV777DVNJ& zy{UnHBSvHIh%pX*h;cs5IMia|%=7s880+in>PO#=jSXb8S*;6WS1J{)GxunPLIGZ{ zSN&OQYF=AgLq4C^KCwT<%%1R@GeNGIpWpA-x)5V->=XTHoI`Ww4i69E^ZEYblEd6M zn>>&C?(FQq;c#GjdRlA5dQdxkOdaIWObz@uCMPE`Gc$vDJgzfOU(TJ=>D0ZWqoe;~ z^9=L + Basic Token Deployment diff --git a/js/src/dapps/gavcoin.html b/js/src/dapps/gavcoin.html index f777f2920..3b260bce1 100644 --- a/js/src/dapps/gavcoin.html +++ b/js/src/dapps/gavcoin.html @@ -4,6 +4,7 @@ + GAVcoin diff --git a/js/src/dapps/githubhint.html b/js/src/dapps/githubhint.html index 085b15953..4689fd7d9 100644 --- a/js/src/dapps/githubhint.html +++ b/js/src/dapps/githubhint.html @@ -4,6 +4,7 @@ + GitHub Hint diff --git a/js/src/dapps/registry.html b/js/src/dapps/registry.html index 83c5e8c9b..512932a0a 100644 --- a/js/src/dapps/registry.html +++ b/js/src/dapps/registry.html @@ -4,6 +4,7 @@ + Token Registry diff --git a/js/src/dapps/signaturereg.html b/js/src/dapps/signaturereg.html index be62400d2..3929b425c 100644 --- a/js/src/dapps/signaturereg.html +++ b/js/src/dapps/signaturereg.html @@ -4,6 +4,7 @@ + Method Signature Registry diff --git a/js/src/dapps/tokenreg.html b/js/src/dapps/tokenreg.html index bcf04e298..8510aa817 100644 --- a/js/src/dapps/tokenreg.html +++ b/js/src/dapps/tokenreg.html @@ -4,6 +4,7 @@ + Token Registry diff --git a/js/src/dev.parity.html b/js/src/dev.parity.html index 56811f7c2..c2eb392d3 100644 --- a/js/src/dev.parity.html +++ b/js/src/dev.parity.html @@ -4,6 +4,7 @@ + dev::Parity.js diff --git a/js/src/dev.web3.html b/js/src/dev.web3.html index e55e0109b..ced98fca7 100644 --- a/js/src/dev.web3.html +++ b/js/src/dev.web3.html @@ -4,6 +4,7 @@ + dev::Web3 diff --git a/js/src/index.html b/js/src/index.html index 4ca2e7c1a..397a827d0 100644 --- a/js/src/index.html +++ b/js/src/index.html @@ -4,6 +4,7 @@ + Parity diff --git a/js/assets/images/contracts/unicorn-64x64.png b/js/assets/images/contracts/unicorn-64x64.png deleted file mode 100644 index 7850b09f14879ef90ca456830857c0cd2b7b5696..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7609 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 z zDcrmXR?`uN0!ax7!w5ntXiBBoBhx{39bgQUF*G-qk%$PoW`a@%$_zL;7rtC4%Nr}Q zm@6WR6T%qH08SrAaDgz0(CG}Ya&-e-a7@lNP^`N|5ycb~PZIGedFJf@0^mKTPMsP` zdDYDZ@UR2LTCLVs@o7%ay{EMi<(zCb4H6Do)T!Wi53X+laQ2}h^hM{Bc-vrk<@vE*TD z_c)R`MzvN!9C8-)6S1|``6utZ^`HIve|-3(f4gvEL0eo}&fq&-cidBP*8cLbdNAj(29E46m2~ECr_f@7{UCp9gt@@j2RPnwE?rN zgFW8Gpa0p{5DQN0<7McE0TBr(CXvJfu^*wi(!;saYk0DG0r6;vv5_)L>(nXpoR@$X-FWUL4gtqPzJ^r6w|;A1O2X#C=QV`Y=CG`gn*eA z5|!ZG)$?S2VlRn!07^AQ2#6F4oD2AVh<2-owdF0WuC3$51Gm7+nLtkWi{j`mL`xc=QRMO# zk~l&bhHx|+B#F`6h;aGobzG2_P`=f|+XfP!~&UH~Kmua;&P&1>m_eq+3X6M-Z zKKOfI{f$3g%u1(|Cr`3ZJ^U&8e5A-L0q_uH0f0t9|No?#taN+b_be}8(nuhcD2HTV z7m;v*t*DPnoi(+4@1DWY@%`4|QmCnQ1$91xy4-{HMi0>_MW09*rVcIy`h6c3wGn9v zl2~H>N*gO|4YRMVVRm{H`J4yaHeu)n2%%t90|)^Hfe;Ej;fNv*C_vW<+Wr2)j?|jrV`-{x|Ww@7RNL z1ETj8XcA$tdKp`1&f?X(-&4i(w&~*bE(_t_@r4bDiwq+sbxiAb9N+{U216|Xh>(^MYkjL;NAFXy5o$de! z@4g-7(kOg?fI&P!)8E3yD;Lqa5>E@4S&uQ0gVymb(C{@0BybCM|h43D|!2Lq-XHhM`Y?tASC)yS1|m;d%6wbm#S zX~Luly5SnyE34QtD1t!%%`^~%Ap#@Bzz@Na0sKvY!afIk_D;aFa-fWX(Ufuo2q>j# zQbZ|enjZsHLFoh(Kq+5}<4HOCjPykm7f=~!9(do6p6JUCB9J}wCL8Dg1Ywo`brKvJh zS0EmQU?u}msj5s$m69O@poBuxG*GI65e=nE5ju3n6R!GoU)IM;W~ydV+ny!w_2Wn~Pq&xPUpCfOmSJpA zNIegz2mx=S%UcMRLj?X6@Fa#1X;w->VNe5p6k%j$43%mLhHipx-!KM3QznTq1_A=9 z6tXV?gn|$Sr4%S-uq_*u(6l3@=}5Hn|64qU8}?x5J23KF;37^PBnSx807@XZLZMW| zp}hxCEtWAmHHH1VccE6RL2`+3&ClX3g%ki)GQAm!KuHB9Q|2pEC<2VYFim)t3*FF2 zg>zQ%ORCrFtI}wJR98xe&pmzdu(xHu`=9Ul#kbuo0B?7n!Z5s~lzO)U`D1_{?si+@ z6W@B=DT*TOu7NgeXiT8TJCG%bjby!X$;9UFiiuY433oWk_eSvjE?k?K&6Ly?`c;9-qyMp1M@vF0yQ;{CQ2 z+vU}@bsoUi@+61VAOV2@Y=vMmLZnel{n%lwe{~DN(*p!S1V$Jb(I5s2aS}t$uAQ=hM8nAFC>$$?M8t?hf^OKy z<%^dgI}{$tA&g@vqM(66(=;%ak|m6$iIoC@lz)-IQzwO2wMTHoB_cF2&3q1b5Q3Gf z8#wiiv-tEU{{nyffB!iic>6t=nV*1^Ds`2AmlkPQ!Vr?)Sf+te3dR`9m3kVZd4fp9 z@VSrm%NvL{I8;m#hH;u-6Pg}2f;8F7vN|CJ62TEiF@h+xdV+280JG-hK~md+m`7-0!~=ms)WaD;40nIVb{XOiSZ9q0pEHlbVQ+&8%W#;5x4i9*;2nuS{=}am7;KG*@<9R+D*fQNNdYdOO~PMWxZ3#1KOUPjM;<8GE2K7FR}+m< zj8#Tab;>B(1yu57c$SBpX2WC#2q@n1vu{SNUdCtt;Pbe2<}%inn{XTpny!Jz0+rD! zj=kmT24VqFh*g(0AU0|3g}FSP7UNt7uVAN}%QK+JpuOM&b;OI6?%><(w7-cXthMD;(%pRCQW1<1W zb>LNt*s*5@hi==CH=Mi=54`yV8jT8cMxp6C$PcJOCf8@&+v8-zVj56ffhmf5wGQ1e z@zuY46e8xRPt*XV5CmycB=7_Dx<1;DgQ!pDp<68N zBP)g8dV+r3gPqr5kW?Kig+QFe;NGA|yMx|Mm}D(1P%JH}nYY~fU?W#tEX|J6srn?H zs!d?5)IcR)M%gLCF>%JmX##{?k+ zLQ434h^?&-RxYn&eWeM@wD9`ZzZxe_+=h+I-86E|mLP;o%cunCEDs>82$pGpA{7%x zae^T9RkzorJ->g`C0P^z4jw#MvXT46+)UM|l&i8{tdgQzglFgBSuRZ7gvJ;QYNYhz z5ZvJ(N}w4GCV;*3I}pc7YTbqg-AK!d8~UVan&2?PKvY_v0tA+Bz%fi%wgoS7aN>bG zpg{4ZKm99w;|t$LWh4*Vwg3V|AxF>)py?VWXGU=Atp~Al*A(oEjD_ zu}aHc8J^?8v2B=|0VAUUDPvhZ$}rpyK#;~mLSES&D3sFe=P_i*2oy|RhgK@U$vG$$ zJ&ZI;*lKp*dKTJSd+^P_{2O$dJ$TbOfFY}E(-O|KG+aI1#Q3fnG?PNN8608(ihxmq z)pHs)zuQE!(1lq75|M!4L;zC&a@T#YI&7EnKU=6*Rkc_p)j}DCoCn8rU}+|Frh#gD zN;eXKBI}(hWZW=$2A}*&Ue}(>wdMt(6hul`rU6se;J7v{%R&%FnA$mkC%*gy+RZM; zCq|&@TDJEg?Ikfr(QEk-iGXgV-6zALAPQiNK(`sR3lx_xEo1HSCIZ?)Z_vFt0A5&F zz~Up1X!(Ni3&pXKN~u&;^tw=K6&yaTU=a}`we-&Q!Jb;RcmUbR6{jiMZxiqGacBv z2~E>sPz^exS<8=HYb0mj4&5RhCSDnde?8!@oJC)E*wnQuTBih#W5dh47@M7dOe7*d z0t1C@=+HF=D1{_Qps0eB5>lkDTS0+n0JA8pf{9$sMR_8Jk+~v9W=p7#d9SzylZPLE zSl0IK%Q;^D7s{ifR;64K)k2w-+(K%-hMh7)gn|+Zf)qGxXUorweZ5`}mo8nxU@${{EDO4+A&EG8tpRk0Ws_dn<0t|} z6bJfG#ndb5cZsFn}5q zn#G_y47UBsHX8^C)cl=yjv2-L`%9xETBTS~l|mUsH;f)_>ecs` z#zsd<<%+E4i?rnC)7Yz9Fqj6$G-x!Hqp)G-OrFV0oj!dUPd@o%O4zalnOwhdCgvm7c zVTd?RFz82EzPJu6XTYo22!aT%dPCL9WmJkq6mogEmIc!=pivDvrK$C1>9T?X$w-Z1;dAcXIV4E}#u(x_#-oou zibA0P&N&!kux%T|i9XA+u(GnU4e)bn)$2%-;rE~Y`Gy0X0sQD+d=qQSYp9P`u)eVg zo^W(q0TRwJK3+l;#fak=3FnYfW*#`b9}s||!_w)RqfTBSfYt14Ut6&ArT?isHlD9k zDzaWE)3TR`=h(3IjE>Y87-5;9jeuo>^i-!{fl>jS2F^LcFhnkwgK3(n!co(p zl!9RxD3waj07^=^{dn?vg5o->{a%(n1pWKbay{d)NClDA=DM!okw5!7KKH3FK-U;r z?GA(x2>Kx?W0>AM3dMl_z(){8h~gNwC7@+iygf-Su>GN+wo57cG6&yuRlaWhWWHXz zzf`TLda*<+`63EV4vucZ)D37%17m52C~YQCfIk01p(96*pja#-Ns{zrKg+ESfhUB( z^E`}>j$U(*0Hn&u1E}=xD!uuH(&uHto=vJ;58U?~cPXO~Qli=F;17Q5Pw{KN^qbh& zY@*%k;nJmLT)nb}_4Ou*t)Q8y%O3ax^!)*XFhG<<5JIHSLuN7^1xO*GhH@R?<&ME= zVzuY)yLa2A(g({Uqo`GCs-7>BlIy^=EI6hKgK24E1)8c4GyO5P$$RpQ8$CNai<2i$ z;^QCxID`=CF3LDg>*uT?5yvrn-^W7_J%sV`@n^VQHFVADT5_81!+QQ}+u}(&4UvWp zfJpfa%JlhMIp-Y9m)CIR;tHNVeF>lc>|f!#e|-i@DimsY5R<_+EbKZwhqWuKhz9|Z zBnB5i-x#3R@1yVLU|Ti}%}Bdxl!C}CQBweT`k}Z)lDvEX6D$B&1a-@e4-^`sljUkn zjueZuQb-dkM>k+F3XP>!ORtlarXyj`w5A~iX;~IN_`wh2%$YOz!WX{q3_hB3jvxpS z$1(1{`)<7b?QaKTEX_o-I+|o4tLr#L`aIyM3`{~u`bJAConqMb7$hy@RA&9pwD9yy zNGZ|j^l|#T=kdg2XW)7+?)i~B!88h|ki*#Q2+FlGM#rjn^b3#TYk&DDgjC>Bf_ATu zT&@Q@6Xy-Rl3^GCp=qBx6WuAEW!Nv91*fl29(es5-|Uqu?=05qvQaLRYQBhqn?uep zVHs%yU!yDqn542)nvns|*9siQzNx7xeEj1d$A>=jA$-W% zkGZ|GxbMLxnp+(toP!bqAtaQLa2*RHqcudwe0=e9Ujt7#BqV%)fOe+?(=cG_dfF~U zns^$z0jARJ1jwEXk>q7lRT2=QcHVt=&2{qsx->FsR!b$>C=^J=^Wa%F9Mec$GiA^S z1*Pm7Iger5|19G9Spe+XwF|%XTfc=bfBDNeefl&mT)2Q-E{A*Xy%)!iABSn0*P0H) zt`4Q?V^cDmD}4Se`MS8Pt~<0+Wsj9Tx1l$9zUL`qx-BnGI0Wag9Se1hLGtvC*BGNP z3>}89!!UL1n47@pL<1YETNsD2qiPH)9*+HP?^poDWxD3z}V2OQwXUbgalUt zTnZ>CXgUKk(p-ZP3YxukMku%xsMJe1czho|_wav3wNZj@Xy_08RC^5tDFtHA5yde) zH{DpEpx_4qq9{U=BnZJ@T1{5=eo1ogo%i2UsMmj`HZg6Ck2J_kqfQ#d5{j;aoMXW< zbeK$orm^i8Noi`m^!x1RKBMf(tXf`|_-x0+>@lCym?tkR>yg`unS{vZca@UYe&2P6 z1tgs@8d`3`IU+97eVBqnNNmsjGDble&2o<6L8A~tz{$J#_M=atx!QzR${~s(#8C{T z6u1!JLLlKBF^`eNX?ZvZ2k7_vs@3bzZnyoC%4W%_Q>RpAe%@=0O@E>>xnsUIG9q`3 z)aiJwf>PdtXIpR#9j2yji_#cq*6STM7U+v&lCB4oUKdY?Ks+zFe+PjfrM#A9t84LB zN|j~O*;GL`f2?HseXi1798sJgOcKPLLr9gWJBA@B8(SUzew8)~q*Txi9f}hC&6mE7 zIE=ErT0GT1^K|P#lEg^j7-1Y?;1AI4_t5HgRJ+}#?RM*>6x}TlU{Q`YX5ZtLswcc+ zMb?W&R?Qbsv~zGx1GX_ts?x-Yfq*h{jcA}Zr1PVsl!RpEj8qDY07GrX^|kExbkWx( zSOOAKi1e|fgdhMF>8vr!TzL9MG7_=ih`B(NaBz_>bzy{pF$Jm^h?chah(ao!%g%^` z1Z4!aso~Z;58>k3D_HvUm*5s1czG8>%G5na5&R%Pr`LyLrwxikB#5E_on9N8{oYHr z5FA)i2X4FnkW(suvOY0mH5v^})T?BoRzW52!L@AIrUApyp|MPX!RU7OJiOQ6FH7{o zMopAbN&z(U#Lv$;e^0&`*Q`>?)IG~AX_7)h$n^7&B#9FNKMLW8F@h*Y5XRsQcOTWGF!U^)hD*Mwo}>7bjQ(z;wE zNRkkPppQW?cnK^x8$H>7+x>4Vlp6o4HadxVy+KDSB{Etqqh9oovrU+$21D1N(bO%c zbGF&Z-kzJiaR)Blw?T)WRpw1IZ$V$Uh6f;(gb;vKV!IsS8FQ8-3SlA;gfaSlh;Bc? zAczo#2@;WlpQ=%ig`+gXpqV(IPzsvW(dxQB_mI8Q4Go5Fpj0oxtrh^v;P*oGnq9i{d`vF?r0XqEbwvSkO!ZPyO|Gkk6Z#oEw7>2_OY5Q%C0p>!K;JD0dvZbKbCX|1#&6kk1!c z(apiJY&eFIis%?kOCmO0gTS_V)S=H#5`l~4h5@|2zu3|2 z59IUJs^=DE&h@Bg+c0$@&+Gb&d6sGIh-= zW1@JvQ(6iIo`^JC?nmhN19ZARy1fuVkifPL=tTx0B%~lnxP+!D2%$g{0g|rJN+UA? zAOr{)2vsSV*|_m=j$39r>Y-!N4GkkBRTz4jU`Z*_ZMJds!W!ldOoHkZaTp;8!W-2O zK1l(nnccS@w+#Cs*Yc!m=aDmQIJ%iO@EL`nX<4UexP;_cEYo#O^p_Cm@9B<}Qs7)D zAySaJkdQL%5J@Q^xq{#lQOwhEPd|q5$LRNB^g0o`?FjvD1QBt7r)!X;NRg6B>ZX-U z-LA^&A(asyN~&!6nask0+GfyXwp)5Ax*Ei2n3a& zAfSpO)f8ka5oIc%lSsiXJIHzWoxDgdyF z{Z`v8l`JbKY|CM$X2PH>rQj40XE#Ro?CedZ(l+4VC+=PYS8m6sG=5&=jJGKwDIrO^ z8ZiC7x<+JV)-2PU2V6)ZC4>?}C0sBr6yaPTjs@b7BMuY9QG#wSM62ba-whG^K7#%L zLU5Rdp1NnQwq5YG3?OB$(!?^&q9sw=%TT4frVx-SBRV(hxE^S-_JIp^LxKN=22k&-1vvVJUurfdfe>ZWPXw623bI7J7l83rR>OTaIoG_AQnxZP2BBc?@`MWds zo_o&Oe`~Ei?0wF?qV3kk2-O`dNPqyS|6Yu`s;(s zoPwSL$q-4WRe}26Ruq)#@nz=p1(8LBNTr@vfrvQ7pon0+i@E2GXxo5A?J%3QXlswI zbqKbDZCh+?Y@yIqMyWE8f*e3z89ZZ2&hS~KE~LO(L8u-ilhW|ykQJbynKrHc56KSIRAMcd>I~yAx-yk8U-++-!Gq1Hc5gGV^on?5}+X_geXJ| zAELw{%DRZIbExMQ2Zs$7a|hQ2xUPlmIs_kZYHJgPNfyF-(t{hx!y~!eWlUQDEy?gZ zVIJvD70$;!r{ZL~g7mqhAVVz~uKvrl(x|E;>(DexQ(`hc+3;CzJuaHjXy;caJE^91jrYFm${w&)s%`LRXQF5p~)wmC+j z3v6#~q0oZT=wwco-zu#YjOoQkYFXW?EOX5dpyaO|{#}slY29a|fuPR8O~M_W37gWC ziC-U;@N$CTvx~T>FX0kB`Q+7KD9f`Ks%oM}qp?&)fil}HsLY3!rGi9;F$%Zd*~4T~ z;koA?Tftrb_pOWMDtP5j6B@+ni54g*p`4l%qa*pvLAqP>RlyLIn4fgo8AR!g>c5Yp zfQXcjQG$<%flOzkL?6X=K^BeIi@C*o=Flu^*me%v*67*>&p!P;HrFQr1f|LDKSrU@ z$puJ5qcAWL;7f$;SR=+nU3Xp;z#Gpc6 z&f$Di-bOTylli=p+03cMyn$_wVcR*H#T09!H9T?lBQU}+Dh;X|olv^6&@d*+c$7+D zM#9@O(a#LY3Kk+7Y7WjhlKbmL(!yEJ?~N?e6O;mijaVD6VRK^x>yr_{C#s?^(PNK2 z^SCnj#ZghBG$XaSzJZ;cxA3Je{~Ne4#|zJ0z!(1NuVJ*g1_}`ofGG?Fj5ojY9-7+X z;}@PrH7fEB(d8&4Cw4yD>@NA%l(y0djTs|O><(67#oR`)WCcYpuUSfM5>=@3&&_C6 zB*c(Tb&Le>0-OudwE2-@ zw81BT=2@IRvyoG8PTRRJPU;DjPiGdqF+)Q7n3zFRsw9!MVPgUhuSEi&!PzK(ivnAL?TP=D~vFX69z_E&J>{3o$!Y8b6R zA?cXPLSwo&!ykP0D$c+1G@gIy31|&O&wx%(CUFAgg)@`Glp#O@6NaD=Cm1p9A6!y< zOnEegF$6rNZvs&|;T#k4M3E6gLaCf3*w(>yUe%37Q+HUjbF|G|+P21Vd#vi&6lc#o zfwPZ32E_!U(upmULTMC?0t^I3D-=bVjue$QyW{~flkNP6x}z@~B@LE|#>$coTAuP$ zA%aEVyv4!J6viu@`Ki;W#uYjrPNa)YB!`xtDU3!{7OFG`M1C+CN#H|DpKY^%77ZZ+ zha}BWNUh1JDnLHswO7A~>3)q*{+Z`cRRx^&L&Zp($#y1CBnYL-JW>jFi0RKMpFW}l z?{OeRO4?yC^2Z56WMNQDBDNR_F{TLuA0nI!aBW1_c4*ob3)`S=YII%At!-4(HmYuq z@yKKYr#H93A;L_6LMa%%9DOl*sSZ^+C}|1E3JTZ1UswVnqGWl$vKmrwKID1dM30>_ zj*jM--kf4aSnj!E9RNXaD?5*gCzAwzf&LOzCtb%8o!GQ+JWz zT|^8q1w0U-0`m^D<9lH|rGMFx3EDn;kvoQHGC1-9*Kw65W{oujdh zw$|#VtI;fG&8Vu@M_Z>M9H4?X>mL9B5XwnJK~#uPC_$-I4sx1?)H%pTn-#Q1p!$_L zMXmwD5Iyhg@12CmR!PhmxdNah$$ZdnPs>@zd5?Fm-A1?D;Pf-w2+^bU3mi_TnAS)4 z6<`HGUTDXqZ0L_9?Xm_1hflry88Ais(Uq^F?P`4NxeNFkpZ|4iOxEE#i=rwZtP(Gp z1Wc(}&gtGU&m+u(s`UYw}nOBrb392(rAbY`1hFv$oFM%0nR3l);kaH z0)oo{&dLjl>46HKAi0GLF(8JB5CWWcaNdgZR%~dvX=`nLr<l|ac z0uAuaTRXV+%{TGL#u%+{F>@ZXW`_O4eH_mY@BajQ@@AygHF;J!Z3G z?A*SC*?j;01I(Kn3Q&bs335{`;OJqy1lp)$0&+2?c;?3bH-bHff;zf zlY7#j)Ef0cjcHxPWpBd6CfogN`j#P0q+j*pK~tr?6bCx9w-y%20Z;=lxELau@UX0*)= z6l@yit_+|Of<%%kN;bNm7tD*PGQhq9P>f20gLe<{?QdMeD}U}q1RLOON~gB-=oSuL zW3iaESR6Gtp4FJoXFP9f-P%UAT@C9yc<&H7A~Jy}r2Cg5NQjAIj7;w{hg*^PZ546d zD|f4H!?$nUxpD)5zXx~EF$wR6U}rJ7Ev6{Kq*Rhz!sOFyw1T2M@j#qpz(sa3lRWR- z7gar|vpSB-DVK*3@x8ZhqHS8tXEok?=QiGa<855Ob`xb;VzM#8(R>Dq5!I-`^DqAt zE_~`^*w`M!lfSP3Cvx9Z#KJR<^f+?>bAFO3LM3K8eV%82L{yqQFAO-w<NnYL>mP&E4xQKJ3G@KOr~5^z$M0zM1dm6jYxEbIQ~45 z2V#I0hRU1DvJE(8+uK$yu;TOw(Zx%Z3{Gh$>YFxsZUJ6GPtsCpiDVbRtd z7RN2>;|9mm84iyQWq-bhFN9c(|Y<}rnCKj_}yEtz4=4*S1QYT@0HTtt4jWnL}AQyBLoi$KHcXbKtco} zrJ6(p5;(1kJ=(PGSAAGHK!ljAfT8lO^obSLs zaiL0pqQuFd%Ek4}XUID`(~TTnShj1Gm?Akk2qY=Ve55!zZou}*|P6IiA8fS5rX zvw|^cp@;)q@HlD~*uJ}k$!Hx1cMouMJjL$u9v{{RdiQ9ztDE^h|HErv|4jhy;>C;R z(xprMg9G?a9-z>gulis+B0u36ggGc7&P$!8R~@9DSJFIhsTG9tBb~id2I@0gBFPF! z1YROqED;h{~#EfK>B15Ap(W1Ve%uoLI4IM*syjuKTSCJg}<1^_3a zyQJWX(gU%Oevdqvp7!p6nGqR%4(C(7f4q17a54V} zZ`^qGKlgX$rAwFSLzeF;0Mfbm#v^O--ALgjie89<0tG0N^j{|Ud)FJS6h!erHHgXq zMSpsiL*V)FkHoDIS4l2X|iIl*cC`rst zMd}<>0(4j>&o5Ccs>z|8%lLEliRcYg22o+&!<4w-)qM_tqQo!>b9$(Vhwm*;`Xqbu z|Dkn@zdI_*rz%r%WeTmKAVMIXpeh4ODp2Thm<8&WG7>Wc>Exz8xiJMcSah~S<64=w zGwz%ek=Z)VRQu2>KxY9pVF2rwn*ZrLsf*}eLIVhnVWMVKABugs7T(E$2x|jyH)%~-PQk9m@dQ+#6~M?-5Awq ztg50=W!^8CC~)a3X9*l6h5#3Xa?vX1z3xH>>+RQV@W1{3^u2$V$81#6g8jsUI|TUD z(Wza(5x(t0_q_KOHdut~n9^gZ^XW5to?fs4{%~fPAtTfZChw-8i>(?w4!J|H1|P*L-xUDJxW6g|aBvXw60$ zXl1}6LK3wwn?S4rdD!6KgY!PpS0dAIby$3PI-Sl}_1E}NMu+b|b6v$%e;()m*ai11 zK6>q`gCh&3bx;VPkoFr9B_2_e0SRZpyIT5QqOYLSX{5f8l4ZIE^NNMy(lcbW(iRRN zN-S9~2rnMwFT6Y3y@~VZ|K_=!!!N`b{ckPezqTsKmsy|i!K3GKW!BhfVKqvq@`A3onRG|h% z<=`XHr`jGFpcS*!Txi{!zJ>S$Uo+^cDG@%xgz$hjpQXHU^{U?meh0wsoEx2c)=bPR zotI~|(vL!6Q$v%0YJtp&Yc@%YyA(|yS&8Xb87y-`lXcIaNh5`U>|eA!Uc;M z95_^98F;?zg5T3V z;8iKB)Y`7wy$Oe#e+rOaev;MkeLT?cR<4J!zv`m@3(j{O$m$R~L_b1F1vqMG1CU~9 zXhdaTB#9n+Av;#E_*Ek{^sO+_QlFM(Ka%H`djJF^ker^GDWBl*p&5er^jfcC=__^v zz}L+E8ipdf9!SR=*Emn*y_zy}K^2aX;R9XJFq2M9+XMhHhRg)G<) zU8+&OeKOJljWq;7PoU%Y^?wUYYd^ zZ#$m~EFT{4v~078(`lC+=#%lm4`0J;;lkC;weY_4_GRlk@xiGO9EeJ= zH(&#h761lXt&;g_Wr8JBh_p1WpAjAg_(2qvG~GE0 zo9pkp;9hpYv5%d$)M4xk6qSJ{1r-=TndDd;hov8)dN=!iz^EqtuZu{sJkQ~}BVV_!+iqPe3)`r1nO4z@Aru%v7X?gFrrE515Zve$<_KAnwbVr~ z$3|BI+e>-S^TP?l4k5rho20_QA#@LmtP{U3<$KtKaNBvm+xl*MVOz}F3WZ4{cAg!I zY6Mf3P(=Y#B#t<_&87jhzCvF07!mMtoTWFGk>wop;JQPAfDl4NYddtdh4mdQ9;WCA zR%mT~^rO3r+jqZRx9yALs*uts(GUdoSPNqmMS-G90ZvYTX&hHHJLoWzP84jlL9w!v=gO_AB|rBUsOw)kGXm){>}eQTe+ z_dRFteb%}Fki7l5ymVL!fHa~vAmcQ7uoQ$&mudEnVrUCi&%W-40ak@%snFBnkD3j81WZzQ5KhzE#g}u)=U+q zaYg)A9Gk{rW&(gBiR}UoD@nwrA|~;}Lfk~W6aXA4@hgu1iUph;f%sBx=^43vZeo&v zuFKM+o7vhj=-!;{RE|Jk6vSkuF!^k{TY6dsla~v?;+;QBMqFFEsL0l4w$|20=Ei1U z73#lk{!NK{yGXBsKlcox^?kAZm0x;20E}5tZFYRI#qR~6V>1Bq_rKUQ4+0=5>RbE3 zSNEZb=OsxX$gndp$O~2}Gii1cZ;QLyD0~q#kKOx{zMvCNhFdBkxcc6a_^`8KLY^-l*j$7HTzW z9jX*njXHvANA;j?qDE0Os847zS_y4{wnO`%BhiWIY;+O265WVyLtjGQMvtT4U@#aO zMh9bq@y0}9k}+#ArI`JgR?K_yPPlex4vr&>=Vw z!U)NPjf5&f3*i#sA>kE~NK_}<5`&3c;s#Leh59VbXch zJ<=;OnXFBACP$M6>atgt3H=1Y2UgM2$qd#E`@bNxY<%q>JP#$vnwQ z$&-=;lG9RnDQzh?DW=pqsT!$MQo~ZS(iCYk=|Jf;=~C&V(pRM?Ww0{ZG9EH)nL?RE zG8bjWC@3{{8fLrtcZP`{)0Q)gslWG!XGWpiX}WY5Ts&=8t7 z&4-psE2EvD-J!jgQfv(`8kfN|tp+n)3 zB1%zTF<3EM@qpqb#pxx~CH6~LONy7ASaM$pR?=4rQCg#PNU2Y0R#`>aOF2V%ukuCZ zX%(7^vr4i`h00l#DOHN9qbgUmLiL>LGrBC@g`P^UqW92e)Rfe`)r4wwYW-^S>N@Jn z)eF>H)gNgPG#DBQ8WkGd8Z(-zngN>mn$4Q`weVUDtt72ITD@9x+B(`1+FP_cv?q1s zb$oR4beeS@>XLPxbXV)v>)z7C=rQzC^!DrB(1-P{^po^!^al)J18W1W!G425L$sl- zAyeeqo|%5^b{6q}Sw=sg-G}X@ltlGZ`~qvjVd& zv)|42%~|F(=C>@!7M>RCEjle;S{hh#EDu=TwW3%BSZ%TDw)$voW6ig2v7WNgw28CX zXEV&8GJ+VTj4QTiTUXolwx@01*;(5O>`vJIW^ZJlVt>?ra;eTz&eDdZV-D&LOouv$ z5l6aXoZ~^q5hpb#rc=Gs6K4%)wsWKNgo~a_vdb}-7p|tReAhPDIX64EwQlF#5qB^5 zV)uRz8IR>2)gF&M)jbnEn>}Z|ti0BEo%cq2`+4v59`;f8Vfi%q%=p^)uJ!HlBl(5; zRr@{h*Z1f9cLl%!z5%-e9xl^b##`1A2m*ZqcLhEQ(g|7}^kXn4I4HO#_-Tk)NPb9f zC?zyD^l0dtFxRlMum{U^mkXD7hf9XXgg1rHMYuc#Ks{QOuo{IxBNlUR|ZQDs|PFSjkvs?8!KETtwW_xDU)gW<7H@-Y0%v z{0z&DwTJbb?aZ!VPjMVL<(!EGhlKKk$wY_5U5QgkPDzzX(_A-hHTPw*cXDm=TuNZd z;gp5ch}70JTv}Y(DV_{3h1Zj=lAe=3m|>7nlrgf}ZuRcfGkiaOVz}3Y2Bx^ zZ`;1P{p|fi2b>SI)GF7O)V@E+J$SdytFFCXyT0-e=1|t5rw!o^z27pvZE93(ENT3B zn0I*ONXU_%CYz?Fqe@51n&D<)^VG4JV>iBY|E{yesHLuz)>?8L92Xvc_I=#J{_+2=_${t8_!le8-Jehe15v28mBOpTuPtA9&j!stev|fQey;ef!rLS781H)DN4%ey&;Ee@Q1wyoW7j9Y zPY)N;78d>m1DNytchBgq0000WV@Og>004R>004l5008;`004mK004C`008P>0026e z000+ooVrmw00006VoOIv00000008+zyMF)x010qNS#tmY4#NNd4#NS*Z>VGd000Mc zNliru;0hHFH7EOkKF$CD8BIw8LUmgQVLh^`ZL*Uy=(o}dY&a&rtDuH z0MF)?0F1U>ZNB4{xpshEzdyesolfUJ@p#8f0A0W1W8VZ%*I|Mg$a1g`tQQU0E<;x{miD}ABK}jCb<=UyJ?h~Yy)CiU{AN7t$TdLlF14C#*-?!c}AL# zgw9!eaS^%MraNEV6U1zQb!&2ee*f>}V>T{9hiRs6bG8?r;W_IrAXX+#ypmKOPMqebxA2yBXpEv;5 zE-wDS-(0#RPcthZn?$y}-p7un0kVsnv>#29lWE7F)KDbx6eggtY(XBYF3M;7P7e#_ zXLH&4IaC$9xN)`XtT^b?GJ)T2ImXKmoO5YwXHu#ukU88Nqo&%dcl1Syo_lR{K|G#5 zXY;N>_h(iVG9F0thxMJ=vE*Q?psW^%xh-%wcbb z$O2WrSoFj%?hELcWr4UR(S0zAj*8s{o`}kVg&A3!ntFX_)d%DjWh7^xTZqvlao#jL zp|H;6DVcokqD)H56ngso2nOitdyl;hdJ^38yB3ChQTn}MUThlV%NLd5w8-RUJE$zO zkmVK$CKBXlOYGe5qosG8rWQX|vxq^F*zmd!gW1T+1{Vz%xH!@sV4x??XnztlDUw}m z;F{}d&VCda=?jyalY!M~X5%(5W5E>RaFT*-6UjshC1s$uGf8ck`-Fa>SehFyF5;H8 z)427XeRO$)T(&q5gGr*feH=?#!Dd#NS?*-RwjrK=wI2Y3EV5#D2F?rz4!4S)=|qkA z5p{vMo<@;m-rOC)WRmC|PJgrj6y)Y87uIL2%(RMHO+^-^c^0j>+$;+QnV6<>&eA+? zzpIG@J>!^-kdkz?v`Aq)yl;ey=9VyTik%0a?_y?+lj+q? zUf4QFT6@po|8?&WoxT*I(JnMWB(A0j`8D$M6xLl<&Y^=L$_k9U)e=F|{HKk;`=-MO zo?GNP&>r!2^(APSVIve!C@Zm&FvKV2dKNFd5n}PYJi@^c#pO<3-#5nIqdgq$8b?0^dNS&dGCKYt zQ*2$wZk3rn+02IB9*hEPZt@cfs+8u+l;jzB<*kqd82Hd8;MsMRD-a2K{bM0yUBoDx z@x(P2u6<>K9XUSSI~+%r1(GQpQ4r8|9Zl2uN3fD50Z{-Q0bSP+1mXDLx{juT`ZtJM z%?2hFnNd}hpWQW&*PDD)x@8V`Cz)PpVE>V5?Y+NlJ@TPXz+ZQCu;);iXh7rEbyax1 zQTls=6XyV#Cfsg27t}e)bXh=76Y?iHIuNHK-^}LSLmcW3e;@$0lN_vFRe~>=psF~7 z^QtX)b$F{igdq)g+~3T2=(x{`#F8B9IL>?8TYN~W#`e7t#FS3hY9LpT&nyZ&_QVk1 z`Hu=VZuRi}Uv#3UG{Uio9qp(u&ZsYrq^FoQHJ?nEz>_ca;tQOfgFM(7;MV&N0x-SO zLC#HeXrjpN_cXt+vu$pRvS>~Q4jp7g#yhTYY~X{xJs}bL)m_UCzq-3&skM}Bo6|~d zv4zS4h0kA+$KrDxNRo_v3O*lOeCHIBf5~n^pNTQwlBY zZA-A{NR&W4h0!LkqM;msYgSafKLCffE_?Tns_N^nDN#1gsmwZdKUb8DVj~ zgE=)OhC>O;s-?6fQ@OIC{4>A)U&Xff1z_r;jdbk#?6-b%&+N>Ka^)eX)3kfl(vlYh zE5aiiM!4-4z1;faR<`a6GddDsa5zYYP2s%h?h}BB#nN>5hFJfn5p?y;GFX2ojjPy( zAZduw#Qxf0SNQY8^YDd0*9~OZ49uVJBs*8at{5(JndC;JBs_oHBSYQq^tt_}sV8fK z13Q;yzOrR()4qLyKw**N%Kb;iX2#=*IS)P2!@h%l;_)~a&da7S*MQL|P@Qif9M!mD zWht_x(bjX^!PS&mxoK@VcDuy>mI$TU4*ES4JHMH=4u;1PC;dIcu5kIXBF?GHq{ty+ z%5w74$B&Tyn^Ry=BraW)%ZM+D-*w0Ho&>lo|kvi-9K+JkA_tSJyAlDBrRPb4t56F zeK+8kItS$0XkcQTv(rjOEKYciOiUm$+3Ybny%x{ zG!coV`Q_d7dG?iKl;m6Rhf-v_Ocdmpxnyx3s}|;AH_80w&wcD|9i#1Nh(z)|09MS; zp|&!MWGc;?#rYiQ9B0s*#v#eE(Qo=}-oV56%q!gs%2 zk2Ax>46~&B*xP1AiL6IvQ7hHs%XOPas0CK6GyO<~IVL z=^jo!*wXD)1VQ6q`@{u_xphvmGOWz1&0zame$JU@W&2(q$#jY%-C^QyPgVeQ;PE@p z!!r`$a9@n7Dm!B%5jO7$viMvpmt9gqro%{k|2R**=%K#MOh&fC7r(vtgL8?i%2_-= zhZnbu(sj(wjy)$!0Vbo!Q$MX|vcnjCX@~!i8qxc|^Jw#$cTM1p4HtaO2#>5BnG!pIBTO98F*_%PgOp#U!^GT~`?QtK?+K^p3<%*eyW-i=q=$ z(=4ygVPq&qPk)lVM~2w`rk9P|Mlc$5G96|{2BQQ6N#5Awr=#~|3U9T_6c<^!=z;=n zzN!>whKZqpFh&J-?)4D}pUhFxs*YWj*sx_(F)4=Jt1roUtmP>E-9!EWfOPBHx&zO> z)U(CpbR9_f!hyN7ZPowrgI0OnXG?jl(Q}G4*+hA%4Lz!}e&;B!HulrfV+Kk1scyg|E-4a^Fx@=rT$_d$K)!?Pb-Y z>%P5X&mCW#TIsM#3#~52(9;v;U`G&DJ2{jU=9#cr&G^D;X4Ygd=!xJDrA|p^mM+Yr z@hvZrFa*3RZygBIKYCmmw_9bFG~}~;Ul_AZMojBOBRY}z$>XKdI(_{Sw(j=u!sbCF zNn&?%kVse~oz{rN(-Y<>Ute9$3%dpZQ#9Kl{WO-+?tA@U=!yQ`%|7Bp4oNLQH2m2Ub6LWJ{V%1blo8H}EC z2R^l+kU5n(OsO%EVK_2IJu{Qsd;=9_PHHM#RN9Qh7pJ)4?tSlT|1+n$ zm{*rcdrJ_z*?=b$g$XiCMH1Q58RSc!E@M`mIc^rE(88M=pL_k)wG^&+;=NoTKi8!t z5v=BE&M)li3C17!eV6kGx6PulaRiss$j$>}%%7Qw#VBKT7#I)47#|lY%C;~(G=13|pwG1`tKxOkC^NJOGMQ)bPw zGNw&6^S%39Cl+UoGLJsfPau#a!(rvyCW&d2os{I6`L~sYSnXy)<6(ln6kB!;)6x||6GVE4 zgGiFh{)0Z2%y99CSNiaWQ$!LP!@(pC=ee;PMB2MURF>KChg4Fj_r#a{EE89*ETpR^ zO1|5G(QIQ*m4ke@35QkUYo9H{Zc+H}TWZ+8e~hKmv%H>!YAq}@&%OH_8}HoPlhRHI zgTMT09qS%Ao`t>e@a&rkD<=KaswTD{Iy$^amL-eJ;%%UQ|Ka_vKF&{6%(}vZ6z= zqknA)K3!gBnf=2@53WBc9e!u=0Ctz5Mw(f>*bB7iT)hF)y+Z{qo>KF|I zvG^$@EtoLE{B5&aMXG9ZDRf7WPjQpsVB0trgFtPuo4QH|vK+)4RmoD|$^}_BKKk)7Be(|H6sOKuw7WmsKJC*qLR6 zMx#P17{+Llki=7~cW)l_apm#?Bt<1D=_p2lwt+ZLZu1^`;F-2(`90|RHC+3@cb}dj z)EQ1wRc<61hrwe>+&KzLh4p~ARIbCOTiyy{ zHX6yyk#U+u*4>y(Xe`bH&%86%?@qro_SCP&SiHo=P&kRxr7$$6qo1PmXf?@9t3Iscvc40lbozvn1ex? z*cO_*qv)E>jA}dUuE-}Qi9Gm>=gbNtd;WO2%jgPu4|MuCd>};iBnxAsX#_2e(KB6SwW{HWEO7CFwA3okWnP!}J z0|z@#j3m)`n&yrPczxhx^LXUeX>4nbk(VVif3B53|22R&oMz|NaTYAfrnNtEX4T|U7SFVX?caEdVylv;F-|A;LSzPZv1Hn`zEl4b1oQoq< zuF~JOtG{mEi%f_!0h0k7@$^ZCyf@umOThcEb^r~Sf;az1r|o8m%+zZ@9n z`isj*hvRH)94DAcq38mlC=l{$3@23QBTu#lPiH-+eNoaA(Qg|cOB;(_29}>+Kq8eu z)kO*lOvtj%;uS?~ZyICNmt=qIar>4;fn!5)JmbfcpU*8W#As1jc0nenIzyfmYEq?b zAcl85MtPo%F@FM0hYK5AWapZ}C^Eg$Mk1DCY&1?J3bG_2i17R-KlwQZKC?9AM~^>$ zEOu5eNL>4wJUR#B{H4*y;oc}sN27Q~6Bs0k>;J8geQyqM{hBJC{mb#)w_9W$yn7a} zY#e5CwS)S)Ogcw{sDjMv`@ERV3e}TL4A>=N{xta+R&w*5I2*@Ku?{a5~+|zMwWskn`u4b;SaC%GU`iA z!21nMsmNmaTsMchqg-)8HlApV85I^fyORuglMmef%Y$G0sISSLRt)=~#RKf?i*WB_ zEx3(FvRp=jDV2_+37&bela8SX(Nh#xO^QHekxZXAfY}UYi%6zLVP8j#M>me)3#w=3 zamPn~RraC_vkCbUJp6(Ovq8u0G$JbkmoCfazQ;QW$0oK@k3T_YUzq7Lb8#kNG#tZd z0?{sT!8`|3atvo<{U7~R*`~&TeBtaYE?t^QT-WIuj!;;j5YROo84`BesdEB{!$Q-Z zQPK$HW*aH3wqce<%JL1k+_L;naRO3l;j!jD{?{(LyhK;b0v4N~Zr&DLHdQhF$fz4A z%GGHNpRD+rO(IsC#HwY5hnqW+g}%7@^(lE-2l_f<=#nm&jEtTQ0M7cOshhu8Ta=D# z2j1En-q0}Jv9`J0!_Ix)6Rx@_vPfFhxb3dH+wo$dLZ^+!{7 zsB#!&SN&8+ovy14jKulHf6h66o z`jeT~KFKqGiuo(agz(3XzIg7E`dqZMDi~x*{@g`XY-$|ff_Yg8f+462N)S!m^ruaM z=Z#AG=j*o)2haBWK9+Zby8ca}u;jPyOtZ4CZ#enV)hpaTl|*5_rVEP2CdBJ&tchPg zKe{xL(yv`O&HlshKYpm6kEwku?*;|oKD{K%d{c3b>CWpfb#H3!ip69_{N~hB3-$G` z$1^jHKfYvP=9Y$8wp*ei=ku@Ds>%xh`1h;pa_+csdI`YH>f*vRiz@YRudTfS;EwC+ zY{@+rn$J>j`eaSuRBc7Wq|S3@Syq?j8xG!Z`#%1^Yd2p#kGXm){>}eQTe+ z_dRFteb%}Fki7l5ymVL!fHa~vAmcQ7uoQ$&mudEnVrUCi&%W-40ak@%snFBnkD3j81WZzQ5KhzE#g}u)=U+q zaYg)A9Gk{rW&(gBiR}UoD@nwrA|~;}Lfk~W6aXA4@hgu1iUph;f%sBx=^43vZeo&v zuFKM+o7vhj=-!;{RE|Jk6vSkuF!^k{TY6dsla~v?;+;QBMqFFEsL0l4w$|20=Ei1U z73#lk{!NK{yGXBsKlcox^?kAZm0x;20E}5tZFYRI#qR~6V>1Bq_rKUQ4+0=5>RbE3 zSNEZb=OsxX$gndp$O~2}Gii1cZ;QLyD0~q#kKOx{zMvCNhFdBkxcc6a_^`8KLY^-l*j$7HTzW z9jX*njXHvANA;j?qDE0Os847zS_y4{wnO`%BhiWIY;+O265WVyLtjGQMvtT4U@#aO zMh9bq@y0}9k}+#ArI`JgR?K_yPPlex4vr&>=Vw z!U)NPjf5&f3*i#sA>kE~NK_}<5`&3c;s#Leh59VbXch zJ<=;OnXFBACP$M6>atgt3H=1Y2UgM2$qd#E`@bNxY<%q>JP#$vnwQ z$&-=;lG9RnDQzh?DW=pqsT!$MQo~ZS(iCYk=|Jf;=~C&V(pRM?Ww0{ZG9EH)nL?RE zG8bjWC@3{{8fLrtcZP`{)0Q)gslWG!XGWpiX}WY5Ts&=8t7 z&4-psE2EvD-J!jgQfv(`8kfN|tp+n)3 zB1%zTF<3EM@qpqb#pxx~CH6~LONy7ASaM$pR?=4rQCg#PNU2Y0R#`>aOF2V%ukuCZ zX%(7^vr4i`h00l#DOHN9qbgUmLiL>LGrBC@g`P^UqW92e)Rfe`)r4wwYW-^S>N@Jn z)eF>H)gNgPG#DBQ8WkGd8Z(-zngN>mn$4Q`weVUDtt72ITD@9x+B(`1+FP_cv?q1s zb$oR4beeS@>XLPxbXV)v>)z7C=rQzC^!DrB(1-P{^po^!^al)J18W1W!G425L$sl- zAyeeqo|%5^b{6q}Sw=sg-G}X@ltlGZ`~qvjVd& zv)|42%~|F(=C>@!7M>RCEjle;S{hh#EDu=TwW3%BSZ%TDw)$voW6ig2v7WNgw28CX zXEV&8GJ+VTj4QTiTUXolwx@01*;(5O>`vJIW^ZJlVt>?ra;eTz&eDdZV-D&LOouv$ z5l6aXoZ~^q5hpb#rc=Gs6K4%)wsWKNgo~a_vdb}-7p|tReAhPDIX64EwQlF#5qB^5 zV)uRz8IR>2)gF&M)jbnEn>}Z|ti0BEo%cq2`+4v59`;f8Vfi%q%=p^)uJ!HlBl(5; zRr@{h*Z1f9cLl%!z5%-e9xl^b##`1A2m*ZqcLhEQ(g|7}^kXn4I4HO#_-Tk)NPb9f zC?zyD^l0dtFxRlMum{U^mkXD7hf9XXgg1rHMYuc#Ks{QOuo{IxBNlUR|ZQDs|PFSjkvs?8!KETtwW_xDU)gW<7H@-Y0%v z{0z&DwTJbb?aZ!VPjMVL<(!EGhlKKk$wY_5U5QgkPDzzX(_A-hHTPw*cXDm=TuNZd z;gp5ch}70JTv}Y(DV_{3h1Zj=lAe=3m|>7nlrgf}ZuRcfGkiaOVz}3Y2Bx^ zZ`;1P{p|fi2b>SI)GF7O)V@E+J$SdytFFCXyT0-e=1|t5rw!o^z27pvZE93(ENT3B zn0I*ONXU_%CYz?Fqe@51n&D<)^VG4JV>iBY|E{yesHLuz)>?8L92Xvc_I=#J{_+2=_${t8_!le8-Jehe15v28mBOpTuPtA9&j!stev|fQey;ef!rLS781H)DN4%ey&;Ee@Q1wyoW7j9Y zPY)N;78d>m1DNytchBgq0000WV@Og>004R>004l5008;`004mK004C`008P>0026e z000+ooVrmw00006VoOIv00000008+zyMF)x010qNS#tmY4#NNd4#NS*Z>VGd000Mc zNliru;0hHFH4sBRo5278AOJ~3K~#9!?7e54CTDr(`>$MYPW|@VIZn<=GozW&XcQ0# zA&DS@Y+wyO>um3uHS4qX;y+K>72uxQ{}4j zVK`tcS<*-tfzkE*^{4Kxr=F_oxx;ne&rk1ofZ=JcID)h?^FA6dj{f<>&w4%&s(_b?=RAw(y!}C>Z|+OBK>`B zVcsObj|u8Ui@QFxFF7)4?XKHOxPN8GhOYi@N=I*flK?*sm@YPX`do2CM^4=2*e>6C zVs2gMzQ5;FpPBfvdJe+B)rtAl-$Mw;#OlzA+~0liRQI7H73J4|dT=3^Q>Q%7cTS(3 zrZpG;gi&`tIdZ{4C3tvshxRT{3Yx-TAk^o)%!=+%{M?Y`f8oBu(hHMj^ACR_$L+s+ z?9CwXhZz%(z75S6C>(t{ySTtFRxR(2kujrH2oQ^NwYpFc9!#a9zklu+d1uf(uxdq= z%(eujnc7Mv2wMN>AC`+dw}pNt*CpRMG*(FujoEDL51sn>ZP`EA_N&hweDK$|@%G<8 z{-y=^Va3sV4sh~pgJ?wlU~$p>>sDP+o0jPG4n%4(&7X*C(w4yIYo=-cMIdrlsphs` z7&qz7DD{r4`tCEM^&6D1__09qCl(j|j^2)Nq&F?2OB_KIZxLX5%}+h`Z{~SP|JDfb zlec#9-#$CZ(F@h$H*8si>m+C-?l@i4*=!xq#n%|2ppb zT0y^cQ%V||GG`t*R{dGsR|dRQepb-kso|N*LP*uuHXJ_Ho0B)TW@H3e;Pg-_NFH$Yrk>(yF0DFLVtUd4_?eWN5xsbZnb zXaDzkVQSuH@Afp;Z%c7utWMSQ8SIZSvtaP>Qwyx_jbk(*uI`;ek_Eo`^c)j24f=b-cp@ZIm%0DQQ;S?2sWHD~3-7+Eoymnd6Z0;b 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 KMNUMnLSTXmi3->N From d47c6db713d00dc80e623d3a2d14fe78d0879b91 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Wed, 2 Nov 2016 11:28:23 +0100 Subject: [PATCH 186/192] Allow owner to update content url (#3083) --- .../githubhint/Application/application.css | 17 +++++---- .../githubhint/Application/application.js | 35 +++++++++++++------ 2 files changed, 35 insertions(+), 17 deletions(-) diff --git a/js/src/dapps/githubhint/Application/application.css b/js/src/dapps/githubhint/Application/application.css index aa4a68dc6..d149d883d 100644 --- a/js/src/dapps/githubhint/Application/application.css +++ b/js/src/dapps/githubhint/Application/application.css @@ -108,14 +108,19 @@ background: #fcc; } -.hashError { +.hashError, .hashWarning, .hashOk { padding-top: 0.5em; - color: #f66; text-align: center; } -.hashOk { - padding-top: 0.5em; - opacity: 0.5; - text-align: center; +.hashError { + color: #f66; +} + +.hashWarning { + color: #f80; +} + +.hashOk { + opacity: 0.5; } diff --git a/js/src/dapps/githubhint/Application/application.js b/js/src/dapps/githubhint/Application/application.js index 8eb3902d3..3c4ecfda8 100644 --- a/js/src/dapps/githubhint/Application/application.js +++ b/js/src/dapps/githubhint/Application/application.js @@ -29,11 +29,13 @@ const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'; export default class Application extends Component { state = { + fromAddress: null, loading: true, url: '', urlError: null, contentHash: '', contentHashError: null, + contentHashOwner: null, registerBusy: false, registerError: null, registerState: '' @@ -63,7 +65,14 @@ export default class Application extends Component { } renderPage () { - const { registerBusy, url, urlError, contentHash, contentHashError } = this.state; + const { fromAddress, registerBusy, url, urlError, contentHash, contentHashError, contentHashOwner } = this.state; + + let hashClass = null; + if (contentHashError) { + hashClass = contentHashOwner !== fromAddress ? styles.hashError : styles.hashWarning; + } else { + hashClass = styles.hashOk; + } return (
    @@ -81,7 +90,7 @@ export default class Application extends Component { className={ urlError ? styles.error : null } onChange={ this.onChangeUrl } />
    -
    +
    { contentHashError || contentHash }
    { registerBusy ? this.renderProgress() : this.renderButtons() } @@ -92,7 +101,7 @@ export default class Application extends Component { } renderButtons () { - const { accounts, fromAddress, url, urlError, contentHashError } = this.state; + const { accounts, fromAddress, url, urlError, contentHashError, contentHashOwner } = this.state; const account = accounts[fromAddress]; return ( @@ -105,7 +114,7 @@ export default class Application extends Component {
    + disabled={ (!!contentHashError && contentHashOwner !== fromAddress) || !!urlError || url.length === 0 }>register url ); } @@ -163,9 +172,9 @@ export default class Application extends Component { } onClickRegister = () => { - const { url, urlError, contentHash, contentHashError, fromAddress, instance } = this.state; + const { url, urlError, contentHash, contentHashError, contentHashOwner, fromAddress, instance } = this.state; - if (!!contentHashError || !!urlError || url.length === 0) { + if ((!!contentHashError && contentHashOwner !== fromAddress) || !!urlError || url.length === 0) { return; } @@ -243,13 +252,17 @@ export default class Application extends Component { instance.entries .call({}, [contentHash]) - .then(([accountSlashRepo, commit, owner]) => { - console.log('lookupHash', accountSlashRepo, api.util.bytesToHex(commit), owner); + .then(([accountSlashRepo, commit, contentHashOwner]) => { + console.log('lookupHash', accountSlashRepo, api.util.bytesToHex(commit), contentHashOwner); - if (owner !== ZERO_ADDRESS) { - this.setState({ contentHashError: contentHash, contentHash: null }); + if (contentHashOwner !== ZERO_ADDRESS) { + this.setState({ + contentHashError: contentHash, + contentHashOwner, + contentHash + }); } else { - this.setState({ contentHashError: null, contentHash }); + this.setState({ contentHashError: null, contentHashOwner, contentHash }); } }); }) From f7b7a3a1c55e2be0fc761245ac125975570a52b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Wed, 2 Nov 2016 11:29:18 +0100 Subject: [PATCH 187/192] Redirecting /home to new UI (#3084) --- dapps/src/router/mod.rs | 8 ++++++-- dapps/src/tests/redirection.rs | 20 ++++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/dapps/src/router/mod.rs b/dapps/src/router/mod.rs index ff60df996..6ca453d6d 100644 --- a/dapps/src/router/mod.rs +++ b/dapps/src/router/mod.rs @@ -105,8 +105,12 @@ impl server::Handler for Router { trace!(target: "dapps", "Resolving to fetchable content."); self.fetch.to_async_handler(path.clone(), control) }, + // NOTE [todr] /home is redirected to home page since some users may have the redirection cached + // (in the past we used 301 instead of 302) + // It should be safe to remove it in (near) future. + // // 404 for non-existent content - (Some(_), _) if *req.method() == hyper::method::Method::Get => { + (Some(ref path), _) if *req.method() == hyper::Method::Get && path.app_id != "home" => { trace!(target: "dapps", "Resolving to 404."); Box::new(ContentHandler::error( StatusCode::NotFound, @@ -116,7 +120,7 @@ impl server::Handler for Router { )) }, // Redirect any other GET request to signer. - _ if *req.method() == hyper::method::Method::Get => { + _ if *req.method() == hyper::Method::Get => { if let Some(port) = self.signer_port { trace!(target: "dapps", "Redirecting to signer interface."); Redirection::boxed(&format!("http://{}", signer_address(port))) diff --git a/dapps/src/tests/redirection.rs b/dapps/src/tests/redirection.rs index aee1cb964..398d08774 100644 --- a/dapps/src/tests/redirection.rs +++ b/dapps/src/tests/redirection.rs @@ -56,6 +56,26 @@ fn should_redirect_to_home_when_trailing_slash_is_missing() { assert_eq!(response.headers.get(0).unwrap(), "Location: http://127.0.0.1:18180"); } +#[test] +fn should_redirect_to_home_for_users_with_cached_redirection() { + // given + let server = serve(); + + // when + let response = request(server, + "\ + GET /home/ HTTP/1.1\r\n\ + Host: 127.0.0.1:8080\r\n\ + Connection: close\r\n\ + \r\n\ + " + ); + + // then + assert_eq!(response.status, "HTTP/1.1 302 Found".to_owned()); + assert_eq!(response.headers.get(0).unwrap(), "Location: http://127.0.0.1:18180"); +} + #[test] fn should_display_404_on_invalid_dapp() { // given From f4863c12b70f1bc6bcc72cedda80cad7710a3d5c Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Wed, 2 Nov 2016 11:07:27 +0000 Subject: [PATCH 188/192] [ci skip] js-precompiled 20161102-110620 --- Cargo.lock | 2 +- js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ff8f6b29b..4ba7843ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1235,7 +1235,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#a086efd6f0f53a8009ce0210a1df2983e0a5e5d8" +source = "git+https://github.com/ethcore/js-precompiled.git#17d0239545fbc998c7576113b7ab3771616285d1" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/js/package.json b/js/package.json index b25b7ecad..b31b36394 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "0.1.33", + "version": "0.1.34", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ", From 27ce6659d3aec017b2f5bee965a0d355f3283281 Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac Date: Wed, 2 Nov 2016 12:08:22 +0100 Subject: [PATCH 189/192] Fixes webpack HTML loader (#3089) --- js/webpack.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/webpack.config.js b/js/webpack.config.js index bd39d0a9f..41da9aa25 100644 --- a/js/webpack.config.js +++ b/js/webpack.config.js @@ -70,7 +70,7 @@ module.exports = { }, { test: /\.html$/, - loader: 'file?name=[path][name].[ext]!extract-loader!html-loader' + loader: 'file?name=[name].[ext]!extract-loader!html-loader' }, { From 5fff63a085aaf1aa7f2103d71d476b67fa212468 Mon Sep 17 00:00:00 2001 From: The Gitter Badger Date: Wed, 2 Nov 2016 06:18:56 -0500 Subject: [PATCH 190/192] Add Gitter badge (#3092) --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index a6c987a69..08c04c097 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # [Parity](https://ethcore.io/parity.html) ### Fast, light, and robust Ethereum implementation +[![Join the chat at https://gitter.im/ethcore/parity.js](https://badges.gitter.im/ethcore/parity.js.svg)](https://gitter.im/ethcore/parity.js?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + [![Build Status][travis-image]][travis-url] [![Coverage Status][coveralls-image]][coveralls-url] [![Join the chat at https://gitter.im/ethcore/parity][gitter-image]][gitter-url] [![GPLv3][license-image]][license-url] [Internal Documentation][doc-url] From feffe59fd4ed64053df9a3a634be4ee4a9b5f30c Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Wed, 2 Nov 2016 11:54:58 +0000 Subject: [PATCH 191/192] [ci skip] js-precompiled 20161102-115346 --- Cargo.lock | 2 +- js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4ba7843ff..b5e47dde2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1235,7 +1235,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#17d0239545fbc998c7576113b7ab3771616285d1" +source = "git+https://github.com/ethcore/js-precompiled.git#8e8432d2986c29e9ff4c338cb4d2a11bc9c3c557" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/js/package.json b/js/package.json index b31b36394..0ff75b720 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "0.1.34", + "version": "0.1.35", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ", From ed89e1efab0cdb13ade2fcab3babea51a741562d Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac Date: Wed, 2 Nov 2016 12:58:03 +0100 Subject: [PATCH 192/192] Fixing dapps sorting (#3086) * Fixing dapps sorting (#3076) * PR Grumble * PR Grumble #2 --- js/src/views/Dapps/Summary/summary.css | 1 + js/src/views/Dapps/dapps.css | 11 ++++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/js/src/views/Dapps/Summary/summary.css b/js/src/views/Dapps/Summary/summary.css index 7c7505895..5390b1ea7 100644 --- a/js/src/views/Dapps/Summary/summary.css +++ b/js/src/views/Dapps/Summary/summary.css @@ -17,6 +17,7 @@ .container { position: relative; + height: 100%; } .image { diff --git a/js/src/views/Dapps/dapps.css b/js/src/views/Dapps/dapps.css index 718641929..1a38af3cf 100644 --- a/js/src/views/Dapps/dapps.css +++ b/js/src/views/Dapps/dapps.css @@ -15,8 +15,9 @@ /* along with Parity. If not, see . */ .list { - column-count: 2; - column-gap: 0.25em; + display: flex; + flex-wrap: wrap; + margin: -0.125em; } .list+.list { @@ -24,7 +25,7 @@ } .item { - display: inline-block; - margin: 0 0 0.25em; - width: 100%; + padding: 0.125em; + flex: 0 1 50%; + box-sizing: border-box; }