diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 94ac69d2c..d27b58f9a 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -108,9 +108,9 @@ linux-centos:
paths:
- target/release/parity
name: "x86_64-unknown-centos-gnu_parity"
-linux-i686:
- stage: build
- image: ethcore/rust-i686:latest
+linux-i686:
+ stage: build
+ image: ethcore/rust-i686:latest
only:
- beta
- tags
@@ -348,7 +348,7 @@ windows:
- set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Include;C:\vs2015\VC\include;C:\Program Files (x86)\Windows Kits\10\Include\10.0.10240.0\ucrt
- 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%
+ - set RUSTFLAGS=%RUSTFLAGS%
- rustup default stable-x86_64-pc-windows-msvc
- cargo build -j 8 --release #%CARGOFLAGS%
- curl -sL --url "https://github.com/ethcore/win-build/raw/master/SimpleFC.dll" -o nsis\SimpleFC.dll
@@ -401,7 +401,7 @@ test-darwin:
- git submodule update --init --recursive
script:
- export RUST_BACKTRACE=1
- - ./test.sh $CARGOFLAGS --no-release
+ - ./test.sh $CARGOFLAGS
tags:
- osx
allow_failure: true
@@ -422,13 +422,14 @@ test-rust-stable:
image: ethcore/rust:stable
before_script:
- git submodule update --init --recursive
- - export JS_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep \.js | wc -l)
- - echo $JS_FILES_MODIFIED
- - if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "skip js test"; else ./js/scripts/install-deps.sh;fi
+ - export RUST_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep -v ^js/ | wc -l)
+ - export JS_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep ^js/ | wc -l)
+ - echo "rust/js modified: $RUST_FILES_MODIFIED / $JS_FILES_MODIFIED"
+ - if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "Skipping JS deps install since no JS files modified."; else ./js/scripts/install-deps.sh;fi
script:
- export RUST_BACKTRACE=1
- - echo $JS_FILES_MODIFIED
- - if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "skip js test"&./test.sh $CARGOFLAGS --no-release; else echo "skip rust test"&./js/scripts/lint.sh&./js/scripts/test.sh&./js/scripts/build.sh; fi
+ - if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "Skipping JS lint since no JS files modified."; else ./js/scripts/lint.sh && ./js/scripts/test.sh && ./js/scripts/build.sh; fi
+ - if [ "$RUST_FILES_MODIFIED" = 0 ]; then echo "Skipping Rust tests since no Rust files modified."; else ./test.sh $CARGOFLAGS; fi
tags:
- rust
- rust-stable
@@ -437,13 +438,13 @@ js-test:
image: ethcore/rust:stable
before_script:
- git submodule update --init --recursive
- - export JS_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep \.js | wc -l)
+ - export JS_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep ^js/ | wc -l)
- echo $JS_FILES_MODIFIED
- - if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "skip js test"; else ./js/scripts/install-deps.sh;fi
+ - if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "Skipping JS deps install since no JS files modified."; else ./js/scripts/install-deps.sh;fi
script:
- export RUST_BACKTRACE=1
- echo $JS_FILES_MODIFIED
- - if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "skip js test"; else echo "skip rust test"&./js/scripts/lint.sh&./js/scripts/test.sh&./js/scripts/build.sh; fi
+ - if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "Skipping JS lint since no JS files modified."; else ./js/scripts/lint.sh && ./js/scripts/test.sh && ./js/scripts/build.sh; fi
tags:
- rust
- rust-stable
@@ -457,7 +458,7 @@ test-rust-beta:
script:
- export RUST_BACKTRACE=1
- echo $JS_FILES_MODIFIED
- - ./test.sh $CARGOFLAGS --no-release
+ - ./test.sh $CARGOFLAGS
tags:
- rust
- rust-beta
@@ -471,7 +472,7 @@ test-rust-nightly:
- git submodule update --init --recursive
script:
- export RUST_BACKTRACE=1
- - ./test.sh $CARGOFLAGS --no-release
+ - ./test.sh $CARGOFLAGS
tags:
- rust
- rust-nightly
@@ -484,11 +485,11 @@ js-release:
- stable
image: ethcore/rust:stable
before_script:
- - export JS_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep \.js | wc -l)
+ - export JS_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep ^js/ | wc -l)
- echo $JS_FILES_MODIFIED
- - if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "skip js build"; else ./js/scripts/install-deps.sh;fi
+ - if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "Skipping JS deps install since no JS files modified."; else ./js/scripts/install-deps.sh;fi
script:
- echo $JS_FILES_MODIFIED
- - if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "skip js build"; else ./js/scripts/build.sh&&./js/scripts/release.sh; fi
+ - if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "Skipping JS rebuild since no JS files modified."; else ./js/scripts/build.sh && ./js/scripts/release.sh; fi
tags:
- javascript
diff --git a/.travis.yml b/.travis.yml
index 6f3fd9933..f1e02396b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -16,7 +16,7 @@ git:
matrix:
include:
- rust: stable
- env: RUN_TESTS="true" TEST_OPTIONS="--no-release"
+ env: RUN_TESTS="true" TEST_OPTIONS=""
- rust: stable
env: RUN_COVERAGE="true"
- rust: stable
@@ -71,8 +71,7 @@ install:
script:
- if [ "$RUN_TESTS" = "true" ]; then
./js/scripts/lint.sh &&
- ./js/scripts/test.sh &&
- ./test.sh $TEST_OPTIONS --verbose;
+ travis_wait 40 ./test.sh $TEST_OPTIONS;
fi
- if [ "$RUN_COVERAGE" = "true" ]; then ./scripts/cov.sh "$KCOV_CMD"; fi
diff --git a/Cargo.lock b/Cargo.lock
index 99d8f86bd..b82a8709d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -10,18 +10,18 @@ dependencies = [
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore 1.5.0",
"ethcore-dapps 1.5.0",
- "ethcore-devtools 1.4.0",
+ "ethcore-devtools 1.5.0",
"ethcore-hash-fetch 1.5.0",
"ethcore-io 1.5.0",
- "ethcore-ipc 1.4.0",
- "ethcore-ipc-codegen 1.4.0",
+ "ethcore-ipc 1.5.0",
+ "ethcore-ipc-codegen 1.5.0",
"ethcore-ipc-hypervisor 1.2.0",
- "ethcore-ipc-nano 1.4.0",
+ "ethcore-ipc-nano 1.5.0",
"ethcore-ipc-tests 0.1.0",
"ethcore-logger 1.5.0",
"ethcore-rpc 1.5.0",
"ethcore-signer 1.5.0",
- "ethcore-stratum 1.4.0",
+ "ethcore-stratum 1.5.0",
"ethcore-util 1.5.0",
"ethsync 1.5.0",
"fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -273,7 +273,7 @@ dependencies = [
[[package]]
name = "ethash"
-version = "1.4.0"
+version = "1.5.0"
dependencies = [
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -292,19 +292,19 @@ dependencies = [
"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)",
"ethabi 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "ethash 1.4.0",
+ "ethash 1.5.0",
"ethcore-bloom-journal 0.1.0",
- "ethcore-devtools 1.4.0",
+ "ethcore-devtools 1.5.0",
"ethcore-hash-fetch 1.5.0",
"ethcore-io 1.5.0",
- "ethcore-ipc 1.4.0",
- "ethcore-ipc-codegen 1.4.0",
- "ethcore-ipc-nano 1.4.0",
+ "ethcore-ipc 1.5.0",
+ "ethcore-ipc-codegen 1.5.0",
+ "ethcore-ipc-nano 1.5.0",
"ethcore-util 1.5.0",
"ethjson 0.1.0",
"ethkey 0.2.0",
"ethstore 0.1.0",
- "evmjit 1.4.0",
+ "evmjit 1.5.0",
"heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)",
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -345,7 +345,7 @@ version = "1.5.0"
dependencies = [
"clippy 0.0.103 (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-devtools 1.5.0",
"ethcore-hash-fetch 1.5.0",
"ethcore-rpc 1.5.0",
"ethcore-util 1.5.0",
@@ -358,7 +358,7 @@ dependencies = [
"mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"mime_guess 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "parity-ui 1.4.0",
+ "parity-ui 1.5.0",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -372,7 +372,7 @@ dependencies = [
[[package]]
name = "ethcore-devtools"
-version = "1.4.0"
+version = "1.5.0"
dependencies = [
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -402,9 +402,9 @@ dependencies = [
[[package]]
name = "ethcore-ipc"
-version = "1.4.0"
+version = "1.5.0"
dependencies = [
- "ethcore-devtools 1.4.0",
+ "ethcore-devtools 1.5.0",
"ethcore-util 1.5.0",
"nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)",
"semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -412,7 +412,7 @@ dependencies = [
[[package]]
name = "ethcore-ipc-codegen"
-version = "1.4.0"
+version = "1.5.0"
dependencies = [
"aster 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
"quasi 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -425,9 +425,9 @@ dependencies = [
name = "ethcore-ipc-hypervisor"
version = "1.2.0"
dependencies = [
- "ethcore-ipc 1.4.0",
- "ethcore-ipc-codegen 1.4.0",
- "ethcore-ipc-nano 1.4.0",
+ "ethcore-ipc 1.5.0",
+ "ethcore-ipc-codegen 1.5.0",
+ "ethcore-ipc-nano 1.5.0",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)",
"semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -436,9 +436,9 @@ dependencies = [
[[package]]
name = "ethcore-ipc-nano"
-version = "1.4.0"
+version = "1.5.0"
dependencies = [
- "ethcore-ipc 1.4.0",
+ "ethcore-ipc 1.5.0",
"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)",
"nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)",
@@ -448,10 +448,10 @@ dependencies = [
name = "ethcore-ipc-tests"
version = "0.1.0"
dependencies = [
- "ethcore-devtools 1.4.0",
- "ethcore-ipc 1.4.0",
- "ethcore-ipc-codegen 1.4.0",
- "ethcore-ipc-nano 1.4.0",
+ "ethcore-devtools 1.5.0",
+ "ethcore-ipc 1.5.0",
+ "ethcore-ipc-codegen 1.5.0",
+ "ethcore-ipc-nano 1.5.0",
"ethcore-util 1.5.0",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)",
@@ -477,7 +477,7 @@ version = "1.5.0"
dependencies = [
"ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "ethcore-devtools 1.4.0",
+ "ethcore-devtools 1.5.0",
"ethcore-io 1.5.0",
"ethcore-util 1.5.0",
"ethcrypto 0.1.0",
@@ -501,11 +501,11 @@ name = "ethcore-rpc"
version = "1.5.0"
dependencies = [
"clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
- "ethash 1.4.0",
+ "ethash 1.5.0",
"ethcore 1.5.0",
- "ethcore-devtools 1.4.0",
+ "ethcore-devtools 1.5.0",
"ethcore-io 1.5.0",
- "ethcore-ipc 1.4.0",
+ "ethcore-ipc 1.5.0",
"ethcore-util 1.5.0",
"ethcrypto 0.1.0",
"ethjson 0.1.0",
@@ -532,14 +532,14 @@ version = "1.5.0"
dependencies = [
"clippy 0.0.103 (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-devtools 1.5.0",
"ethcore-io 1.5.0",
"ethcore-rpc 1.5.0",
"ethcore-util 1.5.0",
"jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "parity-ui 1.4.0",
+ "parity-ui 1.5.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.3 (git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable)",
@@ -547,13 +547,13 @@ dependencies = [
[[package]]
name = "ethcore-stratum"
-version = "1.4.0"
+version = "1.5.0"
dependencies = [
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "ethcore-devtools 1.4.0",
- "ethcore-ipc 1.4.0",
- "ethcore-ipc-codegen 1.4.0",
- "ethcore-ipc-nano 1.4.0",
+ "ethcore-devtools 1.5.0",
+ "ethcore-ipc 1.5.0",
+ "ethcore-ipc-codegen 1.5.0",
+ "ethcore-ipc-nano 1.5.0",
"ethcore-util 1.5.0",
"jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)",
"jsonrpc-tcp-server 0.1.0 (git+https://github.com/ethcore/jsonrpc.git)",
@@ -575,7 +575,7 @@ dependencies = [
"eth-secp256k1 0.5.4 (git+https://github.com/ethcore/rust-secp256k1)",
"ethcore-bigint 0.1.2",
"ethcore-bloom-journal 0.1.0",
- "ethcore-devtools 1.4.0",
+ "ethcore-devtools 1.5.0",
"heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -664,9 +664,9 @@ dependencies = [
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore 1.5.0",
"ethcore-io 1.5.0",
- "ethcore-ipc 1.4.0",
- "ethcore-ipc-codegen 1.4.0",
- "ethcore-ipc-nano 1.4.0",
+ "ethcore-ipc 1.5.0",
+ "ethcore-ipc-codegen 1.5.0",
+ "ethcore-ipc-nano 1.5.0",
"ethcore-network 1.5.0",
"ethcore-util 1.5.0",
"heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -680,7 +680,7 @@ dependencies = [
[[package]]
name = "evmjit"
-version = "1.4.0"
+version = "1.5.0"
dependencies = [
"tiny-keccak 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -1257,7 +1257,7 @@ dependencies = [
[[package]]
name = "parity-ui"
-version = "1.4.0"
+version = "1.5.0"
dependencies = [
"parity-ui-dev 1.4.0",
"parity-ui-precompiled 1.4.0 (git+https://github.com/ethcore/js-precompiled.git)",
@@ -1274,7 +1274,7 @@ dependencies = [
[[package]]
name = "parity-ui-precompiled"
version = "1.4.0"
-source = "git+https://github.com/ethcore/js-precompiled.git#1690a80b9f56e33c8344f28eb637bacd97c9da14"
+source = "git+https://github.com/ethcore/js-precompiled.git#3d3b2f9e8e8b0fd62c172240bfd001a317cf2979"
dependencies = [
"parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
diff --git a/Cargo.toml b/Cargo.toml
index 078d2916c..65bb0dbc6 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -88,4 +88,4 @@ name = "parity"
[profile.release]
debug = false
lto = false
-
+panic = "abort"
diff --git a/dapps/js-glue/Cargo.toml b/dapps/js-glue/Cargo.toml
index b85122e90..827c67ef5 100644
--- a/dapps/js-glue/Cargo.toml
+++ b/dapps/js-glue/Cargo.toml
@@ -1,7 +1,7 @@
[package]
description = "Base Package for all Parity built-in dapps"
name = "parity-dapps-glue"
-version = "1.4.0"
+version = "1.5.0"
license = "GPL-3.0"
authors = ["Ethcore "]
[build-dependencies]
diff --git a/db/Cargo.toml b/db/Cargo.toml
index 2b4a19892..9642ed882 100644
--- a/db/Cargo.toml
+++ b/db/Cargo.toml
@@ -3,7 +3,7 @@ description = "Ethcore Database"
homepage = "http://ethcore.io"
license = "GPL-3.0"
name = "ethcore-db"
-version = "1.4.0"
+version = "1.5.0"
authors = ["Ethcore "]
build = "build.rs"
diff --git a/devtools/Cargo.toml b/devtools/Cargo.toml
index 77b05b4cc..3b648c450 100644
--- a/devtools/Cargo.toml
+++ b/devtools/Cargo.toml
@@ -3,7 +3,7 @@ description = "Ethcore development/test/build tools"
homepage = "http://ethcore.io"
license = "GPL-3.0"
name = "ethcore-devtools"
-version = "1.4.0"
+version = "1.5.0"
authors = ["Ethcore "]
[dependencies]
diff --git a/ethash/Cargo.toml b/ethash/Cargo.toml
index d2fb37d94..bf1ba990e 100644
--- a/ethash/Cargo.toml
+++ b/ethash/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "ethash"
-version = "1.4.0"
+version = "1.5.0"
authors = ["arkpar Option {
let position = LogGroupPosition::from(position.clone());
let result = self.db.read_with_cache(db::COL_EXTRA, &self.blocks_blooms, &position).map(Into::into);
- self.cache_man.lock().note_used(CacheID::BlocksBlooms(position));
+ self.cache_man.lock().note_used(CacheId::BlocksBlooms(position));
result
}
}
@@ -193,7 +193,7 @@ pub struct BlockChain {
db: Arc,
- cache_man: Mutex>,
+ cache_man: Mutex>,
pending_best_block: RwLock>,
pending_block_hashes: RwLock>,
@@ -270,7 +270,7 @@ impl BlockProvider for BlockChain {
None => None
};
- self.cache_man.lock().note_used(CacheID::BlockHeader(hash.clone()));
+ self.cache_man.lock().note_used(CacheId::BlockHeader(hash.clone()));
result
}
@@ -306,7 +306,7 @@ impl BlockProvider for BlockChain {
None => None
};
- self.cache_man.lock().note_used(CacheID::BlockBody(hash.clone()));
+ self.cache_man.lock().note_used(CacheId::BlockBody(hash.clone()));
result
}
@@ -314,28 +314,28 @@ impl BlockProvider for BlockChain {
/// Get the familial details concerning a block.
fn block_details(&self, hash: &H256) -> Option {
let result = self.db.read_with_cache(db::COL_EXTRA, &self.block_details, hash);
- self.cache_man.lock().note_used(CacheID::BlockDetails(hash.clone()));
+ self.cache_man.lock().note_used(CacheId::BlockDetails(hash.clone()));
result
}
/// Get the hash of given block's number.
fn block_hash(&self, index: BlockNumber) -> Option {
let result = self.db.read_with_cache(db::COL_EXTRA, &self.block_hashes, &index);
- self.cache_man.lock().note_used(CacheID::BlockHashes(index));
+ self.cache_man.lock().note_used(CacheId::BlockHashes(index));
result
}
/// Get the address of transaction with given hash.
fn transaction_address(&self, hash: &H256) -> Option {
let result = self.db.read_with_cache(db::COL_EXTRA, &self.transaction_addresses, hash);
- self.cache_man.lock().note_used(CacheID::TransactionAddresses(hash.clone()));
+ self.cache_man.lock().note_used(CacheId::TransactionAddresses(hash.clone()));
result
}
/// Get receipts of block with given hash.
fn block_receipts(&self, hash: &H256) -> Option {
let result = self.db.read_with_cache(db::COL_EXTRA, &self.block_receipts, hash);
- self.cache_man.lock().note_used(CacheID::BlockReceipts(hash.clone()));
+ self.cache_man.lock().note_used(CacheId::BlockReceipts(hash.clone()));
result
}
@@ -809,7 +809,7 @@ impl BlockChain {
let mut write_details = self.block_details.write();
batch.extend_with_cache(db::COL_EXTRA, &mut *write_details, update, CacheUpdatePolicy::Overwrite);
- self.cache_man.lock().note_used(CacheID::BlockDetails(block_hash));
+ self.cache_man.lock().note_used(CacheId::BlockDetails(block_hash));
}
#[cfg_attr(feature="dev", allow(similar_names))]
@@ -968,15 +968,15 @@ impl BlockChain {
let mut cache_man = self.cache_man.lock();
for n in pending_hashes_keys {
- cache_man.note_used(CacheID::BlockHashes(n));
+ cache_man.note_used(CacheId::BlockHashes(n));
}
for hash in enacted_txs_keys {
- cache_man.note_used(CacheID::TransactionAddresses(hash));
+ cache_man.note_used(CacheId::TransactionAddresses(hash));
}
for hash in pending_block_hashes {
- cache_man.note_used(CacheID::BlockDetails(hash));
+ cache_man.note_used(CacheId::BlockDetails(hash));
}
}
@@ -1244,13 +1244,13 @@ impl BlockChain {
cache_man.collect_garbage(current_size, | ids | {
for id in &ids {
match *id {
- CacheID::BlockHeader(ref h) => { block_headers.remove(h); },
- CacheID::BlockBody(ref h) => { block_bodies.remove(h); },
- CacheID::BlockDetails(ref h) => { block_details.remove(h); }
- CacheID::BlockHashes(ref h) => { block_hashes.remove(h); }
- CacheID::TransactionAddresses(ref h) => { transaction_addresses.remove(h); }
- CacheID::BlocksBlooms(ref h) => { blocks_blooms.remove(h); }
- CacheID::BlockReceipts(ref h) => { block_receipts.remove(h); }
+ CacheId::BlockHeader(ref h) => { block_headers.remove(h); },
+ CacheId::BlockBody(ref h) => { block_bodies.remove(h); },
+ CacheId::BlockDetails(ref h) => { block_details.remove(h); }
+ CacheId::BlockHashes(ref h) => { block_hashes.remove(h); }
+ CacheId::TransactionAddresses(ref h) => { transaction_addresses.remove(h); }
+ CacheId::BlocksBlooms(ref h) => { blocks_blooms.remove(h); }
+ CacheId::BlockReceipts(ref h) => { block_receipts.remove(h); }
}
}
diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs
index f068b8d99..f29bbada4 100644
--- a/ethcore/src/client/client.rs
+++ b/ethcore/src/client/client.rs
@@ -1130,6 +1130,10 @@ impl BlockChainClient for Client {
self.transaction_address(id).and_then(|address| self.chain.read().transaction(&address))
}
+ fn transaction_block(&self, id: TransactionId) -> Option {
+ self.transaction_address(id).map(|addr| addr.block_hash)
+ }
+
fn uncle(&self, id: UncleId) -> Option {
let index = id.position;
self.block_body(id.block).and_then(|body| BodyView::new(&body).uncle_rlp_at(index))
diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs
index bd436d4f5..2ba0c5628 100644
--- a/ethcore/src/client/test_client.rs
+++ b/ethcore/src/client/test_client.rs
@@ -432,6 +432,10 @@ impl BlockChainClient for TestBlockChainClient {
None // Simple default.
}
+ fn transaction_block(&self, _id: TransactionId) -> Option {
+ None // Simple default.
+ }
+
fn uncle(&self, _id: UncleId) -> Option {
None // Simple default.
}
diff --git a/ethcore/src/client/traits.rs b/ethcore/src/client/traits.rs
index 6ea2f3bf5..a699c6ea2 100644
--- a/ethcore/src/client/traits.rs
+++ b/ethcore/src/client/traits.rs
@@ -129,6 +129,9 @@ pub trait BlockChainClient : Sync + Send {
/// Get transaction with given hash.
fn transaction(&self, id: TransactionId) -> Option;
+ /// Get the hash of block that contains the transaction, if any.
+ fn transaction_block(&self, id: TransactionId) -> Option;
+
/// Get uncle with given id.
fn uncle(&self, id: UncleId) -> Option;
diff --git a/ethcore/src/engines/authority_round.rs b/ethcore/src/engines/authority_round.rs
index f28a06117..f632c9382 100644
--- a/ethcore/src/engines/authority_round.rs
+++ b/ethcore/src/engines/authority_round.rs
@@ -21,13 +21,15 @@ use std::sync::Weak;
use std::time::{UNIX_EPOCH, Duration};
use util::*;
use ethkey::{verify_address, Signature};
-use rlp::{Rlp, UntrustedRlp, View, encode};
+use rlp::{UntrustedRlp, Rlp, View, encode};
use account_provider::AccountProvider;
use block::*;
use spec::CommonParams;
use engines::Engine;
use header::Header;
use error::{Error, BlockError};
+use blockchain::extras::BlockDetails;
+use views::HeaderView;
use evm::Schedule;
use ethjson;
use io::{IoContext, IoHandler, TimerToken, IoService, IoChannel};
@@ -35,8 +37,6 @@ use service::ClientIoMessage;
use transaction::SignedTransaction;
use env_info::EnvInfo;
use builtin::Builtin;
-use blockchain::extras::BlockDetails;
-use views::HeaderView;
/// `AuthorityRound` params.
#[derive(Debug, PartialEq)]
@@ -68,18 +68,20 @@ pub struct AuthorityRound {
params: CommonParams,
our_params: AuthorityRoundParams,
builtins: BTreeMap,
- transition_service: IoService,
+ transition_service: IoService<()>,
message_channel: Mutex>>,
step: AtomicUsize,
proposed: AtomicBool,
+ account_provider: Mutex >>,
+ password: RwLock >,
}
fn header_step(header: &Header) -> Result {
- UntrustedRlp::new(&header.seal()[0]).as_val()
+ UntrustedRlp::new(&header.seal().get(0).expect("was either checked with verify_block_basic or is genesis; has 2 fields; qed (Make sure the spec file has a correct genesis seal)")).as_val()
}
fn header_signature(header: &Header) -> Result {
- UntrustedRlp::new(&header.seal()[1]).as_val::().map(Into::into)
+ UntrustedRlp::new(&header.seal().get(1).expect("was checked with verify_block_basic; has 2 fields; qed")).as_val::().map(Into::into)
}
trait AsMillis {
@@ -101,10 +103,12 @@ impl AuthorityRound {
params: params,
our_params: our_params,
builtins: builtins,
- transition_service: try!(IoService::::start()),
+ transition_service: try!(IoService::<()>::start()),
message_channel: Mutex::new(None),
step: AtomicUsize::new(initial_step),
- proposed: AtomicBool::new(false)
+ proposed: AtomicBool::new(false),
+ account_provider: Mutex::new(None),
+ password: RwLock::new(None),
});
let handler = TransitionHandler { engine: Arc::downgrade(&engine) };
try!(engine.transition_service.register_handler(Arc::new(handler)));
@@ -143,20 +147,17 @@ struct TransitionHandler {
engine: Weak,
}
-#[derive(Clone)]
-struct BlockArrived;
-
const ENGINE_TIMEOUT_TOKEN: TimerToken = 23;
-impl IoHandler for TransitionHandler {
- fn initialize(&self, io: &IoContext) {
+impl IoHandler<()> for TransitionHandler {
+ fn initialize(&self, io: &IoContext<()>) {
if let Some(engine) = self.engine.upgrade() {
io.register_timer_once(ENGINE_TIMEOUT_TOKEN, engine.remaining_step_duration().as_millis())
.unwrap_or_else(|e| warn!(target: "poa", "Failed to start consensus step timer: {}.", e))
}
}
- fn timeout(&self, io: &IoContext, timer: TimerToken) {
+ fn timeout(&self, io: &IoContext<()>, timer: TimerToken) {
if timer == ENGINE_TIMEOUT_TOKEN {
if let Some(engine) = self.engine.upgrade() {
engine.step.fetch_add(1, AtomicOrdering::SeqCst);
@@ -208,10 +209,6 @@ impl Engine for AuthorityRound {
});
}
- /// Apply the block reward on finalisation of the block.
- /// This assumes that all uncles are valid uncles (i.e. of at least one generation before the current).
- fn on_close_block(&self, _block: &mut ExecutedBlock) {}
-
fn is_sealer(&self, author: &Address) -> Option {
let p = &self.our_params;
Some(p.authorities.contains(author))
@@ -221,14 +218,14 @@ impl Engine for AuthorityRound {
///
/// This operation is synchronous and may (quite reasonably) not be available, in which `false` will
/// be returned.
- fn generate_seal(&self, block: &ExecutedBlock, accounts: Option<&AccountProvider>) -> Option> {
+ fn generate_seal(&self, block: &ExecutedBlock) -> Option> {
if self.proposed.load(AtomicOrdering::SeqCst) { return None; }
let header = block.header();
let step = self.step();
if self.is_step_proposer(step, header.author()) {
- if let Some(ap) = accounts {
+ if let Some(ref ap) = *self.account_provider.lock() {
// Account should be permanently unlocked, otherwise sealing will fail.
- if let Ok(signature) = ap.sign(*header.author(), None, header.bare_hash()) {
+ if let Ok(signature) = ap.sign(*header.author(), self.password.read().clone(), header.bare_hash()) {
trace!(target: "poa", "generate_seal: Issuing a block for step {}.", step);
self.proposed.store(true, AtomicOrdering::SeqCst);
return Some(vec![encode(&step).to_vec(), encode(&(&*signature as &[u8])).to_vec()]);
@@ -303,23 +300,30 @@ impl Engine for AuthorityRound {
t.sender().map(|_|()) // Perform EC recovery and cache sender
}
- fn register_message_channel(&self, message_channel: IoChannel) {
- let mut guard = self.message_channel.lock();
- *guard = Some(message_channel);
- }
-
fn is_new_best_block(&self, _best_total_difficulty: U256, best_header: HeaderView, _parent_details: &BlockDetails, new_header: &HeaderView) -> bool {
let new_number = new_header.number();
let best_number = best_header.number();
if new_number != best_number {
new_number > best_number
} else {
- // Take the oldest step at given height.
- let new_step: usize = Rlp::new(&new_header.seal()[0]).as_val();
+ // Take the oldest step at given height.
+ let new_step: usize = Rlp::new(&new_header.seal()[0]).as_val();
let best_step: usize = Rlp::new(&best_header.seal()[0]).as_val();
new_step < best_step
}
}
+
+ fn register_message_channel(&self, message_channel: IoChannel) {
+ *self.message_channel.lock() = Some(message_channel);
+ }
+
+ fn set_signer(&self, _address: Address, password: String) {
+ *self.password.write() = Some(password);
+ }
+
+ fn register_account_provider(&self, account_provider: Arc) {
+ *self.account_provider.lock() = Some(account_provider);
+ }
}
#[cfg(test)]
@@ -387,12 +391,11 @@ mod tests {
fn generates_seal_and_does_not_double_propose() {
let tap = AccountProvider::transient_provider();
let addr1 = tap.insert_account("1".sha3(), "1").unwrap();
- tap.unlock_account_permanently(addr1, "1".into()).unwrap();
let addr2 = tap.insert_account("2".sha3(), "2").unwrap();
- tap.unlock_account_permanently(addr2, "2".into()).unwrap();
let spec = Spec::new_test_round();
let engine = &*spec.engine;
+ engine.register_account_provider(Arc::new(tap));
let genesis_header = spec.genesis_header();
let mut db1 = get_temp_state_db().take();
spec.ensure_db_good(&mut db1, &TrieFactory::new(TrieSpec::Secure)).unwrap();
@@ -404,16 +407,18 @@ mod tests {
let b2 = OpenBlock::new(engine, Default::default(), false, db2, &genesis_header, last_hashes, addr2, (3141562.into(), 31415620.into()), vec![]).unwrap();
let b2 = b2.close_and_lock();
- if let Some(seal) = engine.generate_seal(b1.block(), Some(&tap)) {
+ engine.set_signer(addr1, "1".into());
+ if let Some(seal) = engine.generate_seal(b1.block()) {
assert!(b1.clone().try_seal(engine, seal).is_ok());
// Second proposal is forbidden.
- assert!(engine.generate_seal(b1.block(), Some(&tap)).is_none());
+ assert!(engine.generate_seal(b1.block()).is_none());
}
- if let Some(seal) = engine.generate_seal(b2.block(), Some(&tap)) {
+ engine.set_signer(addr2, "2".into());
+ if let Some(seal) = engine.generate_seal(b2.block()) {
assert!(b2.clone().try_seal(engine, seal).is_ok());
// Second proposal is forbidden.
- assert!(engine.generate_seal(b2.block(), Some(&tap)).is_none());
+ assert!(engine.generate_seal(b2.block()).is_none());
}
}
diff --git a/ethcore/src/engines/basic_authority.rs b/ethcore/src/engines/basic_authority.rs
index fb2f9bde6..1070d3a3d 100644
--- a/ethcore/src/engines/basic_authority.rs
+++ b/ethcore/src/engines/basic_authority.rs
@@ -58,6 +58,8 @@ pub struct BasicAuthority {
params: CommonParams,
our_params: BasicAuthorityParams,
builtins: BTreeMap,
+ account_provider: Mutex>>,
+ password: RwLock >,
}
impl BasicAuthority {
@@ -67,6 +69,8 @@ impl BasicAuthority {
params: params,
our_params: our_params,
builtins: builtins,
+ account_provider: Mutex::new(None),
+ password: RwLock::new(None),
}
}
}
@@ -98,13 +102,8 @@ impl Engine for BasicAuthority {
max(gas_floor_target, gas_limit - gas_limit / bound_divisor + 1.into())
}
});
-// info!("ethash: populate_from_parent #{}: difficulty={} and gas_limit={}", header.number, header.difficulty, header.gas_limit);
}
- /// Apply the block reward on finalisation of the block.
- /// This assumes that all uncles are valid uncles (i.e. of at least one generation before the current).
- fn on_close_block(&self, _block: &mut ExecutedBlock) {}
-
fn is_sealer(&self, author: &Address) -> Option {
Some(self.our_params.authorities.contains(author))
}
@@ -113,12 +112,12 @@ impl Engine for BasicAuthority {
///
/// This operation is synchronous and may (quite reasonably) not be available, in which `false` will
/// be returned.
- fn generate_seal(&self, block: &ExecutedBlock, accounts: Option<&AccountProvider>) -> Option> {
- if let Some(ap) = accounts {
+ fn generate_seal(&self, block: &ExecutedBlock) -> Option> {
+ if let Some(ref ap) = *self.account_provider.lock() {
let header = block.header();
let message = header.bare_hash();
// account should be pernamently unlocked, otherwise sealing will fail
- if let Ok(signature) = ap.sign(*block.header().author(), None, message) {
+ if let Ok(signature) = ap.sign(*block.header().author(), self.password.read().clone(), message) {
return Some(vec![::rlp::encode(&(&*signature as &[u8])).to_vec()]);
} else {
trace!(target: "basicauthority", "generate_seal: FAIL: accounts secret key unavailable");
@@ -179,6 +178,14 @@ impl Engine for BasicAuthority {
fn verify_transaction(&self, t: &SignedTransaction, _header: &Header) -> Result<(), Error> {
t.sender().map(|_|()) // Perform EC recovery and cache sender
}
+
+ fn set_signer(&self, _address: Address, password: String) {
+ *self.password.write() = Some(password);
+ }
+
+ fn register_account_provider(&self, ap: Arc) {
+ *self.account_provider.lock() = Some(ap);
+ }
}
#[cfg(test)]
@@ -250,10 +257,11 @@ mod tests {
fn can_generate_seal() {
let tap = AccountProvider::transient_provider();
let addr = tap.insert_account("".sha3(), "").unwrap();
- tap.unlock_account_permanently(addr, "".into()).unwrap();
let spec = new_test_authority();
let engine = &*spec.engine;
+ engine.set_signer(addr, "".into());
+ engine.register_account_provider(Arc::new(tap));
let genesis_header = spec.genesis_header();
let mut db_result = get_temp_state_db();
let mut db = db_result.take();
@@ -261,7 +269,7 @@ mod tests {
let last_hashes = Arc::new(vec![genesis_header.hash()]);
let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, addr, (3141562.into(), 31415620.into()), vec![]).unwrap();
let b = b.close_and_lock();
- let seal = engine.generate_seal(b.block(), Some(&tap)).unwrap();
+ let seal = engine.generate_seal(b.block()).unwrap();
assert!(b.try_seal(engine, seal).is_ok());
}
diff --git a/ethcore/src/engines/instant_seal.rs b/ethcore/src/engines/instant_seal.rs
index f50f7344b..83335fb03 100644
--- a/ethcore/src/engines/instant_seal.rs
+++ b/ethcore/src/engines/instant_seal.rs
@@ -23,7 +23,6 @@ use spec::CommonParams;
use evm::Schedule;
use block::ExecutedBlock;
use util::Bytes;
-use account_provider::AccountProvider;
/// An engine which does not provide any consensus mechanism, just seals blocks internally.
pub struct InstantSeal {
@@ -60,7 +59,7 @@ impl Engine for InstantSeal {
fn is_sealer(&self, _author: &Address) -> Option { Some(true) }
- fn generate_seal(&self, _block: &ExecutedBlock, _accounts: Option<&AccountProvider>) -> Option> {
+ fn generate_seal(&self, _block: &ExecutedBlock) -> Option> {
Some(Vec::new())
}
}
@@ -70,16 +69,12 @@ mod tests {
use util::*;
use util::trie::TrieSpec;
use tests::helpers::*;
- use account_provider::AccountProvider;
use spec::Spec;
use header::Header;
use block::*;
#[test]
fn instant_can_seal() {
- let tap = AccountProvider::transient_provider();
- let addr = tap.insert_account("".sha3(), "").unwrap();
-
let spec = Spec::new_instant();
let engine = &*spec.engine;
let genesis_header = spec.genesis_header();
@@ -87,10 +82,9 @@ mod tests {
let mut db = db_result.take();
spec.ensure_db_good(&mut db, &TrieFactory::new(TrieSpec::Secure)).unwrap();
let last_hashes = Arc::new(vec![genesis_header.hash()]);
- let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, addr, (3141562.into(), 31415620.into()), vec![]).unwrap();
+ let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::default(), (3141562.into(), 31415620.into()), vec![]).unwrap();
let b = b.close_and_lock();
- // Seal with empty AccountProvider.
- let seal = engine.generate_seal(b.block(), Some(&tap)).unwrap();
+ let seal = engine.generate_seal(b.block()).unwrap();
assert!(b.try_seal(engine, seal).is_ok());
}
diff --git a/ethcore/src/engines/mod.rs b/ethcore/src/engines/mod.rs
index d216c88b6..d7ff06248 100644
--- a/ethcore/src/engines/mod.rs
+++ b/ethcore/src/engines/mod.rs
@@ -94,7 +94,7 @@ pub trait Engine : Sync + Send {
///
/// This operation is synchronous and may (quite reasonably) not be available, in which None will
/// be returned.
- fn generate_seal(&self, _block: &ExecutedBlock, _accounts: Option<&AccountProvider>) -> Option> { None }
+ fn generate_seal(&self, _block: &ExecutedBlock) -> Option> { None }
/// Phase 1 quick block verification. Only does checks that are cheap. `block` (the header's full block)
/// may be provided for additional checks. Returns either a null `Ok` or a general error detailing the problem with import.
@@ -147,11 +147,17 @@ pub trait Engine : Sync + Send {
self.builtins().get(a).expect("attempted to execute nonexistent builtin").execute(input, output);
}
- /// Add a channel for communication with Client which can be used for sealing.
- fn register_message_channel(&self, _message_channel: IoChannel) {}
-
/// Check if new block should be chosen as the one in chain.
fn is_new_best_block(&self, best_total_difficulty: U256, _best_header: HeaderView, parent_details: &BlockDetails, new_header: &HeaderView) -> bool {
ethash::is_new_best_block(best_total_difficulty, parent_details, new_header)
}
+
+ /// Register an account which signs consensus messages.
+ fn set_signer(&self, _address: Address, _password: String) {}
+
+ /// Add a channel for communication with Client which can be used for sealing.
+ fn register_message_channel(&self, _message_channel: IoChannel) {}
+
+ /// Add an account provider useful for Engines that sign stuff.
+ fn register_account_provider(&self, _account_provider: Arc) {}
}
diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs
index 3825ce71a..b6ebf57b2 100644
--- a/ethcore/src/miner/miner.rs
+++ b/ethcore/src/miner/miner.rs
@@ -19,11 +19,11 @@ use std::time::{Instant, Duration};
use util::*;
use util::using_queue::{UsingQueue, GetAction};
-use account_provider::AccountProvider;
+use account_provider::{AccountProvider, Error as AccountError};
use views::{BlockView, HeaderView};
use header::Header;
use state::{State, CleanupMode};
-use client::{MiningBlockChainClient, Executive, Executed, EnvInfo, TransactOptions, BlockId, CallAnalytics};
+use client::{MiningBlockChainClient, Executive, Executed, EnvInfo, TransactOptions, BlockId, CallAnalytics, TransactionId};
use client::TransactionImportResult;
use executive::contract_address;
use block::{ClosedBlock, SealedBlock, IsBlock, Block};
@@ -357,6 +357,8 @@ impl Miner {
let block_number = open_block.block().fields().header.number();
// TODO Push new uncles too.
+ let mut tx_count: usize = 0;
+ let tx_total = transactions.len();
for tx in transactions {
let hash = tx.hash();
let start = Instant::now();
@@ -378,7 +380,7 @@ impl Miner {
},
_ => {},
}
-
+ trace!(target: "miner", "Adding tx {:?} took {:?}", hash, took);
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);
@@ -407,9 +409,12 @@ impl Miner {
"Error adding transaction to block: number={}. transaction_hash={:?}, Error: {:?}",
block_number, hash, e);
},
- _ => {} // imported ok
+ _ => {
+ tx_count += 1;
+ } // imported ok
}
}
+ trace!(target: "miner", "Pushed {}/{} transactions", tx_count, tx_total);
let block = open_block.close();
@@ -464,15 +469,12 @@ impl Miner {
/// Attempts to perform internal sealing (one that does not require work) to return Ok(sealed),
/// Err(Some(block)) returns for unsuccesful sealing while Err(None) indicates misspecified engine.
fn seal_block_internally(&self, block: ClosedBlock) -> Result> {
- trace!(target: "miner", "seal_block_internally: block has transaction - attempting internal seal.");
- let s = self.engine.generate_seal(block.block(), match self.accounts {
- Some(ref x) => Some(&**x),
- None => None,
- });
+ trace!(target: "miner", "seal_block_internally: attempting internal seal.");
+ let s = self.engine.generate_seal(block.block());
if let Some(seal) = s {
trace!(target: "miner", "seal_block_internally: managed internal seal. importing...");
- block.lock().try_seal(&*self.engine, seal).or_else(|_| {
- warn!("prepare_sealing: ERROR: try_seal failed when given internally generated seal. WTF?");
+ block.lock().try_seal(&*self.engine, seal).or_else(|(e, _)| {
+ warn!("prepare_sealing: ERROR: try_seal failed when given internally generated seal: {}", e);
Err(None)
})
} else {
@@ -485,7 +487,7 @@ impl Miner {
fn seal_and_import_block_internally(&self, chain: &MiningBlockChainClient, block: ClosedBlock) -> bool {
if !block.transactions().is_empty() || self.forced_sealing() {
if let Ok(sealed) = self.seal_block_internally(block) {
- if chain.import_block(sealed.rlp_bytes()).is_ok() {
+ if chain.import_sealed_block(sealed).is_ok() {
trace!(target: "miner", "import_block_internally: imported internally sealed block");
return true
}
@@ -583,6 +585,10 @@ impl Miner {
let best_block_header: Header = ::rlp::decode(&chain.best_block_header());
transactions.into_iter()
.map(|tx| {
+ if chain.transaction_block(TransactionID::Hash(tx.hash())).is_some() {
+ debug!(target: "miner", "Rejected tx {:?}: already in the blockchain", tx.hash());
+ return Err(Error::Transaction(TransactionError::AlreadyImported));
+ }
match self.engine.verify_transaction_basic(&tx, &best_block_header) {
Err(e) => {
debug!(target: "miner", "Rejected tx {:?} with invalid signature: {:?}", tx.hash(), e);
@@ -740,6 +746,19 @@ impl MinerService for Miner {
*self.author.write() = author;
}
+ fn set_engine_signer(&self, address: Address, password: String) -> Result<(), AccountError> {
+ if self.seals_internally {
+ if let Some(ref ap) = self.accounts {
+ try!(ap.sign(address.clone(), Some(password.clone()), Default::default()));
+ }
+ let mut sealing_work = self.sealing_work.lock();
+ sealing_work.enabled = self.engine.is_sealer(&address).unwrap_or(false);
+ *self.author.write() = address;
+ self.engine.set_signer(address, password);
+ }
+ Ok(())
+ }
+
fn set_extra_data(&self, extra_data: Bytes) {
*self.extra_data.write() = extra_data;
}
@@ -1042,7 +1061,7 @@ impl MinerService for Miner {
ret.map(f)
}
- fn submit_seal(&self, chain: &MiningBlockChainClient, pow_hash: H256, seal: Vec) -> Result<(), Error> {
+ fn submit_seal(&self, chain: &MiningBlockChainClient, block_hash: H256, seal: Vec) -> Result<(), Error> {
let result =
if let Some(b) = self.sealing_work.lock().queue.get_used_if(
if self.options.enable_resubmission {
@@ -1050,22 +1069,22 @@ impl MinerService for Miner {
} else {
GetAction::Take
},
- |b| &b.hash() == &pow_hash
+ |b| &b.hash() == &block_hash
) {
- trace!(target: "miner", "Sealing block {}={}={} with seal {:?}", pow_hash, b.hash(), b.header().bare_hash(), seal);
+ trace!(target: "miner", "Submitted block {}={}={} with seal {:?}", block_hash, b.hash(), b.header().bare_hash(), seal);
b.lock().try_seal(&*self.engine, seal).or_else(|(e, _)| {
warn!(target: "miner", "Mined solution rejected: {}", e);
Err(Error::PowInvalid)
})
} else {
- warn!(target: "miner", "Mined solution rejected: Block unknown or out of date.");
+ warn!(target: "miner", "Submitted solution rejected: Block unknown or out of date.");
Err(Error::PowHashInvalid)
};
result.and_then(|sealed| {
let n = sealed.header().number();
let h = sealed.header().hash();
try!(chain.import_sealed_block(sealed));
- info!(target: "miner", "Mined block imported OK. #{}: {}", Colour::White.bold().paint(format!("{}", n)), Colour::White.bold().paint(h.hex()));
+ info!(target: "miner", "Submitted block imported OK. #{}: {}", Colour::White.bold().paint(format!("{}", n)), Colour::White.bold().paint(h.hex()));
Ok(())
})
}
diff --git a/ethcore/src/miner/mod.rs b/ethcore/src/miner/mod.rs
index 1fb2244fd..8c466581b 100644
--- a/ethcore/src/miner/mod.rs
+++ b/ethcore/src/miner/mod.rs
@@ -76,6 +76,9 @@ pub trait MinerService : Send + Sync {
/// Set the author that we will seal blocks as.
fn set_author(&self, author: Address);
+ /// Set info necessary to sign consensus messages.
+ fn set_engine_signer(&self, address: Address, password: String) -> Result<(), ::account_provider::Error>;
+
/// Get the extra_data that we will seal blocks with.
fn extra_data(&self) -> Bytes;
diff --git a/ethcore/src/trace/db.rs b/ethcore/src/trace/db.rs
index 14129d4d9..174c9b386 100644
--- a/ethcore/src/trace/db.rs
+++ b/ethcore/src/trace/db.rs
@@ -94,7 +94,7 @@ impl Key for TraceGroupPosition {
}
#[derive(Debug, Hash, Eq, PartialEq)]
-enum CacheID {
+enum CacheId {
Trace(H256),
Bloom(TraceGroupPosition),
}
@@ -104,7 +104,7 @@ pub struct TraceDB where T: DatabaseExtras {
// cache
traces: RwLock>,
blooms: RwLock>,
- cache_manager: RwLock>,
+ cache_manager: RwLock>,
// db
tracesdb: Arc,
// config,
@@ -119,7 +119,7 @@ impl BloomGroupDatabase for TraceDB where T: DatabaseExtras {
fn blooms_at(&self, position: &GroupPosition) -> Option {
let position = TraceGroupPosition::from(position.clone());
let result = self.tracesdb.read_with_cache(db::COL_TRACE, &self.blooms, &position).map(Into::into);
- self.note_used(CacheID::Bloom(position));
+ self.note_used(CacheId::Bloom(position));
result
}
}
@@ -152,7 +152,7 @@ impl TraceDB where T: DatabaseExtras {
}
/// Let the cache system know that a cacheable item has been used.
- fn note_used(&self, id: CacheID) {
+ fn note_used(&self, id: CacheId) {
let mut cache_manager = self.cache_manager.write();
cache_manager.note_used(id);
}
@@ -168,8 +168,8 @@ impl TraceDB where T: DatabaseExtras {
cache_manager.collect_garbage(current_size, | ids | {
for id in &ids {
match *id {
- CacheID::Trace(ref h) => { traces.remove(h); },
- CacheID::Bloom(ref h) => { blooms.remove(h); },
+ CacheId::Trace(ref h) => { traces.remove(h); },
+ CacheId::Bloom(ref h) => { blooms.remove(h); },
}
}
traces.shrink_to_fit();
@@ -182,7 +182,7 @@ impl TraceDB where T: DatabaseExtras {
/// Returns traces for block with hash.
fn traces(&self, block_hash: &H256) -> Option {
let result = self.tracesdb.read_with_cache(db::COL_TRACE, &self.traces, block_hash);
- self.note_used(CacheID::Trace(block_hash.clone()));
+ self.note_used(CacheId::Trace(block_hash.clone()));
result
}
@@ -289,7 +289,7 @@ impl TraceDatabase for TraceDB where T: DatabaseExtras {
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 {
- self.note_used(CacheID::Bloom(key));
+ self.note_used(CacheId::Bloom(key));
}
}
@@ -300,7 +300,7 @@ impl TraceDatabase for TraceDB where T: DatabaseExtras {
// cause this value might be queried by hash later
batch.write_with_cache(db::COL_TRACE, &mut *traces, request.block_hash, request.traces, CacheUpdatePolicy::Overwrite);
// note_used must be called after locking traces to avoid cache/traces deadlock on garbage collection
- self.note_used(CacheID::Trace(request.block_hash.clone()));
+ self.note_used(CacheId::Trace(request.block_hash.clone()));
}
}
diff --git a/ethstore/src/json/key_file.rs b/ethstore/src/json/key_file.rs
index 5bc5a4e1c..093479b3a 100644
--- a/ethstore/src/json/key_file.rs
+++ b/ethstore/src/json/key_file.rs
@@ -31,7 +31,7 @@ pub struct KeyFile {
}
enum KeyFileField {
- ID,
+ Id,
Version,
Crypto,
Address,
@@ -56,7 +56,7 @@ impl Visitor for KeyFileFieldVisitor {
where E: Error
{
match value {
- "id" => Ok(KeyFileField::ID),
+ "id" => Ok(KeyFileField::Id),
"version" => Ok(KeyFileField::Version),
"crypto" => Ok(KeyFileField::Crypto),
"Crypto" => Ok(KeyFileField::Crypto),
@@ -94,7 +94,7 @@ impl Visitor for KeyFileVisitor {
loop {
match try!(visitor.visit_key()) {
- Some(KeyFileField::ID) => { id = Some(try!(visitor.visit_value())); }
+ Some(KeyFileField::Id) => { id = Some(try!(visitor.visit_value())); }
Some(KeyFileField::Version) => { version = Some(try!(visitor.visit_value())); }
Some(KeyFileField::Crypto) => { crypto = Some(try!(visitor.visit_value())); }
Some(KeyFileField::Address) => { address = Some(try!(visitor.visit_value())); }
diff --git a/evmjit/Cargo.toml b/evmjit/Cargo.toml
index b7a4d1447..12c57a769 100644
--- a/evmjit/Cargo.toml
+++ b/evmjit/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "evmjit"
-version = "1.4.0"
+version = "1.5.0"
authors = ["debris "]
[lib]
diff --git a/ipc/codegen/Cargo.toml b/ipc/codegen/Cargo.toml
index 1c61db49f..2867609d6 100644
--- a/ipc/codegen/Cargo.toml
+++ b/ipc/codegen/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "ethcore-ipc-codegen"
-version = "1.4.0"
+version = "1.5.0"
authors = ["Nikolay Volf"]
license = "GPL-3.0"
description = "Macros to auto-generate implementations for ipc call"
diff --git a/ipc/nano/Cargo.toml b/ipc/nano/Cargo.toml
index b358eb23a..32171bbf4 100644
--- a/ipc/nano/Cargo.toml
+++ b/ipc/nano/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "ethcore-ipc-nano"
-version = "1.4.0"
+version = "1.5.0"
authors = ["Nikolay Volf "]
license = "GPL-3.0"
diff --git a/ipc/rpc/Cargo.toml b/ipc/rpc/Cargo.toml
index 9e0dfd91b..1aecb3292 100644
--- a/ipc/rpc/Cargo.toml
+++ b/ipc/rpc/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "ethcore-ipc"
-version = "1.4.0"
+version = "1.5.0"
authors = ["Nikolay Volf "]
license = "GPL-3.0"
diff --git a/js/.babelrc b/js/.babelrc
index 2298d98c0..8147da435 100644
--- a/js/.babelrc
+++ b/js/.babelrc
@@ -7,6 +7,7 @@
"transform-runtime",
"transform-decorators-legacy",
"transform-class-properties",
+ "transform-object-rest-spread",
"lodash"
],
"retainLines": true,
diff --git a/js/package.json b/js/package.json
index dd4ff1046..404a625b5 100644
--- a/js/package.json
+++ b/js/package.json
@@ -1,6 +1,6 @@
{
"name": "parity.js",
- "version": "0.2.97",
+ "version": "0.2.105",
"main": "release/index.js",
"jsnext:main": "src/index.js",
"author": "Parity Team ",
@@ -48,25 +48,26 @@
},
"devDependencies": {
"babel-cli": "6.18.0",
- "babel-core": "6.18.2",
+ "babel-core": "6.20.0",
"babel-eslint": "7.1.1",
"babel-loader": "6.2.8",
"babel-plugin-lodash": "3.2.10",
- "babel-plugin-transform-class-properties": "6.19.0",
+ "babel-plugin-transform-class-properties": "6.18.0",
"babel-plugin-transform-decorators-legacy": "1.3.4",
+ "babel-plugin-transform-object-rest-spread": "6.20.2",
"babel-plugin-transform-react-remove-prop-types": "0.2.11",
"babel-plugin-transform-runtime": "6.15.0",
- "babel-polyfill": "6.16.0",
+ "babel-polyfill": "6.20.0",
"babel-preset-es2015": "6.18.0",
- "babel-preset-es2015-rollup": "1.2.0",
"babel-preset-es2016": "6.16.0",
"babel-preset-es2017": "6.16.0",
"babel-preset-react": "6.16.0",
"babel-preset-stage-0": "6.16.0",
"babel-register": "6.18.0",
- "babel-runtime": "6.18.0",
+ "babel-runtime": "6.20.0",
"chai": "3.5.0",
"chai-enzyme": "0.6.1",
+ "circular-dependency-plugin": "2.0.0",
"copy-webpack-plugin": "4.0.1",
"core-js": "2.4.1",
"coveralls": "2.11.15",
diff --git a/js/scripts/release.sh b/js/scripts/release.sh
index 3ff4a577c..1cf3095ef 100755
--- a/js/scripts/release.sh
+++ b/js/scripts/release.sh
@@ -34,11 +34,18 @@ git fetch origin 2>$GITLOG
git checkout -b $BRANCH
echo "*** Committing compiled files for $UTCDATE"
+mv build ../build.new
git add .
-git commit -m "$UTCDATE"
+git commit -m "$UTCDATE [update]"
+git merge origin/$BRANCH -X ours --commit -m "$UTCDATE [merge]"
+git rm -r build
+rm -rf build
+git commit -m "$UTCDATE [cleanup]"
+mv ../build.new build
+git add .
+git commit -m "$UTCDATE [release]"
echo "*** Merging remote"
-git merge origin/$BRANCH -X ours --commit -m "$UTCDATE [release]"
git push origin HEAD:refs/heads/$BRANCH 2>$GITLOG
PRECOMPILED_HASH=`git rev-parse HEAD`
diff --git a/js/scripts/test.js b/js/scripts/test.js
new file mode 100644
index 000000000..78f2f99bd
--- /dev/null
+++ b/js/scripts/test.js
@@ -0,0 +1 @@
+// test script 6
diff --git a/js/src/3rdparty/etherscan/links.js b/js/src/3rdparty/etherscan/links.js
index 2745873fc..e85572850 100644
--- a/js/src/3rdparty/etherscan/links.js
+++ b/js/src/3rdparty/etherscan/links.js
@@ -14,10 +14,14 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see .
+export const url = (isTestnet = false) => {
+ return `https://${isTestnet ? 'testnet.' : ''}etherscan.io`;
+};
+
export const txLink = (hash, isTestnet = false) => {
- return `https://${isTestnet ? 'testnet.' : ''}etherscan.io/tx/${hash}`;
+ return `${url(isTestnet)}/tx/${hash}`;
};
export const addressLink = (address, isTestnet = false) => {
- return `https://${isTestnet ? 'testnet.' : ''}etherscan.io/address/${address}`;
+ return `${url(isTestnet)}/address/${address}`;
};
diff --git a/js/src/api/contract/contract.js b/js/src/api/contract/contract.js
index ed922a02c..bfe7cabc4 100644
--- a/js/src/api/contract/contract.js
+++ b/js/src/api/contract/contract.js
@@ -240,8 +240,8 @@ export default class Contract {
}
_bindEvent = (event) => {
- event.subscribe = (options = {}, callback) => {
- return this._subscribe(event, options, callback);
+ event.subscribe = (options = {}, callback, autoRemove) => {
+ return this._subscribe(event, options, callback, autoRemove);
};
event.unsubscribe = (subscriptionId) => {
@@ -262,12 +262,11 @@ export default class Contract {
}
const options = this._getFilterOptions(event, _options);
+ options.fromBlock = 0;
+ options.toBlock = 'latest';
+
return this._api.eth
- .getLogs({
- fromBlock: 0,
- toBlock: 'latest',
- ...options
- })
+ .getLogs(options)
.then((logs) => this.parseEventLogs(logs));
}
@@ -307,16 +306,31 @@ export default class Contract {
return this._api.eth.newFilter(options);
}
- subscribe (eventName = null, options = {}, callback) {
+ subscribe (eventName = null, options = {}, callback, autoRemove) {
try {
const event = this._findEvent(eventName);
- return this._subscribe(event, options, callback);
+ return this._subscribe(event, options, callback, autoRemove);
} catch (e) {
return Promise.reject(e);
}
}
- _subscribe (event = null, _options, callback) {
+ _sendData (subscriptionId, error, logs) {
+ const { autoRemove, callback } = this._subscriptions[subscriptionId];
+ let result = true;
+
+ try {
+ result = callback(error, logs);
+ } catch (error) {
+ console.warn('_sendData', subscriptionId, error);
+ }
+
+ if (autoRemove && result && typeof result === 'boolean') {
+ this.unsubscribe(subscriptionId);
+ }
+ }
+
+ _subscribe (event = null, _options, callback, autoRemove = false) {
const subscriptionId = nextSubscriptionId++;
const { skipInitFetch } = _options;
delete _options['skipInitFetch'];
@@ -326,6 +340,7 @@ export default class Contract {
.then((filterId) => {
this._subscriptions[subscriptionId] = {
options: _options,
+ autoRemove,
callback,
filterId
};
@@ -338,8 +353,7 @@ export default class Contract {
return this._api.eth
.getFilterLogs(filterId)
.then((logs) => {
- callback(null, this.parseEventLogs(logs));
-
+ this._sendData(subscriptionId, null, this.parseEventLogs(logs));
this._subscribeToChanges();
return subscriptionId;
});
@@ -438,13 +452,13 @@ export default class Contract {
})
)
.then((logsArray) => {
- logsArray.forEach((logs, idx) => {
+ logsArray.forEach((logs, subscriptionId) => {
if (!logs || !logs.length) {
return;
}
try {
- subscriptions[idx].callback(null, this.parseEventLogs(logs));
+ this.sendData(subscriptionId, null, this.parseEventLogs(logs));
} catch (error) {
console.error('_sendSubscriptionChanges', error);
}
diff --git a/js/src/api/format/input.js b/js/src/api/format/input.js
index 6d261c674..16fbd4d2e 100644
--- a/js/src/api/format/input.js
+++ b/js/src/api/format/input.js
@@ -112,11 +112,15 @@ export function inNumber10 (number) {
}
export function inNumber16 (number) {
- if (isInstanceOf(number, BigNumber)) {
- return inHex(number.toString(16));
+ const bn = isInstanceOf(number, BigNumber)
+ ? number
+ : (new BigNumber(number || 0));
+
+ if (!bn.isInteger()) {
+ throw new Error(`[format/input::inNumber16] the given number is not an integer: ${bn.toFormat()}`);
}
- return inHex((new BigNumber(number || 0)).toString(16));
+ return inHex(bn.toString(16));
}
export function inOptions (options) {
@@ -130,6 +134,9 @@ export function inOptions (options) {
case 'gas':
case 'gasPrice':
+ options[key] = inNumber16((new BigNumber(options[key])).round());
+ break;
+
case 'value':
case 'nonce':
options[key] = inNumber16(options[key]);
diff --git a/js/src/api/subscriptions/manager.js b/js/src/api/subscriptions/manager.js
index bc9632592..25e6e6129 100644
--- a/js/src/api/subscriptions/manager.js
+++ b/js/src/api/subscriptions/manager.js
@@ -59,7 +59,7 @@ export default class Manager {
return subscription;
}
- subscribe (subscriptionName, callback) {
+ subscribe (subscriptionName, callback, autoRemove = false) {
return new Promise((resolve, reject) => {
const subscription = this._validateType(subscriptionName);
@@ -75,6 +75,7 @@ export default class Manager {
this.subscriptions[subscriptionId] = {
name: subscriptionName,
id: subscriptionId,
+ autoRemove,
callback
};
@@ -101,13 +102,18 @@ export default class Manager {
}
_sendData (subscriptionId, error, data) {
- const { callback } = this.subscriptions[subscriptionId];
+ const { autoRemove, callback } = this.subscriptions[subscriptionId];
+ let result = true;
try {
- callback(error, data);
+ result = callback(error, data);
} catch (error) {
console.error(`Unable to update callback for subscriptionId ${subscriptionId}`, error);
}
+
+ if (autoRemove && result && typeof result === 'boolean') {
+ this.unsubscribe(subscriptionId);
+ }
}
_updateSubscriptions = (subscriptionName, error, data) => {
diff --git a/js/src/api/subscriptions/personal.js b/js/src/api/subscriptions/personal.js
index 2cb0c3ea0..82671184e 100644
--- a/js/src/api/subscriptions/personal.js
+++ b/js/src/api/subscriptions/personal.js
@@ -68,6 +68,7 @@ export default class Personal {
this._accountsInfo();
return;
+ case 'parity_removeAddress':
case 'parity_setAccountName':
case 'parity_setAccountMeta':
this._accountsInfo();
diff --git a/js/src/contracts/code/index.js b/js/src/contracts/code/index.js
index baa144979..ff6d218eb 100644
--- a/js/src/contracts/code/index.js
+++ b/js/src/contracts/code/index.js
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see .
-import wallet from './wallet';
+import { wallet } from './wallet';
export {
wallet
diff --git a/js/src/contracts/code/wallet.js b/js/src/contracts/code/wallet.js
index a4db4459b..92bcf8795 100644
--- a/js/src/contracts/code/wallet.js
+++ b/js/src/contracts/code/wallet.js
@@ -16,8 +16,14 @@
/**
* @version Solidity v0.4.6
- * @from https://github.com/ethereum/dapp-bin/blob/dd5c485359074d49f571693ae064ce78970f3d6d/wallet/wallet.sol
- * @date 22-Nov-2016 @ 15h00 UTC
+ * @from https://github.com/ethcore/parity/blob/63137b15482344ff9df634c086abaabed452eadc/js/src/contracts/snippets/enhanced-wallet.sol
+ * @date 09-Dec-2016 @ 16h00 UTC
*/
-export default '0x606060405234620000005760405162001a0638038062001a06833981016040528080518201919060200180519060200190919080519060200190919050505b805b83835b600060018351016001819055503373ffffffffffffffffffffffffffffffffffffffff166002600161010081101562000000570160005b5081905550600161010260003373ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550600090505b825181101562000158578281815181101562000000579060200190602002015173ffffffffffffffffffffffffffffffffffffffff1660028260020161010081101562000000570160005b50819055508060020161010260008584815181101562000000579060200190602002015173ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505b806001019050620000b4565b816000819055505b5050508061010581905550620001896200019c6401000000000262001832176401000000009004565b610107819055505b505b505050620001b1565b60006201518042811562000000570490505b90565b61184680620001c06000396000f3606060405236156100f4576000357c010000000000000000000000000000000000000000000000000000000090048063173825d91461015c5780632f54bf6e146101795780634123cb6b146101ac57806352375093146101cf5780635c52c2f5146101f2578063659010e7146102015780637065cb4814610224578063746c917114610241578063797af62714610264578063b20d30a914610297578063b61d27f6146102b4578063b75c7dc614610306578063ba51a6df14610323578063c2cf732614610340578063c41a360a1461037c578063cbf0b0c0146103c3578063f00d4b5d146103e0578063f1736d8614610406575b61015a5b6000341115610157577fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c3334604051808373ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019250505060405180910390a15b5b565b005b34610000576101776004808035906020019091905050610429565b005b34610000576101946004808035906020019091905050610553565b60405180821515815260200191505060405180910390f35b34610000576101b961058b565b6040518082815260200191505060405180910390f35b34610000576101dc610591565b6040518082815260200191505060405180910390f35b34610000576101ff610598565b005b346100005761020e6105d3565b6040518082815260200191505060405180910390f35b346100005761023f60048080359060200190919050506105da565b005b346100005761024e61070d565b6040518082815260200191505060405180910390f35b346100005761027f6004808035906020019091905050610713565b60405180821515815260200191505060405180910390f35b34610000576102b26004808035906020019091905050610abf565b005b34610000576102ec60048080359060200190919080359060200190919080359060200190820180359060200191909192905050610afa565b604051808260001916815260200191505060405180910390f35b34610000576103216004808035906020019091905050610e68565b005b346100005761033e6004808035906020019091905050610f5f565b005b34610000576103646004808035906020019091908035906020019091905050610fe7565b60405180821515815260200191505060405180910390f35b34610000576103976004808035906020019091905050611065565b604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34610000576103de6004808035906020019091905050611085565b005b346100005761040460048080359060200190919080359060200190919050506110d0565b005b3461000057610413611254565b6040518082815260200191505060405180910390f35b60006000366040518083838082843782019150509250505060405180910390206104528161125b565b1561054d5761010260008473ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054915060008214156104925761054c565b60016001540360005411156104a65761054c565b6000600283610100811015610000570160005b5081905550600061010260008573ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506104f661147f565b6104fe61157a565b7f58619076adf5bb0943d100ef88d52d7c3fd691b19d3a9071b555b651fbf418da83604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a15b5b5b505050565b6000600061010260008473ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541190505b919050565b60015481565b6101075481565b6000366040518083838082843782019150509250505060405180910390206105bf8161125b565b156105cf576000610106819055505b5b5b50565b6101065481565b6000366040518083838082843782019150509250505060405180910390206106018161125b565b156107085761060f82610553565b1561061957610707565b61062161147f565b60fa6001541015156106365761063561157a565b5b60fa60015410151561064757610707565b6001600081548092919060010191905055508173ffffffffffffffffffffffffffffffffffffffff166002600154610100811015610000570160005b508190555060015461010260008473ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055507f994a936646fe87ffe4f1e469d3d6aa417d6b855598397f323de5b449f765f0c382604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a15b5b5b5050565b60005481565b60008161071f8161125b565b15610ab857600061010860008560001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141515610ab65761010860008460001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1661010860008560001916815260200190815260200160002060010154610108600086600019168152602001908152602001600020600201604051808280546001816001161561010002031660029004801561086d5780601f106108425761010080835404028352916020019161086d565b820191906000526020600020905b81548152906001019060200180831161085057829003601f168201915b505091505060006040518083038185876185025a03f192505050507fe7c957c06e9a662c1a6c77366179f5b702b97651dc28eee7d5bf1dff6e40bb4a33846101086000876000191681526020019081526020016000206001015461010860008860001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16610108600089600019168152602001908152602001600020600201604051808673ffffffffffffffffffffffffffffffffffffffff168152602001856000191681526020018481526020018373ffffffffffffffffffffffffffffffffffffffff168152602001806020018281038252838181546001816001161561010002031660029004815260200191508054600181600116156101000203166002900480156109ef5780601f106109c4576101008083540402835291602001916109ef565b820191906000526020600020905b8154815290600101906020018083116109d257829003601f168201915b5050965050505050505060405180910390a161010860008460001916815260200190815260200160002060006000820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055600182016000905560028201805460018160011615610100020316600290046000825580601f10610a735750610aaa565b601f016020900490600052602060002090810190610aa991905b80821115610aa5576000816000905550600101610a8d565b5090565b5b50505060019150610ab7565b5b5b5b50919050565b600036604051808383808284378201915050925050506040518091039020610ae68161125b565b15610af55781610105819055505b5b5b5050565b6000610b0533610553565b15610e5f57610b13846116c9565b15610bfc577f92ca3a80853e6663fa31fa10b99225f18d4902939b4c53a9caae9043f6efd0043385878686604051808673ffffffffffffffffffffffffffffffffffffffff1681526020018581526020018473ffffffffffffffffffffffffffffffffffffffff1681526020018060200182810382528484828181526020019250808284378201915050965050505050505060405180910390a18473ffffffffffffffffffffffffffffffffffffffff168484846040518083838082843782019150509250505060006040518083038185876185025a03f1925050505060006001029050610e5e565b60003643604051808484808284378201915050828152602001935050505060405180910390209050610c2d81610713565b158015610c8b5750600061010860008360001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16145b15610e5d578461010860008360001916815260200190815260200160002060000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff02191690836c01000000000000000000000000908102040217905550836101086000836000191681526020019081526020016000206001018190555082826101086000846000191681526020019081526020016000206002019190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10610d6657803560ff1916838001178555610d94565b82800160010185558215610d94579182015b82811115610d93578235825591602001919060010190610d78565b5b509050610db991905b80821115610db5576000816000905550600101610d9d565b5090565b50507f1733cbb53659d713b79580f79f3f9ff215f78a7c7aa45890f3b89fc5cddfbf3281338688878760405180876000191681526020018673ffffffffffffffffffffffffffffffffffffffff1681526020018581526020018473ffffffffffffffffffffffffffffffffffffffff168152602001806020018281038252848482818152602001925080828437820191505097505050505050505060405180910390a15b5b5b5b949350505050565b60006000600061010260003373ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205492506000831415610ea957610f59565b8260020a915061010360008560001916815260200190815260200160002090506000828260010154161115610f585780600001600081548092919060010191905055508181600101600082825403925050819055507fc7fb647e59b18047309aa15aad418e5d7ca96d173ad704f1031a2c3d7591734b3385604051808373ffffffffffffffffffffffffffffffffffffffff168152602001826000191681526020019250505060405180910390a15b5b50505050565b600036604051808383808284378201915050925050506040518091039020610f868161125b565b15610fe257600154821115610f9a57610fe1565b81600081905550610fa961147f565b7facbdb084c721332ac59f9b8e392196c9eb0e4932862da8eb9beaf0dad4f550da826040518082815260200191505060405180910390a15b5b5b5050565b6000600060006000610103600087600019168152602001908152602001600020925061010260008673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205491506000821415611048576000935061105c565b8160020a9050600081846001015416141593505b50505092915050565b6000600260018301610100811015610000570160005b505490505b919050565b6000366040518083838082843782019150509250505060405180910390206110ac8161125b565b156110cb578173ffffffffffffffffffffffffffffffffffffffff16ff5b5b5b5050565b60006000366040518083838082843782019150509250505060405180910390206110f98161125b565b1561124d5761110783610553565b156111115761124c565b61010260008573ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549150600082141561114c5761124c565b61115461147f565b8273ffffffffffffffffffffffffffffffffffffffff16600283610100811015610000570160005b5081905550600061010260008673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508161010260008573ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055507fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c8484604051808373ffffffffffffffffffffffffffffffffffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1681526020019250505060405180910390a15b5b5b50505050565b6101055481565b600060006000600061010260003373ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549250600083141561129e57611477565b61010360008660001916815260200190815260200160002091506000826000015414156113555760005482600001819055506000826001018190555061010480548091906001018154818355818115116113245781836000526020600020918201910161132391905b8082111561131f576000816000905550600101611307565b5090565b5b5050508260020181905550846101048360020154815481101561000057906000526020600020900160005b50819055505b8260020a90506000818360010154161415611476577fe1c52dc63b719ade82e8bea94cc41a0d5d28e4aaf536adb5e9cccc9ff8c1aeda3386604051808373ffffffffffffffffffffffffffffffffffffffff168152602001826000191681526020019250505060405180910390a16001826000015411151561144d5761010461010360008760001916815260200190815260200160002060020154815481101561000057906000526020600020900160005b5060009055610103600086600019168152602001908152602001600020600060008201600090556001820160009055600282016000905550506001935061147756611475565b8160000160008154809291906001900391905055508082600101600082825417925050819055505b5b5b505050919050565b60006000610104805490509150600090505b8181101561156d57610108600061010483815481101561000057906000526020600020900160005b505460001916815260200190815260200160002060006000820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055600182016000905560028201805460018160011615610100020316600290046000825580601f10611527575061155e565b601f01602090049060005260206000209081019061155d91905b80821115611559576000816000905550600101611541565b5090565b5b5050505b806001019050611491565b61157561174f565b5b5050565b6000600190505b6001548110156116c5575b600154811080156115b057506000600282610100811015610000570160005b505414155b156115c257808060010191505061158c565b5b60016001541180156115e9575060006002600154610100811015610000570160005b5054145b1561160657600160008154809291906001900391905055506115c3565b6001548110801561162c575060006002600154610100811015610000570160005b505414155b801561164a57506000600282610100811015610000570160005b5054145b156116c0576002600154610100811015610000570160005b5054600282610100811015610000570160005b5081905550806101026000600284610100811015610000570160005b505481526020019081526020016000208190555060006002600154610100811015610000570160005b50819055505b611581565b5b50565b60006116d433610553565b1561174957610107546116e5611832565b1115611704576000610106819055506116fc611832565b610107819055505b610106548261010654011015801561172457506101055482610106540111155b1561174357816101066000828254019250508190555060019050611748565b600090505b5b5b919050565b60006000610104805490509150600090505b818110156117f357600060010261010482815481101561000057906000526020600020900160005b5054600019161415156117e757610103600061010483815481101561000057906000526020600020900160005b5054600019168152602001908152602001600020600060008201600090556001820160009055600282016000905550505b5b806001019050611761565b6101048054600082559060005260206000209081019061182b91905b8082111561182757600081600090555060010161180f565b5090565b5b505b5050565b600062015180428115610000570490505b9056';
+export const wallet = '0x6060604052346100005760405161041b38038061041b83398101604090815281516020830151918301519201915b604080517f696e697457616c6c657428616464726573735b5d2c75696e743235362c75696e81527f7432353629000000000000000000000000000000000000000000000000000000602080830191909152915190819003602501902084516000829052909173__WalletLibrary_________________________91600281019160049182010290819038829003903960006000600483016000866127105a03f45b505050505050505b610337806100e46000396000f36060604052361561006c5760e060020a60003504632f54bf6e81146101245780634123cb6b146101485780635237509314610167578063659010e714610186578063746c9171146101a5578063c2cf7326146101c4578063c41a360a146101eb578063f1736d8614610217575b6101225b60003411156100c15760408051600160a060020a033316815234602082015281517fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c929181900390910190a161011e565b600036111561011e5773__WalletLibrary_________________________600160a060020a0316600036600060405160200152604051808383808284378201915050925050506020604051808303818560325a03f4156100005750505b5b5b565b005b3461000057610134600435610236565b604080519115158252519081900360200190f35b3461000057610155610297565b60408051918252519081900360200190f35b346100005761015561029d565b60408051918252519081900360200190f35b34610000576101556102a3565b60408051918252519081900360200190f35b34610000576101556102a9565b60408051918252519081900360200190f35b34610000576101346004356024356102af565b604080519115158252519081900360200190f35b34610000576101fb600435610311565b60408051600160a060020a039092168252519081900360200190f35b3461000057610155610331565b60408051918252519081900360200190f35b600073__WalletLibrary_________________________600160a060020a0316600036600060405160200152604051808383808284378201915050925050506020604051808303818560325a03f4156100005750506040515190505b919050565b60015481565b60045481565b60035481565b60005481565b600073__WalletLibrary_________________________600160a060020a0316600036600060405160200152604051808383808284378201915050925050506020604051808303818560325a03f4156100005750506040515190505b92915050565b6000600582600101610100811015610000570160005b505490505b919050565b6002548156';
+export const walletLibrary = '0x606060405234610000575b611381806100186000396000f3606060405236156100da5760e060020a6000350463173825d981146100df5780632f54bf6e146100f157806352375093146101155780635c52c2f514610134578063659010e7146101435780637065cb4814610162578063797af627146101745780639da5e0eb14610198578063b20d30a9146101aa578063b61d27f6146101bc578063b75c7dc614610227578063ba51a6df14610239578063c2cf73261461024b578063c57c5f6014610272578063cbf0b0c0146102c6578063e46dcfeb146102d8578063f00d4b5d14610331578063f1736d8614610346575b610000565b34610000576100ef600435610365565b005b3461000057610101600435610452565b604080519115158252519081900360200190f35b3461000057610122610473565b60408051918252519081900360200190f35b34610000576100ef610479565b005b34610000576101226104b0565b60408051918252519081900360200190f35b34610000576100ef6004356104b6565b005b34610000576101016004356105a5565b604080519115158252519081900360200190f35b34610000576100ef60043561081e565b005b34610000576100ef600435610832565b005b3461000057604080516020600460443581810135601f810184900484028501840190955284845261010194823594602480359560649492939190920191819084018382808284375094965061086a95505050505050565b604080519115158252519081900360200190f35b34610000576100ef600435610bcc565b005b34610000576100ef600435610c77565b005b3461000057610101600435602435610cf9565b604080519115158252519081900360200190f35b34610000576100ef6004808035906020019082018035906020019080806020026020016040519081016040528093929190818152602001838360200280828437509496505093359350610d4e92505050565b005b34610000576100ef600435610e13565b005b34610000576100ef60048080359060200190820180359060200190808060200260200160405190810160405280939291908181526020018383602002808284375094965050843594602001359350610e5192505050565b005b34610000576100ef600435602435610e6a565b005b3461000057610122610f63565b60408051918252519081900360200190f35b600060003660405180838380828437820191505092505050604051809103902061038e81610f69565b1561044b57600160a060020a0383166000908152610105602052604090205491508115156103bb5761044b565b60016001540360005411156103cf5761044b565b6000600583610100811015610000570160005b5055600160a060020a03831660009081526101056020526040812055610406611108565b61040e6111dc565b60408051600160a060020a038516815290517f58619076adf5bb0943d100ef88d52d7c3fd691b19d3a9071b555b651fbf418da9181900360200190a15b5b5b505050565b600160a060020a03811660009081526101056020526040812054115b919050565b60045481565b6000366040518083838082843782019150509250505060405180910390206104a081610f69565b156104ab5760006003555b5b5b50565b60035481565b6000366040518083838082843782019150509250505060405180910390206104dd81610f69565b1561059f576104eb82610452565b156104f55761059f565b6104fd611108565b60015460fa9010610510576105106111dc565b5b60015460fa90106105215761059f565b60018054810190819055600160a060020a03831690600590610100811015610000570160005b5055600154600160a060020a03831660008181526101056020908152604091829020939093558051918252517f994a936646fe87ffe4f1e469d3d6aa417d6b855598397f323de5b449f765f0c3929181900390910190a15b5b5b5050565b6000816105b181610f69565b156108155760008381526101086020526040902054600160a060020a0316156108155760008381526101086020526040908190208054600180830154935160029384018054600160a060020a0390941695949093919283928592918116156101000260001901160480156106665780601f1061063b57610100808354040283529160200191610666565b820191906000526020600020905b81548152906001019060200180831161064957829003601f168201915b505091505060006040518083038185876185025a03f15050506000848152610108602090815260409182902060018082015482548551600160a060020a033381811683529682018c905296810183905295166060860181905260a06080870181815260029586018054958616156101000260001901909516959095049087018190527fe7c957c06e9a662c1a6c77366179f5b702b97651dc28eee7d5bf1dff6e40bb4a975094958a95929491939290919060c08301908490801561076b5780601f106107405761010080835404028352916020019161076b565b820191906000526020600020905b81548152906001019060200180831161074e57829003601f168201915b5050965050505050505060405180910390a1600083815261010860205260408120805473ffffffffffffffffffffffffffffffffffffffff19168155600180820183905560028083018054858255939493909281161561010002600019011604601f8190106107da575061080c565b601f01602090049060005260206000209081019061080c91905b8082111561080857600081556001016107f4565b5090565b5b505050600191505b5b5b5b50919050565b600281905561082b61130b565b6004555b50565b60003660405180838380828437820191505092505050604051809103902061085981610f69565b1561059f5760028290555b5b5b5050565b6000600061087733610452565b15610bc05761088584611315565b156109bc577f92ca3a80853e6663fa31fa10b99225f18d4902939b4c53a9caae9043f6efd004338587866040518085600160a060020a0316815260200184815260200183600160a060020a03168152602001806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156109335780820380516001836020036101000a031916815260200191505b509550505050505060405180910390a184600160a060020a03168484604051808280519060200190808383829060006004602084601f0104600302600f01f150905090810190601f16801561099c5780820380516001836020036101000a031916815260200191505b5091505060006040518083038185876185025a03f1925050509150610bc0565b600036436040518084848082843782019150508281526020019350505050604051809103902090506109ed816105a5565b158015610a10575060008181526101086020526040902054600160a060020a0316155b15610bc057600081815261010860209081526040822080546c01000000000000000000000000808a020473ffffffffffffffffffffffffffffffffffffffff199091161781556001808201889055865160029283018054818752958590209095601f9381161561010002600019011693909304820184900483019390929190880190839010610aaa57805160ff1916838001178555610ad7565b82800160010185558215610ad7579182015b82811115610ad7578251825591602001919060010190610abc565b5b50610af89291505b8082111561080857600081556001016107f4565b5090565b50507f1733cbb53659d713b79580f79f3f9ff215f78a7c7aa45890f3b89fc5cddfbf328133868887604051808660001916815260200185600160a060020a0316815260200184815260200183600160a060020a03168152602001806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f168015610bae5780820380516001836020036101000a031916815260200191505b50965050505050505060405180910390a15b5b5b5b5b509392505050565b600160a060020a033316600090815261010560205260408120549080821515610bf457610c70565b50506000828152610106602052604081206001810154600284900a929083161115610c705780546001908101825581018054839003905560408051600160a060020a03331681526020810186905281517fc7fb647e59b18047309aa15aad418e5d7ca96d173ad704f1031a2c3d7591734b929181900390910190a15b5b50505050565b600036604051808383808284378201915050925050506040518091039020610c9e81610f69565b1561059f57600154821115610cb25761059f565b6000829055610cbf611108565b6040805183815290517facbdb084c721332ac59f9b8e392196c9eb0e4932862da8eb9beaf0dad4f550da9181900360200190a15b5b5b5050565b600082815261010660209081526040808320600160a060020a038516845261010590925282205482811515610d315760009350610d45565b8160020a9050808360010154166000141593505b50505092915050565b815160019081019055600033600160a060020a03166006825b505550600160a060020a033316600090815261010560205260408120600190558181555b825181101561044b57828181518110156100005790602001906020020151600160a060020a0316600582600201610100811015610000570160005b5081905550806002016101056000858481518110156100005790602001906020020151600160a060020a03168152602001908152602001600020819055505b600101610d8b565b5b505050565b600036604051808383808284378201915050925050506040518091039020610e3a81610f69565b1561059f5781600160a060020a0316ff5b5b5b5050565b610e5b8383610d4e565b61044b8161081e565b5b505050565b6000600036604051808383808284378201915050925050506040518091039020610e9381610f69565b15610c7057610ea183610452565b15610eab57610c70565b600160a060020a038416600090815261010560205260409020549150811515610ed357610c70565b610edb611108565b82600160a060020a0316600583610100811015610000570160005b5055600160a060020a0380851660008181526101056020908152604080832083905593871680835291849020869055835192835282015281517fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c929181900390910190a15b5b5b50505050565b60025481565b600160a060020a033316600090815261010560205260408120548180821515610f91576110fe565b60008581526101066020526040902080549092501515611025576000805483556001808401919091556101078054918201808255828015829011610ffa57600083815260209020610ffa9181019083015b8082111561080857600081556001016107f4565b5090565b5b50505060028301819055610107805487929081101561000057906000526020600020900160005b50555b8260020a905080826001015416600014156110fe5760408051600160a060020a03331681526020810187905281517fe1c52dc63b719ade82e8bea94cc41a0d5d28e4aaf536adb5e9cccc9ff8c1aeda929181900390910190a18154600190116110eb5760008581526101066020526040902060020154610107805490919081101561000057906000526020600020900160005b5060009081905585815261010660205260408120818155600180820183905560029091019190915593506110fe566110fe565b8154600019018255600182018054821790555b5b5b505050919050565b6101075460005b818110156111855761010781815481101561000057906000526020600020900160005b50541561117c57610106600061010783815481101561000057906000526020600020900160005b505481526020810191909152604001600090812081815560018101829055600201555b5b60010161110f565b610107805460008083559190915261044b907f47c4908e245f386bfc1825973249847f4053a761ddb4880ad63c323a7b5a2a25908101905b8082111561080857600081556001016107f4565b5090565b5b505b5050565b60015b6001548110156104ab575b6001548110801561120c5750600581610100811015610000570160005b505415155b15611219576001016111ea565b5b600160015411801561123e57506005600154610100811015610000570160005b5054155b15611252576001805460001901905561121a565b6001548110801561127657506005600154610100811015610000570160005b505415155b80156112925750600581610100811015610000570160005b5054155b15611302576005600154610100811015610000570160005b5054600582610100811015610000570160005b5055806101056000600583610100811015610000570160005b505481526020019081526020016000208190555060006005600154610100811015610000570160005b50555b6111df565b5b50565b6201518042045b90565b600061132033610452565b1561046e5760045461133061130b565b111561134757600060035561134361130b565b6004555b600354828101108015906113615750600254826003540111155b1561137657506003805482019055600161046e565b5060005b5b5b91905056';
+export const walletSourceURL = 'https://github.com/ethcore/parity/blob/63137b15482344ff9df634c086abaabed452eadc/js/src/contracts/snippets/enhanced-wallet.sol';
+export const walletLibraryRegKey = 'walletLibrary';
+// Used if no Wallet Library found in registry...
+// Compiled from `wallet.sol` using Solidity v0.4.6
+export const fullWalletCode = '0x606060405234610000576040516113bb3803806113bb83398101604090815281516020830151918301519201915b805b83835b815160019081019055600033600160a060020a03166003825b505550600160a060020a033316600090815261010260205260408120600190555b82518110156100ee57828181518110156100005790602001906020020151600160a060020a0316600282600201610100811015610000570160005b5081905550806002016101026000858481518110156100005790602001906020020151600160a060020a03168152602001908152602001600020819055505b60010161006c565b60008290555b50505061010581905561011264010000000061127861012182021704565b610107555b505b50505061012b565b6201518042045b90565b611282806101396000396000f3606060405236156100da5760e060020a6000350463173825d981146101305780632f54bf6e146101425780634123cb6b1461016657806352375093146101855780635c52c2f5146101a4578063659010e7146101b35780637065cb48146101d2578063746c9171146101e4578063797af62714610203578063b20d30a914610227578063b61d27f614610239578063b75c7dc61461026b578063ba51a6df1461027d578063c2cf73261461028f578063c41a360a146102b6578063cbf0b0c0146102e2578063f00d4b5d146102f4578063f1736d8614610309575b61012e5b600034111561012b5760408051600160a060020a033316815234602082015281517fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c929181900390910190a15b5b565b005b346100005761012e600435610328565b005b3461000057610152600435610415565b604080519115158252519081900360200190f35b3461000057610173610436565b60408051918252519081900360200190f35b346100005761017361043c565b60408051918252519081900360200190f35b346100005761012e610443565b005b346100005761017361047b565b60408051918252519081900360200190f35b346100005761012e600435610482565b005b3461000057610173610571565b60408051918252519081900360200190f35b3461000057610152600435610577565b604080519115158252519081900360200190f35b346100005761012e6004356107e3565b005b34610000576101736004803590602480359160443591820191013561081c565b60408051918252519081900360200190f35b346100005761012e600435610ab3565b005b346100005761012e600435610b5e565b005b3461000057610152600435602435610be0565b604080519115158252519081900360200190f35b34610000576102c6600435610c35565b60408051600160a060020a039092168252519081900360200190f35b346100005761012e600435610c55565b005b346100005761012e600435602435610c93565b005b3461000057610173610d8c565b60408051918252519081900360200190f35b600060003660405180838380828437820191505092505050604051809103902061035181610d93565b1561040e57600160a060020a03831660009081526101026020526040902054915081151561037e5761040e565b60016001540360005411156103925761040e565b6000600283610100811015610000570160005b5055600160a060020a038316600090815261010260205260408120556103c9610f32565b6103d1611002565b60408051600160a060020a038516815290517f58619076adf5bb0943d100ef88d52d7c3fd691b19d3a9071b555b651fbf418da9181900360200190a15b5b5b505050565b600160a060020a03811660009081526101026020526040812054115b919050565b60015481565b6101075481565b60003660405180838380828437820191505092505050604051809103902061046a81610d93565b15610476576000610106555b5b5b50565b6101065481565b6000366040518083838082843782019150509250505060405180910390206104a981610d93565b1561056b576104b782610415565b156104c15761056b565b6104c9610f32565b60015460fa90106104dc576104dc611002565b5b60015460fa90106104ed5761056b565b60018054810190819055600160a060020a03831690600290610100811015610000570160005b5055600154600160a060020a03831660008181526101026020908152604091829020939093558051918252517f994a936646fe87ffe4f1e469d3d6aa417d6b855598397f323de5b449f765f0c3929181900390910190a15b5b5b5050565b60005481565b60008161058381610d93565b156107da5760008381526101086020526040902054600160a060020a0316156107da5760008381526101086020526040908190208054600180830154935160029384018054600160a060020a0390941695949093919283928592918116156101000260001901160480156106385780601f1061060d57610100808354040283529160200191610638565b820191906000526020600020905b81548152906001019060200180831161061b57829003601f168201915b505091505060006040518083038185876185025a03f15050506000848152610108602090815260409182902060018082015482548551600160a060020a033381811683529682018c905296810183905295166060860181905260a06080870181815260029586018054958616156101000260001901909516959095049087018190527fe7c957c06e9a662c1a6c77366179f5b702b97651dc28eee7d5bf1dff6e40bb4a975094958a95929491939290919060c08301908490801561073d5780601f106107125761010080835404028352916020019161073d565b820191906000526020600020905b81548152906001019060200180831161072057829003601f168201915b5050965050505050505060405180910390a16000838152610108602052604081208054600160a060020a0319168155600180820183905560028083018054858255939493909281161561010002600019011604601f81901061079f57506107d1565b601f0160209004906000526020600020908101906107d191905b808211156107cd57600081556001016107b9565b5090565b5b505050600191505b5b5b5b50919050565b60003660405180838380828437820191505092505050604051809103902061080a81610d93565b1561056b576101058290555b5b5b5050565b600061082733610415565b15610aa85761083584611131565b156108f3577f92ca3a80853e6663fa31fa10b99225f18d4902939b4c53a9caae9043f6efd00433858786866040518086600160a060020a0316815260200185815260200184600160a060020a0316815260200180602001828103825284848281815260200192508082843760405192018290039850909650505050505050a184600160a060020a03168484846040518083838082843782019150509250505060006040518083038185876185025a03f15060009350610aa892505050565b6000364360405180848480828437820191505082815260200193505050506040518091039020905061092481610577565b158015610947575060008181526101086020526040902054600160a060020a0316155b15610aa857600081815261010860209081526040822080546c01000000000000000000000000808a0204600160a060020a0319909116178155600180820188905560029182018054818652948490209094601f928116156101000260001901169290920481019290920481019185919087908390106109d15782800160ff198235161785556109fe565b828001600101855582156109fe579182015b828111156109fe5782358255916020019190600101906109e3565b5b50610a1f9291505b808211156107cd57600081556001016107b9565b5090565b50507f1733cbb53659d713b79580f79f3f9ff215f78a7c7aa45890f3b89fc5cddfbf32813386888787604051808760001916815260200186600160a060020a0316815260200185815260200184600160a060020a031681526020018060200182810382528484828181526020019250808284376040519201829003995090975050505050505050a15b5b5b5b949350505050565b600160a060020a033316600090815261010260205260408120549080821515610adb57610b57565b50506000828152610103602052604081206001810154600284900a929083161115610b575780546001908101825581018054839003905560408051600160a060020a03331681526020810186905281517fc7fb647e59b18047309aa15aad418e5d7ca96d173ad704f1031a2c3d7591734b929181900390910190a15b5b50505050565b600036604051808383808284378201915050925050506040518091039020610b8581610d93565b1561056b57600154821115610b995761056b565b6000829055610ba6610f32565b6040805183815290517facbdb084c721332ac59f9b8e392196c9eb0e4932862da8eb9beaf0dad4f550da9181900360200190a15b5b5b5050565b600082815261010360209081526040808320600160a060020a038516845261010290925282205482811515610c185760009350610c2c565b8160020a9050808360010154166000141593505b50505092915050565b6000600282600101610100811015610000570160005b505490505b919050565b600036604051808383808284378201915050925050506040518091039020610c7c81610d93565b1561056b5781600160a060020a0316ff5b5b5b5050565b6000600036604051808383808284378201915050925050506040518091039020610cbc81610d93565b15610b5757610cca83610415565b15610cd457610b57565b600160a060020a038416600090815261010260205260409020549150811515610cfc57610b57565b610d04610f32565b82600160a060020a0316600283610100811015610000570160005b5055600160a060020a0380851660008181526101026020908152604080832083905593871680835291849020869055835192835282015281517fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c929181900390910190a15b5b5b50505050565b6101055481565b600160a060020a033316600090815261010260205260408120548180821515610dbb57610f28565b60008581526101036020526040902080549092501515610e4f576000805483556001808401919091556101048054918201808255828015829011610e2457600083815260209020610e249181019083015b808211156107cd57600081556001016107b9565b5090565b5b50505060028301819055610104805487929081101561000057906000526020600020900160005b50555b8260020a90508082600101541660001415610f285760408051600160a060020a03331681526020810187905281517fe1c52dc63b719ade82e8bea94cc41a0d5d28e4aaf536adb5e9cccc9ff8c1aeda929181900390910190a1815460019011610f155760008581526101036020526040902060020154610104805490919081101561000057906000526020600020900160005b506000908190558581526101036020526040812081815560018082018390556002909101919091559350610f2856610f28565b8154600019018255600182018054821790555b5b5b505050919050565b6101045460005b81811015610ff557610108600061010483815481101561000057906000526020600020900160005b50548152602081019190915260400160009081208054600160a060020a0319168155600180820183905560028083018054858255939493909281161561010002600019011604601f819010610fb65750610fe8565b601f016020900490600052602060002090810190610fe891905b808211156107cd57600081556001016107b9565b5090565b5b5050505b600101610f39565b61056b6111a4565b5b5050565b60015b600154811015610476575b600154811080156110325750600281610100811015610000570160005b505415155b1561103f57600101611010565b5b600160015411801561106457506002600154610100811015610000570160005b5054155b156110785760018054600019019055611040565b6001548110801561109c57506002600154610100811015610000570160005b505415155b80156110b85750600281610100811015610000570160005b5054155b15611128576002600154610100811015610000570160005b5054600282610100811015610000570160005b5055806101026000600283610100811015610000570160005b505481526020019081526020016000208190555060006002600154610100811015610000570160005b50555b611005565b5b50565b600061113c33610415565b15610431576101075461114d611278565b111561116657600061010655611161611278565b610107555b610106548281011080159061118357506101055482610106540111155b1561119957506101068054820190556001610431565b5060005b5b5b919050565b6101045460005b818110156112215761010481815481101561000057906000526020600020900160005b50541561121857610103600061010483815481101561000057906000526020600020900160005b505481526020810191909152604001600090812081815560018101829055600201555b5b6001016111ab565b610104805460008083559190915261040e907f4c0be60200faa20559308cb7b5a1bb3255c16cb1cab91f525b5ae7a03d02fabe908101905b808211156107cd57600081556001016107b9565b5090565b5b505b5050565b6201518042045b9056';
diff --git a/js/src/contracts/snippets/enhanced-wallet.sol b/js/src/contracts/snippets/enhanced-wallet.sol
new file mode 100644
index 000000000..374eb595f
--- /dev/null
+++ b/js/src/contracts/snippets/enhanced-wallet.sol
@@ -0,0 +1,460 @@
+//sol Wallet
+// Multi-sig, daily-limited account proxy/wallet.
+// @authors:
+// Gav Wood
+// inheritable "property" contract that enables methods to be protected by requiring the acquiescence of either a
+// single, or, crucially, each of a number of, designated owners.
+// usage:
+// use modifiers onlyowner (just own owned) or onlymanyowners(hash), whereby the same hash must be provided by
+// some number (specified in constructor) of the set of owners (specified in the constructor, modifiable) before the
+// interior is executed.
+pragma solidity ^0.4.6;
+
+contract multisig {
+ // EVENTS
+
+ // this contract can accept a confirmation, in which case
+ // we record owner and operation (hash) alongside it.
+ event Confirmation(address owner, bytes32 operation);
+ event Revoke(address owner, bytes32 operation);
+
+ // some others are in the case of an owner changing.
+ event OwnerChanged(address oldOwner, address newOwner);
+ event OwnerAdded(address newOwner);
+ event OwnerRemoved(address oldOwner);
+
+ // the last one is emitted if the required signatures change
+ event RequirementChanged(uint newRequirement);
+
+ // Funds has arrived into the wallet (record how much).
+ event Deposit(address _from, uint value);
+ // Single transaction going out of the wallet (record who signed for it, how much, and to whom it's going).
+ event SingleTransact(address owner, uint value, address to, bytes data);
+ // Multi-sig transaction going out of the wallet (record who signed for it last, the operation hash, how much, and to whom it's going).
+ event MultiTransact(address owner, bytes32 operation, uint value, address to, bytes data);
+ // Confirmation still needed for a transaction.
+ event ConfirmationNeeded(bytes32 operation, address initiator, uint value, address to, bytes data);
+}
+
+contract multisigAbi is multisig {
+ function isOwner(address _addr) returns (bool);
+
+ function hasConfirmed(bytes32 _operation, address _owner) constant returns (bool);
+
+ function confirm(bytes32 _h) returns(bool);
+
+ // (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent today.
+ function setDailyLimit(uint _newLimit);
+
+ function addOwner(address _owner);
+
+ function removeOwner(address _owner);
+
+ function changeRequirement(uint _newRequired);
+
+ // Revokes a prior confirmation of the given operation
+ function revoke(bytes32 _operation);
+
+ function changeOwner(address _from, address _to);
+
+ function execute(address _to, uint _value, bytes _data) returns(bool);
+}
+
+contract WalletLibrary is multisig {
+ // TYPES
+
+ // struct for the status of a pending operation.
+ struct PendingState {
+ uint yetNeeded;
+ uint ownersDone;
+ uint index;
+ }
+
+ // Transaction structure to remember details of transaction lest it need be saved for a later call.
+ struct Transaction {
+ address to;
+ uint value;
+ bytes data;
+ }
+
+ /******************************
+ ***** MULTI OWNED SECTION ****
+ ******************************/
+
+ // MODIFIERS
+
+ // simple single-sig function modifier.
+ modifier onlyowner {
+ if (isOwner(msg.sender))
+ _;
+ }
+ // multi-sig function modifier: the operation must have an intrinsic hash in order
+ // that later attempts can be realised as the same underlying operation and
+ // thus count as confirmations.
+ modifier onlymanyowners(bytes32 _operation) {
+ if (confirmAndCheck(_operation))
+ _;
+ }
+
+ // METHODS
+
+ // constructor is given number of sigs required to do protected "onlymanyowners" transactions
+ // as well as the selection of addresses capable of confirming them.
+ function initMultiowned(address[] _owners, uint _required) {
+ m_numOwners = _owners.length + 1;
+ m_owners[1] = uint(msg.sender);
+ m_ownerIndex[uint(msg.sender)] = 1;
+ m_required = _required;
+
+ for (uint i = 0; i < _owners.length; ++i)
+ {
+ m_owners[2 + i] = uint(_owners[i]);
+ m_ownerIndex[uint(_owners[i])] = 2 + i;
+ }
+ }
+
+ // Revokes a prior confirmation of the given operation
+ function revoke(bytes32 _operation) {
+ uint ownerIndex = m_ownerIndex[uint(msg.sender)];
+ // make sure they're an owner
+ if (ownerIndex == 0) return;
+ uint ownerIndexBit = 2**ownerIndex;
+ var pending = m_pending[_operation];
+ if (pending.ownersDone & ownerIndexBit > 0) {
+ pending.yetNeeded++;
+ pending.ownersDone -= ownerIndexBit;
+ Revoke(msg.sender, _operation);
+ }
+ }
+
+ // Replaces an owner `_from` with another `_to`.
+ function changeOwner(address _from, address _to) onlymanyowners(sha3(msg.data)) {
+ if (isOwner(_to)) return;
+ uint ownerIndex = m_ownerIndex[uint(_from)];
+ if (ownerIndex == 0) return;
+
+ clearPending();
+ m_owners[ownerIndex] = uint(_to);
+ m_ownerIndex[uint(_from)] = 0;
+ m_ownerIndex[uint(_to)] = ownerIndex;
+ OwnerChanged(_from, _to);
+ }
+
+ function addOwner(address _owner) onlymanyowners(sha3(msg.data)) {
+ if (isOwner(_owner)) return;
+
+ clearPending();
+ if (m_numOwners >= c_maxOwners)
+ reorganizeOwners();
+ if (m_numOwners >= c_maxOwners)
+ return;
+ m_numOwners++;
+ m_owners[m_numOwners] = uint(_owner);
+ m_ownerIndex[uint(_owner)] = m_numOwners;
+ OwnerAdded(_owner);
+ }
+
+ function removeOwner(address _owner) onlymanyowners(sha3(msg.data)) {
+ uint ownerIndex = m_ownerIndex[uint(_owner)];
+ if (ownerIndex == 0) return;
+ if (m_required > m_numOwners - 1) return;
+
+ m_owners[ownerIndex] = 0;
+ m_ownerIndex[uint(_owner)] = 0;
+ clearPending();
+ reorganizeOwners(); //make sure m_numOwner is equal to the number of owners and always points to the optimal free slot
+ OwnerRemoved(_owner);
+ }
+
+ function changeRequirement(uint _newRequired) onlymanyowners(sha3(msg.data)) {
+ if (_newRequired > m_numOwners) return;
+ m_required = _newRequired;
+ clearPending();
+ RequirementChanged(_newRequired);
+ }
+
+ function isOwner(address _addr) returns (bool) {
+ return m_ownerIndex[uint(_addr)] > 0;
+ }
+
+
+ function hasConfirmed(bytes32 _operation, address _owner) constant returns (bool) {
+ var pending = m_pending[_operation];
+ uint ownerIndex = m_ownerIndex[uint(_owner)];
+
+ // make sure they're an owner
+ if (ownerIndex == 0) return false;
+
+ // determine the bit to set for this owner.
+ uint ownerIndexBit = 2**ownerIndex;
+ return !(pending.ownersDone & ownerIndexBit == 0);
+ }
+
+ // INTERNAL METHODS
+
+ function confirmAndCheck(bytes32 _operation) internal returns (bool) {
+ // determine what index the present sender is:
+ uint ownerIndex = m_ownerIndex[uint(msg.sender)];
+ // make sure they're an owner
+ if (ownerIndex == 0) return;
+
+ var pending = m_pending[_operation];
+ // if we're not yet working on this operation, switch over and reset the confirmation status.
+ if (pending.yetNeeded == 0) {
+ // reset count of confirmations needed.
+ pending.yetNeeded = m_required;
+ // reset which owners have confirmed (none) - set our bitmap to 0.
+ pending.ownersDone = 0;
+ pending.index = m_pendingIndex.length++;
+ m_pendingIndex[pending.index] = _operation;
+ }
+ // determine the bit to set for this owner.
+ uint ownerIndexBit = 2**ownerIndex;
+ // make sure we (the message sender) haven't confirmed this operation previously.
+ if (pending.ownersDone & ownerIndexBit == 0) {
+ Confirmation(msg.sender, _operation);
+ // ok - check if count is enough to go ahead.
+ if (pending.yetNeeded <= 1) {
+ // enough confirmations: reset and run interior.
+ delete m_pendingIndex[m_pending[_operation].index];
+ delete m_pending[_operation];
+ return true;
+ }
+ else
+ {
+ // not enough: record that this owner in particular confirmed.
+ pending.yetNeeded--;
+ pending.ownersDone |= ownerIndexBit;
+ }
+ }
+ }
+
+ function reorganizeOwners() private {
+ uint free = 1;
+ while (free < m_numOwners)
+ {
+ while (free < m_numOwners && m_owners[free] != 0) free++;
+ while (m_numOwners > 1 && m_owners[m_numOwners] == 0) m_numOwners--;
+ if (free < m_numOwners && m_owners[m_numOwners] != 0 && m_owners[free] == 0)
+ {
+ m_owners[free] = m_owners[m_numOwners];
+ m_ownerIndex[m_owners[free]] = free;
+ m_owners[m_numOwners] = 0;
+ }
+ }
+ }
+
+ function clearPending() internal {
+ uint length = m_pendingIndex.length;
+ for (uint i = 0; i < length; ++i)
+ if (m_pendingIndex[i] != 0)
+ delete m_pending[m_pendingIndex[i]];
+ delete m_pendingIndex;
+ }
+
+
+ /******************************
+ ****** DAY LIMIT SECTION *****
+ ******************************/
+
+ // MODIFIERS
+
+ // simple modifier for daily limit.
+ modifier limitedDaily(uint _value) {
+ if (underLimit(_value))
+ _;
+ }
+
+ // METHODS
+
+ // constructor - stores initial daily limit and records the present day's index.
+ function initDaylimit(uint _limit) {
+ m_dailyLimit = _limit;
+ m_lastDay = today();
+ }
+ // (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent today.
+ function setDailyLimit(uint _newLimit) onlymanyowners(sha3(msg.data)) {
+ m_dailyLimit = _newLimit;
+ }
+ // resets the amount already spent today. needs many of the owners to confirm.
+ function resetSpentToday() onlymanyowners(sha3(msg.data)) {
+ m_spentToday = 0;
+ }
+
+ // INTERNAL METHODS
+
+ // checks to see if there is at least `_value` left from the daily limit today. if there is, subtracts it and
+ // returns true. otherwise just returns false.
+ function underLimit(uint _value) internal onlyowner returns (bool) {
+ // reset the spend limit if we're on a different day to last time.
+ if (today() > m_lastDay) {
+ m_spentToday = 0;
+ m_lastDay = today();
+ }
+ // check to see if there's enough left - if so, subtract and return true.
+ // overflow protection // dailyLimit check
+ if (m_spentToday + _value >= m_spentToday && m_spentToday + _value <= m_dailyLimit) {
+ m_spentToday += _value;
+ return true;
+ }
+ return false;
+ }
+
+ // determines today's index.
+ function today() private constant returns (uint) { return now / 1 days; }
+
+
+ /******************************
+ ********* WALLET SECTION *****
+ ******************************/
+
+ // METHODS
+
+ // constructor - just pass on the owner array to the multiowned and
+ // the limit to daylimit
+ function initWallet(address[] _owners, uint _required, uint _daylimit) {
+ initMultiowned(_owners, _required);
+ initDaylimit(_daylimit) ;
+ }
+
+ // kills the contract sending everything to `_to`.
+ function kill(address _to) onlymanyowners(sha3(msg.data)) {
+ suicide(_to);
+ }
+
+ // Outside-visible transact entry point. Executes transaction immediately if below daily spend limit.
+ // If not, goes into multisig process. We provide a hash on return to allow the sender to provide
+ // shortcuts for the other confirmations (allowing them to avoid replicating the _to, _value
+ // and _data arguments). They still get the option of using them if they want, anyways.
+ function execute(address _to, uint _value, bytes _data) onlyowner returns(bool _callValue) {
+ // first, take the opportunity to check that we're under the daily limit.
+ if (underLimit(_value)) {
+ SingleTransact(msg.sender, _value, _to, _data);
+ // yes - just execute the call.
+ _callValue =_to.call.value(_value)(_data);
+ } else {
+ // determine our operation hash.
+ bytes32 _r = sha3(msg.data, block.number);
+ if (!confirm(_r) && m_txs[_r].to == 0) {
+ m_txs[_r].to = _to;
+ m_txs[_r].value = _value;
+ m_txs[_r].data = _data;
+ ConfirmationNeeded(_r, msg.sender, _value, _to, _data);
+ }
+ }
+ }
+
+ // confirm a transaction through just the hash. we use the previous transactions map, m_txs, in order
+ // to determine the body of the transaction from the hash provided.
+ function confirm(bytes32 _h) onlymanyowners(_h) returns (bool) {
+ if (m_txs[_h].to != 0) {
+ m_txs[_h].to.call.value(m_txs[_h].value)(m_txs[_h].data);
+ MultiTransact(msg.sender, _h, m_txs[_h].value, m_txs[_h].to, m_txs[_h].data);
+ delete m_txs[_h];
+ return true;
+ }
+ }
+
+ // INTERNAL METHODS
+
+ function clearWalletPending() internal {
+ uint length = m_pendingIndex.length;
+ for (uint i = 0; i < length; ++i)
+ delete m_txs[m_pendingIndex[i]];
+ clearPending();
+ }
+
+ // FIELDS
+ address constant _walletLibrary = 0xcafecafecafecafecafecafecafecafecafecafe;
+
+ // the number of owners that must confirm the same operation before it is run.
+ uint m_required;
+ // pointer used to find a free slot in m_owners
+ uint m_numOwners;
+
+ uint public m_dailyLimit;
+ uint public m_spentToday;
+ uint public m_lastDay;
+
+ // list of owners
+ uint[256] m_owners;
+ uint constant c_maxOwners = 250;
+
+ // index on the list of owners to allow reverse lookup
+ mapping(uint => uint) m_ownerIndex;
+ // the ongoing operations.
+ mapping(bytes32 => PendingState) m_pending;
+ bytes32[] m_pendingIndex;
+
+ // pending transactions we have at present.
+ mapping (bytes32 => Transaction) m_txs;
+}
+
+
+contract Wallet is multisig {
+
+ // WALLET CONSTRUCTOR
+ // calls the `initWallet` method of the Library in this context
+ function Wallet(address[] _owners, uint _required, uint _daylimit) {
+ // Signature of the Wallet Library's init function
+ bytes4 sig = bytes4(sha3("initWallet(address[],uint256,uint256)"));
+ address target = _walletLibrary;
+
+ // Compute the size of the call data : arrays has 2
+ // 32bytes for offset and length, plus 32bytes per element ;
+ // plus 2 32bytes for each uint
+ uint argarraysize = (2 + _owners.length);
+ uint argsize = (2 + argarraysize) * 32;
+
+ assembly {
+ // Add the signature first to memory
+ mstore(0x0, sig)
+ // Add the call data, which is at the end of the
+ // code
+ codecopy(0x4, sub(codesize, argsize), argsize)
+ // Delegate call to the library
+ delegatecall(sub(gas, 10000), target, 0x0, add(argsize, 0x4), 0x0, 0x0)
+ }
+ }
+
+ // METHODS
+
+ // gets called when no other function matches
+ function() payable {
+ // just being sent some cash?
+ if (msg.value > 0)
+ Deposit(msg.sender, msg.value);
+ else if (msg.data.length > 0)
+ _walletLibrary.delegatecall(msg.data);
+ }
+
+ // Gets an owner by 0-indexed position (using numOwners as the count)
+ function getOwner(uint ownerIndex) constant returns (address) {
+ return address(m_owners[ownerIndex + 1]);
+ }
+
+ // As return statement unavailable in fallback, explicit the method here
+
+ function hasConfirmed(bytes32 _operation, address _owner) constant returns (bool) {
+ return _walletLibrary.delegatecall(msg.data);
+ }
+
+ function isOwner(address _addr) returns (bool) {
+ return _walletLibrary.delegatecall(msg.data);
+ }
+
+ // FIELDS
+ address constant _walletLibrary = 0xcafecafecafecafecafecafecafecafecafecafe;
+
+ // the number of owners that must confirm the same operation before it is run.
+ uint public m_required;
+ // pointer used to find a free slot in m_owners
+ uint public m_numOwners;
+
+ uint public m_dailyLimit;
+ uint public m_spentToday;
+ uint public m_lastDay;
+
+ // list of owners
+ uint[256] m_owners;
+}
diff --git a/js/src/dapps/basiccoin/Application/application.js b/js/src/dapps/basiccoin/Application/application.js
index abe0c90c5..a05ab6436 100644
--- a/js/src/dapps/basiccoin/Application/application.js
+++ b/js/src/dapps/basiccoin/Application/application.js
@@ -94,7 +94,6 @@ export default class Application extends Component {
tokenregInstance,
accounts: Object
.keys(accountsInfo)
- .filter((address) => !accountsInfo[address].meta.deleted)
.sort((a, b) => {
return (accountsInfo[b].uuid || '').localeCompare(accountsInfo[a].uuid || '');
})
diff --git a/js/src/dapps/registry/addresses/actions.js b/js/src/dapps/registry/addresses/actions.js
index 666196e88..059366173 100644
--- a/js/src/dapps/registry/addresses/actions.js
+++ b/js/src/dapps/registry/addresses/actions.js
@@ -24,7 +24,6 @@ export const fetch = () => (dispatch) => {
.then((accountsInfo) => {
const addresses = Object
.keys(accountsInfo)
- .filter((address) => accountsInfo[address] && !accountsInfo[address].meta.deleted)
.map((address) => ({
...accountsInfo[address],
address,
diff --git a/js/src/jsonrpc/rollup.config.js b/js/src/jsonrpc/rollup.config.js
deleted file mode 100644
index cc4f8c931..000000000
--- a/js/src/jsonrpc/rollup.config.js
+++ /dev/null
@@ -1,12 +0,0 @@
-import babel from 'rollup-plugin-babel';
-
-export default {
- entry: 'src/index.js',
- dest: 'release/index.js',
- format: 'cjs',
- plugins: [babel({
- babelrc: false,
- presets: ['es2015-rollup', 'stage-0'],
- runtimeHelpers: true
- })]
-};
diff --git a/js/src/modals/AddAddress/addAddress.js b/js/src/modals/AddAddress/addAddress.js
index 9b31d4a42..e44cb0b3c 100644
--- a/js/src/modals/AddAddress/addAddress.js
+++ b/js/src/modals/AddAddress/addAddress.js
@@ -19,7 +19,7 @@ import ContentAdd from 'material-ui/svg-icons/content/add';
import ContentClear from 'material-ui/svg-icons/content/clear';
import { Button, Modal, Form, Input, InputAddress } from '~/ui';
-import { ERRORS, validateAddress, validateName } from '../../util/validation';
+import { ERRORS, validateAddress, validateName } from '~/util/validation';
export default class AddAddress extends Component {
static contextTypes = {
@@ -102,7 +102,7 @@ export default class AddAddress extends Component {
if (!addressError) {
const contact = contacts[address];
- if (contact && !contact.meta.deleted) {
+ if (contact) {
addressError = ERRORS.duplicateAddress;
}
}
diff --git a/js/src/modals/AddContract/addContract.js b/js/src/modals/AddContract/addContract.js
index 70a1fd7dc..b19398001 100644
--- a/js/src/modals/AddContract/addContract.js
+++ b/js/src/modals/AddContract/addContract.js
@@ -21,7 +21,7 @@ import NavigationArrowForward from 'material-ui/svg-icons/navigation/arrow-forwa
import NavigationArrowBack from 'material-ui/svg-icons/navigation/arrow-back';
import { Button, Modal, Form, Input, InputAddress, RadioButtons } from '~/ui';
-import { ERRORS, validateAbi, validateAddress, validateName } from '../../util/validation';
+import { ERRORS, validateAbi, validateAddress, validateName } from '~/util/validation';
import { eip20, wallet } from '~/contracts/abi';
@@ -231,7 +231,7 @@ export default class AddContract extends Component {
if (!addressError) {
const contract = contracts[address];
- if (contract && !contract.meta.deleted) {
+ if (contract) {
addressError = ERRORS.duplicateAddress;
}
}
diff --git a/js/src/modals/CreateWallet/WalletDetails/walletDetails.js b/js/src/modals/CreateWallet/WalletDetails/walletDetails.js
index 5d581b81d..314425dcf 100644
--- a/js/src/modals/CreateWallet/WalletDetails/walletDetails.js
+++ b/js/src/modals/CreateWallet/WalletDetails/walletDetails.js
@@ -117,15 +117,17 @@ export default class WalletDetails extends Component {
onChange={ this.onRequiredChange }
param={ parseAbiType('uint') }
min={ 1 }
+ max={ wallet.owners.length + 1 }
/>
diff --git a/js/src/modals/CreateWallet/WalletInfo/walletInfo.js b/js/src/modals/CreateWallet/WalletInfo/walletInfo.js
index 344f4e09a..bbbe5877f 100644
--- a/js/src/modals/CreateWallet/WalletInfo/walletInfo.js
+++ b/js/src/modals/CreateWallet/WalletInfo/walletInfo.js
@@ -17,6 +17,7 @@
import React, { Component, PropTypes } from 'react';
import { CompletedStep, IdentityIcon, CopyToClipboard } from '~/ui';
+import { fromWei } from '~/api/util/wei';
import styles from '../createWallet.css';
@@ -62,7 +63,7 @@ export default class WalletInfo extends Component {
{ required }
owners are required to confirm a transaction.
- The daily limit is set to { daylimit }
.
+ The daily limit is set to { fromWei(daylimit).toFormat() }
ETH.
);
diff --git a/js/src/modals/CreateWallet/WalletType/walletType.js b/js/src/modals/CreateWallet/WalletType/walletType.js
index e77d58fc6..93dd818f1 100644
--- a/js/src/modals/CreateWallet/WalletType/walletType.js
+++ b/js/src/modals/CreateWallet/WalletType/walletType.js
@@ -17,6 +17,7 @@
import React, { Component, PropTypes } from 'react';
import { RadioButtons } from '~/ui';
+import { walletSourceURL } from '~/contracts/code/wallet';
// import styles from '../createWallet.css';
@@ -43,7 +44,15 @@ export default class WalletType extends Component {
return [
{
label: 'Multi-Sig wallet', key: 'MULTISIG',
- description: 'A standard multi-signature Wallet'
+ description: (
+
+ Create/Deploy a
+
+ standard multi-signature
+
+ Wallet
+
+ )
},
{
label: 'Watch a wallet', key: 'WATCH',
diff --git a/js/src/modals/CreateWallet/createWalletStore.js b/js/src/modals/CreateWallet/createWalletStore.js
index f5e2f1855..3edf8f638 100644
--- a/js/src/modals/CreateWallet/createWalletStore.js
+++ b/js/src/modals/CreateWallet/createWalletStore.js
@@ -16,13 +16,13 @@
import { observable, computed, action, transaction } from 'mobx';
-import { validateUint, validateAddress, validateName } from '../../util/validation';
-import { ERROR_CODES } from '~/api/transport/error';
-
import Contract from '~/api/contract';
+import Contracts from '~/contracts';
+import { ERROR_CODES } from '~/api/transport/error';
import { wallet as walletAbi } from '~/contracts/abi';
-import { wallet as walletCode } from '~/contracts/code';
+import { wallet as walletCode, walletLibraryRegKey, fullWalletCode } from '~/contracts/code/wallet';
+import { validateUint, validateAddress, validateName } from '~/util/validation';
import WalletsUtils from '~/util/wallets';
const STEPS = {
@@ -160,14 +160,25 @@ export default class CreateWalletStore {
const { account, owners, required, daylimit } = this.wallet;
- const options = {
- data: walletCode,
- from: account
- };
+ Contracts
+ .get()
+ .registry
+ .lookupAddress(walletLibraryRegKey)
+ .then((address) => {
+ const walletLibraryAddress = (address || '').replace(/^0x/, '').toLowerCase();
+ const code = walletLibraryAddress.length && !/^0+$/.test(walletLibraryAddress)
+ ? walletCode.replace(/(_)+WalletLibrary(_)+/g, walletLibraryAddress)
+ : fullWalletCode;
- this.api
- .newContract(walletAbi)
- .deploy(options, [ owners, required, daylimit ], this.onDeploymentState)
+ const options = {
+ data: code,
+ from: account
+ };
+
+ return this.api
+ .newContract(walletAbi)
+ .deploy(options, [ owners, required, daylimit ], this.onDeploymentState);
+ })
.then((address) => {
this.deployed = true;
this.wallet.address = address;
diff --git a/js/src/modals/DeployContract/DetailsStep/detailsStep.js b/js/src/modals/DeployContract/DetailsStep/detailsStep.js
index 54ef8f850..aa0a30e55 100644
--- a/js/src/modals/DeployContract/DetailsStep/detailsStep.js
+++ b/js/src/modals/DeployContract/DetailsStep/detailsStep.js
@@ -18,8 +18,8 @@ import React, { Component, PropTypes } from 'react';
import { MenuItem } from 'material-ui';
import { AddressSelect, Form, Input, Select } from '~/ui';
-import { validateAbi } from '../../../util/validation';
-import { parseAbiType } from '../../../util/abi';
+import { validateAbi } from '~/util/validation';
+import { parseAbiType } from '~/util/abi';
export default class DetailsStep extends Component {
static contextTypes = {
diff --git a/js/src/modals/DeployContract/ParametersStep/parametersStep.js b/js/src/modals/DeployContract/ParametersStep/parametersStep.js
index 4c7228b67..4ab5df693 100644
--- a/js/src/modals/DeployContract/ParametersStep/parametersStep.js
+++ b/js/src/modals/DeployContract/ParametersStep/parametersStep.js
@@ -32,7 +32,7 @@
import React, { Component, PropTypes } from 'react';
import { Form, TypedInput } from '~/ui';
-import { parseAbiType } from '../../../util/abi';
+import { parseAbiType } from '~/util/abi';
import styles from '../deployContract.css';
diff --git a/js/src/modals/DeployContract/deployContract.js b/js/src/modals/DeployContract/deployContract.js
index ae5578e40..5bf4fc389 100644
--- a/js/src/modals/DeployContract/deployContract.js
+++ b/js/src/modals/DeployContract/deployContract.js
@@ -19,7 +19,7 @@ import ActionDoneAll from 'material-ui/svg-icons/action/done-all';
import ContentClear from 'material-ui/svg-icons/content/clear';
import { BusyStep, CompletedStep, CopyToClipboard, Button, IdentityIcon, Modal, TxHash } from '~/ui';
-import { ERRORS, validateAbi, validateCode, validateName } from '../../util/validation';
+import { ERRORS, validateAbi, validateCode, validateName } from '~/util/validation';
import DetailsStep from './DetailsStep';
import ParametersStep from './ParametersStep';
diff --git a/js/src/modals/EditMeta/editMeta.js b/js/src/modals/EditMeta/editMeta.js
index ad26c19fe..8ab9233f1 100644
--- a/js/src/modals/EditMeta/editMeta.js
+++ b/js/src/modals/EditMeta/editMeta.js
@@ -19,7 +19,7 @@ import ContentClear from 'material-ui/svg-icons/content/clear';
import ContentSave from 'material-ui/svg-icons/content/save';
import { Button, Form, Input, InputChip, Modal } from '~/ui';
-import { validateName } from '../../util/validation';
+import { validateName } from '~/util/validation';
export default class EditMeta extends Component {
static contextTypes = {
diff --git a/js/src/modals/ExecuteContract/DetailsStep/detailsStep.js b/js/src/modals/ExecuteContract/DetailsStep/detailsStep.js
index 082fac6a7..3ffb929a9 100644
--- a/js/src/modals/ExecuteContract/DetailsStep/detailsStep.js
+++ b/js/src/modals/ExecuteContract/DetailsStep/detailsStep.js
@@ -15,12 +15,19 @@
// along with Parity. If not, see .
import React, { Component, PropTypes } from 'react';
-import { MenuItem } from 'material-ui';
+import { Checkbox, MenuItem } from 'material-ui';
-import { AddressSelect, Form, Input, InputAddressSelect, Select } from '~/ui';
+import { AddressSelect, Form, Input, Select, TypedInput } from '~/ui';
+import { parseAbiType } from '~/util/abi';
import styles from '../executeContract.css';
+const CHECK_STYLE = {
+ position: 'absolute',
+ top: '38px',
+ left: '1em'
+};
+
export default class DetailsStep extends Component {
static propTypes = {
accounts: PropTypes.object.isRequired,
@@ -30,10 +37,12 @@ export default class DetailsStep extends Component {
onAmountChange: PropTypes.func.isRequired,
fromAddress: PropTypes.string,
fromAddressError: PropTypes.string,
+ gasEdit: PropTypes.bool,
onFromAddressChange: PropTypes.func.isRequired,
func: PropTypes.object,
funcError: PropTypes.string,
onFuncChange: PropTypes.func,
+ onGasEditClick: PropTypes.func,
values: PropTypes.array.isRequired,
valuesError: PropTypes.array.isRequired,
warning: PropTypes.string,
@@ -41,7 +50,7 @@ export default class DetailsStep extends Component {
}
render () {
- const { accounts, amount, amountError, fromAddress, fromAddressError, onFromAddressChange, onAmountChange } = this.props;
+ const { accounts, amount, amountError, fromAddress, fromAddressError, gasEdit, onGasEditClick, onFromAddressChange, onAmountChange } = this.props;
return (
);
}
@@ -74,7 +94,7 @@ export default class DetailsStep extends Component {
const functions = contract.functions
.filter((func) => !func.constant)
- .sort((a, b) => a.name.localeCompare(b.name))
+ .sort((a, b) => (a.name || '').localeCompare(b.name || ''))
.map((func) => {
const params = (func.abi.inputs || [])
.map((input, index) => {
@@ -125,56 +145,22 @@ export default class DetailsStep extends Component {
}
return (func.abi.inputs || []).map((input, index) => {
- const onChange = (event, value) => onValueChange(event, index, value);
- const onSelect = (event, _index, value) => onValueChange(event, index, value);
- const onSubmit = (value) => onValueChange(null, index, value);
+ const onChange = (value) => onValueChange(null, index, value);
const label = `${input.name}: ${input.type}`;
- let inputbox;
-
- switch (input.type) {
- case 'address':
- inputbox = (
-
- );
- break;
-
- case 'bool':
- const boolitems = ['false', 'true'].map((bool) => {
- return (
- { bool }
- );
- });
- inputbox = (
- { boolitems }
- );
- break;
-
- default:
- inputbox = (
-
- );
- }
return (
-
- { inputbox }
+
+
);
});
diff --git a/js/src/modals/ExecuteContract/executeContract.css b/js/src/modals/ExecuteContract/executeContract.css
index a83b373ee..6b7132912 100644
--- a/js/src/modals/ExecuteContract/executeContract.css
+++ b/js/src/modals/ExecuteContract/executeContract.css
@@ -42,3 +42,15 @@
padding: 0.75em;
text-align: center;
}
+
+.columns {
+ display: flex;
+ flex-wrap: wrap;
+ position: relative;
+
+ &>div {
+ flex: 0 1 50%;
+ width: 50%;
+ position: relative;
+ }
+}
diff --git a/js/src/modals/ExecuteContract/executeContract.js b/js/src/modals/ExecuteContract/executeContract.js
index c3b64d738..7b4e8ccd2 100644
--- a/js/src/modals/ExecuteContract/executeContract.js
+++ b/js/src/modals/ExecuteContract/executeContract.js
@@ -17,18 +17,36 @@
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
+import { observer } from 'mobx-react';
import ActionDoneAll from 'material-ui/svg-icons/action/done-all';
import ContentClear from 'material-ui/svg-icons/content/clear';
+import NavigationArrowBack from 'material-ui/svg-icons/navigation/arrow-back';
+import NavigationArrowForward from 'material-ui/svg-icons/navigation/arrow-forward';
-import { BusyStep, CompletedStep, Button, IdentityIcon, Modal, TxHash } from '~/ui';
-import { MAX_GAS_ESTIMATION } from '../../util/constants';
-import { validateAddress, validateUint } from '../../util/validation';
+import { BusyStep, Button, CompletedStep, GasPriceEditor, IdentityIcon, Modal, TxHash } from '~/ui';
+import { MAX_GAS_ESTIMATION } from '~/util/constants';
+import { validateAddress, validateUint } from '~/util/validation';
+import { parseAbiType } from '~/util/abi';
import DetailsStep from './DetailsStep';
-import ERRORS from '../Transfer/errors';
import { ERROR_CODES } from '~/api/transport/error';
+const STEP_DETAILS = 0;
+const STEP_BUSY_OR_GAS = 1;
+const STEP_BUSY = 2;
+
+const TITLES = {
+ transfer: 'function details',
+ sending: 'sending',
+ complete: 'complete',
+ gas: 'gas selection',
+ rejected: 'rejected'
+};
+const STAGES_BASIC = [TITLES.transfer, TITLES.sending, TITLES.complete];
+const STAGES_GAS = [TITLES.transfer, TITLES.gas, TITLES.sending, TITLES.complete];
+
+@observer
class ExecuteContract extends Component {
static contextTypes = {
api: PropTypes.object.isRequired,
@@ -45,28 +63,29 @@ class ExecuteContract extends Component {
onFromAddressChange: PropTypes.func.isRequired
}
+ gasStore = new GasPriceEditor.Store(this.context.api, this.props.gasLimit);
+
state = {
amount: '0',
amountError: null,
+ busyState: null,
fromAddressError: null,
func: null,
funcError: null,
- gas: null,
- gasLimitError: null,
+ gasEdit: false,
+ rejected: false,
+ step: STEP_DETAILS,
+ sending: false,
values: [],
valuesError: [],
- step: 0,
- sending: false,
- busyState: null,
- txhash: null,
- rejected: false
+ txhash: null
}
componentDidMount () {
const { contract } = this.props;
const functions = contract.functions
.filter((func) => !func.constant)
- .sort((a, b) => a.name.localeCompare(b.name));
+ .sort((a, b) => (a.name || '').localeCompare(b.name || ''));
this.onFuncChange(null, functions[0]);
}
@@ -78,15 +97,21 @@ class ExecuteContract extends Component {
}
render () {
- const { sending } = this.state;
+ const { sending, step, gasEdit, rejected } = this.state;
+ const steps = gasEdit ? STAGES_GAS : STAGES_BASIC;
+
+ if (rejected) {
+ steps[steps.length - 1] = TITLES.rejected;
+ }
return (
+ current={ step }
+ steps={ steps }
+ visible
+ waiting={ gasEdit ? [STEP_BUSY] : [STEP_BUSY_OR_GAS] }>
{ this.renderStep() }
);
@@ -94,7 +119,7 @@ class ExecuteContract extends Component {
renderDialogActions () {
const { onClose, fromAddress } = this.props;
- const { sending, step, fromAddressError, valuesError } = this.state;
+ const { gasEdit, sending, step, fromAddressError, valuesError } = this.state;
const hasError = fromAddressError || valuesError.find((error) => error);
const cancelBtn = (
@@ -104,21 +129,44 @@ class ExecuteContract extends Component {
icon={
}
onClick={ onClose } />
);
+ const postBtn = (
+
}
+ onClick={ this.postTransaction } />
+ );
+ const nextBtn = (
+
}
+ onClick={ this.onNextClick } />
+ );
+ const prevBtn = (
+
}
+ onClick={ this.onPrevClick } />
+ );
- if (step === 0) {
+ if (step === STEP_DETAILS) {
return [
cancelBtn,
-
}
- onClick={ this.postTransaction } />
+ gasEdit ? nextBtn : postBtn
];
- } else if (step === 1) {
+ } else if (step === (gasEdit ? STEP_BUSY : STEP_BUSY_OR_GAS)) {
return [
cancelBtn
];
+ } else if (gasEdit && (step === STEP_BUSY_OR_GAS)) {
+ return [
+ cancelBtn,
+ prevBtn,
+ postBtn
+ ];
}
return [
@@ -132,7 +180,8 @@ class ExecuteContract extends Component {
renderStep () {
const { onFromAddressChange } = this.props;
- const { step, busyState, gasLimitError, txhash, rejected } = this.state;
+ const { gasEdit, step, busyState, txhash, rejected } = this.state;
+ const { errorEstimated } = this.gasStore;
if (rejected) {
return (
@@ -143,23 +192,29 @@ class ExecuteContract extends Component {
);
}
- if (step === 0) {
+ if (step === STEP_DETAILS) {
return (
);
- } else if (step === 1) {
+ } else if (step === (gasEdit ? STEP_BUSY : STEP_BUSY_OR_GAS)) {
return (
);
+ } else if (gasEdit && (step === STEP_BUSY_OR_GAS)) {
+ return (
+
+ );
}
return (
@@ -170,27 +225,14 @@ class ExecuteContract extends Component {
}
onAmountChange = (amount) => {
+ this.gasStore.setEthValue(amount);
this.setState({ amount }, this.estimateGas);
}
onFuncChange = (event, func) => {
- const values = func.inputs.map((input) => {
- switch (input.kind.type) {
- case 'address':
- return '0x';
-
- case 'bool':
- return false;
-
- case 'bytes':
- return '0x';
-
- case 'uint':
- return '0';
-
- default:
- return '';
- }
+ const values = (func.abi.inputs || []).map((input) => {
+ const parsedType = parseAbiType(input.type);
+ return parsedType.default;
});
this.setState({
@@ -234,7 +276,7 @@ class ExecuteContract extends Component {
estimateGas = (_fromAddress) => {
const { api } = this.context;
- const { fromAddress, gasLimit } = this.props;
+ const { fromAddress } = this.props;
const { amount, func, values } = this.state;
const options = {
gas: MAX_GAS_ESTIMATION,
@@ -250,18 +292,11 @@ class ExecuteContract extends Component {
.estimateGas(options, values)
.then((gasEst) => {
const gas = gasEst.mul(1.2);
- let gasLimitError = null;
- if (gas.gte(MAX_GAS_ESTIMATION)) {
- gasLimitError = ERRORS.gasException;
- } else if (gas.gt(gasLimit)) {
- gasLimitError = ERRORS.gasBlockLimit;
- }
+ console.log(`estimateGas: received ${gasEst.toFormat(0)}, adjusted to ${gas.toFormat(0)}`);
- this.setState({
- gas,
- gasLimitError
- });
+ this.gasStore.setEstimated(gasEst.toFixed(0));
+ this.gasStore.setGas(gas.toFixed(0));
})
.catch((error) => {
console.warn('estimateGas', error);
@@ -271,22 +306,20 @@ class ExecuteContract extends Component {
postTransaction = () => {
const { api, store } = this.context;
const { fromAddress } = this.props;
- const { amount, func, values } = this.state;
+ const { amount, func, gasEdit, values } = this.state;
+ const steps = gasEdit ? STAGES_GAS : STAGES_BASIC;
+ const finalstep = steps.length - 1;
const options = {
- gas: MAX_GAS_ESTIMATION,
+ gas: this.gasStore.gas,
+ gasPrice: this.gasStore.price,
from: fromAddress,
value: api.util.toWei(amount || 0)
};
- this.setState({ sending: true, step: 1 });
+ this.setState({ sending: true, step: gasEdit ? STEP_BUSY : STEP_BUSY_OR_GAS });
func
- .estimateGas(options, values)
- .then((gas) => {
- options.gas = gas.mul(1.2).toFixed(0);
- console.log(`estimateGas: received ${gas.toFormat(0)}, adjusted to ${gas.mul(1.2).toFormat(0)}`);
- return func.postTransaction(options, values);
- })
+ .postTransaction(options, values)
.then((requestId) => {
this.setState({ busyState: 'Waiting for authorization in the Parity Signer' });
@@ -294,7 +327,7 @@ class ExecuteContract extends Component {
.pollMethod('parity_checkRequest', requestId)
.catch((error) => {
if (error.code === ERROR_CODES.REQUEST_REJECTED) {
- this.setState({ rejected: true });
+ this.setState({ rejected: true, step: finalstep });
return false;
}
@@ -302,13 +335,31 @@ class ExecuteContract extends Component {
});
})
.then((txhash) => {
- this.setState({ sending: false, step: 2, txhash, busyState: 'Your transaction has been posted to the network' });
+ this.setState({ sending: false, step: finalstep, txhash, busyState: 'Your transaction has been posted to the network' });
})
.catch((error) => {
console.error('postTransaction', error);
store.dispatch({ type: 'newError', error });
});
}
+
+ onGasEditClick = () => {
+ this.setState({
+ gasEdit: !this.state.gasEdit
+ });
+ }
+
+ onNextClick = () => {
+ this.setState({
+ step: this.state.step + 1
+ });
+ }
+
+ onPrevClick = () => {
+ this.setState({
+ step: this.state.step - 1
+ });
+ }
}
function mapStateToProps (state) {
diff --git a/js/src/modals/SMSVerification/store.js b/js/src/modals/SMSVerification/store.js
index 49b91fa70..7a132aaf7 100644
--- a/js/src/modals/SMSVerification/store.js
+++ b/js/src/modals/SMSVerification/store.js
@@ -21,9 +21,8 @@ import { sha3 } from '~/api/util/sha3';
import Contracts from '~/contracts';
import { checkIfVerified, checkIfRequested, awaitPuzzle } from '~/contracts/sms-verification';
-import { postToServer } from '../../3rdparty/sms-verification';
-import checkIfTxFailed from '../../util/check-if-tx-failed';
-import waitForConfirmations from '../../util/wait-for-block-confirmations';
+import { postToServer } from '~/3rdparty/sms-verification';
+import { checkIfTxFailed, waitForConfirmations } from '~/util/tx';
export const LOADING = 'fetching-contract';
export const QUERY_DATA = 'query-data';
diff --git a/js/src/modals/SaveContract/saveContract.js b/js/src/modals/SaveContract/saveContract.js
index c73fd8a21..5d7863632 100644
--- a/js/src/modals/SaveContract/saveContract.js
+++ b/js/src/modals/SaveContract/saveContract.js
@@ -20,7 +20,7 @@ import SaveIcon from 'material-ui/svg-icons/content/save';
import ContentClear from 'material-ui/svg-icons/content/clear';
import { Button, Modal, Editor, Form, Input } from '~/ui';
-import { ERRORS, validateName } from '../../util/validation';
+import { ERRORS, validateName } from '~/util/validation';
import styles from './saveContract.css';
diff --git a/js/src/modals/Shapeshift/shapeshift.js b/js/src/modals/Shapeshift/shapeshift.js
index 562c8cbf3..9c5d696b0 100644
--- a/js/src/modals/Shapeshift/shapeshift.js
+++ b/js/src/modals/Shapeshift/shapeshift.js
@@ -19,7 +19,7 @@ import ActionDoneAll from 'material-ui/svg-icons/action/done-all';
import ContentClear from 'material-ui/svg-icons/content/clear';
import { Button, IdentityIcon, Modal } from '~/ui';
-import initShapeshift from '../../3rdparty/shapeshift';
+import initShapeshift from '~/3rdparty/shapeshift';
import shapeshiftLogo from '../../../assets/images/shapeshift-logo.png';
import AwaitingDepositStep from './AwaitingDepositStep';
diff --git a/js/src/modals/Transfer/Extras/extras.js b/js/src/modals/Transfer/Extras/extras.js
index f7ff4612b..def5a22c6 100644
--- a/js/src/modals/Transfer/Extras/extras.js
+++ b/js/src/modals/Transfer/Extras/extras.js
@@ -16,96 +16,28 @@
import React, { Component, PropTypes } from 'react';
-import Form, { Input } from '~/ui/Form';
-import GasPriceSelector from '../GasPriceSelector';
-
-import styles from '../transfer.css';
+import { GasPriceEditor, Form, Input } from '~/ui';
export default class Extras extends Component {
static propTypes = {
isEth: PropTypes.bool,
data: PropTypes.string,
dataError: PropTypes.string,
- gas: PropTypes.string,
- gasEst: PropTypes.string,
- gasError: PropTypes.string,
- gasPrice: PropTypes.oneOfType([
- PropTypes.string,
- PropTypes.object
- ]),
- gasPriceDefault: PropTypes.string,
- gasPriceError: PropTypes.string,
- gasPriceHistogram: PropTypes.object,
total: PropTypes.string,
totalError: PropTypes.string,
- onChange: PropTypes.func.isRequired
+ onChange: PropTypes.func.isRequired,
+ gasStore: PropTypes.object.isRequired
}
render () {
- const { gas, gasPrice, gasError, gasEst, gasPriceDefault, gasPriceError, gasPriceHistogram, total, totalError } = this.props;
-
- const gasLabel = `gas amount (estimated: ${gasEst})`;
- const priceLabel = `gas price (current: ${gasPriceDefault})`;
+ const { gasStore, onChange } = this.props;
return (
);
}
@@ -129,14 +61,6 @@ export default class Extras extends Component {
);
}
- onEditGas = (event) => {
- this.props.onChange('gas', event.target.value);
- }
-
- onEditGasPrice = (event, value) => {
- this.props.onChange('gasPrice', value);
- }
-
onEditData = (event) => {
this.props.onChange('data', event.target.value);
}
diff --git a/js/src/modals/Transfer/store.js b/js/src/modals/Transfer/store.js
index a0c7967b9..cbb10f17f 100644
--- a/js/src/modals/Transfer/store.js
+++ b/js/src/modals/Transfer/store.js
@@ -23,7 +23,8 @@ import { bytesToHex } from '~/api/util/format';
import Contract from '~/api/contract';
import ERRORS from './errors';
import { ERROR_CODES } from '~/api/transport/error';
-import { DEFAULT_GAS, DEFAULT_GASPRICE, MAX_GAS_ESTIMATION } from '../../util/constants';
+import { DEFAULT_GAS, MAX_GAS_ESTIMATION } from '~/util/constants';
+import GasPriceStore from '~/ui/GasPriceEditor/store';
const TITLES = {
transfer: 'transfer details',
@@ -48,14 +49,6 @@ export default class TransferStore {
@observable data = '';
@observable dataError = null;
- @observable gas = DEFAULT_GAS;
- @observable gasError = null;
-
- @observable gasEst = '0';
- @observable gasLimitError = null;
- @observable gasPrice = DEFAULT_GASPRICE;
- @observable gasPriceError = null;
-
@observable recipient = '';
@observable recipientError = ERRORS.requireRecipient;
@@ -68,11 +61,8 @@ export default class TransferStore {
@observable value = '0.0';
@observable valueError = null;
- gasPriceHistogram = {};
-
account = null;
balance = null;
- gasLimit = null;
onClose = null;
senders = null;
@@ -81,6 +71,8 @@ export default class TransferStore {
isWallet = false;
wallet = null;
+ gasStore = null;
+
@computed get steps () {
const steps = [].concat(this.extras ? STAGES_EXTRA : STAGES_BASIC);
@@ -93,7 +85,7 @@ export default class TransferStore {
@computed get isValid () {
const detailsValid = !this.recipientError && !this.valueError && !this.totalError && !this.senderError;
- const extrasValid = !this.gasError && !this.gasPriceError && !this.totalError;
+ const extrasValid = !this.gasStore.errorGas && !this.gasStore.errorPrice && !this.totalError;
const verifyValid = !this.passwordError;
switch (this.stage) {
@@ -116,14 +108,14 @@ export default class TransferStore {
this.api = api;
const { account, balance, gasLimit, senders, onClose, newError, sendersBalances } = props;
-
this.account = account;
this.balance = balance;
- this.gasLimit = gasLimit;
this.onClose = onClose;
this.isWallet = account && account.wallet;
this.newError = newError;
+ this.gasStore = new GasPriceStore(api, gasLimit);
+
if (this.isWallet) {
this.wallet = props.wallet;
this.walletContract = new Contract(this.api, walletAbi);
@@ -180,26 +172,6 @@ export default class TransferStore {
}
}
- @action getDefaults = () => {
- Promise
- .all([
- this.api.parity.gasPriceHistogram(),
- this.api.eth.gasPrice()
- ])
- .then(([gasPriceHistogram, gasPrice]) => {
- transaction(() => {
- this.gasPrice = gasPrice.toString();
- this.gasPriceDefault = gasPrice.toFormat();
- this.gasPriceHistogram = gasPriceHistogram;
-
- this.recalculate();
- });
- })
- .catch((error) => {
- console.warn('getDefaults', error);
- });
- }
-
@action onSend = () => {
this.onNext();
this.sending = true;
@@ -282,25 +254,11 @@ export default class TransferStore {
}
@action _onUpdateGas = (gas) => {
- const gasError = this._validatePositiveNumber(gas);
-
- transaction(() => {
- this.gas = gas;
- this.gasError = gasError;
-
- this.recalculate();
- });
+ this.recalculate();
}
@action _onUpdateGasPrice = (gasPrice) => {
- const gasPriceError = this._validatePositiveNumber(gasPrice);
-
- transaction(() => {
- this.gasPrice = gasPrice;
- this.gasPriceError = gasPriceError;
-
- this.recalculate();
- });
+ this.recalculate();
}
@action _onUpdateRecipient = (recipient) => {
@@ -363,7 +321,7 @@ export default class TransferStore {
@action recalculateGas = () => {
if (!this.isValid) {
- this.gas = 0;
+ this.gasStore.setGas('0');
return this.recalculate();
}
@@ -371,28 +329,20 @@ export default class TransferStore {
.estimateGas()
.then((gasEst) => {
let gas = gasEst;
- let gasLimitError = null;
if (gas.gt(DEFAULT_GAS)) {
gas = gas.mul(1.2);
}
- if (gas.gte(MAX_GAS_ESTIMATION)) {
- gasLimitError = ERRORS.gasException;
- } else if (gas.gt(this.gasLimit)) {
- gasLimitError = ERRORS.gasBlockLimit;
- }
-
transaction(() => {
- this.gas = gas.toFixed(0);
- this.gasEst = gasEst.toFormat();
- this.gasLimitError = gasLimitError;
+ this.gasStore.setEstimated(gasEst.toFixed(0));
+ this.gasStore.setGas(gas.toFixed(0));
this.recalculate();
});
})
.catch((error) => {
- console.error('etimateGas', error);
+ console.warn('etimateGas', error);
this.recalculate();
});
}
@@ -412,34 +362,38 @@ export default class TransferStore {
return;
}
- const { gas, gasPrice, tag, valueAll, isEth } = this;
+ const { tag, valueAll, isEth, isWallet } = this;
- const gasTotal = new BigNumber(gasPrice || 0).mul(new BigNumber(gas || 0));
+ const gasTotal = new BigNumber(this.gasStore.price || 0).mul(new BigNumber(this.gasStore.gas || 0));
const availableEth = new BigNumber(balance.tokens[0].value);
const senderBalance = this.balance.tokens.find((b) => tag === b.token.tag);
- const available = new BigNumber(senderBalance.value);
const format = new BigNumber(senderBalance.token.format || 1);
+ const available = isWallet
+ ? this.api.util.fromWei(new BigNumber(senderBalance.value))
+ : (new BigNumber(senderBalance.value)).div(format);
let { value, valueError } = this;
let totalEth = gasTotal;
let totalError = null;
if (valueAll) {
- if (isEth) {
+ if (isEth && !isWallet) {
const bn = this.api.util.fromWei(availableEth.minus(gasTotal));
value = (bn.lt(0) ? new BigNumber(0.0) : bn).toString();
+ } else if (isEth) {
+ value = (available.lt(0) ? new BigNumber(0.0) : available).toString();
} else {
- value = available.div(format).toString();
+ value = available.toString();
}
}
- if (isEth) {
+ if (isEth && !isWallet) {
totalEth = totalEth.plus(this.api.util.toWei(value || 0));
}
- if (new BigNumber(value || 0).gt(available.div(format))) {
+ if (new BigNumber(value || 0).gt(available)) {
valueError = ERRORS.largeAmount;
} else if (valueError === ERRORS.largeAmount) {
valueError = null;
@@ -450,10 +404,12 @@ export default class TransferStore {
}
transaction(() => {
- this.total = this.api.util.fromWei(totalEth).toString();
+ this.total = this.api.util.fromWei(totalEth).toFixed();
this.totalError = totalError;
this.value = value;
this.valueError = valueError;
+ this.gasStore.setErrorTotal(totalError);
+ this.gasStore.setEthValue(totalEth);
});
}
@@ -519,8 +475,8 @@ export default class TransferStore {
};
if (!gas) {
- options.gas = this.gas;
- options.gasPrice = this.gasPrice;
+ options.gas = this.gasStore.gas;
+ options.gasPrice = this.gasStore.price;
} else {
options.gas = MAX_GAS_ESTIMATION;
}
diff --git a/js/src/modals/Transfer/transfer.css b/js/src/modals/Transfer/transfer.css
index 3bd17ba96..28612f299 100644
--- a/js/src/modals/Transfer/transfer.css
+++ b/js/src/modals/Transfer/transfer.css
@@ -144,15 +144,6 @@
font-size: 1.2rem;
}
-.chart {
- position: absolute;
- width: 100%;
-}
-
-.gasPriceDesc {
- font-size: 0.9em;
-}
-
.warning {
border-radius: 0.5em;
background: #f80;
diff --git a/js/src/modals/Transfer/transfer.js b/js/src/modals/Transfer/transfer.js
index 402e3ae4f..00e84adaf 100644
--- a/js/src/modals/Transfer/transfer.js
+++ b/js/src/modals/Transfer/transfer.js
@@ -56,10 +56,6 @@ class Transfer extends Component {
store = new TransferStore(this.context.api, this.props);
- componentDidMount () {
- this.store.getDefaults();
- }
-
render () {
const { stage, extras, steps } = this.store;
@@ -139,8 +135,8 @@ class Transfer extends Component {
? (
-
- This transaction needs confirmation from other owners.
+
+
This transaction needs confirmation from other owners.
-
+
)
: null
@@ -186,27 +182,20 @@ class Transfer extends Component {
}
renderExtrasPage () {
- if (!this.store.gasPriceHistogram) {
+ if (!this.store.gasStore.histogram) {
return null;
}
- const { isEth, data, dataError, gas, gasEst, gasError, gasPrice } = this.store;
- const { gasPriceDefault, gasPriceError, gasPriceHistogram, total, totalError } = this.store;
+ const { isEth, data, dataError, total, totalError } = this.store;
return (
);
}
@@ -263,15 +252,15 @@ class Transfer extends Component {
}
renderWarning () {
- const { gasLimitError } = this.store;
+ const { errorEstimated } = this.store.gasStore;
- if (!gasLimitError) {
+ if (!errorEstimated) {
return null;
}
return (
- { gasLimitError }
+ { errorEstimated }
);
}
@@ -298,7 +287,6 @@ function mapStateToProps (initState, initProps) {
return (state) => {
const { gasLimit } = state.nodeStatus;
const sendersBalances = senders ? pick(state.balances.balances, Object.keys(senders)) : null;
-
return { gasLimit, wallet, senders, sendersBalances };
};
}
diff --git a/js/src/views/Status/components-compositors/Animated/index.js b/js/src/modals/WalletSettings/index.js
similarity index 94%
rename from js/src/views/Status/components-compositors/Animated/index.js
rename to js/src/modals/WalletSettings/index.js
index ee48d0704..9b15a5f3b 100644
--- a/js/src/views/Status/components-compositors/Animated/index.js
+++ b/js/src/modals/WalletSettings/index.js
@@ -14,4 +14,4 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see
.
-export default from './Animated';
+export default from './walletSettings';
diff --git a/js/src/views/Contracts/contracts.css b/js/src/modals/WalletSettings/walletSettings.css
similarity index 52%
rename from js/src/views/Contracts/contracts.css
rename to js/src/modals/WalletSettings/walletSettings.css
index eab0858e7..ae52013cd 100644
--- a/js/src/views/Contracts/contracts.css
+++ b/js/src/modals/WalletSettings/walletSettings.css
@@ -14,5 +14,50 @@
/* You should have received a copy of the GNU General Public License
/* along with Parity. If not, see
.
*/
-.contracts {
+
+.splitInput {
+ display: flex;
+ flex-direction: row;
+
+ > * {
+ flex: 1;
+
+ margin: 0 0.25em;
+
+ &:first-child {
+ margin-left: 0;
+ }
+
+ &:last-child {
+ margin-right: 0;
+ }
+ }
}
+
+.change {
+ background-color: rgba(255, 255, 255, 0.1);
+ padding: 0.75em 1.75em;
+ margin-bottom: 1em;
+
+ &.add {
+ background-color: rgba(139, 195, 74, 0.5);
+ }
+
+ &.remove {
+ background-color: rgba(244, 67, 54, 0.5);
+ }
+
+ .label {
+ text-transform: uppercase;
+ margin-bottom: 0.5em;
+ margin-left: -1em;
+ font-size: 0.8em;
+ }
+}
+
+.eth:after {
+ content: 'ETH';
+ font-size: 0.75em;
+ margin-left: 0.125em;
+}
+
diff --git a/js/src/modals/WalletSettings/walletSettings.js b/js/src/modals/WalletSettings/walletSettings.js
new file mode 100644
index 000000000..1b2b2cc1a
--- /dev/null
+++ b/js/src/modals/WalletSettings/walletSettings.js
@@ -0,0 +1,321 @@
+// 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
.
+
+import React, { Component, PropTypes } from 'react';
+import { connect } from 'react-redux';
+import { observer } from 'mobx-react';
+import { pick } from 'lodash';
+
+import ActionDone from 'material-ui/svg-icons/action/done';
+import ContentClear from 'material-ui/svg-icons/content/clear';
+import NavigationArrowForward from 'material-ui/svg-icons/navigation/arrow-forward';
+import { parseAbiType } from '~/util/abi';
+
+import { Button, Modal, TxHash, BusyStep, Form, TypedInput, InputAddress, AddressSelect } from '~/ui';
+import { fromWei } from '~/api/util/wei';
+
+import WalletSettingsStore from './walletSettingsStore.js';
+import styles from './walletSettings.css';
+
+@observer
+class WalletSettings extends Component {
+ static contextTypes = {
+ api: PropTypes.object.isRequired
+ };
+
+ static propTypes = {
+ accounts: PropTypes.object.isRequired,
+ wallet: PropTypes.object.isRequired,
+ onClose: PropTypes.func.isRequired,
+ senders: PropTypes.object.isRequired
+ };
+
+ store = new WalletSettingsStore(this.context.api, this.props.wallet);
+
+ render () {
+ const { stage, steps, waiting, rejected } = this.store;
+
+ if (rejected) {
+ return (
+
+
+
+ );
+ }
+
+ return (
+
s.title) }
+ waiting={ waiting }
+ >
+ { this.renderPage() }
+
+ );
+ }
+
+ renderPage () {
+ const { step } = this.store;
+
+ switch (step) {
+ case 'SENDING':
+ return (
+
+ {
+ this.store.requests.map((req) => {
+ const key = req.id;
+
+ if (req.txhash) {
+ return ( );
+ }
+
+ if (req.rejected) {
+ return (The transaction #{parseInt(key, 16)} has been rejected
);
+ }
+ })
+ }
+
+ );
+
+ case 'CONFIRMATION':
+ const { changes } = this.store;
+
+ return (
+
+
You are about to make the following modifications
+
+ { this.renderChanges(changes) }
+
+
+ );
+
+ default:
+ case 'EDIT':
+ const { wallet, errors } = this.store;
+ const { accounts, senders } = this.props;
+
+ return (
+
+ );
+ }
+ }
+
+ renderChanges (changes) {
+ return changes.map((change, index) => (
+
+ { this.renderChange(change) }
+
+ ));
+ }
+
+ renderChange (change) {
+ const { accounts } = this.props;
+
+ switch (change.type) {
+ case 'dailylimit':
+ return (
+
+
Change Daily Limit
+
+ from
+ { fromWei(change.initial).toFormat() }
+
+ to
+ { fromWei(change.value).toFormat() }
+
+
+
+ );
+
+ case 'require':
+ return (
+
+
Change Required Owners
+
+ from
+ { change.initial.toNumber() }
+ to
+ { change.value.toNumber() }
+
+
+ );
+
+ case 'add_owner':
+ return (
+
+ );
+
+ case 'remove_owner':
+ return (
+
+ );
+ }
+ }
+
+ renderDialogActions () {
+ const { onClose } = this.props;
+ const { step, hasErrors, rejected, onNext, send, done } = this.store;
+
+ const cancelBtn = (
+
}
+ label='Cancel'
+ onClick={ onClose }
+ />
+ );
+
+ const closeBtn = (
+
}
+ label='Close'
+ onClick={ onClose }
+ />
+ );
+
+ const sendingBtn = (
+
}
+ label='Sending...'
+ disabled
+ />
+ );
+
+ const nextBtn = (
+
}
+ label='Next'
+ onClick={ onNext }
+ disabled={ hasErrors }
+ />
+ );
+
+ const sendBtn = (
+
}
+ label='Send'
+ onClick={ send }
+ disabled={ hasErrors }
+ />
+ );
+
+ if (rejected) {
+ return [ closeBtn ];
+ }
+
+ switch (step) {
+ case 'SENDING':
+ return done ? [ closeBtn ] : [ closeBtn, sendingBtn ];
+
+ case 'CONFIRMATION':
+ return [ cancelBtn, sendBtn ];
+
+ default:
+ case 'TYPE':
+ return [ cancelBtn, nextBtn ];
+
+ }
+ }
+}
+
+function mapStateToProps (initState, initProps) {
+ const { accountsInfo, accounts } = initState.personal;
+ const { owners } = initProps.wallet;
+
+ const senders = pick(accounts, owners);
+
+ return () => {
+ return { accounts: accountsInfo, senders };
+ };
+}
+
+export default connect(mapStateToProps)(WalletSettings);
diff --git a/js/src/modals/WalletSettings/walletSettingsStore.js b/js/src/modals/WalletSettings/walletSettingsStore.js
new file mode 100644
index 000000000..f4d5d2275
--- /dev/null
+++ b/js/src/modals/WalletSettings/walletSettingsStore.js
@@ -0,0 +1,306 @@
+// 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
.
+
+import { observable, computed, action, transaction } from 'mobx';
+import BigNumber from 'bignumber.js';
+
+import { validateUint, validateAddress } from '~/util/validation';
+import { DEFAULT_GAS, MAX_GAS_ESTIMATION } from '~/util/constants';
+import { ERROR_CODES } from '~/api/transport/error';
+
+const STEPS = {
+ EDIT: { title: 'wallet settings' },
+ CONFIRMATION: { title: 'confirmation' },
+ SENDING: { title: 'sending transaction', waiting: true }
+};
+
+export default class WalletSettingsStore {
+ @observable step = null;
+ @observable requests = [];
+ @observable deployState = '';
+ @observable done = false;
+
+ @observable wallet = {
+ owners: null,
+ require: null,
+ dailylimit: null,
+ sender: ''
+ };
+
+ @observable errors = {
+ owners: null,
+ require: null,
+ dailylimit: null,
+ sender: null
+ };
+
+ @computed get stage () {
+ return this.stepsKeys.findIndex((k) => k === this.step);
+ }
+
+ @computed get hasErrors () {
+ return !!Object.keys(this.errors).find((key) => !!this.errors[key]);
+ }
+
+ @computed get stepsKeys () {
+ return this.steps.map((s) => s.key);
+ }
+
+ @computed get steps () {
+ return Object
+ .keys(STEPS)
+ .map((key) => {
+ return {
+ ...STEPS[key],
+ key
+ };
+ });
+ }
+
+ @computed get waiting () {
+ this.steps
+ .map((s, idx) => ({ idx, waiting: s.waiting }))
+ .filter((s) => s.waiting)
+ .map((s) => s.idx);
+ }
+
+ get changes () {
+ const changes = [];
+
+ const prevDailylimit = new BigNumber(this.initialWallet.dailylimit);
+ const nextDailylimit = new BigNumber(this.wallet.dailylimit);
+
+ const prevRequire = new BigNumber(this.initialWallet.require);
+ const nextRequire = new BigNumber(this.wallet.require);
+
+ if (!prevDailylimit.equals(nextDailylimit)) {
+ changes.push({
+ type: 'dailylimit',
+ initial: prevDailylimit,
+ value: nextDailylimit
+ });
+ }
+
+ if (!prevRequire.equals(nextRequire)) {
+ changes.push({
+ type: 'require',
+ initial: prevRequire,
+ value: nextRequire
+ });
+ }
+
+ const prevOwners = this.initialWallet.owners;
+ const nextOwners = this.wallet.owners;
+
+ const ownersToRemove = prevOwners.filter((owner) => !nextOwners.includes(owner));
+ const ownersToAdd = nextOwners.filter((owner) => !prevOwners.includes(owner));
+
+ ownersToRemove.forEach((owner) => {
+ changes.push({
+ type: 'remove_owner',
+ value: owner
+ });
+ });
+
+ ownersToAdd.forEach((owner) => {
+ changes.push({
+ type: 'add_owner',
+ value: owner
+ });
+ });
+
+ return changes;
+ }
+
+ constructor (api, wallet) {
+ this.api = api;
+ this.step = this.stepsKeys[0];
+
+ this.walletInstance = wallet.instance;
+
+ this.initialWallet = {
+ address: wallet.address,
+ owners: wallet.owners,
+ require: wallet.require,
+ dailylimit: wallet.dailylimit.limit
+ };
+
+ transaction(() => {
+ this.wallet.owners = wallet.owners;
+ this.wallet.require = wallet.require;
+ this.wallet.dailylimit = wallet.dailylimit.limit;
+
+ this.validateWallet(this.wallet);
+ });
+ }
+
+ @action onNext = () => {
+ const stepIndex = this.stepsKeys.findIndex((k) => k === this.step) + 1;
+ this.step = this.stepsKeys[stepIndex];
+ }
+
+ @action onChange = (_wallet) => {
+ const newWallet = Object.assign({}, this.wallet, _wallet);
+ this.validateWallet(newWallet);
+ }
+
+ @action onOwnersChange = (owners) => {
+ this.onChange({ owners });
+ }
+
+ @action onRequireChange = (require) => {
+ this.onChange({ require });
+ }
+
+ @action onSenderChange = (_, sender) => {
+ this.onChange({ sender });
+ }
+
+ @action onDailylimitChange = (dailylimit) => {
+ this.onChange({ dailylimit });
+ }
+
+ @action send = () => {
+ const changes = this.changes;
+ const walletInstance = this.walletInstance;
+ this.step = 'SENDING';
+
+ this.onTransactionsState('postTransaction');
+ Promise
+ .all(changes.map((change) => this.sendChange(change, walletInstance)))
+ .then((requestIds) => {
+ this.onTransactionsState('checkRequest');
+ this.requests = requestIds.map((id) => ({ id, rejected: false, txhash: null }));
+
+ return Promise
+ .all(requestIds.map((id) => {
+ return this.api
+ .pollMethod('parity_checkRequest', id)
+ .then((txhash) => {
+ const index = this.requests.findIndex((r) => r.id === id);
+ this.requests[index].txhash = txhash;
+ })
+ .catch((e) => {
+ if (e.code === ERROR_CODES.REQUEST_REJECTED) {
+ const index = this.requests.findIndex((r) => r.id === id);
+ this.requests[index].rejected = true;
+ return false;
+ }
+
+ throw e;
+ });
+ }));
+ })
+ .then(() => {
+ this.done = true;
+ this.onTransactionsState('completed');
+ });
+ }
+
+ @action sendChange = (change, walletInstance) => {
+ const { method, values } = this.getChangeMethod(change, walletInstance);
+
+ const options = {
+ from: this.wallet.sender,
+ to: this.initialWallet.address,
+ gas: MAX_GAS_ESTIMATION
+ };
+
+ return method
+ .estimateGas(options, values)
+ .then((gasEst) => {
+ let gas = gasEst;
+
+ if (gas.gt(DEFAULT_GAS)) {
+ gas = gas.mul(1.2);
+ }
+ options.gas = gas;
+
+ return method.postTransaction(options, values);
+ });
+ }
+
+ getChangeMethod = (change, walletInstance) => {
+ if (change.type === 'require') {
+ return {
+ method: walletInstance.changeRequirement,
+ values: [ change.value ]
+ };
+ }
+
+ if (change.type === 'dailylimit') {
+ return {
+ method: walletInstance.setDailyLimit,
+ values: [ change.value ]
+ };
+ }
+
+ if (change.type === 'add_owner') {
+ return {
+ method: walletInstance.addOwner,
+ values: [ change.value ]
+ };
+ }
+
+ if (change.type === 'remove_owner') {
+ return {
+ method: walletInstance.removeOwner,
+ values: [ change.value ]
+ };
+ }
+ }
+
+ @action onTransactionsState = (state) => {
+ switch (state) {
+ case 'estimateGas':
+ case 'postTransaction':
+ this.deployState = 'Preparing transaction for network transmission';
+ return;
+
+ case 'checkRequest':
+ this.deployState = 'Waiting for confirmation of the transaction in the Parity Secure Signer';
+ return;
+
+ case 'completed':
+ this.deployState = '';
+ return;
+ }
+ }
+
+ @action validateWallet = (_wallet) => {
+ const senderValidation = validateAddress(_wallet.sender);
+ const requireValidation = validateUint(_wallet.require);
+ const dailylimitValidation = validateUint(_wallet.dailylimit);
+
+ const errors = {
+ sender: senderValidation.addressError,
+ require: requireValidation.valueError,
+ dailylimit: dailylimitValidation.valueError
+ };
+
+ const wallet = {
+ ..._wallet,
+ sender: senderValidation.address,
+ require: requireValidation.value,
+ dailylimit: dailylimitValidation.value
+ };
+
+ transaction(() => {
+ this.wallet = wallet;
+ this.errors = errors;
+ });
+ }
+}
diff --git a/js/src/modals/index.js b/js/src/modals/index.js
index 895ca40e8..0f0844e40 100644
--- a/js/src/modals/index.js
+++ b/js/src/modals/index.js
@@ -29,6 +29,7 @@ import Transfer from './Transfer';
import PasswordManager from './PasswordManager';
import SaveContract from './SaveContract';
import LoadContract from './LoadContract';
+import WalletSettings from './WalletSettings';
export {
AddAddress,
@@ -45,5 +46,6 @@ export {
Transfer,
PasswordManager,
LoadContract,
- SaveContract
+ SaveContract,
+ WalletSettings
};
diff --git a/js/src/redux/providers/compilerWorker.js b/js/src/redux/providers/compilerWorker.js
index 247333f5e..f624a4e5f 100644
--- a/js/src/redux/providers/compilerWorker.js
+++ b/js/src/redux/providers/compilerWorker.js
@@ -92,7 +92,7 @@ function findImports (path) {
return { error: 'File not found' };
}
-function compile (data) {
+function compile (data, optimized = 1) {
const { sourcecode, build } = data;
const { longVersion } = build;
@@ -109,7 +109,7 @@ function compile (data) {
'': sourcecode
};
- const compiled = compiler.compile({ sources: input }, 0, findImports);
+ const compiled = compiler.compile({ sources: input }, optimized, findImports);
self.lastCompile = {
version: longVersion, result: compiled,
diff --git a/js/src/redux/providers/personal.js b/js/src/redux/providers/personal.js
index fd67ab5f7..e061051b0 100644
--- a/js/src/redux/providers/personal.js
+++ b/js/src/redux/providers/personal.js
@@ -23,6 +23,7 @@ export default class Personal {
}
start () {
+ this._removeDeleted();
this._subscribeAccountsInfo();
}
@@ -40,4 +41,29 @@ export default class Personal {
console.log('personal._subscribeAccountsInfo', 'subscriptionId', subscriptionId);
});
}
+
+ _removeDeleted () {
+ this._api.parity
+ .accountsInfo()
+ .then((accountsInfo) => {
+ return Promise.all(
+ Object
+ .keys(accountsInfo)
+ .filter((address) => {
+ const account = accountsInfo[address];
+
+ return !account.uuid && account.meta.deleted;
+ })
+ .map((address) => this._api.parity.removeAddress(address))
+ );
+ })
+ .then((results) => {
+ if (results.length) {
+ console.log(`Removed ${results.length} previously marked addresses`);
+ }
+ })
+ .catch((error) => {
+ console.warn('removeDeleted', error);
+ });
+ }
}
diff --git a/js/src/redux/providers/personalActions.js b/js/src/redux/providers/personalActions.js
index a1e9845db..47056af2f 100644
--- a/js/src/redux/providers/personalActions.js
+++ b/js/src/redux/providers/personalActions.js
@@ -27,7 +27,7 @@ export function personalAccountsInfo (accountsInfo) {
Object.keys(accountsInfo || {})
.map((address) => Object.assign({}, accountsInfo[address], { address }))
- .filter((account) => !account.meta.deleted)
+ .filter((account) => account.uuid || !account.meta.deleted)
.forEach((account) => {
if (account.uuid) {
accounts[account.address] = account;
diff --git a/js/src/redux/providers/walletActions.js b/js/src/redux/providers/walletActions.js
index d68803531..4a8a3f82a 100644
--- a/js/src/redux/providers/walletActions.js
+++ b/js/src/redux/providers/walletActions.js
@@ -17,12 +17,10 @@
import { isEqual, uniq } from 'lodash';
import Contract from '~/api/contract';
-import { wallet as WALLET_ABI } from '~/contracts/abi';
import { bytesToHex, toHex } from '~/api/util/format';
-
import { ERROR_CODES } from '~/api/transport/error';
-import { MAX_GAS_ESTIMATION } from '../../util/constants';
-
+import { wallet as WALLET_ABI } from '~/contracts/abi';
+import { MAX_GAS_ESTIMATION } from '~/util/constants';
import WalletsUtils from '~/util/wallets';
import { newError } from '~/ui/Errors/actions';
@@ -58,7 +56,7 @@ function modifyOperation (method, address, owner, operation) {
contract.instance[method]
.estimateGas(options, values)
.then((gas) => {
- options.gas = gas;
+ options.gas = gas.mul(1.2);
return contract.instance[method].postTransaction(options, values);
})
.then((requestId) => {
@@ -228,7 +226,7 @@ function fetchWalletInfo (contract, update, getState) {
const owners = ownersUpdate && ownersUpdate.value || null;
const transactions = transactionsUpdate && transactionsUpdate.value || null;
- return fetchWalletConfirmations(contract, owners, transactions, getState)
+ return fetchWalletConfirmations(contract, update[UPDATE_CONFIRMATIONS], owners, transactions, getState)
.then((update) => {
updates.push(update);
return updates;
@@ -292,77 +290,113 @@ function fetchWalletDailylimit (contract) {
});
}
-function fetchWalletConfirmations (contract, _owners = null, _transactions = null, getState) {
+function fetchWalletConfirmations (contract, _operations, _owners = null, _transactions = null, getState) {
const walletInstance = contract.instance;
const wallet = getState().wallet.wallets[contract.address];
const owners = _owners || (wallet && wallet.owners) || null;
const transactions = _transactions || (wallet && wallet.transactions) || null;
+ // Full load if no operations given, or if the one given aren't loaded yet
+ const fullLoad = !Array.isArray(_operations) || _operations
+ .filter((op) => !wallet.confirmations.find((conf) => conf.operation === op))
+ .length > 0;
- return walletInstance
- .ConfirmationNeeded
- .getAllLogs()
- .then((logs) => {
- return logs.sort((logA, logB) => {
- const comp = logA.blockNumber.comparedTo(logB.blockNumber);
+ let promise;
- if (comp !== 0) {
- return comp;
+ if (fullLoad) {
+ promise = walletInstance
+ .ConfirmationNeeded
+ .getAllLogs()
+ .then((logs) => {
+ return logs.map((log) => ({
+ initiator: log.params.initiator.value,
+ to: log.params.to.value,
+ data: log.params.data.value,
+ value: log.params.value.value,
+ operation: bytesToHex(log.params.operation.value),
+ transactionIndex: log.transactionIndex,
+ transactionHash: log.transactionHash,
+ blockNumber: log.blockNumber,
+ confirmedBy: []
+ }));
+ })
+ .then((logs) => {
+ return logs.sort((logA, logB) => {
+ const comp = logA.blockNumber.comparedTo(logB.blockNumber);
+
+ if (comp !== 0) {
+ return comp;
+ }
+
+ return logA.transactionIndex.comparedTo(logB.transactionIndex);
+ });
+ })
+ .then((confirmations) => {
+ if (confirmations.length === 0) {
+ return confirmations;
}
- return logA.transactionIndex.comparedTo(logB.transactionIndex);
+ // Only fetch confirmations for operations not
+ // yet confirmed (ie. not yet a transaction)
+ if (transactions) {
+ const operations = transactions
+ .filter((t) => t.operation)
+ .map((t) => t.operation);
+
+ return confirmations.filter((confirmation) => {
+ return !operations.includes(confirmation.operation);
+ });
+ }
+
+ return confirmations;
});
- })
- .then((logs) => {
- return logs.map((log) => ({
- initiator: log.params.initiator.value,
- to: log.params.to.value,
- data: log.params.data.value,
- value: log.params.value.value,
- operation: bytesToHex(log.params.operation.value),
- transactionHash: log.transactionHash,
- blockNumber: log.blockNumber,
- confirmedBy: []
- }));
- })
+ } else {
+ const { confirmations } = wallet;
+ const nextConfirmations = confirmations
+ .filter((conf) => _operations.includes(conf.operation));
+
+ promise = Promise.resolve(nextConfirmations);
+ }
+
+ return promise
.then((confirmations) => {
if (confirmations.length === 0) {
return confirmations;
}
- if (transactions) {
- const operations = transactions
- .filter((t) => t.operation)
- .map((t) => t.operation);
+ const uniqConfirmations = Object.values(
+ confirmations.reduce((confirmations, confirmation) => {
+ confirmations[confirmation.operation] = confirmation;
+ return confirmations;
+ }, {})
+ );
- return confirmations.filter((confirmation) => {
- return !operations.includes(confirmation.operation);
- });
- }
+ const operations = uniqConfirmations.map((conf) => conf.operation);
- return confirmations;
- })
- .then((confirmations) => {
- if (confirmations.length === 0) {
- return confirmations;
- }
-
- const operations = confirmations.map((conf) => conf.operation);
return Promise
.all(operations.map((op) => fetchOperationConfirmations(contract, op, owners)))
.then((confirmedBys) => {
- confirmations.forEach((_, index) => {
- confirmations[index].confirmedBy = confirmedBys[index];
+ uniqConfirmations.forEach((_, index) => {
+ uniqConfirmations[index].confirmedBy = confirmedBys[index];
});
- return confirmations;
+ return uniqConfirmations;
});
})
.then((confirmations) => {
+ const prevConfirmations = wallet.confirmations || [];
+ const nextConfirmations = prevConfirmations
+ .filter((conA) => !confirmations.find((conB) => conB.operation === conA.operation))
+ .concat(confirmations)
+ .map((conf) => ({
+ ...conf,
+ pending: false
+ }));
+
return {
key: UPDATE_CONFIRMATIONS,
- value: confirmations
+ value: nextConfirmations
};
});
}
@@ -417,7 +451,10 @@ function parseLogs (logs) {
logs.forEach((log) => {
const { address, topics } = log;
const eventSignature = toHex(topics[0]);
- const prev = updates[address] || { address };
+ const prev = updates[address] || {
+ [ UPDATE_DAILYLIMIT ]: true,
+ address
+ };
switch (eventSignature) {
case signatures.OwnerChanged:
@@ -436,16 +473,18 @@ function parseLogs (logs) {
};
return;
+ case signatures.ConfirmationNeeded:
case signatures.Confirmation:
case signatures.Revoke:
- const operation = log.params.operation.value;
+ const operation = bytesToHex(log.params.operation.value);
updates[address] = {
...prev,
[ UPDATE_CONFIRMATIONS ]: uniq(
- (prev.operations || []).concat(operation)
+ (prev[UPDATE_CONFIRMATIONS] || []).concat(operation)
)
};
+
return;
case signatures.Deposit:
@@ -456,17 +495,6 @@ function parseLogs (logs) {
[ UPDATE_TRANSACTIONS ]: true
};
return;
-
- case signatures.ConfirmationNeeded:
- const op = log.params.operation.value;
-
- updates[address] = {
- ...prev,
- [ UPDATE_CONFIRMATIONS ]: uniq(
- (prev.operations || []).concat(op)
- )
- };
- return;
}
});
diff --git a/js/src/ui/CopyToClipboard/copyToClipboard.css b/js/src/ui/CopyToClipboard/copyToClipboard.css
index 0c511ba45..6cc8d8030 100644
--- a/js/src/ui/CopyToClipboard/copyToClipboard.css
+++ b/js/src/ui/CopyToClipboard/copyToClipboard.css
@@ -20,5 +20,14 @@
}
.data {
+ flex: 1;
font-family: monospace;
+ padding: 0 0.5em;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.container {
+ display: flex;
}
diff --git a/js/src/ui/CopyToClipboard/copyToClipboard.js b/js/src/ui/CopyToClipboard/copyToClipboard.js
index 7a654f9ce..b7041658c 100644
--- a/js/src/ui/CopyToClipboard/copyToClipboard.js
+++ b/js/src/ui/CopyToClipboard/copyToClipboard.js
@@ -80,7 +80,13 @@ class CopyToClipboard extends Component {
onCopy = () => {
const { data, onCopy, cooldown, showSnackbar } = this.props;
- const message = (
copied { data }
to clipboard
);
+ const message = (
+
+ copied
+ { data }
+ to clipboard
+
+ );
this.setState({
copied: true,
diff --git a/js/src/ui/Form/AddressSelect/addressSelect.js b/js/src/ui/Form/AddressSelect/addressSelect.js
index fbaef2ee6..d0f331c34 100644
--- a/js/src/ui/Form/AddressSelect/addressSelect.js
+++ b/js/src/ui/Form/AddressSelect/addressSelect.js
@@ -170,7 +170,7 @@ export default class AddressSelect extends Component {
handleFilter = (searchText, name, item) => {
const { address } = item;
const entry = this.state.entries[address];
- const lowCaseSearch = searchText.toLowerCase();
+ const lowCaseSearch = (searchText || '').toLowerCase();
return [entry.name, entry.address]
.some(text => text.toLowerCase().indexOf(lowCaseSearch) !== -1);
diff --git a/js/src/ui/Form/InputAddress/inputAddress.js b/js/src/ui/Form/InputAddress/inputAddress.js
index da2c3b8d3..f403cf311 100644
--- a/js/src/ui/Form/InputAddress/inputAddress.js
+++ b/js/src/ui/Form/InputAddress/inputAddress.js
@@ -53,7 +53,6 @@ class InputAddress extends Component {
const { small, allowCopy, hideUnderline, onSubmit, accountsInfo, tokens } = this.props;
const account = accountsInfo[value] || tokens[value];
- const hasAccount = account && !(account.meta && account.meta.deleted);
const icon = this.renderIcon();
@@ -74,7 +73,7 @@ class InputAddress extends Component {
label={ label }
hint={ hint }
error={ error }
- value={ text && hasAccount ? account.name : value }
+ value={ text && account ? account.name : value }
onChange={ this.handleInputChange }
onSubmit={ onSubmit }
allowCopy={ allowCopy && (disabled ? value : false) }
diff --git a/js/src/ui/Form/TypedInput/typedInput.css b/js/src/ui/Form/TypedInput/typedInput.css
index c13206c96..ab911edac 100644
--- a/js/src/ui/Form/TypedInput/typedInput.css
+++ b/js/src/ui/Form/TypedInput/typedInput.css
@@ -29,3 +29,30 @@
position: relative;
}
}
+
+.ethInput {
+ display: flex;
+ flex-direction: row;
+ align-items: flex-end;
+
+ .input {
+ flex: 1;
+ position: relative;
+
+ .label {
+ position: absolute;
+ right: 1.5em;
+ bottom: 1em;
+ font-size: 0.85em;
+ margin-left: 0.5em;
+ }
+ }
+
+ .toggle {
+ margin-bottom: 0.5em;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ }
+}
+
diff --git a/js/src/ui/Form/TypedInput/typedInput.js b/js/src/ui/Form/TypedInput/typedInput.js
index c0b1f5548..a81ec7b79 100644
--- a/js/src/ui/Form/TypedInput/typedInput.js
+++ b/js/src/ui/Form/TypedInput/typedInput.js
@@ -15,18 +15,18 @@
// along with Parity. If not, see
.
import React, { Component, PropTypes } from 'react';
-import { MenuItem } from 'material-ui';
+import { MenuItem, Toggle } from 'material-ui';
import { range } from 'lodash';
import IconButton from 'material-ui/IconButton';
import AddIcon from 'material-ui/svg-icons/content/add';
import RemoveIcon from 'material-ui/svg-icons/content/remove';
+import { fromWei, toWei } from '~/api/util/wei';
import Input from '~/ui/Form/Input';
import InputAddressSelect from '~/ui/Form/InputAddressSelect';
import Select from '~/ui/Form/Select';
-
-import { ABI_TYPES } from '../../../util/abi';
+import { ABI_TYPES } from '~/util/abi';
import styles from './typedInput.css';
@@ -42,16 +42,29 @@ export default class TypedInput extends Component {
label: PropTypes.string,
hint: PropTypes.string,
min: PropTypes.number,
- max: PropTypes.number
+ max: PropTypes.number,
+ isEth: PropTypes.bool
};
static defaultProps = {
min: null,
- max: null
+ max: null,
+ isEth: false
};
+ state = {
+ isEth: true,
+ ethValue: 0
+ };
+
+ componentDidMount () {
+ if (this.props.isEth && this.props.value) {
+ this.setState({ ethValue: fromWei(this.props.value) });
+ }
+ }
+
render () {
- const { param } = this.props;
+ const { param, isEth } = this.props;
const { type } = param;
if (type === ABI_TYPES.ARRAY) {
@@ -87,6 +100,10 @@ export default class TypedInput extends Component {
);
}
+ if (isEth) {
+ return this.renderEth();
+ }
+
return this.renderType(type);
}
@@ -157,16 +174,43 @@ export default class TypedInput extends Component {
return this.renderDefault();
}
- renderNumber () {
- const { label, value, error, param, hint, min, max } = this.props;
+ renderEth () {
+ const { ethValue } = this.state;
+
+ const value = ethValue && typeof ethValue.toNumber === 'function'
+ ? ethValue.toNumber()
+ : ethValue;
+
+ return (
+
+
+ { this.renderNumber(value, this.onEthValueChange) }
+ { this.state.isEth ? (
ETH
) : null }
+
+
+
+
+
+ );
+ }
+
+ renderNumber (value = this.props.value, onChange = this.onChange) {
+ const { label, error, param, hint, min, max } = this.props;
+ const realValue = value && typeof value.toNumber === 'function'
+ ? value.toNumber()
+ : value;
return (
{
+ const { isEth, ethValue } = this.state;
+
+ if (ethValue === '' || ethValue === undefined) {
+ return this.setState({ isEth: !isEth });
+ }
+
+ const value = isEth ? toWei(ethValue) : fromWei(ethValue);
+ this.setState({ isEth: !isEth, ethValue: value }, () => {
+ this.onEthValueChange(null, value);
+ });
+ }
+
+ onEthValueChange = (event, value) => {
+ const realValue = this.state.isEth && value !== '' && value !== undefined
+ ? toWei(value)
+ : value;
+
+ this.setState({ ethValue: value });
+ this.props.onChange(realValue);
+ }
+
onChange = (event, value) => {
this.props.onChange(value);
}
diff --git a/js/src/modals/Transfer/GasPriceSelector/gasPriceSelector.css b/js/src/ui/GasPriceEditor/GasPriceSelector/gasPriceSelector.css
similarity index 85%
rename from js/src/modals/Transfer/GasPriceSelector/gasPriceSelector.css
rename to js/src/ui/GasPriceEditor/GasPriceSelector/gasPriceSelector.css
index 445174c59..247211c50 100644
--- a/js/src/modals/Transfer/GasPriceSelector/gasPriceSelector.css
+++ b/js/src/ui/GasPriceEditor/GasPriceSelector/gasPriceSelector.css
@@ -15,3 +15,13 @@
/* along with Parity. If not, see
.
*/
+.chart {
+ position: absolute;
+ width: 100%;
+}
+
+.columns {
+ display: flex;
+ flex-wrap: wrap;
+ position: relative;
+}
diff --git a/js/src/modals/Transfer/GasPriceSelector/gasPriceSelector.js b/js/src/ui/GasPriceEditor/GasPriceSelector/gasPriceSelector.js
similarity index 77%
rename from js/src/modals/Transfer/GasPriceSelector/gasPriceSelector.js
rename to js/src/ui/GasPriceEditor/GasPriceSelector/gasPriceSelector.js
index adf644c4e..893a50188 100644
--- a/js/src/modals/Transfer/GasPriceSelector/gasPriceSelector.js
+++ b/js/src/ui/GasPriceEditor/GasPriceSelector/gasPriceSelector.js
@@ -29,10 +29,7 @@ import {
import Slider from 'material-ui/Slider';
import BigNumber from 'bignumber.js';
-import componentStyles from './gasPriceSelector.css';
-import mainStyles from '../transfer.css';
-
-const styles = Object.assign({}, mainStyles, componentStyles);
+import styles from './gasPriceSelector.css';
const COLORS = {
default: 'rgba(255, 99, 132, 0.2)',
@@ -194,10 +191,7 @@ class CustomizedShape extends Component {
class CustomTooltip extends Component {
static propTypes = {
- gasPriceHistogram: PropTypes.shape({
- bucketBounds: PropTypes.array.isRequired,
- counts: PropTypes.array.isRequired
- }).isRequired,
+ gasPriceHistogram: PropTypes.object.isRequired,
type: PropTypes.string,
payload: PropTypes.array,
label: PropTypes.number,
@@ -231,12 +225,16 @@ class CustomTooltip extends Component {
}
}
+const TOOL_STYLE = {
+ color: 'rgba(255,255,255,0.5)',
+ backgroundColor: 'rgba(0, 0, 0, 0.75)',
+ padding: '0 0.5em',
+ fontSize: '0.75em'
+};
+
export default class GasPriceSelector extends Component {
static propTypes = {
- gasPriceHistogram: PropTypes.shape({
- bucketBounds: PropTypes.array.isRequired,
- counts: PropTypes.array.isRequired
- }).isRequired,
+ gasPriceHistogram: PropTypes.object.isRequired,
onChange: PropTypes.func.isRequired,
gasPrice: PropTypes.oneOfType([
@@ -287,21 +285,23 @@ export default class GasPriceSelector extends Component {
renderSlider () {
const { sliderValue } = this.state;
- return (
-
-
);
+ return (
+
+
+
+ );
}
renderChart () {
@@ -316,85 +316,83 @@ export default class GasPriceSelector extends Component {
const countIndex = Math.max(0, Math.min(selectedIndex, gasPriceHistogram.counts.length - 1));
const selectedCount = countModifier(gasPriceHistogram.counts[countIndex]);
- return (
-
-
-
-
+
+
+
- }
- line
- isAnimationActive={ false }
- />
+
+ }
+ line
+ isAnimationActive={ false }
+ />
-
-
-
-
-
+
+
+
+
+
-
-
-
+
- }
- />
+
+ }
+ />
- }
- />
+ }
+ />
-
-
-
-
+
+
+
+
+
-
);
+ );
}
renderCustomCursor = () => {
diff --git a/js/src/modals/Transfer/GasPriceSelector/index.js b/js/src/ui/GasPriceEditor/GasPriceSelector/index.js
similarity index 100%
rename from js/src/modals/Transfer/GasPriceSelector/index.js
rename to js/src/ui/GasPriceEditor/GasPriceSelector/index.js
diff --git a/js/src/views/Signer/signer.css b/js/src/ui/GasPriceEditor/gasPriceEditor.css
similarity index 62%
rename from js/src/views/Signer/signer.css
rename to js/src/ui/GasPriceEditor/gasPriceEditor.css
index 83d1b800f..cf1fff81c 100644
--- a/js/src/views/Signer/signer.css
+++ b/js/src/ui/GasPriceEditor/gasPriceEditor.css
@@ -14,11 +14,36 @@
/* You should have received a copy of the GNU General Public License
/* along with Parity. If not, see
.
*/
-.signer {
+
+.columns {
+ display: flex;
+ flex-wrap: wrap;
+ position: relative;
}
-.container {
+.graphColumn {
+ flex: 65;
}
-.mainContainer {
+.editColumn {
+ flex: 35;
+ padding-left: 1em;
+ justify-ontent: space-around;
+ padding-bottom: 12;
+ display: flex;
+ flex-wrap: wrap;
+ position: relative;
+ flex-direction: column;
+}
+
+.gasPriceDesc {
+ font-size: 0.75em;
+ opacity: 0.5;
+}
+
+.row {
+ display: flex;
+ flex-wrap: wrap;
+ position: relative;
+ flex-direction: column;
}
diff --git a/js/src/ui/GasPriceEditor/gasPriceEditor.js b/js/src/ui/GasPriceEditor/gasPriceEditor.js
new file mode 100644
index 000000000..8c94dfca7
--- /dev/null
+++ b/js/src/ui/GasPriceEditor/gasPriceEditor.js
@@ -0,0 +1,108 @@
+// 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
.
+
+import BigNumber from 'bignumber.js';
+import React, { Component, PropTypes } from 'react';
+import { observer } from 'mobx-react';
+
+import Input from '../Form/Input';
+import GasPriceSelector from './GasPriceSelector';
+import Store from './store';
+
+import styles from './gasPriceEditor.css';
+
+@observer
+export default class GasPriceEditor extends Component {
+ static contextTypes = {
+ api: PropTypes.object.isRequired
+ };
+
+ static propTypes = {
+ store: PropTypes.object.isRequired,
+ onChange: PropTypes.func
+ }
+
+ static Store = Store;
+
+ render () {
+ const { api } = this.context;
+ const { store } = this.props;
+ const { estimated, priceDefault, price, gas, histogram, errorGas, errorPrice, errorTotal, totalValue } = store;
+
+ const eth = api.util.fromWei(totalValue).toFormat();
+ const gasLabel = `gas (estimated: ${new BigNumber(estimated).toFormat()})`;
+ const priceLabel = `price (current: ${new BigNumber(priceDefault).toFormat()})`;
+
+ return (
+
+
+
+
+ You can choose the gas price based on the
+ distribution of recent included transaction gas prices.
+ The lower the gas price is, the cheaper the transaction will
+ be. The higher the gas price is, the faster it should
+ get mined by the network.
+
+
+
+
+
+ );
+ }
+
+ onEditGas = (event, gas) => {
+ const { store, onChange } = this.props;
+
+ store.setGas(gas);
+ onChange && onChange('gas', gas);
+ }
+
+ onEditGasPrice = (event, price) => {
+ const { store, onChange } = this.props;
+
+ store.setPrice(price);
+ onChange && onChange('gasPrice', price);
+ }
+}
diff --git a/js/src/util/is-testnet.js b/js/src/ui/GasPriceEditor/index.js
similarity index 86%
rename from js/src/util/is-testnet.js
rename to js/src/ui/GasPriceEditor/index.js
index c2bf2c450..956f6ffd2 100644
--- a/js/src/util/is-testnet.js
+++ b/js/src/ui/GasPriceEditor/index.js
@@ -14,6 +14,4 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see
.
-export default (chain) => {
- return chain === 'morden' || chain === 'ropsten' || chain === 'testnet';
-};
+export default from './gasPriceEditor';
diff --git a/js/src/ui/GasPriceEditor/store.js b/js/src/ui/GasPriceEditor/store.js
new file mode 100644
index 000000000..afa5e15b2
--- /dev/null
+++ b/js/src/ui/GasPriceEditor/store.js
@@ -0,0 +1,123 @@
+// 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
.
+
+import BigNumber from 'bignumber.js';
+import { action, computed, observable, transaction } from 'mobx';
+
+import { ERRORS, validatePositiveNumber } from '~/util/validation';
+import { DEFAULT_GAS, DEFAULT_GASPRICE, MAX_GAS_ESTIMATION } from '~/util/constants';
+
+export default class GasPriceEditor {
+ @observable errorEstimated = null;
+ @observable errorGas = null;
+ @observable errorPrice = null;
+ @observable errorTotal = null;
+ @observable estimated = DEFAULT_GAS;
+ @observable histogram = null;
+ @observable price = DEFAULT_GASPRICE;
+ @observable priceDefault = DEFAULT_GASPRICE;
+ @observable gas = DEFAULT_GAS;
+ @observable gasLimit = 0;
+ @observable weiValue = '0';
+
+ constructor (api, gasLimit, loadDefaults = true) {
+ this._api = api;
+ this.gasLimit = gasLimit;
+
+ if (loadDefaults) {
+ this.loadDefaults();
+ }
+ }
+
+ @computed get totalValue () {
+ try {
+ return new BigNumber(this.gas).mul(this.price).add(this.weiValue);
+ } catch (error) {
+ return new BigNumber(0);
+ }
+ }
+
+ @action setErrorTotal = (errorTotal) => {
+ this.errorTotal = errorTotal;
+ }
+
+ @action setEstimated = (estimated) => {
+ transaction(() => {
+ const bn = new BigNumber(estimated);
+
+ this.estimated = estimated;
+
+ if (bn.gte(MAX_GAS_ESTIMATION)) {
+ this.errorEstimated = ERRORS.gasException;
+ } else if (bn.gte(this.gasLimit)) {
+ this.errorEstimated = ERRORS.gasBlockLimit;
+ } else {
+ this.errorEstimated = null;
+ }
+ });
+ }
+
+ @action setEthValue = (weiValue) => {
+ this.weiValue = weiValue;
+ }
+
+ @action setHistogram = (gasHistogram) => {
+ this.histogram = gasHistogram;
+ }
+
+ @action setPrice = (price) => {
+ transaction(() => {
+ this.errorPrice = validatePositiveNumber(price).numberError;
+ this.price = price;
+ });
+ }
+
+ @action setGas = (gas) => {
+ transaction(() => {
+ const { numberError } = validatePositiveNumber(gas);
+ const bn = new BigNumber(gas);
+
+ this.gas = gas;
+
+ if (numberError) {
+ this.errorGas = numberError;
+ } else if (bn.gte(this.gasLimit)) {
+ this.errorGas = ERRORS.gasBlockLimit;
+ } else {
+ this.errorGas = null;
+ }
+ });
+ }
+
+ @action loadDefaults () {
+ Promise
+ .all([
+ this._api.parity.gasPriceHistogram(),
+ this._api.eth.gasPrice()
+ ])
+ .then(([gasPriceHistogram, gasPrice]) => {
+ transaction(() => {
+ this.setPrice(gasPrice.toFixed(0));
+ this.setHistogram(gasPriceHistogram);
+
+ this.priceDefault = gasPrice.toFixed();
+ });
+ })
+ .catch((error) => {
+ console.warn('getDefaults', error);
+ });
+ }
+}
diff --git a/js/src/ui/IdentityName/identityName.js b/js/src/ui/IdentityName/identityName.js
index 85ff34a35..9da886ba0 100644
--- a/js/src/ui/IdentityName/identityName.js
+++ b/js/src/ui/IdentityName/identityName.js
@@ -37,17 +37,16 @@ class IdentityName extends Component {
render () {
const { address, accountsInfo, tokens, empty, name, shorten, unknown, className } = this.props;
const account = accountsInfo[address] || tokens[address];
- const hasAccount = account && (!account.meta || !account.meta.deleted);
- if (!hasAccount && empty) {
+ if (!account && empty) {
return null;
}
const addressFallback = shorten ? (
) : address;
const fallback = unknown ? defaultName : addressFallback;
- const isUuid = hasAccount && account.name === account.uuid;
+ const isUuid = account && account.name === account.uuid;
const displayName = (name && name.toUpperCase().trim()) ||
- (hasAccount && !isUuid
+ (account && !isUuid
? account.name.toUpperCase().trim()
: fallback);
diff --git a/js/src/ui/TxHash/txHash.js b/js/src/ui/TxHash/txHash.js
index 81c4e0bf5..715568fd9 100644
--- a/js/src/ui/TxHash/txHash.js
+++ b/js/src/ui/TxHash/txHash.js
@@ -20,7 +20,7 @@ import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { LinearProgress } from 'material-ui';
-import { txLink } from '../../3rdparty/etherscan/links';
+import { txLink } from '~/3rdparty/etherscan/links';
import ShortenedHash from '../ShortenedHash';
import styles from './txHash.css';
diff --git a/js/src/ui/TxList/txList.js b/js/src/ui/TxList/txList.js
index b8c53c1d9..3f5dab264 100644
--- a/js/src/ui/TxList/txList.js
+++ b/js/src/ui/TxList/txList.js
@@ -20,7 +20,7 @@ import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { observer } from 'mobx-react';
-import { txLink, addressLink } from '../../3rdparty/etherscan/links';
+import { txLink, addressLink } from '~/3rdparty/etherscan/links';
import IdentityIcon from '../IdentityIcon';
import IdentityName from '../IdentityName';
diff --git a/js/src/ui/index.js b/js/src/ui/index.js
index 6c763f0f3..c5a965458 100644
--- a/js/src/ui/index.js
+++ b/js/src/ui/index.js
@@ -31,6 +31,7 @@ import CopyToClipboard from './CopyToClipboard';
import Editor from './Editor';
import Errors from './Errors';
import Form, { AddressSelect, FormWrap, TypedInput, Input, InputAddress, InputAddressSelect, InputChip, InputInline, Select, RadioButtons } from './Form';
+import GasPriceEditor from './GasPriceEditor';
import IdentityIcon from './IdentityIcon';
import IdentityName from './IdentityName';
import Loading from './Loading';
@@ -67,7 +68,7 @@ export {
Errors,
Form,
FormWrap,
- TypedInput,
+ GasPriceEditor,
Input,
InputAddress,
InputAddressSelect,
@@ -91,5 +92,6 @@ export {
Tooltip,
Tooltips,
TxHash,
- TxList
+ TxList,
+ TypedInput
};
diff --git a/js/src/util/check-if-tx-failed.js b/js/src/util/check-if-tx-failed.js
deleted file mode 100644
index 39689bedd..000000000
--- a/js/src/util/check-if-tx-failed.js
+++ /dev/null
@@ -1,28 +0,0 @@
-// 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
.
-
-const checkIfTxFailed = (api, tx, gasSent) => {
- return api.pollMethod('eth_getTransactionReceipt', tx)
- .then((receipt) => {
- // TODO: Right now, there's no way to tell wether the EVM code crashed.
- // Because you usually send a bit more gas than estimated (to make sure
- // it gets mined quickly), we transaction probably failed if all the gas
- // has been used up.
- return receipt.gasUsed.eq(gasSent);
- });
-};
-
-export default checkIfTxFailed;
diff --git a/js/src/util/wait-for-block-confirmations.js b/js/src/util/tx.js
similarity index 73%
rename from js/src/util/wait-for-block-confirmations.js
rename to js/src/util/tx.js
index 79ba2be25..b688d06fe 100644
--- a/js/src/util/wait-for-block-confirmations.js
+++ b/js/src/util/tx.js
@@ -18,7 +18,18 @@ const isValidReceipt = (receipt) => {
return receipt && receipt.blockNumber && receipt.blockNumber.gt(0);
};
-const waitForConfirmations = (api, tx, confirmations) => {
+export function checkIfTxFailed (api, tx, gasSent) {
+ return api.pollMethod('eth_getTransactionReceipt', tx)
+ .then((receipt) => {
+ // TODO: Right now, there's no way to tell wether the EVM code crashed.
+ // Because you usually send a bit more gas than estimated (to make sure
+ // it gets mined quickly), we transaction probably failed if all the gas
+ // has been used up.
+ return receipt.gasUsed.eq(gasSent);
+ });
+}
+
+export function waitForConfirmations (api, tx, confirmations) {
return new Promise((resolve, reject) => {
api.pollMethod('eth_getTransactionReceipt', tx, isValidReceipt)
.then((receipt) => {
@@ -39,6 +50,4 @@ const waitForConfirmations = (api, tx, confirmations) => {
.catch(reject);
});
});
-};
-
-export default waitForConfirmations;
+}
diff --git a/js/src/util/validation.js b/js/src/util/validation.js
index 0666f1ad3..3d312a2f4 100644
--- a/js/src/util/validation.js
+++ b/js/src/util/validation.js
@@ -20,6 +20,7 @@ import util from '~/api/util';
export const ERRORS = {
invalidAddress: 'address is an invalid network address',
+ invalidAmount: 'the supplied amount should be a valid positive number',
duplicateAddress: 'the address is already in your address book',
invalidChecksum: 'address has failed the checksum formatting',
invalidName: 'name should not be blank and longer than 2',
@@ -27,7 +28,9 @@ export const ERRORS = {
invalidCode: 'code should be the compiled hex string',
invalidNumber: 'invalid number format',
negativeNumber: 'input number should be positive',
- decimalNumber: 'input number should not contain decimals'
+ decimalNumber: 'input number should not contain decimals',
+ gasException: 'the transaction will throw an exception with the current values',
+ gasBlockLimit: 'the transaction execution will exceed the block gas limit'
};
export function validateAbi (abi, api) {
@@ -133,6 +136,25 @@ export function validateName (name) {
};
}
+export function validatePositiveNumber (number) {
+ let numberError = null;
+
+ try {
+ const v = new BigNumber(number);
+
+ if (v.lt(0)) {
+ numberError = ERRORS.invalidAmount;
+ }
+ } catch (e) {
+ numberError = ERRORS.invalidAmount;
+ }
+
+ return {
+ number,
+ numberError
+ };
+}
+
export function validateUint (value) {
let valueError = null;
@@ -140,7 +162,7 @@ export function validateUint (value) {
const bn = new BigNumber(value);
if (bn.lt(0)) {
valueError = ERRORS.negativeNumber;
- } else if (bn.toString().indexOf('.') !== -1) {
+ } else if (!bn.isInteger()) {
valueError = ERRORS.decimalNumber;
}
} catch (e) {
diff --git a/js/src/views/Account/Transactions/transactions.css b/js/src/views/Account/Transactions/transactions.css
index 13d727deb..6b44d298f 100644
--- a/js/src/views/Account/Transactions/transactions.css
+++ b/js/src/views/Account/Transactions/transactions.css
@@ -15,9 +15,6 @@
/* along with Parity. If not, see
.
*/
-.transactions {
-}
-
.infonone {
opacity: 0.25;
}
diff --git a/js/src/views/Account/Transactions/transactions.js b/js/src/views/Account/Transactions/transactions.js
index e71781adf..2e98208b6 100644
--- a/js/src/views/Account/Transactions/transactions.js
+++ b/js/src/views/Account/Transactions/transactions.js
@@ -18,7 +18,7 @@ import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
-import etherscan from '../../../3rdparty/etherscan';
+import etherscan from '~/3rdparty/etherscan';
import { Container, TxList } from '~/ui';
import styles from './transactions.css';
diff --git a/js/src/views/Account/account.css b/js/src/views/Account/account.css
index b1410f23a..a9a039c83 100644
--- a/js/src/views/Account/account.css
+++ b/js/src/views/Account/account.css
@@ -14,8 +14,6 @@
/* You should have received a copy of the GNU General Public License
/* along with Parity. If not, see
.
*/
-.account {
-}
.btnicon {
width: 24px;
diff --git a/js/src/views/Account/account.js b/js/src/views/Account/account.js
index 98b0a5e97..cbacd5280 100644
--- a/js/src/views/Account/account.js
+++ b/js/src/views/Account/account.js
@@ -105,7 +105,7 @@ class Account extends Component {
}
return (
-
+
{ this.renderDeleteDialog(account) }
{ this.renderEditDialog(account) }
{ this.renderFundDialog() }
diff --git a/js/src/views/Accounts/List/list.js b/js/src/views/Accounts/List/list.js
index c2e961bb8..4d54b640f 100644
--- a/js/src/views/Accounts/List/list.js
+++ b/js/src/views/Accounts/List/list.js
@@ -24,6 +24,7 @@ import styles from './list.css';
export default class List extends Component {
static propTypes = {
accounts: PropTypes.object,
+ walletsOwners: PropTypes.object,
balances: PropTypes.object,
link: PropTypes.string,
search: PropTypes.array,
@@ -42,7 +43,7 @@ export default class List extends Component {
}
renderAccounts () {
- const { accounts, balances, link, empty, handleAddSearchToken } = this.props;
+ const { accounts, balances, link, empty, handleAddSearchToken, walletsOwners } = this.props;
if (empty) {
return (
@@ -60,6 +61,8 @@ export default class List extends Component {
const account = accounts[address] || {};
const balance = balances[address] || {};
+ const owners = walletsOwners && walletsOwners[address] || null;
+
return (
);
diff --git a/js/src/views/Accounts/Summary/summary.js b/js/src/views/Accounts/Summary/summary.js
index 842629c6f..aeff8a2e5 100644
--- a/js/src/views/Accounts/Summary/summary.js
+++ b/js/src/views/Accounts/Summary/summary.js
@@ -14,11 +14,16 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see
.
+import BigNumber from 'bignumber.js';
import React, { Component, PropTypes } from 'react';
import { Link } from 'react-router';
import { isEqual } from 'lodash';
+import ReactTooltip from 'react-tooltip';
import { Balance, Container, ContainerTitle, IdentityIcon, IdentityName, Tags, Input } from '~/ui';
+import { nullableProptype } from '~/util/proptypes';
+
+import styles from '../accounts.css';
export default class Summary extends Component {
static contextTypes = {
@@ -31,7 +36,8 @@ export default class Summary extends Component {
link: PropTypes.string,
name: PropTypes.string,
noLink: PropTypes.bool,
- handleAddSearchToken: PropTypes.func
+ handleAddSearchToken: PropTypes.func,
+ owners: nullableProptype(PropTypes.array)
};
static defaultProps = {
@@ -100,11 +106,42 @@ export default class Summary extends Component {
title={ this.renderLink() }
byline={ addressComponent } />
+ { this.renderOwners() }
{ this.renderBalance() }
);
}
+ renderOwners () {
+ const { owners } = this.props;
+ const ownersValid = (owners || []).filter((owner) => owner.address && new BigNumber(owner.address).gt(0));
+
+ if (!ownersValid || ownersValid.length === 0) {
+ return null;
+ }
+
+ return (
+
+ {
+ ownersValid.map((owner) => (
+
+
+
+
+
+ { owner.name } (owner)
+
+
+ ))
+ }
+
+ );
+ }
+
renderLink () {
const { link, noLink, account, name } = this.props;
diff --git a/js/src/views/Accounts/accounts.css b/js/src/views/Accounts/accounts.css
index 0ed8b5256..317905fd5 100644
--- a/js/src/views/Accounts/accounts.css
+++ b/js/src/views/Accounts/accounts.css
@@ -14,14 +14,18 @@
/* You should have received a copy of the GNU General Public License
/* along with Parity. If not, see
.
*/
-.accounts {
-}
.accountTooltip {
top: 13.3em;
left: 7em;
}
+.owners {
+ margin-top: 1em;
+ display: flex;
+ margin-bottom: -0.5em;
+}
+
.toolbar {
position: relative;
}
diff --git a/js/src/views/Accounts/accounts.js b/js/src/views/Accounts/accounts.js
index f71426aec..a844ae989 100644
--- a/js/src/views/Accounts/accounts.js
+++ b/js/src/views/Accounts/accounts.js
@@ -37,6 +37,7 @@ class Accounts extends Component {
accounts: PropTypes.object.isRequired,
hasAccounts: PropTypes.bool.isRequired,
wallets: PropTypes.object.isRequired,
+ walletsOwners: PropTypes.object.isRequired,
hasWallets: PropTypes.bool.isRequired,
balances: PropTypes.object
@@ -81,7 +82,7 @@ class Accounts extends Component {
render () {
return (
-
+
{ this.renderNewDialog() }
{ this.renderNewWalletDialog() }
{ this.renderActionbar() }
@@ -137,9 +138,13 @@ class Accounts extends Component {
return this.renderLoading(this.props.wallets);
}
- const { wallets, hasWallets, balances } = this.props;
+ const { wallets, hasWallets, balances, walletsOwners } = this.props;
const { searchValues, sortOrder } = this.state;
+ if (!wallets || Object.keys(wallets).length === 0) {
+ return null;
+ }
+
return (
);
}
@@ -281,13 +287,33 @@ class Accounts extends Component {
}
function mapStateToProps (state) {
- const { accounts, hasAccounts, wallets, hasWallets } = state.personal;
+ const { accounts, hasAccounts, wallets, hasWallets, accountsInfo } = state.personal;
const { balances } = state.balances;
+ const walletsInfo = state.wallet.wallets;
+
+ const walletsOwners = Object
+ .keys(walletsInfo)
+ .map((wallet) => {
+ const owners = walletsInfo[wallet].owners || [];
+
+ return {
+ owners: owners.map((owner) => ({
+ address: owner,
+ name: accountsInfo[owner] && accountsInfo[owner].name || owner
+ })),
+ address: wallet
+ };
+ })
+ .reduce((walletsOwners, wallet) => {
+ walletsOwners[wallet.address] = wallet.owners;
+ return walletsOwners;
+ }, {});
return {
accounts,
hasAccounts,
wallets,
+ walletsOwners,
hasWallets,
balances
};
diff --git a/js/src/views/Address/Delete/delete.js b/js/src/views/Address/Delete/delete.js
index d1501ab8b..2d19f2141 100644
--- a/js/src/views/Address/Delete/delete.js
+++ b/js/src/views/Address/Delete/delete.js
@@ -80,10 +80,8 @@ class Delete extends Component {
const { api, router } = this.context;
const { account, route, newError } = this.props;
- account.meta.deleted = true;
-
api.parity
- .setAccountMeta(account.address, account.meta)
+ .removeAddress(account.address)
.then(() => {
router.push(route);
this.closeDeleteDialog();
diff --git a/js/src/views/Address/address.css b/js/src/views/Address/address.css
index d93b88b59..cd44688bb 100644
--- a/js/src/views/Address/address.css
+++ b/js/src/views/Address/address.css
@@ -14,37 +14,37 @@
/* You should have received a copy of the GNU General Public License
/* along with Parity. If not, see
.
*/
-.address {
-}
-.delete .hero {
- padding-bottom: 1em;
-}
+.delete {
+ .hero {
+ padding-bottom: 1em;
+ }
-.delete .info {
- display: inline-block;
-}
+ .info {
+ display: inline-block;
+ }
-.delete .icon {
- display: inline-block;
-}
+ .icon {
+ display: inline-block;
+ }
-.delete .nameinfo {
- display: inline-block;
- text-align: left;
-}
+ .nameinfo {
+ display: inline-block;
+ text-align: left;
+ }
-.delete .header {
- text-transform: uppercase;
- font-size: 1.25em;
- padding-bottom: 0.25em;
-}
+ .header {
+ text-transform: uppercase;
+ font-size: 1.25em;
+ padding-bottom: 0.25em;
+ }
-.delete .address {
-}
+ .address {
+ }
-.delete .description {
- padding-top: 1em;
- font-size: 0.75em;
- color: #aaa;
+ .description {
+ padding-top: 1em;
+ font-size: 0.75em;
+ color: #aaa;
+ }
}
diff --git a/js/src/views/Address/address.js b/js/src/views/Address/address.js
index 6c0ff0920..c1427b2be 100644
--- a/js/src/views/Address/address.js
+++ b/js/src/views/Address/address.js
@@ -28,8 +28,6 @@ import Transactions from '../Account/Transactions';
import Delete from './Delete';
import { setVisibleAccounts } from '~/redux/providers/personalActions';
-import styles from './address.css';
-
class Address extends Component {
static contextTypes = {
api: PropTypes.object.isRequired,
@@ -85,7 +83,7 @@ class Address extends Component {
}
return (
-
+
{ this.renderEditDialog(contact) }
{ this.renderActionbar(contact) }
+ buttons={ !contact ? [] : buttons } />
);
}
diff --git a/js/src/views/Addresses/addresses.css b/js/src/views/Addresses/addresses.css
index 5dbbe9aec..5bbbf8a7e 100644
--- a/js/src/views/Addresses/addresses.css
+++ b/js/src/views/Addresses/addresses.css
@@ -14,8 +14,6 @@
/* You should have received a copy of the GNU General Public License
/* along with Parity. If not, see
.
*/
-.addresses {
-}
.list {
display: flex;
@@ -26,21 +24,21 @@
flex: 0 1 50%;
width: 50%;
position: relative;
-}
-.address:nth-child(odd)>div {
- padding-right: 0.5em !important;
-}
+ &:nth-child(odd)>div {
+ padding-right: 0.5em !important;
+ }
-.address:nth-child(even)>div {
- padding-left: 0.5em !important;
+ &:nth-child(even)>div {
+ padding-left: 0.5em !important;
+ }
}
.empty {
width: 100%;
display: block;
-}
-.empty div {
- color: #aaa;
+ div {
+ color: #aaa;
+ }
}
diff --git a/js/src/views/Addresses/addresses.js b/js/src/views/Addresses/addresses.js
index 4edfb0e6e..5e0ed4e18 100644
--- a/js/src/views/Addresses/addresses.js
+++ b/js/src/views/Addresses/addresses.js
@@ -76,7 +76,7 @@ class Addresses extends Component {
const { searchValues, sortOrder } = this.state;
return (
-
+
{ this.renderActionbar() }
{ this.renderAddAddress() }
diff --git a/js/src/views/Connection/connection.js b/js/src/views/Connection/connection.js
index 451624a2b..ad0e0c140 100644
--- a/js/src/views/Connection/connection.js
+++ b/js/src/views/Connection/connection.js
@@ -19,7 +19,6 @@ import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import ActionCompareArrows from 'material-ui/svg-icons/action/compare-arrows';
import ActionDashboard from 'material-ui/svg-icons/action/dashboard';
-// import CommunicationVpnKey from 'material-ui/svg-icons/communication/vpn-key';
import HardwareDesktopMac from 'material-ui/svg-icons/hardware/desktop-mac';
import NotificationVpnLock from 'material-ui/svg-icons/notification/vpn-lock';
diff --git a/js/src/views/Contract/Events/Event/event.js b/js/src/views/Contract/Events/Event/event.js
index b42295198..1ed114d07 100644
--- a/js/src/views/Contract/Events/Event/event.js
+++ b/js/src/views/Contract/Events/Event/event.js
@@ -20,7 +20,7 @@ import React, { Component, PropTypes } from 'react';
import { IdentityIcon, IdentityName, Input, InputAddress } from '~/ui';
import ShortenedHash from '~/ui/ShortenedHash';
-import { txLink } from '../../../../3rdparty/etherscan/links';
+import { txLink } from '~/3rdparty/etherscan/links';
import styles from '../../contract.css';
diff --git a/js/src/views/Contract/Events/events.js b/js/src/views/Contract/Events/events.js
index f0bee3e25..69ae8fd6a 100644
--- a/js/src/views/Contract/Events/events.js
+++ b/js/src/views/Contract/Events/events.js
@@ -15,6 +15,7 @@
// along with Parity. If not, see .
import React, { Component, PropTypes } from 'react';
+import { uniq } from 'lodash';
import { Container } from '~/ui';
@@ -38,7 +39,10 @@ export default class Events extends Component {
return null;
}
- const list = events.map((event) => {
+ const eventsKey = uniq(events.map((e) => e.key));
+ const list = eventsKey.map((eventKey) => {
+ const event = events.find((e) => e.key === eventKey);
+
return (
.
*/
-.contract {
-}
-
.events {
width: 100%;
border: none;
border-spacing: 0;
-}
-.events tr {
- line-height: 32px;
- vertical-align: top;
+ tr {
+ line-height: 32px;
+ vertical-align: top;
+ }
}
.event {
-}
+ td {
+ vertical-align: top;
+ padding: 1em 0.5em;
-.event td {
- vertical-align: top;
- padding: 1em 0.5em;
+ div {
+ white-space: nowrap;
+ }
+ }
}
.txhash {
@@ -47,10 +47,6 @@
color: #aaa;
}
-.event td div {
- white-space: nowrap;
-}
-
.mined {
}
diff --git a/js/src/views/Contract/contract.js b/js/src/views/Contract/contract.js
index 8ae4fcb33..35ad95fe2 100644
--- a/js/src/views/Contract/contract.js
+++ b/js/src/views/Contract/contract.js
@@ -124,7 +124,7 @@ class Contract extends Component {
}
return (
-
+
{ this.renderActionbar(account) }
{ this.renderDeleteDialog(account) }
{ this.renderEditDialog(account) }
@@ -229,7 +229,7 @@ class Contract extends Component {
return (
+ buttons={ !account ? [] : buttons } />
);
}
diff --git a/js/src/views/Contracts/contracts.js b/js/src/views/Contracts/contracts.js
index d97d88b09..b84705b32 100644
--- a/js/src/views/Contracts/contracts.js
+++ b/js/src/views/Contracts/contracts.js
@@ -28,8 +28,6 @@ import { setVisibleAccounts } from '~/redux/providers/personalActions';
import List from '../Accounts/List';
-import styles from './contracts.css';
-
class Contracts extends Component {
static contextTypes = {
api: PropTypes.object.isRequired
@@ -80,7 +78,7 @@ class Contracts extends Component {
const { searchValues, sortOrder } = this.state;
return (
-
+
{ this.renderActionbar() }
{ this.renderAddContract() }
{ this.renderAddContract() }
@@ -159,7 +157,6 @@ class Contracts extends Component {
return (
);
diff --git a/js/src/views/Settings/settings.css b/js/src/views/Settings/settings.css
index 91138db68..1eab3aac3 100644
--- a/js/src/views/Settings/settings.css
+++ b/js/src/views/Settings/settings.css
@@ -15,9 +15,6 @@
/* along with Parity. If not, see
.
*/
-.layout {
-}
-
.menu {
display: inline-block;
}
@@ -35,31 +32,24 @@
padding: 16px 2em !important;
line-height: 24px !important;
width: auto !important;
-}
-.tabactive {
-}
+ &>div {
+ height: 24px !important;
-.tab>div,
-.tabactive>div {
- height: 24px !important;
-}
+ &>div {
+ display: inline-block !important;
+ }
+ }
-.tab>div>div,
-.tabactive>div>div {
- display: inline-block !important;
-}
+ svg {
+ margin-right: 0.5em;
+ margin-bottom: 0 !important;
+ }
-.tab svg,
-.tabactive svg {
- margin-right: 0.5em;
- margin-bottom: 0 !important;
-}
-
-.tab .menu,
-.tabactive .menu {
- vertical-align: top;
- display: inline-block;
+ .menu {
+ vertical-align: top;
+ display: inline-block;
+ }
}
.imageIcon {
@@ -68,6 +58,8 @@
opacity: 0.5;
}
-.tabactive .imageIcon {
- opacity: 1;
+.tabactive {
+ .imageIcon {
+ opacity: 1;
+ }
}
diff --git a/js/src/views/Settings/settings.js b/js/src/views/Settings/settings.js
index 54b4fa6dd..a6c455dad 100644
--- a/js/src/views/Settings/settings.js
+++ b/js/src/views/Settings/settings.js
@@ -45,7 +45,7 @@ export default class Settings extends Component {
}
return (
-
+
{ this.renderTab(hash, 'views', ) }
diff --git a/js/src/views/Signer/components/Account/AccountLink/AccountLink.js b/js/src/views/Signer/components/Account/AccountLink/AccountLink.js
index 97ff35ce9..f42675474 100644
--- a/js/src/views/Signer/components/Account/AccountLink/AccountLink.js
+++ b/js/src/views/Signer/components/Account/AccountLink/AccountLink.js
@@ -16,7 +16,7 @@
import React, { Component, PropTypes } from 'react';
-import { addressLink } from '../../../../../3rdparty/etherscan/links';
+import { addressLink } from '~/3rdparty/etherscan/links';
import styles from './AccountLink.css';
export default class AccountLink extends Component {
diff --git a/js/src/views/Signer/components/TxHashLink/TxHashLink.js b/js/src/views/Signer/components/TxHashLink/TxHashLink.js
index 5fbd5695e..bce30eded 100644
--- a/js/src/views/Signer/components/TxHashLink/TxHashLink.js
+++ b/js/src/views/Signer/components/TxHashLink/TxHashLink.js
@@ -16,7 +16,7 @@
import React, { Component, PropTypes } from 'react';
-import { txLink } from '../../../../3rdparty/etherscan/links';
+import { txLink } from '~/3rdparty/etherscan/links';
export default class TxHashLink extends Component {
diff --git a/js/src/views/Signer/containers/Embedded/embedded.css b/js/src/views/Signer/containers/Embedded/embedded.css
index 94e0f3933..ffc6a209f 100644
--- a/js/src/views/Signer/containers/Embedded/embedded.css
+++ b/js/src/views/Signer/containers/Embedded/embedded.css
@@ -23,9 +23,6 @@
width: $embedWidth;
}
-.pending {
-}
-
.none {
color: #aaa;
}
diff --git a/js/src/views/Signer/containers/Embedded/embedded.js b/js/src/views/Signer/containers/Embedded/embedded.js
index 57639ea11..4629a6522 100644
--- a/js/src/views/Signer/containers/Embedded/embedded.js
+++ b/js/src/views/Signer/containers/Embedded/embedded.js
@@ -71,7 +71,7 @@ class Embedded extends Component {
const items = pending.sort(this._sortRequests).map(this.renderPending);
return (
-
+
{ items }
);
diff --git a/js/src/views/Signer/containers/RequestsPage/RequestsPage.css b/js/src/views/Signer/containers/RequestsPage/RequestsPage.css
index 3701c3097..662c45817 100644
--- a/js/src/views/Signer/containers/RequestsPage/RequestsPage.css
+++ b/js/src/views/Signer/containers/RequestsPage/RequestsPage.css
@@ -15,12 +15,6 @@
/* along with Parity. If not, see
.
*/
-.request {
-}
-
.noRequestsMsg {
color: #aaa;
}
-
-.items {
-}
diff --git a/js/src/views/Signer/containers/RequestsPage/RequestsPage.js b/js/src/views/Signer/containers/RequestsPage/RequestsPage.js
index ecb2ccd43..f3cd5a267 100644
--- a/js/src/views/Signer/containers/RequestsPage/RequestsPage.js
+++ b/js/src/views/Signer/containers/RequestsPage/RequestsPage.js
@@ -98,9 +98,7 @@ class RequestsPage extends Component {
return (
-
- { items }
-
+ { items }
);
}
@@ -111,7 +109,6 @@ class RequestsPage extends Component {
return (
+
diff --git a/js/src/views/Status/components-compositors/Animated/AnimateChildren.css b/js/src/views/Status/components-compositors/Animated/AnimateChildren.css
deleted file mode 100644
index 5d7b5b39a..000000000
--- a/js/src/views/Status/components-compositors/Animated/AnimateChildren.css
+++ /dev/null
@@ -1,55 +0,0 @@
-/* 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
.
-*/
-/* todo [adgo] - make local */
-:global .transition-appear {
- opacity: 0.01;
-}
-
-:global .transition-appear.transition-appear-active {
- opacity: 1;
- transition: opacity .3s ease-in-out;
-}
-
-:global .transition-enter {
- opacity: 0.01;
-}
-
-:global .transition-enter.transition-enter-active {
- opacity: 1;
- transition: opacity .3s ease-in-out;
-}
-
-:global .transition-leave {
- opacity: 1;
-}
-
-:global .transition-leave.transition-leave-active {
- opacity: 0.01;
- transition: opacity .3s ease-in-out;
-}
-
-:global .absoluteAnimationContainer {
- position: relative;
-}
-
-:global .absoluteAnimationContainer > .transition-leave {
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- width: 100%;
-}
diff --git a/js/src/views/Status/components-compositors/Animated/Animated.js b/js/src/views/Status/components-compositors/Animated/Animated.js
deleted file mode 100644
index c22344c01..000000000
--- a/js/src/views/Status/components-compositors/Animated/Animated.js
+++ /dev/null
@@ -1,28 +0,0 @@
-// 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
.
-
-import React, { Component } from 'react';
-import AnimateChildren from './children';
-
-export default Wrapped => class Animated extends Component {
- render () {
- return (
-
-
-
- );
- }
-};
diff --git a/js/src/views/Status/components-compositors/Animated/children.js b/js/src/views/Status/components-compositors/Animated/children.js
deleted file mode 100644
index be7f910bf..000000000
--- a/js/src/views/Status/components-compositors/Animated/children.js
+++ /dev/null
@@ -1,63 +0,0 @@
-// 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
.
-
-import React, { Component, PropTypes } from 'react';
-import { isReactComponent } from '../../util/react';
-import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
-import './AnimateChildren.css';
-
-export default class AnimateChildren extends Component {
- render () {
- const className = this.props.absolute ? 'absoluteAnimationContainer' : '';
- return (
-
- { this.renderChildren() }
-
- );
- }
-
- renderChildren () {
- const { children, isView } = this.props;
-
- if (isView) {
- return React.cloneElement(this.props.children, {
- key: this.props.pathname
- });
- }
-
- if (isReactComponent(children)) {
- return React.cloneElement(this.props.children, { ...this.props });
- }
-
- return children;
- }
-
- static propTypes = {
- children: PropTypes.any.isRequired,
- pathname: PropTypes.string,
- isView: PropTypes.bool,
- absolute: PropTypes.bool
- }
-
-}
diff --git a/js/src/views/Status/components/Calls/Calls.js b/js/src/views/Status/components/Calls/Calls.js
index 7734104ec..1115ec632 100644
--- a/js/src/views/Status/components/Calls/Calls.js
+++ b/js/src/views/Status/components/Calls/Calls.js
@@ -15,7 +15,6 @@
// along with Parity. If not, see
.
import React, { Component, PropTypes } from 'react';
-import AnimateChildren from '../../components-compositors/Animated/children';
import Call from '../Call';
import CallsToolbar from '../CallsToolbar';
import styles from './Calls.css';
@@ -73,13 +72,11 @@ export default class Calls extends Component {
}
return (
-
-
-
- Fire up some calls and the results will be here.
-
-
-
+
+
+ Fire up some calls and the results will be here.
+
+
);
}
@@ -90,17 +87,13 @@ export default class Calls extends Component {
return;
}
- return (
-
- { calls.map((call, idx) => (
-
- )) }
-
- );
+ return calls.map((call, idx) => (
+
+ ));
}
clearActiveCall = () => {
diff --git a/js/src/views/Status/containers/StatusPage/StatusPage.js b/js/src/views/Status/containers/StatusPage/StatusPage.js
index 286e2c20e..c7ab56c5e 100644
--- a/js/src/views/Status/containers/StatusPage/StatusPage.js
+++ b/js/src/views/Status/containers/StatusPage/StatusPage.js
@@ -23,8 +23,6 @@ import { clearStatusLogs, toggleStatusLogs, toggleStatusRefresh } from '~/redux/
import Debug from '../../components/Debug';
import Status from '../../components/Status';
-import styles from './statusPage.css';
-
class StatusPage extends Component {
static propTypes = {
nodeStatus: PropTypes.object.isRequired,
@@ -41,7 +39,7 @@ class StatusPage extends Component {
render () {
return (
-
+
diff --git a/js/src/views/Status/containers/StatusPage/statusPage.css b/js/src/views/Status/containers/StatusPage/statusPage.css
deleted file mode 100644
index 850ee0856..000000000
--- a/js/src/views/Status/containers/StatusPage/statusPage.css
+++ /dev/null
@@ -1,18 +0,0 @@
-/* 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
.
-*/
-.body {
-}
diff --git a/js/src/views/Wallet/Confirmations/confirmations.js b/js/src/views/Wallet/Confirmations/confirmations.js
index 468e55136..7e7e74032 100644
--- a/js/src/views/Wallet/Confirmations/confirmations.js
+++ b/js/src/views/Wallet/Confirmations/confirmations.js
@@ -60,12 +60,14 @@ class WalletConfirmations extends Component {
}
renderConfirmations () {
const { confirmations, ...others } = this.props;
+ const realConfirmations = confirmations && confirmations
+ .filter((conf) => conf.confirmedBy.length > 0);
- if (!confirmations) {
+ if (!realConfirmations) {
return null;
}
- if (confirmations.length === 0) {
+ if (realConfirmations.length === 0) {
return (
No transactions needs confirmation right now.
@@ -73,13 +75,14 @@ class WalletConfirmations extends Component {
);
}
- return confirmations.map((confirmation) => (
-
- ));
+ return realConfirmations
+ .map((confirmation) => (
+
+ ));
}
}
@@ -320,22 +323,35 @@ class WalletConfirmation extends Component {
const { address, isTest } = this.props;
const { operation, transactionHash, blockNumber, value, to, data } = confirmation;
+ if (value && to && data) {
+ return (
+
+ );
+ }
+
return (
-
+ className={ className }
+ >
+
+ { operation }
+
+
);
}
diff --git a/js/src/views/Wallet/wallet.js b/js/src/views/Wallet/wallet.js
index 4aa3c8dd7..e67eaefdb 100644
--- a/js/src/views/Wallet/wallet.js
+++ b/js/src/views/Wallet/wallet.js
@@ -22,9 +22,10 @@ import moment from 'moment';
import ContentCreate from 'material-ui/svg-icons/content/create';
import ActionDelete from 'material-ui/svg-icons/action/delete';
import ContentSend from 'material-ui/svg-icons/content/send';
+import SettingsIcon from 'material-ui/svg-icons/action/settings';
import { nullableProptype } from '~/util/proptypes';
-import { EditMeta, Transfer } from '~/modals';
+import { EditMeta, Transfer, WalletSettings } from '~/modals';
import { Actionbar, Button, Page, Loading } from '~/ui';
import Delete from '../Address/Delete';
@@ -74,6 +75,7 @@ class Wallet extends Component {
state = {
showEditDialog: false,
+ showSettingsDialog: false,
showTransferDialog: false,
showDeleteDialog: false
};
@@ -115,6 +117,7 @@ class Wallet extends Component {
return (
{ this.renderEditDialog(wallet) }
+ { this.renderSettingsDialog() }
{ this.renderTransferDialog() }
{ this.renderDeleteDialog(wallet) }
{ this.renderActionbar() }
@@ -212,13 +215,18 @@ class Wallet extends Component {
}
- label='delete wallet'
+ label='delete'
onClick={ this.showDeleteDialog } />,
}
label='edit'
- onClick={ this.onEditClick } />
+ onClick={ this.onEditClick } />,
+
}
+ label='settings'
+ onClick={ this.onSettingsClick } />
];
return (
@@ -250,11 +258,27 @@ class Wallet extends Component {
return (
);
}
+ renderSettingsDialog () {
+ const { wallet } = this.props;
+ const { showSettingsDialog } = this.state;
+
+ if (!showSettingsDialog) {
+ return null;
+ }
+
+ return (
+
+ );
+ }
+
renderTransferDialog () {
const { showTransferDialog } = this.state;
@@ -281,6 +305,12 @@ class Wallet extends Component {
});
}
+ onSettingsClick = () => {
+ this.setState({
+ showSettingsDialog: !this.state.showSettingsDialog
+ });
+ }
+
onTransferClick = () => {
this.setState({
showTransferDialog: !this.state.showTransferDialog
diff --git a/js/webpack/shared.js b/js/webpack/shared.js
index 8887c41fb..8b6807b2a 100644
--- a/js/webpack/shared.js
+++ b/js/webpack/shared.js
@@ -23,6 +23,7 @@ const postcssImport = require('postcss-import');
const postcssNested = require('postcss-nested');
const postcssVars = require('postcss-simple-vars');
const rucksack = require('rucksack-css');
+const CircularDependencyPlugin = require('circular-dependency-plugin');
const ENV = process.env.NODE_ENV || 'development';
const isProd = ENV === 'production';
@@ -102,7 +103,12 @@ function getPlugins (_isProd = isProd) {
}
}),
- new webpack.optimize.OccurrenceOrderPlugin(!_isProd)
+ new webpack.optimize.OccurrenceOrderPlugin(!_isProd),
+
+ new CircularDependencyPlugin({
+ exclude: /node_modules/,
+ failOnError: true
+ })
];
if (_isProd) {
diff --git a/parity/cli/config.full.toml b/parity/cli/config.full.toml
index ffb0848ea..a35d4c389 100644
--- a/parity/cli/config.full.toml
+++ b/parity/cli/config.full.toml
@@ -61,6 +61,7 @@ pass = "test_pass"
[mining]
author = "0xdeadbeefcafe0000000000000000000000000001"
+engine_signer = "0xdeadbeefcafe0000000000000000000000000001"
force_sealing = true
reseal_on_txs = "all"
reseal_min_period = 4000
diff --git a/parity/cli/config.toml b/parity/cli/config.toml
index 74f3477f3..608999799 100644
--- a/parity/cli/config.toml
+++ b/parity/cli/config.toml
@@ -40,6 +40,7 @@ pass = "password"
[mining]
author = "0xdeadbeefcafe0000000000000000000000000001"
+engine_signer = "0xdeadbeefcafe0000000000000000000000000001"
force_sealing = true
reseal_on_txs = "all"
reseal_min_period = 4000
diff --git a/parity/cli/mod.rs b/parity/cli/mod.rs
index 4bb682d68..7cff24af9 100644
--- a/parity/cli/mod.rs
+++ b/parity/cli/mod.rs
@@ -181,6 +181,8 @@ usage! {
// -- Sealing/Mining Options
flag_author: Option
= None,
or |c: &Config| otry!(c.mining).author.clone().map(Some),
+ flag_engine_signer: Option = None,
+ or |c: &Config| otry!(c.mining).engine_signer.clone().map(Some),
flag_force_sealing: bool = false,
or |c: &Config| otry!(c.mining).force_sealing.clone(),
flag_reseal_on_txs: String = "own",
@@ -377,6 +379,7 @@ struct Dapps {
#[derive(Default, Debug, PartialEq, RustcDecodable)]
struct Mining {
author: Option,
+ engine_signer: Option,
force_sealing: Option,
reseal_on_txs: Option,
reseal_min_period: Option,
@@ -584,6 +587,7 @@ mod tests {
// -- Sealing/Mining Options
flag_author: Some("0xdeadbeefcafe0000000000000000000000000001".into()),
+ flag_engine_signer: Some("0xdeadbeefcafe0000000000000000000000000001".into()),
flag_force_sealing: true,
flag_reseal_on_txs: "all".into(),
flag_reseal_min_period: 4000u64,
@@ -758,6 +762,7 @@ mod tests {
}),
mining: Some(Mining {
author: Some("0xdeadbeefcafe0000000000000000000000000001".into()),
+ engine_signer: Some("0xdeadbeefcafe0000000000000000000000000001".into()),
force_sealing: Some(true),
reseal_on_txs: Some("all".into()),
reseal_min_period: Some(4000),
diff --git a/parity/cli/usage.txt b/parity/cli/usage.txt
index 3178cb30f..fb5279c6d 100644
--- a/parity/cli/usage.txt
+++ b/parity/cli/usage.txt
@@ -161,6 +161,10 @@ Sealing/Mining Options:
for sending block rewards from sealed blocks.
NOTE: MINING WILL NOT WORK WITHOUT THIS OPTION.
(default: {flag_author:?})
+ --engine-signer ADDRESS Specify the address which should be used to
+ sign consensus messages and issue blocks.
+ Relevant only to non-PoW chains.
+ (default: {flag_engine_signer:?})
--force-sealing Force the node to author new blocks as if it were
always sealing/mining.
(default: {flag_force_sealing})
diff --git a/parity/configuration.rs b/parity/configuration.rs
index 0ee3f8a44..4a766e4c6 100644
--- a/parity/configuration.rs
+++ b/parity/configuration.rs
@@ -308,6 +308,7 @@ impl Configuration {
gas_floor_target: try!(to_u256(&self.args.flag_gas_floor_target)),
gas_ceil_target: try!(to_u256(&self.args.flag_gas_cap)),
transactions_limit: self.args.flag_tx_queue_size,
+ engine_signer: try!(self.engine_signer()),
};
Ok(extras)
@@ -317,6 +318,10 @@ impl Configuration {
to_address(self.args.flag_etherbase.clone().or(self.args.flag_author.clone()))
}
+ fn engine_signer(&self) -> Result {
+ to_address(self.args.flag_engine_signer.clone())
+ }
+
fn format(&self) -> Result, String> {
match self.args.flag_format {
Some(ref f) => Ok(Some(try!(f.parse()))),
diff --git a/parity/helpers.rs b/parity/helpers.rs
index d748caa6a..8de7da9f4 100644
--- a/parity/helpers.rs
+++ b/parity/helpers.rs
@@ -302,14 +302,14 @@ pub fn password_prompt() -> Result {
/// Read a password from password file.
pub fn password_from_file(path: String) -> Result {
- let passwords = try!(passwords_from_files(vec![path]));
+ let passwords = try!(passwords_from_files(&[path]));
// use only first password from the file
passwords.get(0).map(String::to_owned)
.ok_or_else(|| "Password file seems to be empty.".to_owned())
}
/// Reads passwords from files. Treats each line as a separate password.
-pub fn passwords_from_files(files: Vec) -> Result, String> {
+pub fn passwords_from_files(files: &[String]) -> Result, String> {
let passwords = files.iter().map(|filename| {
let file = try!(File::open(filename).map_err(|_| format!("{} Unable to read password file. Ensure it exists and permissions are correct.", filename)));
let reader = BufReader::new(&file);
diff --git a/parity/params.rs b/parity/params.rs
index 3ce07e889..25ddc8814 100644
--- a/parity/params.rs
+++ b/parity/params.rs
@@ -204,6 +204,7 @@ pub struct MinerExtras {
pub gas_floor_target: U256,
pub gas_ceil_target: U256,
pub transactions_limit: usize,
+ pub engine_signer: Address,
}
impl Default for MinerExtras {
@@ -214,6 +215,7 @@ impl Default for MinerExtras {
gas_floor_target: U256::from(4_700_000),
gas_ceil_target: U256::from(6_283_184),
transactions_limit: 1024,
+ engine_signer: Default::default(),
}
}
}
diff --git a/parity/run.rs b/parity/run.rs
index dbcc6c716..6261ebda6 100644
--- a/parity/run.rs
+++ b/parity/run.rs
@@ -212,8 +212,13 @@ pub fn execute(cmd: RunCmd, logger: Arc) -> Result<(), String> {
sync_config.warp_sync = cmd.warp_sync;
sync_config.download_old_blocks = cmd.download_old_blocks;
+ let passwords = try!(passwords_from_files(&cmd.acc_conf.password_files));
+
// prepare account provider
- let account_provider = Arc::new(try!(prepare_account_provider(&cmd.dirs, cmd.acc_conf)));
+ let account_provider = Arc::new(try!(prepare_account_provider(&cmd.dirs, cmd.acc_conf, &passwords)));
+
+ // let the Engine access the accounts
+ spec.engine.register_account_provider(account_provider.clone());
// create miner
let miner = Miner::new(cmd.miner_options, cmd.gas_pricer.into(), &spec, Some(account_provider.clone()));
@@ -222,6 +227,12 @@ pub fn execute(cmd: RunCmd, logger: Arc) -> Result<(), String> {
miner.set_gas_ceil_target(cmd.miner_extras.gas_ceil_target);
miner.set_extra_data(cmd.miner_extras.extra_data);
miner.set_transactions_limit(cmd.miner_extras.transactions_limit);
+ let engine_signer = cmd.miner_extras.engine_signer;
+ if engine_signer != Default::default() {
+ if !passwords.into_iter().any(|p| miner.set_engine_signer(engine_signer, p).is_ok()) {
+ return Err(format!("No password found for the consensus signer {}. Make sure valid password is present in files passed using `--password`.", engine_signer));
+ }
+ }
// create client config
let mut client_config = to_client_config(
@@ -431,19 +442,17 @@ fn daemonize(_pid_file: String) -> Result<(), String> {
Err("daemon is no supported on windows".into())
}
-fn prepare_account_provider(dirs: &Directories, cfg: AccountsConfig) -> Result {
+fn prepare_account_provider(dirs: &Directories, cfg: AccountsConfig, passwords: &[String]) -> Result {
use ethcore::ethstore::EthStore;
use ethcore::ethstore::dir::DiskDirectory;
- let passwords = try!(passwords_from_files(cfg.password_files));
-
let dir = Box::new(try!(DiskDirectory::create(dirs.keys.clone()).map_err(|e| format!("Could not open keys directory: {}", e))));
let account_service = AccountProvider::new(Box::new(
try!(EthStore::open_with_iterations(dir, cfg.iterations).map_err(|e| format!("Could not open keys directory: {}", e)))
));
for a in cfg.unlocked_accounts {
- if passwords.iter().find(|p| account_service.unlock_account_permanently(a, (*p).clone()).is_ok()).is_none() {
+ if !passwords.iter().any(|p| account_service.unlock_account_permanently(a, (*p).clone()).is_ok()) {
return Err(format!("No password found to unlock account {}. Make sure valid password is present in files passed using `--password`.", a));
}
}
diff --git a/rpc/rpctest/Cargo.toml b/rpc/rpctest/Cargo.toml
index 37248ccc3..19c7feb7c 100644
--- a/rpc/rpctest/Cargo.toml
+++ b/rpc/rpctest/Cargo.toml
@@ -1,7 +1,7 @@
[package]
description = "Rpc test client."
name = "rpctest"
-version = "1.4.0"
+version = "1.5.0"
license = "GPL-3.0"
authors = ["Ethcore "]
diff --git a/rpc/src/v1/impls/parity_accounts.rs b/rpc/src/v1/impls/parity_accounts.rs
index 7bbfb1dd3..188e2290e 100644
--- a/rpc/src/v1/impls/parity_accounts.rs
+++ b/rpc/src/v1/impls/parity_accounts.rs
@@ -57,7 +57,7 @@ impl ParityAccounts for ParityAccountsClient where C: MiningBlock
let info = try!(store.accounts_info().map_err(|e| errors::account("Could not fetch account info.", e)));
let other = store.addresses_info().expect("addresses_info always returns Ok; qed");
- Ok(info.into_iter().chain(other.into_iter()).map(|(a, v)| {
+ Ok(other.into_iter().chain(info.into_iter()).map(|(a, v)| {
let m = map![
"name".to_owned() => to_value(&v.name),
"meta".to_owned() => to_value(&v.meta),
diff --git a/rpc/src/v1/impls/parity_set.rs b/rpc/src/v1/impls/parity_set.rs
index 47634d518..92de99a1f 100644
--- a/rpc/src/v1/impls/parity_set.rs
+++ b/rpc/src/v1/impls/parity_set.rs
@@ -116,6 +116,12 @@ impl ParitySet for ParitySetClient where
Ok(true)
}
+ fn set_engine_signer(&self, address: H160, password: String) -> Result {
+ try!(self.active());
+ try!(take_weak!(self.miner).set_engine_signer(address.into(), password).map_err(Into::into).map_err(errors::from_password_error));
+ Ok(true)
+ }
+
fn set_transactions_limit(&self, limit: usize) -> Result {
try!(self.active());
diff --git a/rpc/src/v1/tests/eth.rs b/rpc/src/v1/tests/eth.rs
index 4d04dec7c..428cae3e0 100644
--- a/rpc/src/v1/tests/eth.rs
+++ b/rpc/src/v1/tests/eth.rs
@@ -116,6 +116,7 @@ impl EthTester {
fn from_spec(spec: Spec) -> Self {
let dir = RandomTempPath::new();
let account_provider = account_provider();
+ spec.engine.register_account_provider(account_provider.clone());
let miner_service = miner_service(&spec, account_provider.clone());
let snapshot_service = snapshot_service();
diff --git a/rpc/src/v1/tests/helpers/miner_service.rs b/rpc/src/v1/tests/helpers/miner_service.rs
index ad55faa7b..39fba8406 100644
--- a/rpc/src/v1/tests/helpers/miner_service.rs
+++ b/rpc/src/v1/tests/helpers/miner_service.rs
@@ -25,6 +25,7 @@ use ethcore::header::BlockNumber;
use ethcore::transaction::SignedTransaction;
use ethcore::receipt::{Receipt, RichReceipt};
use ethcore::miner::{MinerService, MinerStatus, TransactionImportResult, LocalTransactionStatus};
+use ethcore::account_provider::Error as AccountError;
/// Test miner service.
pub struct TestMinerService {
@@ -40,6 +41,8 @@ pub struct TestMinerService {
pub pending_receipts: Mutex>,
/// Last nonces.
pub last_nonces: RwLock>,
+ /// Password held by Engine.
+ pub password: RwLock,
min_gas_price: RwLock,
gas_range_target: RwLock<(U256, U256)>,
@@ -61,6 +64,7 @@ impl Default for TestMinerService {
min_gas_price: RwLock::new(U256::from(20_000_000)),
gas_range_target: RwLock::new((U256::from(12345), U256::from(54321))),
author: RwLock::new(Address::zero()),
+ password: RwLock::new(String::new()),
extra_data: RwLock::new(vec![1, 2, 3, 4]),
limit: RwLock::new(1024),
tx_gas_limit: RwLock::new(!U256::zero()),
@@ -83,6 +87,12 @@ impl MinerService for TestMinerService {
*self.author.write() = author;
}
+ fn set_engine_signer(&self, address: Address, password: String) -> Result<(), AccountError> {
+ *self.author.write() = address;
+ *self.password.write() = password;
+ Ok(())
+ }
+
fn set_extra_data(&self, extra_data: Bytes) {
*self.extra_data.write() = extra_data;
}
diff --git a/rpc/src/v1/tests/mocked/parity_set.rs b/rpc/src/v1/tests/mocked/parity_set.rs
index 01f33e251..55f155693 100644
--- a/rpc/src/v1/tests/mocked/parity_set.rs
+++ b/rpc/src/v1/tests/mocked/parity_set.rs
@@ -106,6 +106,23 @@ fn rpc_parity_set_author() {
assert_eq!(miner.author(), Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap());
}
+#[test]
+fn rpc_parity_set_engine_signer() {
+ let miner = miner_service();
+ let client = client_service();
+ let network = network_service();
+ let io = IoHandler::new();
+ io.add_delegate(parity_set_client(&client, &miner, &network).to_delegate());
+
+ let request = r#"{"jsonrpc": "2.0", "method": "parity_setEngineSigner", "params":["0xcd1722f3947def4cf144679da39c4c32bdc35681", "password"], "id": 1}"#;
+ let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#;
+
+ assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
+ assert_eq!(miner.author(), Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap());
+ assert_eq!(*miner.password.read(), "password".to_string());
+}
+
+
#[test]
fn rpc_parity_set_transactions_limit() {
let miner = miner_service();
diff --git a/rpc/src/v1/traits/parity_set.rs b/rpc/src/v1/traits/parity_set.rs
index c83eff022..c40abd01f 100644
--- a/rpc/src/v1/traits/parity_set.rs
+++ b/rpc/src/v1/traits/parity_set.rs
@@ -44,6 +44,10 @@ build_rpc_trait! {
#[rpc(name = "parity_setAuthor")]
fn set_author(&self, H160) -> Result;
+ /// Sets account for signing consensus messages.
+ #[rpc(name = "parity_setEngineSigner")]
+ fn set_engine_signer(&self, H160, String) -> Result;
+
/// Sets the limits for transaction queue.
#[rpc(name = "parity_setTransactionsLimit")]
fn set_transactions_limit(&self, usize) -> Result;
diff --git a/stratum/Cargo.toml b/stratum/Cargo.toml
index 28f5208dd..609f4ee9b 100644
--- a/stratum/Cargo.toml
+++ b/stratum/Cargo.toml
@@ -1,7 +1,7 @@
[package]
description = "Ethcore stratum lib"
name = "ethcore-stratum"
-version = "1.4.0"
+version = "1.5.0"
license = "GPL-3.0"
authors = ["Ethcore "]
build = "build.rs"
diff --git a/test.sh b/test.sh
index 3e9074478..f5a636bab 100755
--- a/test.sh
+++ b/test.sh
@@ -2,7 +2,7 @@
# Running Parity Full Test Sute
FEATURES="json-tests"
-OPTIONS="--verbose --release"
+OPTIONS="--release"
case $1 in
--no-json)
diff --git a/util/io/src/service.rs b/util/io/src/service.rs
index 5d773f525..6086acadd 100644
--- a/util/io/src/service.rs
+++ b/util/io/src/service.rs
@@ -394,7 +394,7 @@ impl IoChannel where Message: Send + Clone + Sync + 'static {
/// 'Message' is a notification message type
pub struct IoService where Message: Send + Sync + Clone + 'static {
panic_handler: Arc,
- thread: Option>,
+ thread: Mutex>>,
host_channel: Mutex>>,
handlers: Arc>, HandlerId>>>,
}
@@ -424,12 +424,26 @@ impl IoService where Message: Send + Sync + Clone + 'static {
});
Ok(IoService {
panic_handler: panic_handler,
- thread: Some(thread),
+ thread: Mutex::new(Some(thread)),
host_channel: Mutex::new(channel),
handlers: handlers,
})
}
+ pub fn stop(&self) {
+ trace!(target: "shutdown", "[IoService] Closing...");
+ // Clear handlers so that shared pointers are not stuck on stack
+ // in Channel::send_sync
+ self.handlers.write().clear();
+ self.host_channel.lock().send(IoMessage::Shutdown).unwrap_or_else(|e| warn!("Error on IO service shutdown: {:?}", e));
+ if let Some(thread) = self.thread.lock().take() {
+ thread.join().unwrap_or_else(|e| {
+ debug!(target: "shutdown", "Error joining IO service event loop thread: {:?}", e);
+ });
+ }
+ trace!(target: "shutdown", "[IoService] Closed.");
+ }
+
/// Regiter an IO handler with the event loop.
pub fn register_handler(&self, handler: Arc+Send>) -> Result<(), IoError> {
try!(self.host_channel.lock().send(IoMessage::AddHandler {
@@ -452,17 +466,7 @@ impl IoService where Message: Send + Sync + Clone + 'static {
impl Drop for IoService where Message: Send + Sync + Clone {
fn drop(&mut self) {
- trace!(target: "shutdown", "[IoService] Closing...");
- // Clear handlers so that shared pointers are not stuck on stack
- // in Channel::send_sync
- self.handlers.write().clear();
- self.host_channel.lock().send(IoMessage::Shutdown).unwrap_or_else(|e| warn!("Error on IO service shutdown: {:?}", e));
- if let Some(thread) = self.thread.take() {
- thread.join().unwrap_or_else(|e| {
- debug!(target: "shutdown", "Error joining IO service event loop thread: {:?}", e);
- });
- }
- trace!(target: "shutdown", "[IoService] Closed.");
+ self.stop()
}
}