Merge branch 'master' into secretstore_kovan
This commit is contained in:
commit
12e9c1cebc
26
Cargo.lock
generated
26
Cargo.lock
generated
@ -38,7 +38,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.3.23"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"nodrop 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -430,11 +430,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "eth-secp256k1"
|
||||
version = "0.5.6"
|
||||
source = "git+https://github.com/paritytech/rust-secp256k1#b6b67055edc929057e97d64f036c78ad91f58a7f"
|
||||
version = "0.5.7"
|
||||
source = "git+https://github.com/paritytech/rust-secp256k1#6370d63adf4e8c91e2eae9225eef4b4e0294c5d0"
|
||||
dependencies = [
|
||||
"arrayvec 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"arrayvec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cc 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -620,7 +620,7 @@ name = "ethcore-logger"
|
||||
version = "1.9.0"
|
||||
dependencies = [
|
||||
"ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"arrayvec 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"arrayvec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"isatty 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -726,7 +726,7 @@ dependencies = [
|
||||
"clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"elastic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"eth-secp256k1 0.5.6 (git+https://github.com/paritytech/rust-secp256k1)",
|
||||
"eth-secp256k1 0.5.7 (git+https://github.com/paritytech/rust-secp256k1)",
|
||||
"ethcore-bigint 0.2.1",
|
||||
"ethcore-bytes 0.1.0",
|
||||
"ethcore-logger 1.9.0",
|
||||
@ -756,7 +756,7 @@ dependencies = [
|
||||
name = "ethcrypto"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"eth-secp256k1 0.5.6 (git+https://github.com/paritytech/rust-secp256k1)",
|
||||
"eth-secp256k1 0.5.7 (git+https://github.com/paritytech/rust-secp256k1)",
|
||||
"ethcore-bigint 0.2.1",
|
||||
"ethkey 0.2.0",
|
||||
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -781,7 +781,7 @@ name = "ethkey"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"eth-secp256k1 0.5.6 (git+https://github.com/paritytech/rust-secp256k1)",
|
||||
"eth-secp256k1 0.5.7 (git+https://github.com/paritytech/rust-secp256k1)",
|
||||
"ethcore-bigint 0.2.1",
|
||||
"lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -2223,7 +2223,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "parity-ui-old-precompiled"
|
||||
version = "1.9.0"
|
||||
source = "git+https://github.com/paritytech/js-precompiled.git?branch=v1#f3fa5e5efb0ed8ada6cd091694fcdaa921e6720f"
|
||||
source = "git+https://github.com/paritytech/js-precompiled.git?branch=v1#acfb0974cbbdacf12463bf40e51ea8b60939be95"
|
||||
dependencies = [
|
||||
"parity-dapps-glue 1.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@ -2231,7 +2231,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "parity-ui-precompiled"
|
||||
version = "1.9.0"
|
||||
source = "git+https://github.com/paritytech/js-precompiled.git#5b3e31509c369f558c8cb90f6f9918e50cdb2501"
|
||||
source = "git+https://github.com/paritytech/js-precompiled.git#9b9f760d25aa28504177742f629bf6c639ff5062"
|
||||
dependencies = [
|
||||
"parity-dapps-glue 1.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@ -3515,7 +3515,7 @@ dependencies = [
|
||||
"checksum aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "500909c4f87a9e52355b26626d890833e9e1d53ac566db76c36faa984b889699"
|
||||
"checksum ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "23ac7c30002a5accbf7e8987d0632fa6de155b7c3d39d0067317a391e00a2ef6"
|
||||
"checksum app_dirs 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b7d1c0d48a81bbb13043847f957971f4d87c81542d80ece5e84ba3cba4058fd4"
|
||||
"checksum arrayvec 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)" = "699e63a93b79d717e8c3b5eb1b28b7780d0d6d9e59a72eb769291c83b0c8dc67"
|
||||
"checksum arrayvec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1c0250693b17316353df525fb088da32a8c18f84eb65d113dde31f5a76ed17b6"
|
||||
"checksum aster 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ccfdf7355d9db158df68f976ed030ab0f6578af811f5a7bb6dcf221ec24e0e0"
|
||||
"checksum atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d912da0db7fa85514874458ca3651fe2cddace8d0b0505571dbdcd41ab490159"
|
||||
"checksum backtrace 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "99f2ce94e22b8e664d95c57fff45b98a966c2252b60691d0b7aeeccd88d70983"
|
||||
@ -3560,7 +3560,7 @@ dependencies = [
|
||||
"checksum elastic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "258ff6a9a94f648d0379dbd79110e057edbb53eb85cc237e33eadf8e5a30df85"
|
||||
"checksum env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3ddf21e73e016298f5cb37d6ef8e8da8e39f91f9ec8b0df44b7deb16a9f8cd5b"
|
||||
"checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3"
|
||||
"checksum eth-secp256k1 0.5.6 (git+https://github.com/paritytech/rust-secp256k1)" = "<none>"
|
||||
"checksum eth-secp256k1 0.5.7 (git+https://github.com/paritytech/rust-secp256k1)" = "<none>"
|
||||
"checksum ethabi 4.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c819a3adef0413a2519cbd9a19a35dd1c20c7a0110705beaba8aa4aa87eda95f"
|
||||
"checksum fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1ee15a7050e5580b3712877157068ea713b245b080ff302ae2ca973cfcd9baa"
|
||||
"checksum flate2 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)" = "e6234dd4468ae5d1e2dbb06fe2b058696fdc50a339c68a393aefbf00bc81e423"
|
||||
|
@ -26,6 +26,8 @@ pub struct App {
|
||||
pub author: String,
|
||||
#[serde(rename="iconUrl")]
|
||||
pub icon_url: String,
|
||||
#[serde(rename="localUrl")]
|
||||
pub local_url: Option<String>,
|
||||
}
|
||||
|
||||
impl App {
|
||||
@ -38,6 +40,7 @@ impl App {
|
||||
version: info.version.to_owned(),
|
||||
author: info.author.to_owned(),
|
||||
icon_url: info.icon_url.to_owned(),
|
||||
local_url: info.local_url.to_owned(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -50,6 +53,7 @@ impl Into<EndpointInfo> for App {
|
||||
version: self.version,
|
||||
author: self.author,
|
||||
icon_url: self.icon_url,
|
||||
local_url: self.local_url,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -306,6 +306,7 @@ mod tests {
|
||||
version: "".into(),
|
||||
author: "".into(),
|
||||
icon_url: "".into(),
|
||||
local_url: "".into(),
|
||||
}, Default::default(), None);
|
||||
|
||||
// when
|
||||
|
@ -56,6 +56,7 @@ fn read_manifest(name: &str, mut path: PathBuf) -> EndpointInfo {
|
||||
version: "0.0.0".into(),
|
||||
author: "?".into(),
|
||||
icon_url: "icon.png".into(),
|
||||
local_url: None,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -44,6 +44,7 @@ pub struct EndpointInfo {
|
||||
pub version: String,
|
||||
pub author: String,
|
||||
pub icon_url: String,
|
||||
pub local_url: Option<String>,
|
||||
}
|
||||
|
||||
pub type Endpoints = BTreeMap<String, Box<Endpoint>>;
|
||||
|
@ -132,6 +132,7 @@ impl From<Info> for EndpointInfo {
|
||||
description: info.description.into(),
|
||||
author: info.author.into(),
|
||||
icon_url: info.icon_url.into(),
|
||||
local_url: None,
|
||||
version: info.version.into(),
|
||||
}
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 9a1fcbf0d4e73bea437577e807bc38c7ba243d80
|
||||
Subproject commit e5fcae4e9c2a570f293a22baa4d78d9f4b391a0f
|
@ -25,12 +25,12 @@ pub const SIGNATURES: &'static [UserFunctionDescriptor] = &[
|
||||
Static(
|
||||
"_storage_read",
|
||||
&[I32; 2],
|
||||
Some(I32),
|
||||
None,
|
||||
),
|
||||
Static(
|
||||
"_storage_write",
|
||||
&[I32; 2],
|
||||
Some(I32),
|
||||
None,
|
||||
),
|
||||
Static(
|
||||
"_balance",
|
||||
|
@ -31,6 +31,7 @@ mod result;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
mod env;
|
||||
mod panic_payload;
|
||||
|
||||
const DEFAULT_STACK_SPACE: u32 = 5 * 1024 * 1024;
|
||||
|
||||
|
168
ethcore/wasm/src/panic_payload.rs
Normal file
168
ethcore/wasm/src/panic_payload.rs
Normal file
@ -0,0 +1,168 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use byteorder::{LittleEndian, ReadBytesExt};
|
||||
use std::io::{self, Read};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct PanicPayload {
|
||||
pub msg: Option<String>,
|
||||
pub file: Option<String>,
|
||||
pub line: Option<u32>,
|
||||
pub col: Option<u32>,
|
||||
}
|
||||
|
||||
fn read_string(rdr: &mut io::Cursor<&[u8]>) -> io::Result<Option<String>> {
|
||||
let string_len = rdr.read_u32::<LittleEndian>()?;
|
||||
let string = if string_len == 0 {
|
||||
None
|
||||
} else {
|
||||
let mut content = vec![0; string_len as usize];
|
||||
rdr.read_exact(&mut content)?;
|
||||
Some(String::from_utf8_lossy(&content).into_owned())
|
||||
};
|
||||
Ok(string)
|
||||
}
|
||||
|
||||
pub fn decode(raw: &[u8]) -> PanicPayload {
|
||||
let mut rdr = io::Cursor::new(raw);
|
||||
let msg = read_string(&mut rdr).ok().and_then(|x| x);
|
||||
let file = read_string(&mut rdr).ok().and_then(|x| x);
|
||||
let line = rdr.read_u32::<LittleEndian>().ok();
|
||||
let col = rdr.read_u32::<LittleEndian>().ok();
|
||||
PanicPayload {
|
||||
msg: msg,
|
||||
file: file,
|
||||
line: line,
|
||||
col: col,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use byteorder::WriteBytesExt;
|
||||
|
||||
fn write_u32(payload: &mut Vec<u8>, val: u32) {
|
||||
payload.write_u32::<LittleEndian>(val).unwrap();
|
||||
}
|
||||
|
||||
fn write_bytes(payload: &mut Vec<u8>, bytes: &[u8]) {
|
||||
write_u32(payload, bytes.len() as u32);
|
||||
payload.extend(bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let mut raw = Vec::new();
|
||||
write_bytes(&mut raw, b"msg");
|
||||
write_bytes(&mut raw, b"file");
|
||||
write_u32(&mut raw, 1);
|
||||
write_u32(&mut raw, 2);
|
||||
|
||||
let payload = decode(&raw);
|
||||
|
||||
assert_eq!(
|
||||
payload,
|
||||
PanicPayload {
|
||||
msg: Some("msg".to_string()),
|
||||
file: Some("file".to_string()),
|
||||
line: Some(1),
|
||||
col: Some(2),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn only_msg() {
|
||||
let mut raw = Vec::new();
|
||||
write_bytes(&mut raw, b"msg");
|
||||
|
||||
let payload = decode(&raw);
|
||||
|
||||
assert_eq!(
|
||||
payload,
|
||||
PanicPayload {
|
||||
msg: Some("msg".to_string()),
|
||||
file: None,
|
||||
line: None,
|
||||
col: None,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_utf8() {
|
||||
let mut raw = Vec::new();
|
||||
write_bytes(&mut raw, b"\xF0\x90\x80msg");
|
||||
write_bytes(&mut raw, b"file");
|
||||
write_u32(&mut raw, 1);
|
||||
write_u32(&mut raw, 2);
|
||||
|
||||
let payload = decode(&raw);
|
||||
|
||||
assert_eq!(
|
||||
payload,
|
||||
PanicPayload {
|
||||
msg: Some("<EFBFBD>msg".to_string()),
|
||||
file: Some("file".to_string()),
|
||||
line: Some(1),
|
||||
col: Some(2),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trailing_data() {
|
||||
let mut raw = Vec::new();
|
||||
write_bytes(&mut raw, b"msg");
|
||||
write_bytes(&mut raw, b"file");
|
||||
write_u32(&mut raw, 1);
|
||||
write_u32(&mut raw, 2);
|
||||
write_u32(&mut raw, 0xdeadbeef);
|
||||
|
||||
let payload = decode(&raw);
|
||||
|
||||
assert_eq!(
|
||||
payload,
|
||||
PanicPayload {
|
||||
msg: Some("msg".to_string()),
|
||||
file: Some("file".to_string()),
|
||||
line: Some(1),
|
||||
col: Some(2),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_str_is_none() {
|
||||
let mut raw = Vec::new();
|
||||
write_bytes(&mut raw, b"msg");
|
||||
write_bytes(&mut raw, b"");
|
||||
|
||||
let payload = decode(&raw);
|
||||
|
||||
assert_eq!(
|
||||
payload,
|
||||
PanicPayload {
|
||||
msg: Some("msg".to_string()),
|
||||
file: None,
|
||||
line: None,
|
||||
col: None,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
@ -21,6 +21,7 @@ use std::sync::Arc;
|
||||
use byteorder::{LittleEndian, ByteOrder};
|
||||
|
||||
use vm;
|
||||
use panic_payload;
|
||||
use parity_wasm::interpreter;
|
||||
use wasm_utils::rules;
|
||||
use bigint::prelude::U256;
|
||||
@ -167,7 +168,7 @@ impl<'a, 'b> Runtime<'a, 'b> {
|
||||
|
||||
self.ext.set_storage(key, val).map_err(|_| UserTrap::StorageUpdateError)?;
|
||||
|
||||
Ok(Some(0i32.into()))
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Read from the storage to wasm memory
|
||||
@ -183,7 +184,7 @@ impl<'a, 'b> Runtime<'a, 'b> {
|
||||
|
||||
self.memory.set(val_ptr as u32, &*val)?;
|
||||
|
||||
Ok(Some(0.into()))
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Fetches balance for address
|
||||
@ -626,12 +627,26 @@ impl<'a, 'b> Runtime<'a, 'b> {
|
||||
fn user_panic(&mut self, context: InterpreterCallerContext)
|
||||
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
|
||||
{
|
||||
let msg_len = context.value_stack.pop_as::<i32>()? as u32;
|
||||
let msg_ptr = context.value_stack.pop_as::<i32>()? as u32;
|
||||
|
||||
let msg = String::from_utf8(self.memory.get(msg_ptr, msg_len as usize)?)
|
||||
.map_err(|_| UserTrap::BadUtf8)?;
|
||||
let payload_len = context.value_stack.pop_as::<i32>()? as u32;
|
||||
let payload_ptr = context.value_stack.pop_as::<i32>()? as u32;
|
||||
|
||||
let raw_payload = self.memory.get(payload_ptr, payload_len as usize)?;
|
||||
let payload = panic_payload::decode(&raw_payload);
|
||||
let msg = format!(
|
||||
"{msg}, {file}:{line}:{col}",
|
||||
msg = payload
|
||||
.msg
|
||||
.as_ref()
|
||||
.map(String::as_ref)
|
||||
.unwrap_or("<msg was stripped>"),
|
||||
file = payload
|
||||
.file
|
||||
.as_ref()
|
||||
.map(String::as_ref)
|
||||
.unwrap_or("<unknown>"),
|
||||
line = payload.line.unwrap_or(0),
|
||||
col = payload.col.unwrap_or(0)
|
||||
);
|
||||
trace!(target: "wasm", "Contract custom panic message: {}", msg);
|
||||
|
||||
Err(UserTrap::Panic(msg).into())
|
||||
|
@ -112,7 +112,7 @@ fn logger() {
|
||||
U256::from(1_000_000_000),
|
||||
"Logger sets 0x04 key to the trasferred value"
|
||||
);
|
||||
assert_eq!(gas_left, U256::from(19_143));
|
||||
assert_eq!(gas_left, U256::from(19_147));
|
||||
}
|
||||
|
||||
// This test checks if the contract can allocate memory and pass pointer to the result stream properly.
|
||||
@ -180,7 +180,7 @@ fn dispersion() {
|
||||
result,
|
||||
vec![0u8, 0, 125, 11, 197, 7, 255, 8, 19, 0]
|
||||
);
|
||||
assert_eq!(gas_left, U256::from(99_469));
|
||||
assert_eq!(gas_left, U256::from(96_393));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -208,7 +208,7 @@ fn suicide_not() {
|
||||
result,
|
||||
vec![0u8]
|
||||
);
|
||||
assert_eq!(gas_left, U256::from(99_724));
|
||||
assert_eq!(gas_left, U256::from(96_725));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -240,7 +240,7 @@ fn suicide() {
|
||||
};
|
||||
|
||||
assert!(ext.suicides.contains(&refund));
|
||||
assert_eq!(gas_left, U256::from(99_663));
|
||||
assert_eq!(gas_left, U256::from(96_687));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -270,7 +270,7 @@ fn create() {
|
||||
assert!(ext.calls.contains(
|
||||
&FakeCall {
|
||||
call_type: FakeCallType::Create,
|
||||
gas: U256::from(65_903),
|
||||
gas: U256::from(65_899),
|
||||
sender_address: None,
|
||||
receive_address: None,
|
||||
value: Some(1_000_000_000.into()),
|
||||
@ -278,7 +278,7 @@ fn create() {
|
||||
code_address: None,
|
||||
}
|
||||
));
|
||||
assert_eq!(gas_left, U256::from(65_896));
|
||||
assert_eq!(gas_left, U256::from(65_892));
|
||||
}
|
||||
|
||||
|
||||
@ -312,7 +312,7 @@ fn call_code() {
|
||||
assert!(ext.calls.contains(
|
||||
&FakeCall {
|
||||
call_type: FakeCallType::Call,
|
||||
gas: U256::from(98_709),
|
||||
gas: U256::from(98_713),
|
||||
sender_address: Some(sender),
|
||||
receive_address: Some(receiver),
|
||||
value: None,
|
||||
@ -324,7 +324,7 @@ fn call_code() {
|
||||
// siphash result
|
||||
let res = LittleEndian::read_u32(&result[..]);
|
||||
assert_eq!(res, 4198595614);
|
||||
assert_eq!(gas_left, U256::from(93_851));
|
||||
assert_eq!(gas_left, U256::from(93_855));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -357,7 +357,7 @@ fn call_static() {
|
||||
assert!(ext.calls.contains(
|
||||
&FakeCall {
|
||||
call_type: FakeCallType::Call,
|
||||
gas: U256::from(98_709),
|
||||
gas: U256::from(98_713),
|
||||
sender_address: Some(sender),
|
||||
receive_address: Some(receiver),
|
||||
value: None,
|
||||
@ -370,7 +370,7 @@ fn call_static() {
|
||||
let res = LittleEndian::read_u32(&result[..]);
|
||||
assert_eq!(res, 317632590);
|
||||
|
||||
assert_eq!(gas_left, U256::from(93_851));
|
||||
assert_eq!(gas_left, U256::from(93_855));
|
||||
}
|
||||
|
||||
// Realloc test
|
||||
@ -393,7 +393,7 @@ fn realloc() {
|
||||
}
|
||||
};
|
||||
assert_eq!(result, vec![0u8; 2]);
|
||||
assert_eq!(gas_left, U256::from(99_787));
|
||||
assert_eq!(gas_left, U256::from(96_723));
|
||||
}
|
||||
|
||||
// Tests that contract's ability to read from a storage
|
||||
@ -419,7 +419,7 @@ fn storage_read() {
|
||||
};
|
||||
|
||||
assert_eq!(Address::from(&result[12..32]), address);
|
||||
assert_eq!(gas_left, U256::from(99_702));
|
||||
assert_eq!(gas_left, U256::from(99_767));
|
||||
}
|
||||
|
||||
// Tests keccak calculation
|
||||
@ -445,7 +445,7 @@ fn keccak() {
|
||||
};
|
||||
|
||||
assert_eq!(H256::from_slice(&result), H256::from("68371d7e884c168ae2022c82bd837d51837718a7f7dfb7aa3f753074a35e1d87"));
|
||||
assert_eq!(gas_left, U256::from(84_520));
|
||||
assert_eq!(gas_left, U256::from(81_446));
|
||||
}
|
||||
|
||||
// memcpy test.
|
||||
@ -477,7 +477,7 @@ fn memcpy() {
|
||||
};
|
||||
|
||||
assert_eq!(result, test_payload);
|
||||
assert_eq!(gas_left, U256::from(75_324));
|
||||
assert_eq!(gas_left, U256::from(72_216));
|
||||
}
|
||||
|
||||
// memmove test.
|
||||
@ -509,7 +509,7 @@ fn memmove() {
|
||||
};
|
||||
|
||||
assert_eq!(result, test_payload);
|
||||
assert_eq!(gas_left, U256::from(75_324));
|
||||
assert_eq!(gas_left, U256::from(72_216));
|
||||
}
|
||||
|
||||
// memset test
|
||||
@ -534,7 +534,7 @@ fn memset() {
|
||||
};
|
||||
|
||||
assert_eq!(result, vec![228u8; 8192]);
|
||||
assert_eq!(gas_left, U256::from(75_324));
|
||||
assert_eq!(gas_left, U256::from(72_196));
|
||||
}
|
||||
|
||||
macro_rules! reqrep_test {
|
||||
@ -591,7 +591,7 @@ fn math_add() {
|
||||
U256::from_dec_str("1888888888888888888888888888887").unwrap(),
|
||||
(&result[..]).into()
|
||||
);
|
||||
assert_eq!(gas_left, U256::from(98_576));
|
||||
assert_eq!(gas_left, U256::from(95_524));
|
||||
}
|
||||
|
||||
// multiplication
|
||||
@ -613,7 +613,7 @@ fn math_mul() {
|
||||
U256::from_dec_str("888888888888888888888888888887111111111111111111111111111112").unwrap(),
|
||||
(&result[..]).into()
|
||||
);
|
||||
assert_eq!(gas_left, U256::from(97_726));
|
||||
assert_eq!(gas_left, U256::from(94_674));
|
||||
}
|
||||
|
||||
// subtraction
|
||||
@ -635,7 +635,7 @@ fn math_sub() {
|
||||
U256::from_dec_str("111111111111111111111111111111").unwrap(),
|
||||
(&result[..]).into()
|
||||
);
|
||||
assert_eq!(gas_left, U256::from(98_568));
|
||||
assert_eq!(gas_left, U256::from(95_516));
|
||||
}
|
||||
|
||||
// subtraction with overflow
|
||||
@ -677,7 +677,7 @@ fn math_div() {
|
||||
U256::from_dec_str("1125000").unwrap(),
|
||||
(&result[..]).into()
|
||||
);
|
||||
assert_eq!(gas_left, U256::from(91_564));
|
||||
assert_eq!(gas_left, U256::from(88_514));
|
||||
}
|
||||
|
||||
// This test checks the ability of wasm contract to invoke
|
||||
@ -765,7 +765,7 @@ fn externs() {
|
||||
"Gas limit requested and returned does not match"
|
||||
);
|
||||
|
||||
assert_eq!(gas_left, U256::from(97_740));
|
||||
assert_eq!(gas_left, U256::from(94_733));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -791,7 +791,7 @@ fn embedded_keccak() {
|
||||
};
|
||||
|
||||
assert_eq!(H256::from_slice(&result), H256::from("68371d7e884c168ae2022c82bd837d51837718a7f7dfb7aa3f753074a35e1d87"));
|
||||
assert_eq!(gas_left, U256::from(84_520));
|
||||
assert_eq!(gas_left, U256::from(81_446));
|
||||
}
|
||||
|
||||
/// This test checks the correctness of log extern
|
||||
@ -826,5 +826,5 @@ fn events() {
|
||||
assert_eq!(&log_entry.data, b"gnihtemos");
|
||||
|
||||
assert_eq!(&result, b"gnihtemos");
|
||||
assert_eq!(gas_left, U256::from(82_721));
|
||||
assert_eq!(gas_left, U256::from(79_637));
|
||||
}
|
||||
|
@ -1,34 +0,0 @@
|
||||
# @parity/etherscan
|
||||
|
||||
A thin, lightweight promise wrapper for the api.etherscan.io/apis service, exposing a common endpoint for use in JavaScript applications.
|
||||
|
||||
[https://github.com/paritytech/parity/tree/master/js/src/3rdparty/etherscan](https://github.com/paritytech/parity/tree/master/js/src/3rdparty/etherscan)
|
||||
|
||||
## usage
|
||||
|
||||
installation -
|
||||
|
||||
```
|
||||
npm install --save @parity/etherscan
|
||||
```
|
||||
|
||||
Usage -
|
||||
|
||||
```
|
||||
const etherscan = require('@parity/etherscan');
|
||||
|
||||
// api calls goes here
|
||||
```
|
||||
|
||||
## api
|
||||
|
||||
account (exposed on etherscan.account) -
|
||||
|
||||
- `balance(address)`
|
||||
- `balances(addresses)` (array or addresses)
|
||||
- `transactions(address, page)` (page offset starts at 0, returns 25)
|
||||
|
||||
stats (exposed on etherscan.stats) -
|
||||
|
||||
- `price()`
|
||||
- `supply()`
|
@ -1,33 +0,0 @@
|
||||
{
|
||||
"name": "@parity/etherscan",
|
||||
"description": "The Parity Promise-based library for interfacing with Etherscan over HTTP",
|
||||
"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/paritytech/parity.git"
|
||||
},
|
||||
"keywords": [
|
||||
"Ethereum",
|
||||
"ABI",
|
||||
"API",
|
||||
"RPC",
|
||||
"Parity",
|
||||
"Promise"
|
||||
],
|
||||
"scripts": {
|
||||
},
|
||||
"devDependencies": {
|
||||
"chai": "3.5.0",
|
||||
"mocha": "3.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"node-fetch": "~1.6.3"
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
# @parity/jsonrpc
|
||||
|
||||
JSON and JS interface defintions for RPC calls.
|
||||
|
||||
[https://github.com/paritytech/parity/tree/master/js/src/jsonrpc](https://github.com/paritytech/parity/tree/master/js/src/jsonrpc)
|
@ -1,29 +0,0 @@
|
||||
{
|
||||
"name": "@parity/jsonrpc",
|
||||
"description": "JSON and JS interface defintions for 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/paritytech/parity.git"
|
||||
},
|
||||
"keywords": [
|
||||
"Ethereum",
|
||||
"ABI",
|
||||
"API",
|
||||
"RPC",
|
||||
"Parity"
|
||||
],
|
||||
"scripts": {
|
||||
},
|
||||
"devDependencies": {
|
||||
},
|
||||
"dependencies": {
|
||||
}
|
||||
}
|
@ -1,83 +0,0 @@
|
||||
# @parity/parity.js
|
||||
|
||||
Parity.js is a thin, fast, Promise-based wrapper around the Ethereum APIs.
|
||||
|
||||
[https://github.com/paritytech/parity/tree/master/js/src/api](https://github.com/paritytech/parity/tree/master/js/src/api)
|
||||
|
||||
## 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/paritytech/ethereum-rpc-json/) definitions. Mapping follows the naming conventions of the originals, i.e. `eth_call` becomes `eth.call`, `personal_accounts` becomes `personal.accounts`, etc.
|
@ -1,33 +0,0 @@
|
||||
{
|
||||
"name": "@parity/parity.js",
|
||||
"description": "The Parity Promise-based 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/paritytech/parity.git"
|
||||
},
|
||||
"keywords": [
|
||||
"Ethereum",
|
||||
"ABI",
|
||||
"API",
|
||||
"RPC",
|
||||
"Parity",
|
||||
"Promise"
|
||||
],
|
||||
"scripts": {
|
||||
},
|
||||
"devDependencies": {
|
||||
},
|
||||
"dependencies": {
|
||||
"bignumber.js": "~2.3.0",
|
||||
"js-sha3": "~0.5.2",
|
||||
"node-fetch": "~1.6.3"
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
const parity = require('../');
|
||||
|
||||
describe('load the Parity library', function () {
|
||||
it('should no throw any error', () => {
|
||||
expect(parity).to.be.ok;
|
||||
|
||||
expect(parity.Api).to.be.ok;
|
||||
expect(parity.Abi).to.be.ok;
|
||||
});
|
||||
});
|
@ -1,34 +0,0 @@
|
||||
# @parity/shapeshift
|
||||
|
||||
A thin ES6 promise wrapper around the shapeshift.io APIs as documented at https://shapeshift.io/api
|
||||
|
||||
[https://github.com/paritytech/parity/tree/master/js/src/3rdparty/shapeshift](https://github.com/paritytech/parity/tree/master/js/src/3rdparty/shapeshift)
|
||||
|
||||
## usage
|
||||
|
||||
installation -
|
||||
|
||||
```
|
||||
npm install --save @parity/shapeshift
|
||||
```
|
||||
|
||||
Usage -
|
||||
|
||||
```
|
||||
const APIKEY = 'private affiliate key or undefined';
|
||||
const shapeshift = require('@parity/shapeshift')(APIKEY);
|
||||
|
||||
// api calls goes here
|
||||
```
|
||||
|
||||
## api
|
||||
|
||||
queries -
|
||||
|
||||
- `getCoins()` [https://shapeshift.io/api#api-104](https://shapeshift.io/api#api-104)
|
||||
- `getMarketInfo(pair)` [https://shapeshift.io/api#api-103](https://shapeshift.io/api#api-103)
|
||||
- `getStatus(depositAddress)` [https://shapeshift.io/api#api-5](https://shapeshift.io/api#api-5)
|
||||
|
||||
transactions -
|
||||
|
||||
- `shift(toAddress, returnAddress, pair)` [https://shapeshift.io/api#api-7](https://shapeshift.io/api#api-7)
|
@ -1,31 +0,0 @@
|
||||
{
|
||||
"name": "@parity/shapeshift",
|
||||
"description": "The Parity Promise-based library for interfacing with ShapeShift over HTTP",
|
||||
"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/paritytech/parity.git"
|
||||
},
|
||||
"keywords": [
|
||||
"Ethereum",
|
||||
"ABI",
|
||||
"API",
|
||||
"RPC",
|
||||
"Parity",
|
||||
"Promise"
|
||||
],
|
||||
"scripts": {
|
||||
},
|
||||
"devDependencies": {
|
||||
},
|
||||
"dependencies": {
|
||||
"node-fetch": "~1.6.3"
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
const chai = require('chai');
|
||||
// const chaiAsPromised from 'chai-as-promised';
|
||||
// const chaiEnzyme from 'chai-enzyme';
|
||||
// const sinonChai from 'sinon-chai';
|
||||
|
||||
// chai.use(chaiAsPromised);
|
||||
// chai.use(chaiEnzyme());
|
||||
// chai.use(sinonChai);
|
||||
|
||||
// expose expect to global so we won't have to manually import & define it in every test
|
||||
global.expect = chai.expect;
|
||||
|
||||
module.exports = {};
|
@ -1 +0,0 @@
|
||||
-r ./test/mocha.config
|
@ -27,9 +27,8 @@
|
||||
],
|
||||
"scripts": {
|
||||
"install": "napa",
|
||||
"build": "npm run build:lib && npm run build:dll && npm run build:app",
|
||||
"build": "npm run build:dll && npm run build:app",
|
||||
"build:app": "webpack --config webpack/app",
|
||||
"build:lib": "webpack --config webpack/libraries",
|
||||
"build:dll": "webpack --config webpack/vendor",
|
||||
"ci:build": "cross-env NODE_ENV=production npm run build",
|
||||
"clean": "rimraf ./.build ./.coverage ./.happypack ./.npmjs ./build ./node_modules/.cache",
|
||||
@ -135,6 +134,7 @@
|
||||
"yargs": "6.6.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@parity/abi": "2.1.x",
|
||||
"@parity/api": "2.1.x",
|
||||
"@parity/wordlist": "1.1.x",
|
||||
"base32.js": "0.1.0",
|
||||
|
@ -1,70 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import yargs from 'yargs';
|
||||
|
||||
import interfaces from '../src/jsonrpc';
|
||||
|
||||
const argv = yargs.default('output', 'release').argv;
|
||||
|
||||
const INDEX_JSON = path.join(__dirname, `../${argv.output}/index.json`);
|
||||
const methods = [];
|
||||
|
||||
function formatDescription (obj) {
|
||||
const optional = obj.optional ? '(optional) ' : '';
|
||||
const defaults = obj.default ? `(default: ${obj.default}) ` : '';
|
||||
|
||||
return `${obj.type.name} - ${optional}${defaults}${obj.desc}`;
|
||||
}
|
||||
|
||||
function formatType (obj) {
|
||||
if (obj.type === Object && obj.details) {
|
||||
const formatted = {};
|
||||
|
||||
Object.keys(obj.details).sort().forEach((key) => {
|
||||
formatted[key] = formatType(obj.details[key]);
|
||||
});
|
||||
|
||||
return {
|
||||
desc: formatDescription(obj),
|
||||
details: formatted
|
||||
};
|
||||
} else if (obj.type && obj.type.name) {
|
||||
return formatDescription(obj);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
Object.keys(interfaces).sort().forEach((group) => {
|
||||
Object.keys(interfaces[group]).sort().forEach((name) => {
|
||||
const method = interfaces[group][name];
|
||||
const deprecated = method.deprecated ? ' (Deprecated and not supported, to be removed in a future version)' : '';
|
||||
|
||||
methods.push({
|
||||
name: `${group}_${name}`,
|
||||
desc: `${method.desc}${deprecated}`,
|
||||
params: method.params.map(formatType),
|
||||
returns: formatType(method.returns),
|
||||
inputFormatters: method.params.map((param) => param.format || null),
|
||||
outputFormatter: method.returns.format || null
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
fs.writeFileSync(INDEX_JSON, JSON.stringify({ methods: methods }, null, 2), 'utf8');
|
@ -1,328 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { isPlainObject } from 'lodash';
|
||||
|
||||
import { info, warn, error } from './helpers/log';
|
||||
import { Dummy } from '../src/jsonrpc/helpers';
|
||||
import interfaces from '../src/jsonrpc';
|
||||
import rustMethods from './helpers/parsed-rpc-traits';
|
||||
|
||||
const ROOT_DIR = path.join(__dirname, '../docs');
|
||||
|
||||
if (!fs.existsSync(ROOT_DIR)) {
|
||||
fs.mkdirSync(ROOT_DIR);
|
||||
}
|
||||
|
||||
Object.keys(rustMethods).forEach((group) => {
|
||||
Object.keys(rustMethods[group]).forEach((method) => {
|
||||
if (interfaces[group] == null || interfaces[group][method] == null) {
|
||||
error(`${group}_${method} is defined in Rust traits, but not in js/src/jsonrpc/interfaces`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function printType (type, obj) {
|
||||
if (!type) {
|
||||
throw new Error(`Invalid type in ${JSON.stringify(obj)}`);
|
||||
}
|
||||
|
||||
return type.print || `\`${type.name}\``;
|
||||
}
|
||||
|
||||
function formatDescription (obj, prefix = '', indent = '') {
|
||||
const optional = obj.optional ? '(optional) ' : '';
|
||||
const defaults = obj.default ? `(default: \`${obj.default}\`) ` : '';
|
||||
|
||||
return `${indent}${prefix}${printType(obj.type, obj)} - ${optional}${defaults}${obj.desc}`;
|
||||
}
|
||||
|
||||
function formatType (obj) {
|
||||
if (obj == null || obj.type == null) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
const details = obj.details || obj.type.details;
|
||||
|
||||
if (details) {
|
||||
const sub = Object.keys(details).map((key) => {
|
||||
return formatDescription(details[key], `\`${key}\`: `, ' - ');
|
||||
}).join('\n');
|
||||
|
||||
return `${formatDescription(obj)}\n${sub}`;
|
||||
} else if (obj.type && obj.type.name) {
|
||||
return formatDescription(obj);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
const rpcReqTemplate = {
|
||||
method: 'web3_clientVersion',
|
||||
params: [],
|
||||
id: 1,
|
||||
jsonrpc: '2.0'
|
||||
};
|
||||
|
||||
const { isDummy } = Dummy;
|
||||
const { isArray } = Array;
|
||||
|
||||
// Checks if a field definition has an example,
|
||||
// or describes an object with fields that recursively have examples of their own,
|
||||
// or is optional.
|
||||
function hasExample ({ optional, example, details } = {}) {
|
||||
if (optional || example !== undefined) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (details !== undefined) {
|
||||
const values = Object.keys(details).map((key) => details[key]);
|
||||
|
||||
return values.every(hasExample);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove all optional (trailing) params without examples from an array
|
||||
function removeOptionalWithoutExamples (arr) {
|
||||
return arr.filter(({ optional, example, details }) => {
|
||||
return !optional || example !== undefined || details !== undefined;
|
||||
});
|
||||
}
|
||||
|
||||
// Grabs JSON compatible
|
||||
function getExample (obj) {
|
||||
if (isArray(obj)) {
|
||||
return removeOptionalWithoutExamples(obj).map(getExample);
|
||||
}
|
||||
|
||||
const { example, details } = obj;
|
||||
|
||||
if (example === undefined && details !== undefined) {
|
||||
const nested = {};
|
||||
|
||||
Object.keys(details).forEach((key) => {
|
||||
nested[key] = getExample(details[key]);
|
||||
});
|
||||
|
||||
return nested;
|
||||
}
|
||||
|
||||
return example;
|
||||
}
|
||||
|
||||
function stringifyExample (example, dent = '') {
|
||||
const indent = `${dent} `;
|
||||
|
||||
if (isDummy(example)) {
|
||||
return example.toString();
|
||||
}
|
||||
|
||||
if (isArray(example)) {
|
||||
const last = example.length - 1;
|
||||
|
||||
// If all elements are dummies, print out a single line.
|
||||
// Also covers empty arrays.
|
||||
if (example.every(isDummy)) {
|
||||
const dummies = example.map(d => d.toString());
|
||||
|
||||
return `[${dummies.join(', ')}]`;
|
||||
}
|
||||
|
||||
// For arrays containing just one object or string, don't unwind the array to multiline
|
||||
if (last === 0 && (isPlainObject(example[0]) || typeof example[0] === 'string')) {
|
||||
return `[${stringifyExample(example[0], dent)}]`;
|
||||
}
|
||||
|
||||
const elements = example.map((value, index) => {
|
||||
const comma = index !== last ? ',' : '';
|
||||
const comment = value != null && value._comment ? ` // ${value._comment}` : '';
|
||||
|
||||
return `${stringifyExample(value, indent)}${comma}${comment}`;
|
||||
});
|
||||
|
||||
return `[\n${indent}${elements.join(`\n${indent}`)}\n${dent}]`;
|
||||
}
|
||||
|
||||
if (isPlainObject(example)) {
|
||||
const keys = Object.keys(example);
|
||||
const last = keys.length - 1;
|
||||
|
||||
// print out an empty object
|
||||
if (last === -1) {
|
||||
return '{}';
|
||||
}
|
||||
|
||||
const elements = keys.map((key, index) => {
|
||||
const value = example[key];
|
||||
const comma = index !== last ? ',' : '';
|
||||
const comment = value && value._comment ? ` // ${example[key]._comment}` : '';
|
||||
|
||||
return `${JSON.stringify(key)}: ${stringifyExample(value, indent)}${comma}${comment}`;
|
||||
});
|
||||
|
||||
return `{\n${indent}${elements.join(`\n${indent}`)}\n${dent}}`;
|
||||
}
|
||||
|
||||
return JSON.stringify(example);
|
||||
}
|
||||
|
||||
function buildExample (name, method) {
|
||||
// deprecated, don't care
|
||||
if (method.deprecated) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const logPostfix = method.subdoc ? ` (${method.subdoc})` : '';
|
||||
|
||||
const hasReqExample = method.params.every(hasExample);
|
||||
const hasResExample = hasExample(method.returns);
|
||||
|
||||
if (!hasReqExample && !hasResExample) {
|
||||
error(`${name} has no examples${logPostfix}`);
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
const examples = [];
|
||||
|
||||
if (hasReqExample) {
|
||||
const params = getExample(method.params);
|
||||
const req = Dummy.stringifyJSON(Object.assign({}, rpcReqTemplate, { method: name, params }));
|
||||
|
||||
examples.push(`Request\n\`\`\`bash\ncurl --data '${req}' -H "Content-Type: application/json" -X POST localhost:8545\n\`\`\``);
|
||||
} else {
|
||||
warn(`${name} has a response example but not a request example${logPostfix}`);
|
||||
}
|
||||
|
||||
if (hasResExample) {
|
||||
const res = stringifyExample({
|
||||
id: 1,
|
||||
jsonrpc: '2.0',
|
||||
result: getExample(method.returns)
|
||||
});
|
||||
|
||||
examples.push(`Response\n\`\`\`js\n${res}\n\`\`\``);
|
||||
} else {
|
||||
if (typeof method.returns === 'string') {
|
||||
info(`${name} has a request example and only text description for response${logPostfix}`);
|
||||
} else {
|
||||
warn(`${name} has a request example but not a response example${logPostfix}`);
|
||||
}
|
||||
}
|
||||
|
||||
return `\n\n#### Example\n\n${examples.join('\n\n')}`;
|
||||
}
|
||||
|
||||
function buildParameters (params) {
|
||||
if (params.length === 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
let md = `0. ${params.map(formatType).join('\n0. ')}`;
|
||||
|
||||
if (params.length > 0 && params.every(hasExample) && !isDummy(params[0].example)) {
|
||||
const example = getExample(params);
|
||||
|
||||
md = `${md}\n\n\`\`\`js\nparams: ${stringifyExample(example)}\n\`\`\``;
|
||||
}
|
||||
|
||||
return md;
|
||||
}
|
||||
|
||||
Object.keys(interfaces).sort().forEach((group) => {
|
||||
const spec = interfaces[group];
|
||||
|
||||
for (const key in spec) {
|
||||
const method = spec[key];
|
||||
|
||||
if (!method || !method.subdoc) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const subgroup = `${group}_${method.subdoc}`;
|
||||
|
||||
interfaces[subgroup] = interfaces[subgroup] || {};
|
||||
|
||||
interfaces[subgroup][key] = method;
|
||||
delete spec[key];
|
||||
}
|
||||
});
|
||||
|
||||
Object.keys(interfaces).sort().forEach((group) => {
|
||||
let preamble = `# The \`${group}\` Module`;
|
||||
let markdown = `## JSON-RPC methods\n`;
|
||||
|
||||
const spec = interfaces[group];
|
||||
|
||||
if (spec._preamble) {
|
||||
preamble = `${preamble}\n\n${spec._preamble}`;
|
||||
}
|
||||
|
||||
const content = [];
|
||||
const tocMain = [];
|
||||
const tocSections = {};
|
||||
|
||||
// Comparator that will sort by sections first, names second
|
||||
function methodComparator (a, b) {
|
||||
const sectionA = spec[a].section || '';
|
||||
const sectionB = spec[b].section || '';
|
||||
|
||||
return sectionA.localeCompare(sectionB) || a.localeCompare(b);
|
||||
}
|
||||
|
||||
Object.keys(spec).sort(methodComparator).forEach((iname) => {
|
||||
const method = spec[iname];
|
||||
const groupName = group.replace(/_.*$/, '');
|
||||
const name = `${groupName}_${iname}`;
|
||||
|
||||
if (method.nodoc || method.deprecated) {
|
||||
info(`Skipping ${name}: ${method.nodoc || 'Deprecated'}`);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (rustMethods[groupName] == null || rustMethods[groupName][iname] == null) {
|
||||
error(`${name} is defined in js/src/jsonrpc/interfaces, but not in Rust traits`);
|
||||
}
|
||||
|
||||
const desc = method.desc;
|
||||
const params = buildParameters(method.params);
|
||||
const returns = `- ${formatType(method.returns)}`;
|
||||
const example = buildExample(name, method);
|
||||
|
||||
const { section } = method;
|
||||
const toc = section ? tocSections[section] = tocSections[section] || [] : tocMain;
|
||||
|
||||
toc.push(`- [${name}](#${name.toLowerCase()})`);
|
||||
content.push(`### ${name}\n\n${desc}\n\n#### Parameters\n\n${params || 'None'}\n\n#### Returns\n\n${returns || 'None'}${example}`);
|
||||
});
|
||||
|
||||
markdown = `${markdown}\n${tocMain.join('\n')}`;
|
||||
|
||||
Object.keys(tocSections).sort().forEach((section) => {
|
||||
markdown = `${markdown}\n\n#### ${section}\n${tocSections[section].join('\n')}`;
|
||||
});
|
||||
|
||||
markdown = `${markdown}\n\n## JSON-RPC API Reference\n\n${content.join('\n\n***\n\n')}\n\n`;
|
||||
|
||||
const mdFile = path.join(ROOT_DIR, `${group}.md`);
|
||||
|
||||
fs.writeFileSync(mdFile, `${preamble}\n\n${markdown}`, 'utf8');
|
||||
});
|
@ -1,36 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# variables
|
||||
PACKAGES=( "parity" "etherscan" "shapeshift" "jsonrpc" )
|
||||
|
||||
# change into the build directory
|
||||
BASEDIR=`dirname $0`
|
||||
cd $BASEDIR/..
|
||||
|
||||
# build jsonrpc
|
||||
echo "*** Building JSONRPC .json"
|
||||
mkdir -p .npmjs/jsonrpc
|
||||
npm run ci:build:jsonrpc
|
||||
|
||||
# build all packages
|
||||
echo "*** Building packages for npmjs"
|
||||
echo "$NPM_TOKEN" >> ~/.npmrc
|
||||
|
||||
for PACKAGE in ${PACKAGES[@]}
|
||||
do
|
||||
echo "*** Building $PACKAGE"
|
||||
LIBRARY=$PACKAGE npm run ci:build:npm
|
||||
DIRECTORY=.npmjs/$PACKAGE
|
||||
|
||||
cd $DIRECTORY
|
||||
|
||||
echo "*** Publishing $PACKAGE from $DIRECTORY"
|
||||
echo "npm publish --access public || true"
|
||||
cd ../..
|
||||
|
||||
done
|
||||
cd ..
|
||||
|
||||
# exit with exit code
|
||||
exit 0
|
@ -1,32 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import chalk from 'chalk';
|
||||
|
||||
// INFO Logging helper
|
||||
export function info (log) {
|
||||
console.log(chalk.blue(`INFO:\t${log}`));
|
||||
}
|
||||
|
||||
// WARN Logging helper
|
||||
export function warn (log) {
|
||||
console.warn(chalk.yellow(`WARN:\t${log}`));
|
||||
}
|
||||
|
||||
// ERROR Logging helper
|
||||
export function error (log) {
|
||||
console.error(chalk.red(`ERROR:\t${log}`));
|
||||
}
|
@ -1,81 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
// ```js
|
||||
// rustMethods['eth']['call'] === true
|
||||
// ```
|
||||
const rustMethods = {};
|
||||
|
||||
export default rustMethods;
|
||||
|
||||
// Get a list of JSON-RPC from Rust trait source code
|
||||
function parseMethodsFromRust (source) {
|
||||
// Matching the custom `rpc` attribute with it's doc comment
|
||||
const attributePattern = /((?:\s*\/\/\/.*$)*)\s*#\[rpc\(([^)]+)\)]/gm;
|
||||
const commentPattern = /\s*\/\/\/\s*/g;
|
||||
const separatorPattern = /\s*,\s*/g;
|
||||
const assignPattern = /([\S]+)\s*=\s*"([^"]*)"/;
|
||||
const ignorePattern = /@(ignore|deprecated|unimplemented|alias)\b/i;
|
||||
|
||||
const methods = [];
|
||||
|
||||
source.toString().replace(attributePattern, (match, comment, props) => {
|
||||
comment = comment.replace(commentPattern, '\n').trim();
|
||||
|
||||
// Skip deprecated methods
|
||||
if (ignorePattern.test(comment)) {
|
||||
return match;
|
||||
}
|
||||
|
||||
props.split(separatorPattern).forEach((prop) => {
|
||||
const [, key, value] = prop.split(assignPattern) || [];
|
||||
|
||||
if (key === 'name' && value != null) {
|
||||
methods.push(value);
|
||||
}
|
||||
});
|
||||
|
||||
return match;
|
||||
});
|
||||
|
||||
return methods;
|
||||
}
|
||||
|
||||
// Get a list of all JSON-RPC methods from all defined traits
|
||||
function getMethodsFromRustTraits () {
|
||||
const traitsDir = path.join(__dirname, '../../../rpc/src/v1/traits');
|
||||
|
||||
return fs.readdirSync(traitsDir)
|
||||
.filter((name) => name !== 'mod.rs' && /\.rs$/.test(name))
|
||||
.map((name) => fs.readFileSync(path.join(traitsDir, name)))
|
||||
.map(parseMethodsFromRust)
|
||||
.reduce((a, b) => a.concat(b));
|
||||
}
|
||||
|
||||
getMethodsFromRustTraits().sort().forEach((method) => {
|
||||
const [group, name] = method.split('_');
|
||||
|
||||
// Skip methods with malformed names
|
||||
if (group == null || name == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
rustMethods[group] = rustMethods[group] || {};
|
||||
rustMethods[group][name] = true;
|
||||
});
|
2
js-old/src/3rdparty/etherscan/account.js
vendored
2
js-old/src/3rdparty/etherscan/account.js
vendored
@ -18,7 +18,7 @@ import BigNumber from 'bignumber.js';
|
||||
|
||||
const PAGE_SIZE = 25;
|
||||
|
||||
import util from '../../api/util';
|
||||
import util from '@parity/api/lib/util';
|
||||
import { call } from './call';
|
||||
|
||||
function _call (method, params, test, netVersion) {
|
||||
|
@ -1,32 +0,0 @@
|
||||
# ethabi-js
|
||||
|
||||
A very early, very POC-type port of [https://github.com/paritytech/ethabi](https://github.com/paritytech/ethabi) to JavaScript
|
||||
|
||||
[![Build Status](https://travis-ci.org/jacogr/ethabi-js.svg?branch=master)](https://travis-ci.org/jacogr/ethabi-js)
|
||||
[![Coverage Status](https://coveralls.io/repos/github/jacogr/ethabi-js/badge.svg?branch=master)](https://coveralls.io/github/jacogr/ethabi-js?branch=master)
|
||||
[![Dependency Status](https://david-dm.org/jacogr/ethabi-js.svg)](https://david-dm.org/jacogr/ethabi-js)
|
||||
[![devDependency Status](https://david-dm.org/jacogr/ethabi-js/dev-status.svg)](https://david-dm.org/jacogr/ethabi-js#info=devDependencies)
|
||||
|
||||
## contributing
|
||||
|
||||
Clone the repo and install dependencies via `npm install`. Tests can be executed via
|
||||
|
||||
- `npm run testOnce` (100% covered unit tests)
|
||||
|
||||
## installation
|
||||
|
||||
Install the package with `npm install --save ethabi-js` from the [npm registry ethabi-js](https://www.npmjs.com/package/ethabi-js)
|
||||
|
||||
|
||||
## implementation
|
||||
### approach
|
||||
|
||||
- this version tries to stay as close to the original Rust version in intent, function names & purpose
|
||||
- it is a basic port of the Rust version, relying on effectively the same test-suite (expanded where deemed appropriate)
|
||||
- it is meant as a library to be used in other projects, i.e. [ethapi-js](https://www.npmjs.com/package/ethapi-js)
|
||||
|
||||
### differences to original Rust version
|
||||
|
||||
- internally the library operates on string binary representations as opposed to Vector bytes, lengths are therefore 64 bytes as opposed to 32 bytes
|
||||
- function names are adapted from the Rust standard snake_case to the JavaScript standard camelCase
|
||||
- due to the initial library focus, the cli component (as implemented by the original) is not supported nor mplemented
|
@ -1,20 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import Interface from './spec/interface';
|
||||
|
||||
export default class Abi extends Interface {
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default class BytesTaken {
|
||||
constructor (bytes, newOffset) {
|
||||
this._bytes = bytes;
|
||||
this._newOffset = newOffset;
|
||||
}
|
||||
|
||||
get bytes () {
|
||||
return this._bytes;
|
||||
}
|
||||
|
||||
get newOffset () {
|
||||
return this._newOffset;
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import BytesTaken from './bytesTaken';
|
||||
|
||||
describe('abi/decoder/BytesTaken', () => {
|
||||
describe('constructor', () => {
|
||||
it('sets the bytes of the object', () => {
|
||||
expect((new BytesTaken(1, 2)).bytes).to.equal(1);
|
||||
});
|
||||
|
||||
it('sets the newOffset of the object', () => {
|
||||
expect((new BytesTaken(3, 4)).newOffset).to.equal(4);
|
||||
});
|
||||
});
|
||||
});
|
@ -1,30 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default class DecodeResult {
|
||||
constructor (token, newOffset) {
|
||||
this._token = token;
|
||||
this._newOffset = newOffset;
|
||||
}
|
||||
|
||||
get token () {
|
||||
return this._token;
|
||||
}
|
||||
|
||||
get newOffset () {
|
||||
return this._newOffset;
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import DecodeResult from './decodeResult';
|
||||
|
||||
describe('abi/decoder/DecodeResult', () => {
|
||||
describe('constructor', () => {
|
||||
it('sets the token of the object', () => {
|
||||
expect((new DecodeResult('token', 2)).token).to.equal('token');
|
||||
});
|
||||
|
||||
it('sets the newOffset of the object', () => {
|
||||
expect((new DecodeResult('baz', 4)).newOffset).to.equal(4);
|
||||
});
|
||||
});
|
||||
});
|
@ -1,156 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import utf8 from 'utf8';
|
||||
|
||||
import Token from '../token/token';
|
||||
import BytesTaken from './bytesTaken';
|
||||
import DecodeResult from './decodeResult';
|
||||
import ParamType from '../spec/paramType/paramType';
|
||||
import { sliceData } from '../util/slice';
|
||||
import { asAddress, asBool, asI32, asU32 } from '../util/sliceAs';
|
||||
import { isArray, isInstanceOf } from '../util/types';
|
||||
|
||||
const NULL = '0000000000000000000000000000000000000000000000000000000000000000';
|
||||
|
||||
export default class Decoder {
|
||||
static decode (params, data) {
|
||||
if (!isArray(params)) {
|
||||
throw new Error('Parameters should be array of ParamType');
|
||||
}
|
||||
|
||||
const slices = sliceData(data);
|
||||
let offset = 0;
|
||||
|
||||
return params.map((param) => {
|
||||
const result = Decoder.decodeParam(param, slices, offset);
|
||||
|
||||
offset = result.newOffset;
|
||||
return result.token;
|
||||
});
|
||||
}
|
||||
|
||||
static peek (slices, position) {
|
||||
if (!slices || !slices[position]) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return slices[position];
|
||||
}
|
||||
|
||||
static takeBytes (slices, position, length) {
|
||||
const slicesLength = Math.floor((length + 31) / 32);
|
||||
let bytesStr = '';
|
||||
|
||||
for (let idx = 0; idx < slicesLength; idx++) {
|
||||
bytesStr = `${bytesStr}${Decoder.peek(slices, position + idx)}`;
|
||||
}
|
||||
|
||||
const bytes = (bytesStr.substr(0, length * 2).match(/.{1,2}/g) || []).map((code) => parseInt(code, 16));
|
||||
|
||||
return new BytesTaken(bytes, position + slicesLength);
|
||||
}
|
||||
|
||||
static decodeParam (param, slices, offset) {
|
||||
if (!isInstanceOf(param, ParamType)) {
|
||||
throw new Error('param should be instanceof ParamType');
|
||||
}
|
||||
|
||||
const tokens = [];
|
||||
let taken;
|
||||
let lengthOffset;
|
||||
let length;
|
||||
let newOffset;
|
||||
|
||||
switch (param.type) {
|
||||
case 'address':
|
||||
return new DecodeResult(new Token(param.type, asAddress(Decoder.peek(slices, offset))), offset + 1);
|
||||
|
||||
case 'bool':
|
||||
return new DecodeResult(new Token(param.type, asBool(Decoder.peek(slices, offset))), offset + 1);
|
||||
|
||||
case 'int':
|
||||
return new DecodeResult(new Token(param.type, asI32(Decoder.peek(slices, offset))), offset + 1);
|
||||
|
||||
case 'uint':
|
||||
return new DecodeResult(new Token(param.type, asU32(Decoder.peek(slices, offset))), offset + 1);
|
||||
|
||||
case 'fixedBytes':
|
||||
taken = Decoder.takeBytes(slices, offset, param.length);
|
||||
|
||||
return new DecodeResult(new Token(param.type, taken.bytes), taken.newOffset);
|
||||
|
||||
case 'bytes':
|
||||
lengthOffset = asU32(Decoder.peek(slices, offset)).div(32).toNumber();
|
||||
length = asU32(Decoder.peek(slices, lengthOffset)).toNumber();
|
||||
taken = Decoder.takeBytes(slices, lengthOffset + 1, length);
|
||||
|
||||
return new DecodeResult(new Token(param.type, taken.bytes), offset + 1);
|
||||
|
||||
case 'string':
|
||||
if (param.indexed) {
|
||||
taken = Decoder.takeBytes(slices, offset, 32);
|
||||
|
||||
return new DecodeResult(new Token('fixedBytes', taken.bytes), offset + 1);
|
||||
}
|
||||
|
||||
lengthOffset = asU32(Decoder.peek(slices, offset)).div(32).toNumber();
|
||||
length = asU32(Decoder.peek(slices, lengthOffset)).toNumber();
|
||||
taken = Decoder.takeBytes(slices, lengthOffset + 1, length);
|
||||
|
||||
const str = taken.bytes.map((code) => String.fromCharCode(code)).join('');
|
||||
|
||||
let decoded;
|
||||
|
||||
try {
|
||||
decoded = utf8.decode(str);
|
||||
} catch (error) {
|
||||
decoded = str;
|
||||
}
|
||||
|
||||
return new DecodeResult(new Token(param.type, decoded), offset + 1);
|
||||
|
||||
case 'array':
|
||||
lengthOffset = asU32(Decoder.peek(slices, offset)).div(32).toNumber();
|
||||
length = asU32(Decoder.peek(slices, lengthOffset)).toNumber();
|
||||
newOffset = lengthOffset + 1;
|
||||
|
||||
for (let idx = 0; idx < length; idx++) {
|
||||
const result = Decoder.decodeParam(param.subtype, slices, newOffset);
|
||||
|
||||
newOffset = result.newOffset;
|
||||
tokens.push(result.token);
|
||||
}
|
||||
|
||||
return new DecodeResult(new Token(param.type, tokens), offset + 1);
|
||||
|
||||
case 'fixedArray':
|
||||
newOffset = offset;
|
||||
|
||||
for (let idx = 0; idx < param.length; idx++) {
|
||||
const result = Decoder.decodeParam(param.subtype, slices, newOffset);
|
||||
|
||||
newOffset = result.newOffset;
|
||||
tokens.push(result.token);
|
||||
}
|
||||
|
||||
return new DecodeResult(new Token(param.type, tokens), newOffset);
|
||||
|
||||
default:
|
||||
throw new Error(`Invalid param type ${param.type} in decodeParam`);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,319 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import BigNumber from 'bignumber.js';
|
||||
|
||||
import Decoder from './decoder';
|
||||
import ParamType from '../spec/paramType';
|
||||
import Token from '../token';
|
||||
import { padU32 } from '../util/pad';
|
||||
|
||||
describe('abi/decoder/Decoder', () => {
|
||||
const stringToBytes = function (str) {
|
||||
return str.match(/.{1,2}/g).map((code) => parseInt(code, 16));
|
||||
};
|
||||
|
||||
const address1 = '0000000000000000000000001111111111111111111111111111111111111111';
|
||||
const address2 = '0000000000000000000000002222222222222222222222222222222222222222';
|
||||
const address3 = '0000000000000000000000003333333333333333333333333333333333333333';
|
||||
const address4 = '0000000000000000000000004444444444444444444444444444444444444444';
|
||||
const bool1 = '0000000000000000000000000000000000000000000000000000000000000001';
|
||||
const bytes1 = '1234000000000000000000000000000000000000000000000000000000000000';
|
||||
const bytes2 = '1000000000000000000000000000000000000000000000000000000000000000';
|
||||
const bytes3 = '10000000000000000000000000000000000000000000000000000000000002';
|
||||
const bytes4 = '0010000000000000000000000000000000000000000000000000000000000002';
|
||||
const int1 = '0111111111111111111111111111111111111111111111111111111111111111';
|
||||
const intn = 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff85';
|
||||
const string1 = '6761766f66796f726b0000000000000000000000000000000000000000000000';
|
||||
const string2 = '4665726ee16e64657a0000000000000000000000000000000000000000000000';
|
||||
const tokenAddress1 = new Token('address', `0x${address1.slice(-40)}`);
|
||||
const tokenAddress2 = new Token('address', `0x${address2.slice(-40)}`);
|
||||
const tokenAddress3 = new Token('address', `0x${address3.slice(-40)}`);
|
||||
const tokenAddress4 = new Token('address', `0x${address4.slice(-40)}`);
|
||||
const tokenBool1 = new Token('bool', true);
|
||||
const tokenFixedBytes1 = new Token('fixedBytes', [0x12, 0x34]);
|
||||
const tokenBytes1 = new Token('bytes', [0x12, 0x34]);
|
||||
const tokenBytes2 = new Token('bytes', stringToBytes(bytes2).concat(stringToBytes(bytes2)));
|
||||
const tokenBytes3 = new Token('bytes', stringToBytes(bytes3));
|
||||
const tokenBytes4 = new Token('bytes', stringToBytes(bytes4));
|
||||
const tokenInt1 = new Token('int', new BigNumber(int1, 16));
|
||||
const tokenIntn = new Token('int', new BigNumber(-123));
|
||||
const tokenUint1 = new Token('uint', new BigNumber(int1, 16));
|
||||
const tokenUintn = new Token('uint', new BigNumber(intn, 16));
|
||||
const tokenString1 = new Token('string', 'gavofyork');
|
||||
const tokenString2 = new Token('string', 'Fernández');
|
||||
const slices = [ address1, address2, address3, address4 ];
|
||||
|
||||
describe('peek', () => {
|
||||
it('returns the slice at the correct position', () => {
|
||||
expect(Decoder.peek(slices, 1)).to.equal(slices[1]);
|
||||
});
|
||||
|
||||
it('returns empty on invalid slices', () => {
|
||||
expect(Decoder.peek(null, 4)).to.equal('0000000000000000000000000000000000000000000000000000000000000000');
|
||||
});
|
||||
});
|
||||
|
||||
describe('takeBytes', () => {
|
||||
it('returns a single slice', () => {
|
||||
expect(Decoder.takeBytes(slices, 0, 32).bytes).to.deep.equal(stringToBytes(slices[0]));
|
||||
});
|
||||
|
||||
it('returns a single partial slice', () => {
|
||||
expect(Decoder.takeBytes(slices, 0, 20).bytes).to.deep.equal(stringToBytes(slices[0].substr(0, 40)));
|
||||
});
|
||||
|
||||
it('returns multiple slices', () => {
|
||||
expect(Decoder.takeBytes(slices, 0, 64).bytes).to.deep.equal(stringToBytes(`${slices[0]}${slices[1]}`));
|
||||
});
|
||||
|
||||
it('returns a single offset slice', () => {
|
||||
expect(Decoder.takeBytes(slices, 1, 32).bytes).to.deep.equal(stringToBytes(slices[1]));
|
||||
});
|
||||
|
||||
it('returns multiple offset slices', () => {
|
||||
expect(Decoder.takeBytes(slices, 1, 64).bytes).to.deep.equal(stringToBytes(`${slices[1]}${slices[2]}`));
|
||||
});
|
||||
|
||||
it('returns the requires length from slices', () => {
|
||||
expect(
|
||||
Decoder.takeBytes(slices, 1, 75).bytes
|
||||
).to.deep.equal(stringToBytes(`${slices[1]}${slices[2]}${slices[3]}`.substr(0, 150)));
|
||||
});
|
||||
});
|
||||
|
||||
describe('decodeParam', () => {
|
||||
it('throws an error on non ParamType param', () => {
|
||||
expect(() => Decoder.decodeParam({})).to.throw(/ParamType/);
|
||||
});
|
||||
|
||||
it('throws an error on invalid param type', () => {
|
||||
const pt = new ParamType('address');
|
||||
|
||||
pt._type = 'noMatch';
|
||||
|
||||
expect(() => Decoder.decodeParam(pt)).to.throw(/noMatch/);
|
||||
});
|
||||
|
||||
it('decodes an address', () => {
|
||||
expect(
|
||||
Decoder.decodeParam(new ParamType('address'), [address1], 0).token
|
||||
).to.deep.equal(tokenAddress1);
|
||||
});
|
||||
|
||||
it('decodes a bool', () => {
|
||||
expect(
|
||||
Decoder.decodeParam(new ParamType('bool'), [bool1], 0).token
|
||||
).to.deep.equal(tokenBool1);
|
||||
});
|
||||
|
||||
it('decodes an int', () => {
|
||||
expect(
|
||||
Decoder.decodeParam(new ParamType('int'), [int1], 0).token
|
||||
).to.deep.equal(tokenInt1);
|
||||
});
|
||||
|
||||
it('decodes a negative int', () => {
|
||||
expect(
|
||||
Decoder.decodeParam(new ParamType('int'), [intn], 0).token
|
||||
).to.deep.equal(tokenIntn);
|
||||
});
|
||||
|
||||
it('decodes an uint', () => {
|
||||
expect(
|
||||
Decoder.decodeParam(new ParamType('uint'), [int1], 0).token
|
||||
).to.deep.equal(tokenUint1);
|
||||
});
|
||||
|
||||
it('decodes an uint (negative as int)', () => {
|
||||
expect(
|
||||
Decoder.decodeParam(new ParamType('uint'), [intn], 0).token
|
||||
).to.deep.equal(tokenUintn);
|
||||
});
|
||||
|
||||
it('decodes fixedBytes', () => {
|
||||
expect(
|
||||
Decoder.decodeParam(new ParamType('fixedBytes', null, 2), [bytes1], 0).token
|
||||
).to.deep.equal(tokenFixedBytes1);
|
||||
});
|
||||
|
||||
it('decodes bytes', () => {
|
||||
expect(
|
||||
Decoder.decodeParam(new ParamType('bytes'), [padU32(0x20), padU32(2), bytes1], 0).token
|
||||
).to.deep.equal(tokenBytes1);
|
||||
});
|
||||
|
||||
it('decodes string', () => {
|
||||
expect(
|
||||
Decoder.decodeParam(new ParamType('string'), [padU32(0x20), padU32(9), string1], 0).token
|
||||
).to.deep.equal(tokenString1);
|
||||
});
|
||||
|
||||
it('decodes utf8-invalid string', () => {
|
||||
expect(
|
||||
Decoder.decodeParam(new ParamType('string'), [padU32(0x20), padU32(9), string2], 0).token
|
||||
).to.deep.equal(tokenString2);
|
||||
});
|
||||
|
||||
it('decodes string (indexed)', () => {
|
||||
expect(
|
||||
Decoder.decodeParam(new ParamType('string', null, 0, true), [bytes1], 0)
|
||||
).to.deep.equal(Decoder.decodeParam(new ParamType('fixedBytes', null, 32, true), [bytes1], 0));
|
||||
});
|
||||
});
|
||||
|
||||
describe('decode', () => {
|
||||
it('throws an error on invalid params', () => {
|
||||
expect(() => Decoder.decode(null, '123')).to.throw(/array/);
|
||||
});
|
||||
|
||||
describe('address', () => {
|
||||
it('decodes an address', () => {
|
||||
expect(
|
||||
Decoder.decode(
|
||||
[new ParamType('address')],
|
||||
`${address1}`
|
||||
)
|
||||
).to.deep.equal([tokenAddress1]);
|
||||
});
|
||||
|
||||
it('decodes 2 addresses', () => {
|
||||
expect(
|
||||
Decoder.decode(
|
||||
[new ParamType('address'), new ParamType('address')],
|
||||
`${address1}${address2}`
|
||||
)
|
||||
).to.deep.equal([tokenAddress1, tokenAddress2]);
|
||||
});
|
||||
|
||||
it('decodes a fixedArray of addresses', () => {
|
||||
expect(
|
||||
Decoder.decode(
|
||||
[new ParamType('fixedArray', new ParamType('address'), 2)],
|
||||
`${address1}${address2}`
|
||||
)
|
||||
).to.deep.equal([new Token('fixedArray', [tokenAddress1, tokenAddress2])]);
|
||||
});
|
||||
|
||||
it('decodes a dynamic array of addresses', () => {
|
||||
expect(
|
||||
Decoder.decode(
|
||||
[new ParamType('array', new ParamType('address'))],
|
||||
`${padU32(0x20)}${padU32(2)}${address1}${address2}`
|
||||
)
|
||||
).to.deep.equal([new Token('array', [tokenAddress1, tokenAddress2])]);
|
||||
});
|
||||
|
||||
it('decodes a dynamic array of fixed arrays', () => {
|
||||
expect(
|
||||
Decoder.decode(
|
||||
[new ParamType('array', new ParamType('fixedArray', new ParamType('address'), 2))],
|
||||
`${padU32(0x20)}${padU32(2)}${address1}${address2}${address3}${address4}`
|
||||
)
|
||||
).to.deep.equal([
|
||||
new Token('array', [
|
||||
new Token('fixedArray', [tokenAddress1, tokenAddress2]),
|
||||
new Token('fixedArray', [tokenAddress3, tokenAddress4])
|
||||
])
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('int', () => {
|
||||
it('decodes an int', () => {
|
||||
expect(
|
||||
Decoder.decode(
|
||||
[new ParamType('int')],
|
||||
`${int1}`
|
||||
)
|
||||
).to.deep.equal([tokenInt1]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('uint', () => {
|
||||
it('decodes an uint', () => {
|
||||
expect(
|
||||
Decoder.decode(
|
||||
[new ParamType('uint')],
|
||||
`${int1}`
|
||||
)
|
||||
).to.deep.equal([tokenUint1]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('fixedBytes', () => {
|
||||
it('decodes fixedBytes', () => {
|
||||
expect(
|
||||
Decoder.decode(
|
||||
[new ParamType('fixedBytes', null, 2)],
|
||||
`${bytes1}`
|
||||
)
|
||||
).to.deep.equal([tokenFixedBytes1]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('bytes', () => {
|
||||
it('decodes bytes', () => {
|
||||
expect(
|
||||
Decoder.decode(
|
||||
[new ParamType('bytes')],
|
||||
`${padU32(0x20)}${padU32(2)}${bytes1}`
|
||||
)
|
||||
).to.deep.equal([tokenBytes1]);
|
||||
});
|
||||
|
||||
it('decodes bytes sequence', () => {
|
||||
expect(
|
||||
Decoder.decode(
|
||||
[new ParamType('bytes')],
|
||||
`${padU32(0x20)}${padU32(0x40)}${bytes2}${bytes2}`
|
||||
)
|
||||
).to.deep.equal([tokenBytes2]);
|
||||
});
|
||||
|
||||
it('decodes bytes seuence (2)', () => {
|
||||
expect(
|
||||
Decoder.decode(
|
||||
[new ParamType('bytes'), new ParamType('bytes')],
|
||||
`${padU32(0x40)}${padU32(0x80)}${padU32(0x1f)}${bytes3}00${padU32(0x20)}${bytes4}`
|
||||
)
|
||||
).to.deep.equal([tokenBytes3, tokenBytes4]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('bool', () => {
|
||||
it('decodes a single bool', () => {
|
||||
expect(
|
||||
Decoder.decode(
|
||||
[new ParamType('bool')],
|
||||
bool1
|
||||
)
|
||||
).to.deep.equal([tokenBool1]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('string', () => {
|
||||
it('decodes a string', () => {
|
||||
expect(
|
||||
Decoder.decode(
|
||||
[new ParamType('string')],
|
||||
`${padU32(0x20)}${padU32(9)}${string1}`
|
||||
)
|
||||
).to.deep.equal([tokenString1]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -1,75 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { padAddress, padBool, padBytes, padFixedBytes, padU32, padString } from '../util/pad';
|
||||
import Mediate from './mediate';
|
||||
import Token from '../token/token';
|
||||
import { isArray, isInstanceOf } from '../util/types';
|
||||
|
||||
export default class Encoder {
|
||||
static encode (tokens) {
|
||||
if (!isArray(tokens)) {
|
||||
throw new Error('tokens should be array of Token');
|
||||
}
|
||||
|
||||
const mediates = tokens.map((token, index) => Encoder.encodeToken(token, index));
|
||||
const inits = mediates
|
||||
.map((mediate, idx) => mediate.init(Mediate.offsetFor(mediates, idx)))
|
||||
.join('');
|
||||
const closings = mediates
|
||||
.map((mediate, idx) => mediate.closing(Mediate.offsetFor(mediates, idx)))
|
||||
.join('');
|
||||
|
||||
return `${inits}${closings}`;
|
||||
}
|
||||
|
||||
static encodeToken (token, index = 0) {
|
||||
if (!isInstanceOf(token, Token)) {
|
||||
throw new Error('token should be instanceof Token');
|
||||
}
|
||||
|
||||
try {
|
||||
switch (token.type) {
|
||||
case 'address':
|
||||
return new Mediate('raw', padAddress(token.value));
|
||||
|
||||
case 'int':
|
||||
case 'uint':
|
||||
return new Mediate('raw', padU32(token.value));
|
||||
|
||||
case 'bool':
|
||||
return new Mediate('raw', padBool(token.value));
|
||||
|
||||
case 'fixedBytes':
|
||||
return new Mediate('raw', padFixedBytes(token.value));
|
||||
|
||||
case 'bytes':
|
||||
return new Mediate('prefixed', padBytes(token.value));
|
||||
|
||||
case 'string':
|
||||
return new Mediate('prefixed', padString(token.value));
|
||||
|
||||
case 'fixedArray':
|
||||
case 'array':
|
||||
return new Mediate(token.type, token.value.map((token) => Encoder.encodeToken(token)));
|
||||
}
|
||||
} catch (e) {
|
||||
throw new Error(`Cannot encode token #${index} [${token.type}: ${token.value}]. ${e.message}`);
|
||||
}
|
||||
|
||||
throw new Error(`Invalid token type ${token.type} in encodeToken`);
|
||||
}
|
||||
}
|
@ -1,291 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import Encoder from './encoder';
|
||||
import Token from '../token';
|
||||
import { padAddress, padFixedBytes, padU32 } from '../util/pad';
|
||||
|
||||
describe('abi/encoder/Encoder', () => {
|
||||
describe('encodeToken', () => {
|
||||
it('requires token as Token', () => {
|
||||
expect(() => Encoder.encodeToken()).to.throw(/Token/);
|
||||
});
|
||||
|
||||
it('encodes address tokens in Mediate(raw)', () => {
|
||||
const mediate = Encoder.encodeToken(new Token('address', '123'));
|
||||
|
||||
expect(mediate.type).to.equal('raw');
|
||||
expect(mediate.value).to.be.ok;
|
||||
});
|
||||
|
||||
it('encodes bool tokens in Mediate(raw)', () => {
|
||||
const mediatet = Encoder.encodeToken(new Token('bool', true));
|
||||
const mediatef = Encoder.encodeToken(new Token('bool', false));
|
||||
|
||||
expect(mediatet.type).to.equal('raw');
|
||||
expect(mediatet.value).to.be.ok;
|
||||
|
||||
expect(mediatef.type).to.equal('raw');
|
||||
expect(mediatef.value).to.be.ok;
|
||||
});
|
||||
|
||||
it('encodes int tokens in Mediate(raw)', () => {
|
||||
const mediate = Encoder.encodeToken(new Token('int', '123'));
|
||||
|
||||
expect(mediate.type).to.equal('raw');
|
||||
expect(mediate.value).to.be.ok;
|
||||
});
|
||||
|
||||
it('encodes uint tokens in Mediate(raw)', () => {
|
||||
const mediate = Encoder.encodeToken(new Token('uint', '123'));
|
||||
|
||||
expect(mediate.type).to.equal('raw');
|
||||
expect(mediate.value).to.be.ok;
|
||||
});
|
||||
|
||||
it('encodes fixedBytes tokens in Mediate(raw)', () => {
|
||||
const mediate = Encoder.encodeToken(new Token('fixedBytes', '123'));
|
||||
|
||||
expect(mediate.type).to.equal('raw');
|
||||
expect(mediate.value).to.be.ok;
|
||||
});
|
||||
|
||||
it('encodes bytes tokens in Mediate(prefixed)', () => {
|
||||
const mediate = Encoder.encodeToken(new Token('bytes', '123'));
|
||||
|
||||
expect(mediate.type).to.equal('prefixed');
|
||||
expect(mediate.value).to.be.ok;
|
||||
});
|
||||
|
||||
it('encodes string tokens in Mediate(prefixed)', () => {
|
||||
const mediate = Encoder.encodeToken(new Token('string', '123'));
|
||||
|
||||
expect(mediate.type).to.equal('prefixed');
|
||||
expect(mediate.value).to.be.ok;
|
||||
});
|
||||
|
||||
it('encodes fixedArray tokens in Mediate(fixedArray)', () => {
|
||||
const mediate = Encoder.encodeToken(new Token('fixedArray', [new Token('uint', '123')]));
|
||||
|
||||
expect(mediate.type).to.equal('fixedArray');
|
||||
expect(mediate.value).to.be.ok;
|
||||
});
|
||||
|
||||
it('encodes array tokens in Mediate(array)', () => {
|
||||
const mediate = Encoder.encodeToken(new Token('array', [new Token('uint', '123')]));
|
||||
|
||||
expect(mediate.type).to.equal('array');
|
||||
expect(mediate.value).to.be.ok;
|
||||
});
|
||||
|
||||
it('throws an Error on invalid tokens', () => {
|
||||
const token = new Token('address');
|
||||
|
||||
token._type = 'noMatch';
|
||||
|
||||
expect(() => Encoder.encodeToken(token)).to.throw(/noMatch/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('encode', () => {
|
||||
it('requires tokens array', () => {
|
||||
expect(() => Encoder.encode()).to.throw(/array/);
|
||||
});
|
||||
|
||||
describe('addresses', () => {
|
||||
const address1 = '1111111111111111111111111111111111111111';
|
||||
const address2 = '2222222222222222222222222222222222222222';
|
||||
const address3 = '3333333333333333333333333333333333333333';
|
||||
const address4 = '4444444444444444444444444444444444444444';
|
||||
const encAddress1 = padAddress(address1);
|
||||
const encAddress2 = padAddress(address2);
|
||||
const encAddress3 = padAddress(address3);
|
||||
const encAddress4 = padAddress(address4);
|
||||
const tokenAddress1 = new Token('address', address1);
|
||||
const tokenAddress2 = new Token('address', address2);
|
||||
const tokenAddress3 = new Token('address', address3);
|
||||
const tokenAddress4 = new Token('address', address4);
|
||||
|
||||
it('encodes an address', () => {
|
||||
const token = tokenAddress1;
|
||||
|
||||
expect(Encoder.encode([token])).to.equal(encAddress1);
|
||||
});
|
||||
|
||||
it('encodes an array of addresses', () => {
|
||||
const expected = `${padU32(0x20)}${padU32(2)}${encAddress1}${encAddress2}`;
|
||||
const token = new Token('array', [tokenAddress1, tokenAddress2]);
|
||||
|
||||
expect(Encoder.encode([token])).to.equal(expected);
|
||||
});
|
||||
|
||||
it('encodes an fixedArray of addresses', () => {
|
||||
const expected = `${encAddress1}${encAddress2}`;
|
||||
const token = new Token('fixedArray', [tokenAddress1, tokenAddress2]);
|
||||
|
||||
expect(Encoder.encode([token])).to.equal(expected);
|
||||
});
|
||||
|
||||
it('encodes two addresses', () => {
|
||||
const expected = `${encAddress1}${encAddress2}`;
|
||||
const tokens = [tokenAddress1, tokenAddress2];
|
||||
|
||||
expect(Encoder.encode(tokens)).to.equal(expected);
|
||||
});
|
||||
|
||||
it('encodes fixed array of dynamic array addresses', () => {
|
||||
const tokens1 = new Token('array', [tokenAddress1, tokenAddress2]);
|
||||
const tokens2 = new Token('array', [tokenAddress3, tokenAddress4]);
|
||||
const fixed = new Token('fixedArray', [tokens1, tokens2]);
|
||||
const expected = `${padU32(0x40)}${padU32(0xa0)}${padU32(2)}${encAddress1}${encAddress2}${padU32(2)}${encAddress3}${encAddress4}`;
|
||||
|
||||
expect(Encoder.encode([fixed])).to.equal(expected);
|
||||
});
|
||||
|
||||
it('encodes dynamic array of fixed array addresses', () => {
|
||||
const tokens1 = new Token('fixedArray', [tokenAddress1, tokenAddress2]);
|
||||
const tokens2 = new Token('fixedArray', [tokenAddress3, tokenAddress4]);
|
||||
const dynamic = new Token('array', [tokens1, tokens2]);
|
||||
const expected = `${padU32(0x20)}${padU32(2)}${encAddress1}${encAddress2}${encAddress3}${encAddress4}`;
|
||||
|
||||
expect(Encoder.encode([dynamic])).to.equal(expected);
|
||||
});
|
||||
|
||||
it('encodes dynamic array of dynamic array addresses', () => {
|
||||
const tokens1 = new Token('array', [tokenAddress1]);
|
||||
const tokens2 = new Token('array', [tokenAddress2]);
|
||||
const dynamic = new Token('array', [tokens1, tokens2]);
|
||||
const expected = `${padU32(0x20)}${padU32(2)}${padU32(0x80)}${padU32(0xc0)}${padU32(1)}${encAddress1}${padU32(1)}${encAddress2}`;
|
||||
|
||||
expect(Encoder.encode([dynamic])).to.equal(expected);
|
||||
});
|
||||
|
||||
it('encodes dynamic array of dynamic array addresses (2)', () => {
|
||||
const tokens1 = new Token('array', [tokenAddress1, tokenAddress2]);
|
||||
const tokens2 = new Token('array', [tokenAddress3, tokenAddress4]);
|
||||
const dynamic = new Token('array', [tokens1, tokens2]);
|
||||
const expected = `${padU32(0x20)}${padU32(2)}${padU32(0x80)}${padU32(0xe0)}${padU32(2)}${encAddress1}${encAddress2}${padU32(2)}${encAddress3}${encAddress4}`;
|
||||
|
||||
expect(Encoder.encode([dynamic])).to.equal(expected);
|
||||
});
|
||||
|
||||
it('encodes fixed array of fixed array addresses', () => {
|
||||
const tokens1 = new Token('fixedArray', [tokenAddress1, tokenAddress2]);
|
||||
const tokens2 = new Token('fixedArray', [tokenAddress3, tokenAddress4]);
|
||||
const dynamic = new Token('fixedArray', [tokens1, tokens2]);
|
||||
const expected = `${encAddress1}${encAddress2}${encAddress3}${encAddress4}`;
|
||||
|
||||
expect(Encoder.encode([dynamic])).to.equal(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('bytes', () => {
|
||||
const bytes1 = '0x1234';
|
||||
const bytes2 = '0x10000000000000000000000000000000000000000000000000000000000002';
|
||||
const bytes3 = '0x1000000000000000000000000000000000000000000000000000000000000000';
|
||||
|
||||
it('encodes fixed bytes', () => {
|
||||
const token = new Token('fixedBytes', bytes1);
|
||||
|
||||
expect(Encoder.encode([token])).to.equal(padFixedBytes(bytes1));
|
||||
});
|
||||
|
||||
it('encodes bytes', () => {
|
||||
const token = new Token('bytes', bytes1);
|
||||
|
||||
expect(Encoder.encode([token])).to.equal(`${padU32(0x20)}${padU32(2)}${padFixedBytes(bytes1)}`);
|
||||
});
|
||||
|
||||
it('encodes bytes (short of boundary)', () => {
|
||||
const token = new Token('bytes', bytes2);
|
||||
|
||||
expect(Encoder.encode([token])).to.equal(`${padU32(0x20)}${padU32(0x1f)}${padFixedBytes(bytes2)}`);
|
||||
});
|
||||
|
||||
it('encodes bytes (two blocks)', () => {
|
||||
const input = `${bytes3}${bytes3.slice(-64)}`;
|
||||
const token = new Token('bytes', input);
|
||||
|
||||
expect(Encoder.encode([token])).to.equal(`${padU32(0x20)}${padU32(0x40)}${padFixedBytes(input)}`);
|
||||
});
|
||||
|
||||
it('encodes two consecutive bytes', () => {
|
||||
const in1 = '0x10000000000000000000000000000000000000000000000000000000000002';
|
||||
const in2 = '0x0010000000000000000000000000000000000000000000000000000000000002';
|
||||
const tokens = [new Token('bytes', in1), new Token('bytes', in2)];
|
||||
|
||||
expect(Encoder.encode(tokens)).to.equal(`${padU32(0x40)}${padU32(0x80)}${padU32(0x1f)}${padFixedBytes(in1)}${padU32(0x20)}${padFixedBytes(in2)}`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('string', () => {
|
||||
it('encodes a string', () => {
|
||||
const string = 'gavofyork';
|
||||
const stringEnc = padFixedBytes('0x6761766f66796f726b');
|
||||
const token = new Token('string', string);
|
||||
|
||||
expect(Encoder.encode([token])).to.equal(`${padU32(0x20)}${padU32(string.length.toString(16))}${stringEnc}`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('uint', () => {
|
||||
it('encodes a uint', () => {
|
||||
const token = new Token('uint', 4);
|
||||
|
||||
expect(Encoder.encode([token])).to.equal(padU32(4));
|
||||
});
|
||||
});
|
||||
|
||||
describe('int', () => {
|
||||
it('encodes a int', () => {
|
||||
const token = new Token('int', 4);
|
||||
|
||||
expect(Encoder.encode([token])).to.equal(padU32(4));
|
||||
});
|
||||
});
|
||||
|
||||
describe('bool', () => {
|
||||
it('encodes a bool (true)', () => {
|
||||
const token = new Token('bool', true);
|
||||
|
||||
expect(Encoder.encode([token])).to.equal(padU32(1));
|
||||
});
|
||||
|
||||
it('encodes a bool (false)', () => {
|
||||
const token = new Token('bool', false);
|
||||
|
||||
expect(Encoder.encode([token])).to.equal(padU32(0));
|
||||
});
|
||||
});
|
||||
|
||||
describe('comprehensive test', () => {
|
||||
it('encodes a complex sequence', () => {
|
||||
const bytes = '0x131a3afc00d1b1e3461b955e53fc866dcf303b3eb9f4c16f89e388930f48134b131a3afc00d1b1e3461b955e53fc866dcf303b3eb9f4c16f89e388930f48134b';
|
||||
const tokens = [new Token('int', 5), new Token('bytes', bytes), new Token('int', 3), new Token('bytes', bytes)];
|
||||
|
||||
expect(Encoder.encode(tokens)).to.equal(`${padU32(5)}${padU32(0x80)}${padU32(3)}${padU32(0xe0)}${padU32(0x40)}${bytes.substr(2)}${padU32(0x40)}${bytes.substr(2)}`);
|
||||
});
|
||||
|
||||
it('encodes a complex sequence (nested)', () => {
|
||||
const array = [new Token('int', 5), new Token('int', 6), new Token('int', 7)];
|
||||
const tokens = [new Token('int', 1), new Token('string', 'gavofyork'), new Token('int', 2), new Token('int', 3), new Token('int', 4), new Token('array', array)];
|
||||
const stringEnc = padFixedBytes('0x6761766f66796f726b');
|
||||
|
||||
expect(Encoder.encode(tokens)).to.equal(`${padU32(1)}${padU32(0xc0)}${padU32(2)}${padU32(3)}${padU32(4)}${padU32(0x100)}${padU32(9)}${stringEnc}${padU32(3)}${padU32(5)}${padU32(6)}${padU32(7)}`);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -1,17 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './encoder';
|
@ -1,142 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
const TYPES = ['raw', 'prefixed', 'fixedArray', 'array'];
|
||||
|
||||
import { padU32 } from '../util/pad';
|
||||
|
||||
export default class Mediate {
|
||||
constructor (type, value) {
|
||||
Mediate.validateType(type);
|
||||
|
||||
this._type = type;
|
||||
this._value = value;
|
||||
}
|
||||
|
||||
initLength () {
|
||||
switch (this._type) {
|
||||
case 'raw':
|
||||
return this._value.length / 2;
|
||||
|
||||
case 'array':
|
||||
case 'prefixed':
|
||||
return 32;
|
||||
|
||||
case 'fixedArray':
|
||||
return this._value
|
||||
.reduce((total, mediate) => {
|
||||
return total + mediate.initLength();
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
closingLength () {
|
||||
switch (this._type) {
|
||||
case 'raw':
|
||||
return 0;
|
||||
|
||||
case 'prefixed':
|
||||
return this._value.length / 2;
|
||||
|
||||
case 'array':
|
||||
return this._value
|
||||
.reduce((total, mediate) => {
|
||||
return total + mediate.initLength();
|
||||
}, 32);
|
||||
|
||||
case 'fixedArray':
|
||||
return this._value
|
||||
.reduce((total, mediate) => {
|
||||
return total + mediate.initLength() + mediate.closingLength();
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
init (suffixOffset) {
|
||||
switch (this._type) {
|
||||
case 'raw':
|
||||
return this._value;
|
||||
|
||||
case 'fixedArray':
|
||||
return this._value
|
||||
.map((mediate, idx) => mediate.init(Mediate.offsetFor(this._value, idx)).toString(16))
|
||||
.join('');
|
||||
|
||||
case 'prefixed':
|
||||
case 'array':
|
||||
return padU32(suffixOffset);
|
||||
}
|
||||
}
|
||||
|
||||
closing (offset) {
|
||||
switch (this._type) {
|
||||
case 'raw':
|
||||
return '';
|
||||
|
||||
case 'prefixed':
|
||||
return this._value;
|
||||
|
||||
case 'fixedArray':
|
||||
return this._value
|
||||
.map((mediate, idx) => mediate.closing(Mediate.offsetFor(this._value, idx)).toString(16))
|
||||
.join('');
|
||||
|
||||
case 'array':
|
||||
const prefix = padU32(this._value.length);
|
||||
const inits = this._value
|
||||
.map((mediate, idx) => mediate.init(offset + Mediate.offsetFor(this._value, idx) + 32).toString(16))
|
||||
.join('');
|
||||
const closings = this._value
|
||||
.map((mediate, idx) => mediate.closing(offset + Mediate.offsetFor(this._value, idx)).toString(16))
|
||||
.join('');
|
||||
|
||||
return `${prefix}${inits}${closings}`;
|
||||
}
|
||||
}
|
||||
|
||||
get type () {
|
||||
return this._type;
|
||||
}
|
||||
|
||||
get value () {
|
||||
return this._value;
|
||||
}
|
||||
|
||||
static offsetFor (mediates, position) {
|
||||
if (position < 0 || position >= mediates.length) {
|
||||
throw new Error(`Invalid position ${position} specified for Mediate.offsetFor`);
|
||||
}
|
||||
|
||||
const initLength = mediates
|
||||
.reduce((total, mediate) => {
|
||||
return total + mediate.initLength();
|
||||
}, 0);
|
||||
|
||||
return mediates
|
||||
.slice(0, position)
|
||||
.reduce((total, mediate) => {
|
||||
return total + mediate.closingLength();
|
||||
}, initLength);
|
||||
}
|
||||
|
||||
static validateType (type) {
|
||||
if (TYPES.filter((_type) => type === _type).length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
throw new Error(`Invalid type ${type} received for Mediate.validateType`);
|
||||
}
|
||||
}
|
@ -1,105 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import Mediate from './mediate';
|
||||
|
||||
describe('abi/encoder/Mediate', () => {
|
||||
const LONG15 = '1234567890abcdef000000000000000000000000000000000000000000000000';
|
||||
const DOUBLE15 = `${LONG15}${LONG15}`;
|
||||
const ARRAY = [new Mediate('raw', DOUBLE15), new Mediate('raw', LONG15)];
|
||||
|
||||
describe('validateType', () => {
|
||||
it('validates raw', () => {
|
||||
expect(Mediate.validateType('raw')).to.be.true;
|
||||
});
|
||||
|
||||
it('validates prefixed', () => {
|
||||
expect(Mediate.validateType('prefixed')).to.be.true;
|
||||
});
|
||||
|
||||
it('validates fixedArray', () => {
|
||||
expect(Mediate.validateType('fixedArray')).to.be.true;
|
||||
});
|
||||
|
||||
it('validates array', () => {
|
||||
expect(Mediate.validateType('array')).to.be.true;
|
||||
});
|
||||
|
||||
it('throws an error on invalid types', () => {
|
||||
expect(() => Mediate.validateType('noMatch')).to.throw(/noMatch/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('offsetFor', () => {
|
||||
it('thows an error when offset < 0', () => {
|
||||
expect(() => Mediate.offsetFor([1], -1)).to.throw(/Invalid position/);
|
||||
});
|
||||
|
||||
it('throws an error when offset >= length', () => {
|
||||
expect(() => Mediate.offsetFor([1], 1)).to.throw(/Invalid position/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('constructor', () => {
|
||||
it('throws an error on invalid types', () => {
|
||||
expect(() => new Mediate('noMatch', '1')).to.throw(/noMatch/);
|
||||
});
|
||||
|
||||
it('sets the type of the object', () => {
|
||||
expect((new Mediate('raw', '1')).type).to.equal('raw');
|
||||
});
|
||||
|
||||
it('sets the value of the object', () => {
|
||||
expect((new Mediate('raw', '1')).value).to.equal('1');
|
||||
});
|
||||
});
|
||||
|
||||
describe('initLength', () => {
|
||||
it('returns correct variable byte length for raw', () => {
|
||||
expect(new Mediate('raw', DOUBLE15).initLength()).to.equal(64);
|
||||
});
|
||||
|
||||
it('returns correct fixed byte length for array', () => {
|
||||
expect(new Mediate('array', [1, 2, 3, 4]).initLength()).to.equal(32);
|
||||
});
|
||||
|
||||
it('returns correct fixed byte length for prefixed', () => {
|
||||
expect(new Mediate('prefixed', 0).initLength()).to.equal(32);
|
||||
});
|
||||
|
||||
it('returns correct variable byte length for fixedArray', () => {
|
||||
expect(new Mediate('fixedArray', ARRAY).initLength()).to.equal(96);
|
||||
});
|
||||
});
|
||||
|
||||
describe('closingLength', () => {
|
||||
it('returns 0 byte length for raw', () => {
|
||||
expect(new Mediate('raw', DOUBLE15).closingLength()).to.equal(0);
|
||||
});
|
||||
|
||||
it('returns prefix + size for prefixed', () => {
|
||||
expect(new Mediate('prefixed', DOUBLE15).closingLength()).to.equal(64);
|
||||
});
|
||||
|
||||
it('returns prefix + size for array', () => {
|
||||
expect(new Mediate('array', ARRAY).closingLength()).to.equal(128);
|
||||
});
|
||||
|
||||
it('returns total length for fixedArray', () => {
|
||||
expect(new Mediate('fixedArray', ARRAY).closingLength()).to.equal(96);
|
||||
});
|
||||
});
|
||||
});
|
@ -1,17 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './abi';
|
@ -1,36 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import Encoder from '../encoder/encoder';
|
||||
import Param from './param';
|
||||
|
||||
export default class Constructor {
|
||||
constructor (abi) {
|
||||
this._inputs = Param.toParams(abi.inputs || []);
|
||||
}
|
||||
|
||||
get inputs () {
|
||||
return this._inputs;
|
||||
}
|
||||
|
||||
inputParamTypes () {
|
||||
return this._inputs.map((input) => input.kind);
|
||||
}
|
||||
|
||||
encodeCall (tokens) {
|
||||
return Encoder.encode(tokens);
|
||||
}
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import Constructor from './constructor';
|
||||
import Param from './param';
|
||||
import Token from '../token';
|
||||
|
||||
describe('abi/spec/Constructor', () => {
|
||||
const inputsArr = [{ name: 'boolin', type: 'bool' }, { name: 'stringin', type: 'string' }];
|
||||
const bool = new Param('boolin', 'bool');
|
||||
const string = new Param('stringin', 'string');
|
||||
|
||||
const inputs = [bool, string];
|
||||
const cr = new Constructor({ inputs: inputsArr });
|
||||
|
||||
describe('constructor', () => {
|
||||
it('stores the inputs as received', () => {
|
||||
expect(cr.inputs).to.deep.equal(inputs);
|
||||
});
|
||||
|
||||
it('matches empty inputs with []', () => {
|
||||
expect(new Constructor({}).inputs).to.deep.equal([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('inputParamTypes', () => {
|
||||
it('retrieves the input types as received', () => {
|
||||
expect(cr.inputParamTypes()).to.deep.equal([bool.kind, string.kind]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('encodeCall', () => {
|
||||
it('encodes correctly', () => {
|
||||
const result = cr.encodeCall([new Token('bool', true), new Token('string', 'jacogr')]);
|
||||
|
||||
expect(result).to.equal('0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000066a61636f67720000000000000000000000000000000000000000000000000000');
|
||||
});
|
||||
});
|
||||
});
|
@ -1,30 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default class DecodedLog {
|
||||
constructor (params, address) {
|
||||
this._params = params;
|
||||
this._address = address;
|
||||
}
|
||||
|
||||
get address () {
|
||||
return this._address;
|
||||
}
|
||||
|
||||
get params () {
|
||||
return this._params;
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import DecodedLog from './decodedLog';
|
||||
|
||||
const log = new DecodedLog('someParams', 'someAddress');
|
||||
|
||||
describe('abi/spec/event/DecodedLog', () => {
|
||||
describe('constructor', () => {
|
||||
it('sets internal state', () => {
|
||||
expect(log.params).to.equal('someParams');
|
||||
expect(log.address).to.equal('someAddress');
|
||||
});
|
||||
});
|
||||
});
|
@ -1,45 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import ParamType from '../paramType/paramType';
|
||||
import Token from '../../token/token';
|
||||
import { isInstanceOf } from '../../util/types';
|
||||
|
||||
export default class DecodedLogParam {
|
||||
constructor (name, kind, token) {
|
||||
if (!isInstanceOf(kind, ParamType)) {
|
||||
throw new Error('kind not instanceof ParamType');
|
||||
} else if (!isInstanceOf(token, Token)) {
|
||||
throw new Error('token not instanceof Token');
|
||||
}
|
||||
|
||||
this._name = name;
|
||||
this._kind = kind;
|
||||
this._token = token;
|
||||
}
|
||||
|
||||
get name () {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
get kind () {
|
||||
return this._kind;
|
||||
}
|
||||
|
||||
get token () {
|
||||
return this._token;
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import DecodedLogParam from './decodedLogParam';
|
||||
import ParamType from '../paramType';
|
||||
import Token from '../../token';
|
||||
|
||||
describe('abi/spec/event/DecodedLogParam', () => {
|
||||
describe('constructor', () => {
|
||||
const pt = new ParamType('bool');
|
||||
const tk = new Token('bool');
|
||||
|
||||
it('disallows kind not instanceof ParamType', () => {
|
||||
expect(() => new DecodedLogParam('test', 'param')).to.throw(/ParamType/);
|
||||
});
|
||||
|
||||
it('disallows token not instanceof Token', () => {
|
||||
expect(() => new DecodedLogParam('test', pt, 'token')).to.throw(/Token/);
|
||||
});
|
||||
|
||||
it('stores all parameters received', () => {
|
||||
const log = new DecodedLogParam('test', pt, tk);
|
||||
|
||||
expect(log.name).to.equal('test');
|
||||
expect(log.kind).to.equal(pt);
|
||||
expect(log.token).to.equal(tk);
|
||||
});
|
||||
});
|
||||
});
|
@ -1,114 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import Decoder from '../../decoder/decoder';
|
||||
import DecodedLog from './decodedLog';
|
||||
import DecodedLogParam from './decodedLogParam';
|
||||
import EventParam from './eventParam';
|
||||
import { asAddress } from '../../util/sliceAs';
|
||||
import { eventSignature } from '../../util/signature';
|
||||
|
||||
export default class Event {
|
||||
constructor (abi) {
|
||||
this._inputs = EventParam.toEventParams(abi.inputs || []);
|
||||
this._anonymous = !!abi.anonymous;
|
||||
|
||||
const { id, name, signature } = eventSignature(abi.name, this.inputParamTypes());
|
||||
|
||||
this._id = id;
|
||||
this._name = name;
|
||||
this._signature = signature;
|
||||
}
|
||||
|
||||
get name () {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
get id () {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
get inputs () {
|
||||
return this._inputs;
|
||||
}
|
||||
|
||||
get anonymous () {
|
||||
return this._anonymous;
|
||||
}
|
||||
|
||||
get signature () {
|
||||
return this._signature;
|
||||
}
|
||||
|
||||
inputParamTypes () {
|
||||
return this._inputs.map((input) => input.kind);
|
||||
}
|
||||
|
||||
inputParamNames () {
|
||||
return this._inputs.map((input) => input.name);
|
||||
}
|
||||
|
||||
indexedParams (indexed) {
|
||||
return this._inputs.filter((input) => input.indexed === indexed);
|
||||
}
|
||||
|
||||
decodeLog (topics, data) {
|
||||
const topicParams = this.indexedParams(true);
|
||||
const dataParams = this.indexedParams(false);
|
||||
|
||||
let address;
|
||||
let toSkip;
|
||||
|
||||
if (!this.anonymous) {
|
||||
address = asAddress(topics[0]);
|
||||
toSkip = 1;
|
||||
} else {
|
||||
toSkip = 0;
|
||||
}
|
||||
|
||||
const topicTypes = topicParams.map((param) => param.kind);
|
||||
const flatTopics = topics
|
||||
.filter((topic, idx) => idx >= toSkip)
|
||||
.map((topic) => {
|
||||
return (topic.substr(0, 2) === '0x')
|
||||
? topic.substr(2)
|
||||
: topic;
|
||||
}).join('');
|
||||
const topicTokens = Decoder.decode(topicTypes, flatTopics);
|
||||
|
||||
if (topicTokens.length !== (topics.length - toSkip)) {
|
||||
throw new Error('Invalid topic data');
|
||||
}
|
||||
|
||||
const dataTypes = dataParams.map((param) => param.kind);
|
||||
const dataTokens = Decoder.decode(dataTypes, data);
|
||||
|
||||
const namedTokens = {};
|
||||
|
||||
topicParams.forEach((param, idx) => {
|
||||
namedTokens[param.name || idx] = topicTokens[idx];
|
||||
});
|
||||
dataParams.forEach((param, idx) => {
|
||||
namedTokens[param.name || idx] = dataTokens[idx];
|
||||
});
|
||||
|
||||
const inputParamTypes = this.inputParamTypes();
|
||||
const decodedParams = this.inputParamNames()
|
||||
.map((name, idx) => new DecodedLogParam(name, inputParamTypes[idx], namedTokens[name || idx]));
|
||||
|
||||
return new DecodedLog(decodedParams, address);
|
||||
}
|
||||
}
|
@ -1,111 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import BigNumber from 'bignumber.js';
|
||||
|
||||
import Event from './event';
|
||||
import EventParam from './eventParam';
|
||||
import DecodedLogParam from './decodedLogParam';
|
||||
import ParamType from '../paramType';
|
||||
import Token from '../../token';
|
||||
|
||||
describe('abi/spec/event/Event', () => {
|
||||
const inputArr = [{ name: 'a', type: 'bool' }, { name: 'b', type: 'uint', indexed: true }];
|
||||
const inputs = [new EventParam('a', 'bool', false), new EventParam('b', 'uint', true)];
|
||||
const event = new Event({ name: 'test', inputs: inputArr, anonymous: true });
|
||||
|
||||
describe('constructor', () => {
|
||||
it('stores the parameters as received', () => {
|
||||
expect(event.name).to.equal('test');
|
||||
expect(event.inputs).to.deep.equal(inputs);
|
||||
expect(event.anonymous).to.be.true;
|
||||
});
|
||||
|
||||
it('matches empty inputs with []', () => {
|
||||
expect(new Event({ name: 'test' }).inputs).to.deep.equal([]);
|
||||
});
|
||||
|
||||
it('sets the event signature', () => {
|
||||
expect(new Event({ name: 'baz' }).signature)
|
||||
.to.equal('a7916fac4f538170f7cd12c148552e2cba9fcd72329a2dd5b07a6fa906488ddf');
|
||||
});
|
||||
});
|
||||
|
||||
describe('inputParamTypes', () => {
|
||||
it('returns all the types', () => {
|
||||
expect(event.inputParamTypes()).to.deep.equal([new ParamType('bool'), new ParamType('uint', null, 256, true)]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('inputParamNames', () => {
|
||||
it('returns all the names', () => {
|
||||
expect(event.inputParamNames()).to.deep.equal(['a', 'b']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('indexedParams', () => {
|
||||
it('returns all indexed parameters (indexed)', () => {
|
||||
expect(event.indexedParams(true)).to.deep.equal([inputs[1]]);
|
||||
});
|
||||
|
||||
it('returns all indexed parameters (non-indexed)', () => {
|
||||
expect(event.indexedParams(false)).to.deep.equal([inputs[0]]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('decodeLog', () => {
|
||||
it('decodes an event', () => {
|
||||
const event = new Event({
|
||||
name: 'foo',
|
||||
inputs: [
|
||||
{ name: 'a', type: 'int' },
|
||||
{ name: 'b', type: 'int', indexed: true },
|
||||
{ name: 'c', type: 'address' },
|
||||
{ name: 'd', type: 'address', indexed: true }
|
||||
]
|
||||
});
|
||||
const decoded = event.decodeLog([
|
||||
'0000000000000000000000004444444444444444444444444444444444444444',
|
||||
'0000000000000000000000000000000000000000000000000000000000000002',
|
||||
'0000000000000000000000001111111111111111111111111111111111111111' ],
|
||||
'00000000000000000000000000000000000000000000000000000000000000030000000000000000000000002222222222222222222222222222222222222222');
|
||||
|
||||
expect(decoded.address).to.equal('0x4444444444444444444444444444444444444444');
|
||||
expect(decoded.params).to.deep.equal([
|
||||
new DecodedLogParam('a', new ParamType('int', null, 256), new Token('int', new BigNumber(3))),
|
||||
new DecodedLogParam('b', new ParamType('int', null, 256, true), new Token('int', new BigNumber(2))),
|
||||
new DecodedLogParam('c', new ParamType('address'), new Token('address', '0x2222222222222222222222222222222222222222')),
|
||||
new DecodedLogParam('d', new ParamType('address', null, 0, true), new Token('address', '0x1111111111111111111111111111111111111111'))
|
||||
]);
|
||||
});
|
||||
|
||||
it('decodes an anonymous event', () => {
|
||||
const event = new Event({ name: 'foo', inputs: [{ name: 'a', type: 'int' }], anonymous: true });
|
||||
const decoded = event.decodeLog([], '0000000000000000000000000000000000000000000000000000000000000003');
|
||||
|
||||
expect(decoded.address).to.not.be.ok;
|
||||
expect(decoded.params).to.deep.equal([
|
||||
new DecodedLogParam('a', new ParamType('int', null, 256), new Token('int', new BigNumber(3)))
|
||||
]);
|
||||
});
|
||||
|
||||
it('throws on invalid topics', () => {
|
||||
const event = new Event({ name: 'foo', inputs: [{ name: 'a', type: 'int' }], anonymous: true });
|
||||
|
||||
expect(() => event.decodeLog(['0000000000000000000000004444444444444444444444444444444444444444'], '0000000000000000000000000000000000000000000000000000000000000003')).to.throw(/Invalid/);
|
||||
});
|
||||
});
|
||||
});
|
@ -1,41 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { toParamType } from '../paramType/format';
|
||||
|
||||
export default class EventParam {
|
||||
constructor (name, type, indexed = false) {
|
||||
this._name = name;
|
||||
this._indexed = indexed;
|
||||
this._kind = toParamType(type, indexed);
|
||||
}
|
||||
|
||||
get name () {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
get kind () {
|
||||
return this._kind;
|
||||
}
|
||||
|
||||
get indexed () {
|
||||
return this._indexed;
|
||||
}
|
||||
|
||||
static toEventParams (params) {
|
||||
return params.map((param) => new EventParam(param.name, param.type, param.indexed));
|
||||
}
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import EventParam from './eventParam';
|
||||
|
||||
describe('abi/spec/event/EventParam', () => {
|
||||
describe('constructor', () => {
|
||||
it('sets the properties', () => {
|
||||
const param = new EventParam('foo', 'uint', true);
|
||||
|
||||
expect(param.name).to.equal('foo');
|
||||
expect(param.kind.type).to.equal('uint');
|
||||
expect(param.indexed).to.be.true;
|
||||
});
|
||||
|
||||
it('uses defaults for indexed', () => {
|
||||
expect(new EventParam('foo', 'uint').indexed).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
describe('toEventParams', () => {
|
||||
it('maps an array of params', () => {
|
||||
const params = EventParam.toEventParams([{ name: 'foo', type: 'uint' }]);
|
||||
|
||||
expect(params.length).to.equal(1);
|
||||
expect(params[0].indexed).to.be.false;
|
||||
expect(params[0].name).to.equal('foo');
|
||||
expect(params[0].kind.type).to.equal('uint');
|
||||
});
|
||||
});
|
||||
});
|
@ -1,17 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './event';
|
@ -1,88 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import Decoder from '../decoder/decoder';
|
||||
import Encoder from '../encoder/encoder';
|
||||
import Param from './param';
|
||||
import { methodSignature } from '../util/signature';
|
||||
|
||||
export default class Func {
|
||||
constructor (abi) {
|
||||
this._abi = abi;
|
||||
this._constant = !!abi.constant;
|
||||
this._payable = abi.payable;
|
||||
this._inputs = Param.toParams(abi.inputs || []);
|
||||
this._outputs = Param.toParams(abi.outputs || []);
|
||||
|
||||
const { id, name, signature } = methodSignature(abi.name, this.inputParamTypes());
|
||||
|
||||
this._id = id;
|
||||
this._name = name;
|
||||
this._signature = signature;
|
||||
}
|
||||
|
||||
get abi () {
|
||||
return this._abi;
|
||||
}
|
||||
|
||||
get constant () {
|
||||
return this._constant;
|
||||
}
|
||||
|
||||
get name () {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
get id () {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
get payable () {
|
||||
return this._payable;
|
||||
}
|
||||
|
||||
get inputs () {
|
||||
return this._inputs;
|
||||
}
|
||||
|
||||
get outputs () {
|
||||
return this._outputs;
|
||||
}
|
||||
|
||||
get signature () {
|
||||
return this._signature;
|
||||
}
|
||||
|
||||
inputParamTypes () {
|
||||
return this._inputs.map((input) => input.kind);
|
||||
}
|
||||
|
||||
outputParamTypes () {
|
||||
return this._outputs.map((output) => output.kind);
|
||||
}
|
||||
|
||||
encodeCall (tokens) {
|
||||
return `${this._signature}${Encoder.encode(tokens)}`;
|
||||
}
|
||||
|
||||
decodeInput (data) {
|
||||
return Decoder.decode(this.inputParamTypes(), data);
|
||||
}
|
||||
|
||||
decodeOutput (data) {
|
||||
return Decoder.decode(this.outputParamTypes(), data);
|
||||
}
|
||||
}
|
@ -1,101 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import Func from './function';
|
||||
import Param from './param';
|
||||
import Token from '../token';
|
||||
|
||||
describe('abi/spec/Function', () => {
|
||||
const inputsArr = [{ name: 'boolin', type: 'bool' }, { name: 'stringin', type: 'string' }];
|
||||
const outputsArr = [{ name: 'output', type: 'uint' }];
|
||||
|
||||
const uint = new Param('output', 'uint');
|
||||
const bool = new Param('boolin', 'bool');
|
||||
const string = new Param('stringin', 'string');
|
||||
const inputs = [bool, string];
|
||||
const outputs = [uint];
|
||||
|
||||
const func = new Func({
|
||||
name: 'test',
|
||||
inputs: inputsArr,
|
||||
outputs: outputsArr
|
||||
});
|
||||
|
||||
describe('constructor', () => {
|
||||
it('returns signature correctly if name already contains it', () => {
|
||||
const func = new Func({
|
||||
name: 'test(bool,string)',
|
||||
inputs: inputsArr,
|
||||
outputs: outputsArr
|
||||
});
|
||||
|
||||
expect(func.name).to.equal('test');
|
||||
expect(func.id).to.equal('test(bool,string)');
|
||||
expect(func.signature).to.equal('02356205');
|
||||
});
|
||||
|
||||
it('stores the parameters as received', () => {
|
||||
expect(func.name).to.equal('test');
|
||||
expect(func.constant).to.be.false;
|
||||
expect(func.inputs).to.deep.equal(inputs);
|
||||
expect(func.outputs).to.deep.equal(outputs);
|
||||
});
|
||||
|
||||
it('matches empty inputs with []', () => {
|
||||
expect(new Func({ name: 'test', outputs: outputsArr }).inputs).to.deep.equal([]);
|
||||
});
|
||||
|
||||
it('matches empty outputs with []', () => {
|
||||
expect(new Func({ name: 'test', inputs: inputsArr }).outputs).to.deep.equal([]);
|
||||
});
|
||||
|
||||
it('sets the method signature', () => {
|
||||
expect(new Func({ name: 'baz' }).signature).to.equal('a7916fac');
|
||||
});
|
||||
|
||||
it('allows constant functions', () => {
|
||||
expect(new Func({ name: 'baz', constant: true }).constant).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe('inputParamTypes', () => {
|
||||
it('retrieves the input types as received', () => {
|
||||
expect(func.inputParamTypes()).to.deep.equal([bool.kind, string.kind]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('outputParamTypes', () => {
|
||||
it('retrieves the output types as received', () => {
|
||||
expect(func.outputParamTypes()).to.deep.equal([uint.kind]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('encodeCall', () => {
|
||||
it('encodes the call correctly', () => {
|
||||
const result = func.encodeCall([new Token('bool', true), new Token('string', 'jacogr')]);
|
||||
|
||||
expect(result).to.equal('023562050000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000066a61636f67720000000000000000000000000000000000000000000000000000');
|
||||
});
|
||||
});
|
||||
|
||||
describe('decodeOutput', () => {
|
||||
it('decodes the result correctly', () => {
|
||||
const result = func.decodeOutput('1111111111111111111111111111111111111111111111111111111111111111');
|
||||
|
||||
expect(result[0].value.toString(16)).to.equal('1111111111111111111111111111111111111111111111111111111111111111');
|
||||
});
|
||||
});
|
||||
});
|
@ -1,17 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './interface';
|
@ -1,77 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import Constructor from './constructor';
|
||||
import Event from './event/event';
|
||||
import Func from './function';
|
||||
import Token from '../token';
|
||||
|
||||
export default class Interface {
|
||||
constructor (abi) {
|
||||
this._interface = Interface.parseABI(abi);
|
||||
}
|
||||
|
||||
get interface () {
|
||||
return this._interface;
|
||||
}
|
||||
|
||||
get constructors () {
|
||||
return this._interface.filter((item) => item instanceof Constructor);
|
||||
}
|
||||
|
||||
get events () {
|
||||
return this._interface.filter((item) => item instanceof Event);
|
||||
}
|
||||
|
||||
get functions () {
|
||||
return this._interface.filter((item) => item instanceof Func);
|
||||
}
|
||||
|
||||
encodeTokens (paramTypes, values) {
|
||||
return Interface.encodeTokens(paramTypes, values);
|
||||
}
|
||||
|
||||
static encodeTokens (paramTypes, values) {
|
||||
const createToken = function (paramType, value) {
|
||||
if (paramType.subtype) {
|
||||
return new Token(paramType.type, value.map((entry) => createToken(paramType.subtype, entry)));
|
||||
}
|
||||
|
||||
return new Token(paramType.type, value);
|
||||
};
|
||||
|
||||
return paramTypes.map((paramType, idx) => createToken(paramType, values[idx]));
|
||||
}
|
||||
|
||||
static parseABI (abi) {
|
||||
return abi.map((item) => {
|
||||
switch (item.type) {
|
||||
case 'constructor':
|
||||
return new Constructor(item);
|
||||
|
||||
case 'event':
|
||||
return new Event(item);
|
||||
|
||||
case 'function':
|
||||
case 'fallback':
|
||||
return new Func(item);
|
||||
|
||||
default:
|
||||
throw new Error(`Unknown ABI type ${item.type}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -1,126 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import Interface from './interface';
|
||||
import ParamType from './paramType';
|
||||
import Token from '../token';
|
||||
|
||||
describe('abi/spec/Interface', () => {
|
||||
const construct = {
|
||||
type: 'constructor',
|
||||
inputs: []
|
||||
};
|
||||
const event = {
|
||||
type: 'event',
|
||||
name: 'Event2',
|
||||
anonymous: false,
|
||||
inputs: [{ name: 'a', type: 'uint256', indexed: true }, { name: 'b', type: 'bytes32', indexed: false }]
|
||||
};
|
||||
const func = {
|
||||
type: 'function',
|
||||
name: 'foo',
|
||||
inputs: [{ name: 'a', type: 'uint256' }],
|
||||
outputs: []
|
||||
};
|
||||
|
||||
describe('parseABI', () => {
|
||||
it('throws on invalid types', () => {
|
||||
expect(() => Interface.parseABI([{ type: 'noMatch' }])).to.throw(/noMatch/);
|
||||
});
|
||||
|
||||
it('creates constructors', () => {
|
||||
expect(Interface.parseABI([ construct ])).to.deep.equal([{ _inputs: [] }]);
|
||||
});
|
||||
|
||||
it('creates events', () => {
|
||||
expect(Interface.parseABI([ event ])[0].name).to.equal('Event2');
|
||||
});
|
||||
|
||||
it('creates functions', () => {
|
||||
expect(Interface.parseABI([ func ])[0].name).to.equal('foo');
|
||||
});
|
||||
|
||||
it('parse complex interfaces', () => {
|
||||
expect(Interface.parseABI([ construct, event, func ]).length).to.equal(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('constructor', () => {
|
||||
const int = new Interface([ construct, event, func ]);
|
||||
|
||||
it('contains the full interface', () => {
|
||||
expect(int.interface.length).to.equal(3);
|
||||
});
|
||||
|
||||
it('contains the constructors', () => {
|
||||
expect(int.constructors.length).to.equal(1);
|
||||
});
|
||||
|
||||
it('contains the events', () => {
|
||||
expect(int.events.length).to.equal(1);
|
||||
});
|
||||
|
||||
it('contains the functions', () => {
|
||||
expect(int.functions.length).to.equal(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('encodeTokens', () => {
|
||||
const int = new Interface([ construct, event, func ]);
|
||||
|
||||
it('encodes simple types', () => {
|
||||
expect(
|
||||
int.encodeTokens(
|
||||
[new ParamType('bool'), new ParamType('string'), new ParamType('int'), new ParamType('uint')],
|
||||
[true, 'gavofyork', -123, 123]
|
||||
)
|
||||
).to.deep.equal([
|
||||
new Token('bool', true), new Token('string', 'gavofyork'), new Token('int', -123), new Token('uint', 123)
|
||||
]);
|
||||
});
|
||||
|
||||
it('encodes array', () => {
|
||||
expect(
|
||||
int.encodeTokens(
|
||||
[new ParamType('array', new ParamType('bool'))],
|
||||
[[true, false, true]]
|
||||
)
|
||||
).to.deep.equal([
|
||||
new Token('array', [
|
||||
new Token('bool', true), new Token('bool', false), new Token('bool', true)
|
||||
])
|
||||
]);
|
||||
});
|
||||
|
||||
it('encodes simple with array of array', () => {
|
||||
expect(
|
||||
int.encodeTokens(
|
||||
[
|
||||
new ParamType('bool'),
|
||||
new ParamType('fixedArray', new ParamType('array', new ParamType('uint')), 2)
|
||||
],
|
||||
[true, [[0, 1], [2, 3]]]
|
||||
)
|
||||
).to.deep.equal([
|
||||
new Token('bool', true),
|
||||
new Token('fixedArray', [
|
||||
new Token('array', [new Token('uint', 0), new Token('uint', 1)]),
|
||||
new Token('array', [new Token('uint', 2), new Token('uint', 3)])
|
||||
])
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
@ -1,42 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { toParamType } from './paramType/format';
|
||||
|
||||
export default class Param {
|
||||
constructor (name, type) {
|
||||
this._name = name;
|
||||
this._kind = toParamType(type);
|
||||
}
|
||||
|
||||
get name () {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
get kind () {
|
||||
return this._kind;
|
||||
}
|
||||
|
||||
static toParams (params) {
|
||||
return params.map((param) => {
|
||||
if (param instanceof Param) {
|
||||
return param;
|
||||
}
|
||||
|
||||
return new Param(param.name, param.type);
|
||||
});
|
||||
}
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import Param from './param';
|
||||
|
||||
describe('abi/spec/Param', () => {
|
||||
describe('constructor', () => {
|
||||
const param = new Param('foo', 'uint');
|
||||
|
||||
it('sets the properties', () => {
|
||||
expect(param.name).to.equal('foo');
|
||||
expect(param.kind.type).to.equal('uint');
|
||||
});
|
||||
});
|
||||
|
||||
describe('toParams', () => {
|
||||
it('maps an array of params', () => {
|
||||
const params = Param.toParams([{ name: 'foo', type: 'uint' }]);
|
||||
|
||||
expect(params.length).to.equal(1);
|
||||
expect(params[0].name).to.equal('foo');
|
||||
expect(params[0].kind.type).to.equal('uint');
|
||||
});
|
||||
|
||||
it('converts only if needed', () => {
|
||||
const _params = Param.toParams([{ name: 'foo', type: 'uint' }]);
|
||||
const params = Param.toParams(_params);
|
||||
|
||||
expect(params.length).to.equal(1);
|
||||
expect(params[0].name).to.equal('foo');
|
||||
expect(params[0].kind.type).to.equal('uint');
|
||||
});
|
||||
});
|
||||
});
|
@ -1,80 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import ParamType from './paramType';
|
||||
|
||||
export function toParamType (type, indexed) {
|
||||
if (type[type.length - 1] === ']') {
|
||||
const last = type.lastIndexOf('[');
|
||||
const length = type.substr(last + 1, type.length - last - 2);
|
||||
const subtype = toParamType(type.substr(0, last));
|
||||
|
||||
if (length.length === 0) {
|
||||
return new ParamType('array', subtype, 0, indexed);
|
||||
}
|
||||
|
||||
return new ParamType('fixedArray', subtype, parseInt(length, 10), indexed);
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case 'address':
|
||||
case 'bool':
|
||||
case 'bytes':
|
||||
case 'string':
|
||||
return new ParamType(type, null, 0, indexed);
|
||||
|
||||
case 'int':
|
||||
case 'uint':
|
||||
return new ParamType(type, null, 256, indexed);
|
||||
|
||||
default:
|
||||
if (type.indexOf('uint') === 0) {
|
||||
return new ParamType('uint', null, parseInt(type.substr(4), 10), indexed);
|
||||
} else if (type.indexOf('int') === 0) {
|
||||
return new ParamType('int', null, parseInt(type.substr(3), 10), indexed);
|
||||
} else if (type.indexOf('bytes') === 0) {
|
||||
return new ParamType('fixedBytes', null, parseInt(type.substr(5), 10), indexed);
|
||||
}
|
||||
|
||||
throw new Error(`Cannot convert ${type} to valid ParamType`);
|
||||
}
|
||||
}
|
||||
|
||||
export function fromParamType (paramType) {
|
||||
switch (paramType.type) {
|
||||
case 'address':
|
||||
case 'bool':
|
||||
case 'bytes':
|
||||
case 'string':
|
||||
return paramType.type;
|
||||
|
||||
case 'int':
|
||||
case 'uint':
|
||||
return `${paramType.type}${paramType.length}`;
|
||||
|
||||
case 'fixedBytes':
|
||||
return `bytes${paramType.length}`;
|
||||
|
||||
case 'fixedArray':
|
||||
return `${fromParamType(paramType.subtype)}[${paramType.length}]`;
|
||||
|
||||
case 'array':
|
||||
return `${fromParamType(paramType.subtype)}[]`;
|
||||
|
||||
default:
|
||||
throw new Error(`Cannot convert from ParamType ${paramType.type}`);
|
||||
}
|
||||
}
|
@ -1,228 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import ParamType from './paramType';
|
||||
import { fromParamType, toParamType } from './format';
|
||||
|
||||
describe('abi/spec/paramType/format', () => {
|
||||
describe('fromParamType', () => {
|
||||
it('errors on invalid types', () => {
|
||||
expect(() => fromParamType({ type: 'noMatch' })).to.throw(/noMatch/);
|
||||
});
|
||||
|
||||
describe('simple types', () => {
|
||||
it('converts address to address', () => {
|
||||
const pt = new ParamType('address');
|
||||
|
||||
expect(fromParamType(pt)).to.equal('address');
|
||||
});
|
||||
|
||||
it('converts bool to bool', () => {
|
||||
const pt = new ParamType('bool');
|
||||
|
||||
expect(fromParamType(pt)).to.equal('bool');
|
||||
});
|
||||
|
||||
it('converts bytes to bytes', () => {
|
||||
const pt = new ParamType('bytes');
|
||||
|
||||
expect(fromParamType(pt)).to.equal('bytes');
|
||||
});
|
||||
|
||||
it('converts string to string', () => {
|
||||
const pt = new ParamType('string');
|
||||
|
||||
expect(fromParamType(pt)).to.equal('string');
|
||||
});
|
||||
});
|
||||
|
||||
describe('length types', () => {
|
||||
it('converts int32 to int32', () => {
|
||||
const pt = new ParamType('int', null, 32);
|
||||
|
||||
expect(fromParamType(pt)).to.equal('int32');
|
||||
});
|
||||
|
||||
it('converts uint64 to int64', () => {
|
||||
const pt = new ParamType('uint', null, 64);
|
||||
|
||||
expect(fromParamType(pt)).to.equal('uint64');
|
||||
});
|
||||
|
||||
it('converts fixedBytes8 to bytes8', () => {
|
||||
const pt = new ParamType('fixedBytes', null, 8);
|
||||
|
||||
expect(fromParamType(pt)).to.equal('bytes8');
|
||||
});
|
||||
});
|
||||
|
||||
describe('arrays', () => {
|
||||
it('converts string[2] to string[2]', () => {
|
||||
const pt = new ParamType('fixedArray', new ParamType('string'), 2);
|
||||
|
||||
expect(fromParamType(pt)).to.equal('string[2]');
|
||||
});
|
||||
|
||||
it('converts bool[] to bool[]', () => {
|
||||
const pt = new ParamType('array', new ParamType('bool'));
|
||||
|
||||
expect(fromParamType(pt)).to.equal('bool[]');
|
||||
});
|
||||
|
||||
it('converts bool[][2] to bool[][2]', () => {
|
||||
const pt = new ParamType('fixedArray', new ParamType('array', new ParamType('bool')), 2);
|
||||
|
||||
expect(fromParamType(pt)).to.equal('bool[][2]');
|
||||
});
|
||||
|
||||
it('converts bool[2][] to bool[2][]', () => {
|
||||
const pt = new ParamType('array', new ParamType('fixedArray', new ParamType('bool'), 2));
|
||||
|
||||
expect(fromParamType(pt)).to.equal('bool[2][]');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('toParamType', () => {
|
||||
it('errors on invalid types', () => {
|
||||
expect(() => toParamType('noMatch')).to.throw(/noMatch/);
|
||||
});
|
||||
|
||||
describe('simple mapping', () => {
|
||||
it('converts address to address', () => {
|
||||
const pt = toParamType('address');
|
||||
|
||||
expect(pt.type).to.equal('address');
|
||||
});
|
||||
|
||||
it('converts bool to bool', () => {
|
||||
const pt = toParamType('bool');
|
||||
|
||||
expect(pt.type).to.equal('bool');
|
||||
});
|
||||
|
||||
it('converts bytes to bytes', () => {
|
||||
const pt = toParamType('bytes');
|
||||
|
||||
expect(pt.type).to.equal('bytes');
|
||||
});
|
||||
|
||||
it('converts string to string', () => {
|
||||
const pt = toParamType('string');
|
||||
|
||||
expect(pt.type).to.equal('string');
|
||||
});
|
||||
});
|
||||
|
||||
describe('number', () => {
|
||||
it('converts int to int256', () => {
|
||||
const pt = toParamType('int');
|
||||
|
||||
expect(pt.type).to.equal('int');
|
||||
expect(pt.length).to.equal(256);
|
||||
});
|
||||
|
||||
it('converts uint to uint256', () => {
|
||||
const pt = toParamType('uint');
|
||||
|
||||
expect(pt.type).to.equal('uint');
|
||||
expect(pt.length).to.equal(256);
|
||||
});
|
||||
});
|
||||
|
||||
describe('sized types', () => {
|
||||
it('converts int32 to int32', () => {
|
||||
const pt = toParamType('int32');
|
||||
|
||||
expect(pt.type).to.equal('int');
|
||||
expect(pt.length).to.equal(32);
|
||||
});
|
||||
|
||||
it('converts uint16 to uint16', () => {
|
||||
const pt = toParamType('uint32');
|
||||
|
||||
expect(pt.type).to.equal('uint');
|
||||
expect(pt.length).to.equal(32);
|
||||
});
|
||||
|
||||
it('converts bytes8 to fixedBytes8', () => {
|
||||
const pt = toParamType('bytes8');
|
||||
|
||||
expect(pt.type).to.equal('fixedBytes');
|
||||
expect(pt.length).to.equal(8);
|
||||
});
|
||||
});
|
||||
|
||||
describe('arrays', () => {
|
||||
describe('fixed arrays', () => {
|
||||
it('creates fixed array', () => {
|
||||
const pt = toParamType('bytes[8]');
|
||||
|
||||
expect(pt.type).to.equal('fixedArray');
|
||||
expect(pt.subtype.type).to.equal('bytes');
|
||||
expect(pt.length).to.equal(8);
|
||||
});
|
||||
|
||||
it('creates fixed arrays of fixed arrays', () => {
|
||||
const pt = toParamType('bytes[45][3]');
|
||||
|
||||
expect(pt.type).to.equal('fixedArray');
|
||||
expect(pt.length).to.equal(3);
|
||||
expect(pt.subtype.type).to.equal('fixedArray');
|
||||
expect(pt.subtype.length).to.equal(45);
|
||||
expect(pt.subtype.subtype.type).to.equal('bytes');
|
||||
});
|
||||
});
|
||||
|
||||
describe('dynamic arrays', () => {
|
||||
it('creates a dynamic array', () => {
|
||||
const pt = toParamType('bytes[]');
|
||||
|
||||
expect(pt.type).to.equal('array');
|
||||
expect(pt.subtype.type).to.equal('bytes');
|
||||
});
|
||||
|
||||
it('creates a dynamic array of dynamic arrays', () => {
|
||||
const pt = toParamType('bool[][]');
|
||||
|
||||
expect(pt.type).to.equal('array');
|
||||
expect(pt.subtype.type).to.equal('array');
|
||||
expect(pt.subtype.subtype.type).to.equal('bool');
|
||||
});
|
||||
});
|
||||
|
||||
describe('mixed arrays', () => {
|
||||
it('creates a fixed dynamic array', () => {
|
||||
const pt = toParamType('bool[][3]');
|
||||
|
||||
expect(pt.type).to.equal('fixedArray');
|
||||
expect(pt.length).to.equal(3);
|
||||
expect(pt.subtype.type).to.equal('array');
|
||||
expect(pt.subtype.subtype.type).to.equal('bool');
|
||||
});
|
||||
|
||||
it('creates a dynamic fixed array', () => {
|
||||
const pt = toParamType('bool[3][]');
|
||||
|
||||
expect(pt.type).to.equal('array');
|
||||
expect(pt.subtype.type).to.equal('fixedArray');
|
||||
expect(pt.subtype.length).to.equal(3);
|
||||
expect(pt.subtype.subtype.type).to.equal('bool');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -1,17 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './paramType';
|
@ -1,52 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import TYPES from './types';
|
||||
|
||||
export default class ParamType {
|
||||
constructor (type, subtype = null, length = 0, indexed = false) {
|
||||
ParamType.validateType(type);
|
||||
|
||||
this._type = type;
|
||||
this._subtype = subtype;
|
||||
this._length = length;
|
||||
this._indexed = indexed;
|
||||
}
|
||||
|
||||
get type () {
|
||||
return this._type;
|
||||
}
|
||||
|
||||
get subtype () {
|
||||
return this._subtype;
|
||||
}
|
||||
|
||||
get length () {
|
||||
return this._length;
|
||||
}
|
||||
|
||||
get indexed () {
|
||||
return this._indexed;
|
||||
}
|
||||
|
||||
static validateType (type) {
|
||||
if (TYPES.filter((_type) => type === _type).length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
throw new Error(`Invalid type ${type} received for ParamType`);
|
||||
}
|
||||
}
|
@ -1,87 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import ParamType from './paramType';
|
||||
|
||||
describe('abi/spec/paramType/ParamType', () => {
|
||||
describe('validateType', () => {
|
||||
it('validates address', () => {
|
||||
expect(ParamType.validateType('address')).to.be.true;
|
||||
});
|
||||
|
||||
it('validates fixedArray', () => {
|
||||
expect(ParamType.validateType('fixedArray')).to.be.true;
|
||||
});
|
||||
|
||||
it('validates array', () => {
|
||||
expect(ParamType.validateType('array')).to.be.true;
|
||||
});
|
||||
|
||||
it('validates fixedBytes', () => {
|
||||
expect(ParamType.validateType('fixedBytes')).to.be.true;
|
||||
});
|
||||
|
||||
it('validates bytes', () => {
|
||||
expect(ParamType.validateType('bytes')).to.be.true;
|
||||
});
|
||||
|
||||
it('validates bool', () => {
|
||||
expect(ParamType.validateType('bool')).to.be.true;
|
||||
});
|
||||
|
||||
it('validates int', () => {
|
||||
expect(ParamType.validateType('int')).to.be.true;
|
||||
});
|
||||
|
||||
it('validates uint', () => {
|
||||
expect(ParamType.validateType('uint')).to.be.true;
|
||||
});
|
||||
|
||||
it('validates string', () => {
|
||||
expect(ParamType.validateType('string')).to.be.true;
|
||||
});
|
||||
|
||||
it('throws an error on invalid types', () => {
|
||||
expect(() => ParamType.validateType('noMatch')).to.throw(/noMatch/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('constructor', () => {
|
||||
it('throws an error on invalid types', () => {
|
||||
expect(() => new ParamType('noMatch')).to.throw(/noMatch/);
|
||||
});
|
||||
|
||||
it('sets the type of the object', () => {
|
||||
expect((new ParamType('bool', null, 1)).type).to.equal('bool');
|
||||
});
|
||||
|
||||
it('sets the subtype of the object', () => {
|
||||
expect((new ParamType('array', 'bool', 1)).subtype).to.equal('bool');
|
||||
});
|
||||
|
||||
it('sets the length of the object', () => {
|
||||
expect((new ParamType('array', 'bool', 1)).length).to.equal(1);
|
||||
});
|
||||
|
||||
it('sets the index of the object', () => {
|
||||
expect((new ParamType('array', 'bool', 1, true)).indexed).to.be.true;
|
||||
});
|
||||
|
||||
it('sets default values where none supplied', () => {
|
||||
expect(Object.values(new ParamType('string'))).to.deep.equal(['string', null, 0, false]);
|
||||
});
|
||||
});
|
||||
});
|
@ -1,19 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
const TYPES = ['address', 'bytes', 'int', 'uint', 'bool', 'string', 'array', 'fixedBytes', 'fixedArray'];
|
||||
|
||||
export default TYPES;
|
@ -1,17 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './token';
|
@ -1,42 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import TYPES from '../spec/paramType/types';
|
||||
|
||||
export default class Token {
|
||||
constructor (type, value) {
|
||||
Token.validateType(type);
|
||||
|
||||
this._type = type;
|
||||
this._value = value;
|
||||
}
|
||||
|
||||
get type () {
|
||||
return this._type;
|
||||
}
|
||||
|
||||
get value () {
|
||||
return this._value;
|
||||
}
|
||||
|
||||
static validateType (type) {
|
||||
if (TYPES.filter((_type) => type === _type).length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
throw new Error(`Invalid type ${type} received for Token`);
|
||||
}
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import Token from './token';
|
||||
|
||||
describe('abi/token/token', () => {
|
||||
describe('validateType', () => {
|
||||
it('validates address', () => {
|
||||
expect(Token.validateType('address')).to.be.true;
|
||||
});
|
||||
|
||||
it('validates fixedArray', () => {
|
||||
expect(Token.validateType('fixedArray')).to.be.true;
|
||||
});
|
||||
|
||||
it('validates array', () => {
|
||||
expect(Token.validateType('array')).to.be.true;
|
||||
});
|
||||
|
||||
it('validates fixedBytes', () => {
|
||||
expect(Token.validateType('fixedBytes')).to.be.true;
|
||||
});
|
||||
|
||||
it('validates bytes', () => {
|
||||
expect(Token.validateType('bytes')).to.be.true;
|
||||
});
|
||||
|
||||
it('validates bool', () => {
|
||||
expect(Token.validateType('bool')).to.be.true;
|
||||
});
|
||||
|
||||
it('validates int', () => {
|
||||
expect(Token.validateType('int')).to.be.true;
|
||||
});
|
||||
|
||||
it('validates uint', () => {
|
||||
expect(Token.validateType('uint')).to.be.true;
|
||||
});
|
||||
|
||||
it('validates string', () => {
|
||||
expect(Token.validateType('string')).to.be.true;
|
||||
});
|
||||
|
||||
it('throws an error on invalid types', () => {
|
||||
expect(() => Token.validateType('noMatch')).to.throw(/noMatch/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('constructor', () => {
|
||||
it('throws an error on invalid types', () => {
|
||||
expect(() => new Token('noMatch', '1')).to.throw(/noMatch/);
|
||||
});
|
||||
|
||||
it('sets the type of the object', () => {
|
||||
expect((new Token('bool', '1')).type).to.equal('bool');
|
||||
});
|
||||
|
||||
it('sets the value of the object', () => {
|
||||
expect((new Token('bool', '1')).value).to.equal('1');
|
||||
});
|
||||
});
|
||||
});
|
@ -1,66 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { keccak_256 } from 'js-sha3'; // eslint-disable-line camelcase
|
||||
|
||||
export function isChecksumValid (_address) {
|
||||
const address = _address.replace('0x', '');
|
||||
const hash = keccak_256(address.toLowerCase());
|
||||
|
||||
for (let n = 0; n < 40; n++) {
|
||||
const char = address[n];
|
||||
const isLower = char !== char.toUpperCase();
|
||||
const isUpper = char !== char.toLowerCase();
|
||||
const hashval = parseInt(hash[n], 16);
|
||||
|
||||
if ((hashval > 7 && isLower) || (hashval <= 7 && isUpper)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export function isAddress (address) {
|
||||
if (address && address.length === 42) {
|
||||
if (!/^(0x)?[0-9a-f]{40}$/i.test(address)) {
|
||||
return false;
|
||||
} else if (/^(0x)?[0-9a-f]{40}$/.test(address) || /^(0x)?[0-9A-F]{40}$/.test(address)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return isChecksumValid(address);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function toChecksumAddress (_address) {
|
||||
const address = (_address || '').toLowerCase();
|
||||
|
||||
if (!isAddress(address)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const hash = keccak_256(address.slice(-40));
|
||||
let result = '0x';
|
||||
|
||||
for (let n = 0; n < 40; n++) {
|
||||
result = `${result}${parseInt(hash[n], 16) > 7 ? address[n + 2].toUpperCase() : address[n + 2]}`;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
@ -1,100 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { isChecksumValid, isAddress, toChecksumAddress } from './address';
|
||||
|
||||
describe('abi/util/address', () => {
|
||||
const value = '63Cf90D3f0410092FC0fca41846f596223979195';
|
||||
const address = `0x${value}`;
|
||||
const lowercase = `0x${value.toLowerCase()}`;
|
||||
const uppercase = `0x${value.toUpperCase()}`;
|
||||
const invalid = '0x' + value.split('').map((char) => {
|
||||
if (char >= 'a' && char <= 'f') {
|
||||
return char.toUpperCase();
|
||||
} else if (char >= 'A' && char <= 'F') {
|
||||
return char.toLowerCase();
|
||||
}
|
||||
|
||||
return char;
|
||||
}).join('');
|
||||
const invalidhex = '0x01234567890123456789012345678901234567gh';
|
||||
|
||||
describe('isChecksumValid', () => {
|
||||
it('returns false when fully lowercase', () => {
|
||||
expect(isChecksumValid(lowercase)).to.be.false;
|
||||
});
|
||||
|
||||
it('returns false when fully uppercase', () => {
|
||||
expect(isChecksumValid(uppercase)).to.be.false;
|
||||
});
|
||||
|
||||
it('returns false on a mixed-case address', () => {
|
||||
expect(isChecksumValid(invalid)).to.be.false;
|
||||
});
|
||||
|
||||
it('returns true on a checksummed address', () => {
|
||||
expect(isChecksumValid(address)).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe('isAddress', () => {
|
||||
it('returns true when fully lowercase', () => {
|
||||
expect(isAddress(lowercase)).to.be.true;
|
||||
});
|
||||
|
||||
it('returns true when fully uppercase', () => {
|
||||
expect(isAddress(uppercase)).to.be.true;
|
||||
});
|
||||
|
||||
it('returns true when checksummed', () => {
|
||||
expect(isAddress(address)).to.be.true;
|
||||
});
|
||||
|
||||
it('returns false when invalid checksum', () => {
|
||||
expect(isAddress(invalid)).to.be.false;
|
||||
});
|
||||
|
||||
it('returns false on valid length, non-hex', () => {
|
||||
expect(isAddress(invalidhex)).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
describe('toChecksumAddress', () => {
|
||||
it('returns empty when no address specified', () => {
|
||||
expect(toChecksumAddress()).to.equal('');
|
||||
});
|
||||
|
||||
it('returns empty on invalid address structure', () => {
|
||||
expect(toChecksumAddress('0xnotaddress')).to.equal('');
|
||||
});
|
||||
|
||||
it('returns formatted address on checksum input', () => {
|
||||
expect(toChecksumAddress(address)).to.equal(address);
|
||||
});
|
||||
|
||||
it('returns formatted address on lowercase input', () => {
|
||||
expect(toChecksumAddress(lowercase)).to.equal(address);
|
||||
});
|
||||
|
||||
it('returns formatted address on uppercase input', () => {
|
||||
expect(toChecksumAddress(uppercase)).to.equal(address);
|
||||
});
|
||||
|
||||
it('returns formatted address on mixed input', () => {
|
||||
expect(toChecksumAddress(invalid)).to.equal(address);
|
||||
});
|
||||
});
|
||||
});
|
@ -1,77 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import BigNumber from 'bignumber.js';
|
||||
import utf8 from 'utf8';
|
||||
|
||||
import { isArray } from './types';
|
||||
|
||||
const ZERO_64 = '0000000000000000000000000000000000000000000000000000000000000000';
|
||||
|
||||
export function padAddress (_input) {
|
||||
const input = _input.substr(0, 2) === '0x' ? _input.substr(2) : _input;
|
||||
|
||||
return `${ZERO_64}${input}`.slice(-64);
|
||||
}
|
||||
|
||||
export function padBool (input) {
|
||||
return `${ZERO_64}${input ? '1' : '0'}`.slice(-64);
|
||||
}
|
||||
|
||||
export function padU32 (input) {
|
||||
let bn = new BigNumber(input);
|
||||
|
||||
if (bn.lessThan(0)) {
|
||||
bn = new BigNumber('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 16)
|
||||
.plus(bn).plus(1);
|
||||
}
|
||||
|
||||
return `${ZERO_64}${bn.toString(16)}`.slice(-64);
|
||||
}
|
||||
|
||||
function stringToBytes (input) {
|
||||
if (isArray(input)) {
|
||||
return input;
|
||||
} else if (input.substr(0, 2) === '0x') {
|
||||
const matches = input.substr(2).toLowerCase().match(/.{1,2}/g) || [];
|
||||
|
||||
return matches.map((value) => parseInt(value, 16));
|
||||
} else {
|
||||
return input.split('').map((char) => char.charCodeAt(0));
|
||||
}
|
||||
}
|
||||
|
||||
export function padBytes (_input) {
|
||||
const input = stringToBytes(_input);
|
||||
|
||||
return `${padU32(input.length)}${padFixedBytes(input)}`;
|
||||
}
|
||||
|
||||
export function padFixedBytes (_input) {
|
||||
const input = stringToBytes(_input);
|
||||
const sinput = input.map((code) => `0${code.toString(16)}`.slice(-2)).join('');
|
||||
const max = Math.floor((sinput.length + 63) / 64) * 64;
|
||||
|
||||
return `${sinput}${ZERO_64}`.substr(0, max);
|
||||
}
|
||||
|
||||
export function padString (input) {
|
||||
const array = utf8.encode(input)
|
||||
.split('')
|
||||
.map((char) => char.charCodeAt(0));
|
||||
|
||||
return padBytes(array);
|
||||
}
|
@ -1,124 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { padAddress, padBool, padBytes, padFixedBytes, padString, padU32 } from './pad';
|
||||
|
||||
describe('abi/util/pad', () => {
|
||||
const SHORT15 = '1234567890abcdef';
|
||||
const BYTES15 = [0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef];
|
||||
const LONG15 = `${SHORT15}000000000000000000000000000000000000000000000000`;
|
||||
const PAD123 = '0000000000000000000000000000000000000000000000000000000000000123';
|
||||
|
||||
describe('padAddress', () => {
|
||||
it('pads to 64 characters', () => {
|
||||
expect(padAddress('123')).to.equal(PAD123);
|
||||
});
|
||||
|
||||
it('strips leading 0x when passed in', () => {
|
||||
expect(padFixedBytes(`0x${PAD123}`)).to.equal(PAD123);
|
||||
});
|
||||
});
|
||||
|
||||
describe('padBool', () => {
|
||||
const TRUE = '0000000000000000000000000000000000000000000000000000000000000001';
|
||||
const FALSE = '0000000000000000000000000000000000000000000000000000000000000000';
|
||||
|
||||
it('pads true to 64 characters', () => {
|
||||
expect(padBool(true)).to.equal(TRUE);
|
||||
});
|
||||
|
||||
it('pads false to 64 characters', () => {
|
||||
expect(padBool(false)).to.equal(FALSE);
|
||||
});
|
||||
});
|
||||
|
||||
describe('padU32', () => {
|
||||
it('left pads length < 64 bytes to 64 bytes', () => {
|
||||
expect(padU32(1)).to.equal('0000000000000000000000000000000000000000000000000000000000000001');
|
||||
});
|
||||
|
||||
it('pads hex representation', () => {
|
||||
expect(padU32(0x123)).to.equal(PAD123);
|
||||
});
|
||||
|
||||
it('pads decimal representation', () => {
|
||||
expect(padU32(291)).to.equal(PAD123);
|
||||
});
|
||||
|
||||
it('pads string representation', () => {
|
||||
expect(padU32('0x123')).to.equal(PAD123);
|
||||
});
|
||||
|
||||
it('pads BigNumber representation', () => {
|
||||
expect(padU32(new BigNumber(0x123))).to.equal(PAD123);
|
||||
});
|
||||
|
||||
it('converts negative numbers to 2s complement', () => {
|
||||
expect(padU32(-123)).to.equal('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff85');
|
||||
});
|
||||
});
|
||||
|
||||
describe('padFixedBytes', () => {
|
||||
it('right pads length < 64 bytes to 64 bytes (string)', () => {
|
||||
expect(padFixedBytes(`0x${SHORT15}`)).to.equal(LONG15);
|
||||
});
|
||||
|
||||
it('right pads length < 64 bytes to 64 bytes (array)', () => {
|
||||
expect(padFixedBytes(BYTES15)).to.equal(LONG15);
|
||||
});
|
||||
|
||||
it('right pads length > 64 bytes (64 byte multiples)', () => {
|
||||
expect(padFixedBytes(`0x${LONG15}${SHORT15}`)).to.equal(`${LONG15}${LONG15}`);
|
||||
});
|
||||
|
||||
it('strips leading 0x when passed in', () => {
|
||||
expect(padFixedBytes(`0x${SHORT15}`)).to.equal(LONG15);
|
||||
});
|
||||
});
|
||||
|
||||
describe('padBytes', () => {
|
||||
it('right pads length < 64, adding the length (string)', () => {
|
||||
const result = padBytes(`0x${SHORT15}`);
|
||||
|
||||
expect(result.length).to.equal(128);
|
||||
expect(result).to.equal(`${padU32(8)}${LONG15}`);
|
||||
});
|
||||
|
||||
it('right pads length < 64, adding the length (array)', () => {
|
||||
const result = padBytes(BYTES15);
|
||||
|
||||
expect(result.length).to.equal(128);
|
||||
expect(result).to.equal(`${padU32(8)}${LONG15}`);
|
||||
});
|
||||
|
||||
it('right pads length > 64, adding the length', () => {
|
||||
const result = padBytes(`0x${LONG15}${SHORT15}`);
|
||||
|
||||
expect(result.length).to.equal(192);
|
||||
expect(result).to.equal(`${padU32(0x28)}${LONG15}${LONG15}`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('padString', () => {
|
||||
it('correctly converts & pads strings', () => {
|
||||
const result = padString('gavofyork');
|
||||
|
||||
expect(result.length).to.equal(128);
|
||||
expect(result).to.equal(padBytes('0x6761766f66796f726b'));
|
||||
});
|
||||
});
|
||||
});
|
@ -1,49 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { keccak_256 } from 'js-sha3'; // eslint-disable-line camelcase
|
||||
import { fromParamType } from '../spec/paramType/format';
|
||||
|
||||
export function eventSignature (eventName, params) {
|
||||
const { strName, name } = parseName(eventName);
|
||||
const types = (params || []).map(fromParamType).join(',');
|
||||
const id = `${strName}(${types})`;
|
||||
const signature = strName ? keccak_256(id) : '';
|
||||
|
||||
return { id, name, signature };
|
||||
}
|
||||
|
||||
export function methodSignature (methodName, params) {
|
||||
const { id, name, signature } = eventSignature(methodName, params);
|
||||
|
||||
return { id, name, signature: signature.substr(0, 8) };
|
||||
}
|
||||
|
||||
function parseName (name) {
|
||||
const strName = `${name || ''}`;
|
||||
const idx = strName.indexOf('(');
|
||||
|
||||
if (idx === -1) {
|
||||
return { strName, name };
|
||||
}
|
||||
|
||||
const trimmedName = strName.slice(0, idx);
|
||||
|
||||
return {
|
||||
strName: trimmedName,
|
||||
name: trimmedName
|
||||
};
|
||||
}
|
@ -1,111 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { eventSignature, methodSignature } from './signature';
|
||||
|
||||
describe('abi/util/signature', () => {
|
||||
describe('eventSignature', () => {
|
||||
it('encodes signature baz() correctly', () => {
|
||||
expect(eventSignature('baz', [])).to.deep.equal({
|
||||
id: 'baz()',
|
||||
name: 'baz',
|
||||
signature: 'a7916fac4f538170f7cd12c148552e2cba9fcd72329a2dd5b07a6fa906488ddf'
|
||||
});
|
||||
});
|
||||
|
||||
it('encodes signature baz(uint32) correctly', () => {
|
||||
expect(eventSignature('baz', [{ type: 'uint', length: 32 }])).to.deep.equal({
|
||||
id: 'baz(uint32)',
|
||||
name: 'baz',
|
||||
signature: '7d68785e8fc871be024b75964bd86d093511d4bc2dc7cf7bea32c48a0efaecb1'
|
||||
});
|
||||
});
|
||||
|
||||
it('encodes signature baz(uint32, bool) correctly', () => {
|
||||
expect(eventSignature('baz', [{ type: 'uint', length: 32 }, { type: 'bool' }])).to.deep.equal({
|
||||
id: 'baz(uint32,bool)',
|
||||
name: 'baz',
|
||||
signature: 'cdcd77c0992ec5bbfc459984220f8c45084cc24d9b6efed1fae540db8de801d2'
|
||||
});
|
||||
});
|
||||
|
||||
it('encodes no-name signature correctly as ()', () => {
|
||||
expect(eventSignature(undefined, [])).to.deep.equal({
|
||||
id: '()',
|
||||
name: undefined,
|
||||
signature: ''
|
||||
});
|
||||
});
|
||||
|
||||
it('encodes no-params signature correctly as ()', () => {
|
||||
expect(eventSignature(undefined, undefined)).to.deep.equal({
|
||||
id: '()',
|
||||
name: undefined,
|
||||
signature: ''
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('methodSignature', () => {
|
||||
it('encodes signature baz() correctly', () => {
|
||||
expect(methodSignature('baz', [])).to.deep.equal({
|
||||
id: 'baz()',
|
||||
name: 'baz',
|
||||
signature: 'a7916fac'
|
||||
});
|
||||
});
|
||||
|
||||
it('encodes signature baz(uint32) correctly', () => {
|
||||
expect(methodSignature('baz', [{ type: 'uint', length: 32 }])).to.deep.equal({
|
||||
id: 'baz(uint32)',
|
||||
name: 'baz',
|
||||
signature: '7d68785e'
|
||||
});
|
||||
});
|
||||
|
||||
it('encodes signature baz(uint32, bool) correctly', () => {
|
||||
expect(methodSignature('baz', [{ type: 'uint', length: 32 }, { type: 'bool' }])).to.deep.equal({
|
||||
id: 'baz(uint32,bool)',
|
||||
name: 'baz',
|
||||
signature: 'cdcd77c0'
|
||||
});
|
||||
});
|
||||
|
||||
it('encodes signature in name correctly', () => {
|
||||
expect(methodSignature('baz(uint32,bool)', [{ type: 'uint', length: 32 }, { type: 'bool' }])).to.deep.equal({
|
||||
id: 'baz(uint32,bool)',
|
||||
name: 'baz',
|
||||
signature: 'cdcd77c0'
|
||||
});
|
||||
});
|
||||
|
||||
it('encodes no-name signature correctly as ()', () => {
|
||||
expect(methodSignature(undefined, [])).to.deep.equal({
|
||||
id: '()',
|
||||
name: undefined,
|
||||
signature: ''
|
||||
});
|
||||
});
|
||||
|
||||
it('encodes no-params signature correctly as ()', () => {
|
||||
expect(methodSignature(undefined, undefined)).to.deep.equal({
|
||||
id: '()',
|
||||
name: undefined,
|
||||
signature: ''
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -1,31 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { padAddress } from './pad';
|
||||
|
||||
export function sliceData (_data) {
|
||||
if (!_data || !_data.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let data = (_data.substr(0, 2) === '0x') ? _data.substr(2) : _data;
|
||||
|
||||
if (!data.length) {
|
||||
data = padAddress('');
|
||||
}
|
||||
|
||||
return data.match(/.{1,64}/g);
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { sliceData } from './slice';
|
||||
|
||||
describe('abi/util/slice', () => {
|
||||
describe('sliceData', () => {
|
||||
const slice1 = '131a3afc00d1b1e3461b955e53fc866dcf303b3eb9f4c16f89e388930f48134b';
|
||||
const slice2 = '2124768576358735263578356373526387638357635873563586353756358763';
|
||||
|
||||
it('returns an empty array when length === 0', () => {
|
||||
expect(sliceData('')).to.deep.equal([]);
|
||||
});
|
||||
|
||||
it('returns an array with the slices otherwise', () => {
|
||||
const sliced = sliceData(`${slice1}${slice2}`);
|
||||
|
||||
expect(sliced.length).to.equal(2);
|
||||
expect(sliced[0]).to.equal(slice1);
|
||||
expect(sliced[1]).to.equal(slice2);
|
||||
});
|
||||
|
||||
it('removes leading 0x when passed in', () => {
|
||||
const sliced = sliceData(`0x${slice1}${slice2}`);
|
||||
|
||||
expect(sliced.length).to.equal(2);
|
||||
expect(sliced[0]).to.equal(slice1);
|
||||
expect(sliced[1]).to.equal(slice2);
|
||||
});
|
||||
});
|
||||
});
|
@ -1,47 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import BigNumber from 'bignumber.js';
|
||||
|
||||
import { toChecksumAddress } from './address';
|
||||
|
||||
export function asU32 (slice) {
|
||||
// TODO: validation
|
||||
|
||||
return new BigNumber(slice, 16);
|
||||
}
|
||||
|
||||
export function asI32 (slice) {
|
||||
if (new BigNumber(slice.substr(0, 1), 16).toString(2)[0] === '1') {
|
||||
return new BigNumber(slice, 16)
|
||||
.minus(new BigNumber('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 16))
|
||||
.minus(1);
|
||||
}
|
||||
|
||||
return new BigNumber(slice, 16);
|
||||
}
|
||||
|
||||
export function asAddress (slice) {
|
||||
// TODO: address validation?
|
||||
|
||||
return toChecksumAddress(`0x${slice.slice(-40)}`);
|
||||
}
|
||||
|
||||
export function asBool (slice) {
|
||||
// TODO: everything else should be 0
|
||||
|
||||
return new BigNumber(slice[63]).eq(1);
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { asAddress, asBool, asI32, asU32 } from './sliceAs';
|
||||
|
||||
describe('abi/util/sliceAs', () => {
|
||||
const MAX_INT = 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff';
|
||||
|
||||
describe('asAddress', () => {
|
||||
it('correctly returns the last 0x40 characters', () => {
|
||||
const address = '1111111111222222222233333333334444444444';
|
||||
|
||||
expect(asAddress(`000000000000000000000000${address}`)).to.equal(`0x${address}`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('asBool', () => {
|
||||
it('correctly returns true', () => {
|
||||
expect(asBool('0000000000000000000000000000000000000000000000000000000000000001')).to.be.true;
|
||||
});
|
||||
|
||||
it('correctly returns false', () => {
|
||||
expect(asBool('0000000000000000000000000000000000000000000000000000000000000000')).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
describe('asI32', () => {
|
||||
it('correctly decodes positive numbers', () => {
|
||||
expect(asI32('000000000000000000000000000000000000000000000000000000000000007b').toString()).to.equal('123');
|
||||
});
|
||||
|
||||
it('correctly decodes negative numbers', () => {
|
||||
expect(asI32('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff85').toString()).to.equal('-123');
|
||||
});
|
||||
});
|
||||
|
||||
describe('asU32', () => {
|
||||
it('returns a maxium U32', () => {
|
||||
expect(asU32(MAX_INT).toString(16)).to.equal(MAX_INT);
|
||||
});
|
||||
});
|
||||
});
|
@ -1,27 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export function isArray (test) {
|
||||
return Object.prototype.toString.call(test) === '[object Array]';
|
||||
}
|
||||
|
||||
export function isString (test) {
|
||||
return Object.prototype.toString.call(test) === '[object String]';
|
||||
}
|
||||
|
||||
export function isInstanceOf (test, clazz) {
|
||||
return test instanceof clazz;
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { isArray, isString, isInstanceOf } from './types';
|
||||
import Token from '../token';
|
||||
|
||||
describe('abi/util/types', () => {
|
||||
describe('isArray', () => {
|
||||
it('correctly identifies empty arrays as Array', () => {
|
||||
expect(isArray([])).to.be.true;
|
||||
});
|
||||
|
||||
it('correctly identifies non-empty arrays as Array', () => {
|
||||
expect(isArray([1, 2, 3])).to.be.true;
|
||||
});
|
||||
|
||||
it('correctly identifies strings as non-Array', () => {
|
||||
expect(isArray('not an array')).to.be.false;
|
||||
});
|
||||
|
||||
it('correctly identifies objects as non-Array', () => {
|
||||
expect(isArray({})).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
describe('isString', () => {
|
||||
it('correctly identifies empty string as string', () => {
|
||||
expect(isString('')).to.be.true;
|
||||
});
|
||||
|
||||
it('correctly identifies string as string', () => {
|
||||
expect(isString('123')).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe('isInstanceOf', () => {
|
||||
it('correctly identifies build-in instanceof', () => {
|
||||
expect(isInstanceOf(new String('123'), String)).to.be.true; // eslint-disable-line no-new-wrappers
|
||||
});
|
||||
|
||||
it('correctly identifies own instanceof', () => {
|
||||
expect(isInstanceOf(new Token('int', 123), Token)).to.be.true;
|
||||
});
|
||||
|
||||
it('correctly reports false for own', () => {
|
||||
expect(isInstanceOf({ type: 'int' }, Token)).to.be.false;
|
||||
});
|
||||
});
|
||||
});
|
@ -1,146 +0,0 @@
|
||||
# ethapi-js
|
||||
|
||||
A thin, fast, low-level Promise-based wrapper around the Ethereum APIs.
|
||||
|
||||
[![Build Status](https://travis-ci.org/jacogr/ethapi-js.svg?branch=master)](https://travis-ci.org/jacogr/ethapi-js)
|
||||
[![Coverage Status](https://coveralls.io/repos/github/jacogr/ethapi-js/badge.svg?branch=master)](https://coveralls.io/github/jacogr/ethapi-js?branch=master)
|
||||
[![Dependency Status](https://david-dm.org/jacogr/ethapi-js.svg)](https://david-dm.org/jacogr/ethapi-js)
|
||||
[![devDependency Status](https://david-dm.org/jacogr/ethapi-js/dev-status.svg)](https://david-dm.org/jacogr/ethapi-js#info=devDependencies)
|
||||
|
||||
## contributing
|
||||
|
||||
Clone the repo and install dependencies via `npm install`. Tests can be executed via
|
||||
|
||||
- `npm run testOnce` (100% covered unit tests)
|
||||
- `npm run testE2E` (E2E against a running RPC-enabled testnet Parity/Geth instance, `parity --testnet` and for WebScokets, `geth --testnet --ws --wsorigins '*' --rpc`)
|
||||
- setting the environment `DEBUG=true` will display the RPC POST bodies and responses on E2E tests
|
||||
|
||||
## installation
|
||||
|
||||
Install the package with `npm install --save ethapi-js` from the [npm registry ethapi-js](https://www.npmjs.com/package/ethapi-js)
|
||||
|
||||
## usage
|
||||
|
||||
### initialisation
|
||||
|
||||
```javascript
|
||||
// import the actual EthApi class
|
||||
import EthApi from 'ethapi-js';
|
||||
|
||||
// do the setup
|
||||
const transport = new EthApi.Transport.Http('http://localhost:8545'); // or .Ws('ws://localhost:8546')
|
||||
const ethapi = new EthApi(transport);
|
||||
```
|
||||
|
||||
You will require native Promises and fetch support (latest browsers only), they can be utilised by
|
||||
|
||||
```javascript
|
||||
import 'isomorphic-fetch';
|
||||
|
||||
import es6Promise from 'es6-promise';
|
||||
es6Promise.polyfill();
|
||||
```
|
||||
|
||||
### making calls
|
||||
|
||||
perform a call
|
||||
|
||||
```javascript
|
||||
ethapi.eth
|
||||
.coinbase()
|
||||
.then((coinbase) => {
|
||||
console.log(`The coinbase is ${coinbase}`);
|
||||
});
|
||||
```
|
||||
|
||||
multiple promises
|
||||
|
||||
```javascript
|
||||
Promise
|
||||
.all([
|
||||
ethapi.eth.coinbase(),
|
||||
ethapi.net.listening()
|
||||
])
|
||||
.then(([coinbase, listening]) => {
|
||||
// do stuff here
|
||||
});
|
||||
```
|
||||
|
||||
chaining promises
|
||||
|
||||
```javascript
|
||||
ethapi.eth
|
||||
.newFilter({...})
|
||||
.then((filterId) => ethapi.eth.getFilterChanges(filterId))
|
||||
.then((changes) => {
|
||||
console.log(changes);
|
||||
});
|
||||
```
|
||||
|
||||
### contracts
|
||||
|
||||
attach contract
|
||||
|
||||
```javascript
|
||||
const abi = [{ name: 'callMe', inputs: [{ type: 'bool', ...}, { type: 'string', ...}]}, ...abi...];
|
||||
const contract = new ethapi.newContract(abi);
|
||||
```
|
||||
|
||||
deploy
|
||||
|
||||
```javascript
|
||||
contract
|
||||
.deploy('0xc0de', [params], 'superPassword')
|
||||
.then((address) => {
|
||||
console.log(`the contract was deployed at ${address}`);
|
||||
});
|
||||
```
|
||||
|
||||
attach a contract at address
|
||||
|
||||
```javascript
|
||||
// via the constructor & .at function
|
||||
const contract = api.newContract(abi).at('0xa9280...7347b');
|
||||
// or on an already initialised contract
|
||||
contract.at('0xa9280...7347b');
|
||||
// perform calls here
|
||||
```
|
||||
|
||||
find & call a function
|
||||
|
||||
```javascript
|
||||
contract.instance
|
||||
.myContractMethodName
|
||||
.call({}, [myContractMethodParameter]) // or estimateGas or sendTransaction
|
||||
.then((result) => {
|
||||
console.log(`the result was ${result}`);
|
||||
});
|
||||
```
|
||||
|
||||
parse events from transaction receipt
|
||||
|
||||
```javascript
|
||||
contract
|
||||
.parseTransactionEvents(txReceipt)
|
||||
.then((receipt) => {
|
||||
receipt.logs.forEach((log) => {
|
||||
console.log('log parameters', log.params);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## apis
|
||||
|
||||
APIs implement the calls as exposed in the [Ethcore JSON Ethereum RPC](https://github.com/paritytech/ethereum-rpc-json/) definitions. Mapping follows the naming conventions of the originals, i.e. `eth_call` becomes `eth.call`, `personal_accounts` becomes `personal.accounts`, etc.
|
||||
|
||||
- [ethapi.db](https://github.com/paritytech/ethereum-rpc-json/blob/master/interfaces.md#db)
|
||||
- [ethapi.eth](https://github.com/paritytech/ethereum-rpc-json/blob/master/interfaces.md#eth)
|
||||
- [ethapi.parity](https://github.com/paritytech/ethereum-rpc-json/blob/master/interfaces.md#parity)
|
||||
- [ethapi.net](https://github.com/paritytech/ethereum-rpc-json/blob/master/interfaces.md#net)
|
||||
- [ethapi.personal](https://github.com/paritytech/ethereum-rpc-json/blob/master/interfaces.md#personal)
|
||||
- [ethapi.shh](https://github.com/paritytech/ethereum-rpc-json/blob/master/interfaces.md#shh)
|
||||
- [ethapi.signer](https://github.com/paritytech/ethereum-rpc-json/blob/master/interfaces.md#signer)
|
||||
- [ethapi.trace](https://github.com/paritytech/ethereum-rpc-json/blob/master/interfaces.md#trace)
|
||||
- [ethapi.web3](https://github.com/paritytech/ethereum-rpc-json/blob/master/interfaces.md#web3)
|
||||
|
||||
As a verification step, all exposed interfaces are tested for existing and pointing to the correct endpoints by using the generated interfaces from the above repo.
|
@ -1,179 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import EventEmitter from 'eventemitter3';
|
||||
|
||||
import { Http, Ws } from './transport';
|
||||
import Contract from './contract';
|
||||
|
||||
import { Db, Eth, Parity, Net, Personal, Shh, Signer, Trace, Web3 } from './rpc';
|
||||
import Subscriptions from './subscriptions';
|
||||
import Pubsub from './pubsub';
|
||||
import util from './util';
|
||||
import { isFunction } from './util/types';
|
||||
|
||||
import LocalAccountsMiddleware from '~/api/local';
|
||||
|
||||
export default class Api extends EventEmitter {
|
||||
constructor (transport, allowSubscriptions = true) {
|
||||
super();
|
||||
|
||||
if (!transport || !isFunction(transport.execute)) {
|
||||
throw new Error('EthApi needs transport with execute() function defined');
|
||||
}
|
||||
|
||||
this._transport = transport;
|
||||
|
||||
this._db = new Db(transport);
|
||||
this._eth = new Eth(transport);
|
||||
this._net = new Net(transport);
|
||||
this._parity = new Parity(transport);
|
||||
this._personal = new Personal(transport);
|
||||
this._shh = new Shh(transport);
|
||||
this._signer = new Signer(transport);
|
||||
this._trace = new Trace(transport);
|
||||
this._web3 = new Web3(transport);
|
||||
|
||||
if (isFunction(transport.subscribe)) {
|
||||
this._pubsub = new Pubsub(transport);
|
||||
}
|
||||
|
||||
if (allowSubscriptions) {
|
||||
this._subscriptions = new Subscriptions(this);
|
||||
}
|
||||
// Doing a request here in test env would cause an error
|
||||
if (LocalAccountsMiddleware && process.env.NODE_ENV !== 'test') {
|
||||
const middleware = this.parity
|
||||
.nodeKind()
|
||||
.then((nodeKind) => {
|
||||
if (nodeKind.availability === 'public') {
|
||||
return LocalAccountsMiddleware;
|
||||
}
|
||||
|
||||
return null;
|
||||
})
|
||||
.catch(() => null);
|
||||
|
||||
transport.addMiddleware(middleware);
|
||||
}
|
||||
}
|
||||
|
||||
get pubsub () {
|
||||
if (!this._pubsub) {
|
||||
throw Error('Pubsub is only available with a subscribing-supported transport injected!');
|
||||
}
|
||||
return this._pubsub;
|
||||
}
|
||||
|
||||
get db () {
|
||||
return this._db;
|
||||
}
|
||||
|
||||
get eth () {
|
||||
return this._eth;
|
||||
}
|
||||
|
||||
get parity () {
|
||||
return this._parity;
|
||||
}
|
||||
|
||||
get net () {
|
||||
return this._net;
|
||||
}
|
||||
|
||||
get personal () {
|
||||
return this._personal;
|
||||
}
|
||||
|
||||
get shh () {
|
||||
return this._shh;
|
||||
}
|
||||
|
||||
get signer () {
|
||||
return this._signer;
|
||||
}
|
||||
|
||||
get trace () {
|
||||
return this._trace;
|
||||
}
|
||||
|
||||
get transport () {
|
||||
return this._transport;
|
||||
}
|
||||
|
||||
get web3 () {
|
||||
return this._web3;
|
||||
}
|
||||
|
||||
get util () {
|
||||
return util;
|
||||
}
|
||||
|
||||
newContract (abi, address) {
|
||||
return new Contract(this, abi).at(address);
|
||||
}
|
||||
|
||||
subscribe (subscriptionName, callback) {
|
||||
if (!this._subscriptions) {
|
||||
return Promise.resolve(1);
|
||||
}
|
||||
|
||||
return this._subscriptions.subscribe(subscriptionName, callback);
|
||||
}
|
||||
|
||||
unsubscribe (subscriptionId) {
|
||||
if (!this._subscriptions) {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
return this._subscriptions.unsubscribe(subscriptionId);
|
||||
}
|
||||
|
||||
pollMethod (method, input, validate) {
|
||||
const [_group, endpoint] = method.split('_');
|
||||
const group = `_${_group}`;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const timeout = () => {
|
||||
this[group][endpoint](input)
|
||||
.then((result) => {
|
||||
if (validate ? validate(result) : result) {
|
||||
resolve(result);
|
||||
} else {
|
||||
setTimeout(timeout, 500);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
// Don't print if the request is rejected: that's ok
|
||||
if (error.type !== 'REQUEST_REJECTED') {
|
||||
console.error('pollMethod', error);
|
||||
}
|
||||
|
||||
reject(error);
|
||||
});
|
||||
};
|
||||
|
||||
timeout();
|
||||
});
|
||||
}
|
||||
|
||||
static util = util
|
||||
|
||||
static Transport = {
|
||||
Http: Http,
|
||||
Ws: Ws
|
||||
}
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { TEST_HTTP_URL, endpointTest } from '../../test/mockRpc';
|
||||
|
||||
import util from './util';
|
||||
import Api from './api';
|
||||
|
||||
import ethereumRpc from '../jsonrpc/';
|
||||
|
||||
describe('api/Api', () => {
|
||||
describe('constructor', () => {
|
||||
it('requires defined/non-null transport object', () => {
|
||||
expect(() => new Api()).to.throw(/Api needs transport/);
|
||||
expect(() => new Api(null)).to.throw(/Api needs transport/);
|
||||
});
|
||||
|
||||
it('requires an execute function on the transport object', () => {
|
||||
expect(() => new Api({})).to.throw(/Api needs transport/);
|
||||
expect(() => new Api({ execute: true })).to.throw(/Api needs transport/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('interface', () => {
|
||||
const api = new Api(new Api.Transport.Http(TEST_HTTP_URL, -1));
|
||||
const ignored = [
|
||||
'eth_subscribe', 'eth_unsubscribe',
|
||||
'parity_subscribe', 'parity_unsubscribe',
|
||||
'signer_subscribePending', 'signer_unsubscribePending'
|
||||
];
|
||||
|
||||
Object.keys(ethereumRpc).sort().forEach((endpoint) => {
|
||||
describe(endpoint, () => {
|
||||
Object.keys(ethereumRpc[endpoint]).sort()
|
||||
.filter(method => ignored.indexOf(method) !== -1)
|
||||
.forEach((method) => {
|
||||
endpointTest(api, endpoint, method);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('exposes util as static property', () => {
|
||||
expect(Api.util).to.equal(util);
|
||||
});
|
||||
});
|
@ -1,561 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import Abi from '~/abi';
|
||||
|
||||
let nextSubscriptionId = 0;
|
||||
|
||||
export default class Contract {
|
||||
constructor (api, abi) {
|
||||
if (!api) {
|
||||
throw new Error('API instance needs to be provided to Contract');
|
||||
}
|
||||
|
||||
if (!abi) {
|
||||
throw new Error('ABI needs to be provided to Contract instance');
|
||||
}
|
||||
|
||||
this._api = api;
|
||||
this._abi = new Abi(abi);
|
||||
|
||||
this._subscriptions = {};
|
||||
this._constructors = this._abi.constructors.map(this._bindFunction);
|
||||
this._functions = this._abi.functions.map(this._bindFunction);
|
||||
this._events = this._abi.events.map(this._bindEvent);
|
||||
|
||||
this._instance = {};
|
||||
|
||||
this._events.forEach((evt) => {
|
||||
this._instance[evt.name] = evt;
|
||||
this._instance[evt.signature] = evt;
|
||||
});
|
||||
|
||||
this._functions.forEach((fn) => {
|
||||
this._instance[fn.name] = fn;
|
||||
this._instance[fn.signature] = fn;
|
||||
});
|
||||
|
||||
this._subscribedToPendings = false;
|
||||
this._pendingsSubscriptionId = null;
|
||||
|
||||
this._subscribedToBlock = false;
|
||||
this._blockSubscriptionId = null;
|
||||
|
||||
if (api && api.patch && api.patch.contract) {
|
||||
api.patch.contract(this);
|
||||
}
|
||||
}
|
||||
|
||||
get address () {
|
||||
return this._address;
|
||||
}
|
||||
|
||||
get constructors () {
|
||||
return this._constructors;
|
||||
}
|
||||
|
||||
get events () {
|
||||
return this._events;
|
||||
}
|
||||
|
||||
get functions () {
|
||||
return this._functions;
|
||||
}
|
||||
|
||||
get receipt () {
|
||||
return this._receipt;
|
||||
}
|
||||
|
||||
get instance () {
|
||||
this._instance.address = this._address;
|
||||
return this._instance;
|
||||
}
|
||||
|
||||
get api () {
|
||||
return this._api;
|
||||
}
|
||||
|
||||
get abi () {
|
||||
return this._abi;
|
||||
}
|
||||
|
||||
at (address) {
|
||||
this._address = address;
|
||||
return this;
|
||||
}
|
||||
|
||||
deployEstimateGas (options, values) {
|
||||
const _options = this._encodeOptions(this.constructors[0], options, values);
|
||||
|
||||
return this._api.eth
|
||||
.estimateGas(_options)
|
||||
.then((gasEst) => {
|
||||
return [gasEst, gasEst.mul(1.2)];
|
||||
});
|
||||
}
|
||||
|
||||
deploy (options, values, statecb = () => {}, skipGasEstimate = false) {
|
||||
let gasEstPromise;
|
||||
|
||||
if (skipGasEstimate) {
|
||||
gasEstPromise = Promise.resolve(null);
|
||||
} else {
|
||||
statecb(null, { state: 'estimateGas' });
|
||||
|
||||
gasEstPromise = this.deployEstimateGas(options, values)
|
||||
.then(([gasEst, gas]) => gas);
|
||||
}
|
||||
|
||||
return gasEstPromise
|
||||
.then((_gas) => {
|
||||
if (_gas) {
|
||||
options.gas = _gas.toFixed(0);
|
||||
}
|
||||
|
||||
const gas = _gas || options.gas;
|
||||
|
||||
statecb(null, { state: 'postTransaction', gas });
|
||||
|
||||
const encodedOptions = this._encodeOptions(this.constructors[0], options, values);
|
||||
|
||||
return this._api.parity
|
||||
.postTransaction(encodedOptions)
|
||||
.then((requestId) => {
|
||||
statecb(null, { state: 'checkRequest', requestId });
|
||||
return this._pollCheckRequest(requestId);
|
||||
})
|
||||
.then((txhash) => {
|
||||
statecb(null, { state: 'getTransactionReceipt', txhash });
|
||||
return this._pollTransactionReceipt(txhash, gas);
|
||||
})
|
||||
.then((receipt) => {
|
||||
if (receipt.gasUsed.eq(gas)) {
|
||||
throw new Error(`Contract not deployed, gasUsed == ${gas.toFixed(0)}`);
|
||||
}
|
||||
|
||||
statecb(null, { state: 'hasReceipt', receipt });
|
||||
this._receipt = receipt;
|
||||
this._address = receipt.contractAddress;
|
||||
return this._address;
|
||||
})
|
||||
.then((address) => {
|
||||
statecb(null, { state: 'getCode' });
|
||||
return this._api.eth.getCode(this._address);
|
||||
})
|
||||
.then((code) => {
|
||||
if (code === '0x') {
|
||||
throw new Error('Contract not deployed, getCode returned 0x');
|
||||
}
|
||||
|
||||
statecb(null, { state: 'completed' });
|
||||
return this._address;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
parseEventLogs (logs) {
|
||||
return logs
|
||||
.map((log) => {
|
||||
const signature = log.topics[0].substr(2);
|
||||
const event = this.events.find((evt) => evt.signature === signature);
|
||||
|
||||
if (!event) {
|
||||
console.warn(`Unable to find event matching signature ${signature}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const decoded = event.decodeLog(log.topics, log.data);
|
||||
|
||||
log.params = {};
|
||||
log.event = event.name;
|
||||
|
||||
decoded.params.forEach((param, index) => {
|
||||
const { type, value } = param.token;
|
||||
const key = param.name || index;
|
||||
|
||||
log.params[key] = { type, value };
|
||||
});
|
||||
|
||||
return log;
|
||||
} catch (error) {
|
||||
console.warn('Error decoding log', log);
|
||||
console.warn(error);
|
||||
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.filter((log) => log);
|
||||
}
|
||||
|
||||
parseTransactionEvents (receipt) {
|
||||
receipt.logs = this.parseEventLogs(receipt.logs);
|
||||
|
||||
return receipt;
|
||||
}
|
||||
|
||||
_pollCheckRequest = (requestId) => {
|
||||
return this._api.pollMethod('parity_checkRequest', requestId);
|
||||
}
|
||||
|
||||
_pollTransactionReceipt = (txhash, gas) => {
|
||||
return this.api.pollMethod('eth_getTransactionReceipt', txhash, (receipt) => {
|
||||
if (!receipt || !receipt.blockNumber || receipt.blockNumber.eq(0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
getCallData = (func, options, values) => {
|
||||
let data = options.data;
|
||||
|
||||
const tokens = func ? Abi.encodeTokens(func.inputParamTypes(), values) : null;
|
||||
const call = tokens ? func.encodeCall(tokens) : null;
|
||||
|
||||
if (data && data.substr(0, 2) === '0x') {
|
||||
data = data.substr(2);
|
||||
}
|
||||
|
||||
return `0x${data || ''}${call || ''}`;
|
||||
}
|
||||
|
||||
_encodeOptions (func, options, values) {
|
||||
const data = this.getCallData(func, options, values);
|
||||
|
||||
return {
|
||||
...options,
|
||||
data
|
||||
};
|
||||
}
|
||||
|
||||
_addOptionsTo (options = {}) {
|
||||
return {
|
||||
to: this._address,
|
||||
...options
|
||||
};
|
||||
}
|
||||
|
||||
_bindFunction = (func) => {
|
||||
func.contract = this;
|
||||
|
||||
func.call = (_options = {}, values = []) => {
|
||||
const rawTokens = !!_options.rawTokens;
|
||||
const options = {
|
||||
..._options
|
||||
};
|
||||
|
||||
delete options.rawTokens;
|
||||
|
||||
let callParams;
|
||||
|
||||
try {
|
||||
callParams = this._encodeOptions(func, this._addOptionsTo(options), values);
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
return this._api.eth
|
||||
.call(callParams)
|
||||
.then((encoded) => func.decodeOutput(encoded))
|
||||
.then((tokens) => {
|
||||
if (rawTokens) {
|
||||
return tokens;
|
||||
}
|
||||
|
||||
return tokens.map((token) => token.value);
|
||||
})
|
||||
.then((returns) => returns.length === 1 ? returns[0] : returns)
|
||||
.catch((error) => {
|
||||
console.warn(`${func.name}.call`, values, error);
|
||||
throw error;
|
||||
});
|
||||
};
|
||||
|
||||
if (!func.constant) {
|
||||
func.postTransaction = (options, values = []) => {
|
||||
let _options;
|
||||
|
||||
try {
|
||||
_options = this._encodeOptions(func, this._addOptionsTo(options), values);
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
return this._api.parity
|
||||
.postTransaction(_options)
|
||||
.catch((error) => {
|
||||
console.warn(`${func.name}.postTransaction`, values, error);
|
||||
throw error;
|
||||
});
|
||||
};
|
||||
|
||||
func.estimateGas = (options, values = []) => {
|
||||
const _options = this._encodeOptions(func, this._addOptionsTo(options), values);
|
||||
|
||||
return this._api.eth
|
||||
.estimateGas(_options)
|
||||
.catch((error) => {
|
||||
console.warn(`${func.name}.estimateGas`, values, error);
|
||||
throw error;
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
return func;
|
||||
}
|
||||
|
||||
_bindEvent = (event) => {
|
||||
event.subscribe = (options = {}, callback, autoRemove) => {
|
||||
return this._subscribe(event, options, callback, autoRemove);
|
||||
};
|
||||
|
||||
event.unsubscribe = (subscriptionId) => {
|
||||
return this.unsubscribe(subscriptionId);
|
||||
};
|
||||
|
||||
event.getAllLogs = (options = {}) => {
|
||||
return this.getAllLogs(event);
|
||||
};
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
getAllLogs (event, _options) {
|
||||
// Options as first parameter
|
||||
if (!_options && event && event.topics) {
|
||||
return this.getAllLogs(null, event);
|
||||
}
|
||||
|
||||
const options = this._getFilterOptions(event, _options);
|
||||
|
||||
options.fromBlock = 0;
|
||||
options.toBlock = 'latest';
|
||||
|
||||
return this._api.eth
|
||||
.getLogs(options)
|
||||
.then((logs) => this.parseEventLogs(logs));
|
||||
}
|
||||
|
||||
_findEvent (eventName = null) {
|
||||
const event = eventName
|
||||
? this._events.find((evt) => evt.name === eventName)
|
||||
: null;
|
||||
|
||||
if (eventName && !event) {
|
||||
const events = this._events.map((evt) => evt.name).join(', ');
|
||||
|
||||
throw new Error(`${eventName} is not a valid eventName, subscribe using one of ${events} (or null to include all)`);
|
||||
}
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
_getFilterOptions (event = null, _options = {}) {
|
||||
const optionTopics = _options.topics || [];
|
||||
const signature = event && event.signature || null;
|
||||
|
||||
// If event provided, remove the potential event signature
|
||||
// as the first element of the topics
|
||||
const topics = signature
|
||||
? [ signature ].concat(optionTopics.filter((t, idx) => idx > 0 || t !== signature))
|
||||
: optionTopics;
|
||||
|
||||
const options = Object.assign({}, _options, {
|
||||
address: this._address,
|
||||
topics
|
||||
});
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
_createEthFilter (event = null, _options) {
|
||||
const options = this._getFilterOptions(event, _options);
|
||||
|
||||
return this._api.eth.newFilter(options);
|
||||
}
|
||||
|
||||
subscribe (eventName = null, options = {}, callback, autoRemove) {
|
||||
try {
|
||||
const event = this._findEvent(eventName);
|
||||
|
||||
return this._subscribe(event, options, callback, autoRemove);
|
||||
} catch (e) {
|
||||
return Promise.reject(e);
|
||||
}
|
||||
}
|
||||
|
||||
_sendData (subscriptionId, error, logs) {
|
||||
const { autoRemove, callback } = this._subscriptions[subscriptionId];
|
||||
let result = true;
|
||||
|
||||
try {
|
||||
result = callback(error, logs);
|
||||
} catch (error) {
|
||||
console.warn('_sendData', subscriptionId, error);
|
||||
}
|
||||
|
||||
if (autoRemove && result && typeof result === 'boolean') {
|
||||
this.unsubscribe(subscriptionId);
|
||||
}
|
||||
}
|
||||
|
||||
_subscribe (event = null, _options, callback, autoRemove = false) {
|
||||
const subscriptionId = nextSubscriptionId++;
|
||||
const { skipInitFetch } = _options;
|
||||
|
||||
delete _options['skipInitFetch'];
|
||||
|
||||
return this
|
||||
._createEthFilter(event, _options)
|
||||
.then((filterId) => {
|
||||
this._subscriptions[subscriptionId] = {
|
||||
options: _options,
|
||||
autoRemove,
|
||||
callback,
|
||||
filterId,
|
||||
id: subscriptionId
|
||||
};
|
||||
|
||||
if (skipInitFetch) {
|
||||
this._subscribeToChanges();
|
||||
return subscriptionId;
|
||||
}
|
||||
|
||||
return this._api.eth
|
||||
.getFilterLogs(filterId)
|
||||
.then((logs) => {
|
||||
this._sendData(subscriptionId, null, this.parseEventLogs(logs));
|
||||
this._subscribeToChanges();
|
||||
return subscriptionId;
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
console.warn('subscribe', event, _options, error);
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
|
||||
unsubscribe (subscriptionId) {
|
||||
return this._api.eth
|
||||
.uninstallFilter(this._subscriptions[subscriptionId].filterId)
|
||||
.catch((error) => {
|
||||
console.error('unsubscribe', error);
|
||||
})
|
||||
.then(() => {
|
||||
delete this._subscriptions[subscriptionId];
|
||||
this._unsubscribeFromChanges();
|
||||
});
|
||||
}
|
||||
|
||||
_subscribeToChanges = () => {
|
||||
const subscriptions = Object.values(this._subscriptions);
|
||||
|
||||
const pendingSubscriptions = subscriptions
|
||||
.filter((s) => s.options.toBlock && s.options.toBlock === 'pending');
|
||||
|
||||
const otherSubscriptions = subscriptions
|
||||
.filter((s) => !(s.options.toBlock && s.options.toBlock === 'pending'));
|
||||
|
||||
if (pendingSubscriptions.length > 0 && !this._subscribedToPendings) {
|
||||
this._subscribedToPendings = true;
|
||||
this._subscribeToPendings();
|
||||
}
|
||||
|
||||
if (otherSubscriptions.length > 0 && !this._subscribedToBlock) {
|
||||
this._subscribedToBlock = true;
|
||||
this._subscribeToBlock();
|
||||
}
|
||||
}
|
||||
|
||||
_unsubscribeFromChanges = () => {
|
||||
const subscriptions = Object.values(this._subscriptions);
|
||||
|
||||
const pendingSubscriptions = subscriptions
|
||||
.filter((s) => s.options.toBlock && s.options.toBlock === 'pending');
|
||||
|
||||
const otherSubscriptions = subscriptions
|
||||
.filter((s) => !(s.options.toBlock && s.options.toBlock === 'pending'));
|
||||
|
||||
if (pendingSubscriptions.length === 0 && this._subscribedToPendings) {
|
||||
this._subscribedToPendings = false;
|
||||
clearTimeout(this._pendingsSubscriptionId);
|
||||
}
|
||||
|
||||
if (otherSubscriptions.length === 0 && this._subscribedToBlock) {
|
||||
this._subscribedToBlock = false;
|
||||
this._api.unsubscribe(this._blockSubscriptionId);
|
||||
}
|
||||
}
|
||||
|
||||
_subscribeToBlock = () => {
|
||||
this._api
|
||||
.subscribe('eth_blockNumber', (error) => {
|
||||
if (error) {
|
||||
console.error('::_subscribeToBlock', error, error && error.stack);
|
||||
}
|
||||
|
||||
const subscriptions = Object.values(this._subscriptions)
|
||||
.filter((s) => !(s.options.toBlock && s.options.toBlock === 'pending'));
|
||||
|
||||
this._sendSubscriptionChanges(subscriptions);
|
||||
})
|
||||
.then((blockSubId) => {
|
||||
this._blockSubscriptionId = blockSubId;
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error('::_subscribeToBlock', e, e && e.stack);
|
||||
});
|
||||
}
|
||||
|
||||
_subscribeToPendings = () => {
|
||||
const subscriptions = Object.values(this._subscriptions)
|
||||
.filter((s) => s.options.toBlock && s.options.toBlock === 'pending');
|
||||
|
||||
const timeout = () => setTimeout(() => this._subscribeToPendings(), 1000);
|
||||
|
||||
this._sendSubscriptionChanges(subscriptions)
|
||||
.then(() => {
|
||||
this._pendingsSubscriptionId = timeout();
|
||||
});
|
||||
}
|
||||
|
||||
_sendSubscriptionChanges = (subscriptions) => {
|
||||
return Promise
|
||||
.all(
|
||||
subscriptions.map((subscription) => {
|
||||
return this._api.eth.getFilterChanges(subscription.filterId);
|
||||
})
|
||||
)
|
||||
.then((logsArray) => {
|
||||
logsArray.forEach((logs, index) => {
|
||||
if (!logs || !logs.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this._sendData(subscriptions[index].id, null, this.parseEventLogs(logs));
|
||||
} catch (error) {
|
||||
console.error('_sendSubscriptionChanges', error);
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('_sendSubscriptionChanges', error);
|
||||
});
|
||||
}
|
||||
}
|
@ -1,597 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import BigNumber from 'bignumber.js';
|
||||
import sinon from 'sinon';
|
||||
|
||||
import { TEST_HTTP_URL, mockHttp } from '../../../test/mockRpc';
|
||||
|
||||
import Abi from '../../abi';
|
||||
import { sha3 } from '../util/sha3';
|
||||
|
||||
import Api from '../api';
|
||||
import Contract from './contract';
|
||||
import { isInstanceOf, isFunction } from '../util/types';
|
||||
|
||||
const transport = new Api.Transport.Http(TEST_HTTP_URL, -1);
|
||||
const eth = new Api(transport);
|
||||
|
||||
describe('api/contract/Contract', () => {
|
||||
const ADDR = '0x0123456789';
|
||||
|
||||
const ABI = [
|
||||
{
|
||||
type: 'function', name: 'test',
|
||||
inputs: [{ name: 'boolin', type: 'bool' }, { name: 'stringin', type: 'string' }],
|
||||
outputs: [{ type: 'uint' }]
|
||||
},
|
||||
{
|
||||
type: 'function', name: 'test2',
|
||||
outputs: [{ type: 'uint' }, { type: 'uint' }]
|
||||
},
|
||||
{
|
||||
type: 'constructor',
|
||||
inputs: [{ name: 'boolin', type: 'bool' }, { name: 'stringin', type: 'string' }]
|
||||
},
|
||||
{ type: 'event', name: 'baz' },
|
||||
{ type: 'event', name: 'foo' }
|
||||
];
|
||||
|
||||
const ABI_NO_PARAMS = [
|
||||
{
|
||||
type: 'function', name: 'test',
|
||||
inputs: [{ name: 'boolin', type: 'bool' }, { name: 'stringin', type: 'string' }],
|
||||
outputs: [{ type: 'uint' }]
|
||||
},
|
||||
{
|
||||
type: 'function', name: 'test2',
|
||||
outputs: [{ type: 'uint' }, { type: 'uint' }]
|
||||
},
|
||||
{
|
||||
type: 'constructor'
|
||||
},
|
||||
{ type: 'event', name: 'baz' },
|
||||
{ type: 'event', name: 'foo' }
|
||||
];
|
||||
|
||||
const VALUES = [ true, 'jacogr' ];
|
||||
const CALLDATA = `
|
||||
0000000000000000000000000000000000000000000000000000000000000001
|
||||
0000000000000000000000000000000000000000000000000000000000000040
|
||||
0000000000000000000000000000000000000000000000000000000000000006
|
||||
6a61636f67720000000000000000000000000000000000000000000000000000
|
||||
`.replace(/\s/g, '');
|
||||
const SIGNATURE = '02356205';
|
||||
|
||||
const ENCODED = `0x${SIGNATURE}${CALLDATA}`;
|
||||
|
||||
const RETURN1 = '0000000000000000000000000000000000000000000000000000000000123456';
|
||||
const RETURN2 = '0000000000000000000000000000000000000000000000000000000000456789';
|
||||
let scope;
|
||||
|
||||
describe('constructor', () => {
|
||||
it('needs an EthAbi instance', () => {
|
||||
expect(() => new Contract()).to.throw(/API instance needs to be provided to Contract/);
|
||||
});
|
||||
|
||||
it('needs an ABI', () => {
|
||||
expect(() => new Contract(eth)).to.throw(/ABI needs to be provided to Contract instance/);
|
||||
});
|
||||
|
||||
describe('internal setup', () => {
|
||||
const contract = new Contract(eth, ABI);
|
||||
|
||||
it('sets EthApi & parsed interface', () => {
|
||||
expect(contract.address).to.not.be.ok;
|
||||
expect(contract.api).to.deep.equal(eth);
|
||||
expect(isInstanceOf(contract.abi, Abi)).to.be.ok;
|
||||
});
|
||||
|
||||
it('attaches functions', () => {
|
||||
expect(contract.functions.length).to.equal(2);
|
||||
expect(contract.functions[0].name).to.equal('test');
|
||||
});
|
||||
|
||||
it('attaches constructors', () => {
|
||||
expect(contract.constructors.length).to.equal(1);
|
||||
});
|
||||
|
||||
it('attaches events', () => {
|
||||
expect(contract.events.length).to.equal(2);
|
||||
expect(contract.events[0].name).to.equal('baz');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('at', () => {
|
||||
it('sets returns the functions, events & sets the address', () => {
|
||||
const contract = new Contract(eth, [
|
||||
{
|
||||
constant: true,
|
||||
inputs: [{
|
||||
name: '_who',
|
||||
type: 'address'
|
||||
}],
|
||||
name: 'balanceOf',
|
||||
outputs: [{
|
||||
name: '',
|
||||
type: 'uint256'
|
||||
}],
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
anonymous: false,
|
||||
inputs: [{
|
||||
indexed: false,
|
||||
name: 'amount',
|
||||
type: 'uint256'
|
||||
}],
|
||||
name: 'Drained',
|
||||
type: 'event'
|
||||
}
|
||||
]);
|
||||
|
||||
contract.at('6789');
|
||||
|
||||
expect(Object.keys(contract.instance)).to.deep.equal([
|
||||
'Drained',
|
||||
/^(?:0x)(.+)$/.exec(sha3('Drained(uint256)'))[1],
|
||||
'balanceOf',
|
||||
/^(?:0x)(.+)$/.exec(sha3('balanceOf(address)'))[1].substr(0, 8),
|
||||
'address'
|
||||
]);
|
||||
expect(contract.address).to.equal('6789');
|
||||
});
|
||||
});
|
||||
|
||||
describe('parseTransactionEvents', () => {
|
||||
it('parses a transaction log into the data', () => {
|
||||
const contract = new Contract(eth, [
|
||||
{
|
||||
anonymous: false, name: 'Message', type: 'event',
|
||||
inputs: [
|
||||
{ indexed: true, name: 'postId', type: 'uint256' },
|
||||
{ indexed: false, name: 'parentId', type: 'uint256' },
|
||||
{ indexed: false, name: 'sender', type: 'address' },
|
||||
{ indexed: false, name: 'at', type: 'uint256' },
|
||||
{ indexed: false, name: 'messageId', type: 'uint256' },
|
||||
{ indexed: false, name: 'message', type: 'string' }
|
||||
]
|
||||
}
|
||||
]);
|
||||
const decoded = contract.parseTransactionEvents({
|
||||
blockHash: '0xa9280530a3b47bee2fc80f2862fd56502ae075350571d724d6442ea4c597347b',
|
||||
blockNumber: '0x4fcd',
|
||||
cumulativeGasUsed: '0xb57f',
|
||||
gasUsed: '0xb57f',
|
||||
logs: [{
|
||||
address: '0x22bff18ec62281850546a664bb63a5c06ac5f76c',
|
||||
blockHash: '0xa9280530a3b47bee2fc80f2862fd56502ae075350571d724d6442ea4c597347b',
|
||||
blockNumber: '0x4fcd',
|
||||
data: '0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000063cf90d3f0410092fc0fca41846f5962239791950000000000000000000000000000000000000000000000000000000056e6c85f0000000000000000000000000000000000000000000000000001000000004fcd00000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000d706f7374286d6573736167652900000000000000000000000000000000000000',
|
||||
logIndex: '0x0',
|
||||
topics: [
|
||||
'0x954ba6c157daf8a26539574ffa64203c044691aa57251af95f4b48d85ec00dd5',
|
||||
'0x0000000000000000000000000000000000000000000000000001000000004fe0'
|
||||
],
|
||||
transactionHash: '0xca16f537d761d13e4e80953b754e2b15541f267d6cad9381f750af1bae1e4917',
|
||||
transactionIndex: '0x0'
|
||||
}],
|
||||
to: '0x22bff18ec62281850546a664bb63a5c06ac5f76c',
|
||||
transactionHash: '0xca16f537d761d13e4e80953b754e2b15541f267d6cad9381f750af1bae1e4917',
|
||||
transactionIndex: '0x0'
|
||||
});
|
||||
const log = decoded.logs[0];
|
||||
|
||||
expect(log.event).to.equal('Message');
|
||||
expect(log.address).to.equal('0x22bff18ec62281850546a664bb63a5c06ac5f76c');
|
||||
expect(log.params).to.deep.equal({
|
||||
at: { type: 'uint', value: new BigNumber('1457965151') },
|
||||
message: { type: 'string', value: 'post(message)' },
|
||||
messageId: { type: 'uint', value: new BigNumber('281474976731085') },
|
||||
parentId: { type: 'uint', value: new BigNumber(0) },
|
||||
postId: { type: 'uint', value: new BigNumber('281474976731104') },
|
||||
sender: { type: 'address', value: '0x63Cf90D3f0410092FC0fca41846f596223979195' }
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('_pollTransactionReceipt', () => {
|
||||
const contract = new Contract(eth, ABI);
|
||||
const ADDRESS = '0xD337e80eEdBdf86eDBba021797d7e4e00Bb78351';
|
||||
const BLOCKNUMBER = '555000';
|
||||
const RECEIPT = { contractAddress: ADDRESS.toLowerCase(), blockNumber: BLOCKNUMBER };
|
||||
const EXPECT = { contractAddress: ADDRESS, blockNumber: new BigNumber(BLOCKNUMBER) };
|
||||
|
||||
let scope;
|
||||
let receipt;
|
||||
|
||||
describe('success', () => {
|
||||
before(() => {
|
||||
scope = mockHttp([
|
||||
{ method: 'eth_getTransactionReceipt', reply: { result: null } },
|
||||
{ method: 'eth_getTransactionReceipt', reply: { result: null } },
|
||||
{ method: 'eth_getTransactionReceipt', reply: { result: RECEIPT } }
|
||||
]);
|
||||
|
||||
return contract
|
||||
._pollTransactionReceipt('0x123')
|
||||
.then((_receipt) => {
|
||||
receipt = _receipt;
|
||||
});
|
||||
});
|
||||
|
||||
it('sends multiple getTransactionReceipt calls', () => {
|
||||
expect(scope.isDone()).to.be.true;
|
||||
});
|
||||
|
||||
it('passes the txhash through', () => {
|
||||
expect(scope.body.eth_getTransactionReceipt.params[0]).to.equal('0x123');
|
||||
});
|
||||
|
||||
it('receives the final receipt', () => {
|
||||
expect(receipt).to.deep.equal(EXPECT);
|
||||
});
|
||||
});
|
||||
|
||||
describe('error', () => {
|
||||
before(() => {
|
||||
scope = mockHttp([{ method: 'eth_getTransactionReceipt', reply: { error: { code: -1, message: 'failure' } } }]);
|
||||
});
|
||||
|
||||
it('returns the errors', () => {
|
||||
return contract
|
||||
._pollTransactionReceipt('0x123')
|
||||
.catch((error) => {
|
||||
expect(error.message).to.match(/failure/);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('deploy without parameters', () => {
|
||||
const contract = new Contract(eth, ABI_NO_PARAMS);
|
||||
const CODE = '0x123';
|
||||
const ADDRESS = '0xD337e80eEdBdf86eDBba021797d7e4e00Bb78351';
|
||||
const RECEIPT_DONE = { contractAddress: ADDRESS.toLowerCase(), gasUsed: 50, blockNumber: 2500 };
|
||||
|
||||
let scope;
|
||||
|
||||
describe('success', () => {
|
||||
before(() => {
|
||||
scope = mockHttp([
|
||||
{ method: 'eth_estimateGas', reply: { result: 1000 } },
|
||||
{ method: 'parity_postTransaction', reply: { result: '0x678' } },
|
||||
{ method: 'parity_checkRequest', reply: { result: '0x890' } },
|
||||
{ method: 'eth_getTransactionReceipt', reply: { result: RECEIPT_DONE } },
|
||||
{ method: 'eth_getCode', reply: { result: CODE } }
|
||||
]);
|
||||
|
||||
return contract.deploy({ data: CODE }, []);
|
||||
});
|
||||
|
||||
it('passes the options through to postTransaction (incl. gas calculation)', () => {
|
||||
expect(scope.body.parity_postTransaction.params[0].data).to.equal(CODE);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('deploy', () => {
|
||||
const contract = new Contract(eth, ABI);
|
||||
const ADDRESS = '0xD337e80eEdBdf86eDBba021797d7e4e00Bb78351';
|
||||
const RECEIPT_PEND = { contractAddress: ADDRESS.toLowerCase(), gasUsed: 50, blockNumber: 0 };
|
||||
const RECEIPT_DONE = { contractAddress: ADDRESS.toLowerCase(), gasUsed: 50, blockNumber: 2500 };
|
||||
const RECEIPT_EXCP = { contractAddress: ADDRESS.toLowerCase(), gasUsed: 1200, blockNumber: 2500 };
|
||||
|
||||
let scope;
|
||||
|
||||
describe('success', () => {
|
||||
before(() => {
|
||||
scope = mockHttp([
|
||||
{ method: 'eth_estimateGas', reply: { result: 1000 } },
|
||||
{ method: 'parity_postTransaction', reply: { result: '0x678' } },
|
||||
{ method: 'parity_checkRequest', reply: { result: null } },
|
||||
{ method: 'parity_checkRequest', reply: { result: '0x890' } },
|
||||
{ method: 'eth_getTransactionReceipt', reply: { result: null } },
|
||||
{ method: 'eth_getTransactionReceipt', reply: { result: RECEIPT_PEND } },
|
||||
{ method: 'eth_getTransactionReceipt', reply: { result: RECEIPT_DONE } },
|
||||
{ method: 'eth_getCode', reply: { result: '0x456' } }
|
||||
]);
|
||||
|
||||
return contract.deploy({ data: '0x123' }, VALUES);
|
||||
});
|
||||
|
||||
it('calls estimateGas, postTransaction, checkRequest, getTransactionReceipt & getCode in order', () => {
|
||||
expect(scope.isDone()).to.be.true;
|
||||
});
|
||||
|
||||
it('passes the options through to postTransaction (incl. gas calculation)', () => {
|
||||
expect(scope.body.parity_postTransaction.params).to.deep.equal([
|
||||
{ data: `0x123${CALLDATA}`, gas: '0x4b0' }
|
||||
]);
|
||||
});
|
||||
|
||||
it('sets the address of the contract', () => {
|
||||
expect(contract.address).to.equal(ADDRESS);
|
||||
});
|
||||
});
|
||||
|
||||
describe('error', () => {
|
||||
it('fails when gasUsed == gas', () => {
|
||||
mockHttp([
|
||||
{ method: 'eth_estimateGas', reply: { result: 1000 } },
|
||||
{ method: 'parity_postTransaction', reply: { result: '0x678' } },
|
||||
{ method: 'parity_checkRequest', reply: { result: '0x789' } },
|
||||
{ method: 'eth_getTransactionReceipt', reply: { result: RECEIPT_EXCP } }
|
||||
]);
|
||||
|
||||
return contract
|
||||
.deploy({ data: '0x123' }, VALUES)
|
||||
.catch((error) => {
|
||||
expect(error.message).to.match(/not deployed, gasUsed/);
|
||||
});
|
||||
});
|
||||
|
||||
it('fails when no code was deployed', () => {
|
||||
mockHttp([
|
||||
{ method: 'eth_estimateGas', reply: { result: 1000 } },
|
||||
{ method: 'parity_postTransaction', reply: { result: '0x678' } },
|
||||
{ method: 'parity_checkRequest', reply: { result: '0x789' } },
|
||||
{ method: 'eth_getTransactionReceipt', reply: { result: RECEIPT_DONE } },
|
||||
{ method: 'eth_getCode', reply: { result: '0x' } }
|
||||
]);
|
||||
|
||||
return contract
|
||||
.deploy({ data: '0x123' }, VALUES)
|
||||
.catch((error) => {
|
||||
expect(error.message).to.match(/not deployed, getCode/);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('bindings', () => {
|
||||
let contract;
|
||||
let cons;
|
||||
let func;
|
||||
|
||||
beforeEach(() => {
|
||||
contract = new Contract(eth, ABI);
|
||||
contract.at(ADDR);
|
||||
cons = contract.constructors[0];
|
||||
func = contract.functions.find((fn) => fn.name === 'test');
|
||||
});
|
||||
|
||||
describe('_addOptionsTo', () => {
|
||||
it('works on no object specified', () => {
|
||||
expect(contract._addOptionsTo()).to.deep.equal({ to: ADDR });
|
||||
});
|
||||
|
||||
it('uses the contract address when none specified', () => {
|
||||
expect(contract._addOptionsTo({ from: 'me' })).to.deep.equal({ to: ADDR, from: 'me' });
|
||||
});
|
||||
|
||||
it('overrides the contract address when specified', () => {
|
||||
expect(contract._addOptionsTo({ to: 'you', from: 'me' })).to.deep.equal({ to: 'you', from: 'me' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('attachments', () => {
|
||||
it('attaches .call, .postTransaction & .estimateGas to constructors', () => {
|
||||
expect(isFunction(cons.call)).to.be.true;
|
||||
expect(isFunction(cons.postTransaction)).to.be.true;
|
||||
expect(isFunction(cons.estimateGas)).to.be.true;
|
||||
});
|
||||
|
||||
it('attaches .call, .postTransaction & .estimateGas to functions', () => {
|
||||
expect(isFunction(func.call)).to.be.true;
|
||||
expect(isFunction(func.postTransaction)).to.be.true;
|
||||
expect(isFunction(func.estimateGas)).to.be.true;
|
||||
});
|
||||
|
||||
it('attaches .call only to constant functions', () => {
|
||||
func = (new Contract(eth, [{ type: 'function', name: 'test', constant: true }])).functions[0];
|
||||
|
||||
expect(isFunction(func.call)).to.be.true;
|
||||
expect(isFunction(func.postTransaction)).to.be.false;
|
||||
expect(isFunction(func.estimateGas)).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
describe('postTransaction', () => {
|
||||
beforeEach(() => {
|
||||
scope = mockHttp([{ method: 'parity_postTransaction', reply: { result: ['hashId'] } }]);
|
||||
});
|
||||
|
||||
it('encodes options and mades an parity_postTransaction call', () => {
|
||||
return func
|
||||
.postTransaction({ someExtras: 'foo' }, VALUES)
|
||||
.then(() => {
|
||||
expect(scope.isDone()).to.be.true;
|
||||
expect(scope.body.parity_postTransaction.params[0]).to.deep.equal({
|
||||
someExtras: 'foo',
|
||||
to: ADDR,
|
||||
data: ENCODED
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('estimateGas', () => {
|
||||
beforeEach(() => {
|
||||
scope = mockHttp([{ method: 'eth_estimateGas', reply: { result: ['0x123'] } }]);
|
||||
});
|
||||
|
||||
it('encodes options and mades an eth_estimateGas call', () => {
|
||||
return func
|
||||
.estimateGas({ someExtras: 'foo' }, VALUES)
|
||||
.then((amount) => {
|
||||
expect(scope.isDone()).to.be.true;
|
||||
expect(amount.toString(16)).to.equal('123');
|
||||
expect(scope.body.eth_estimateGas.params).to.deep.equal([{
|
||||
someExtras: 'foo',
|
||||
to: ADDR,
|
||||
data: ENCODED
|
||||
}]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('call', () => {
|
||||
it('encodes options and mades an eth_call call', () => {
|
||||
scope = mockHttp([{ method: 'eth_call', reply: { result: RETURN1 } }]);
|
||||
|
||||
return func
|
||||
.call({ someExtras: 'foo' }, VALUES)
|
||||
.then((result) => {
|
||||
expect(scope.isDone()).to.be.true;
|
||||
expect(scope.body.eth_call.params).to.deep.equal([{
|
||||
someExtras: 'foo',
|
||||
to: ADDR,
|
||||
data: ENCODED
|
||||
}, 'latest']);
|
||||
expect(result.toString(16)).to.equal('123456');
|
||||
});
|
||||
});
|
||||
|
||||
it('encodes options and mades an eth_call call (multiple returns)', () => {
|
||||
scope = mockHttp([{ method: 'eth_call', reply: { result: `${RETURN1}${RETURN2}` } }]);
|
||||
|
||||
return contract.functions[1]
|
||||
.call({}, [])
|
||||
.then((result) => {
|
||||
expect(scope.isDone()).to.be.true;
|
||||
expect(result.length).to.equal(2);
|
||||
expect(result[0].toString(16)).to.equal('123456');
|
||||
expect(result[1].toString(16)).to.equal('456789');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('subscribe', () => {
|
||||
const abi = [
|
||||
{
|
||||
anonymous: false, name: 'Message', type: 'event',
|
||||
inputs: [
|
||||
{ indexed: true, name: 'postId', type: 'uint256' },
|
||||
{ indexed: false, name: 'parentId', type: 'uint256' },
|
||||
{ indexed: false, name: 'sender', type: 'address' },
|
||||
{ indexed: false, name: 'at', type: 'uint256' },
|
||||
{ indexed: false, name: 'messageId', type: 'uint256' },
|
||||
{ indexed: false, name: 'message', type: 'string' }
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
const logs = [{
|
||||
address: '0x22bff18ec62281850546a664bb63a5c06ac5f76c',
|
||||
blockHash: '0xa9280530a3b47bee2fc80f2862fd56502ae075350571d724d6442ea4c597347b',
|
||||
blockNumber: '0x4fcd',
|
||||
data: '0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000063cf90d3f0410092fc0fca41846f5962239791950000000000000000000000000000000000000000000000000000000056e6c85f0000000000000000000000000000000000000000000000000001000000004fcd00000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000d706f7374286d6573736167652900000000000000000000000000000000000000',
|
||||
logIndex: '0x0',
|
||||
topics: [
|
||||
'0x954ba6c157daf8a26539574ffa64203c044691aa57251af95f4b48d85ec00dd5',
|
||||
'0x0000000000000000000000000000000000000000000000000001000000004fe0'
|
||||
],
|
||||
transactionHash: '0xca16f537d761d13e4e80953b754e2b15541f267d6cad9381f750af1bae1e4917',
|
||||
transactionIndex: '0x0'
|
||||
}];
|
||||
|
||||
const parsed = [{
|
||||
address: '0x22bfF18ec62281850546a664bb63a5C06AC5F76C',
|
||||
blockHash: '0xa9280530a3b47bee2fc80f2862fd56502ae075350571d724d6442ea4c597347b',
|
||||
blockNumber: new BigNumber(20429),
|
||||
data: '0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000063cf90d3f0410092fc0fca41846f5962239791950000000000000000000000000000000000000000000000000000000056e6c85f0000000000000000000000000000000000000000000000000001000000004fcd00000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000d706f7374286d6573736167652900000000000000000000000000000000000000',
|
||||
event: 'Message',
|
||||
logIndex: new BigNumber(0),
|
||||
params: {
|
||||
at: { type: 'uint', value: new BigNumber(1457965151) },
|
||||
message: { type: 'string', value: 'post(message)' },
|
||||
messageId: { type: 'uint', value: new BigNumber(281474976731085) },
|
||||
parentId: { type: 'uint', value: new BigNumber(0) },
|
||||
postId: { type: 'uint', value: new BigNumber(281474976731104) },
|
||||
sender: { type: 'address', value: '0x63Cf90D3f0410092FC0fca41846f596223979195' }
|
||||
},
|
||||
topics: [
|
||||
'0x954ba6c157daf8a26539574ffa64203c044691aa57251af95f4b48d85ec00dd5',
|
||||
'0x0000000000000000000000000000000000000000000000000001000000004fe0'
|
||||
],
|
||||
transactionHash: '0xca16f537d761d13e4e80953b754e2b15541f267d6cad9381f750af1bae1e4917',
|
||||
transactionIndex: new BigNumber(0)
|
||||
}];
|
||||
|
||||
let contract;
|
||||
|
||||
beforeEach(() => {
|
||||
contract = new Contract(eth, abi);
|
||||
contract.at(ADDR);
|
||||
});
|
||||
|
||||
describe('invalid events', () => {
|
||||
it('fails to subscribe to an invalid names', () => {
|
||||
return contract
|
||||
.subscribe('invalid')
|
||||
.catch((error) => {
|
||||
expect(error.message).to.match(/invalid is not a valid eventName/);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('valid events', () => {
|
||||
let cbb;
|
||||
let cbe;
|
||||
|
||||
beforeEach(() => {
|
||||
scope = mockHttp([
|
||||
{ method: 'eth_newFilter', reply: { result: '0x123' } },
|
||||
{ method: 'eth_getFilterLogs', reply: { result: logs } },
|
||||
{ method: 'eth_getFilterChanges', reply: { result: logs } },
|
||||
{ method: 'eth_newFilter', reply: { result: '0x123' } },
|
||||
{ method: 'eth_getFilterLogs', reply: { result: logs } }
|
||||
]);
|
||||
cbb = sinon.stub();
|
||||
cbe = sinon.stub();
|
||||
|
||||
return contract.subscribe('Message', { toBlock: 'pending' }, cbb);
|
||||
});
|
||||
|
||||
it('sets the subscriptionId returned', () => {
|
||||
return contract
|
||||
.subscribe('Message', { toBlock: 'pending' }, cbe)
|
||||
.then((subscriptionId) => {
|
||||
expect(subscriptionId).to.equal(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('creates a new filter and retrieves the logs on it', () => {
|
||||
return contract
|
||||
.subscribe('Message', { toBlock: 'pending' }, cbe)
|
||||
.then((subscriptionId) => {
|
||||
expect(scope.isDone()).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
it('returns the logs to the callback', () => {
|
||||
return contract
|
||||
.subscribe('Message', { toBlock: 'pending' }, cbe)
|
||||
.then((subscriptionId) => {
|
||||
expect(cbe).to.have.been.calledWith(null, parsed);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -1,17 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './contract';
|
@ -1,242 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import BigNumber from 'bignumber.js';
|
||||
|
||||
import { isArray, isHex, isInstanceOf, isString } from '../util/types';
|
||||
import { padLeft, toHex } from '../util/format';
|
||||
|
||||
export function inAddress (address) {
|
||||
// TODO: address validation if we have upper-lower addresses
|
||||
return inHex(address);
|
||||
}
|
||||
|
||||
export function inAddresses (addresses) {
|
||||
return (addresses || []).map(inAddress);
|
||||
}
|
||||
|
||||
export function inBlockNumber (blockNumber) {
|
||||
if (isString(blockNumber)) {
|
||||
switch (blockNumber) {
|
||||
case 'earliest':
|
||||
case 'latest':
|
||||
case 'pending':
|
||||
return blockNumber;
|
||||
}
|
||||
}
|
||||
|
||||
return inNumber16(blockNumber);
|
||||
}
|
||||
|
||||
export function inData (data) {
|
||||
if (data && data.length && !isHex(data)) {
|
||||
data = data.split('').map((chr) => {
|
||||
return `0${chr.charCodeAt(0).toString(16)}`.slice(-2);
|
||||
}).join('');
|
||||
}
|
||||
|
||||
return inHex(data);
|
||||
}
|
||||
|
||||
export function inHash (hash) {
|
||||
return inHex(hash);
|
||||
}
|
||||
|
||||
export function inTopics (_topics) {
|
||||
let topics = (_topics || [])
|
||||
.filter((topic) => topic === null || topic)
|
||||
.map((topic) => {
|
||||
if (topic === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Array.isArray(topic)) {
|
||||
return inTopics(topic);
|
||||
}
|
||||
|
||||
return padLeft(topic, 32);
|
||||
});
|
||||
|
||||
return topics;
|
||||
}
|
||||
|
||||
export function inFilter (options) {
|
||||
if (options) {
|
||||
Object.keys(options).forEach((key) => {
|
||||
switch (key) {
|
||||
case 'address':
|
||||
if (isArray(options[key])) {
|
||||
options[key] = options[key].map(inAddress);
|
||||
} else {
|
||||
options[key] = inAddress(options[key]);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'fromBlock':
|
||||
case 'toBlock':
|
||||
options[key] = inBlockNumber(options[key]);
|
||||
break;
|
||||
|
||||
case 'limit':
|
||||
options[key] = inNumber10(options[key]);
|
||||
break;
|
||||
|
||||
case 'topics':
|
||||
options[key] = inTopics(options[key]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
export function inHex (str) {
|
||||
return toHex(str);
|
||||
}
|
||||
|
||||
export function inNumber10 (number) {
|
||||
if (isInstanceOf(number, BigNumber)) {
|
||||
return number.toNumber();
|
||||
}
|
||||
|
||||
return (new BigNumber(number || 0)).toNumber();
|
||||
}
|
||||
|
||||
export function inNumber16 (number) {
|
||||
const bn = isInstanceOf(number, BigNumber)
|
||||
? number
|
||||
: (new BigNumber(number || 0));
|
||||
|
||||
if (!bn.isInteger()) {
|
||||
throw new Error(`[format/input::inNumber16] the given number is not an integer: ${bn.toFormat()}`);
|
||||
}
|
||||
|
||||
return inHex(bn.toString(16));
|
||||
}
|
||||
|
||||
export function inOptionsCondition (condition) {
|
||||
if (condition) {
|
||||
if (condition.block) {
|
||||
condition.block = condition.block ? inNumber10(condition.block) : null;
|
||||
} else if (condition.time) {
|
||||
condition.time = inNumber10(Math.floor(condition.time.getTime() / 1000));
|
||||
}
|
||||
}
|
||||
|
||||
return condition;
|
||||
}
|
||||
|
||||
export function inOptions (_options = {}) {
|
||||
const options = { ..._options };
|
||||
|
||||
Object.keys(options).forEach((key) => {
|
||||
switch (key) {
|
||||
case 'to':
|
||||
// Don't encode the `to` option if it's empty
|
||||
// (eg. contract deployments)
|
||||
if (options[key]) {
|
||||
options.to = inAddress(options[key]);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'from':
|
||||
options[key] = inAddress(options[key]);
|
||||
break;
|
||||
|
||||
case 'condition':
|
||||
options[key] = inOptionsCondition(options[key]);
|
||||
break;
|
||||
|
||||
case 'gas':
|
||||
case 'gasPrice':
|
||||
options[key] = inNumber16((new BigNumber(options[key])).round());
|
||||
break;
|
||||
|
||||
case 'value':
|
||||
case 'nonce':
|
||||
options[key] = inNumber16(options[key]);
|
||||
break;
|
||||
|
||||
case 'data':
|
||||
options[key] = inData(options[key]);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
export function inTraceFilter (filterObject) {
|
||||
if (filterObject) {
|
||||
Object.keys(filterObject).forEach((key) => {
|
||||
switch (key) {
|
||||
case 'fromAddress':
|
||||
case 'toAddress':
|
||||
filterObject[key] = [].concat(filterObject[key])
|
||||
.map(address => inAddress(address));
|
||||
break;
|
||||
|
||||
case 'toBlock':
|
||||
case 'fromBlock':
|
||||
filterObject[key] = inBlockNumber(filterObject[key]);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return filterObject;
|
||||
}
|
||||
|
||||
export function inTraceType (whatTrace) {
|
||||
if (isString(whatTrace)) {
|
||||
return [whatTrace];
|
||||
}
|
||||
|
||||
return whatTrace;
|
||||
}
|
||||
|
||||
function inDeriveType (derive) {
|
||||
return derive && derive.type === 'hard' ? 'hard' : 'soft';
|
||||
}
|
||||
|
||||
export function inDeriveHash (derive) {
|
||||
const hash = derive && derive.hash ? derive.hash : derive;
|
||||
const type = inDeriveType(derive);
|
||||
|
||||
return {
|
||||
hash: inHex(hash),
|
||||
type
|
||||
};
|
||||
}
|
||||
|
||||
export function inDeriveIndex (derive) {
|
||||
if (!derive) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (!isArray(derive)) {
|
||||
derive = [derive];
|
||||
}
|
||||
|
||||
return derive.map(item => {
|
||||
const index = inNumber10(item && item.index ? item.index : item);
|
||||
|
||||
return {
|
||||
index,
|
||||
type: inDeriveType(item)
|
||||
};
|
||||
});
|
||||
}
|
@ -1,341 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import BigNumber from 'bignumber.js';
|
||||
|
||||
import {
|
||||
inAddress, inBlockNumber, inData, inFilter, inHex,
|
||||
inNumber10, inNumber16, inOptions, inTraceType,
|
||||
inDeriveHash, inDeriveIndex
|
||||
} from './input';
|
||||
import { isAddress } from '../../../test/types';
|
||||
|
||||
describe('api/format/input', () => {
|
||||
const address = '0x63cf90d3f0410092fc0fca41846f596223979195';
|
||||
|
||||
describe('inAddress', () => {
|
||||
const address = '63cf90d3f0410092fc0fca41846f596223979195';
|
||||
|
||||
it('adds the leading 0x as required', () => {
|
||||
expect(inAddress(address)).to.equal(`0x${address}`);
|
||||
});
|
||||
|
||||
it('returns verified addresses as-is', () => {
|
||||
expect(inAddress(`0x${address}`)).to.equal(`0x${address}`);
|
||||
});
|
||||
|
||||
it('returns lowercase equivalents', () => {
|
||||
expect(inAddress(address.toUpperCase())).to.equal(`0x${address}`);
|
||||
});
|
||||
|
||||
it('returns 0x on null addresses', () => {
|
||||
expect(inAddress()).to.equal('0x');
|
||||
});
|
||||
});
|
||||
|
||||
describe('inBlockNumber()', () => {
|
||||
it('returns earliest as-is', () => {
|
||||
expect(inBlockNumber('earliest')).to.equal('earliest');
|
||||
});
|
||||
|
||||
it('returns latest as-is', () => {
|
||||
expect(inBlockNumber('latest')).to.equal('latest');
|
||||
});
|
||||
|
||||
it('returns pending as-is', () => {
|
||||
expect(inBlockNumber('pending')).to.equal('pending');
|
||||
});
|
||||
|
||||
it('formats existing BigNumber into hex', () => {
|
||||
expect(inBlockNumber(new BigNumber(0x123456))).to.equal('0x123456');
|
||||
});
|
||||
|
||||
it('formats hex strings into hex', () => {
|
||||
expect(inBlockNumber('0x123456')).to.equal('0x123456');
|
||||
});
|
||||
|
||||
it('formats numbers into hex', () => {
|
||||
expect(inBlockNumber(0x123456)).to.equal('0x123456');
|
||||
});
|
||||
});
|
||||
|
||||
describe('inData', () => {
|
||||
it('formats to hex', () => {
|
||||
expect(inData('123456')).to.equal('0x123456');
|
||||
});
|
||||
|
||||
it('converts a string to a hex representation', () => {
|
||||
expect(inData('jaco')).to.equal('0x6a61636f');
|
||||
});
|
||||
});
|
||||
|
||||
describe('inHex', () => {
|
||||
it('leaves leading 0x as-is', () => {
|
||||
expect(inHex('0x123456')).to.equal('0x123456');
|
||||
});
|
||||
|
||||
it('adds a leading 0x', () => {
|
||||
expect(inHex('123456')).to.equal('0x123456');
|
||||
});
|
||||
|
||||
it('returns uppercase as lowercase (leading 0x)', () => {
|
||||
expect(inHex('0xABCDEF')).to.equal('0xabcdef');
|
||||
});
|
||||
|
||||
it('returns uppercase as lowercase (no leading 0x)', () => {
|
||||
expect(inHex('ABCDEF')).to.equal('0xabcdef');
|
||||
});
|
||||
|
||||
it('handles empty & null', () => {
|
||||
expect(inHex()).to.equal('0x');
|
||||
expect(inHex('')).to.equal('0x');
|
||||
});
|
||||
});
|
||||
|
||||
describe('inFilter', () => {
|
||||
['address'].forEach((input) => {
|
||||
it(`formats ${input} address as address`, () => {
|
||||
const block = {};
|
||||
|
||||
block[input] = address;
|
||||
const formatted = inFilter(block)[input];
|
||||
|
||||
expect(isAddress(formatted)).to.be.true;
|
||||
expect(formatted).to.equal(address);
|
||||
});
|
||||
});
|
||||
|
||||
['fromBlock', 'toBlock'].forEach((input) => {
|
||||
it(`formats ${input} number as blockNumber`, () => {
|
||||
const block = {};
|
||||
|
||||
block[input] = 0x123;
|
||||
const formatted = inFilter(block)[input];
|
||||
|
||||
expect(formatted).to.equal('0x123');
|
||||
});
|
||||
});
|
||||
|
||||
it('ignores and passes through unknown keys', () => {
|
||||
expect(inFilter({ someRandom: 'someRandom' })).to.deep.equal({ someRandom: 'someRandom' });
|
||||
});
|
||||
|
||||
it('formats an filter options object with relevant entries converted', () => {
|
||||
expect(
|
||||
inFilter({
|
||||
address: address,
|
||||
fromBlock: 'latest',
|
||||
toBlock: 0x101,
|
||||
extraData: 'someExtraStuffInHere',
|
||||
limit: 0x32
|
||||
})
|
||||
).to.deep.equal({
|
||||
address: address,
|
||||
fromBlock: 'latest',
|
||||
toBlock: '0x101',
|
||||
extraData: 'someExtraStuffInHere',
|
||||
limit: 50
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('inNumber10()', () => {
|
||||
it('formats existing BigNumber into number', () => {
|
||||
expect(inNumber10(new BigNumber(123))).to.equal(123);
|
||||
});
|
||||
|
||||
it('formats hex strings into decimal', () => {
|
||||
expect(inNumber10('0x0a')).to.equal(10);
|
||||
});
|
||||
|
||||
it('formats numbers into number', () => {
|
||||
expect(inNumber10(123)).to.equal(123);
|
||||
});
|
||||
|
||||
it('formats undefined into 0', () => {
|
||||
expect(inNumber10()).to.equal(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('inNumber16()', () => {
|
||||
it('formats existing BigNumber into hex', () => {
|
||||
expect(inNumber16(new BigNumber(0x123456))).to.equal('0x123456');
|
||||
});
|
||||
|
||||
it('formats hex strings into hex', () => {
|
||||
expect(inNumber16('0x123456')).to.equal('0x123456');
|
||||
});
|
||||
|
||||
it('formats numbers into hex', () => {
|
||||
expect(inNumber16(0x123456)).to.equal('0x123456');
|
||||
});
|
||||
|
||||
it('formats undefined into 0', () => {
|
||||
expect(inNumber16()).to.equal('0x0');
|
||||
});
|
||||
});
|
||||
|
||||
describe('inOptions', () => {
|
||||
['data'].forEach((input) => {
|
||||
it(`converts ${input} to hex data`, () => {
|
||||
const block = {};
|
||||
|
||||
block[input] = '1234';
|
||||
const formatted = inData(block[input]);
|
||||
|
||||
expect(formatted).to.equal('0x1234');
|
||||
});
|
||||
});
|
||||
|
||||
['from', 'to'].forEach((input) => {
|
||||
it(`formats ${input} address as address`, () => {
|
||||
const block = {};
|
||||
|
||||
block[input] = address;
|
||||
const formatted = inOptions(block)[input];
|
||||
|
||||
expect(isAddress(formatted)).to.be.true;
|
||||
expect(formatted).to.equal(address);
|
||||
});
|
||||
});
|
||||
|
||||
it('does not encode an empty `to` value', () => {
|
||||
const options = { to: '' };
|
||||
const formatted = inOptions(options);
|
||||
|
||||
expect(formatted.to).to.equal('');
|
||||
});
|
||||
|
||||
['gas', 'gasPrice', 'value', 'nonce'].forEach((input) => {
|
||||
it(`formats ${input} number as hexnumber`, () => {
|
||||
const block = {};
|
||||
|
||||
block[input] = 0x123;
|
||||
const formatted = inOptions(block)[input];
|
||||
|
||||
expect(formatted).to.equal('0x123');
|
||||
});
|
||||
});
|
||||
|
||||
it('passes condition as null when specified as such', () => {
|
||||
expect(inOptions({ condition: null })).to.deep.equal({ condition: null });
|
||||
});
|
||||
|
||||
it('ignores and passes through unknown keys', () => {
|
||||
expect(inOptions({ someRandom: 'someRandom' })).to.deep.equal({ someRandom: 'someRandom' });
|
||||
});
|
||||
|
||||
it('formats an options object with relevant entries converted', () => {
|
||||
expect(
|
||||
inOptions({
|
||||
from: address,
|
||||
to: address,
|
||||
gas: new BigNumber('0x100'),
|
||||
gasPrice: 0x101,
|
||||
value: 258,
|
||||
nonce: '0x104',
|
||||
data: '0123456789',
|
||||
extraData: 'someExtraStuffInHere'
|
||||
})
|
||||
).to.deep.equal({
|
||||
from: address,
|
||||
to: address,
|
||||
gas: '0x100',
|
||||
gasPrice: '0x101',
|
||||
value: '0x102',
|
||||
nonce: '0x104',
|
||||
data: '0x0123456789',
|
||||
extraData: 'someExtraStuffInHere'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('inTraceType', () => {
|
||||
it('returns array of types as is', () => {
|
||||
const types = ['vmTrace', 'trace', 'stateDiff'];
|
||||
|
||||
expect(inTraceType(types)).to.deep.equal(types);
|
||||
});
|
||||
|
||||
it('formats single string type into array', () => {
|
||||
const type = 'vmTrace';
|
||||
|
||||
expect(inTraceType(type)).to.deep.equal([type]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('inDeriveHash', () => {
|
||||
it('returns derive hash', () => {
|
||||
expect(inDeriveHash(1)).to.deep.equal({
|
||||
hash: '0x1',
|
||||
type: 'soft'
|
||||
});
|
||||
|
||||
expect(inDeriveHash(null)).to.deep.equal({
|
||||
hash: '0x',
|
||||
type: 'soft'
|
||||
});
|
||||
|
||||
expect(inDeriveHash({
|
||||
hash: 5
|
||||
})).to.deep.equal({
|
||||
hash: '0x5',
|
||||
type: 'soft'
|
||||
});
|
||||
|
||||
expect(inDeriveHash({
|
||||
hash: 5,
|
||||
type: 'hard'
|
||||
})).to.deep.equal({
|
||||
hash: '0x5',
|
||||
type: 'hard'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('inDeriveIndex', () => {
|
||||
it('returns derive hash', () => {
|
||||
expect(inDeriveIndex(null)).to.deep.equal([]);
|
||||
expect(inDeriveIndex([])).to.deep.equal([]);
|
||||
|
||||
expect(inDeriveIndex([1])).to.deep.equal([{
|
||||
index: 1,
|
||||
type: 'soft'
|
||||
}]);
|
||||
|
||||
expect(inDeriveIndex({
|
||||
index: 1
|
||||
})).to.deep.equal([{
|
||||
index: 1,
|
||||
type: 'soft'
|
||||
}]);
|
||||
|
||||
expect(inDeriveIndex([{
|
||||
index: 1,
|
||||
type: 'hard'
|
||||
}, 5])).to.deep.equal([
|
||||
{
|
||||
index: 1,
|
||||
type: 'hard'
|
||||
},
|
||||
{
|
||||
index: 5,
|
||||
type: 'soft'
|
||||
}
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
@ -1,414 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import BigNumber from 'bignumber.js';
|
||||
|
||||
import { toChecksumAddress } from '../../abi/util/address';
|
||||
import { isString } from '../util/types';
|
||||
|
||||
export function outAccountInfo (infos) {
|
||||
return Object
|
||||
.keys(infos)
|
||||
.reduce((ret, _address) => {
|
||||
const info = infos[_address];
|
||||
const address = outAddress(_address);
|
||||
|
||||
ret[address] = {
|
||||
name: info.name
|
||||
};
|
||||
|
||||
if (info.meta) {
|
||||
ret[address].uuid = info.uuid;
|
||||
ret[address].meta = JSON.parse(info.meta);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}, {});
|
||||
}
|
||||
|
||||
export function outAddress (address) {
|
||||
return toChecksumAddress(address);
|
||||
}
|
||||
|
||||
export function outAddresses (addresses) {
|
||||
return (addresses || []).map(outAddress);
|
||||
}
|
||||
|
||||
export function outBlock (block) {
|
||||
if (block) {
|
||||
Object.keys(block).forEach((key) => {
|
||||
switch (key) {
|
||||
case 'author':
|
||||
case 'miner':
|
||||
block[key] = outAddress(block[key]);
|
||||
break;
|
||||
|
||||
case 'difficulty':
|
||||
case 'gasLimit':
|
||||
case 'gasUsed':
|
||||
case 'nonce':
|
||||
case 'number':
|
||||
case 'totalDifficulty':
|
||||
block[key] = outNumber(block[key]);
|
||||
break;
|
||||
|
||||
case 'timestamp':
|
||||
block[key] = outDate(block[key]);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
export function outChainStatus (status) {
|
||||
if (status) {
|
||||
Object.keys(status).forEach((key) => {
|
||||
switch (key) {
|
||||
case 'blockGap':
|
||||
status[key] = status[key]
|
||||
? status[key].map(outNumber)
|
||||
: status[key];
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
export function outDate (date) {
|
||||
if (typeof date.toISOString === 'function') {
|
||||
return date;
|
||||
}
|
||||
|
||||
try {
|
||||
if (typeof date === 'string' && (new Date(date)).toISOString() === date) {
|
||||
return new Date(date);
|
||||
}
|
||||
} catch (error) {}
|
||||
|
||||
return new Date(outNumber(date).toNumber() * 1000);
|
||||
}
|
||||
|
||||
export function outHistogram (histogram) {
|
||||
if (histogram) {
|
||||
Object.keys(histogram).forEach((key) => {
|
||||
switch (key) {
|
||||
case 'bucketBounds':
|
||||
case 'counts':
|
||||
histogram[key] = histogram[key].map(outNumber);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return histogram;
|
||||
}
|
||||
|
||||
export function outLog (log) {
|
||||
Object.keys(log).forEach((key) => {
|
||||
switch (key) {
|
||||
case 'blockNumber':
|
||||
case 'logIndex':
|
||||
case 'transactionIndex':
|
||||
log[key] = outNumber(log[key]);
|
||||
break;
|
||||
|
||||
case 'address':
|
||||
log[key] = outAddress(log[key]);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
return log;
|
||||
}
|
||||
|
||||
export function outHwAccountInfo (infos) {
|
||||
return Object
|
||||
.keys(infos)
|
||||
.reduce((ret, _address) => {
|
||||
const address = outAddress(_address);
|
||||
|
||||
ret[address] = infos[_address];
|
||||
|
||||
return ret;
|
||||
}, {});
|
||||
}
|
||||
|
||||
export function outNodeKind (info) {
|
||||
return info;
|
||||
}
|
||||
|
||||
export function outNumber (number) {
|
||||
return new BigNumber(number || 0);
|
||||
}
|
||||
|
||||
export function outPeer (peer) {
|
||||
const protocols = Object.keys(peer.protocols)
|
||||
.reduce((obj, key) => {
|
||||
if (peer.protocols[key]) {
|
||||
obj[key] = {
|
||||
...peer.protocols[key],
|
||||
difficulty: outNumber(peer.protocols[key].difficulty)
|
||||
};
|
||||
}
|
||||
|
||||
return obj;
|
||||
}, {});
|
||||
|
||||
return {
|
||||
...peer,
|
||||
protocols
|
||||
};
|
||||
}
|
||||
|
||||
export function outPeers (peers) {
|
||||
return {
|
||||
active: outNumber(peers.active),
|
||||
connected: outNumber(peers.connected),
|
||||
max: outNumber(peers.max),
|
||||
peers: peers.peers.map((peer) => outPeer(peer))
|
||||
};
|
||||
}
|
||||
|
||||
export function outReceipt (receipt) {
|
||||
if (receipt) {
|
||||
Object.keys(receipt).forEach((key) => {
|
||||
switch (key) {
|
||||
case 'blockNumber':
|
||||
case 'cumulativeGasUsed':
|
||||
case 'gasUsed':
|
||||
case 'transactionIndex':
|
||||
receipt[key] = outNumber(receipt[key]);
|
||||
break;
|
||||
|
||||
case 'contractAddress':
|
||||
receipt[key] = outAddress(receipt[key]);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return receipt;
|
||||
}
|
||||
|
||||
export function outRecentDapps (recentDapps) {
|
||||
if (recentDapps) {
|
||||
Object.keys(recentDapps).forEach((url) => {
|
||||
recentDapps[url] = outDate(recentDapps[url]);
|
||||
});
|
||||
}
|
||||
|
||||
return recentDapps;
|
||||
}
|
||||
|
||||
export function outSignerRequest (request) {
|
||||
if (request) {
|
||||
Object.keys(request).forEach((key) => {
|
||||
switch (key) {
|
||||
case 'id':
|
||||
request[key] = outNumber(request[key]);
|
||||
break;
|
||||
|
||||
case 'payload':
|
||||
request[key].decrypt = outSigningPayload(request[key].decrypt);
|
||||
request[key].sign = outSigningPayload(request[key].sign);
|
||||
request[key].signTransaction = outTransaction(request[key].signTransaction);
|
||||
request[key].sendTransaction = outTransaction(request[key].sendTransaction);
|
||||
break;
|
||||
|
||||
case 'origin':
|
||||
const type = Object.keys(request[key])[0];
|
||||
const details = request[key][type];
|
||||
|
||||
request[key] = { type, details };
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
export function outSyncing (syncing) {
|
||||
if (syncing && syncing !== 'false') {
|
||||
Object.keys(syncing).forEach((key) => {
|
||||
switch (key) {
|
||||
case 'currentBlock':
|
||||
case 'highestBlock':
|
||||
case 'startingBlock':
|
||||
case 'warpChunksAmount':
|
||||
case 'warpChunksProcessed':
|
||||
syncing[key] = outNumber(syncing[key]);
|
||||
break;
|
||||
|
||||
case 'blockGap':
|
||||
syncing[key] = syncing[key] ? syncing[key].map(outNumber) : syncing[key];
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return syncing;
|
||||
}
|
||||
|
||||
export function outTransactionCondition (condition) {
|
||||
if (condition) {
|
||||
if (condition.block) {
|
||||
condition.block = outNumber(condition.block);
|
||||
} else if (condition.time) {
|
||||
condition.time = outDate(condition.time);
|
||||
}
|
||||
}
|
||||
|
||||
return condition;
|
||||
}
|
||||
|
||||
export function outTransaction (tx) {
|
||||
if (tx) {
|
||||
Object.keys(tx).forEach((key) => {
|
||||
switch (key) {
|
||||
case 'blockNumber':
|
||||
case 'gasPrice':
|
||||
case 'gas':
|
||||
case 'nonce':
|
||||
case 'transactionIndex':
|
||||
case 'value':
|
||||
tx[key] = outNumber(tx[key]);
|
||||
break;
|
||||
|
||||
case 'condition':
|
||||
tx[key] = outTransactionCondition(tx[key]);
|
||||
break;
|
||||
|
||||
case 'creates':
|
||||
case 'from':
|
||||
case 'to':
|
||||
tx[key] = outAddress(tx[key]);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return tx;
|
||||
}
|
||||
|
||||
export function outSigningPayload (payload) {
|
||||
if (payload) {
|
||||
Object.keys(payload).forEach((key) => {
|
||||
switch (key) {
|
||||
case 'address':
|
||||
payload[key] = outAddress(payload[key]);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return payload;
|
||||
}
|
||||
|
||||
export function outTrace (trace) {
|
||||
if (trace) {
|
||||
if (trace.action) {
|
||||
Object.keys(trace.action).forEach(key => {
|
||||
switch (key) {
|
||||
case 'gas':
|
||||
case 'value':
|
||||
case 'balance':
|
||||
trace.action[key] = outNumber(trace.action[key]);
|
||||
break;
|
||||
|
||||
case 'from':
|
||||
case 'to':
|
||||
case 'address':
|
||||
case 'refundAddress':
|
||||
trace.action[key] = outAddress(trace.action[key]);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (trace.result) {
|
||||
Object.keys(trace.result).forEach(key => {
|
||||
switch (key) {
|
||||
case 'gasUsed':
|
||||
trace.result[key] = outNumber(trace.result[key]);
|
||||
break;
|
||||
|
||||
case 'address':
|
||||
trace.action[key] = outAddress(trace.action[key]);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (trace.traceAddress) {
|
||||
trace.traceAddress.forEach((address, index) => {
|
||||
trace.traceAddress[index] = outNumber(address);
|
||||
});
|
||||
}
|
||||
|
||||
Object.keys(trace).forEach((key) => {
|
||||
switch (key) {
|
||||
case 'subtraces':
|
||||
case 'transactionPosition':
|
||||
case 'blockNumber':
|
||||
trace[key] = outNumber(trace[key]);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return trace;
|
||||
}
|
||||
|
||||
export function outTraces (traces) {
|
||||
if (traces) {
|
||||
return traces.map(outTrace);
|
||||
}
|
||||
|
||||
return traces;
|
||||
}
|
||||
|
||||
export function outTraceReplay (trace) {
|
||||
if (trace) {
|
||||
Object.keys(trace).forEach((key) => {
|
||||
switch (key) {
|
||||
case 'trace':
|
||||
trace[key] = outTraces(trace[key]);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return trace;
|
||||
}
|
||||
|
||||
export function outVaultMeta (meta) {
|
||||
if (isString(meta)) {
|
||||
try {
|
||||
const obj = JSON.parse(meta);
|
||||
|
||||
return obj;
|
||||
} catch (error) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
return meta || {};
|
||||
}
|
@ -1,502 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import BigNumber from 'bignumber.js';
|
||||
|
||||
import { outBlock, outAccountInfo, outAddress, outChainStatus, outDate, outHistogram, outHwAccountInfo, outNodeKind, outNumber, outPeer, outPeers, outReceipt, outRecentDapps, outSyncing, outTransaction, outTrace, outVaultMeta } from './output';
|
||||
import { isAddress, isBigNumber, isInstanceOf } from '../../../test/types';
|
||||
|
||||
describe('api/format/output', () => {
|
||||
const address = '0x63cf90d3f0410092fc0fca41846f596223979195';
|
||||
const checksum = '0x63Cf90D3f0410092FC0fca41846f596223979195';
|
||||
|
||||
describe('outAccountInfo', () => {
|
||||
it('returns meta objects parsed', () => {
|
||||
expect(outAccountInfo(
|
||||
{ '0x63cf90d3f0410092fc0fca41846f596223979195': {
|
||||
name: 'name', uuid: 'uuid', meta: '{"name":"456"}' }
|
||||
}
|
||||
)).to.deep.equal({
|
||||
'0x63Cf90D3f0410092FC0fca41846f596223979195': {
|
||||
name: 'name', uuid: 'uuid', meta: { name: '456' }
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('returns objects without meta & uuid as required', () => {
|
||||
expect(outAccountInfo(
|
||||
{ '0x63cf90d3f0410092fc0fca41846f596223979195': { name: 'name' } }
|
||||
)).to.deep.equal({
|
||||
'0x63Cf90D3f0410092FC0fca41846f596223979195': { name: 'name' }
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('outAddress', () => {
|
||||
it('retuns the address as checksummed', () => {
|
||||
expect(outAddress(address)).to.equal(checksum);
|
||||
});
|
||||
|
||||
it('retuns the checksum as checksummed', () => {
|
||||
expect(outAddress(checksum)).to.equal(checksum);
|
||||
});
|
||||
});
|
||||
|
||||
describe('outBlock', () => {
|
||||
['author', 'miner'].forEach((input) => {
|
||||
it(`formats ${input} address as address`, () => {
|
||||
const block = {};
|
||||
|
||||
block[input] = address;
|
||||
const formatted = outBlock(block)[input];
|
||||
|
||||
expect(isAddress(formatted)).to.be.true;
|
||||
expect(formatted).to.equal(checksum);
|
||||
});
|
||||
});
|
||||
|
||||
['difficulty', 'gasLimit', 'gasUsed', 'number', 'nonce', 'totalDifficulty'].forEach((input) => {
|
||||
it(`formats ${input} number as hexnumber`, () => {
|
||||
const block = {};
|
||||
|
||||
block[input] = 0x123;
|
||||
const formatted = outBlock(block)[input];
|
||||
|
||||
expect(isInstanceOf(formatted, BigNumber)).to.be.true;
|
||||
expect(formatted.toString(16)).to.equal('123');
|
||||
});
|
||||
});
|
||||
|
||||
['timestamp'].forEach((input) => {
|
||||
it(`formats ${input} number as Date`, () => {
|
||||
const block = {};
|
||||
|
||||
block[input] = 0x57513668;
|
||||
const formatted = outBlock(block)[input];
|
||||
|
||||
expect(isInstanceOf(formatted, Date)).to.be.true;
|
||||
expect(formatted.getTime()).to.equal(1464940136000);
|
||||
});
|
||||
});
|
||||
|
||||
it('ignores and passes through unknown keys', () => {
|
||||
expect(outBlock({ someRandom: 'someRandom' })).to.deep.equal({ someRandom: 'someRandom' });
|
||||
});
|
||||
|
||||
it('formats a block with all the info converted', () => {
|
||||
expect(
|
||||
outBlock({
|
||||
author: address,
|
||||
miner: address,
|
||||
difficulty: '0x100',
|
||||
gasLimit: '0x101',
|
||||
gasUsed: '0x102',
|
||||
number: '0x103',
|
||||
nonce: '0x104',
|
||||
totalDifficulty: '0x105',
|
||||
timestamp: '0x57513668',
|
||||
extraData: 'someExtraStuffInHere'
|
||||
})
|
||||
).to.deep.equal({
|
||||
author: checksum,
|
||||
miner: checksum,
|
||||
difficulty: new BigNumber('0x100'),
|
||||
gasLimit: new BigNumber('0x101'),
|
||||
gasUsed: new BigNumber('0x102'),
|
||||
number: new BigNumber('0x103'),
|
||||
nonce: new BigNumber('0x104'),
|
||||
totalDifficulty: new BigNumber('0x105'),
|
||||
timestamp: new Date('2016-06-03T07:48:56.000Z'),
|
||||
extraData: 'someExtraStuffInHere'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('outChainStatus', () => {
|
||||
it('formats blockGap values', () => {
|
||||
const status = {
|
||||
blockGap: [0x1234, '0x5678']
|
||||
};
|
||||
|
||||
expect(outChainStatus(status)).to.deep.equal({
|
||||
blockGap: [new BigNumber(0x1234), new BigNumber(0x5678)]
|
||||
});
|
||||
});
|
||||
|
||||
it('handles null blockGap values', () => {
|
||||
const status = {
|
||||
blockGap: null
|
||||
};
|
||||
|
||||
expect(outChainStatus(status)).to.deep.equal(status);
|
||||
});
|
||||
});
|
||||
|
||||
describe('outDate', () => {
|
||||
it('converts a second date in unix timestamp', () => {
|
||||
expect(outDate(0x57513668)).to.deep.equal(new Date('2016-06-03T07:48:56.000Z'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('outHistogram', () => {
|
||||
['bucketBounds', 'counts'].forEach((type) => {
|
||||
it(`formats ${type} as number arrays`, () => {
|
||||
expect(
|
||||
outHistogram({ [type]: [0x123, 0x456, 0x789] })
|
||||
).to.deep.equal({
|
||||
[type]: [new BigNumber(0x123), new BigNumber(0x456), new BigNumber(0x789)]
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('outHwAccountInfo', () => {
|
||||
it('returns objects with formatted addresses', () => {
|
||||
expect(outHwAccountInfo(
|
||||
{ '0x63cf90d3f0410092fc0fca41846f596223979195': { manufacturer: 'mfg', name: 'type' } }
|
||||
)).to.deep.equal({
|
||||
'0x63Cf90D3f0410092FC0fca41846f596223979195': { manufacturer: 'mfg', name: 'type' }
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('outNodeKind', () => {
|
||||
it('formats the input as received', () => {
|
||||
const kind = { availability: 'personal', capability: 'full' };
|
||||
|
||||
expect(outNodeKind(kind)).to.deep.equal(kind);
|
||||
});
|
||||
});
|
||||
|
||||
describe('outNumber', () => {
|
||||
it('returns a BigNumber equalling the value', () => {
|
||||
const bn = outNumber('0x123456');
|
||||
|
||||
expect(isBigNumber(bn)).to.be.true;
|
||||
expect(bn.eq(0x123456)).to.be.true;
|
||||
});
|
||||
|
||||
it('assumes 0 when ivalid input', () => {
|
||||
expect(outNumber().eq(0)).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe('outPeer', () => {
|
||||
it('converts all internal numbers to BigNumbers', () => {
|
||||
expect(outPeer({
|
||||
caps: ['par/1'],
|
||||
id: '0x01',
|
||||
name: 'Parity',
|
||||
network: {
|
||||
localAddress: '10.0.0.1',
|
||||
remoteAddress: '10.0.0.1'
|
||||
},
|
||||
protocols: {
|
||||
par: {
|
||||
difficulty: '0x0f',
|
||||
head: '0x02',
|
||||
version: 63
|
||||
}
|
||||
}
|
||||
})).to.deep.equal({
|
||||
caps: ['par/1'],
|
||||
id: '0x01',
|
||||
name: 'Parity',
|
||||
network: {
|
||||
localAddress: '10.0.0.1',
|
||||
remoteAddress: '10.0.0.1'
|
||||
},
|
||||
protocols: {
|
||||
par: {
|
||||
difficulty: new BigNumber(15),
|
||||
head: '0x02',
|
||||
version: 63
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('does not output null protocols', () => {
|
||||
expect(outPeer({
|
||||
caps: ['par/1'],
|
||||
id: '0x01',
|
||||
name: 'Parity',
|
||||
network: {
|
||||
localAddress: '10.0.0.1',
|
||||
remoteAddress: '10.0.0.1'
|
||||
},
|
||||
protocols: {
|
||||
les: null
|
||||
}
|
||||
})).to.deep.equal({
|
||||
caps: ['par/1'],
|
||||
id: '0x01',
|
||||
name: 'Parity',
|
||||
network: {
|
||||
localAddress: '10.0.0.1',
|
||||
remoteAddress: '10.0.0.1'
|
||||
},
|
||||
protocols: {}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('outPeers', () => {
|
||||
it('converts all internal numbers to BigNumbers', () => {
|
||||
expect(outPeers({
|
||||
active: 789,
|
||||
connected: '456',
|
||||
max: 0x7b,
|
||||
peers: [
|
||||
{
|
||||
caps: ['par/1'],
|
||||
id: '0x01',
|
||||
name: 'Parity',
|
||||
network: {
|
||||
localAddress: '10.0.0.1',
|
||||
remoteAddress: '10.0.0.1'
|
||||
},
|
||||
protocols: {
|
||||
par: {
|
||||
difficulty: '0x0f',
|
||||
head: '0x02',
|
||||
version: 63
|
||||
},
|
||||
les: null
|
||||
}
|
||||
}
|
||||
]
|
||||
})).to.deep.equal({
|
||||
active: new BigNumber(789),
|
||||
connected: new BigNumber(456),
|
||||
max: new BigNumber(123),
|
||||
peers: [
|
||||
{
|
||||
caps: ['par/1'],
|
||||
id: '0x01',
|
||||
name: 'Parity',
|
||||
network: {
|
||||
localAddress: '10.0.0.1',
|
||||
remoteAddress: '10.0.0.1'
|
||||
},
|
||||
protocols: {
|
||||
par: {
|
||||
difficulty: new BigNumber(15),
|
||||
head: '0x02',
|
||||
version: 63
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('outReceipt', () => {
|
||||
['contractAddress'].forEach((input) => {
|
||||
it(`formats ${input} address as address`, () => {
|
||||
const block = {};
|
||||
|
||||
block[input] = address;
|
||||
const formatted = outReceipt(block)[input];
|
||||
|
||||
expect(isAddress(formatted)).to.be.true;
|
||||
expect(formatted).to.equal(checksum);
|
||||
});
|
||||
});
|
||||
|
||||
['blockNumber', 'cumulativeGasUsed', 'cumulativeGasUsed', 'gasUsed', 'transactionIndex'].forEach((input) => {
|
||||
it(`formats ${input} number as hexnumber`, () => {
|
||||
const block = {};
|
||||
|
||||
block[input] = 0x123;
|
||||
const formatted = outReceipt(block)[input];
|
||||
|
||||
expect(isInstanceOf(formatted, BigNumber)).to.be.true;
|
||||
expect(formatted.toString(16)).to.equal('123');
|
||||
});
|
||||
});
|
||||
|
||||
it('ignores and passes through unknown keys', () => {
|
||||
expect(outReceipt({ someRandom: 'someRandom' })).to.deep.equal({ someRandom: 'someRandom' });
|
||||
});
|
||||
|
||||
it('formats a receipt with all the info converted', () => {
|
||||
expect(
|
||||
outReceipt({
|
||||
contractAddress: address,
|
||||
blockNumber: '0x100',
|
||||
cumulativeGasUsed: '0x101',
|
||||
gasUsed: '0x102',
|
||||
transactionIndex: '0x103',
|
||||
extraData: 'someExtraStuffInHere'
|
||||
})
|
||||
).to.deep.equal({
|
||||
contractAddress: checksum,
|
||||
blockNumber: new BigNumber('0x100'),
|
||||
cumulativeGasUsed: new BigNumber('0x101'),
|
||||
gasUsed: new BigNumber('0x102'),
|
||||
transactionIndex: new BigNumber('0x103'),
|
||||
extraData: 'someExtraStuffInHere'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('outRecentDapps', () => {
|
||||
it('formats the URLs with timestamps', () => {
|
||||
expect(outRecentDapps({ testing: 0x57513668 })).to.deep.equal({
|
||||
testing: new Date('2016-06-03T07:48:56.000Z')
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('outSyncing', () => {
|
||||
['currentBlock', 'highestBlock', 'startingBlock', 'warpChunksAmount', 'warpChunksProcessed'].forEach((input) => {
|
||||
it(`formats ${input} numbers as a number`, () => {
|
||||
expect(outSyncing({ [input]: '0x123' })).to.deep.equal({
|
||||
[input]: new BigNumber('0x123')
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('formats blockGap properly', () => {
|
||||
expect(outSyncing({ blockGap: [0x123, 0x456] })).to.deep.equal({
|
||||
blockGap: [new BigNumber(0x123), new BigNumber(0x456)]
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('outTransaction', () => {
|
||||
['from', 'to'].forEach((input) => {
|
||||
it(`formats ${input} address as address`, () => {
|
||||
const block = {};
|
||||
|
||||
block[input] = address;
|
||||
const formatted = outTransaction(block)[input];
|
||||
|
||||
expect(isAddress(formatted)).to.be.true;
|
||||
expect(formatted).to.equal(checksum);
|
||||
});
|
||||
});
|
||||
|
||||
['blockNumber', 'gasPrice', 'gas', 'nonce', 'transactionIndex', 'value'].forEach((input) => {
|
||||
it(`formats ${input} number as hexnumber`, () => {
|
||||
const block = {};
|
||||
|
||||
block[input] = 0x123;
|
||||
const formatted = outTransaction(block)[input];
|
||||
|
||||
expect(isInstanceOf(formatted, BigNumber)).to.be.true;
|
||||
expect(formatted.toString(16)).to.equal('123');
|
||||
});
|
||||
});
|
||||
|
||||
it('passes condition as null when null', () => {
|
||||
expect(outTransaction({ condition: null })).to.deep.equal({ condition: null });
|
||||
});
|
||||
|
||||
it('ignores and passes through unknown keys', () => {
|
||||
expect(outTransaction({ someRandom: 'someRandom' })).to.deep.equal({ someRandom: 'someRandom' });
|
||||
});
|
||||
|
||||
it('formats a transaction with all the info converted', () => {
|
||||
expect(
|
||||
outTransaction({
|
||||
from: address,
|
||||
to: address,
|
||||
blockNumber: '0x100',
|
||||
gasPrice: '0x101',
|
||||
gas: '0x102',
|
||||
nonce: '0x103',
|
||||
transactionIndex: '0x104',
|
||||
value: '0x105',
|
||||
extraData: 'someExtraStuffInHere'
|
||||
})
|
||||
).to.deep.equal({
|
||||
from: checksum,
|
||||
to: checksum,
|
||||
blockNumber: new BigNumber('0x100'),
|
||||
gasPrice: new BigNumber('0x101'),
|
||||
gas: new BigNumber('0x102'),
|
||||
nonce: new BigNumber('0x103'),
|
||||
transactionIndex: new BigNumber('0x104'),
|
||||
value: new BigNumber('0x105'),
|
||||
extraData: 'someExtraStuffInHere'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('outTrace', () => {
|
||||
it('ignores and passes through unknown keys', () => {
|
||||
expect(outTrace({ someRandom: 'someRandom' })).to.deep.equal({ someRandom: 'someRandom' });
|
||||
});
|
||||
|
||||
it('formats a trace with all the info converted', () => {
|
||||
const formatted = outTrace({
|
||||
type: 'call',
|
||||
action: {
|
||||
from: address,
|
||||
to: address,
|
||||
value: '0x06',
|
||||
gas: '0x07',
|
||||
input: '0x1234',
|
||||
callType: 'call'
|
||||
},
|
||||
result: {
|
||||
gasUsed: '0x08',
|
||||
output: '0x5678'
|
||||
},
|
||||
traceAddress: [ '0x2' ],
|
||||
subtraces: 3,
|
||||
transactionPosition: '0xb',
|
||||
transactionHash: '0x000000000000000000000000000000000000000000000000000000000000000c',
|
||||
blockNumber: '0x0d',
|
||||
blockHash: '0x000000000000000000000000000000000000000000000000000000000000000e'
|
||||
});
|
||||
|
||||
expect(isBigNumber(formatted.action.gas)).to.be.true;
|
||||
expect(formatted.action.gas.toNumber()).to.equal(7);
|
||||
expect(isBigNumber(formatted.action.value)).to.be.true;
|
||||
expect(formatted.action.value.toNumber()).to.equal(6);
|
||||
|
||||
expect(formatted.action.from).to.equal(checksum);
|
||||
expect(formatted.action.to).to.equal(checksum);
|
||||
|
||||
expect(isBigNumber(formatted.blockNumber)).to.be.true;
|
||||
expect(formatted.blockNumber.toNumber()).to.equal(13);
|
||||
expect(isBigNumber(formatted.transactionPosition)).to.be.true;
|
||||
expect(formatted.transactionPosition.toNumber()).to.equal(11);
|
||||
});
|
||||
});
|
||||
|
||||
describe('outVaultMeta', () => {
|
||||
it('returns an exmpt object on null', () => {
|
||||
expect(outVaultMeta(null)).to.deep.equal({});
|
||||
});
|
||||
|
||||
it('returns the original value if not string', () => {
|
||||
expect(outVaultMeta({ test: 123 })).to.deep.equal({ test: 123 });
|
||||
});
|
||||
|
||||
it('returns an object from JSON string', () => {
|
||||
expect(outVaultMeta('{"test":123}')).to.deep.equal({ test: 123 });
|
||||
});
|
||||
|
||||
it('returns an empty object on invalid JSON', () => {
|
||||
expect(outVaultMeta('{"test"}')).to.deep.equal({});
|
||||
});
|
||||
});
|
||||
});
|
@ -1,17 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './api';
|
@ -1,116 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { createKeyObject, decryptPrivateKey } from '../ethkey';
|
||||
|
||||
export default class Account {
|
||||
constructor (persist, data = {}) {
|
||||
const {
|
||||
keyObject = null,
|
||||
meta = {},
|
||||
name = ''
|
||||
} = data;
|
||||
|
||||
this._persist = persist;
|
||||
this._keyObject = keyObject;
|
||||
this._name = name;
|
||||
this._meta = meta;
|
||||
}
|
||||
|
||||
isValidPassword (password) {
|
||||
if (!this._keyObject) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return decryptPrivateKey(this._keyObject, password)
|
||||
.then((privateKey) => {
|
||||
if (!privateKey) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
export () {
|
||||
const exported = Object.assign({}, this._keyObject);
|
||||
|
||||
exported.meta = JSON.stringify(this._meta);
|
||||
exported.name = this._name;
|
||||
|
||||
return exported;
|
||||
}
|
||||
|
||||
get address () {
|
||||
return `0x${this._keyObject.address.toLowerCase()}`;
|
||||
}
|
||||
|
||||
get name () {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
set name (name) {
|
||||
this._name = name;
|
||||
|
||||
this._persist();
|
||||
}
|
||||
|
||||
get meta () {
|
||||
return JSON.stringify(this._meta);
|
||||
}
|
||||
|
||||
set meta (meta) {
|
||||
this._meta = JSON.parse(meta);
|
||||
|
||||
this._persist();
|
||||
}
|
||||
|
||||
get uuid () {
|
||||
if (!this._keyObject) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this._keyObject.id;
|
||||
}
|
||||
|
||||
decryptPrivateKey (password) {
|
||||
return decryptPrivateKey(this._keyObject, password);
|
||||
}
|
||||
|
||||
changePassword (key, password) {
|
||||
return createKeyObject(key, password).then((keyObject) => {
|
||||
this._keyObject = keyObject;
|
||||
|
||||
this._persist();
|
||||
});
|
||||
}
|
||||
|
||||
static fromPrivateKey (persist, key, password) {
|
||||
return createKeyObject(key, password).then((keyObject) => {
|
||||
const account = new Account(persist, { keyObject });
|
||||
|
||||
return account;
|
||||
});
|
||||
}
|
||||
|
||||
toJSON () {
|
||||
return {
|
||||
keyObject: this._keyObject,
|
||||
name: this._name,
|
||||
meta: this._meta
|
||||
};
|
||||
}
|
||||
}
|
@ -1,229 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import Account from './account';
|
||||
import localStore from 'store';
|
||||
import { debounce } from 'lodash';
|
||||
import { decryptPrivateKey } from '../ethkey';
|
||||
|
||||
const NULL_ADDRESS = '0x0000000000000000000000000000000000000000';
|
||||
const LS_STORE_KEY = '_parity::localAccounts';
|
||||
|
||||
export default class Accounts {
|
||||
persist = debounce(() => {
|
||||
this._lastState = JSON.stringify(this);
|
||||
|
||||
localStore.set(LS_STORE_KEY, this);
|
||||
}, 100);
|
||||
|
||||
constructor (data = localStore.get(LS_STORE_KEY) || {}) {
|
||||
this._lastState = JSON.stringify(data);
|
||||
|
||||
window.addEventListener('storage', ({ key, newValue }) => {
|
||||
if (key !== LS_STORE_KEY) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (newValue !== this._lastState) {
|
||||
console.log('Data changed in a second tab, syncing state');
|
||||
|
||||
this.restore(JSON.parse(newValue));
|
||||
}
|
||||
});
|
||||
|
||||
this.restore(data);
|
||||
}
|
||||
|
||||
restore (data) {
|
||||
const {
|
||||
last = NULL_ADDRESS,
|
||||
dappsDefault = NULL_ADDRESS,
|
||||
store = {}
|
||||
} = data;
|
||||
|
||||
this._last = last;
|
||||
this._dappsDefaultAddress = dappsDefault;
|
||||
this._store = {};
|
||||
|
||||
if (Array.isArray(store)) {
|
||||
// Recover older version that stored accounts as an array
|
||||
store.forEach((data) => {
|
||||
const account = new Account(this.persist, data);
|
||||
|
||||
this._store[account.address] = account;
|
||||
});
|
||||
} else {
|
||||
Object.keys(store).forEach((key) => {
|
||||
this._store[key] = new Account(this.persist, store[key]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_addAccount = (account) => {
|
||||
const { address } = account;
|
||||
|
||||
if (address in this._store && this._store[address].uuid) {
|
||||
throw new Error(`Account ${address} already exists!`);
|
||||
}
|
||||
|
||||
this._store[address] = account;
|
||||
this.lastAddress = address;
|
||||
|
||||
this.persist();
|
||||
|
||||
return account.address;
|
||||
}
|
||||
|
||||
create (secret, password) {
|
||||
const privateKey = Buffer.from(secret.slice(2), 'hex');
|
||||
|
||||
return Account
|
||||
.fromPrivateKey(this.persist, privateKey, password)
|
||||
.then(this._addAccount);
|
||||
}
|
||||
|
||||
restoreFromWallet (wallet, password) {
|
||||
return decryptPrivateKey(wallet, password)
|
||||
.then((privateKey) => {
|
||||
if (!privateKey) {
|
||||
throw new Error('Invalid password');
|
||||
}
|
||||
|
||||
return Account.fromPrivateKey(this.persist, privateKey, password);
|
||||
})
|
||||
.then(this._addAccount);
|
||||
}
|
||||
|
||||
set lastAddress (value) {
|
||||
this._last = value.toLowerCase();
|
||||
}
|
||||
|
||||
get lastAddress () {
|
||||
return this._last;
|
||||
}
|
||||
|
||||
get dappsDefaultAddress () {
|
||||
if (this._dappsDefaultAddress === NULL_ADDRESS) {
|
||||
return this._last;
|
||||
}
|
||||
|
||||
if (this._dappsDefaultAddress in this._store) {
|
||||
return this._dappsDefaultAddress;
|
||||
}
|
||||
|
||||
return NULL_ADDRESS;
|
||||
}
|
||||
|
||||
set dappsDefaultAddress (value) {
|
||||
this._dappsDefaultAddress = value.toLowerCase();
|
||||
|
||||
this.persist();
|
||||
}
|
||||
|
||||
get (address) {
|
||||
address = address.toLowerCase();
|
||||
|
||||
const account = this._store[address];
|
||||
|
||||
if (!account) {
|
||||
throw new Error(`Account not found: ${address}`);
|
||||
}
|
||||
|
||||
this.lastAddress = address;
|
||||
|
||||
return account;
|
||||
}
|
||||
|
||||
getLazyCreate (address) {
|
||||
address = address.toLowerCase();
|
||||
|
||||
this.lastAddress = address;
|
||||
|
||||
if (!(address in this._store)) {
|
||||
this._store[address] = new Account(this.persist);
|
||||
}
|
||||
|
||||
return this._store[address];
|
||||
}
|
||||
|
||||
remove (address, password) {
|
||||
address = address.toLowerCase();
|
||||
|
||||
const account = this.get(address);
|
||||
|
||||
if (!account) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!account.uuid) {
|
||||
this.removeUnsafe(address);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return account
|
||||
.isValidPassword(password)
|
||||
.then((isValid) => {
|
||||
if (!isValid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (address === this.lastAddress) {
|
||||
this.lastAddress = NULL_ADDRESS;
|
||||
}
|
||||
|
||||
this.removeUnsafe(address);
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
removeUnsafe (address) {
|
||||
address = address.toLowerCase();
|
||||
|
||||
delete this._store[address];
|
||||
|
||||
this.persist();
|
||||
}
|
||||
|
||||
allAddresses () {
|
||||
return Object.keys(this._store);
|
||||
}
|
||||
|
||||
accountAddresses () {
|
||||
return Object
|
||||
.keys(this._store)
|
||||
.filter((address) => this._store[address].uuid);
|
||||
}
|
||||
|
||||
map (mapper) {
|
||||
const result = {};
|
||||
|
||||
Object.keys(this._store).forEach((key) => {
|
||||
result[key] = mapper(this._store[key]);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
toJSON () {
|
||||
return {
|
||||
last: this._last,
|
||||
dappsDefault: this._dappsDefaultAddress,
|
||||
store: this._store
|
||||
};
|
||||
}
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import Accounts from './accounts';
|
||||
|
||||
const accounts = new Accounts();
|
||||
|
||||
export default accounts;
|
@ -1,147 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/* global WebAssembly */
|
||||
|
||||
import wasmBuffer from './ethkey.wasm.js';
|
||||
|
||||
const NOOP = () => {};
|
||||
|
||||
// WASM memory setup
|
||||
const WASM_PAGE_SIZE = 65536;
|
||||
const STATIC_BASE = 1024;
|
||||
const STATICTOP = STATIC_BASE + WASM_PAGE_SIZE * 2;
|
||||
const STACK_BASE = align(STATICTOP + 16);
|
||||
const STACKTOP = STACK_BASE;
|
||||
const TOTAL_STACK = 5 * 1024 * 1024;
|
||||
const TOTAL_MEMORY = 16777216;
|
||||
const STACK_MAX = STACK_BASE + TOTAL_STACK;
|
||||
const DYNAMIC_BASE = STACK_MAX + 64;
|
||||
const DYNAMICTOP_PTR = STACK_MAX;
|
||||
|
||||
function mockWebAssembly () {
|
||||
function throwWasmError () {
|
||||
throw new Error('Missing WebAssembly support');
|
||||
}
|
||||
|
||||
// Simple mock replacement
|
||||
return {
|
||||
Memory: class { buffer = new ArrayBuffer(2048) },
|
||||
Table: class {},
|
||||
Module: class {},
|
||||
Instance: class {
|
||||
exports = {
|
||||
'_input_ptr': () => 0,
|
||||
'_secret_ptr': () => 0,
|
||||
'_public_ptr': () => 0,
|
||||
'_address_ptr': () => 0,
|
||||
'_ecpointg': NOOP,
|
||||
'_brain': throwWasmError,
|
||||
'_verify_secret': throwWasmError
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const { Memory, Table, Module, Instance } = typeof WebAssembly !== 'undefined' ? WebAssembly : mockWebAssembly();
|
||||
|
||||
const wasmMemory = new Memory({
|
||||
initial: TOTAL_MEMORY / WASM_PAGE_SIZE,
|
||||
maximum: TOTAL_MEMORY / WASM_PAGE_SIZE
|
||||
});
|
||||
|
||||
const wasmTable = new Table({
|
||||
initial: 8,
|
||||
maximum: 8,
|
||||
element: 'anyfunc'
|
||||
});
|
||||
|
||||
// TypedArray views into the memory
|
||||
const wasmMemoryU8 = new Uint8Array(wasmMemory.buffer);
|
||||
const wasmMemoryU32 = new Uint32Array(wasmMemory.buffer);
|
||||
|
||||
// Keep DYNAMIC_BASE in memory
|
||||
wasmMemoryU32[DYNAMICTOP_PTR >> 2] = align(DYNAMIC_BASE);
|
||||
|
||||
function align (mem) {
|
||||
const ALIGN_SIZE = 16;
|
||||
|
||||
return (Math.ceil(mem / ALIGN_SIZE) * ALIGN_SIZE) | 0;
|
||||
}
|
||||
|
||||
export function slice (ptr, len) {
|
||||
return wasmMemoryU8.subarray(ptr, ptr + len);
|
||||
}
|
||||
|
||||
// Required by emscripten
|
||||
function abort (what) {
|
||||
throw new Error(what || 'WASM abort');
|
||||
}
|
||||
|
||||
// Required by emscripten
|
||||
function abortOnCannotGrowMemory () {
|
||||
abort(`Cannot enlarge memory arrays.`);
|
||||
}
|
||||
|
||||
// Required by emscripten
|
||||
function enlargeMemory () {
|
||||
abortOnCannotGrowMemory();
|
||||
}
|
||||
|
||||
// Required by emscripten
|
||||
function getTotalMemory () {
|
||||
return TOTAL_MEMORY;
|
||||
}
|
||||
|
||||
// Required by emscripten - used to perform memcpy on large data
|
||||
function memcpy (dest, src, len) {
|
||||
wasmMemoryU8.set(wasmMemoryU8.subarray(src, src + len), dest);
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
// Synchronously compile WASM from the buffer
|
||||
const module = new Module(wasmBuffer);
|
||||
|
||||
// Instantiated WASM module
|
||||
const instance = new Instance(module, {
|
||||
global: {},
|
||||
env: {
|
||||
DYNAMICTOP_PTR,
|
||||
STACKTOP,
|
||||
STACK_MAX,
|
||||
abort,
|
||||
enlargeMemory,
|
||||
getTotalMemory,
|
||||
abortOnCannotGrowMemory,
|
||||
___lock: NOOP,
|
||||
___syscall6: () => 0,
|
||||
___setErrNo: (no) => no,
|
||||
_abort: abort,
|
||||
___syscall140: () => 0,
|
||||
_emscripten_memcpy_big: memcpy,
|
||||
___syscall54: () => 0,
|
||||
___unlock: NOOP,
|
||||
_llvm_trap: abort,
|
||||
___syscall146: () => 0,
|
||||
'memory': wasmMemory,
|
||||
'table': wasmTable,
|
||||
tableBase: 0,
|
||||
memoryBase: STATIC_BASE
|
||||
}
|
||||
});
|
||||
|
||||
export const extern = instance.exports;
|
File diff suppressed because one or more lines are too long
@ -1,47 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import workerPool from './workerPool';
|
||||
|
||||
export function createKeyObject (key, password) {
|
||||
return workerPool.action('createKeyObject', { key, password })
|
||||
.then((obj) => JSON.parse(obj));
|
||||
}
|
||||
|
||||
export function decryptPrivateKey (keyObject, password) {
|
||||
return workerPool
|
||||
.action('decryptPrivateKey', { keyObject, password })
|
||||
.then((privateKey) => {
|
||||
if (privateKey) {
|
||||
return Buffer.from(privateKey);
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
export function phraseToAddress (phrase) {
|
||||
return phraseToWallet(phrase)
|
||||
.then((wallet) => wallet.address);
|
||||
}
|
||||
|
||||
export function phraseToWallet (phrase) {
|
||||
return workerPool.action('phraseToWallet', phrase);
|
||||
}
|
||||
|
||||
export function verifySecret (secret) {
|
||||
return workerPool.action('verifySecret', secret);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user