Merge branch 'master' into secretstore_kovan

This commit is contained in:
Svyatoslav Nikolsky 2017-11-23 07:27:13 +03:00
commit 12e9c1cebc
666 changed files with 1080 additions and 47327 deletions

26
Cargo.lock generated
View File

@ -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"

View File

@ -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,
}
}
}

View File

@ -306,6 +306,7 @@ mod tests {
version: "".into(),
author: "".into(),
icon_url: "".into(),
local_url: "".into(),
}, Default::default(), None);
// when

View File

@ -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,
}
})
}

View File

@ -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>>;

View File

@ -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

View File

@ -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",

View File

@ -31,6 +31,7 @@ mod result;
#[cfg(test)]
mod tests;
mod env;
mod panic_payload;
const DEFAULT_STACK_SPACE: u32 = 5 * 1024 * 1024;

View 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,
}
);
}
}

View File

@ -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())

View File

@ -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));
}

View File

@ -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()`

View File

@ -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"
}
}

View File

@ -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)

View File

@ -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": {
}
}

View File

@ -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.

View File

@ -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"
}
}

View File

@ -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;
});
});

View File

@ -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)

View File

@ -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"
}
}

View File

@ -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 = {};

View File

@ -1 +0,0 @@
-r ./test/mocha.config

View File

@ -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",

View File

@ -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');

View File

@ -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');
});

View File

@ -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

View File

@ -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}`));
}

View File

@ -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;
});

View File

@ -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) {

View File

@ -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

View File

@ -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 {
}

View File

@ -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;
}
}

View File

@ -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);
});
});
});

View File

@ -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;
}
}

View File

@ -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);
});
});
});

View File

@ -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`);
}
}
}

View File

@ -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]);
});
});
});
});

View File

@ -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`);
}
}

View File

@ -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)}`);
});
});
});
});

View File

@ -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';

View File

@ -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`);
}
}

View File

@ -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);
});
});
});

View File

@ -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';

View File

@ -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);
}
}

View File

@ -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');
});
});
});

View File

@ -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;
}
}

View File

@ -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');
});
});
});

View File

@ -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;
}
}

View File

@ -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);
});
});
});

View File

@ -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);
}
}

View File

@ -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/);
});
});
});

View File

@ -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));
}
}

View File

@ -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');
});
});
});

View File

@ -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';

View File

@ -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);
}
}

View File

@ -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');
});
});
});

View File

@ -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';

View File

@ -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}`);
}
});
}
}

View File

@ -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)])
])
]);
});
});
});

View File

@ -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);
});
}
}

View File

@ -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');
});
});
});

View File

@ -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}`);
}
}

View File

@ -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');
});
});
});
});
});

View File

@ -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';

View File

@ -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`);
}
}

View File

@ -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]);
});
});
});

View File

@ -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;

View File

@ -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';

View File

@ -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`);
}
}

View File

@ -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');
});
});
});

View File

@ -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;
}

View File

@ -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);
});
});
});

View File

@ -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);
}

View File

@ -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'));
});
});
});

View File

@ -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
};
}

View File

@ -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: ''
});
});
});
});

View File

@ -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);
}

View File

@ -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);
});
});
});

View File

@ -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);
}

View File

@ -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);
});
});
});

View File

@ -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;
}

View File

@ -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;
});
});
});

View File

@ -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.

View File

@ -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
}
}

View File

@ -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);
});
});

View File

@ -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);
});
}
}

View File

@ -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);
});
});
});
});
});

View File

@ -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';

View File

@ -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)
};
});
}

View File

@ -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'
}
]);
});
});
});

View File

@ -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 || {};
}

View File

@ -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({});
});
});
});

View File

@ -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';

View File

@ -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
};
}
}

View File

@ -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
};
}
}

View File

@ -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;

View File

@ -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

View File

@ -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