Merge remote-tracking branch 'parity/master'
This commit is contained in:
commit
37ca1fa497
@ -5,7 +5,7 @@ variables:
|
||||
GIT_DEPTH: "3"
|
||||
SIMPLECOV: "true"
|
||||
RUST_BACKTRACE: "1"
|
||||
RUSTFLAGS: "-D warnings"
|
||||
RUSTFLAGS: ""
|
||||
cache:
|
||||
key: "$CI_BUILD_NAME/$CI_BUILD_REF_NAME"
|
||||
untracked: true
|
||||
@ -316,7 +316,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% -Zorbit=off -D warnings
|
||||
- set RUSTFLAGS=%RUSTFLAGS% -Zorbit=off
|
||||
- rustup default stable-x86_64-pc-windows-msvc
|
||||
- cargo build --release --verbose
|
||||
- curl -sL --url "https://github.com/ethcore/win-build/raw/master/SimpleFC.dll" -o nsis\SimpleFC.dll
|
||||
@ -380,8 +380,8 @@ test-windows:
|
||||
before_script:
|
||||
- git submodule update --init --recursive
|
||||
script:
|
||||
- export RUST_BACKTRACE=1
|
||||
- ./test.sh --verbose
|
||||
- set RUST_BACKTRACE=1
|
||||
- PowerShell ./test.sh --verbose
|
||||
tags:
|
||||
- rust-windows
|
||||
dependencies:
|
||||
@ -391,9 +391,6 @@ js-release:
|
||||
image: ethcore/javascript:latest
|
||||
only:
|
||||
- master
|
||||
- beta
|
||||
- tags
|
||||
- stable
|
||||
before_script:
|
||||
- ./js/scripts/install-deps.sh
|
||||
script:
|
||||
|
50
Cargo.lock
generated
50
Cargo.lock
generated
@ -370,7 +370,7 @@ version = "1.4.0"
|
||||
dependencies = [
|
||||
"crossbeam 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mio 0.5.1 (git+https://github.com/ethcore/mio?branch=v0.5.x)",
|
||||
"mio 0.6.0 (git+https://github.com/carllerche/mio)",
|
||||
"parking_lot 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"slab 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@ -451,6 +451,7 @@ name = "ethcore-network"
|
||||
version = "1.4.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-io 1.4.0",
|
||||
"ethcore-util 1.4.0",
|
||||
@ -459,7 +460,7 @@ dependencies = [
|
||||
"igd 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mio 0.5.1 (git+https://github.com/ethcore/mio?branch=v0.5.x)",
|
||||
"mio 0.6.0 (git+https://github.com/carllerche/mio)",
|
||||
"parking_lot 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rlp 0.1.0",
|
||||
@ -516,7 +517,7 @@ dependencies = [
|
||||
"parity-ui 1.4.0",
|
||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ws 0.5.2 (git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable)",
|
||||
"ws 0.5.3 (git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -886,6 +887,11 @@ name = "lazy_static"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "lazycell"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.15"
|
||||
@ -990,7 +996,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.6.0-dev"
|
||||
source = "git+https://github.com/carllerche/mio?rev=62ec763c9cc34d8a452ed0392c575c50ddd5fc8d#62ec763c9cc34d8a452ed0392c575c50ddd5fc8d"
|
||||
source = "git+https://github.com/ethcore/mio?branch=timer-fix#31eccc40ece3d47abaefaf23bb2114033175b972"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -1002,6 +1008,22 @@ dependencies = [
|
||||
"winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.6.0"
|
||||
source = "git+https://github.com/carllerche/mio#9f17b70d6fecbf912168267ea74cf536f2cba705"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazycell 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nix 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miow"
|
||||
version = "0.1.3"
|
||||
@ -1213,7 +1235,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "parity-ui-precompiled"
|
||||
version = "1.4.0"
|
||||
source = "git+https://github.com/ethcore/js-precompiled.git#ba726039185238d6fd604f092b089a7d52c0f436"
|
||||
source = "git+https://github.com/ethcore/js-precompiled.git#eec3d41e6fd1a10e4d69470a9e8c2a7b1b464466"
|
||||
dependencies = [
|
||||
"parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@ -1593,6 +1615,11 @@ name = "slab"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "0.1.8"
|
||||
@ -1884,13 +1911,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "ws"
|
||||
version = "0.5.2"
|
||||
source = "git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable#00bd2134b07b4bc8ea47b7f6c7afce16bbe34c8f"
|
||||
version = "0.5.3"
|
||||
source = "git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable#0cd6c5e3e9d5e61a37d53eb8dcbad523dcc69314"
|
||||
dependencies = [
|
||||
"bytes 0.4.0-dev (git+https://github.com/carllerche/bytes)",
|
||||
"httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mio 0.6.0-dev (git+https://github.com/carllerche/mio?rev=62ec763c9cc34d8a452ed0392c575c50ddd5fc8d)",
|
||||
"mio 0.6.0-dev (git+https://github.com/ethcore/mio?branch=timer-fix)",
|
||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"slab 0.2.0 (git+https://github.com/carllerche/slab?rev=5476efcafb)",
|
||||
@ -1984,6 +2011,7 @@ dependencies = [
|
||||
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
||||
"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
|
||||
"checksum lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "49247ec2a285bb3dcb23cbd9c35193c025e7251bfce77c1d5da97e6362dffe7f"
|
||||
"checksum lazycell 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce12306c4739d86ee97c23139f3a34ddf0387bbf181bc7929d287025a8c3ef6b"
|
||||
"checksum libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "23e3757828fa702a20072c37ff47938e9dd331b92fac6e223d26d4b7a55f7ee2"
|
||||
"checksum linked-hash-map 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bda158e0dabeb97ee8a401f4d17e479d6b891a14de0bba79d5cc2d4d325b5e48"
|
||||
"checksum linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6d262045c5b87c0861b3f004610afd0e2c851e2908d08b6c870cbb9d5f494ecd"
|
||||
@ -1996,7 +2024,8 @@ dependencies = [
|
||||
"checksum miniz-sys 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "9d1f4d337a01c32e1f2122510fed46393d53ca35a7f429cb0450abaedfa3ed54"
|
||||
"checksum mio 0.5.1 (git+https://github.com/ethcore/mio?branch=v0.5.x)" = "<none>"
|
||||
"checksum mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a637d1ca14eacae06296a008fa7ad955347e34efcb5891cfd8ba05491a37907e"
|
||||
"checksum mio 0.6.0-dev (git+https://github.com/carllerche/mio?rev=62ec763c9cc34d8a452ed0392c575c50ddd5fc8d)" = "<none>"
|
||||
"checksum mio 0.6.0 (git+https://github.com/carllerche/mio)" = "<none>"
|
||||
"checksum mio 0.6.0-dev (git+https://github.com/ethcore/mio?branch=timer-fix)" = "<none>"
|
||||
"checksum miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d5bfc6782530ac8ace97af10a540054a37126b63b0702ddaaa243b73b5745b9a"
|
||||
"checksum msdos_time 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c04b68cc63a8480fb2550343695f7be72effdec953a9d4508161c3e69041c7d8"
|
||||
"checksum nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)" = "<none>"
|
||||
@ -2062,6 +2091,7 @@ dependencies = [
|
||||
"checksum slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d807fd58c4181bbabed77cb3b891ba9748241a552bcc5be698faaebefc54f46e"
|
||||
"checksum slab 0.2.0 (git+https://github.com/carllerche/slab?rev=5476efcafb)" = "<none>"
|
||||
"checksum slab 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6dbdd334bd28d328dad1c41b0ea662517883d8880d8533895ef96c8003dec9c4"
|
||||
"checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23"
|
||||
"checksum smallvec 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "fcc8d19212aacecf95e4a7a2179b26f7aeb9732a915cf01f05b0d3e044865410"
|
||||
"checksum solicit 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "172382bac9424588d7840732b250faeeef88942e37b6e35317dce98cafdd75b2"
|
||||
"checksum spmc 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "93bdab61c1a413e591c4d17388ffa859eaff2df27f1e13a5ec8b716700605adf"
|
||||
@ -2099,7 +2129,7 @@ dependencies = [
|
||||
"checksum webpki 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "813503a5985585e0812d430cd1328ee322f47f66629c8ed4ecab939cf9e92f91"
|
||||
"checksum winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4dfaaa8fbdaa618fa6914b59b2769d690dd7521920a18d84b42d254678dd5fd4"
|
||||
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
|
||||
"checksum ws 0.5.2 (git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable)" = "<none>"
|
||||
"checksum ws 0.5.3 (git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable)" = "<none>"
|
||||
"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
|
||||
"checksum xml-rs 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "65e74b96bd3179209dc70a980da6df843dff09e46eee103a0376c0949257e3ef"
|
||||
"checksum xmltree 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "472a9d37c7c53ab2391161df5b89b1f3bf76dab6ab150d7941ecbdd832282082"
|
||||
|
@ -139,7 +139,7 @@ pub struct Client {
|
||||
miner: Arc<Miner>,
|
||||
sleep_state: Mutex<SleepState>,
|
||||
liveness: AtomicBool,
|
||||
io_channel: IoChannel<ClientIoMessage>,
|
||||
io_channel: Mutex<IoChannel<ClientIoMessage>>,
|
||||
notify: RwLock<Vec<Weak<ChainNotify>>>,
|
||||
queue_transactions: AtomicUsize,
|
||||
last_hashes: RwLock<VecDeque<H256>>,
|
||||
@ -235,7 +235,7 @@ impl Client {
|
||||
import_lock: Mutex::new(()),
|
||||
panic_handler: panic_handler,
|
||||
miner: miner,
|
||||
io_channel: message_channel,
|
||||
io_channel: Mutex::new(message_channel),
|
||||
notify: RwLock::new(Vec::new()),
|
||||
queue_transactions: AtomicUsize::new(0),
|
||||
last_hashes: RwLock::new(VecDeque::new()),
|
||||
@ -1139,7 +1139,7 @@ impl BlockChainClient for Client {
|
||||
debug!("Ignoring {} transactions: queue is full", transactions.len());
|
||||
} else {
|
||||
let len = transactions.len();
|
||||
match self.io_channel.send(ClientIoMessage::NewTransactions(transactions)) {
|
||||
match self.io_channel.lock().send(ClientIoMessage::NewTransactions(transactions)) {
|
||||
Ok(_) => {
|
||||
self.queue_transactions.fetch_add(len, AtomicOrdering::SeqCst);
|
||||
}
|
||||
|
@ -130,7 +130,7 @@ impl BanningTransactionQueue {
|
||||
// Ban sender
|
||||
let sender_banned = self.ban_sender(sender);
|
||||
// Ban recipient and codehash
|
||||
let is_banned = sender_banned || match transaction.action {
|
||||
let recipient_or_code_banned = match transaction.action {
|
||||
Action::Call(recipient) => {
|
||||
self.ban_recipient(recipient)
|
||||
},
|
||||
@ -138,7 +138,7 @@ impl BanningTransactionQueue {
|
||||
self.ban_codehash(transaction.data.sha3())
|
||||
},
|
||||
};
|
||||
is_banned
|
||||
sender_banned || recipient_or_code_banned
|
||||
},
|
||||
None => false,
|
||||
}
|
||||
|
@ -110,6 +110,7 @@ impl PartialOrd for TransactionOrigin {
|
||||
}
|
||||
|
||||
impl Ord for TransactionOrigin {
|
||||
#[cfg_attr(feature="dev", allow(match_same_arms))]
|
||||
fn cmp(&self, other: &TransactionOrigin) -> Ordering {
|
||||
if *other == *self {
|
||||
return Ordering::Equal;
|
||||
|
@ -178,7 +178,7 @@ impl Account {
|
||||
CodeState::Hash => {
|
||||
let code_hash = try!(rlp.val_at(3));
|
||||
if let Some(code) = code_map.get(&code_hash) {
|
||||
acct_db.emplace(code_hash.clone(), DBValue::from_slice(&code));
|
||||
acct_db.emplace(code_hash.clone(), DBValue::from_slice(code));
|
||||
}
|
||||
|
||||
(code_hash, None)
|
||||
|
@ -213,7 +213,7 @@ pub struct Service {
|
||||
restoration: Mutex<Option<Restoration>>,
|
||||
snapshot_root: PathBuf,
|
||||
db_config: DatabaseConfig,
|
||||
io_channel: Channel,
|
||||
io_channel: Mutex<Channel>,
|
||||
pruning: Algorithm,
|
||||
status: Mutex<RestorationStatus>,
|
||||
reader: RwLock<Option<LooseReader>>,
|
||||
@ -233,7 +233,7 @@ impl Service {
|
||||
restoration: Mutex::new(None),
|
||||
snapshot_root: params.snapshot_root,
|
||||
db_config: params.db_config,
|
||||
io_channel: params.channel,
|
||||
io_channel: Mutex::new(params.channel),
|
||||
pruning: params.pruning,
|
||||
status: Mutex::new(RestorationStatus::Inactive),
|
||||
reader: RwLock::new(None),
|
||||
@ -567,7 +567,7 @@ impl SnapshotService for Service {
|
||||
}
|
||||
|
||||
fn begin_restore(&self, manifest: ManifestData) {
|
||||
if let Err(e) = self.io_channel.send(ClientIoMessage::BeginRestoration(manifest)) {
|
||||
if let Err(e) = self.io_channel.lock().send(ClientIoMessage::BeginRestoration(manifest)) {
|
||||
trace!("Error sending snapshot service message: {:?}", e);
|
||||
}
|
||||
}
|
||||
@ -578,13 +578,13 @@ impl SnapshotService for Service {
|
||||
}
|
||||
|
||||
fn restore_state_chunk(&self, hash: H256, chunk: Bytes) {
|
||||
if let Err(e) = self.io_channel.send(ClientIoMessage::FeedStateChunk(hash, chunk)) {
|
||||
if let Err(e) = self.io_channel.lock().send(ClientIoMessage::FeedStateChunk(hash, chunk)) {
|
||||
trace!("Error sending snapshot service message: {:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
fn restore_block_chunk(&self, hash: H256, chunk: Bytes) {
|
||||
if let Err(e) = self.io_channel.send(ClientIoMessage::FeedBlockChunk(hash, chunk)) {
|
||||
if let Err(e) = self.io_channel.lock().send(ClientIoMessage::FeedBlockChunk(hash, chunk)) {
|
||||
trace!("Error sending snapshot service message: {:?}", e);
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
//! Watcher for snapshot-related chain events.
|
||||
|
||||
use util::Mutex;
|
||||
use client::{BlockChainClient, Client, ChainNotify};
|
||||
use ids::BlockID;
|
||||
use service::ClientIoMessage;
|
||||
@ -55,7 +56,7 @@ trait Broadcast: Send + Sync {
|
||||
fn take_at(&self, num: Option<u64>);
|
||||
}
|
||||
|
||||
impl Broadcast for IoChannel<ClientIoMessage> {
|
||||
impl Broadcast for Mutex<IoChannel<ClientIoMessage>> {
|
||||
fn take_at(&self, num: Option<u64>) {
|
||||
let num = match num {
|
||||
Some(n) => n,
|
||||
@ -64,7 +65,7 @@ impl Broadcast for IoChannel<ClientIoMessage> {
|
||||
|
||||
trace!(target: "snapshot_watcher", "broadcast: {}", num);
|
||||
|
||||
if let Err(e) = self.send(ClientIoMessage::TakeSnapshot(num)) {
|
||||
if let Err(e) = self.lock().send(ClientIoMessage::TakeSnapshot(num)) {
|
||||
warn!("Snapshot watcher disconnected from IoService: {}", e);
|
||||
}
|
||||
}
|
||||
@ -91,7 +92,7 @@ impl Watcher {
|
||||
client: client,
|
||||
sync_status: sync_status,
|
||||
}),
|
||||
broadcast: Box::new(channel),
|
||||
broadcast: Box::new(Mutex::new(channel)),
|
||||
period: period,
|
||||
history: history,
|
||||
}
|
||||
|
@ -43,8 +43,6 @@ struct AccountCache {
|
||||
// When changing the type of the values here, be sure to update `mem_used` and
|
||||
// `new`.
|
||||
accounts: LruCache<Address, Option<Account>>,
|
||||
/// DB Code cache. Maps code hashes to shared bytes.
|
||||
code: MemoryLruCache<H256, Arc<Vec<u8>>>,
|
||||
/// Information on the modifications in recently committed blocks; specifically which addresses
|
||||
/// changed in which block. Ordered by block number.
|
||||
modifications: VecDeque<BlockChanges>,
|
||||
@ -95,6 +93,8 @@ pub struct StateDB {
|
||||
db: Box<JournalDB>,
|
||||
/// Shared canonical state cache.
|
||||
account_cache: Arc<Mutex<AccountCache>>,
|
||||
/// DB Code cache. Maps code hashes to shared bytes.
|
||||
code_cache: Arc<Mutex<MemoryLruCache<H256, Arc<Vec<u8>>>>>,
|
||||
/// Local dirty cache.
|
||||
local_cache: Vec<CacheQueueItem>,
|
||||
/// Shared account bloom. Does not handle chain reorganizations.
|
||||
@ -125,9 +125,9 @@ impl StateDB {
|
||||
db: db,
|
||||
account_cache: Arc::new(Mutex::new(AccountCache {
|
||||
accounts: LruCache::new(cache_items),
|
||||
code: MemoryLruCache::new(code_cache_size),
|
||||
modifications: VecDeque::new(),
|
||||
})),
|
||||
code_cache: Arc::new(Mutex::new(MemoryLruCache::new(code_cache_size))),
|
||||
local_cache: Vec::new(),
|
||||
account_bloom: Arc::new(Mutex::new(bloom)),
|
||||
cache_size: cache_size,
|
||||
@ -320,6 +320,7 @@ impl StateDB {
|
||||
StateDB {
|
||||
db: self.db.boxed_clone(),
|
||||
account_cache: self.account_cache.clone(),
|
||||
code_cache: self.code_cache.clone(),
|
||||
local_cache: Vec::new(),
|
||||
account_bloom: self.account_bloom.clone(),
|
||||
cache_size: self.cache_size,
|
||||
@ -334,6 +335,7 @@ impl StateDB {
|
||||
StateDB {
|
||||
db: self.db.boxed_clone(),
|
||||
account_cache: self.account_cache.clone(),
|
||||
code_cache: self.code_cache.clone(),
|
||||
local_cache: Vec::new(),
|
||||
account_bloom: self.account_bloom.clone(),
|
||||
cache_size: self.cache_size,
|
||||
@ -352,10 +354,9 @@ impl StateDB {
|
||||
pub fn mem_used(&self) -> usize {
|
||||
// TODO: account for LRU-cache overhead; this is a close approximation.
|
||||
self.db.mem_used() + {
|
||||
let cache = self.account_cache.lock();
|
||||
|
||||
cache.code.current_size() +
|
||||
cache.accounts.len() * ::std::mem::size_of::<Option<Account>>()
|
||||
let accounts = self.account_cache.lock().accounts.len();
|
||||
let code_size = self.code_cache.lock().current_size();
|
||||
code_size + accounts * ::std::mem::size_of::<Option<Account>>()
|
||||
}
|
||||
}
|
||||
|
||||
@ -380,9 +381,9 @@ impl StateDB {
|
||||
/// it simply maps hashes to raw code and will always be correct in the absence of
|
||||
/// hash collisions.
|
||||
pub fn cache_code(&self, hash: H256, code: Arc<Vec<u8>>) {
|
||||
let mut cache = self.account_cache.lock();
|
||||
let mut cache = self.code_cache.lock();
|
||||
|
||||
cache.code.insert(hash, code);
|
||||
cache.insert(hash, code);
|
||||
}
|
||||
|
||||
/// Get basic copy of the cached account. Does not include storage.
|
||||
@ -397,9 +398,9 @@ impl StateDB {
|
||||
|
||||
/// Get cached code based on hash.
|
||||
pub fn get_cached_code(&self, hash: &H256) -> Option<Arc<Vec<u8>>> {
|
||||
let mut cache = self.account_cache.lock();
|
||||
let mut cache = self.code_cache.lock();
|
||||
|
||||
cache.code.get_mut(hash).map(|code| code.clone())
|
||||
cache.get_mut(hash).map(|code| code.clone())
|
||||
}
|
||||
|
||||
/// Get value from a cached account.
|
||||
|
@ -109,7 +109,7 @@ pub struct VerificationQueue<K: Kind> {
|
||||
struct QueueSignal {
|
||||
deleting: Arc<AtomicBool>,
|
||||
signalled: AtomicBool,
|
||||
message_channel: IoChannel<ClientIoMessage>,
|
||||
message_channel: Mutex<IoChannel<ClientIoMessage>>,
|
||||
}
|
||||
|
||||
impl QueueSignal {
|
||||
@ -121,7 +121,8 @@ impl QueueSignal {
|
||||
}
|
||||
|
||||
if self.signalled.compare_and_swap(false, true, AtomicOrdering::Relaxed) == false {
|
||||
if let Err(e) = self.message_channel.send_sync(ClientIoMessage::BlockVerified) {
|
||||
let channel = self.message_channel.lock().clone();
|
||||
if let Err(e) = channel.send_sync(ClientIoMessage::BlockVerified) {
|
||||
debug!("Error sending BlockVerified message: {:?}", e);
|
||||
}
|
||||
}
|
||||
@ -135,7 +136,8 @@ impl QueueSignal {
|
||||
}
|
||||
|
||||
if self.signalled.compare_and_swap(false, true, AtomicOrdering::Relaxed) == false {
|
||||
if let Err(e) = self.message_channel.send(ClientIoMessage::BlockVerified) {
|
||||
let channel = self.message_channel.lock().clone();
|
||||
if let Err(e) = channel.send(ClientIoMessage::BlockVerified) {
|
||||
debug!("Error sending BlockVerified message: {:?}", e);
|
||||
}
|
||||
}
|
||||
@ -178,7 +180,7 @@ impl<K: Kind> VerificationQueue<K> {
|
||||
let ready_signal = Arc::new(QueueSignal {
|
||||
deleting: deleting.clone(),
|
||||
signalled: AtomicBool::new(false),
|
||||
message_channel: message_channel
|
||||
message_channel: Mutex::new(message_channel),
|
||||
});
|
||||
let empty = Arc::new(SCondvar::new());
|
||||
let panic_handler = PanicHandler::new_in_arc();
|
||||
|
1
js/.gitignore
vendored
1
js/.gitignore
vendored
@ -5,3 +5,4 @@ build
|
||||
.coverage
|
||||
.dist
|
||||
.happypack
|
||||
.npmjs
|
||||
|
BIN
js/assets/images/ethcore-block-black.png
Normal file
BIN
js/assets/images/ethcore-block-black.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
BIN
js/assets/images/parity.ico
Normal file
BIN
js/assets/images/parity.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
@ -1,9 +1,9 @@
|
||||
{
|
||||
"name": "parity.js",
|
||||
"version": "0.0.1",
|
||||
"version": "0.1.5",
|
||||
"main": "release/index.js",
|
||||
"jsnext:main": "src/index.js",
|
||||
"author": "Ethcore Team <admin@ethcore.io>",
|
||||
"author": "Parity Team <admin@parity.io>",
|
||||
"maintainers": [
|
||||
"Jaco Greeff"
|
||||
],
|
||||
@ -11,7 +11,7 @@
|
||||
"license": "GPL-3.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/ethcore/parity.js.git"
|
||||
"url": "git+https://github.com/ethcore/parity.git"
|
||||
},
|
||||
"keywords": [
|
||||
"Ethereum",
|
||||
@ -27,15 +27,13 @@
|
||||
"build:app": "webpack --progress",
|
||||
"build:lib": "webpack --config webpack.libraries --progress",
|
||||
"build:dll": "webpack --config webpack.vendor --progress",
|
||||
|
||||
"ci:build": "npm run ci:build:dll && npm run ci:build:app && npm run ci:build:lib",
|
||||
"ci:build:app": "NODE_ENV=production webpack",
|
||||
"ci:build:lib": "NODE_ENV=production webpack --config webpack.libraries",
|
||||
"ci:build:dll": "NODE_ENV=production webpack --config webpack.vendor",
|
||||
|
||||
"ci:build:npm": "NODE_ENV=production webpack --config webpack.npm",
|
||||
"start": "npm install && npm run build:dll && npm run start:app",
|
||||
"start:app": "webpack-dev-server -d --history-api-fallback --open --hot --inline --progress --colors --port 3000",
|
||||
|
||||
"clean": "rm -rf ./build ./coverage",
|
||||
"coveralls": "npm run testCoverage && coveralls < coverage/lcov.info",
|
||||
"lint": "eslint --ignore-path .gitignore ./src/",
|
||||
@ -65,7 +63,7 @@
|
||||
"chai": "^3.5.0",
|
||||
"chai-enzyme": "0.4.2",
|
||||
"cheerio": "0.20.0",
|
||||
"copy-webpack-plugin": "^3.0.1",
|
||||
"copy-webpack-plugin": "^4.0.0",
|
||||
"core-js": "^2.4.1",
|
||||
"coveralls": "^2.11.11",
|
||||
"css-loader": "^0.23.1",
|
||||
@ -79,6 +77,7 @@
|
||||
"eslint-plugin-standard": "^2.0.0",
|
||||
"extract-text-webpack-plugin": "^1.0.1",
|
||||
"file-loader": "^0.8.5",
|
||||
"fs-extra": "^0.30.0",
|
||||
"happypack": "^2.2.1",
|
||||
"history": "^2.0.0",
|
||||
"html-loader": "^0.4.3",
|
||||
@ -125,6 +124,7 @@
|
||||
"material-ui": "^0.16.1",
|
||||
"material-ui-chip-input": "^0.8.0",
|
||||
"moment": "^2.14.1",
|
||||
"qs": "^6.3.0",
|
||||
"react": "^15.2.1",
|
||||
"react-addons-css-transition-group": "^15.2.1",
|
||||
"react-dom": "^15.2.1",
|
||||
|
81
js/parity.md
Normal file
81
js/parity.md
Normal file
@ -0,0 +1,81 @@
|
||||
# parity.js
|
||||
|
||||
Parity.js is a thin, fast, Promise-based wrapper around the Ethereum APIs.
|
||||
|
||||
## installation
|
||||
|
||||
Install the package with `npm install --save @parity/parity.js`
|
||||
|
||||
## usage
|
||||
|
||||
### initialisation
|
||||
|
||||
```javascript
|
||||
// import the actual Api class
|
||||
import { Api } from '@parity/parity.js';
|
||||
|
||||
// do the setup
|
||||
const transport = new Api.Transport.Http('http://localhost:8545');
|
||||
const api = new Api(transport);
|
||||
```
|
||||
|
||||
### making calls
|
||||
|
||||
perform a call
|
||||
|
||||
```javascript
|
||||
api.eth
|
||||
.coinbase()
|
||||
.then((coinbase) => {
|
||||
console.log(`The coinbase is ${coinbase}`);
|
||||
});
|
||||
```
|
||||
|
||||
multiple promises
|
||||
|
||||
```javascript
|
||||
Promise
|
||||
.all([
|
||||
api.eth.coinbase(),
|
||||
api.net.listening()
|
||||
])
|
||||
.then(([coinbase, listening]) => {
|
||||
// do stuff here
|
||||
});
|
||||
```
|
||||
|
||||
chaining promises
|
||||
|
||||
```javascript
|
||||
api.eth
|
||||
.newFilter({...})
|
||||
.then((filterId) => api.eth.getFilterChanges(filterId))
|
||||
.then((changes) => {
|
||||
console.log(changes);
|
||||
});
|
||||
```
|
||||
|
||||
### contracts
|
||||
|
||||
attach contract
|
||||
|
||||
```javascript
|
||||
const abi = [{ name: 'callMe', inputs: [{ type: 'bool', ...}, { type: 'string', ...}]}, ...abi...];
|
||||
const address = '0x123456...9abc';
|
||||
const contract = new api.newContract(abi, address);
|
||||
```
|
||||
|
||||
find & call a function
|
||||
|
||||
```javascript
|
||||
contract.instance
|
||||
.callMe
|
||||
.call({ gas: 21000 }, [true, 'someString']) // or estimateGas or postTransaction
|
||||
.then((result) => {
|
||||
console.log(`the result was ${result}`);
|
||||
});
|
||||
```
|
||||
|
||||
## apis
|
||||
|
||||
APIs implement the calls as exposed in the [Ethcore JSON Ethereum RPC](https://github.com/ethcore/ethereum-rpc-json/) definitions. Mapping follows the naming conventions of the originals, i.e. `eth_call` becomes `eth.call`, `personal_accounts` becomes `personal.accounts`, etc.
|
32
js/parity.package.json
Normal file
32
js/parity.package.json
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "@parity/parity.js",
|
||||
"description": "The Parity Promise-base API & ABI library for interfacing with Ethereum over RPC",
|
||||
"version": "0.0.0",
|
||||
"main": "library.js",
|
||||
"author": "Parity Team <admin@parity.io>",
|
||||
"maintainers": [
|
||||
"Jaco Greeff"
|
||||
],
|
||||
"contributors": [],
|
||||
"license": "GPL-3.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/ethcore/parity.git"
|
||||
},
|
||||
"keywords": [
|
||||
"Ethereum",
|
||||
"ABI",
|
||||
"API",
|
||||
"RPC",
|
||||
"Parity",
|
||||
"Promise"
|
||||
],
|
||||
"scripts": {
|
||||
},
|
||||
"devDependencies": {
|
||||
},
|
||||
"dependencies": {
|
||||
"bignumber.js": "^2.3.0",
|
||||
"js-sha3": "^0.5.2"
|
||||
}
|
||||
}
|
@ -1,11 +1,18 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# variables
|
||||
UTCDATE=`date -u "+%Y%m%d-%H%M%S"`
|
||||
PACKAGES=( "parity.js" )
|
||||
BRANCH=$CI_BUILD_REF_NAME
|
||||
GIT_JS_PRECOMPILED="https://${GITHUB_JS_PRECOMPILED}:@github.com/ethcore/js-precompiled.git"
|
||||
GIT_PARITY="https://${GITHUB_JS_PRECOMPILED}:@github.com/ethcore/parity.git"
|
||||
|
||||
# setup the git user defaults for the current repo
|
||||
function setup_git_user {
|
||||
git config push.default simple
|
||||
git config merge.ours.driver true
|
||||
git config user.email "jaco+gitlab@ethcore.io"
|
||||
git config user.email "$GITHUB_EMAIL"
|
||||
git config user.name "GitLab Build Bot"
|
||||
}
|
||||
|
||||
@ -15,47 +22,63 @@ GITLOG=./.git/gitcommand.log
|
||||
pushd $BASEDIR
|
||||
cd ../.dist
|
||||
|
||||
# variables
|
||||
UTCDATE=`date -u "+%Y%m%d-%H%M%S"`
|
||||
|
||||
# init git
|
||||
# add local files and send it up
|
||||
echo "*** Setting up GitHub config for js-precompiled"
|
||||
rm -rf ./.git
|
||||
git init
|
||||
|
||||
# add local files and send it up
|
||||
echo "Setting up GitHub config for js-precompiled"
|
||||
setup_git_user
|
||||
|
||||
echo "Checking out $CI_BUILD_REF_NAME branch"
|
||||
git remote add origin https://${GITHUB_JS_PRECOMPILED}:@github.com/ethcore/js-precompiled.git
|
||||
echo "*** Checking out $BRANCH branch"
|
||||
git remote add origin $GIT_JS_PRECOMPILED
|
||||
git fetch origin 2>$GITLOG
|
||||
git checkout -b $CI_BUILD_REF_NAME
|
||||
git checkout -b $BRANCH
|
||||
|
||||
echo "Committing compiled files for $UTCDATE"
|
||||
echo "*** Committing compiled files for $UTCDATE"
|
||||
git add .
|
||||
git commit -m "$UTCDATE"
|
||||
|
||||
echo "Merging remote"
|
||||
git merge origin/$CI_BUILD_REF_NAME -X ours --commit -m "$UTCDATE [release]"
|
||||
git push origin HEAD:refs/heads/$CI_BUILD_REF_NAME 2>$GITLOG
|
||||
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`
|
||||
|
||||
# back to root
|
||||
popd
|
||||
# move to root
|
||||
cd ../..
|
||||
|
||||
echo "Setting up GitHub config for parity"
|
||||
echo "*** Setting up GitHub config for parity"
|
||||
setup_git_user
|
||||
git remote set-url origin https://${GITHUB_JS_PRECOMPILED}:@github.com/ethcore/parity.git
|
||||
git reset --hard origin/$CI_BUILD_REF_NAME 2>$GITLOG
|
||||
git remote set-url origin $GIT_PARITY
|
||||
git reset --hard origin/$BRANCH 2>$GITLOG
|
||||
|
||||
echo "Updating cargo package parity-ui-precompiled#$PRECOMPILED_HASH"
|
||||
echo "*** Bumping package.json patch version"
|
||||
cd js
|
||||
npm --no-git-tag-version version
|
||||
npm version patch
|
||||
cd ..
|
||||
|
||||
echo "*** Updating cargo parity-ui-precompiled#$PRECOMPILED_HASH"
|
||||
cargo update -p parity-ui-precompiled
|
||||
# --precise "$PRECOMPILED_HASH"
|
||||
|
||||
echo "Committing updated files"
|
||||
git add .
|
||||
echo "*** Committing updated files"
|
||||
git add Cargo.lock js/package.json
|
||||
git commit -m "[ci skip] js-precompiled $UTCDATE"
|
||||
git push origin HEAD:refs/heads/$CI_BUILD_REF_NAME 2>$GITLOG
|
||||
git push origin HEAD:refs/heads/$BRANCH 2>$GITLOG
|
||||
|
||||
echo "*** Building packages for npmjs"
|
||||
cd js
|
||||
# echo -e "$NPM_USERNAME\n$NPM_PASSWORD\n$NPM_EMAIL" | npm login
|
||||
echo "$NPM_TOKEN" >> ~/.npmrc
|
||||
npm run ci:build:npm
|
||||
|
||||
echo "*** Publishing $PACKAGE to npmjs"
|
||||
cd .npmjs
|
||||
npm publish --access public
|
||||
cd ..
|
||||
|
||||
# back to root
|
||||
echo "*** Release completed"
|
||||
popd
|
||||
|
||||
# exit with exit code
|
||||
exit 0
|
||||
|
17
js/src/3rdparty/etherscan/call.js
vendored
17
js/src/3rdparty/etherscan/call.js
vendored
@ -14,6 +14,8 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { stringify } from 'qs';
|
||||
|
||||
const options = {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
@ -23,19 +25,14 @@ const options = {
|
||||
|
||||
export function call (module, action, _params, test) {
|
||||
const host = test ? 'testnet.etherscan.io' : 'api.etherscan.io';
|
||||
let params = '';
|
||||
|
||||
if (_params) {
|
||||
Object.keys(_params).map((param) => {
|
||||
const value = _params[param];
|
||||
const query = stringify(Object.assign({
|
||||
module, action
|
||||
}, _params || {}));
|
||||
|
||||
params = `${params}&${param}=${value}`;
|
||||
});
|
||||
}
|
||||
|
||||
return fetch(`http://${host}/api?module=${module}&action=${action}${params}`, options)
|
||||
return fetch(`https://${host}/api?${query}`, options)
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
if (!response.ok) {
|
||||
throw { code: response.status, message: response.statusText }; // eslint-disable-line
|
||||
}
|
||||
|
||||
|
5
js/src/3rdparty/etherscan/index.js
vendored
5
js/src/3rdparty/etherscan/index.js
vendored
@ -16,10 +16,13 @@
|
||||
|
||||
import { account } from './account';
|
||||
import { stats } from './stats';
|
||||
import { txLink, addressLink } from './links';
|
||||
|
||||
const etherscan = {
|
||||
account: account,
|
||||
stats: stats
|
||||
stats: stats,
|
||||
txLink: txLink,
|
||||
addressLink: addressLink
|
||||
};
|
||||
|
||||
export default etherscan;
|
||||
|
@ -14,8 +14,10 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// links to chain explorers
|
||||
export const BASE_LINK_ACCOUNT_MORDEN = 'https://testnet.etherscan.io/address/';
|
||||
export const BASE_LINK_ACCOUNT_HOMESTEAD = 'https://etherscan.io/address/';
|
||||
export const BASE_LINK_TX_MORDEN = 'https://testnet.etherscan.io/tx/';
|
||||
export const BASE_LINK_TX_HOMESTEAD = 'https://etherscan.io/tx/';
|
||||
export const txLink = (hash, isTestnet = false) => {
|
||||
return `https://${isTestnet ? 'testnet.' : ''}etherscan.io/tx/${hash}`;
|
||||
};
|
||||
|
||||
export const addressLink = (address, isTestnet = false) => {
|
||||
return `https://${isTestnet ? 'testnet.' : ''}etherscan.io/address/${address}`;
|
||||
};
|
@ -4,6 +4,7 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<link rel="icon" href="parity.ico" type="image/x-icon">
|
||||
<title>Basic Token Deployment</title>
|
||||
</head>
|
||||
<body>
|
||||
|
@ -4,6 +4,7 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<link rel="icon" href="parity.ico" type="image/x-icon">
|
||||
<title>GAVcoin</title>
|
||||
</head>
|
||||
<body>
|
||||
|
@ -43,7 +43,7 @@ export default class ActionBuyIn extends Component {
|
||||
accountError: ERRORS.invalidAccount,
|
||||
amount: 0,
|
||||
amountError: ERRORS.invalidAmount,
|
||||
maxPrice: api.util.fromWei(this.props.price.mul(1.2)).toString(),
|
||||
maxPrice: api.util.fromWei(this.props.price.mul(1.2)).toFixed(0),
|
||||
maxPriceError: null,
|
||||
sending: false,
|
||||
complete: false
|
||||
|
@ -53,6 +53,7 @@ export default class Application extends Component {
|
||||
action: null,
|
||||
address: null,
|
||||
accounts: [],
|
||||
accountsInfo: {},
|
||||
blockNumber: new BigNumber(-1),
|
||||
ethBalance: new BigNumber(0),
|
||||
gavBalance: new BigNumber(0),
|
||||
@ -68,7 +69,7 @@ export default class Application extends Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
const { accounts, address, blockNumber, gavBalance, loading, price, remaining, totalSupply } = this.state;
|
||||
const { accounts, accountsInfo, address, blockNumber, gavBalance, loading, price, remaining, totalSupply } = this.state;
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
@ -93,7 +94,7 @@ export default class Application extends Component {
|
||||
gavBalance={ gavBalance }
|
||||
onAction={ this.onAction } />
|
||||
<Events
|
||||
accounts={ accounts } />
|
||||
accountsInfo={ accountsInfo } />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -216,8 +217,8 @@ export default class Application extends Component {
|
||||
api.personal.accountsInfo()
|
||||
]);
|
||||
})
|
||||
.then(([address, addresses, infos]) => {
|
||||
infos = infos || {};
|
||||
.then(([address, addresses, accountsInfo]) => {
|
||||
accountsInfo = accountsInfo || {};
|
||||
console.log(`gavcoin was found at ${address}`);
|
||||
|
||||
const contract = api.newContract(abis.gavcoin, address);
|
||||
@ -226,9 +227,10 @@ export default class Application extends Component {
|
||||
loading: false,
|
||||
address,
|
||||
contract,
|
||||
accountsInfo,
|
||||
instance: contract.instance,
|
||||
accounts: addresses.map((address) => {
|
||||
const info = infos[address] || {};
|
||||
const info = accountsInfo[address] || {};
|
||||
|
||||
return {
|
||||
address,
|
||||
|
@ -14,10 +14,11 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import moment from 'moment';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
import IdentityIcon from '../../IdentityIcon';
|
||||
import { formatBlockNumber, formatCoins, formatEth } from '../../format';
|
||||
import { formatCoins, formatEth, formatHash } from '../../format';
|
||||
|
||||
import styles from '../events.css';
|
||||
|
||||
@ -27,7 +28,8 @@ const EMPTY_COLUMN = (
|
||||
|
||||
export default class Event extends Component {
|
||||
static contextTypes = {
|
||||
accounts: PropTypes.array.isRequired
|
||||
accountsInfo: PropTypes.object.isRequired,
|
||||
api: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
@ -38,14 +40,23 @@ export default class Event extends Component {
|
||||
toAddress: PropTypes.string
|
||||
}
|
||||
|
||||
state = {
|
||||
block: null
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
this.loadBlock();
|
||||
}
|
||||
|
||||
render () {
|
||||
const { event, fromAddress, toAddress, price, value } = this.props;
|
||||
const { blockNumber, state, type } = event;
|
||||
const { block } = this.state;
|
||||
const { state, type } = event;
|
||||
const cls = `${styles.event} ${styles[state]} ${styles[type.toLowerCase()]}`;
|
||||
|
||||
return (
|
||||
<tr className={ cls }>
|
||||
{ this.renderBlockNumber(blockNumber) }
|
||||
{ this.renderTimestamp(block) }
|
||||
{ this.renderType(type) }
|
||||
{ this.renderValue(value) }
|
||||
{ this.renderPrice(price) }
|
||||
@ -55,10 +66,10 @@ export default class Event extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
renderBlockNumber (blockNumber) {
|
||||
renderTimestamp (block) {
|
||||
return (
|
||||
<td className={ styles.blocknumber }>
|
||||
{ formatBlockNumber(blockNumber) }
|
||||
{ !block ? ' ' : moment(block.timestamp).fromNow() }
|
||||
</td>
|
||||
);
|
||||
}
|
||||
@ -77,8 +88,8 @@ export default class Event extends Component {
|
||||
}
|
||||
|
||||
renderAddressName (address) {
|
||||
const { accounts } = this.context;
|
||||
const account = accounts.find((_account) => _account.address === address);
|
||||
const { accountsInfo } = this.context;
|
||||
const account = accountsInfo[address];
|
||||
|
||||
if (account && account.name) {
|
||||
return (
|
||||
@ -90,7 +101,7 @@ export default class Event extends Component {
|
||||
|
||||
return (
|
||||
<div className={ styles.address }>
|
||||
{ address }
|
||||
{ formatHash(address) }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -126,4 +137,19 @@ export default class Event extends Component {
|
||||
</td>
|
||||
);
|
||||
}
|
||||
|
||||
loadBlock () {
|
||||
const { api } = this.context;
|
||||
const { event } = this.props;
|
||||
|
||||
if (!event || !event.blockNumber || event.blockNumber.eq(0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
api.eth
|
||||
.getBlockByNumber(event.blockNumber)
|
||||
.then((block) => {
|
||||
this.setState({ block });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -16,18 +16,20 @@
|
||||
*/
|
||||
.events {
|
||||
padding: 4em 2em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.list {
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
border: none;
|
||||
border-spacing: 0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.list td {
|
||||
vertical-align: top;
|
||||
padding: 4px 0.5em;
|
||||
max-height: 32px;
|
||||
padding: 0.25em 1em;
|
||||
max-height: 1.5em;
|
||||
}
|
||||
|
||||
.event {
|
||||
@ -38,7 +40,6 @@
|
||||
.blocknumber,
|
||||
.ethvalue,
|
||||
.gavvalue {
|
||||
font-family: 'Roboto Mono', monospace;
|
||||
}
|
||||
|
||||
.blocknumber,
|
||||
|
@ -27,7 +27,7 @@ import styles from './events.css';
|
||||
|
||||
export default class Events extends Component {
|
||||
static childContextTypes = {
|
||||
accounts: PropTypes.array
|
||||
accountsInfo: PropTypes.object
|
||||
}
|
||||
|
||||
static contextTypes = {
|
||||
@ -36,7 +36,7 @@ export default class Events extends Component {
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
accounts: PropTypes.array
|
||||
accountsInfo: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
state = {
|
||||
@ -84,11 +84,9 @@ export default class Events extends Component {
|
||||
}
|
||||
|
||||
getChildContext () {
|
||||
const { accounts } = this.props;
|
||||
const { accountsInfo } = this.props;
|
||||
|
||||
return {
|
||||
accounts
|
||||
};
|
||||
return { accountsInfo };
|
||||
}
|
||||
|
||||
setupFilters () {
|
||||
|
@ -50,3 +50,7 @@ export function formatCoins (amount, decimals = 6) {
|
||||
export function formatEth (eth, decimals = 3) {
|
||||
return api.util.fromWei(eth).toFormat(decimals);
|
||||
}
|
||||
|
||||
export function formatHash (hash) {
|
||||
return `${hash.substr(0, 10)}...${hash.substr(-8)}`;
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<link rel="icon" href="parity.ico" type="image/x-icon">
|
||||
<title>GitHub Hint</title>
|
||||
</head>
|
||||
<body>
|
||||
|
@ -151,7 +151,7 @@ export default class Application extends Component {
|
||||
let urlError = null;
|
||||
|
||||
if (url && url.length) {
|
||||
var re = /^https?:\/\/(?:www\.|(?!www))[^\s\.]+\.[^\s]{2,}/g;
|
||||
const re = /^https?:\/\/(?:www\.|(?!www))[^\s\.]+\.[^\s]{2,}/g; // eslint-disable-line
|
||||
urlError = re.test(url)
|
||||
? null
|
||||
: 'not matching rexex';
|
||||
|
@ -4,6 +4,7 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<link rel="icon" href="parity.ico" type="image/x-icon">
|
||||
<title>Token Registry</title>
|
||||
</head>
|
||||
<body>
|
||||
|
@ -4,6 +4,7 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<link rel="icon" href="parity.ico" type="image/x-icon">
|
||||
<title>Method Signature Registry</title>
|
||||
</head>
|
||||
<body>
|
||||
|
@ -4,6 +4,7 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<link rel="icon" href="parity.ico" type="image/x-icon">
|
||||
<title>Token Registry</title>
|
||||
</head>
|
||||
<body>
|
||||
|
@ -4,6 +4,7 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<link rel="icon" href="parity.ico" type="image/x-icon">
|
||||
<title>dev::Parity.js</title>
|
||||
</head>
|
||||
<body>
|
||||
|
@ -4,6 +4,7 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<link rel="icon" href="parity.ico" type="image/x-icon">
|
||||
<title>dev::Web3</title>
|
||||
</head>
|
||||
<body>
|
||||
|
@ -4,6 +4,7 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<link rel="icon" href="parity.ico" type="image/x-icon">
|
||||
<title>Parity</title>
|
||||
<style>
|
||||
html, body, #container {
|
||||
|
@ -39,6 +39,8 @@ import './environment';
|
||||
|
||||
import '../assets/fonts/Roboto/font.css';
|
||||
import '../assets/fonts/RobotoMono/font.css';
|
||||
import '../assets/images/parity.ico';
|
||||
|
||||
import styles from './reset.css';
|
||||
import './index.html';
|
||||
|
||||
|
@ -14,12 +14,10 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { BASE_LINK_ACCOUNT_MORDEN, BASE_LINK_ACCOUNT_HOMESTEAD } from '../constants/constants';
|
||||
import Abi from './abi';
|
||||
import Api from './api';
|
||||
|
||||
export const getAccountLink = _getAccountLink;
|
||||
|
||||
function _getAccountLink (address, chain) {
|
||||
const isTestNet = chain === 'morden' || chain === 'testnet';
|
||||
const base = isTestNet ? BASE_LINK_ACCOUNT_MORDEN : BASE_LINK_ACCOUNT_HOMESTEAD;
|
||||
return base + address;
|
||||
}
|
||||
export {
|
||||
Abi,
|
||||
Api
|
||||
};
|
@ -50,6 +50,9 @@ export default class CreationType extends Component {
|
||||
<RadioButton
|
||||
label='Import account from an Ethereum pre-sale wallet'
|
||||
value='fromPresale' />
|
||||
<RadioButton
|
||||
label='Import raw private key'
|
||||
value='fromRaw' />
|
||||
</RadioButtonGroup>
|
||||
</div>
|
||||
);
|
||||
|
@ -26,6 +26,8 @@ import styles from '../createAccount.css';
|
||||
const ERRORS = {
|
||||
noName: 'you need to specify a valid name for the account',
|
||||
noPhrase: 'you need to specify the recovery phrase',
|
||||
noKey: 'you need to provide the raw private key',
|
||||
invalidKey: 'the raw key needs to be hex, 64 characters in length',
|
||||
invalidPassword: 'you need to specify a password >= 8 characters',
|
||||
noMatchPassword: 'the supplied passwords does not match'
|
||||
};
|
||||
|
17
js/src/modals/CreateAccount/RawKey/index.js
Normal file
17
js/src/modals/CreateAccount/RawKey/index.js
Normal file
@ -0,0 +1,17 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './rawKey';
|
189
js/src/modals/CreateAccount/RawKey/rawKey.js
Normal file
189
js/src/modals/CreateAccount/RawKey/rawKey.js
Normal file
@ -0,0 +1,189 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
import { Form, Input } from '../../../ui';
|
||||
|
||||
import styles from '../createAccount.css';
|
||||
|
||||
import { ERRORS } from '../NewAccount';
|
||||
|
||||
export default class RawKey extends Component {
|
||||
static contextTypes = {
|
||||
api: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
onChange: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
state = {
|
||||
rawKey: '',
|
||||
rawKeyError: ERRORS.noKey,
|
||||
accountName: '',
|
||||
accountNameError: ERRORS.noName,
|
||||
passwordHint: '',
|
||||
password1: '',
|
||||
password1Error: ERRORS.invalidPassword,
|
||||
password2: '',
|
||||
password2Error: ERRORS.noMatchPassword,
|
||||
isValidPass: false,
|
||||
isValidName: false,
|
||||
isValidKey: false
|
||||
}
|
||||
|
||||
componentWillMount () {
|
||||
this.props.onChange(false, {});
|
||||
}
|
||||
|
||||
render () {
|
||||
const { accountName, accountNameError, passwordHint, password1, password1Error, password2, password2Error, rawKey, rawKeyError } = this.state;
|
||||
|
||||
return (
|
||||
<Form>
|
||||
<Input
|
||||
hint='the raw hex encoded private key'
|
||||
label='private key'
|
||||
error={ rawKeyError }
|
||||
value={ rawKey }
|
||||
onChange={ this.onEditKey } />
|
||||
<Input
|
||||
label='account name'
|
||||
hint='a descriptive name for the account'
|
||||
error={ accountNameError }
|
||||
value={ accountName }
|
||||
onChange={ this.onEditAccountName } />
|
||||
<Input
|
||||
label='password hint'
|
||||
hint='(optional) a hint to help with remembering the password'
|
||||
value={ passwordHint }
|
||||
onChange={ this.onEditPasswordHint } />
|
||||
<div className={ styles.passwords }>
|
||||
<div className={ styles.password }>
|
||||
<Input
|
||||
className={ styles.password }
|
||||
label='password'
|
||||
hint='a strong, unique password'
|
||||
type='password'
|
||||
error={ password1Error }
|
||||
value={ password1 }
|
||||
onChange={ this.onEditPassword1 } />
|
||||
</div>
|
||||
<div className={ styles.password }>
|
||||
<Input
|
||||
className={ styles.password }
|
||||
label='password (repeat)'
|
||||
hint='verify your password'
|
||||
type='password'
|
||||
error={ password2Error }
|
||||
value={ password2 }
|
||||
onChange={ this.onEditPassword2 } />
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
|
||||
updateParent = () => {
|
||||
const { isValidName, isValidPass, isValidKey, accountName, passwordHint, password1, rawKey } = this.state;
|
||||
const isValid = isValidName && isValidPass && isValidKey;
|
||||
|
||||
this.props.onChange(isValid, {
|
||||
name: accountName,
|
||||
passwordHint,
|
||||
password: password1,
|
||||
rawKey
|
||||
});
|
||||
}
|
||||
|
||||
onEditPasswordHint = (event, value) => {
|
||||
this.setState({
|
||||
passwordHint: value
|
||||
});
|
||||
}
|
||||
|
||||
onEditKey = (event) => {
|
||||
const { api } = this.context;
|
||||
const rawKey = event.target.value;
|
||||
let rawKeyError = null;
|
||||
|
||||
console.log(rawKey.length, rawKey);
|
||||
|
||||
if (!rawKey || !rawKey.trim().length) {
|
||||
rawKeyError = ERRORS.noKey;
|
||||
} else if (rawKey.substr(0, 2) !== '0x' || rawKey.substr(2).length !== 64 || !api.util.isHex(rawKey)) {
|
||||
rawKeyError = ERRORS.invalidKey;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
rawKey,
|
||||
rawKeyError,
|
||||
isValidKey: !rawKeyError
|
||||
}, this.updateParent);
|
||||
}
|
||||
|
||||
onEditAccountName = (event) => {
|
||||
const accountName = event.target.value;
|
||||
let accountNameError = null;
|
||||
|
||||
if (!accountName || accountName.trim().length < 2) {
|
||||
accountNameError = ERRORS.noName;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
accountName,
|
||||
accountNameError,
|
||||
isValidName: !accountNameError
|
||||
}, this.updateParent);
|
||||
}
|
||||
|
||||
onEditPassword1 = (event) => {
|
||||
const value = event.target.value;
|
||||
let error1 = null;
|
||||
let error2 = null;
|
||||
|
||||
if (!value || value.trim().length < 8) {
|
||||
error1 = ERRORS.invalidPassword;
|
||||
}
|
||||
|
||||
if (value !== this.state.password2) {
|
||||
error2 = ERRORS.noMatchPassword;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
password1: value,
|
||||
password1Error: error1,
|
||||
password2Error: error2,
|
||||
isValidPass: !error1 && !error2
|
||||
}, this.updateParent);
|
||||
}
|
||||
|
||||
onEditPassword2 = (event) => {
|
||||
const value = event.target.value;
|
||||
let error2 = null;
|
||||
|
||||
if (value !== this.state.password1) {
|
||||
error2 = ERRORS.noMatchPassword;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
password2: value,
|
||||
password2Error: error2,
|
||||
isValidPass: !error2
|
||||
}, this.updateParent);
|
||||
}
|
||||
}
|
@ -29,6 +29,7 @@ import CreationType from './CreationType';
|
||||
import NewAccount from './NewAccount';
|
||||
import NewGeth from './NewGeth';
|
||||
import NewImport from './NewImport';
|
||||
import RawKey from './RawKey';
|
||||
import RecoveryPhrase from './RecoveryPhrase';
|
||||
|
||||
const TITLES = {
|
||||
@ -58,6 +59,7 @@ export default class CreateAccount extends Component {
|
||||
passwordHint: null,
|
||||
password: null,
|
||||
phrase: null,
|
||||
rawKey: null,
|
||||
json: null,
|
||||
canCreate: false,
|
||||
createType: null,
|
||||
@ -110,6 +112,11 @@ export default class CreateAccount extends Component {
|
||||
<RecoveryPhrase
|
||||
onChange={ this.onChangeDetails } />
|
||||
);
|
||||
} else if (createType === 'fromRaw') {
|
||||
return (
|
||||
<RawKey
|
||||
onChange={ this.onChangeDetails } />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
@ -220,6 +227,28 @@ export default class CreateAccount extends Component {
|
||||
canCreate: true
|
||||
});
|
||||
|
||||
this.newError(error);
|
||||
});
|
||||
} else if (createType === 'fromRaw') {
|
||||
return api.personal
|
||||
.newAccountFromSecret(this.state.rawKey, this.state.password)
|
||||
.then((address) => {
|
||||
this.setState({ address });
|
||||
return api.personal
|
||||
.setAccountName(address, this.state.name)
|
||||
.then(() => api.personal.setAccountMeta(address, { passwordHint: this.state.passwordHint }));
|
||||
})
|
||||
.then(() => {
|
||||
this.onNext();
|
||||
this.props.onUpdate && this.props.onUpdate();
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('onCreate', error);
|
||||
|
||||
this.setState({
|
||||
canCreate: true
|
||||
});
|
||||
|
||||
this.newError(error);
|
||||
});
|
||||
} else if (createType === 'fromGeth') {
|
||||
@ -288,27 +317,35 @@ export default class CreateAccount extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
onChangeDetails = (valid, { name, passwordHint, address, password, phrase }) => {
|
||||
onChangeDetails = (canCreate, { name, passwordHint, address, password, phrase, rawKey }) => {
|
||||
this.setState({
|
||||
canCreate: valid,
|
||||
canCreate,
|
||||
name,
|
||||
passwordHint,
|
||||
address,
|
||||
password,
|
||||
phrase
|
||||
phrase,
|
||||
rawKey
|
||||
});
|
||||
}
|
||||
|
||||
onChangeGeth = (valid, gethAddresses) => {
|
||||
onChangeRaw = (canCreate, rawKey) => {
|
||||
this.setState({
|
||||
canCreate: valid,
|
||||
canCreate,
|
||||
rawKey
|
||||
});
|
||||
}
|
||||
|
||||
onChangeGeth = (canCreate, gethAddresses) => {
|
||||
this.setState({
|
||||
canCreate,
|
||||
gethAddresses
|
||||
});
|
||||
}
|
||||
|
||||
onChangeWallet = (valid, { name, passwordHint, password, json }) => {
|
||||
onChangeWallet = (canCreate, { name, passwordHint, password, json }) => {
|
||||
this.setState({
|
||||
canCreate: valid,
|
||||
canCreate,
|
||||
name,
|
||||
passwordHint,
|
||||
password,
|
||||
|
@ -122,7 +122,7 @@ export default class FirstRun extends Component {
|
||||
icon={ <ActionDoneAll /> }
|
||||
label='Close'
|
||||
onClick={ this.onClose } />
|
||||
);
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,6 +46,16 @@
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.passwords {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.password {
|
||||
flex: 0 1 50%;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.passwordHint {
|
||||
font-size: 0.9rem;
|
||||
color: lightgrey;
|
||||
|
@ -181,33 +181,37 @@ export default class PasswordManager extends Component {
|
||||
disabled={ disabled }
|
||||
onSubmit={ this.handleChangePassword }
|
||||
onChange={ this.onEditCurrent } />
|
||||
|
||||
<Input
|
||||
label='new password'
|
||||
hint='the new password for this account'
|
||||
type='password'
|
||||
submitOnBlur={ false }
|
||||
disabled={ disabled }
|
||||
onSubmit={ this.handleChangePassword }
|
||||
onChange={ this.onEditNew } />
|
||||
<Input
|
||||
label='repeat new password'
|
||||
hint='repeat the new password for this account'
|
||||
type='password'
|
||||
submitOnBlur={ false }
|
||||
error={ repeatError }
|
||||
disabled={ disabled }
|
||||
onSubmit={ this.handleChangePassword }
|
||||
onChange={ this.onEditRepeatNew } />
|
||||
|
||||
<Input
|
||||
label='new password hint'
|
||||
label='(optional) new password hint'
|
||||
hint='hint for the new password'
|
||||
submitOnBlur={ false }
|
||||
value={ passwordHint }
|
||||
disabled={ disabled }
|
||||
onSubmit={ this.handleChangePassword }
|
||||
onChange={ this.onEditHint } />
|
||||
<div className={ styles.passwords }>
|
||||
<div className={ styles.password }>
|
||||
<Input
|
||||
label='new password'
|
||||
hint='the new password for this account'
|
||||
type='password'
|
||||
submitOnBlur={ false }
|
||||
disabled={ disabled }
|
||||
onSubmit={ this.handleChangePassword }
|
||||
onChange={ this.onEditNew } />
|
||||
</div>
|
||||
<div className={ styles.password }>
|
||||
<Input
|
||||
label='repeat new password'
|
||||
hint='repeat the new password for this account'
|
||||
type='password'
|
||||
submitOnBlur={ false }
|
||||
error={ repeatError }
|
||||
disabled={ disabled }
|
||||
onSubmit={ this.handleChangePassword }
|
||||
onChange={ this.onEditRepeatNew } />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
</Tab>
|
||||
|
@ -420,13 +420,15 @@ export default class Transfer extends Component {
|
||||
|
||||
_sendToken () {
|
||||
const { account, balance } = this.props;
|
||||
const { recipient, value, tag } = this.state;
|
||||
const { gas, gasPrice, recipient, value, tag } = this.state;
|
||||
const token = balance.tokens.find((balance) => balance.token.tag === tag).token;
|
||||
|
||||
return token.contract.instance.transfer
|
||||
.postTransaction({
|
||||
from: account.address,
|
||||
to: token.address
|
||||
to: token.address,
|
||||
gas,
|
||||
gasPrice
|
||||
}, [
|
||||
recipient,
|
||||
new BigNumber(value).mul(token.format).toFixed(0)
|
||||
@ -483,7 +485,7 @@ export default class Transfer extends Component {
|
||||
to: token.address
|
||||
}, [
|
||||
recipient,
|
||||
new BigNumber(value || 0).mul(token.format).toString()
|
||||
new BigNumber(value || 0).mul(token.format).toFixed(0)
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -139,7 +139,7 @@ class MethodDecoding extends Component {
|
||||
|
||||
return (
|
||||
<div className={ styles.gasDetails }>
|
||||
{ historic ? 'Used' : 'Will use' } <span className={ styles.highlight }>{ gas.toFormat(0) } gas ({ gasPrice.div(1000000).toFormat(0) }M/<small>ETH</small>)</span> for a total transaction cost of <span className={ styles.highlight }>{ this.renderEtherValue(gasValue) }</span>
|
||||
{ historic ? 'Provided' : 'Provides' } <span className={ styles.highlight }>{ gas.toFormat(0) } gas ({ gasPrice.div(1000000).toFormat(0) }M/<small>ETH</small>)</span> for a total transaction value of <span className={ styles.highlight }>{ this.renderEtherValue(gasValue) }</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
.hash {
|
||||
padding-top: 1em;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.confirm {
|
||||
@ -31,11 +32,13 @@
|
||||
}
|
||||
|
||||
.progressbar {
|
||||
margin: 0.5em !important;
|
||||
margin: 0.5em 0 !important;
|
||||
width: 30% !important;
|
||||
min-width: 220px;
|
||||
display: inline-block !important;
|
||||
height: 0.75em !important;
|
||||
}
|
||||
|
||||
.progressinfo {
|
||||
text-align: center;
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ import React, { Component, PropTypes } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { LinearProgress } from 'material-ui';
|
||||
import { txLink } from '../../3rdparty/etherscan/links';
|
||||
|
||||
import styles from './txHash.css';
|
||||
|
||||
@ -29,7 +30,8 @@ class TxHash extends Component {
|
||||
|
||||
static propTypes = {
|
||||
hash: PropTypes.string.isRequired,
|
||||
isTest: PropTypes.bool
|
||||
isTest: PropTypes.bool,
|
||||
summary: PropTypes.bool
|
||||
}
|
||||
|
||||
state = {
|
||||
@ -54,16 +56,22 @@ class TxHash extends Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
const { hash, isTest } = this.props;
|
||||
const link = `https://${isTest ? 'testnet.' : ''}etherscan.io/tx/${hash}`;
|
||||
const { hash, isTest, summary } = this.props;
|
||||
let header = null;
|
||||
|
||||
return (
|
||||
<div className={ styles.details }>
|
||||
if (!summary) {
|
||||
header = (
|
||||
<div className={ styles.header }>
|
||||
The transaction has been posted to the network with a transaction hash of
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={ styles.details }>
|
||||
{ header }
|
||||
<div className={ styles.hash }>
|
||||
<a href={ link } target='_blank'>{ hash }</a>
|
||||
<a href={ txLink(hash, isTest) } target='_blank'>{ hash }</a>
|
||||
</div>
|
||||
{ this.renderConfirmations() }
|
||||
</div>
|
||||
|
@ -23,6 +23,7 @@ import { bindActionCreators } from 'redux';
|
||||
import { fetchBlock, fetchTransaction } from '../../../../redux/providers/blockchainActions';
|
||||
|
||||
import { IdentityIcon, IdentityName, MethodDecoding } from '../../../../ui';
|
||||
import { txLink, addressLink } from '../../../../3rdparty/etherscan/links';
|
||||
|
||||
import styles from '../transactions.css';
|
||||
|
||||
@ -55,9 +56,7 @@ class Transaction extends Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
const { block, transaction, isTest } = this.props;
|
||||
|
||||
const prefix = `https://${isTest ? 'testnet.' : ''}etherscan.io/`;
|
||||
const { block, transaction } = this.props;
|
||||
|
||||
return (
|
||||
<tr>
|
||||
@ -65,9 +64,9 @@ class Transaction extends Component {
|
||||
<div>{ this.formatBlockTimestamp(block) }</div>
|
||||
<div>{ this.formatNumber(transaction.blockNumber) }</div>
|
||||
</td>
|
||||
{ this.renderAddress(prefix, transaction.from) }
|
||||
{ this.renderAddress(transaction.from) }
|
||||
{ this.renderTransaction() }
|
||||
{ this.renderAddress(prefix, transaction.to) }
|
||||
{ this.renderAddress(transaction.to) }
|
||||
<td className={ styles.method }>
|
||||
{ this.renderMethod() }
|
||||
</td>
|
||||
@ -93,15 +92,16 @@ class Transaction extends Component {
|
||||
renderTransaction () {
|
||||
const { transaction, isTest } = this.props;
|
||||
|
||||
const prefix = `https://${isTest ? 'testnet.' : ''}etherscan.io/`;
|
||||
const hashLink = `${prefix}tx/${transaction.hash}`;
|
||||
|
||||
return (
|
||||
<td className={ styles.transaction }>
|
||||
{ this.renderEtherValue() }
|
||||
<div>⇒</div>
|
||||
<div>
|
||||
<a href={ hashLink } target='_blank' className={ styles.link }>
|
||||
<a
|
||||
className={ styles.link }
|
||||
href={ txLink(transaction.hash, isTest) }
|
||||
target='_blank'
|
||||
>
|
||||
{ this.formatHash(transaction.hash) }
|
||||
</a>
|
||||
</div>
|
||||
@ -109,10 +109,12 @@ class Transaction extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
renderAddress (prefix, address) {
|
||||
renderAddress (address) {
|
||||
const { isTest } = this.props;
|
||||
|
||||
const eslink = address ? (
|
||||
<a
|
||||
href={ `${prefix}address/${address}` }
|
||||
href={ addressLink(address, isTest) }
|
||||
target='_blank'
|
||||
className={ styles.link }>
|
||||
<IdentityName address={ address } shorten />
|
||||
|
@ -22,6 +22,7 @@ import { bindActionCreators } from 'redux';
|
||||
|
||||
import { fetchBlock, fetchTransaction } from '../../../../redux/providers/blockchainActions';
|
||||
import { IdentityIcon, IdentityName, Input, InputAddress } from '../../../../ui';
|
||||
import { txLink } from '../../../../3rdparty/etherscan/links';
|
||||
|
||||
import styles from '../../contract.css';
|
||||
|
||||
@ -49,7 +50,7 @@ class Event extends Component {
|
||||
const block = blocks[event.blockNumber.toString()];
|
||||
const transaction = transactions[event.transactionHash] || {};
|
||||
const classes = `${styles.event} ${styles[event.state]}`;
|
||||
const url = `https://${isTest ? 'testnet.' : ''}etherscan.io/tx/${event.transactionHash}`;
|
||||
const url = txLink(event.transactionHash, isTest);
|
||||
const keys = Object.keys(event.params).join(', ');
|
||||
const values = Object.keys(event.params).map((name, index) => {
|
||||
const param = event.params[name];
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
import { getAccountLink } from '../../util/account';
|
||||
import { addressLink } from '../../../../../3rdparty/etherscan/links';
|
||||
import styles from './AccountLink.css';
|
||||
|
||||
export default class AccountLink extends Component {
|
||||
@ -57,7 +57,7 @@ export default class AccountLink extends Component {
|
||||
}
|
||||
|
||||
updateLink (address, chain) {
|
||||
const link = getAccountLink(address, chain);
|
||||
const link = addressLink(address, chain === 'morden' || chain === 'testnet');
|
||||
|
||||
this.setState({
|
||||
link
|
||||
|
@ -29,7 +29,7 @@
|
||||
|
||||
.statusContainer {
|
||||
width: 220px;
|
||||
padding: 20px 40px 0 40px;
|
||||
padding: 0 40px 0 40px;
|
||||
/*border-left: 1px solid #aaa;*/
|
||||
position: absolute;
|
||||
top: 0;
|
||||
@ -46,7 +46,8 @@
|
||||
}
|
||||
|
||||
.isRejected {
|
||||
opacity: 0.7;
|
||||
opacity: 0.5;
|
||||
padding-top: 2em;
|
||||
}
|
||||
|
||||
.txHash {
|
||||
|
@ -17,6 +17,9 @@
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
import CircularProgress from 'material-ui/CircularProgress';
|
||||
|
||||
import { TxHash } from '../../../../ui';
|
||||
|
||||
import TransactionMainDetails from '../TransactionMainDetails';
|
||||
import TxHashLink from '../TxHashLink';
|
||||
import TransactionSecondaryDetails from '../TransactionSecondaryDetails';
|
||||
@ -57,7 +60,7 @@ export default class TransactionFinished extends Component {
|
||||
};
|
||||
|
||||
componentWillMount () {
|
||||
const { gas, gasPrice, value } = this.props;
|
||||
const { from, to, gas, gasPrice, value } = this.props;
|
||||
const fee = tUtil.getFee(gas, gasPrice); // BigNumber object
|
||||
const totalValue = tUtil.getTotalValue(fee, value);
|
||||
this.setState({ totalValue });
|
||||
@ -70,9 +73,10 @@ export default class TransactionFinished extends Component {
|
||||
console.error('could not fetch chain', err);
|
||||
});
|
||||
|
||||
const { from, to } = this.props;
|
||||
this.fetchBalance(from, 'fromBalance');
|
||||
if (to) this.fetchBalance(to, 'toBalance');
|
||||
if (to) {
|
||||
this.fetchBalance(to, 'toBalance');
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
@ -109,19 +113,27 @@ export default class TransactionFinished extends Component {
|
||||
}
|
||||
|
||||
renderStatus () {
|
||||
const { status } = this.props;
|
||||
const klass = status === 'confirmed' ? styles.isConfirmed : styles.isRejected;
|
||||
const { status, txHash } = this.props;
|
||||
|
||||
if (status !== 'confirmed') {
|
||||
return (
|
||||
<div>
|
||||
<span className={ styles.isRejected }>{ capitalize(status) }</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<span className={ klass }>{ capitalize(status) }</span>
|
||||
{ this.renderTxHash() }
|
||||
</div>
|
||||
<TxHash
|
||||
summary
|
||||
hash={ txHash } />
|
||||
);
|
||||
}
|
||||
|
||||
renderTxHash () {
|
||||
const { txHash, chain } = this.props;
|
||||
if (!txHash) {
|
||||
const { txHash } = this.props;
|
||||
const { chain } = this.state;
|
||||
if (!txHash || !chain) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
import { getTxLink } from '../util/transaction';
|
||||
import { txLink } from '../../../../3rdparty/etherscan/links';
|
||||
|
||||
export default class TxHashLink extends Component {
|
||||
|
||||
@ -27,27 +27,12 @@ export default class TxHashLink extends Component {
|
||||
className: PropTypes.string
|
||||
}
|
||||
|
||||
state = {
|
||||
link: null
|
||||
};
|
||||
|
||||
componentWillMount () {
|
||||
const { txHash, chain } = this.props;
|
||||
this.updateLink(txHash, chain);
|
||||
}
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
const { txHash, chain } = nextProps;
|
||||
this.updateLink(txHash, chain);
|
||||
}
|
||||
|
||||
render () {
|
||||
const { children, txHash, className } = this.props;
|
||||
const { link } = this.state;
|
||||
const { children, txHash, className, chain } = this.props;
|
||||
|
||||
return (
|
||||
<a
|
||||
href={ link }
|
||||
href={ txLink(txHash, chain === 'morden' || chain === 'testnet') }
|
||||
target='_blank'
|
||||
className={ className }>
|
||||
{ children || txHash }
|
||||
@ -55,9 +40,4 @@ export default class TxHashLink extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
updateLink (txHash, chain) {
|
||||
const link = getTxLink(txHash, chain);
|
||||
this.setState({ link });
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -18,7 +18,6 @@ import BigNumber from 'bignumber.js';
|
||||
|
||||
const WEI_TO_ETH_MULTIPLIER = 0.000000000000000001;
|
||||
const WEI_TO_SZABU_MULTIPLIER = 0.000000000001;
|
||||
import { BASE_LINK_TX_MORDEN, BASE_LINK_TX_HOMESTEAD } from '../constants/constants';
|
||||
|
||||
export const getShortData = _getShortData;
|
||||
// calculations
|
||||
@ -33,8 +32,6 @@ export const getTotalValueDisplay = _getTotalValueDisplay;
|
||||
export const getTotalValueDisplayWei = _getTotalValueDisplayWei;
|
||||
export const getEthmFromWeiDisplay = _getEthmFromWeiDisplay;
|
||||
export const getGasDisplay = _getGasDisplay;
|
||||
// links
|
||||
export const getTxLink = _getTxLink;
|
||||
|
||||
function _getShortData (data) {
|
||||
if (data.length <= 3) {
|
||||
@ -111,11 +108,6 @@ function _getEthmFromWeiDisplay (weiHexString) {
|
||||
return value.times(WEI_TO_ETH_MULTIPLIER).times(1e7).toFixed(5);
|
||||
}
|
||||
|
||||
function _getTxLink (txHash, chain) {
|
||||
const base = chain === 'morden' || chain === 'testnet' ? BASE_LINK_TX_MORDEN : BASE_LINK_TX_HOMESTEAD;
|
||||
return base + txHash;
|
||||
}
|
||||
|
||||
function _getGasDisplay (gas) {
|
||||
return new BigNumber(gas).times(1e-7).toFormat(4);
|
||||
}
|
||||
|
@ -83,9 +83,13 @@ module.exports = {
|
||||
loader: 'style!css'
|
||||
},
|
||||
{
|
||||
test: /\.(png|jpg|)$/,
|
||||
test: /\.(png|jpg)$/,
|
||||
loader: 'file-loader'
|
||||
},
|
||||
{
|
||||
test: /\.ico$/,
|
||||
loader: 'file-loader?name=[name].[ext]'
|
||||
},
|
||||
{
|
||||
test: /\.(woff(2)|ttf|eot|svg|otf)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
|
||||
loader: 'file-loader'
|
||||
|
82
js/webpack.npm.js
Normal file
82
js/webpack.npm.js
Normal file
@ -0,0 +1,82 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||
const packageJson = require('./package.json');
|
||||
|
||||
const ENV = process.env.NODE_ENV || 'development';
|
||||
const isProd = ENV === 'production';
|
||||
|
||||
module.exports = {
|
||||
context: path.join(__dirname, './src'),
|
||||
entry: 'library.js',
|
||||
output: {
|
||||
path: path.join(__dirname, '.npmjs'),
|
||||
filename: 'library.js',
|
||||
libraryTarget: 'commonjs'
|
||||
},
|
||||
module: {
|
||||
loaders: [
|
||||
{
|
||||
test: /(\.jsx|\.js)$/,
|
||||
loader: 'babel',
|
||||
exclude: /node_modules/
|
||||
}
|
||||
]
|
||||
},
|
||||
resolve: {
|
||||
root: path.resolve('./src'),
|
||||
extensions: ['', '.js']
|
||||
},
|
||||
plugins: (function () {
|
||||
const plugins = [
|
||||
new CopyWebpackPlugin([
|
||||
{
|
||||
from: '../parity.package.json',
|
||||
to: 'package.json',
|
||||
transform: function (content, path) {
|
||||
const json = JSON.parse(content.toString());
|
||||
json.version = packageJson.version;
|
||||
return new Buffer(JSON.stringify(json, null, ' '), 'utf-8');
|
||||
}
|
||||
},
|
||||
{
|
||||
from: '../LICENSE'
|
||||
},
|
||||
{
|
||||
from: '../parity.md',
|
||||
to: 'README.md'
|
||||
}
|
||||
], { copyUnmodified: true })
|
||||
];
|
||||
|
||||
if (isProd) {
|
||||
plugins.push(new webpack.optimize.UglifyJsPlugin({
|
||||
screwIe8: true,
|
||||
compress: {
|
||||
warnings: false
|
||||
},
|
||||
output: {
|
||||
comments: false
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
return plugins;
|
||||
}())
|
||||
};
|
@ -14,6 +14,13 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
macro_rules! println_stderr(
|
||||
($($arg:tt)*) => { {
|
||||
let r = writeln!(&mut ::std::io::stderr(), $($arg)*);
|
||||
r.expect("failed printing to stderr");
|
||||
} }
|
||||
);
|
||||
|
||||
macro_rules! otry {
|
||||
($e: expr) => (
|
||||
match $e {
|
||||
@ -39,7 +46,7 @@ macro_rules! usage {
|
||||
) => {
|
||||
use toml;
|
||||
use std::{fs, io, process};
|
||||
use std::io::Read;
|
||||
use std::io::{Read, Write};
|
||||
use util::version;
|
||||
use docopt::{Docopt, Error as DocoptError};
|
||||
use helpers::replace_home;
|
||||
@ -58,20 +65,20 @@ macro_rules! usage {
|
||||
match self {
|
||||
ArgsError::Docopt(e) => e.exit(),
|
||||
ArgsError::Parsing(errors) => {
|
||||
println!("There is an error in config file.");
|
||||
println_stderr!("There is an error in config file.");
|
||||
for e in &errors {
|
||||
println!("{}", e);
|
||||
println_stderr!("{}", e);
|
||||
}
|
||||
process::exit(2)
|
||||
},
|
||||
ArgsError::Decode(e) => {
|
||||
println!("You might have supplied invalid parameters in config file.");
|
||||
println!("{}", e);
|
||||
println_stderr!("You might have supplied invalid parameters in config file.");
|
||||
println_stderr!("{}", e);
|
||||
process::exit(2)
|
||||
},
|
||||
ArgsError::Config(path, e) => {
|
||||
println!("There was an error reading your config file at: {}", path);
|
||||
println!("{}", e);
|
||||
println_stderr!("There was an error reading your config file at: {}", path);
|
||||
println_stderr!("{}", e);
|
||||
process::exit(2)
|
||||
}
|
||||
}
|
||||
@ -136,7 +143,7 @@ macro_rules! usage {
|
||||
let config = match (fs::File::open(&config_file), raw_args.flag_config.is_some()) {
|
||||
// Load config file
|
||||
(Ok(mut file), _) => {
|
||||
println!("Loading config file from {}", &config_file);
|
||||
println_stderr!("Loading config file from {}", &config_file);
|
||||
let mut config = String::new();
|
||||
try!(file.read_to_string(&mut config).map_err(|e| ArgsError::Config(config_file, e)));
|
||||
try!(Self::parse_config(&config))
|
||||
|
@ -184,7 +184,7 @@ impl ChainNotify for Informant {
|
||||
let ripe = Instant::now() > *last_import + Duration::from_secs(1) && !importing;
|
||||
let txs_imported = imported.iter()
|
||||
.take(imported.len() - if ripe {1} else {0})
|
||||
.filter_map(|h| self.client.block(BlockID::Hash(h.clone())))
|
||||
.filter_map(|h| self.client.block(BlockID::Hash(*h)))
|
||||
.map(|b| BlockView::new(&b).transactions_count())
|
||||
.sum();
|
||||
|
||||
|
@ -207,10 +207,10 @@ fn main() {
|
||||
|
||||
match start() {
|
||||
Ok(result) => {
|
||||
println!("{}", result);
|
||||
info!("{}", result);
|
||||
},
|
||||
Err(err) => {
|
||||
println!("{}", err);
|
||||
info!("{}", err);
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ use url;
|
||||
const SNAPSHOT_PERIOD: u64 = 10000;
|
||||
|
||||
// how many blocks to wait before starting a periodic snapshot.
|
||||
const SNAPSHOT_HISTORY: u64 = 500;
|
||||
const SNAPSHOT_HISTORY: u64 = 100;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct RunCmd {
|
||||
|
@ -58,7 +58,7 @@ impl Default for SyncConfig {
|
||||
network_id: U256::from(1),
|
||||
subprotocol_name: *b"eth",
|
||||
fork_block: None,
|
||||
warp_sync: true,
|
||||
warp_sync: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ use sync_io::SyncIo;
|
||||
use blocks::BlockCollection;
|
||||
|
||||
const MAX_HEADERS_TO_REQUEST: usize = 128;
|
||||
const MAX_BODIES_TO_REQUEST: usize = 128;
|
||||
const MAX_BODIES_TO_REQUEST: usize = 64;
|
||||
const MAX_RECEPITS_TO_REQUEST: usize = 128;
|
||||
const SUBCHAIN_SIZE: u64 = 256;
|
||||
const MAX_ROUND_PARENTS: usize = 32;
|
||||
|
@ -123,6 +123,7 @@ const MAX_NEW_BLOCK_AGE: BlockNumber = 20;
|
||||
const MAX_TRANSACTION_SIZE: usize = 300*1024;
|
||||
// Min number of blocks to be behind for a snapshot sync
|
||||
const SNAPSHOT_RESTORE_THRESHOLD: BlockNumber = 100000;
|
||||
const SNAPSHOT_MIN_PEERS: usize = 3;
|
||||
|
||||
const STATUS_PACKET: u8 = 0x00;
|
||||
const NEW_BLOCK_HASHES_PACKET: u8 = 0x01;
|
||||
@ -147,21 +148,27 @@ const SNAPSHOT_DATA_PACKET: u8 = 0x14;
|
||||
|
||||
pub const SNAPSHOT_SYNC_PACKET_COUNT: u8 = 0x15;
|
||||
|
||||
const HEADERS_TIMEOUT_SEC: f64 = 15f64;
|
||||
const BODIES_TIMEOUT_SEC: f64 = 10f64;
|
||||
const RECEIPTS_TIMEOUT_SEC: f64 = 10f64;
|
||||
const FORK_HEADER_TIMEOUT_SEC: f64 = 3f64;
|
||||
const SNAPSHOT_MANIFEST_TIMEOUT_SEC: f64 = 3f64;
|
||||
const SNAPSHOT_DATA_TIMEOUT_SEC: f64 = 60f64;
|
||||
const MAX_SNAPSHOT_CHUNKS_DOWNLOAD_AHEAD: usize = 3;
|
||||
|
||||
const WAIT_PEERS_TIMEOUT_SEC: u64 = 5;
|
||||
const STATUS_TIMEOUT_SEC: u64 = 5;
|
||||
const HEADERS_TIMEOUT_SEC: u64 = 15;
|
||||
const BODIES_TIMEOUT_SEC: u64 = 10;
|
||||
const RECEIPTS_TIMEOUT_SEC: u64 = 10;
|
||||
const FORK_HEADER_TIMEOUT_SEC: u64 = 3;
|
||||
const SNAPSHOT_MANIFEST_TIMEOUT_SEC: u64 = 3;
|
||||
const SNAPSHOT_DATA_TIMEOUT_SEC: u64 = 60;
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
/// Sync state
|
||||
pub enum SyncState {
|
||||
/// Waiting for pv64 peers to start snapshot syncing
|
||||
/// Collecting enough peers to start syncing.
|
||||
WaitingPeers,
|
||||
/// Waiting for snapshot manifest download
|
||||
SnapshotManifest,
|
||||
/// Downloading snapshot data
|
||||
SnapshotData,
|
||||
/// Waiting for snapshot restoration to complete
|
||||
/// Waiting for snapshot restoration progress.
|
||||
SnapshotWaiting,
|
||||
/// Downloading new blocks
|
||||
Blocks,
|
||||
@ -276,7 +283,7 @@ struct PeerInfo {
|
||||
/// Holds requested snapshot chunk hash if any.
|
||||
asking_snapshot_data: Option<H256>,
|
||||
/// Request timestamp
|
||||
ask_time: f64,
|
||||
ask_time: u64,
|
||||
/// Holds a set of transactions recently sent to this peer to avoid spamming.
|
||||
last_sent_transactions: HashSet<H256>,
|
||||
/// Pending request is expired and result should be ignored
|
||||
@ -324,10 +331,13 @@ pub struct ChainSync {
|
||||
network_id: U256,
|
||||
/// Optional fork block to check
|
||||
fork_block: Option<(BlockNumber, H256)>,
|
||||
/// Snapshot sync allowed.
|
||||
snapshot_sync_enabled: bool,
|
||||
/// Snapshot downloader.
|
||||
snapshot: Snapshot,
|
||||
/// Connected peers pending Status message.
|
||||
/// Value is request timestamp.
|
||||
handshaking_peers: HashMap<PeerId, u64>,
|
||||
/// Sync start timestamp. Measured when first peer is connected
|
||||
sync_start_time: Option<u64>,
|
||||
}
|
||||
|
||||
type RlpResponseResult = Result<Option<(PacketId, RlpStream)>, PacketDecodeError>;
|
||||
@ -337,20 +347,21 @@ impl ChainSync {
|
||||
pub fn new(config: SyncConfig, chain: &BlockChainClient) -> ChainSync {
|
||||
let chain_info = chain.chain_info();
|
||||
let mut sync = ChainSync {
|
||||
state: SyncState::Idle,
|
||||
state: if config.warp_sync { SyncState::WaitingPeers } else { SyncState::Idle },
|
||||
starting_block: chain.chain_info().best_block_number,
|
||||
highest_block: None,
|
||||
peers: HashMap::new(),
|
||||
handshaking_peers: HashMap::new(),
|
||||
active_peers: HashSet::new(),
|
||||
new_blocks: BlockDownloader::new(false, &chain_info.best_block_hash, chain_info.best_block_number),
|
||||
old_blocks: None,
|
||||
last_sent_block_number: 0,
|
||||
network_id: config.network_id,
|
||||
fork_block: config.fork_block,
|
||||
snapshot_sync_enabled: config.warp_sync,
|
||||
snapshot: Snapshot::new(),
|
||||
sync_start_time: None,
|
||||
};
|
||||
sync.init_downloaders(chain);
|
||||
sync.update_targets(chain);
|
||||
sync
|
||||
}
|
||||
|
||||
@ -442,20 +453,67 @@ impl ChainSync {
|
||||
self.active_peers.remove(&peer_id);
|
||||
}
|
||||
|
||||
fn start_snapshot_sync(&mut self, io: &mut SyncIo, peer_id: PeerId) {
|
||||
fn maybe_start_snapshot_sync(&mut self, io: &mut SyncIo) {
|
||||
if self.state != SyncState::WaitingPeers {
|
||||
return;
|
||||
}
|
||||
let best_block = io.chain().chain_info().best_block_number;
|
||||
|
||||
let (best_hash, max_peers, snapshot_peers) = {
|
||||
//collect snapshot infos from peers
|
||||
let snapshots = self.peers.iter()
|
||||
.filter(|&(_, p)| p.is_allowed() && p.snapshot_number.map_or(false, |sn| best_block < sn && (sn - best_block) > SNAPSHOT_RESTORE_THRESHOLD))
|
||||
.filter_map(|(p, peer)| peer.snapshot_hash.map(|hash| (p, hash.clone())));
|
||||
|
||||
let mut snapshot_peers = HashMap::new();
|
||||
let mut max_peers: usize = 0;
|
||||
let mut best_hash = None;
|
||||
for (p, hash) in snapshots {
|
||||
let peers = snapshot_peers.entry(hash).or_insert_with(Vec::new);
|
||||
peers.push(*p);
|
||||
if peers.len() > max_peers {
|
||||
max_peers = peers.len();
|
||||
best_hash = Some(hash);
|
||||
}
|
||||
}
|
||||
(best_hash, max_peers, snapshot_peers)
|
||||
};
|
||||
|
||||
let timeout = self.sync_start_time.map_or(false, |t| ((time::precise_time_ns() - t) / 1_000_000_000) > WAIT_PEERS_TIMEOUT_SEC);
|
||||
|
||||
if let (Some(hash), Some(peers)) = (best_hash, best_hash.map_or(None, |h| snapshot_peers.get(&h))) {
|
||||
if max_peers >= SNAPSHOT_MIN_PEERS {
|
||||
trace!(target: "sync", "Starting confirmed snapshot sync {:?} with {:?}", hash, peers);
|
||||
self.start_snapshot_sync(io, peers);
|
||||
} else if timeout {
|
||||
trace!(target: "sync", "Starting unconfirmed snapshot sync {:?} with {:?}", hash, peers);
|
||||
self.start_snapshot_sync(io, peers);
|
||||
}
|
||||
} else if timeout {
|
||||
trace!(target: "sync", "No snapshots found, starting full sync");
|
||||
self.state = SyncState::Idle;
|
||||
self.continue_sync(io);
|
||||
}
|
||||
}
|
||||
|
||||
fn start_snapshot_sync(&mut self, io: &mut SyncIo, peers: &[PeerId]) {
|
||||
self.snapshot.clear();
|
||||
self.request_snapshot_manifest(io, peer_id);
|
||||
for p in peers {
|
||||
if self.peers.get(p).map_or(false, |p| p.asking == PeerAsking::Nothing) {
|
||||
self.request_snapshot_manifest(io, *p);
|
||||
}
|
||||
}
|
||||
self.state = SyncState::SnapshotManifest;
|
||||
}
|
||||
|
||||
/// Restart sync disregarding the block queue status. May end up re-downloading up to QUEUE_SIZE blocks
|
||||
pub fn restart(&mut self, io: &mut SyncIo) {
|
||||
self.init_downloaders(io.chain());
|
||||
self.update_targets(io.chain());
|
||||
self.reset_and_continue(io);
|
||||
}
|
||||
|
||||
/// Restart sync after bad block has been detected. May end up re-downloading up to QUEUE_SIZE blocks
|
||||
fn init_downloaders(&mut self, chain: &BlockChainClient) {
|
||||
/// Update sync after the blockchain has been changed externally.
|
||||
pub fn update_targets(&mut self, chain: &BlockChainClient) {
|
||||
// Do not assume that the block queue/chain still has our last_imported_block
|
||||
let chain = chain.chain_info();
|
||||
self.new_blocks = BlockDownloader::new(false, &chain.best_block_hash, chain.best_block_number);
|
||||
@ -475,6 +533,7 @@ impl ChainSync {
|
||||
|
||||
/// Called by peer to report status
|
||||
fn on_peer_status(&mut self, io: &mut SyncIo, peer_id: PeerId, r: &UntrustedRlp) -> Result<(), PacketDecodeError> {
|
||||
self.handshaking_peers.remove(&peer_id);
|
||||
let protocol_version: u8 = try!(r.val_at(0));
|
||||
let warp_protocol = io.protocol_version(&WARP_SYNC_PROTOCOL_ID, peer_id) != 0;
|
||||
let peer = PeerInfo {
|
||||
@ -486,7 +545,7 @@ impl ChainSync {
|
||||
asking: PeerAsking::Nothing,
|
||||
asking_blocks: Vec::new(),
|
||||
asking_hash: None,
|
||||
ask_time: 0f64,
|
||||
ask_time: 0,
|
||||
last_sent_transactions: HashSet::new(),
|
||||
expired: false,
|
||||
confirmation: if self.fork_block.is_none() { ForkConfirmation::Confirmed } else { ForkConfirmation::Unconfirmed },
|
||||
@ -496,7 +555,12 @@ impl ChainSync {
|
||||
block_set: None,
|
||||
};
|
||||
|
||||
trace!(target: "sync", "New peer {} (protocol: {}, network: {:?}, difficulty: {:?}, latest:{}, genesis:{})", peer_id, peer.protocol_version, peer.network_id, peer.difficulty, peer.latest_hash, peer.genesis);
|
||||
if self.sync_start_time.is_none() {
|
||||
self.sync_start_time = Some(time::precise_time_ns());
|
||||
}
|
||||
|
||||
trace!(target: "sync", "New peer {} (protocol: {}, network: {:?}, difficulty: {:?}, latest:{}, genesis:{}, snapshot:{:?})",
|
||||
peer_id, peer.protocol_version, peer.network_id, peer.difficulty, peer.latest_hash, peer.genesis, peer.snapshot_number);
|
||||
if io.is_expired() {
|
||||
trace!(target: "sync", "Status packet from expired session {}:{}", peer_id, io.peer_info(peer_id));
|
||||
return Ok(());
|
||||
@ -578,7 +642,7 @@ impl ChainSync {
|
||||
}
|
||||
let item_count = r.item_count();
|
||||
trace!(target: "sync", "{} -> BlockHeaders ({} entries), state = {:?}, set = {:?}", peer_id, item_count, self.state, block_set);
|
||||
if self.state == SyncState::Idle && self.old_blocks.is_none() {
|
||||
if (self.state == SyncState::Idle || self.state == SyncState::WaitingPeers) && self.old_blocks.is_none() {
|
||||
trace!(target: "sync", "Ignored unexpected block headers");
|
||||
self.continue_sync(io);
|
||||
return Ok(());
|
||||
@ -875,7 +939,7 @@ impl ChainSync {
|
||||
}
|
||||
self.clear_peer_download(peer_id);
|
||||
if !self.reset_peer_asking(peer_id, PeerAsking::SnapshotManifest) || self.state != SyncState::SnapshotManifest {
|
||||
trace!(target: "sync", "{}: Ignored unexpected manifest", peer_id);
|
||||
trace!(target: "sync", "{}: Ignored unexpected/expired manifest", peer_id);
|
||||
self.continue_sync(io);
|
||||
return Ok(());
|
||||
}
|
||||
@ -918,7 +982,7 @@ impl ChainSync {
|
||||
match io.snapshot_service().status() {
|
||||
RestorationStatus::Inactive | RestorationStatus::Failed => {
|
||||
trace!(target: "sync", "{}: Snapshot restoration aborted", peer_id);
|
||||
self.state = SyncState::Idle;
|
||||
self.state = SyncState::WaitingPeers;
|
||||
self.snapshot.clear();
|
||||
self.continue_sync(io);
|
||||
return Ok(());
|
||||
@ -960,6 +1024,7 @@ impl ChainSync {
|
||||
/// Called by peer when it is disconnecting
|
||||
pub fn on_peer_aborting(&mut self, io: &mut SyncIo, peer: PeerId) {
|
||||
trace!(target: "sync", "== Disconnecting {}: {}", peer, io.peer_info(peer));
|
||||
self.handshaking_peers.remove(&peer);
|
||||
if self.peers.contains_key(&peer) {
|
||||
debug!(target: "sync", "Disconnected {}", peer);
|
||||
self.clear_peer_download(peer);
|
||||
@ -975,12 +1040,14 @@ impl ChainSync {
|
||||
if let Err(e) = self.send_status(io, peer) {
|
||||
debug!(target:"sync", "Error sending status request: {:?}", e);
|
||||
io.disable_peer(peer);
|
||||
} else {
|
||||
self.handshaking_peers.insert(peer, time::precise_time_ns());
|
||||
}
|
||||
}
|
||||
|
||||
/// Resume downloading
|
||||
fn continue_sync(&mut self, io: &mut SyncIo) {
|
||||
if self.state != SyncState::Waiting && self.state != SyncState::SnapshotWaiting
|
||||
if (self.state == SyncState::Blocks || self.state == SyncState::NewBlocks || self.state == SyncState::Idle)
|
||||
&& !self.peers.values().any(|p| p.asking != PeerAsking::Nothing && p.block_set != Some(BlockSet::OldBlocks) && p.can_sync()) {
|
||||
self.complete_sync(io);
|
||||
}
|
||||
@ -1040,11 +1107,9 @@ impl ChainSync {
|
||||
let higher_difficulty = peer_difficulty.map_or(true, |pd| pd > syncing_difficulty);
|
||||
if force || self.state == SyncState::NewBlocks || higher_difficulty || self.old_blocks.is_some() {
|
||||
match self.state {
|
||||
SyncState::Idle if self.snapshot_sync_enabled
|
||||
&& chain_info.best_block_number < peer_snapshot_number
|
||||
&& (peer_snapshot_number - chain_info.best_block_number) > SNAPSHOT_RESTORE_THRESHOLD => {
|
||||
trace!(target: "sync", "Starting snapshot sync: {} vs {}", peer_snapshot_number, chain_info.best_block_number);
|
||||
self.start_snapshot_sync(io, peer_id);
|
||||
SyncState::WaitingPeers => {
|
||||
trace!(target: "sync", "Checking snapshot sync: {} vs {}", peer_snapshot_number, chain_info.best_block_number);
|
||||
self.maybe_start_snapshot_sync(io);
|
||||
},
|
||||
SyncState::Idle | SyncState::Blocks | SyncState::NewBlocks => {
|
||||
if io.chain().queue_info().is_full() {
|
||||
@ -1070,6 +1135,13 @@ impl ChainSync {
|
||||
}
|
||||
},
|
||||
SyncState::SnapshotData => {
|
||||
if let RestorationStatus::Ongoing { state_chunks: _, block_chunks: _, state_chunks_done, block_chunks_done, } = io.snapshot_service().status() {
|
||||
if self.snapshot.done_chunks() - (state_chunks_done + block_chunks_done) as usize > MAX_SNAPSHOT_CHUNKS_DOWNLOAD_AHEAD {
|
||||
trace!(target: "sync", "Snapshot queue full, pausing sync");
|
||||
self.state = SyncState::SnapshotWaiting;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if peer_snapshot_hash.is_some() && peer_snapshot_hash == self.snapshot.snapshot_hash() {
|
||||
self.request_snapshot_data(io, peer_id);
|
||||
}
|
||||
@ -1253,7 +1325,7 @@ impl ChainSync {
|
||||
warn!(target:"sync", "Asking {:?} while requesting {:?}", peer.asking, asking);
|
||||
}
|
||||
peer.asking = asking;
|
||||
peer.ask_time = time::precise_time_s();
|
||||
peer.ask_time = time::precise_time_ns();
|
||||
let result = if packet_id >= ETH_PACKET_COUNT {
|
||||
sync.send_protocol(WARP_SYNC_PROTOCOL_ID, peer_id, packet_id, packet)
|
||||
} else {
|
||||
@ -1277,7 +1349,7 @@ impl ChainSync {
|
||||
/// Called when peer sends us new transactions
|
||||
fn on_peer_transactions(&mut self, io: &mut SyncIo, peer_id: PeerId, r: &UntrustedRlp) -> Result<(), PacketDecodeError> {
|
||||
// Accept transactions only when fully synced
|
||||
if !io.is_chain_queue_empty() || self.state != SyncState::Idle || self.state != SyncState::NewBlocks {
|
||||
if !io.is_chain_queue_empty() || (self.state != SyncState::Idle && self.state != SyncState::NewBlocks) {
|
||||
trace!(target: "sync", "{} Ignoring transactions while syncing", peer_id);
|
||||
return Ok(());
|
||||
}
|
||||
@ -1437,7 +1509,7 @@ impl ChainSync {
|
||||
}
|
||||
trace!(target: "sync", "{} -> GetNodeData: return {} entries", peer_id, added);
|
||||
let mut rlp = RlpStream::new_list(added);
|
||||
for d in data.into_iter() {
|
||||
for d in data {
|
||||
rlp.append(&d);
|
||||
}
|
||||
Ok(Some((NODE_DATA_PACKET, rlp)))
|
||||
@ -1590,17 +1662,18 @@ impl ChainSync {
|
||||
|
||||
#[cfg_attr(feature="dev", allow(match_same_arms))]
|
||||
pub fn maintain_peers(&mut self, io: &mut SyncIo) {
|
||||
let tick = time::precise_time_s();
|
||||
let tick = time::precise_time_ns();
|
||||
let mut aborting = Vec::new();
|
||||
for (peer_id, peer) in &self.peers {
|
||||
let elapsed = (tick - peer.ask_time) / 1_000_000_000;
|
||||
let timeout = match peer.asking {
|
||||
PeerAsking::BlockHeaders => (tick - peer.ask_time) > HEADERS_TIMEOUT_SEC,
|
||||
PeerAsking::BlockBodies => (tick - peer.ask_time) > BODIES_TIMEOUT_SEC,
|
||||
PeerAsking::BlockReceipts => (tick - peer.ask_time) > RECEIPTS_TIMEOUT_SEC,
|
||||
PeerAsking::BlockHeaders => elapsed > HEADERS_TIMEOUT_SEC,
|
||||
PeerAsking::BlockBodies => elapsed > BODIES_TIMEOUT_SEC,
|
||||
PeerAsking::BlockReceipts => elapsed > RECEIPTS_TIMEOUT_SEC,
|
||||
PeerAsking::Nothing => false,
|
||||
PeerAsking::ForkHeader => (tick - peer.ask_time) > FORK_HEADER_TIMEOUT_SEC,
|
||||
PeerAsking::SnapshotManifest => (tick - peer.ask_time) > SNAPSHOT_MANIFEST_TIMEOUT_SEC,
|
||||
PeerAsking::SnapshotData => (tick - peer.ask_time) > SNAPSHOT_DATA_TIMEOUT_SEC,
|
||||
PeerAsking::ForkHeader => elapsed > FORK_HEADER_TIMEOUT_SEC,
|
||||
PeerAsking::SnapshotManifest => elapsed > SNAPSHOT_MANIFEST_TIMEOUT_SEC,
|
||||
PeerAsking::SnapshotData => elapsed > SNAPSHOT_DATA_TIMEOUT_SEC,
|
||||
};
|
||||
if timeout {
|
||||
trace!(target:"sync", "Timeout {}", peer_id);
|
||||
@ -1611,16 +1684,42 @@ impl ChainSync {
|
||||
for p in aborting {
|
||||
self.on_peer_aborting(io, p);
|
||||
}
|
||||
|
||||
// Check for handshake timeouts
|
||||
for (peer, ask_time) in &self.handshaking_peers {
|
||||
let elapsed = (tick - ask_time) / 1_000_000_000;
|
||||
if elapsed > STATUS_TIMEOUT_SEC {
|
||||
trace!(target:"sync", "Status timeout {}", peer);
|
||||
io.disconnect_peer(*peer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_resume(&mut self, io: &mut SyncIo) {
|
||||
if self.state == SyncState::Waiting && !io.chain().queue_info().is_full() && self.state == SyncState::Waiting {
|
||||
self.state = SyncState::Blocks;
|
||||
self.continue_sync(io);
|
||||
} else if self.state == SyncState::SnapshotWaiting && io.snapshot_service().status() == RestorationStatus::Inactive {
|
||||
trace!(target:"sync", "Snapshot restoration is complete");
|
||||
self.restart(io);
|
||||
self.continue_sync(io);
|
||||
} else if self.state == SyncState::SnapshotWaiting {
|
||||
match io.snapshot_service().status() {
|
||||
RestorationStatus::Inactive => {
|
||||
trace!(target:"sync", "Snapshot restoration is complete");
|
||||
self.restart(io);
|
||||
self.continue_sync(io);
|
||||
},
|
||||
RestorationStatus::Ongoing { state_chunks: _, block_chunks: _, state_chunks_done, block_chunks_done, } => {
|
||||
if !self.snapshot.is_complete() && self.snapshot.done_chunks() - (state_chunks_done + block_chunks_done) as usize <= MAX_SNAPSHOT_CHUNKS_DOWNLOAD_AHEAD {
|
||||
trace!(target:"sync", "Resuming snapshot sync");
|
||||
self.state = SyncState::SnapshotData;
|
||||
self.continue_sync(io);
|
||||
}
|
||||
},
|
||||
RestorationStatus::Failed => {
|
||||
trace!(target: "sync", "Snapshot restoration aborted");
|
||||
self.state = SyncState::WaitingPeers;
|
||||
self.snapshot.clear();
|
||||
self.continue_sync(io);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1828,6 +1927,7 @@ impl ChainSync {
|
||||
|
||||
/// Maintain other peers. Send out any new blocks and transactions
|
||||
pub fn maintain_sync(&mut self, io: &mut SyncIo) {
|
||||
self.maybe_start_snapshot_sync(io);
|
||||
self.check_resume(io);
|
||||
}
|
||||
|
||||
@ -2050,7 +2150,7 @@ mod tests {
|
||||
asking: PeerAsking::Nothing,
|
||||
asking_blocks: Vec::new(),
|
||||
asking_hash: None,
|
||||
ask_time: 0f64,
|
||||
ask_time: 0,
|
||||
last_sent_transactions: HashSet::new(),
|
||||
expired: false,
|
||||
confirmation: super::ForkConfirmation::Confirmed,
|
||||
|
@ -113,7 +113,7 @@ impl Snapshot {
|
||||
}
|
||||
|
||||
pub fn done_chunks(&self) -> usize {
|
||||
self.total_chunks() - self.completed_chunks.len()
|
||||
self.completed_chunks.len()
|
||||
}
|
||||
|
||||
pub fn is_complete(&self) -> bool {
|
||||
@ -165,6 +165,7 @@ mod test {
|
||||
let mut snapshot = Snapshot::new();
|
||||
let (manifest, mhash, state_chunks, block_chunks) = test_manifest();
|
||||
snapshot.reset_to(&manifest, &mhash);
|
||||
assert_eq!(snapshot.done_chunks(), 0);
|
||||
assert!(snapshot.validate_chunk(&H256::random().to_vec()).is_err());
|
||||
|
||||
let requested: Vec<H256> = (0..40).map(|_| snapshot.needed_chunk().unwrap()).collect();
|
||||
@ -194,6 +195,8 @@ mod test {
|
||||
}
|
||||
|
||||
assert!(snapshot.is_complete());
|
||||
assert_eq!(snapshot.done_chunks(), 40);
|
||||
assert_eq!(snapshot.done_chunks(), snapshot.total_chunks());
|
||||
assert_eq!(snapshot.snapshot_hash(), Some(manifest.into_rlp().sha3()));
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ use util::*;
|
||||
use ethcore::client::{TestBlockChainClient, BlockChainClient, BlockID, EachBlockWith};
|
||||
use chain::{SyncState};
|
||||
use super::helpers::*;
|
||||
use SyncConfig;
|
||||
|
||||
#[test]
|
||||
fn two_peers() {
|
||||
@ -156,6 +157,10 @@ fn restart() {
|
||||
fn status_empty() {
|
||||
let net = TestNet::new(2);
|
||||
assert_eq!(net.peer(0).sync.read().status().state, SyncState::Idle);
|
||||
let mut config = SyncConfig::default();
|
||||
config.warp_sync = true;
|
||||
let net = TestNet::new_with_config(2, config);
|
||||
assert_eq!(net.peer(0).sync.read().status().state, SyncState::WaitingPeers);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -127,20 +127,24 @@ pub struct TestNet {
|
||||
|
||||
impl TestNet {
|
||||
pub fn new(n: usize) -> TestNet {
|
||||
Self::new_with_fork(n, None)
|
||||
Self::new_with_config(n, SyncConfig::default())
|
||||
}
|
||||
|
||||
pub fn new_with_fork(n: usize, fork: Option<(BlockNumber, H256)>) -> TestNet {
|
||||
let mut config = SyncConfig::default();
|
||||
config.fork_block = fork;
|
||||
Self::new_with_config(n, config)
|
||||
}
|
||||
|
||||
pub fn new_with_config(n: usize, config: SyncConfig) -> TestNet {
|
||||
let mut net = TestNet {
|
||||
peers: Vec::new(),
|
||||
started: false,
|
||||
};
|
||||
for _ in 0..n {
|
||||
let chain = TestBlockChainClient::new();
|
||||
let mut config = SyncConfig::default();
|
||||
config.fork_block = fork;
|
||||
let ss = Arc::new(TestSnapshotService::new());
|
||||
let sync = ChainSync::new(config, &chain);
|
||||
let sync = ChainSync::new(config.clone(), &chain);
|
||||
net.peers.push(TestPeer {
|
||||
sync: RwLock::new(sync),
|
||||
snapshot_service: ss,
|
||||
@ -164,7 +168,7 @@ impl TestNet {
|
||||
for client in 0..self.peers.len() {
|
||||
if peer != client {
|
||||
let mut p = self.peers.get_mut(peer).unwrap();
|
||||
p.sync.write().restart(&mut TestIo::new(&mut p.chain, &p.snapshot_service, &mut p.queue, Some(client as PeerId)));
|
||||
p.sync.write().update_targets(&mut p.chain);
|
||||
p.sync.write().on_peer_connected(&mut TestIo::new(&mut p.chain, &p.snapshot_service, &mut p.queue, Some(client as PeerId)), client as PeerId);
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ use ethcore::snapshot::{SnapshotService, ManifestData, RestorationStatus};
|
||||
use ethcore::header::BlockNumber;
|
||||
use ethcore::client::{EachBlockWith};
|
||||
use super::helpers::*;
|
||||
use SyncConfig;
|
||||
|
||||
pub struct TestSnapshotService {
|
||||
manifest: Option<ManifestData>,
|
||||
@ -122,11 +123,16 @@ impl SnapshotService for TestSnapshotService {
|
||||
#[test]
|
||||
fn snapshot_sync() {
|
||||
::env_logger::init().ok();
|
||||
let mut net = TestNet::new(2);
|
||||
net.peer_mut(0).snapshot_service = Arc::new(TestSnapshotService::new_with_snapshot(16, H256::new(), 500000));
|
||||
net.peer_mut(0).chain.add_blocks(1, EachBlockWith::Nothing);
|
||||
net.sync_steps(19); // status + manifest + chunks
|
||||
assert_eq!(net.peer(1).snapshot_service.state_restoration_chunks.lock().len(), net.peer(0).snapshot_service.manifest.as_ref().unwrap().state_hashes.len());
|
||||
assert_eq!(net.peer(1).snapshot_service.block_restoration_chunks.lock().len(), net.peer(0).snapshot_service.manifest.as_ref().unwrap().block_hashes.len());
|
||||
let mut config = SyncConfig::default();
|
||||
config.warp_sync = true;
|
||||
let mut net = TestNet::new_with_config(5, config);
|
||||
let snapshot_service = Arc::new(TestSnapshotService::new_with_snapshot(16, H256::new(), 500000));
|
||||
for i in 0..4 {
|
||||
net.peer_mut(i).snapshot_service = snapshot_service.clone();
|
||||
net.peer_mut(i).chain.add_blocks(1, EachBlockWith::Nothing);
|
||||
}
|
||||
net.sync_steps(50);
|
||||
assert_eq!(net.peer(4).snapshot_service.state_restoration_chunks.lock().len(), net.peer(0).snapshot_service.manifest.as_ref().unwrap().state_hashes.len());
|
||||
assert_eq!(net.peer(4).snapshot_service.block_restoration_chunks.lock().len(), net.peer(0).snapshot_service.manifest.as_ref().unwrap().block_hashes.len());
|
||||
}
|
||||
|
||||
|
2
test.sh
2
test.sh
@ -2,7 +2,7 @@
|
||||
# Running Parity Full Test Sute
|
||||
|
||||
FEATURES="json-tests"
|
||||
OPTIONS="--release"
|
||||
OPTIONS="--verbose --release"
|
||||
|
||||
case $1 in
|
||||
--no-json)
|
||||
|
@ -7,7 +7,7 @@ version = "1.4.0"
|
||||
authors = ["Ethcore <admin@ethcore.io>"]
|
||||
|
||||
[dependencies]
|
||||
mio = { git = "https://github.com/ethcore/mio", branch = "v0.5.x" }
|
||||
mio = { git = "https://github.com/carllerche/mio" }
|
||||
crossbeam = "0.2"
|
||||
parking_lot = "0.3"
|
||||
log = "0.3"
|
||||
|
@ -65,7 +65,8 @@ mod service;
|
||||
mod worker;
|
||||
mod panics;
|
||||
|
||||
use mio::{EventLoop, Token};
|
||||
use mio::{Token};
|
||||
use mio::deprecated::{EventLoop, NotifyError};
|
||||
use std::fmt;
|
||||
|
||||
pub use worker::LOCAL_STACK_SIZE;
|
||||
@ -96,8 +97,8 @@ impl From<::std::io::Error> for IoError {
|
||||
}
|
||||
}
|
||||
|
||||
impl<Message> From<::mio::NotifyError<service::IoMessage<Message>>> for IoError where Message: Send + Clone {
|
||||
fn from(_err: ::mio::NotifyError<service::IoMessage<Message>>) -> IoError {
|
||||
impl<Message> From<NotifyError<service::IoMessage<Message>>> for IoError where Message: Send + Clone {
|
||||
fn from(_err: NotifyError<service::IoMessage<Message>>) -> IoError {
|
||||
IoError::Mio(::std::io::Error::new(::std::io::ErrorKind::ConnectionAborted, "Network IO notification error"))
|
||||
}
|
||||
}
|
||||
|
@ -18,13 +18,16 @@ use std::sync::{Arc, Weak};
|
||||
use std::thread::{self, JoinHandle};
|
||||
use std::collections::HashMap;
|
||||
use mio::*;
|
||||
use mio::timer::{Timeout};
|
||||
use mio::deprecated::{EventLoop, Handler, Sender, EventLoopBuilder};
|
||||
use crossbeam::sync::chase_lev;
|
||||
use slab::Slab;
|
||||
use {IoError, IoHandler};
|
||||
use worker::{Worker, Work, WorkType};
|
||||
use panics::*;
|
||||
use parking_lot::{RwLock};
|
||||
use parking_lot::{RwLock, Mutex};
|
||||
use std::sync::{Condvar as SCondvar, Mutex as SMutex};
|
||||
use std::time::Duration;
|
||||
|
||||
/// Timer ID
|
||||
pub type TimerToken = usize;
|
||||
@ -223,9 +226,9 @@ impl<Message> Handler for IoManager<Message> where Message: Send + Clone + Sync
|
||||
type Timeout = Token;
|
||||
type Message = IoMessage<Message>;
|
||||
|
||||
fn ready(&mut self, _event_loop: &mut EventLoop<Self>, token: Token, events: EventSet) {
|
||||
let handler_index = token.as_usize() / TOKENS_PER_HANDLER;
|
||||
let token_id = token.as_usize() % TOKENS_PER_HANDLER;
|
||||
fn ready(&mut self, _event_loop: &mut EventLoop<Self>, token: Token, events: Ready) {
|
||||
let handler_index = token.0 / TOKENS_PER_HANDLER;
|
||||
let token_id = token.0 % TOKENS_PER_HANDLER;
|
||||
if let Some(handler) = self.handlers.read().get(handler_index) {
|
||||
if events.is_hup() {
|
||||
self.worker_channel.push(Work { work_type: WorkType::Hup, token: token_id, handler: handler.clone(), handler_id: handler_index });
|
||||
@ -243,15 +246,15 @@ impl<Message> Handler for IoManager<Message> where Message: Send + Clone + Sync
|
||||
}
|
||||
|
||||
fn timeout(&mut self, event_loop: &mut EventLoop<Self>, token: Token) {
|
||||
let handler_index = token.as_usize() / TOKENS_PER_HANDLER;
|
||||
let token_id = token.as_usize() % TOKENS_PER_HANDLER;
|
||||
let handler_index = token.0 / TOKENS_PER_HANDLER;
|
||||
let token_id = token.0 % TOKENS_PER_HANDLER;
|
||||
if let Some(handler) = self.handlers.read().get(handler_index) {
|
||||
if let Some(timer) = self.timers.read().get(&token.as_usize()) {
|
||||
if let Some(timer) = self.timers.read().get(&token.0) {
|
||||
if timer.once {
|
||||
self.timers.write().remove(&token_id);
|
||||
event_loop.clear_timeout(timer.timeout);
|
||||
event_loop.clear_timeout(&timer.timeout);
|
||||
} else {
|
||||
event_loop.timeout_ms(token, timer.delay).expect("Error re-registering user timer");
|
||||
event_loop.timeout(token, Duration::from_millis(timer.delay)).expect("Error re-registering user timer");
|
||||
}
|
||||
self.worker_channel.push(Work { work_type: WorkType::Timeout, token: token_id, handler: handler.clone(), handler_id: handler_index });
|
||||
self.work_ready.notify_all();
|
||||
@ -277,18 +280,18 @@ impl<Message> Handler for IoManager<Message> where Message: Send + Clone + Sync
|
||||
let to_remove: Vec<_> = timers.keys().cloned().filter(|timer_id| timer_id / TOKENS_PER_HANDLER == handler_id).collect();
|
||||
for timer_id in to_remove {
|
||||
let timer = timers.remove(&timer_id).expect("to_remove only contains keys from timers; qed");
|
||||
event_loop.clear_timeout(timer.timeout);
|
||||
event_loop.clear_timeout(&timer.timeout);
|
||||
}
|
||||
},
|
||||
IoMessage::AddTimer { handler_id, token, delay, once } => {
|
||||
let timer_id = token + handler_id * TOKENS_PER_HANDLER;
|
||||
let timeout = event_loop.timeout_ms(Token(timer_id), delay).expect("Error registering user timer");
|
||||
let timeout = event_loop.timeout(Token(timer_id), Duration::from_millis(delay)).expect("Error registering user timer");
|
||||
self.timers.write().insert(timer_id, UserTimer { delay: delay, timeout: timeout, once: once });
|
||||
},
|
||||
IoMessage::RemoveTimer { handler_id, token } => {
|
||||
let timer_id = token + handler_id * TOKENS_PER_HANDLER;
|
||||
if let Some(timer) = self.timers.write().remove(&timer_id) {
|
||||
event_loop.clear_timeout(timer.timeout);
|
||||
event_loop.clear_timeout(&timer.timeout);
|
||||
}
|
||||
},
|
||||
IoMessage::RegisterStream { handler_id, token } => {
|
||||
@ -302,7 +305,7 @@ impl<Message> Handler for IoManager<Message> where Message: Send + Clone + Sync
|
||||
// unregister a timer associated with the token (if any)
|
||||
let timer_id = token + handler_id * TOKENS_PER_HANDLER;
|
||||
if let Some(timer) = self.timers.write().remove(&timer_id) {
|
||||
event_loop.clear_timeout(timer.timeout);
|
||||
event_loop.clear_timeout(&timer.timeout);
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -391,7 +394,7 @@ impl<Message> IoChannel<Message> where Message: Send + Clone + Sync + 'static {
|
||||
pub struct IoService<Message> where Message: Send + Sync + Clone + 'static {
|
||||
panic_handler: Arc<PanicHandler>,
|
||||
thread: Option<JoinHandle<()>>,
|
||||
host_channel: Sender<IoMessage<Message>>,
|
||||
host_channel: Mutex<Sender<IoMessage<Message>>>,
|
||||
handlers: Arc<RwLock<Slab<Arc<IoHandler<Message>>, HandlerId>>>,
|
||||
}
|
||||
|
||||
@ -405,9 +408,9 @@ impl<Message> IoService<Message> where Message: Send + Sync + Clone + 'static {
|
||||
/// Starts IO event loop
|
||||
pub fn start() -> Result<IoService<Message>, IoError> {
|
||||
let panic_handler = PanicHandler::new_in_arc();
|
||||
let mut config = EventLoopConfig::new();
|
||||
let mut config = EventLoopBuilder::new();
|
||||
config.messages_per_tick(1024);
|
||||
let mut event_loop = EventLoop::configured(config).expect("Error creating event loop");
|
||||
let mut event_loop = config.build().expect("Error creating event loop");
|
||||
let channel = event_loop.channel();
|
||||
let panic = panic_handler.clone();
|
||||
let handlers = Arc::new(RwLock::new(Slab::new(MAX_HANDLERS)));
|
||||
@ -421,14 +424,14 @@ impl<Message> IoService<Message> where Message: Send + Sync + Clone + 'static {
|
||||
Ok(IoService {
|
||||
panic_handler: panic_handler,
|
||||
thread: Some(thread),
|
||||
host_channel: channel,
|
||||
host_channel: Mutex::new(channel),
|
||||
handlers: handlers,
|
||||
})
|
||||
}
|
||||
|
||||
/// Regiter an IO handler with the event loop.
|
||||
pub fn register_handler(&self, handler: Arc<IoHandler<Message>+Send>) -> Result<(), IoError> {
|
||||
try!(self.host_channel.send(IoMessage::AddHandler {
|
||||
try!(self.host_channel.lock().send(IoMessage::AddHandler {
|
||||
handler: handler,
|
||||
}));
|
||||
Ok(())
|
||||
@ -436,20 +439,20 @@ impl<Message> IoService<Message> where Message: Send + Sync + Clone + 'static {
|
||||
|
||||
/// Send a message over the network. Normaly `HostIo::send` should be used. This can be used from non-io threads.
|
||||
pub fn send_message(&self, message: Message) -> Result<(), IoError> {
|
||||
try!(self.host_channel.send(IoMessage::UserMessage(message)));
|
||||
try!(self.host_channel.lock().send(IoMessage::UserMessage(message)));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Create a new message channel
|
||||
pub fn channel(&self) -> IoChannel<Message> {
|
||||
IoChannel::new(self.host_channel.clone(), Arc::downgrade(&self.handlers))
|
||||
IoChannel::new(self.host_channel.lock().clone(), Arc::downgrade(&self.handlers))
|
||||
}
|
||||
}
|
||||
|
||||
impl<Message> Drop for IoService<Message> where Message: Send + Sync + Clone {
|
||||
fn drop(&mut self) {
|
||||
trace!(target: "shutdown", "[IoService] Closing...");
|
||||
self.host_channel.send(IoMessage::Shutdown).unwrap_or_else(|e| warn!("Error on IO service shutdown: {:?}", e));
|
||||
self.host_channel.lock().send(IoMessage::Shutdown).unwrap_or_else(|e| warn!("Error on IO service shutdown: {:?}", e));
|
||||
self.thread.take().unwrap().join().ok();
|
||||
trace!(target: "shutdown", "[IoService] Closed.");
|
||||
}
|
||||
|
@ -8,7 +8,8 @@ authors = ["Ethcore <admin@ethcore.io>"]
|
||||
|
||||
[dependencies]
|
||||
log = "0.3"
|
||||
mio = { git = "https://github.com/ethcore/mio", branch = "v0.5.x" }
|
||||
mio = { git = "https://github.com/carllerche/mio" }
|
||||
bytes = "0.3.0"
|
||||
rand = "0.3.12"
|
||||
time = "0.1.34"
|
||||
tiny-keccak = "1.0"
|
||||
|
@ -18,7 +18,8 @@ use std::sync::Arc;
|
||||
use std::collections::VecDeque;
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::atomic::{AtomicBool, Ordering as AtomicOrdering};
|
||||
use mio::{Handler, Token, EventSet, EventLoop, PollOpt, TryRead, TryWrite};
|
||||
use mio::{Token, Ready, PollOpt};
|
||||
use mio::deprecated::{Handler, EventLoop, TryRead, TryWrite};
|
||||
use mio::tcp::*;
|
||||
use util::hash::*;
|
||||
use util::sha3::*;
|
||||
@ -34,6 +35,7 @@ use rcrypto::aessafe::*;
|
||||
use rcrypto::symmetriccipher::*;
|
||||
use rcrypto::buffer::*;
|
||||
use tiny_keccak::Keccak;
|
||||
use bytes::{Buf, MutBuf};
|
||||
use crypto;
|
||||
|
||||
const ENCRYPTED_HEADER_LEN: usize = 32;
|
||||
@ -57,7 +59,7 @@ pub struct GenericConnection<Socket: GenericSocket> {
|
||||
/// Send out packets FIFO
|
||||
send_queue: VecDeque<Cursor<Bytes>>,
|
||||
/// Event flags this connection expects
|
||||
interest: EventSet,
|
||||
interest: Ready,
|
||||
/// Shared network statistics
|
||||
stats: Arc<NetworkStats>,
|
||||
/// Registered flag
|
||||
@ -81,8 +83,9 @@ impl<Socket: GenericSocket> GenericConnection<Socket> {
|
||||
let sock_ref = <Socket as Read>::by_ref(&mut self.socket);
|
||||
loop {
|
||||
let max = self.rec_size - self.rec_buf.len();
|
||||
match sock_ref.take(max as u64).try_read_buf(&mut self.rec_buf) {
|
||||
match sock_ref.take(max as u64).try_read(unsafe { self.rec_buf.mut_bytes() }) {
|
||||
Ok(Some(size)) if size != 0 => {
|
||||
unsafe { self.rec_buf.advance(size); }
|
||||
self.stats.inc_recv(size);
|
||||
trace!(target:"network", "{}: Read {} of {} bytes", self.token, self.rec_buf.len(), self.rec_size);
|
||||
if self.rec_size != 0 && self.rec_buf.len() == self.rec_size {
|
||||
@ -109,7 +112,7 @@ impl<Socket: GenericSocket> GenericConnection<Socket> {
|
||||
trace!(target:"network", "{}: Sending {} bytes", self.token, data.len());
|
||||
self.send_queue.push_back(Cursor::new(data));
|
||||
if !self.interest.is_writable() {
|
||||
self.interest.insert(EventSet::writable());
|
||||
self.interest.insert(Ready::writable());
|
||||
}
|
||||
io.update_registration(self.token).ok();
|
||||
}
|
||||
@ -128,16 +131,19 @@ impl<Socket: GenericSocket> GenericConnection<Socket> {
|
||||
{
|
||||
let buf = self.send_queue.front_mut().unwrap();
|
||||
let send_size = buf.get_ref().len();
|
||||
if (buf.position() as usize) >= send_size {
|
||||
let pos = buf.position() as usize;
|
||||
if (pos as usize) >= send_size {
|
||||
warn!(target:"net", "Unexpected connection data");
|
||||
return Ok(WriteStatus::Complete)
|
||||
}
|
||||
match self.socket.try_write_buf(buf) {
|
||||
Ok(Some(size)) if (buf.position() as usize) < send_size => {
|
||||
let buf = buf as &mut Buf;
|
||||
match self.socket.try_write(buf.bytes()) {
|
||||
Ok(Some(size)) if (pos + size) < send_size => {
|
||||
buf.advance(size);
|
||||
self.stats.inc_send(size);
|
||||
Ok(WriteStatus::Ongoing)
|
||||
},
|
||||
Ok(Some(size)) if (buf.position() as usize) == send_size => {
|
||||
Ok(Some(size)) if (pos + size) == send_size => {
|
||||
self.stats.inc_send(size);
|
||||
trace!(target:"network", "{}: Wrote {} bytes", self.token, send_size);
|
||||
Ok(WriteStatus::Complete)
|
||||
@ -151,7 +157,7 @@ impl<Socket: GenericSocket> GenericConnection<Socket> {
|
||||
self.send_queue.pop_front();
|
||||
}
|
||||
if self.send_queue.is_empty() {
|
||||
self.interest.remove(EventSet::writable());
|
||||
self.interest.remove(Ready::writable());
|
||||
try!(io.update_registration(self.token));
|
||||
}
|
||||
Ok(r)
|
||||
@ -171,7 +177,7 @@ impl Connection {
|
||||
send_queue: VecDeque::new(),
|
||||
rec_buf: Bytes::new(),
|
||||
rec_size: 0,
|
||||
interest: EventSet::hup() | EventSet::readable(),
|
||||
interest: Ready::hup() | Ready::readable(),
|
||||
stats: stats,
|
||||
registered: AtomicBool::new(false),
|
||||
}
|
||||
@ -205,7 +211,7 @@ impl Connection {
|
||||
rec_buf: Vec::new(),
|
||||
rec_size: 0,
|
||||
send_queue: self.send_queue.clone(),
|
||||
interest: EventSet::hup(),
|
||||
interest: Ready::hup(),
|
||||
stats: self.stats.clone(),
|
||||
registered: AtomicBool::new(false),
|
||||
})
|
||||
@ -499,7 +505,7 @@ mod tests {
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use super::super::stats::*;
|
||||
use std::io::{Read, Write, Error, Cursor, ErrorKind};
|
||||
use mio::{EventSet};
|
||||
use mio::{Ready};
|
||||
use std::collections::VecDeque;
|
||||
use util::bytes::*;
|
||||
use devtools::*;
|
||||
@ -545,7 +551,7 @@ mod tests {
|
||||
send_queue: VecDeque::new(),
|
||||
rec_buf: Bytes::new(),
|
||||
rec_size: 0,
|
||||
interest: EventSet::hup() | EventSet::readable(),
|
||||
interest: Ready::hup() | Ready::readable(),
|
||||
stats: Arc::<NetworkStats>::new(NetworkStats::new()),
|
||||
registered: AtomicBool::new(false),
|
||||
}
|
||||
@ -568,7 +574,7 @@ mod tests {
|
||||
send_queue: VecDeque::new(),
|
||||
rec_buf: Bytes::new(),
|
||||
rec_size: 0,
|
||||
interest: EventSet::hup() | EventSet::readable(),
|
||||
interest: Ready::hup() | Ready::readable(),
|
||||
stats: Arc::<NetworkStats>::new(NetworkStats::new()),
|
||||
registered: AtomicBool::new(false),
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ use std::collections::{HashSet, HashMap, BTreeMap, VecDeque};
|
||||
use std::mem;
|
||||
use std::default::Default;
|
||||
use mio::*;
|
||||
use mio::deprecated::{Handler, EventLoop};
|
||||
use mio::udp::*;
|
||||
use util::sha3::*;
|
||||
use time;
|
||||
@ -57,6 +58,7 @@ pub struct NodeEntry {
|
||||
|
||||
pub struct BucketEntry {
|
||||
pub address: NodeEntry,
|
||||
pub id_hash: H256,
|
||||
pub timeout: Option<u64>,
|
||||
}
|
||||
|
||||
@ -85,6 +87,7 @@ struct Datagramm {
|
||||
|
||||
pub struct Discovery {
|
||||
id: NodeId,
|
||||
id_hash: H256,
|
||||
secret: Secret,
|
||||
public_endpoint: NodeEndpoint,
|
||||
udp_socket: UdpSocket,
|
||||
@ -106,9 +109,10 @@ pub struct TableUpdates {
|
||||
|
||||
impl Discovery {
|
||||
pub fn new(key: &KeyPair, listen: SocketAddr, public: NodeEndpoint, token: StreamToken, allow_ips: AllowIP) -> Discovery {
|
||||
let socket = UdpSocket::bound(&listen).expect("Error binding UDP socket");
|
||||
let socket = UdpSocket::bind(&listen).expect("Error binding UDP socket");
|
||||
Discovery {
|
||||
id: key.public().clone(),
|
||||
id_hash: key.public().sha3(),
|
||||
secret: key.secret().clone(),
|
||||
public_endpoint: public,
|
||||
token: token,
|
||||
@ -150,8 +154,9 @@ impl Discovery {
|
||||
|
||||
fn update_node(&mut self, e: NodeEntry) {
|
||||
trace!(target: "discovery", "Inserting {:?}", &e);
|
||||
let id_hash = e.id.sha3();
|
||||
let ping = {
|
||||
let mut bucket = self.node_buckets.get_mut(Discovery::distance(&self.id, &e.id) as usize).unwrap();
|
||||
let mut bucket = self.node_buckets.get_mut(Discovery::distance(&self.id_hash, &id_hash) as usize).unwrap();
|
||||
let updated = if let Some(node) = bucket.nodes.iter_mut().find(|n| n.address.id == e.id) {
|
||||
node.address = e.clone();
|
||||
node.timeout = None;
|
||||
@ -159,7 +164,7 @@ impl Discovery {
|
||||
} else { false };
|
||||
|
||||
if !updated {
|
||||
bucket.nodes.push_front(BucketEntry { address: e, timeout: None });
|
||||
bucket.nodes.push_front(BucketEntry { address: e, timeout: None, id_hash: id_hash, });
|
||||
}
|
||||
|
||||
if bucket.nodes.len() > BUCKET_SIZE {
|
||||
@ -174,7 +179,7 @@ impl Discovery {
|
||||
}
|
||||
|
||||
fn clear_ping(&mut self, id: &NodeId) {
|
||||
let mut bucket = self.node_buckets.get_mut(Discovery::distance(&self.id, id) as usize).unwrap();
|
||||
let mut bucket = self.node_buckets.get_mut(Discovery::distance(&self.id_hash, &id.sha3()) as usize).unwrap();
|
||||
if let Some(node) = bucket.nodes.iter_mut().find(|n| &n.address.id == id) {
|
||||
node.timeout = None;
|
||||
}
|
||||
@ -224,8 +229,8 @@ impl Discovery {
|
||||
self.discovery_round += 1;
|
||||
}
|
||||
|
||||
fn distance(a: &NodeId, b: &NodeId) -> u32 {
|
||||
let d = a.sha3() ^ b.sha3();
|
||||
fn distance(a: &H256, b: &H256) -> u32 {
|
||||
let d = *a ^ *b;
|
||||
let mut ret:u32 = 0;
|
||||
for i in 0..32 {
|
||||
let mut v: u8 = d[i];
|
||||
@ -279,11 +284,12 @@ impl Discovery {
|
||||
fn nearest_node_entries(target: &NodeId, buckets: &[NodeBucket]) -> Vec<NodeEntry> {
|
||||
let mut found: BTreeMap<u32, Vec<&NodeEntry>> = BTreeMap::new();
|
||||
let mut count = 0;
|
||||
let target_hash = target.sha3();
|
||||
|
||||
// Sort nodes by distance to target
|
||||
for bucket in buckets {
|
||||
for node in &bucket.nodes {
|
||||
let distance = Discovery::distance(target, &node.address.id);
|
||||
let distance = Discovery::distance(&target_hash, &node.id_hash);
|
||||
found.entry(distance).or_insert_with(Vec::new).push(&node.address);
|
||||
if count == BUCKET_SIZE {
|
||||
// delete the most distant element
|
||||
@ -527,15 +533,15 @@ impl Discovery {
|
||||
}
|
||||
|
||||
pub fn register_socket<Host:Handler>(&self, event_loop: &mut EventLoop<Host>) -> Result<(), NetworkError> {
|
||||
event_loop.register(&self.udp_socket, Token(self.token), EventSet::all(), PollOpt::edge()).expect("Error registering UDP socket");
|
||||
event_loop.register(&self.udp_socket, Token(self.token), Ready::all(), PollOpt::edge()).expect("Error registering UDP socket");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update_registration<Host:Handler>(&self, event_loop: &mut EventLoop<Host>) -> Result<(), NetworkError> {
|
||||
let registration = if !self.send_queue.is_empty() {
|
||||
EventSet::readable() | EventSet::writable()
|
||||
Ready::readable() | Ready::writable()
|
||||
} else {
|
||||
EventSet::readable()
|
||||
Ready::readable()
|
||||
};
|
||||
event_loop.reregister(&self.udp_socket, Token(self.token), registration, PollOpt::edge()).expect("Error reregistering UDP socket");
|
||||
Ok(())
|
||||
@ -546,6 +552,7 @@ impl Discovery {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use util::hash::*;
|
||||
use util::sha3::*;
|
||||
use std::net::*;
|
||||
use node_table::*;
|
||||
use std::str::FromStr;
|
||||
@ -626,7 +633,8 @@ mod tests {
|
||||
for _ in 0..(16 + 10) {
|
||||
buckets[0].nodes.push_back(BucketEntry {
|
||||
address: NodeEntry { id: NodeId::new(), endpoint: ep.clone() },
|
||||
timeout: None
|
||||
timeout: None,
|
||||
id_hash: NodeId::new().sha3(),
|
||||
});
|
||||
}
|
||||
let nearest = Discovery::nearest_node_entries(&NodeId::new(), &buckets);
|
||||
|
@ -26,6 +26,7 @@ use std::io::{Read, Write};
|
||||
use std::fs;
|
||||
use ethkey::{KeyPair, Secret, Random, Generator};
|
||||
use mio::*;
|
||||
use mio::deprecated::{EventLoop};
|
||||
use mio::tcp::*;
|
||||
use util::hash::*;
|
||||
use util::Hashable;
|
||||
@ -61,7 +62,7 @@ const SYS_TIMER: usize = LAST_SESSION + 1;
|
||||
|
||||
// Timeouts
|
||||
const MAINTENANCE_TIMEOUT: u64 = 1000;
|
||||
const DISCOVERY_REFRESH_TIMEOUT: u64 = 7200;
|
||||
const DISCOVERY_REFRESH_TIMEOUT: u64 = 60_000;
|
||||
const DISCOVERY_ROUND_TIMEOUT: u64 = 300;
|
||||
const NODE_TABLE_TIMEOUT: u64 = 300_000;
|
||||
|
||||
@ -744,10 +745,9 @@ impl Host {
|
||||
trace!(target: "network", "Accepting incoming connection");
|
||||
loop {
|
||||
let socket = match self.tcp_listener.lock().accept() {
|
||||
Ok(None) => break,
|
||||
Ok(Some((sock, _addr))) => sock,
|
||||
Ok((sock, _addr)) => sock,
|
||||
Err(e) => {
|
||||
warn!("Error accepting connection: {:?}", e);
|
||||
debug!(target: "network", "Error accepting connection: {:?}", e);
|
||||
break
|
||||
},
|
||||
};
|
||||
@ -801,29 +801,31 @@ impl Host {
|
||||
},
|
||||
Ok(SessionData::Ready) => {
|
||||
self.num_sessions.fetch_add(1, AtomicOrdering::SeqCst);
|
||||
if !s.info.originated {
|
||||
let session_count = self.session_count();
|
||||
let (max_peers, reserved_only) = {
|
||||
let info = self.info.read();
|
||||
let mut max_peers = info.config.max_peers;
|
||||
for cap in s.info.capabilities.iter() {
|
||||
if let Some(num) = info.config.reserved_protocols.get(&cap.protocol) {
|
||||
max_peers += *num;
|
||||
break;
|
||||
}
|
||||
}
|
||||
(max_peers, info.config.non_reserved_mode == NonReservedPeerMode::Deny)
|
||||
};
|
||||
|
||||
if session_count >= max_peers as usize || reserved_only {
|
||||
// only proceed if the connecting peer is reserved.
|
||||
if !self.reserved_nodes.read().contains(s.id().unwrap()) {
|
||||
s.disconnect(io, DisconnectReason::TooManyPeers);
|
||||
return;
|
||||
let session_count = self.session_count();
|
||||
let (min_peers, max_peers, reserved_only) = {
|
||||
let info = self.info.read();
|
||||
let mut max_peers = info.config.max_peers;
|
||||
for cap in s.info.capabilities.iter() {
|
||||
if let Some(num) = info.config.reserved_protocols.get(&cap.protocol) {
|
||||
max_peers += *num;
|
||||
break;
|
||||
}
|
||||
}
|
||||
(info.config.min_peers as usize, max_peers as usize, info.config.non_reserved_mode == NonReservedPeerMode::Deny)
|
||||
};
|
||||
|
||||
// Add it no node table
|
||||
if reserved_only ||
|
||||
(s.info.originated && session_count >= min_peers) ||
|
||||
(!s.info.originated && session_count >= max_peers) {
|
||||
// only proceed if the connecting peer is reserved.
|
||||
if !self.reserved_nodes.read().contains(s.id().unwrap()) {
|
||||
s.disconnect(io, DisconnectReason::TooManyPeers);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Add it to the node table
|
||||
if !s.info.originated {
|
||||
if let Ok(address) = s.remote_addr() {
|
||||
let entry = NodeEntry { id: s.id().unwrap().clone(), endpoint: NodeEndpoint { address: address, udp_port: address.port() } };
|
||||
self.nodes.write().add_node(Node::new(entry.id.clone(), entry.endpoint.clone()));
|
||||
@ -1101,7 +1103,7 @@ impl IoHandler<NetworkIoMessage> for Host {
|
||||
}
|
||||
}
|
||||
DISCOVERY => self.discovery.lock().as_ref().unwrap().register_socket(event_loop).expect("Error registering discovery socket"),
|
||||
TCP_ACCEPT => event_loop.register(&*self.tcp_listener.lock(), Token(TCP_ACCEPT), EventSet::all(), PollOpt::edge()).expect("Error registering stream"),
|
||||
TCP_ACCEPT => event_loop.register(&*self.tcp_listener.lock(), Token(TCP_ACCEPT), Ready::all(), PollOpt::edge()).expect("Error registering stream"),
|
||||
_ => warn!("Unexpected stream registration")
|
||||
}
|
||||
}
|
||||
@ -1129,7 +1131,7 @@ impl IoHandler<NetworkIoMessage> for Host {
|
||||
}
|
||||
}
|
||||
DISCOVERY => self.discovery.lock().as_ref().unwrap().update_registration(event_loop).expect("Error reregistering discovery socket"),
|
||||
TCP_ACCEPT => event_loop.reregister(&*self.tcp_listener.lock(), Token(TCP_ACCEPT), EventSet::all(), PollOpt::edge()).expect("Error reregistering stream"),
|
||||
TCP_ACCEPT => event_loop.reregister(&*self.tcp_listener.lock(), Token(TCP_ACCEPT), Ready::all(), PollOpt::edge()).expect("Error reregistering stream"),
|
||||
_ => warn!("Unexpected stream update")
|
||||
}
|
||||
}
|
||||
|
@ -70,6 +70,7 @@ extern crate slab;
|
||||
extern crate ethkey;
|
||||
extern crate ethcrypto as crypto;
|
||||
extern crate rlp;
|
||||
extern crate bytes;
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
@ -19,6 +19,7 @@ use std::net::SocketAddr;
|
||||
use std::cmp::Ordering;
|
||||
use std::sync::*;
|
||||
use mio::*;
|
||||
use mio::deprecated::{Handler, EventLoop};
|
||||
use mio::tcp::*;
|
||||
use util::hash::*;
|
||||
use rlp::*;
|
||||
|
@ -133,7 +133,7 @@ impl<'db> TrieDB<'db> {
|
||||
}
|
||||
|
||||
/// Get the data of the root node.
|
||||
fn root_data<'a, R: 'a + Recorder>(&self, r: &'a mut R) -> super::Result<DBValue> {
|
||||
fn root_data<R: Recorder>(&self, r: &mut R) -> super::Result<DBValue> {
|
||||
self.db.get(self.root).ok_or_else(|| Box::new(TrieError::InvalidStateRoot(*self.root)))
|
||||
.map(|node| { r.record(self.root, &*node, 0); node })
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user