Commit 947fa8c8 authored by WuEcho's avatar WuEcho

upload code

parents
Pipeline #637 failed with stages
[package]
name = "ewasm_api"
version = "0.9.1"
authors = ["Alex Beregszaszi <alex@rtfs.hu>", "Jake Lang <jak3lang@gmail.com>"]
license = "Apache-2.0"
repository = "https://github.com/ewasm/ewasm-rust-api"
description = "ewasm API for Rust"
edition = "2018"
[dependencies]
cfg-if = "0.1.7"
wee_alloc = { version = "0.4.4", optional = true }
qimalloc = { version = "0.1", optional = true }
ethabi = { version = "8.0.0", path = "./libs/ethabi" }
[features]
default = ["std", "wee_alloc"]
std = []
debug = []
experimental = []
This diff is collapsed.
# ewasm-rust-api
# Usage
Add the dependency, as usual:
```toml
[dependencies]
ewasm_api = { git = "https://github.com/WuEcho/ewasm-rust-api", tag = "0.9" }
```
In your project, include the prelude:
```rust
use ewasm_api::prelude::*;
```
Other modules are available as well, outside of the prelude. Refer to the documentation for more info.
`ewasm-rust-api` builds with various feature sets:
- `default`: Builds with `wee_alloc` as the global allocator and with the Rust standard library.
- `qimalloc`: Builds with [qimalloc](https://github.com/wasmx/qimalloc) as the global allocator.
- `debug`: Exposes the debugging interface.
- `experimental`: Exposes the experimental bignum system library API.
Further documentation is available [here](https://docs.rs/ewasm_api/).
## Author(s)
Alex Beregszaszi, Jake Lang
## License
Apache 2.0
version: 2
jobs:
build:
docker:
- image: rust:1
steps:
- checkout
- run:
name: Update rustc
command: |
rustup target add wasm32-unknown-unknown
rustup component add rustfmt-preview
rustup update
- run:
name: Check formatting
command: |
rustfmt --version
cargo fmt
git diff --exit-code
- run:
name: Test
command: cargo test --target=x86_64-unknown-linux-gnu
- run:
name: Build
command: |
cargo build --release
# debug
cargo build
cargo build --release --no-default-features
cargo build --release --features debug
cargo build --release --no-default-features --features debug
cargo build --release --features experimental
cargo build --release --no-default-features --features experimental
cargo build --release --features experimental,debug
cargo build --release --no-default-features --features experimental,debug
cargo build --release --features wee_alloc
cargo build --release --features qimalloc
[package]
name = "ethabi"
version = "8.0.1"
authors = ["Parity Technologies <admin@parity.io>"]
homepage = "https://github.com/paritytech/ethabi"
license = "MIT/Apache-2.0"
keywords = ["ethereum", "eth", "abi", "solidity", "cli"]
description = "Easy to use conversion of ethereum contract calls to bytecode."
[dependencies]
rustc-hex = "2.0"
serde = "1.0"
serde_json = "1.0"
serde_derive = "1.0"
tiny-keccak = "1.4"
error-chain = { version = "0.12", default-features = false }
ethereum-types = "0.6.0"
[dev-dependencies]
hex-literal = "0.2.0"
paste = "0.1.5"
[features]
backtrace = ["error-chain/backtrace"]
[badges]
travis-ci = { repository = "paritytech/ethabi", branch = "master" }
coveralls = { repository = "paritytech/ethabi", branch = "master" }
//! Contract constructor call builder.
use {Param, Result, ErrorKind, Token, ParamType, encode, Bytes};
/// Contract constructor specification.
#[derive(Debug, Clone, PartialEq, Deserialize)]
pub struct Constructor {
/// Constructor input.
pub inputs: Vec<Param>,
}
impl Constructor {
/// Returns all input params of given constructor.
fn param_types(&self) -> Vec<ParamType> {
self.inputs.iter()
.map(|p| p.kind.clone())
.collect()
}
/// Prepares ABI constructor call with given input params.
pub fn encode_input(&self, code: Bytes, tokens: &[Token]) -> Result<Bytes> {
let params = self.param_types();
if Token::types_check(tokens, &params) {
Ok(code.into_iter().chain(encode(tokens)).collect())
} else {
Err(ErrorKind::InvalidData.into())
}
}
}
use std::{io, fmt};
use std::collections::HashMap;
use std::collections::hash_map::Values;
use std::iter::Flatten;
use serde::{Deserialize, Deserializer};
use serde::de::{Visitor, SeqAccess};
use serde_json;
use operation::Operation;
use {errors, ErrorKind, Event, Constructor, Function, ParamType};
use signature::short_signature;
/// API building calls to contracts ABI.
#[derive(Clone, Debug, PartialEq)]
pub struct Contract {
/// Contract constructor.
pub constructor: Option<Constructor>,
/// Contract functions.
pub functions: HashMap<String, Function>,
/// Contract events, maps signature to event.
pub events: HashMap<String, Vec<Event>>,
/// Contract has fallback function.
pub fallback: bool,
/// add by echo : sig -> functions.key
pub signers: HashMap<Vec<u8>, String>,
}
impl<'a> Deserialize<'a> for Contract {
fn deserialize<D>(deserializer: D) -> Result<Contract, D::Error> where D: Deserializer<'a> {
deserializer.deserialize_any(ContractVisitor)
}
}
struct ContractVisitor;
impl<'a> Visitor<'a> for ContractVisitor {
type Value = Contract;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("valid abi spec file")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error> where A: SeqAccess<'a> {
let mut result = Contract {
constructor: None,
functions: HashMap::default(),
events: HashMap::default(),
fallback: false,
signers: HashMap::default(),
};
while let Some(operation) = seq.next_element()? {
match operation {
Operation::Constructor(constructor) => {
result.constructor = Some(constructor);
}
Operation::Function(func) => {
result.functions.insert(func.name.clone(), func);
}
Operation::Event(event) => {
result.events.entry(event.name.clone()).or_default().push(event);
}
Operation::Fallback => {
result.fallback = true;
}
}
}
Ok(result)
}
}
impl Contract {
/*
/// Loads contract from json.
pub fn load<T: io::Read>(reader: T) -> errors::Result<Self> {
let c = serde_json::from_reader(reader).map_err(From::from);
return c;
}*/
/// Creates constructor call builder.
pub fn constructor(&self) -> Option<&Constructor> {
self.constructor.as_ref()
}
/// Creates function call builder.
pub fn function(&self, name: &str) -> errors::Result<&Function> {
self.functions.get(name).ok_or_else(|| ErrorKind::InvalidName(name.to_owned()).into())
}
/// Get the contract event named `name`, the first if there are multiple.
pub fn event(&self, name: &str) -> errors::Result<&Event> {
self.events.get(name).into_iter()
.flatten()
.next()
.ok_or_else(|| ErrorKind::InvalidName(name.to_owned()).into())
}
/// Get all contract events named `name`.
pub fn events_by_name(&self, name: &str) -> errors::Result<&Vec<Event>> {
self.events.get(name)
.ok_or_else(|| ErrorKind::InvalidName(name.to_owned()).into())
}
/// Iterate over all functions of the contract in arbitrary order.
pub fn functions(&self) -> Functions {
Functions(self.functions.values())
}
/// Iterate over all events of the contract in arbitrary order.
pub fn events(&self) -> Events {
Events(self.events.values().flatten())
}
/// Returns true if contract has fallback
pub fn fallback(&self) -> bool {
self.fallback
}
/// add by echo : 初始化函数签名 map
pub fn function_by_sig(&mut self, sig: &Vec<u8>) -> errors::Result<&Function> {
if self.signers.len() == 0 {
self.signers = self.functions.iter().map(|(n, f)| {
let params: Vec<ParamType> = f.inputs.iter().map(|p| p.kind.clone()).collect();
let signed = short_signature(n, &params).to_vec();
(signed.clone(), n.clone())
}).collect();
}
let name = self.signers.get(sig).ok_or_else(|| ErrorKind::InvalidData)?;
self.function(name)
}
}
/// Contract functions interator.
pub struct Functions<'a>(Values<'a, String, Function>);
impl<'a> Iterator for Functions<'a> {
type Item = &'a Function;
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
}
/// Contract events interator.
pub struct Events<'a>(Flatten<Values<'a, String, Vec<Event>>>);
impl<'a> Iterator for Events<'a> {
type Item = &'a Event;
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
}
//! ABI decoder.
use util::slice_data;
use {Word, Token, ErrorKind, Error, ResultExt, ParamType};
struct DecodeResult {
token: Token,
new_offset: usize,
}
struct BytesTaken {
bytes: Vec<u8>,
new_offset: usize,
}
fn as_u32(slice: &Word) -> Result<u32, Error> {
if !slice[..28].iter().all(|x| *x == 0) {
return Err(ErrorKind::InvalidData.into());
}
let result = ((slice[28] as u32) << 24) +
((slice[29] as u32) << 16) +
((slice[30] as u32) << 8) +
(slice[31] as u32);
Ok(result)
}
fn as_bool(slice: &Word) -> Result<bool, Error> {
if !slice[..31].iter().all(|x| *x == 0) {
return Err(ErrorKind::InvalidData.into());
}
Ok(slice[31] == 1)
}
/// Decodes ABI compliant vector of bytes into vector of tokens described by types param.
pub fn decode(types: &[ParamType], data: &[u8]) -> Result<Vec<Token>, Error> {
let is_empty_bytes_valid_encoding = types.iter().all(|t| t.is_empty_bytes_valid_encoding());
if !is_empty_bytes_valid_encoding && data.is_empty() {
bail!("please ensure the contract and method you're calling exist! failed to decode empty bytes. if you're using jsonrpc this is likely due to jsonrpc returning `0x` in case contract or method don't exist");
}
let slices = slice_data(data)?;
let mut tokens = Vec::with_capacity(types.len());
let mut offset = 0;
for param in types {
let res = decode_param(param, &slices, offset).chain_err(|| format!("Cannot decode {}", param))?;
offset = res.new_offset;
tokens.push(res.token);
}
Ok(tokens)
}
fn peek(slices: &[Word], position: usize) -> Result<&Word, Error> {
slices.get(position).ok_or_else(|| ErrorKind::InvalidData.into())
}
fn take_bytes(slices: &[Word], position: usize, len: usize) -> Result<BytesTaken, Error> {
let slices_len = (len + 31) / 32;
let mut bytes_slices = Vec::with_capacity(slices_len);
for i in 0..slices_len {
let slice = try!(peek(slices, position + i));
bytes_slices.push(slice);
}
let bytes = bytes_slices.into_iter()
.flat_map(|slice| slice.to_vec())
.take(len)
.collect();
let taken = BytesTaken {
bytes,
new_offset: position + slices_len,
};
Ok(taken)
}
fn decode_param(param: &ParamType, slices: &[Word], offset: usize) -> Result<DecodeResult, Error> {
match *param {
ParamType::Address => {
let slice = try!(peek(slices, offset));
let mut address = [0u8; 20];
address.copy_from_slice(&slice[12..]);
let result = DecodeResult {
token: Token::Address(address.into()),
new_offset: offset + 1,
};
Ok(result)
},
ParamType::Int(_) => {
let slice = try!(peek(slices, offset));
let result = DecodeResult {
token: Token::Int(slice.clone().into()),
new_offset: offset + 1,
};
Ok(result)
},
ParamType::Uint(_) => {
let slice = try!(peek(slices, offset));
let result = DecodeResult {
token: Token::Uint(slice.clone().into()),
new_offset: offset + 1,
};
Ok(result)
},
ParamType::Bool => {
let slice = try!(peek(slices, offset));
let b = try!(as_bool(slice));
let result = DecodeResult {
token: Token::Bool(b),
new_offset: offset + 1,
};
Ok(result)
},
ParamType::FixedBytes(len) => {
let taken = try!(take_bytes(slices, offset, len));
let result = DecodeResult {
token: Token::FixedBytes(taken.bytes),
new_offset: taken.new_offset,
};
Ok(result)
},
ParamType::Bytes => {
let offset_slice = try!(peek(slices, offset));
let len_offset = (try!(as_u32(offset_slice)) / 32) as usize;
let len_slice = try!(peek(slices, len_offset));
let len = try!(as_u32(len_slice)) as usize;
let taken = try!(take_bytes(slices, len_offset + 1, len));
let result = DecodeResult {
token: Token::Bytes(taken.bytes),
new_offset: offset + 1,
};
Ok(result)
},
ParamType::String => {
let offset_slice = try!(peek(slices, offset));
let len_offset = (try!(as_u32(offset_slice)) / 32) as usize;
let len_slice = try!(peek(slices, len_offset));
let len = try!(as_u32(len_slice)) as usize;
let taken = try!(take_bytes(slices, len_offset + 1, len));
let result = DecodeResult {
token: Token::String(try!(String::from_utf8(taken.bytes))),
new_offset: offset + 1,
};
Ok(result)
},
ParamType::Array(ref t) => {
let offset_slice = try!(peek(slices, offset));
let len_offset = (try!(as_u32(offset_slice)) / 32) as usize;
let len_slice = try!(peek(slices, len_offset));
let len = try!(as_u32(len_slice)) as usize;
let sub_slices = &slices[len_offset + 1..];
let mut tokens = Vec::with_capacity(len);
let mut new_offset = 0;
for _ in 0..len {
let res = try!(decode_param(t, &sub_slices, new_offset));
new_offset = res.new_offset;
tokens.push(res.token);
}
let result = DecodeResult {
token: Token::Array(tokens),
new_offset: offset + 1,
};
Ok(result)
},
ParamType::FixedArray(ref t, len) => {
let mut tokens = Vec::with_capacity(len);
let new_offset = if param.is_dynamic() {
let offset_slice = peek(slices, offset)?;
let tail_offset = (as_u32(offset_slice)? / 32) as usize;
let slices = &slices[tail_offset..];
let mut new_offset = 0;
for _ in 0..len {
let res = decode_param(t, &slices, new_offset)?;
new_offset = res.new_offset;
tokens.push(res.token);
}
offset + 1
} else {
let mut new_offset = offset;
for _ in 0..len {
let res = decode_param(t, &slices, new_offset)?;
new_offset = res.new_offset;
tokens.push(res.token);
}
new_offset
};
let result = DecodeResult {
token: Token::FixedArray(tokens),
new_offset,
};
Ok(result)
}
}
}
#[cfg(test)]
mod tests {
use {decode, ParamType};
#[test]
fn decode_from_empty_byte_slice() {
// these can NOT be decoded from empty byte slice
assert!(decode(&[ParamType::Address], &[]).is_err());
assert!(decode(&[ParamType::Bytes], &[]).is_err());
assert!(decode(&[ParamType::Int(0)], &[]).is_err());
assert!(decode(&[ParamType::Int(1)], &[]).is_err());
assert!(decode(&[ParamType::Int(0)], &[]).is_err());
assert!(decode(&[ParamType::Int(1)], &[]).is_err());
assert!(decode(&[ParamType::Bool], &[]).is_err());
assert!(decode(&[ParamType::String], &[]).is_err());
assert!(decode(&[ParamType::Array(Box::new(ParamType::Bool))], &[]).is_err());
assert!(decode(&[ParamType::FixedBytes(1)], &[]).is_err());
assert!(decode(&[ParamType::FixedArray(Box::new(ParamType::Bool), 1)], &[]).is_err());
// these are the only ones that can be decoded from empty byte slice
assert!(decode(&[ParamType::FixedBytes(0)], &[]).is_ok());
assert!(decode(&[ParamType::FixedArray(Box::new(ParamType::Bool), 0)], &[]).is_ok());
}
}
//! ABI encoder.
use util::pad_u32;
use {Word, Token, Bytes};
fn pad_bytes(bytes: &[u8]) -> Vec<Word> {
let mut result = vec![pad_u32(bytes.len() as u32)];
result.extend(pad_fixed_bytes(bytes));
result
}
fn pad_fixed_bytes(bytes: &[u8]) -> Vec<Word> {
let len = (bytes.len() + 31) / 32;
let mut result = Vec::with_capacity(len);
for i in 0..len {
let mut padded = [0u8; 32];
let to_copy = match i == len - 1 {
false => 32,
true => match bytes.len() % 32 {
0 => 32,
x => x,
},
};
let offset = 32 * i;
padded[..to_copy].copy_from_slice(&bytes[offset..offset + to_copy]);
result.push(padded);
}
result
}
#[derive(Debug)]
enum Mediate {
Raw(Vec<Word>),
Prefixed(Vec<Word>),
PrefixedArray(Vec<Mediate>),
PrefixedArrayWithLength(Vec<Mediate>),
}
impl Mediate {
fn head_len(&self) -> u32 {
match *self {
Mediate::Raw(ref raw) => 32 * raw.len() as u32,
Mediate::Prefixed(_) | Mediate::PrefixedArray(_) | Mediate::PrefixedArrayWithLength(_) => 32,
}
}
fn tail_len(&self) -> u32 {
match *self {
Mediate::Raw(_) => 0,
Mediate::Prefixed(ref pre) => pre.len() as u32 * 32,
Mediate::PrefixedArray(ref mediates) => mediates.iter().fold(0, |acc, m| acc + m.head_len() + m.tail_len()),
Mediate::PrefixedArrayWithLength(ref mediates) => mediates.iter().fold(32, |acc, m| acc + m.head_len() + m.tail_len()),
}
}
fn head(&self, suffix_offset: u32) -> Vec<Word> {
match *self {
Mediate::Raw(ref raw) => raw.clone(),
Mediate::Prefixed(_) | Mediate::PrefixedArray(_) | Mediate::PrefixedArrayWithLength(_) => {
vec![pad_u32(suffix_offset)]
}
}
}
fn tail(&self) -> Vec<Word> {
match *self {
Mediate::Raw(_) => vec![],
Mediate::Prefixed(ref raw) => raw.clone(),
Mediate::PrefixedArray(ref mediates) => encode_head_tail(mediates),
Mediate::PrefixedArrayWithLength(ref mediates) => {
// + 32 added to offset represents len of the array prepanded to tail
let mut result = vec![pad_u32(mediates.len() as u32)];
let head_tail = encode_head_tail(mediates);
result.extend(head_tail);
result
},
}
}
}
fn encode_head_tail(mediates: &Vec<Mediate>) -> Vec<Word> {
let heads_len = mediates.iter()
.fold(0, |acc, m| acc + m.head_len());
let (mut result, len) = mediates.iter()
.fold(
(Vec::with_capacity(heads_len as usize), heads_len),
|(mut acc, offset), m| {
acc.extend(m.head(offset));
(acc, offset + m.tail_len())
}
);
let tails = mediates.iter()
.fold(
Vec::with_capacity((len - heads_len) as usize),
|mut acc, m| {
acc.extend(m.tail());
acc
}
);
result.extend(tails);
result
}
/// Encodes vector of tokens into ABI compliant vector of bytes.
pub fn encode(tokens: &[Token]) -> Bytes {
let mediates = &tokens.iter()
.map(encode_token)
.collect();
encode_head_tail(mediates).iter()
.flat_map(|word| word.to_vec())
.collect()
}
fn encode_token(token: &Token) -> Mediate {
match *token {
Token::Address(ref address) => {
let mut padded = [0u8; 32];
padded[12..].copy_from_slice(address.as_ref());
Mediate::Raw(vec![padded])
},
Token::Bytes(ref bytes) => Mediate::Prefixed(pad_bytes(bytes)),
Token::String(ref s) => Mediate::Prefixed(pad_bytes(s.as_bytes())),
Token::FixedBytes(ref bytes) => Mediate::Raw(pad_fixed_bytes(bytes)),
Token::Int(int) => Mediate::Raw(vec![int.into()]),
Token::Uint(uint) => Mediate::Raw(vec![uint.into()]),
Token::Bool(b) => {
let mut value = [0u8; 32];
if b {
value[31] = 1;
}
Mediate::Raw(vec![value])
},
Token::Array(ref tokens) => {
let mediates = tokens.iter()
.map(encode_token)
.collect();
Mediate::PrefixedArrayWithLength(mediates)
},
Token::FixedArray(ref tokens) => {
let mediates = tokens.iter()
.map(encode_token)
.collect();
if token.is_dynamic() {
Mediate::PrefixedArray(mediates)
} else {
Mediate::Raw(encode_head_tail(&mediates))
}
},
}
}
#![allow(unknown_lints)]
#![allow(missing_docs)]
use std::{num, string};
use {serde_json, hex};
error_chain! {
foreign_links {
SerdeJson(serde_json::Error);
ParseInt(num::ParseIntError);
Utf8(string::FromUtf8Error);
Hex(hex::FromHexError);
}
errors {
InvalidName(name: String) {
description("Invalid name"),
display("Invalid name `{}`", name),
}
InvalidData {
description("Invalid data"),
display("Invalid data"),
}
}
}
//! Contract event.
use std::collections::HashMap;
use tiny_keccak::keccak256;
use signature::long_signature;
use {
Log, Hash, RawLog, LogParam, RawTopicFilter, TopicFilter,
Topic, ParamType, EventParam, encode, decode, Token,
Result, ErrorKind
};
/// Contract event.
#[derive(Clone, Debug, PartialEq, Deserialize)]
pub struct Event {
/// Event name.
pub name: String,
/// Event input.
pub inputs: Vec<EventParam>,
/// If anonymous, event cannot be found using `from` filter.
pub anonymous: bool,
}
impl Event {
/// Returns names of all params.
fn params_names(&self) -> Vec<String> {
self.inputs.iter()
.map(|p| p.name.clone())
.collect()
}
/// Returns types of all params.
fn param_types(&self) -> Vec<ParamType> {
self.inputs.iter()
.map(|p| p.kind.clone())
.collect()
}
/// Returns all params of the event.
fn indexed_params(&self, indexed: bool) -> Vec<EventParam> {
self.inputs.iter()
.filter(|p| p.indexed == indexed)
.cloned()
.collect()
}
/// Event signature
pub fn signature(&self) -> Hash {
long_signature(&self.name, &self.param_types())
}
/// Creates topic filter
pub fn filter(&self, raw: RawTopicFilter) -> Result<TopicFilter> {
fn convert_token(token: Token, kind: &ParamType) -> Result<Hash> {
if !token.type_check(kind) {
return Err(ErrorKind::InvalidData.into());
}
let encoded = encode(&[token]);
if encoded.len() == 32 {
let mut data = [0u8; 32];
data.copy_from_slice(&encoded);
Ok(data.into())
} else {
Ok(keccak256(&encoded).into())
}
}
fn convert_topic(topic: Topic<Token>, kind: Option<&ParamType>) -> Result<Topic<Hash>> {
match topic {
Topic::Any => Ok(Topic::Any),
Topic::OneOf(tokens) => match kind {
None => Err(ErrorKind::InvalidData.into()),
Some(kind) => {
let topics = tokens.into_iter()
.map(|token| convert_token(token, kind))
.collect::<Result<Vec<_>>>()?;
Ok(Topic::OneOf(topics))
}
},
Topic::This(token) => match kind {
None => Err(ErrorKind::InvalidData.into()),
Some(kind) => Ok(Topic::This(convert_token(token, kind)?)),
}
}
}
let kinds: Vec<_> = self.indexed_params(true).into_iter().map(|param| param.kind).collect();
let result = if self.anonymous {
TopicFilter {
topic0: convert_topic(raw.topic0, kinds.get(0))?,
topic1: convert_topic(raw.topic1, kinds.get(1))?,
topic2: convert_topic(raw.topic2, kinds.get(2))?,
topic3: Topic::Any,
}
} else {
TopicFilter {
topic0: Topic::This(self.signature()),
topic1: convert_topic(raw.topic0, kinds.get(0))?,
topic2: convert_topic(raw.topic1, kinds.get(1))?,
topic3: convert_topic(raw.topic2, kinds.get(2))?,
}
};
Ok(result)
}
// Converts param types for indexed parameters to bytes32 where appropriate
// This applies to strings, arrays and bytes to follow the encoding of
// these indexed param types according to
// https://solidity.readthedocs.io/en/develop/abi-spec.html#encoding-of-indexed-event-parameters
fn convert_topic_param_type(&self, kind: &ParamType) -> ParamType {
match kind {
ParamType::String
| ParamType::Bytes
| ParamType::Array(_)
| ParamType::FixedArray(_, _) => ParamType::FixedBytes(32),
_ => kind.clone()
}
}
/// Parses `RawLog` and retrieves all log params from it.
pub fn parse_log(&self, log: RawLog) -> Result<Log> {
let topics = log.topics;
let data = log.data;
let topics_len = topics.len();
// obtains all params info
let topic_params = self.indexed_params(true);
let data_params = self.indexed_params(false);
// then take first topic if event is not anonymous
let to_skip = if self.anonymous {
0
} else {
// verify
let event_signature = topics.get(0).ok_or(ErrorKind::InvalidData)?;
if event_signature != &self.signature() {
return Err(ErrorKind::InvalidData.into());
}
1
};
let topic_types = topic_params.iter()
.map(|p| self.convert_topic_param_type(&p.kind))
.collect::<Vec<ParamType>>();
let flat_topics = topics.into_iter()
.skip(to_skip)
.flat_map(|t| t.as_ref().to_vec())
.collect::<Vec<u8>>();
let topic_tokens = try!(decode(&topic_types, &flat_topics));
// topic may be only a 32 bytes encoded token
if topic_tokens.len() != topics_len - to_skip {
return Err(ErrorKind::InvalidData.into());
}
let topics_named_tokens = topic_params.into_iter()
.map(|p| p.name)
.zip(topic_tokens.into_iter());
let data_types = data_params.iter()
.map(|p| p.kind.clone())
.collect::<Vec<ParamType>>();
let data_tokens = try!(decode(&data_types, &data));
let data_named_tokens = data_params.into_iter()
.map(|p| p.name)
.zip(data_tokens.into_iter());
let named_tokens = topics_named_tokens
.chain(data_named_tokens)
.collect::<HashMap<String, Token>>();
let decoded_params = self.params_names()
.into_iter()
.map(|name| LogParam {
name: name.clone(),
value: named_tokens[&name].clone()
})
.collect();
let result = Log {
params: decoded_params,
};
Ok(result)
}
}
#[cfg(test)]
mod tests {
use hex::FromHex;
use token::Token;
use signature::long_signature;
use log::{RawLog, Log};
use {EventParam, ParamType, Event, LogParam};
#[test]
fn test_decoding_event() {
let event = Event {
name: "foo".to_owned(),
inputs: vec![EventParam {
name: "a".to_owned(),
kind: ParamType::Int(256),
indexed: false,
}, EventParam {
name: "b".to_owned(),
kind: ParamType::Int(256),
indexed: true,
}, EventParam {
name: "c".to_owned(),
kind: ParamType::Address,
indexed: false,
}, EventParam {
name: "d".to_owned(),
kind: ParamType::Address,
indexed: true,
}, EventParam {
name: "e".to_owned(),
kind: ParamType::String,
indexed: true,
}, EventParam {
name: "f".to_owned(),
kind: ParamType::Array(Box::new(ParamType::Int(256))),
indexed: true
}, EventParam {
name: "g".to_owned(),
kind: ParamType::FixedArray(Box::new(ParamType::Address), 5),
indexed: true,
}],
anonymous: false,
};
let log = RawLog {
topics: vec![
long_signature("foo", &[
ParamType::Int(256),
ParamType::Int(256),
ParamType::Address,
ParamType::Address,
ParamType::String,
ParamType::Array(Box::new(ParamType::Int(256))),
ParamType::FixedArray(Box::new(ParamType::Address), 5),
]),
"0000000000000000000000000000000000000000000000000000000000000002".parse().unwrap(),
"0000000000000000000000001111111111111111111111111111111111111111".parse().unwrap(),
"00000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".parse().unwrap(),
"00000000000000000bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb".parse().unwrap(),
"00000000000000000ccccccccccccccccccccccccccccccccccccccccccccccc".parse().unwrap(),
],
data:
("".to_owned() +
"0000000000000000000000000000000000000000000000000000000000000003" +
"0000000000000000000000002222222222222222222222222222222222222222").from_hex().unwrap()
};
let result = event.parse_log(log).unwrap();
assert_eq!(result, Log { params: vec![
("a".to_owned(), Token::Int("0000000000000000000000000000000000000000000000000000000000000003".into())),
("b".to_owned(), Token::Int("0000000000000000000000000000000000000000000000000000000000000002".into())),
("c".to_owned(), Token::Address("2222222222222222222222222222222222222222".parse().unwrap())),
("d".to_owned(), Token::Address("1111111111111111111111111111111111111111".parse().unwrap())),
("e".to_owned(), Token::FixedBytes("00000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".from_hex().unwrap())),
("f".to_owned(), Token::FixedBytes("00000000000000000bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb".from_hex().unwrap())),
("g".to_owned(), Token::FixedBytes("00000000000000000ccccccccccccccccccccccccccccccccccccccccccccccc".from_hex().unwrap())),
].into_iter().map(|(name, value)| LogParam { name, value }).collect::<Vec<_>>()});
}
}
//! Event param specification.
use {ParamType};
/// Event param specification.
#[derive(Debug, Clone, PartialEq, Deserialize)]
pub struct EventParam {
/// Param name.
pub name: String,
/// Param type.
#[serde(rename="type")]
pub kind: ParamType,
/// Indexed flag. If true, param is used to build block bloom.
pub indexed: bool,
}
#[cfg(test)]
mod tests {
use serde_json;
use {EventParam, ParamType};
#[test]
fn event_param_deserialization() {
let s = r#"{
"name": "foo",
"type": "address",
"indexed": true
}"#;
let deserialized: EventParam = serde_json::from_str(s).unwrap();
assert_eq!(deserialized, EventParam {
name: "foo".to_owned(),
kind: ParamType::Address,
indexed: true,
});
}
}
use std::ops;
use serde::{Serialize, Serializer};
use serde_json::Value;
use {Hash, Token};
/// Raw topic filter.
#[derive(Debug, PartialEq, Default)]
pub struct RawTopicFilter {
/// Topic.
pub topic0: Topic<Token>,
/// Topic.
pub topic1: Topic<Token>,
/// Topic.
pub topic2: Topic<Token>,
}
/// Topic filter.
#[derive(Debug, PartialEq, Default)]
pub struct TopicFilter {
/// Usually (for not-anonymous transactions) the first topic is event signature.
pub topic0: Topic<Hash>,
/// Second topic.
pub topic1: Topic<Hash>,
/// Third topic.
pub topic2: Topic<Hash>,
/// Fourth topic.
pub topic3: Topic<Hash>,
}
impl Serialize for TopicFilter {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer {
vec![&self.topic0, &self.topic1, &self.topic2, &self.topic3].serialize(serializer)
}
}
/// Acceptable topic possibilities.
#[derive(Debug, PartialEq)]
pub enum Topic<T> {
/// Match any.
Any,
/// Match any of the hashes.
OneOf(Vec<T>),
/// Match only this hash.
This(T),
}
impl<T> Topic<T> {
/// Map
pub fn map<F, O>(self, f: F) -> Topic<O> where F: Fn(T) -> O {
match self {
Topic::Any => Topic::Any,
Topic::OneOf(topics) => Topic::OneOf(topics.into_iter().map(f).collect()),
Topic::This(topic) => Topic::This(f(topic)),
}
}
/// Returns true if topic is empty (Topic::Any)
pub fn is_any(&self) -> bool {
match *self {
Topic::Any => true,
Topic::This(_) | Topic::OneOf(_) => false,
}
}
}
impl<T> Default for Topic<T> {
fn default() -> Self {
Topic::Any
}
}
impl<T> From<Option<T>> for Topic<T> {
fn from(o: Option<T>) -> Self {
match o {
Some(topic) => Topic::This(topic),
None => Topic::Any,
}
}
}
impl<T> From<T> for Topic<T> {
fn from(topic: T) -> Self {
Topic::This(topic)
}
}
impl<T> From<Vec<T>> for Topic<T> {
fn from(topics: Vec<T>) -> Self {
Topic::OneOf(topics)
}
}
impl<T> Into<Vec<T>> for Topic<T> {
fn into(self: Self) -> Vec<T> {
match self {
Topic::Any => vec![],
Topic::This(topic) => vec![topic],
Topic::OneOf(topics) => topics,
}
}
}
impl Serialize for Topic<Hash> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer {
let value = match *self {
Topic::Any => Value::Null,
Topic::OneOf(ref vec) => {
let v = vec.iter()
.map(|h| format!("0x{:x}", h))
.map(Value::String)
.collect();
Value::Array(v)
},
Topic::This(ref hash) => Value::String(format!("0x{:x}", hash)),
};
value.serialize(serializer)
}
}
impl<T> ops::Index<usize> for Topic<T> {
type Output = T;
fn index(&self, index: usize) -> &Self::Output {
match *self {
Topic::Any => panic!("Topic unavailable"),
Topic::This(ref topic) => {
if index != 0 {
panic!("Topic unavailable");
}
topic
},
Topic::OneOf(ref topics) => topics.index(index),
}
}
}
#[cfg(test)]
mod tests {
use serde_json;
use super::{Topic, TopicFilter};
use Hash;
fn hash(s: &'static str) -> Hash {
s.parse().unwrap()
}
#[test]
fn test_topic_filter_serialization() {
let expected =
r#"["0x000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b",null,["0x000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b","0x0000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebccc"],null]"#;
let topic = TopicFilter {
topic0: Topic::This(hash("000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b")),
topic1: Topic::Any,
topic2: Topic::OneOf(vec![hash("000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b"), hash("0000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebccc")]),
topic3: Topic::Any,
};
let topic_str = serde_json::to_string(&topic).unwrap();
assert_eq!(expected, &topic_str);
}
#[test]
fn test_topic_from() {
assert_eq!(Topic::Any as Topic<u64>, None.into());
assert_eq!(Topic::This(10u64), 10u64.into());
assert_eq!(Topic::OneOf(vec![10u64, 20]), vec![10u64, 20].into());
}
#[test]
fn test_topic_into_vec() {
let expected: Vec<u64> = vec![];
let is: Vec<u64> = (Topic::Any as Topic<u64>).into();
assert_eq!(expected, is);
let expected: Vec<u64> = vec![10];
let is: Vec<u64> = Topic::This(10u64).into();
assert_eq!(expected, is);
let expected: Vec<u64> = vec![10, 20];
let is: Vec<u64> = Topic::OneOf(vec![10u64, 20]).into();
assert_eq!(expected, is);
}
#[test]
fn test_topic_is_any() {
assert!((Topic::Any as Topic<u8>).is_any());
assert!(!Topic::OneOf(vec![10u64, 20]).is_any());
assert!(!Topic::This(10u64).is_any());
}
#[test]
fn test_topic_index() {
assert_eq!(Topic::OneOf(vec![10u64, 20])[0], 10);
assert_eq!(Topic::OneOf(vec![10u64, 20])[1], 20);
assert_eq!(Topic::This(10u64)[0], 10);
}
#[test]
#[should_panic(expected = "Topic unavailable")]
fn test_topic_index_panic() {
let _ = (Topic::Any as Topic<u8>)[0];
}
#[test]
#[should_panic(expected = "Topic unavailable")]
fn test_topic_index_panic2() {
assert_eq!(Topic::This(10u64)[1], 10);
}
}
//! Contract function call builder.
use signature::short_signature;
use {Param, Token, Result, ErrorKind, Bytes, decode, ParamType, encode};
/// Contract function specification.
#[derive(Debug, Clone, PartialEq, Deserialize)]
pub struct Function {
/// Function name.
pub name: String,
/// Function input.
pub inputs: Vec<Param>,
/// Function output.
pub outputs: Vec<Param>,
/// Constant function.
#[serde(default)]
pub constant: bool,
}
impl Function {
/// Returns all input params of given function.
fn input_param_types(&self) -> Vec<ParamType> {
self.inputs.iter()
.map(|p| p.kind.clone())
.collect()
}
/// Returns all output params of given function.
fn output_param_types(&self) -> Vec<ParamType> {
self.outputs.iter()
.map(|p| p.kind.clone())
.collect()
}
/// Prepares ABI function call with given input params.
pub fn encode_input(&self, tokens: &[Token]) -> Result<Bytes> {
let params = self.input_param_types();
if !Token::types_check(tokens, &params) {
return Err(ErrorKind::InvalidData.into());
}
let signed = short_signature(&self.name, &params).to_vec();
let encoded = encode(tokens);
Ok(signed.into_iter().chain(encoded.into_iter()).collect())
}
/// Parses the ABI function output to list of tokens.
pub fn decode_output(&self, data: &[u8]) -> Result<Vec<Token>> {
decode(&self.output_param_types(), &data)
}
/// add by echo : 编写 wasm 合约时用来序列化返回值
pub fn encode_output(&self, tokens: &[Token]) -> Result<Bytes> {
let params = self.output_param_types();
if !Token::types_check(tokens, &params) {
return Err(ErrorKind::InvalidData.into());
}
let encoded = encode(tokens);
Ok(encoded.into_iter().collect())
}
/// add by echo : 编写 wasm 合约时用来反序列化输入参数
pub fn decode_input(&self, data: &[u8]) -> Result<Vec<Token>> {
let d = &data[4..];
decode(&self.input_param_types(), &d)
}
}
#[cfg(test)]
mod tests {
use {Token, Param, Function, ParamType};
#[test]
fn test_function_encode_call() {
let interface = Function {
name: "baz".to_owned(),
inputs: vec![Param {
name: "a".to_owned(),
kind: ParamType::Uint(32),
}, Param {
name: "b".to_owned(),
kind: ParamType::Bool,
}],
outputs: vec![],
constant: false,
};
let func = Function::from(interface);
let mut uint = [0u8; 32];
uint[31] = 69;
let encoded = func.encode_input(&[Token::Uint(uint.into()), Token::Bool(true)]).unwrap();
let expected = hex!("cdcd77c000000000000000000000000000000000000000000000000000000000000000450000000000000000000000000000000000000000000000000000000000000001").to_vec();
assert_eq!(encoded, expected);
}
}
//! Ethereum ABI encoding decoding library.
#![warn(missing_docs)]
pub extern crate rustc_hex as hex;
extern crate serde;
extern crate serde_json;
extern crate tiny_keccak;
#[macro_use]
pub extern crate serde_derive;
#[macro_use]
pub extern crate error_chain;
#[cfg(test)]
#[macro_use]
extern crate hex_literal;
#[cfg(test)]
extern crate paste;
extern crate ethereum_types;
// add by echo >>>>>>>>
//pub use error_chain;
//pub use hex;
// add by echo <<<<<<<<
pub mod param_type;
pub mod token;
mod constructor;
mod contract;
mod decoder;
mod encoder;
mod errors;
mod event;
mod event_param;
mod filter;
mod function;
mod log;
mod operation;
mod param;
pub mod signature;
mod util;
#[cfg(test)]
mod tests;
pub use param_type::ParamType;
pub use constructor::Constructor;
pub use contract::{Contract, Functions, Events};
pub use token::Token;
pub use errors::{Error, ErrorKind, Result, ResultExt};
pub use encoder::encode;
pub use decoder::decode;
pub use filter::{Topic, TopicFilter, RawTopicFilter};
pub use function::Function;
pub use param::Param;
pub use log::{Log, RawLog, LogParam, ParseLog, LogFilter};
pub use event::Event;
pub use event_param::EventParam;
/// ABI word.
pub type Word = [u8; 32];
/// ABI address.
pub type Address = ethereum_types::Address;
/// ABI fixed bytes.
pub type FixedBytes = Vec<u8>;
/// ABI bytes.
pub type Bytes = Vec<u8>;
/// ABI signed integer.
pub type Int = ethereum_types::U256;
/// ABI unsigned integer.
pub type Uint = ethereum_types::U256;
/// Commonly used FixedBytes of size 32
pub type Hash = ethereum_types::H256;
/// Contract functions generated by ethabi-derive
pub trait FunctionOutputDecoder {
/// Output types of the contract function
type Output;
/// Decodes the given bytes output for the contract function
fn decode(&self, &[u8]) -> Result<Self::Output>;
}
use {Hash, Token, Bytes, Result, TopicFilter};
/// Common filtering functions that are available for any event.
pub trait LogFilter {
/// Match any log parameters.
fn wildcard_filter(&self) -> TopicFilter;
}
/// trait common to things (events) that have an associated `Log` type
/// that can be parsed from a `RawLog`
pub trait ParseLog {
/// the associated `Log` type that can be parsed from a `RawLog`
/// by calling `parse_log`
type Log;
/// parse the associated `Log` type from a `RawLog`
fn parse_log(&self, log: RawLog) -> Result<Self::Log>;
}
/// Ethereum log.
#[derive(Debug, PartialEq, Clone)]
pub struct RawLog {
/// Indexed event params are represented as log topics.
pub topics: Vec<Hash>,
/// Others are just plain data.
pub data: Bytes,
}
impl From<(Vec<Hash>, Bytes)> for RawLog {
fn from(raw: (Vec<Hash>, Bytes)) -> Self {
RawLog {
topics: raw.0,
data: raw.1,
}
}
}
/// Decoded log param.
#[derive(Debug, PartialEq, Clone)]
pub struct LogParam {
/// Decoded log name.
pub name: String,
/// Decoded log value.
pub value: Token,
}
/// Decoded log.
#[derive(Debug, PartialEq, Clone)]
pub struct Log {
/// Log params.
pub params: Vec<LogParam>,
}
//! Operation type.
use serde::{Deserialize, Deserializer};
use serde::de::{Error as SerdeError};
use serde_json::Value;
use serde_json::value::from_value;
use {Function, Constructor, Event};
/// Operation type.
#[derive(Clone, Debug, PartialEq)]
pub enum Operation {
/// Contract constructor.
Constructor(Constructor),
/// Contract function.
Function(Function),
/// Contract event.
Event(Event),
/// Fallback, ignored.
Fallback,
}
impl<'a> Deserialize<'a> for Operation {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'a> {
let v: Value = try!(Deserialize::deserialize(deserializer));
let cloned = v.clone();
let map = try!(cloned.as_object().ok_or_else(|| SerdeError::custom("Invalid operation")));
let s = try!(map.get("type").and_then(Value::as_str).ok_or_else(|| SerdeError::custom("Invalid operation type")));
// This is a workaround to support non-spec compliant function and event names,
// see: https://github.com/paritytech/parity/issues/4122
fn sanitize_name(name: &mut String) {
if let Some(i) = name.find('(') {
name.truncate(i);
}
}
let result = match s {
"constructor" => from_value(v).map(Operation::Constructor),
"function" => from_value(v).map(|mut f: Function| {
sanitize_name(&mut f.name);
Operation::Function(f)
}),
"event" => from_value(v).map(|mut e: Event| {
sanitize_name(&mut e.name);
Operation::Event(e)
}),
"fallback" => Ok(Operation::Fallback),
_ => Err(SerdeError::custom("Invalid operation type.")),
};
result.map_err(|e| D::Error::custom(e.to_string()))
}
}
#[cfg(test)]
mod tests {
use serde_json;
use super::Operation;
use {Function, Param, ParamType};
#[test]
fn deserialize_operation() {
let s = r#"{
"type":"function",
"inputs": [{
"name":"a",
"type":"address"
}],
"name":"foo",
"outputs": []
}"#;
let deserialized: Operation = serde_json::from_str(s).unwrap();
assert_eq!(deserialized, Operation::Function(Function {
name: "foo".to_owned(),
inputs: vec![
Param {
name: "a".to_owned(),
kind: ParamType::Address,
}
],
outputs: vec![],
constant: false,
}));
}
#[test]
fn deserialize_sanitize_function_name() {
fn test_sanitize_function_name(name: &str, expected: &str) {
let s = format!(r#"{{
"type":"function",
"inputs": [{{
"name":"a",
"type":"address"
}}],
"name":"{}",
"outputs": []
}}"#, name);
let deserialized: Operation = serde_json::from_str(&s).unwrap();
let function = match deserialized {
Operation::Function(f) => f,
_ => panic!("expected funciton"),
};
assert_eq!(function.name, expected);
}
test_sanitize_function_name("foo", "foo");
test_sanitize_function_name("foo()", "foo");
test_sanitize_function_name("()", "");
test_sanitize_function_name("", "");
}
#[test]
fn deserialize_sanitize_event_name() {
fn test_sanitize_event_name(name: &str, expected: &str) {
let s = format!(r#"{{
"type":"event",
"inputs": [{{
"name":"a",
"type":"address",
"indexed":true
}}],
"name":"{}",
"outputs": [],
"anonymous": false
}}"#, name);
let deserialized: Operation = serde_json::from_str(&s).unwrap();
let event = match deserialized {
Operation::Event(e) => e,
_ => panic!("expected event!"),
};
assert_eq!(event.name, expected);
}
test_sanitize_event_name("foo", "foo");
test_sanitize_event_name("foo()", "foo");
test_sanitize_event_name("()", "");
test_sanitize_event_name("", "");
}
}
//! Function param.
use ParamType;
/// Function param.
#[derive(Debug, Clone, PartialEq, Deserialize)]
pub struct Param {
/// Param name.
pub name: String,
/// Param type.
#[serde(rename="type")]
pub kind: ParamType,
}
#[cfg(test)]
mod tests {
use serde_json;
use {Param, ParamType};
#[test]
fn param_deserialization() {
let s = r#"{
"name": "foo",
"type": "address"
}"#;
let deserialized: Param = serde_json::from_str(s).unwrap();
assert_eq!(deserialized, Param {
name: "foo".to_owned(),
kind: ParamType::Address,
});
}
}
use std::fmt;
use serde::{Deserialize, Deserializer};
use serde::de::{Error as SerdeError, Visitor};
use super::{ParamType, Reader};
impl<'a> Deserialize<'a> for ParamType {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'a> {
deserializer.deserialize_identifier(ParamTypeVisitor)
}
}
struct ParamTypeVisitor;
impl<'a> Visitor<'a> for ParamTypeVisitor {
type Value = ParamType;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "a correct name of abi-encodable parameter type")
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> where E: SerdeError {
Reader::read(value).map_err(|e| SerdeError::custom(format!("{:?}", e).as_str()))
}
fn visit_string<E>(self, value: String) -> Result<Self::Value, E> where E: SerdeError {
self.visit_str(value.as_str())
}
}
#[cfg(test)]
mod tests {
use serde_json;
use ParamType;
#[test]
fn param_type_deserialization() {
let s = r#"["address", "bytes", "bytes32", "bool", "string", "int", "uint", "address[]", "uint[3]", "bool[][5]"]"#;
let deserialized: Vec<ParamType> = serde_json::from_str(s).unwrap();
assert_eq!(deserialized, vec![
ParamType::Address,
ParamType::Bytes,
ParamType::FixedBytes(32),
ParamType::Bool,
ParamType::String,
ParamType::Int(256),
ParamType::Uint(256),
ParamType::Array(Box::new(ParamType::Address)),
ParamType::FixedArray(Box::new(ParamType::Uint(256)), 3),
ParamType::FixedArray(Box::new(ParamType::Array(Box::new(ParamType::Bool))), 5)
]);
}
}
//! Function and event param types.
mod deserialize;
mod param_type;
mod reader;
mod writer;
pub use self::param_type::ParamType;
pub use self::writer::Writer;
pub use self::reader::Reader;
//! Function and event param types.
use std::fmt;
use super::Writer;
/// Function and event param types.
#[derive(Debug, Clone, PartialEq)]
pub enum ParamType {
/// Address.
Address,
/// Bytes.
Bytes,
/// Signed integer.
Int(usize),
/// Unsigned integer.
Uint(usize),
/// Boolean.
Bool,
/// String.
String,
/// Array of unknown size.
Array(Box<ParamType>),
/// Vector of bytes with fixed size.
FixedBytes(usize),
/// Array with fixed size.
FixedArray(Box<ParamType>, usize),
}
impl fmt::Display for ParamType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", Writer::write(self))
}
}
impl ParamType {
/// returns whether a zero length byte slice (`0x`) is
/// a valid encoded form of this param type
pub fn is_empty_bytes_valid_encoding(&self) -> bool {
match self {
ParamType::FixedBytes(len) => *len == 0,
ParamType::FixedArray(_, len) => *len == 0,
_ => false,
}
}
/// returns whether this param type is dynamic
pub fn is_dynamic(&self) -> bool {
match self {
ParamType::Bytes | ParamType::String | ParamType::Array(_) => true,
ParamType::FixedArray(elem_type, _) => elem_type.is_dynamic(),
_ => false
}
}
}
#[cfg(test)]
mod tests {
use ParamType;
#[test]
fn test_param_type_display() {
assert_eq!(format!("{}", ParamType::Address), "address".to_owned());
assert_eq!(format!("{}", ParamType::Bytes), "bytes".to_owned());
assert_eq!(format!("{}", ParamType::FixedBytes(32)), "bytes32".to_owned());
assert_eq!(format!("{}", ParamType::Uint(256)), "uint256".to_owned());
assert_eq!(format!("{}", ParamType::Int(64)), "int64".to_owned());
assert_eq!(format!("{}", ParamType::Bool), "bool".to_owned());
assert_eq!(format!("{}", ParamType::String), "string".to_owned());
assert_eq!(format!("{}", ParamType::Array(Box::new(ParamType::Bool))), "bool[]".to_owned());
assert_eq!(format!("{}", ParamType::FixedArray(Box::new(ParamType::Uint(256)), 2)), "uint256[2]".to_owned());
assert_eq!(format!("{}", ParamType::FixedArray(Box::new(ParamType::String), 2)), "string[2]".to_owned());
assert_eq!(format!("{}", ParamType::FixedArray(Box::new(ParamType::Array(Box::new(ParamType::Bool))), 2)), "bool[][2]".to_owned());
}
#[test]
fn test_is_dynamic() {
assert_eq!(ParamType::Address.is_dynamic(), false);
assert_eq!(ParamType::Bytes.is_dynamic(), true);
assert_eq!(ParamType::FixedBytes(32).is_dynamic(), false);
assert_eq!(ParamType::Uint(256).is_dynamic(), false);
assert_eq!(ParamType::Int(64).is_dynamic(), false);
assert_eq!(ParamType::Bool.is_dynamic(), false);
assert_eq!(ParamType::String.is_dynamic(), true);
assert_eq!(ParamType::Array(Box::new(ParamType::Bool)).is_dynamic(), true);
assert_eq!(ParamType::FixedArray(Box::new(ParamType::Uint(256)), 2).is_dynamic(), false);
assert_eq!(ParamType::FixedArray(Box::new(ParamType::String), 2).is_dynamic(), true);
assert_eq!(ParamType::FixedArray(Box::new(ParamType::Array(Box::new(ParamType::Bool))), 2).is_dynamic(), true);
}
}
use {ParamType, Error, ErrorKind};
/// Used to convert param type represented as a string to rust structure.
pub struct Reader;
impl Reader {
/// Converts string to param type.
pub fn read(name: &str) -> Result<ParamType, Error> {
// check if it is a fixed or dynamic array.
if let Some(']') = name.chars().last() {
// take number part
let num: String = name.chars()
.rev()
.skip(1)
.take_while(|c| *c != '[')
.collect::<String>()
.chars()
.rev()
.collect();
let count = name.chars().count();
if num.is_empty() {
// we already know it's a dynamic array!
let subtype = try!(Reader::read(&name[..count - 2]));
return Ok(ParamType::Array(Box::new(subtype)));
} else {
// it's a fixed array.
let len = try!(usize::from_str_radix(&num, 10));
let subtype = try!(Reader::read(&name[..count - num.len() - 2]));
return Ok(ParamType::FixedArray(Box::new(subtype), len));
}
}
let result = match name {
"address" => ParamType::Address,
"bytes" => ParamType::Bytes,
"bool" => ParamType::Bool,
"string" => ParamType::String,
"int" => ParamType::Int(256),
"uint" => ParamType::Uint(256),
s if s.starts_with("int") => {
let len = try!(usize::from_str_radix(&s[3..], 10));
ParamType::Int(len)
},
s if s.starts_with("uint") => {
let len = try!(usize::from_str_radix(&s[4..], 10));
ParamType::Uint(len)
},
s if s.starts_with("bytes") => {
let len = try!(usize::from_str_radix(&s[5..], 10));
ParamType::FixedBytes(len)
},
_ => {
return Err(ErrorKind::InvalidName(name.to_owned()).into());
}
};
Ok(result)
}
}
#[cfg(test)]
mod tests {
use ParamType;
use super::Reader;
#[test]
fn test_read_param() {
assert_eq!(Reader::read("address").unwrap(), ParamType::Address);
assert_eq!(Reader::read("bytes").unwrap(), ParamType::Bytes);
assert_eq!(Reader::read("bytes32").unwrap(), ParamType::FixedBytes(32));
assert_eq!(Reader::read("bool").unwrap(), ParamType::Bool);
assert_eq!(Reader::read("string").unwrap(), ParamType::String);
assert_eq!(Reader::read("int").unwrap(), ParamType::Int(256));
assert_eq!(Reader::read("uint").unwrap(), ParamType::Uint(256));
assert_eq!(Reader::read("int32").unwrap(), ParamType::Int(32));
assert_eq!(Reader::read("uint32").unwrap(), ParamType::Uint(32));
}
#[test]
fn test_read_array_param() {
assert_eq!(Reader::read("address[]").unwrap(), ParamType::Array(Box::new(ParamType::Address)));
assert_eq!(Reader::read("uint[]").unwrap(), ParamType::Array(Box::new(ParamType::Uint(256))));
assert_eq!(Reader::read("bytes[]").unwrap(), ParamType::Array(Box::new(ParamType::Bytes)));
assert_eq!(Reader::read("bool[][]").unwrap(), ParamType::Array(Box::new(ParamType::Array(Box::new(ParamType::Bool)))));
}
#[test]
fn test_read_fixed_array_param() {
assert_eq!(Reader::read("address[2]").unwrap(), ParamType::FixedArray(Box::new(ParamType::Address), 2));
assert_eq!(Reader::read("bool[17]").unwrap(), ParamType::FixedArray(Box::new(ParamType::Bool), 17));
assert_eq!(Reader::read("bytes[45][3]").unwrap(), ParamType::FixedArray(Box::new(ParamType::FixedArray(Box::new(ParamType::Bytes), 45)), 3));
}
#[test]
fn test_read_mixed_arrays() {
assert_eq!(Reader::read("bool[][3]").unwrap(), ParamType::FixedArray(Box::new(ParamType::Array(Box::new(ParamType::Bool))), 3));
assert_eq!(Reader::read("bool[3][]").unwrap(), ParamType::Array(Box::new(ParamType::FixedArray(Box::new(ParamType::Bool), 3))));
}
}
use ParamType;
/// Output formatter for param type.
pub struct Writer;
impl Writer {
/// Returns string which is a formatted represenation of param.
pub fn write(param: &ParamType) -> String {
match *param {
ParamType::Address => "address".to_owned(),
ParamType::Bytes => "bytes".to_owned(),
ParamType::FixedBytes(len) => format!("bytes{}", len),
ParamType::Int(len) => format!("int{}", len),
ParamType::Uint(len) => format!("uint{}", len),
ParamType::Bool => "bool".to_owned(),
ParamType::String => "string".to_owned(),
ParamType::FixedArray(ref param, len) => format!("{}[{}]", Writer::write(param), len),
ParamType::Array(ref param) => format!("{}[]", Writer::write(param)),
}
}
}
#[cfg(test)]
mod tests {
use ParamType;
use super::Writer;
#[test]
fn test_write_param() {
assert_eq!(Writer::write(&ParamType::Address), "address".to_owned());
assert_eq!(Writer::write(&ParamType::Bytes), "bytes".to_owned());
assert_eq!(Writer::write(&ParamType::FixedBytes(32)), "bytes32".to_owned());
assert_eq!(Writer::write(&ParamType::Uint(256)), "uint256".to_owned());
assert_eq!(Writer::write(&ParamType::Int(64)), "int64".to_owned());
assert_eq!(Writer::write(&ParamType::Bool), "bool".to_owned());
assert_eq!(Writer::write(&ParamType::String), "string".to_owned());
assert_eq!(Writer::write(&ParamType::Array(Box::new(ParamType::Bool))), "bool[]".to_owned());
assert_eq!(Writer::write(&ParamType::FixedArray(Box::new(ParamType::String), 2)), "string[2]".to_owned());
assert_eq!(Writer::write(&ParamType::FixedArray(Box::new(ParamType::Array(Box::new(ParamType::Bool))), 2)), "bool[][2]".to_owned());
}
}
use tiny_keccak::Keccak;
use param_type::{Writer, ParamType};
use Hash;
pub fn short_signature(name: &str, params: &[ParamType]) -> [u8; 4] {
let mut result = [0u8; 4];
fill_signature(name, params, &mut result);
result
}
pub fn long_signature(name: &str, params: &[ParamType]) -> Hash {
let mut result = [0u8; 32];
fill_signature(name, params, &mut result);
result.into()
}
fn fill_signature(name: &str, params: &[ParamType], result: &mut [u8]) {
let types = params.iter()
.map(Writer::write)
.collect::<Vec<String>>()
.join(",");
let data: Vec<u8> = From::from(format!("{}({})", name, types).as_str());
let mut sponge = Keccak::new_keccak256();
sponge.update(&data);
sponge.finalize(result);
}
#[cfg(test)]
mod tests {
use super::short_signature;
use {ParamType};
#[test]
fn test_signature() {
assert_eq!(hex!("cdcd77c0"), short_signature("baz", &[ParamType::Uint(32), ParamType::Bool]));
}
}
This diff is collapsed.
use token::{Tokenizer, StrictTokenizer};
use util::{pad_u32, pad_i32};
use errors::Error;
/// Tries to parse string as a token. Does not require string to clearly represent the value.
pub struct LenientTokenizer;
impl Tokenizer for LenientTokenizer {
fn tokenize_address(value: &str) -> Result<[u8; 20], Error> {
StrictTokenizer::tokenize_address(value)
}
fn tokenize_string(value: &str) -> Result<String, Error> {
StrictTokenizer::tokenize_string(value)
}
fn tokenize_bool(value: &str) -> Result<bool, Error> {
StrictTokenizer::tokenize_bool(value)
}
fn tokenize_bytes(value: &str) -> Result<Vec<u8>, Error> {
StrictTokenizer::tokenize_bytes(value)
}
fn tokenize_fixed_bytes(value: &str, len: usize) -> Result<Vec<u8>, Error> {
StrictTokenizer::tokenize_fixed_bytes(value, len)
}
fn tokenize_uint(value: &str) -> Result<[u8; 32], Error> {
let result = StrictTokenizer::tokenize_uint(value);
if result.is_ok() {
return result;
}
let uint = try!(u32::from_str_radix(value, 10));
Ok(pad_u32(uint))
}
fn tokenize_int(value: &str) -> Result<[u8; 32], Error> {
let result = StrictTokenizer::tokenize_int(value);
if result.is_ok() {
return result;
}
let int = try!(i32::from_str_radix(value, 10));
Ok(pad_i32(int))
}
}
//! ABI param and parsing for it.
mod lenient;
mod strict;
mod token;
use {ParamType, Error, ErrorKind, ResultExt};
pub use self::lenient::LenientTokenizer;
pub use self::strict::StrictTokenizer;
pub use self::token::Token;
/// This trait should be used to parse string values as tokens.
pub trait Tokenizer {
/// Tries to parse a string as a token of given type.
fn tokenize(param: &ParamType, value: &str) -> Result<Token, Error> {
match *param {
ParamType::Address => Self::tokenize_address(value).map(|a| Token::Address(a.into())),
ParamType::String => Self::tokenize_string(value).map(Token::String),
ParamType::Bool => Self::tokenize_bool(value).map(Token::Bool),
ParamType::Bytes => Self::tokenize_bytes(value).map(Token::Bytes),
ParamType::FixedBytes(len) => Self::tokenize_fixed_bytes(value, len).map(Token::FixedBytes),
ParamType::Uint(_) => Self::tokenize_uint(value).map(Into::into).map(Token::Uint),
ParamType::Int(_) => Self::tokenize_int(value).map(Into::into).map(Token::Int),
ParamType::Array(ref p) => Self::tokenize_array(value, p).map(Token::Array),
ParamType::FixedArray(ref p, len) => Self::tokenize_fixed_array(value, p, len).map(Token::FixedArray),
}.chain_err(|| format!("Cannot parse {}", param))
}
/// Tries to parse a value as a vector of tokens of fixed size.
fn tokenize_fixed_array(value: &str, param: &ParamType, len: usize) -> Result<Vec<Token>, Error> {
let result = try!(Self::tokenize_array(value, param));
match result.len() == len {
true => Ok(result),
false => Err(ErrorKind::InvalidData.into()),
}
}
/// Tries to parse a value as a vector of tokens.
fn tokenize_array(value: &str, param: &ParamType) -> Result<Vec<Token>, Error> {
if !value.starts_with('[') || !value.ends_with(']') {
return Err(ErrorKind::InvalidData.into());
}
if value.chars().count() == 2 {
return Ok(vec![]);
}
let mut result = vec![];
let mut nested = 0isize;
let mut ignore = false;
let mut last_item = 1;
for (i, ch) in value.chars().enumerate() {
match ch {
'[' if ignore == false => {
nested += 1;
},
']' if ignore == false => {
nested -= 1;
if nested < 0 {
return Err(ErrorKind::InvalidData.into());
} else if nested == 0 {
let sub = &value[last_item..i];
let token = try!(Self::tokenize(param, sub));
result.push(token);
last_item = i + 1;
}
},
'"' => {
ignore = !ignore;
},
',' if nested == 1 && ignore == false => {
let sub = &value[last_item..i];
let token = try!(Self::tokenize(param, sub));
result.push(token);
last_item = i + 1;
},
_ => ()
}
}
if ignore {
return Err(ErrorKind::InvalidData.into());
}
Ok(result)
}
/// Tries to parse a value as an address.
fn tokenize_address(value: &str) -> Result<[u8; 20], Error>;
/// Tries to parse a value as a string.
fn tokenize_string(value: &str) -> Result<String, Error>;
/// Tries to parse a value as a bool.
fn tokenize_bool(value: &str) -> Result<bool, Error>;
/// Tries to parse a value as bytes.
fn tokenize_bytes(value: &str) -> Result<Vec<u8>, Error>;
/// Tries to parse a value as bytes.
fn tokenize_fixed_bytes(value: &str, len: usize) -> Result<Vec<u8>, Error>;
/// Tries to parse a value as unsigned integer.
fn tokenize_uint(value: &str) -> Result<[u8; 32], Error>;
/// Tries to parse a value as signed integer.
fn tokenize_int(value: &str) -> Result<[u8; 32], Error>;
}
#[cfg(test)]
mod test {
use super::{LenientTokenizer, Tokenizer, ParamType};
#[test]
fn single_quoted_in_array_must_error() {
assert!(LenientTokenizer::tokenize_array("[1,\"0,false]", &ParamType::Bool).is_err());
assert!(LenientTokenizer::tokenize_array("[false\"]", &ParamType::Bool).is_err());
assert!(LenientTokenizer::tokenize_array("[1,false\"]", &ParamType::Bool).is_err());
assert!(LenientTokenizer::tokenize_array("[1,\"0\",false]", &ParamType::Bool).is_err());
assert!(LenientTokenizer::tokenize_array("[1,0]", &ParamType::Bool).is_ok());
}
}
use hex::FromHex;
use token::Tokenizer;
use errors::{Error, ErrorKind};
/// Tries to parse string as a token. Require string to clearly represent the value.
pub struct StrictTokenizer;
impl Tokenizer for StrictTokenizer {
fn tokenize_address(value: &str) -> Result<[u8; 20], Error> {
let hex : Vec<u8> = try!(value.from_hex());
match hex.len() == 20 {
false => Err(ErrorKind::InvalidData.into()),
true => {
let mut address = [0u8; 20];
address.copy_from_slice(&hex);
Ok(address)
}
}
}
fn tokenize_string(value: &str) -> Result<String, Error> {
Ok(value.to_owned())
}
fn tokenize_bool(value: &str) -> Result<bool, Error> {
match value {
"true" | "1" => Ok(true),
"false" | "0" => Ok(false),
_ => Err(ErrorKind::InvalidData.into()),
}
}
fn tokenize_bytes(value: &str) -> Result<Vec<u8>, Error> {
let hex = try!(value.from_hex());
Ok(hex)
}
fn tokenize_fixed_bytes(value: &str, len: usize) -> Result<Vec<u8>, Error> {
let hex : Vec<u8> = try!(value.from_hex());
match hex.len() == len {
true => Ok(hex),
false => Err(ErrorKind::InvalidData.into()),
}
}
fn tokenize_uint(value: &str) -> Result<[u8; 32], Error> {
let hex : Vec<u8> = try!(value.from_hex());
match hex.len() == 32 {
true => {
let mut uint = [0u8; 32];
uint.copy_from_slice(&hex);
Ok(uint)
},
false => Err(ErrorKind::InvalidData.into())
}
}
fn tokenize_int(value: &str) -> Result<[u8; 32], Error> {
let hex : Vec<u8> = try!(value.from_hex());
match hex.len() == 32 {
true => {
let mut int = [0u8; 32];
int.copy_from_slice(&hex);
Ok(int)
},
false => Err(ErrorKind::InvalidData.into())
}
}
}
#[cfg(test)]
mod tests {
use ParamType;
use token::{Token, Tokenizer, StrictTokenizer};
#[test]
fn tokenize_address() {
assert_eq!(StrictTokenizer::tokenize(&ParamType::Address, "1111111111111111111111111111111111111111").unwrap(), Token::Address([0x11u8; 20].into()));
assert_eq!(StrictTokenizer::tokenize(&ParamType::Address, "2222222222222222222222222222222222222222").unwrap(), Token::Address([0x22u8; 20].into()));
}
#[test]
fn tokenize_string() {
assert_eq!(StrictTokenizer::tokenize(&ParamType::String, "gavofyork").unwrap(), Token::String("gavofyork".to_owned()));
assert_eq!(StrictTokenizer::tokenize(&ParamType::String, "hello").unwrap(), Token::String("hello".to_owned()));
}
#[test]
fn tokenize_bool() {
assert_eq!(StrictTokenizer::tokenize(&ParamType::Bool, "true").unwrap(), Token::Bool(true));
assert_eq!(StrictTokenizer::tokenize(&ParamType::Bool, "1").unwrap(), Token::Bool(true));
assert_eq!(StrictTokenizer::tokenize(&ParamType::Bool, "false").unwrap(), Token::Bool(false));
assert_eq!(StrictTokenizer::tokenize(&ParamType::Bool, "0").unwrap(), Token::Bool(false));
}
#[test]
fn tokenize_bytes() {
assert_eq!(StrictTokenizer::tokenize(&ParamType::Bytes, "123456").unwrap(), Token::Bytes(vec![0x12, 0x34, 0x56]));
assert_eq!(StrictTokenizer::tokenize(&ParamType::Bytes, "0017").unwrap(), Token::Bytes(vec![0x00, 0x17]));
}
#[test]
fn tokenize_fixed_bytes() {
assert_eq!(StrictTokenizer::tokenize(&ParamType::FixedBytes(3), "123456").unwrap(), Token::FixedBytes(vec![0x12, 0x34, 0x56]));
assert_eq!(StrictTokenizer::tokenize(&ParamType::FixedBytes(2), "0017").unwrap(), Token::FixedBytes(vec![0x00, 0x17]));
}
#[test]
fn tokenize_uint() {
assert_eq!(
StrictTokenizer::tokenize(&ParamType::Uint(256), "1111111111111111111111111111111111111111111111111111111111111111").unwrap(),
Token::Uint([0x11u8; 32].into())
);
assert_eq!(
StrictTokenizer::tokenize(&ParamType::Uint(256), "2222222222222222222222222222222222222222222222222222222222222222").unwrap(),
Token::Uint([0x22u8; 32].into())
);
}
#[test]
fn tokenize_int() {
assert_eq!(
StrictTokenizer::tokenize(&ParamType::Int(256), "1111111111111111111111111111111111111111111111111111111111111111").unwrap(),
Token::Int([0x11u8; 32].into())
);
assert_eq!(
StrictTokenizer::tokenize(&ParamType::Int(256), "2222222222222222222222222222222222222222222222222222222222222222").unwrap(),
Token::Int([0x22u8; 32].into())
);
}
#[test]
fn tokenize_empty_array() {
assert_eq!(
StrictTokenizer::tokenize(&ParamType::Array(Box::new(ParamType::Bool)), "[]").unwrap(),
Token::Array(vec![])
);
}
#[test]
fn tokenize_bool_array() {
assert_eq!(
StrictTokenizer::tokenize(&ParamType::Array(Box::new(ParamType::Bool)), "[true,1,0,false]").unwrap(),
Token::Array(vec![Token::Bool(true), Token::Bool(true), Token::Bool(false), Token::Bool(false)])
);
}
#[test]
fn tokenize_bool_array_of_arrays() {
assert_eq!(
StrictTokenizer::tokenize(&ParamType::Array(Box::new(ParamType::Array(Box::new(ParamType::Bool)))), "[[true,1,0],[false]]").unwrap(),
Token::Array(vec![
Token::Array(vec![Token::Bool(true), Token::Bool(true), Token::Bool(false)]),
Token::Array(vec![Token::Bool(false)])
])
);
}
}
//! Ethereum ABI params.
use std::fmt;
use hex::ToHex;
use {ParamType, Address, FixedBytes, Bytes, Uint};
/// Ethereum ABI params.
#[derive(Debug, PartialEq, Clone)]
pub enum Token {
/// Address.
///
/// solidity name: address
/// Encoded to left padded [0u8; 32].
Address(Address),
/// Vector of bytes with known size.
///
/// solidity name eg.: bytes8, bytes32, bytes64, bytes1024
/// Encoded to right padded [0u8; ((N + 31) / 32) * 32].
FixedBytes(FixedBytes),
/// Vector of bytes of unknown size.
///
/// solidity name: bytes
/// Encoded in two parts.
/// Init part: offset of 'closing part`.
/// Closing part: encoded length followed by encoded right padded bytes.
Bytes(Bytes),
/// Signed integer.
///
/// solidity name: int
Int(Uint),
/// Unisnged integer.
///
/// solidity name: uint
Uint(Uint),
/// Boolean value.
///
/// solidity name: bool
/// Encoded as left padded [0u8; 32], where last bit represents boolean value.
Bool(bool),
/// String.
///
/// solidity name: string
/// Encoded in the same way as bytes. Must be utf8 compliant.
String(String),
/// Array with known size.
///
/// solidity name eg.: int[3], bool[3], address[][8]
/// Encoding of array is equal to encoding of consecutive elements of array.
FixedArray(Vec<Token>),
/// Array of params with unknown size.
///
/// solidity name eg. int[], bool[], address[5][]
Array(Vec<Token>),
}
impl fmt::Display for Token {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Token::Bool(b) => write!(f, "{}", b),
Token::String(ref s) => write!(f, "{}", s),
Token::Address(ref a) => write!(f, "{:x}", a),
Token::Bytes(ref bytes) | Token::FixedBytes(ref bytes) => write!(f, "{}", bytes.to_hex::<String>()),
Token::Uint(ref i) | Token::Int(ref i) => write!(f, "{:x}", i),
Token::Array(ref arr) | Token::FixedArray(ref arr) => {
let s = arr.iter()
.map(|ref t| format!("{}", t))
.collect::<Vec<String>>()
.join(",");
write!(f, "[{}]", s)
}
}
}
}
impl Token {
/// Returns whether this token is dynamic
pub fn is_dynamic(&self) -> bool {
match self {
Token::Bytes(_) | Token::String(_) | Token::Array(_) => true,
Token::FixedArray(ref tokens) => tokens.iter().any(|ref token| token.is_dynamic()),
_ => false
}
}
/// Check whether the type of the token matches the given parameter type.
///
/// Numeric types (`Int` and `Uint`) type check if the size of the token
/// type is of greater or equal size than the provided parameter type.
pub fn type_check(&self, param_type: &ParamType) -> bool {
match *self {
Token::Address(_) => *param_type == ParamType::Address,
Token::Bytes(_) => *param_type == ParamType::Bytes,
Token::Int(_) =>
if let ParamType::Int(_) = *param_type {
true
} else {
false
},
Token::Uint(_) =>
if let ParamType::Uint(_) = *param_type {
true
} else {
false
},
Token::Bool(_) => *param_type == ParamType::Bool,
Token::String(_) => *param_type == ParamType::String,
Token::FixedBytes(ref bytes) =>
if let ParamType::FixedBytes(size) = *param_type {
size >= bytes.len()
} else {
false
},
Token::Array(ref tokens) =>
if let ParamType::Array(ref param_type) = *param_type {
tokens.iter().all(|t| t.type_check(param_type))
} else {
false
},
Token::FixedArray(ref tokens) =>
if let ParamType::FixedArray(ref param_type, size) = *param_type {
size == tokens.len() && tokens.iter().all(|t| t.type_check(param_type))
} else {
false
},
}
}
/// Converts token to...
pub fn to_address(self) -> Option<Address> {
match self {
Token::Address(address) => Some(address),
_ => None,
}
}
/// Converts token to...
pub fn to_fixed_bytes(self) -> Option<Vec<u8>> {
match self {
Token::FixedBytes(bytes) => Some(bytes),
_ => None,
}
}
/// Converts token to...
pub fn to_bytes(self) -> Option<Vec<u8>> {
match self {
Token::Bytes(bytes) => Some(bytes),
_ => None,
}
}
/// Converts token to...
pub fn to_int(self) -> Option<Uint> {
match self {
Token::Int(int) => Some(int),
_ => None,
}
}
/// Converts token to...
pub fn to_uint(self) -> Option<Uint> {
match self {
Token::Uint(uint) => Some(uint),
_ => None,
}
}
/// Converts token to...
pub fn to_bool(self) -> Option<bool> {
match self {
Token::Bool(b) => Some(b),
_ => None,
}
}
/// Converts token to...
pub fn to_string(self) -> Option<String> {
match self {
Token::String(s) => Some(s),
_ => None,
}
}
/// Converts token to...
pub fn to_fixed_array(self) -> Option<Vec<Token>> {
match self {
Token::FixedArray(arr) => Some(arr),
_ => None,
}
}
/// Converts token to...
pub fn to_array(self) -> Option<Vec<Token>> {
match self {
Token::Array(arr) => Some(arr),
_ => None,
}
}
/// Check if all the types of the tokens match the given parameter types.
pub fn types_check(tokens: &[Token], param_types: &[ParamType]) -> bool {
param_types.len() == tokens.len() && {
param_types.iter().zip(tokens).all(|(param_type, token)| {
token.type_check(param_type)
})
}
}
}
#[cfg(test)]
mod tests {
use {Token, ParamType};
#[test]
fn test_type_check() {
fn assert_type_check(tokens: Vec<Token>, param_types: Vec<ParamType>) {
assert!(Token::types_check(&tokens, &param_types))
}
fn assert_not_type_check(tokens: Vec<Token>, param_types: Vec<ParamType>) {
assert!(!Token::types_check(&tokens, &param_types))
}
assert_type_check(vec![Token::Uint(0.into()), Token::Bool(false)], vec![ParamType::Uint(256), ParamType::Bool]);
assert_type_check(vec![Token::Uint(0.into()), Token::Bool(false)], vec![ParamType::Uint(32), ParamType::Bool]);
assert_not_type_check(vec![Token::Uint(0.into())], vec![ParamType::Uint(32), ParamType::Bool]);
assert_not_type_check(vec![Token::Uint(0.into()), Token::Bool(false)], vec![ParamType::Uint(32)]);
assert_not_type_check(vec![Token::Bool(false), Token::Uint(0.into())], vec![ParamType::Uint(32), ParamType::Bool]);
assert_type_check(vec![Token::FixedBytes(vec![0, 0, 0, 0])], vec![ParamType::FixedBytes(4)]);
assert_type_check(vec![Token::FixedBytes(vec![0, 0, 0])], vec![ParamType::FixedBytes(4)]);
assert_not_type_check(vec![Token::FixedBytes(vec![0, 0, 0, 0])], vec![ParamType::FixedBytes(3)]);
assert_type_check(vec![Token::Array(vec![Token::Bool(false), Token::Bool(true)])], vec![ParamType::Array(Box::new(ParamType::Bool))]);
assert_not_type_check(vec![Token::Array(vec![Token::Bool(false), Token::Uint(0.into())])], vec![ParamType::Array(Box::new(ParamType::Bool))]);
assert_not_type_check(vec![Token::Array(vec![Token::Bool(false), Token::Bool(true)])], vec![ParamType::Array(Box::new(ParamType::Address))]);
assert_type_check(vec![Token::FixedArray(vec![Token::Bool(false), Token::Bool(true)])], vec![ParamType::FixedArray(Box::new(ParamType::Bool), 2)]);
assert_not_type_check(vec![Token::FixedArray(vec![Token::Bool(false), Token::Bool(true)])], vec![ParamType::FixedArray(Box::new(ParamType::Bool), 3)]);
assert_not_type_check(vec![Token::FixedArray(vec![Token::Bool(false), Token::Uint(0.into())])], vec![ParamType::FixedArray(Box::new(ParamType::Bool), 2)]);
assert_not_type_check(vec![Token::FixedArray(vec![Token::Bool(false), Token::Bool(true)])], vec![ParamType::FixedArray(Box::new(ParamType::Address), 2)]);
}
#[test]
fn test_is_dynamic() {
assert_eq!(Token::Address("0000000000000000000000000000000000000000".parse().unwrap()).is_dynamic(), false);
assert_eq!(Token::Bytes(vec![0, 0, 0, 0]).is_dynamic(), true);
assert_eq!(Token::FixedBytes(vec![0, 0, 0, 0]).is_dynamic(), false);
assert_eq!(Token::Uint(0.into()).is_dynamic(), false);
assert_eq!(Token::Int(0.into()).is_dynamic(), false);
assert_eq!(Token::Bool(false).is_dynamic(), false);
assert_eq!(Token::String("".into()).is_dynamic(), true);
assert_eq!(Token::Array(vec![Token::Bool(false)]).is_dynamic(), true);
assert_eq!(Token::FixedArray(vec![Token::Uint(0.into())]).is_dynamic(), false);
assert_eq!(Token::FixedArray(vec![Token::String("".into())]).is_dynamic(), true);
assert_eq!(Token::FixedArray(vec![Token::Array(vec![Token::Bool(false)])]).is_dynamic(), true);
}
}
//! Utils used by different modules.
use {Word, Error, ErrorKind};
/// Convers vector of bytes with len equal n * 32, to a vector of slices.
pub fn slice_data(data: &[u8]) -> Result<Vec<Word>, Error> {
if data.len() % 32 != 0 {
return Err(ErrorKind::InvalidData.into());
}
let times = data.len() / 32;
let mut result = Vec::with_capacity(times);
for i in 0..times {
let mut slice = [0u8; 32];
let offset = 32 * i;
slice.copy_from_slice(&data[offset..offset + 32]);
result.push(slice);
}
Ok(result)
}
/// Converts u32 to right aligned array of 32 bytes.
pub fn pad_u32(value: u32) -> Word {
let mut padded = [0u8; 32];
padded[28] = (value >> 24) as u8;
padded[29] = (value >> 16) as u8;
padded[30] = (value >> 8) as u8;
padded[31] = value as u8;
padded
}
/// Converts i32 to right aligned array of 32 bytes.
pub fn pad_i32(value: i32) -> Word {
if value >= 0 {
return pad_u32(value as u32);
}
let mut padded = [0xffu8; 32];
padded[28] = (value >> 24) as u8;
padded[29] = (value >> 16) as u8;
padded[30] = (value >> 8) as u8;
padded[31] = value as u8;
padded
}
#[cfg(test)]
mod tests {
use super::{pad_u32, pad_i32};
#[test]
fn test_pad_u32() {
// this will fail if endianess is not supported
assert_eq!(pad_u32(0).to_vec(), hex!("0000000000000000000000000000000000000000000000000000000000000000").to_vec());
assert_eq!(pad_u32(1).to_vec(), hex!("0000000000000000000000000000000000000000000000000000000000000001").to_vec());
assert_eq!(pad_u32(0x100).to_vec(), hex!("0000000000000000000000000000000000000000000000000000000000000100").to_vec());
assert_eq!(pad_u32(0xffffffff).to_vec(), hex!("00000000000000000000000000000000000000000000000000000000ffffffff").to_vec());
}
#[test]
fn test_i32() {
assert_eq!(pad_i32(0).to_vec(), hex!("0000000000000000000000000000000000000000000000000000000000000000").to_vec());
assert_eq!(pad_i32(-1).to_vec(), hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").to_vec());
assert_eq!(pad_i32(-2).to_vec(), hex!("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe").to_vec());
assert_eq!(pad_i32(-256).to_vec(), hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00").to_vec());
}
}
//! The bignum system library.
use crate::types::Uint256;
/// The low-level interface to the system library. Use the wrapper functions unless you know what
/// you're doing.
pub mod native {
extern "C" {
pub fn bignum_mul256(a: *const u32, b: *const u32, ret: *const u32);
pub fn bignum_umulmod256(a: *const u32, b: *const u32, modulo: *const u32, ret: *const u32);
}
}
/// Unsigned 256-bit multiplication.
pub fn mul256(a: &Uint256, b: &Uint256) -> Uint256 {
let mut ret = Uint256::default();
unsafe {
native::bignum_mul256(
a.bytes.as_ptr() as *const u32,
b.bytes.as_ptr() as *const u32,
ret.bytes.as_mut_ptr() as *const u32,
)
}
ret
}
/// Unsigned 256-bit multiplication modulo n.
pub fn umulmod256(a: &Uint256, b: &Uint256, modulo: &Uint256) -> Uint256 {
let mut ret = Uint256::default();
unsafe {
native::bignum_umulmod256(
a.bytes.as_ptr() as *const u32,
b.bytes.as_ptr() as *const u32,
modulo.bytes.as_ptr() as *const u32,
ret.bytes.as_mut_ptr() as *const u32,
)
}
ret
}
//! Basic type conversion traits. Unlike the native standard library, `U: From<T>` does not yet
//! imply `T: Into<U>`.
use super::*;
pub trait From<T>: Sized {
fn from(_: T) -> Self;
}
pub trait Into<T>: Sized {
fn from(self) -> T;
}
//! The native debug interface exposed to the ewasm contract. These functions are for testing
//! purposes only. On a live VM, any bytecode trying to import these symbols will be rejected.
use crate::types::StorageKey;
/// The native interface for debugging functions.
mod native {
extern "C" {
pub fn debug_print32(value: u32);
pub fn debug_print64(value: u64);
pub fn debug_printMem(offset: *const u32, len: u32);
pub fn debug_printMemHex(offset: *const u32, len: u32);
pub fn debug_printStorage(pathOffset: *const u32);
pub fn debug_printStorageHex(pathOffset: *const u32);
}
}
/// Prints an unsigned 32-bit int.
pub fn print32(value: u32) {
unsafe { native::debug_print32(value) }
}
/// Prints an unsigned 64-bit int.
pub fn print64(value: u64) {
unsafe { native::debug_print64(value) }
}
/// Prints the contents of a slice.
pub fn print_mem(slice: &[u8]) {
unsafe { native::debug_printMem(slice.as_ptr() as *const u32, slice.len() as u32) }
}
/// Prints the contents of a slice in hexadecimal format.
pub fn print_mem_hex(slice: &[u8]) {
unsafe { native::debug_printMemHex(slice.as_ptr() as *const u32, slice.len() as u32) }
}
/// Prints the value of a storage key.
pub fn print_storage(key: &StorageKey) {
unsafe { native::debug_printStorage(key.bytes.as_ptr() as *const u32) }
}
/// Prints the value of a storage key in hexadecimal format.
pub fn print_storage_hex(key: &StorageKey) {
unsafe { native::debug_printStorageHex(key.bytes.as_ptr() as *const u32) }
}
This diff is collapsed.
// add by echo : 扩展存储,以支持超过 32byte 的 key/value >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
extern "C" {
pub fn ethereum_storageStore2(
keyOffset: *const u32, keyLength: u32,
valueOffset: *const u32, valueLength: u32,
);
pub fn ethereum_storageLoad2(
keyOffset: *const u32, keyLength: u32,
resultOffset: *const u32, resultType: u32,
);
}
// PUT Key->Val
//pub fn storage_store2(key: &[u8], keyLen: u32, value: &[u8], valueLen: u32) {
pub fn storage_store(key: &[u8], value: &[u8]) {
let key_len = key.len();
let value_len = value.len();
unsafe {
ethereum_storageStore2(
key.as_ptr() as *const u32,
key_len as u32,
value.as_ptr() as *const u32,
value_len as u32,
);
}
}
pub fn storage_load(key: &[u8]) -> Vec<u8> {
let key_len = key.len();
let mut val_len: [u8; 32] = [0; 32];
unsafe {
ethereum_storageLoad2(
key.as_ptr() as *const u32,
key_len as u32,
val_len.as_mut_ptr() as *const u32,
1,
);
}
let vl = utils::bytes_to_uint(&val_len[..]);
let mut v: Vec<u8> = vec![0; vl];
unsafe {
ethereum_storageLoad2(
key.as_ptr() as *const u32,
key_len as u32,
v.as_mut_slice().as_mut_ptr() as *const u32,
2,
);
}
v
}
pub mod utils {
pub fn bytes_to_uint(n: &[u8]) -> usize {
let size = n.len();
let mut r: usize = 0;
for i in 0..size {
let x = n[i] as usize;
if x > 0 {
let m = (size - 1 - i) * 8;
r = r + (x << m);
}
}
r
}
pub fn u32_to_bytes(i: u32) -> Vec<u8> {
let mut r: Vec<u8> = Vec::new();
r.insert(0, (i >> 24) as u8);
r.insert(1, (i >> 16) as u8);
r.insert(2, (i >> 8) as u8);
r.insert(3, i as u8);
r
}
pub fn u32_to_bytes32(i: u32) -> [u8; 32] {
let new_b: Vec<u8> = u32_to_bytes(i);
let mut val: [u8; 32] = [0; 32];
val[32 - 4] = new_b[0];
val[32 - 3] = new_b[1];
val[32 - 2] = new_b[2];
val[32 - 1] = new_b[3];
val
}
}
// add by echo : 扩展存储,以支持超过 32byte 的 key/value <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
//! The low-level bindings for the Ethereum Environment Interface (EEI). There is a safe set of wrappers for these functions, so use
//! those unless you are certain you know what you're doing.
extern "C" {
pub fn ethereum_useGas(amount: u64);
pub fn ethereum_getGasLeft() -> u64;
pub fn ethereum_getAddress(resultOffset: *const u32);
pub fn ethereum_getExternalBalance(addressOffset: *const u32, resultOffset: *const u32);
pub fn ethereum_getBlockCoinbase(resultOffset: *const u32);
pub fn ethereum_getBlockDifficulty(resultOffset: *const u32);
pub fn ethereum_getBlockGasLimit() -> u64;
pub fn ethereum_getBlockHash(number: u64, resultOffset: *const u32) -> u32;
pub fn ethereum_getBlockNumber() -> u64;
pub fn ethereum_getBlockTimestamp() -> u64;
pub fn ethereum_getTxGasPrice(valueOffset: *const u32);
pub fn ethereum_getTxOrigin(resultOffset: *const u32);
pub fn ethereum_log(
dataOffset: *const u32,
length: u32,
numberOfTopics: u32,
topic1: *const u32,
topic2: *const u32,
topic3: *const u32,
topic4: *const u32,
);
pub fn ethereum_call(
gas: u64,
addressOffset: *const u32,
valueOffset: *const u32,
dataOffset: *const u32,
dataLength: u32,
) -> u32;
pub fn ethereum_callCode(
gas: u64,
addressOffset: *const u32,
valueOffset: *const u32,
dataOffset: *const u32,
dataLength: u32,
) -> u32;
pub fn ethereum_callDelegate(
gas: u64,
addressOffset: *const u32,
dataOffset: *const u32,
dataLength: u32,
) -> u32;
pub fn ethereum_callStatic(
gas: u64,
addressOffset: *const u32,
dataOffset: *const u32,
dataLength: u32,
) -> u32;
pub fn ethereum_create(
valueOffset: *const u32,
dataOffset: *const u32,
dataLength: u32,
resultOffset: *const u32,
) -> u32;
pub fn ethereum_returnDataCopy(resultOffset: *const u32, dataOffset: u32, length: u32);
pub fn ethereum_getReturnDataSize() -> u32;
pub fn ethereum_finish(dataOffset: *const u32, length: u32) -> !;
pub fn ethereum_revert(dataOffset: *const u32, length: u32) -> !;
pub fn ethereum_callDataCopy(resultOffset: *const u32, dataOffset: u32, length: u32);
pub fn ethereum_getCallDataSize() -> u32;
pub fn ethereum_getCaller(resultOffset: *const u32);
pub fn ethereum_getCallValue(resultOffset: *const u32);
pub fn ethereum_codeCopy(resultOffset: *const u32, codeOffset: u32, length: u32);
pub fn ethereum_getCodeSize() -> u32;
pub fn ethereum_externalCodeCopy(
addressOffset: *const u32,
resultOffset: *const u32,
codeOffset: u32,
length: u32,
);
pub fn ethereum_getExternalCodeSize(addressOfset: *const u32) -> u32;
pub fn ethereum_storageLoad(keyOffset: *const u32, resultOffset: *const u32);
pub fn ethereum_storageStore(keyOffset: *const u32, valueOffset: *const u32);
pub fn ethereum_selfDestruct(addressOffset: *const u32) -> !;
}
//! High-level types commonly used in Ethereum contracts.
/// A little-endian unsigned 128-bit integer.
#[derive(Default, Copy, Clone, Debug, PartialEq, Eq)]
pub struct Uint128 {
pub bytes: [u8; 16],
}
/// A little-endian unsigned 256-bit integer.
#[derive(Default, Copy, Clone, Debug, PartialEq, Eq)]
pub struct Uint256 {
pub bytes: [u8; 32],
}
/// An array of 160 bits.
#[derive(Default, Copy, Clone, Debug, PartialEq, Eq)]
pub struct Bytes20 {
pub bytes: [u8; 20],
}
/// An array of 256 bits.
#[derive(Default, Copy, Clone, Debug, PartialEq, Eq)]
pub struct Bytes32 {
pub bytes: [u8; 32],
}
/// Type definition representing a value in wei.
pub type EtherValue = Uint128;
/// Type definition representing an address.
pub type Address = Bytes20;
/// Type definition representing a storage key.
pub type StorageKey = Bytes32;
/// Type definition representing a storage value.
pub type StorageValue = Bytes32;
/// Type definition representing a log topic.
pub type LogTopic = Bytes32;
/// Type definition representing a Keccak-256 or SHA-256 hash.
pub type Hash = Bytes32;
/// Type definition representing a block's difficulty.
pub type Difficulty = Uint256;
macro_rules! from_primitive_impl {
($f:ident, $size:expr, $to:ident) => {
impl From<[$f; $size]> for $to {
fn from(a: [$f; $size]) -> Self {
$to { bytes: a }
}
}
};
}
macro_rules! from_primitive_ref_impl {
($f:ident, $size:expr, $to:ident) => {
impl From<&[$f; $size]> for $to {
fn from(a: &[$f; $size]) -> Self {
$to { bytes: a.clone() }
}
}
};
}
macro_rules! from_type_for_primitive_impl {
($f:ident, $size:expr, $to:ident) => {
impl From<$f> for [$to; $size] {
fn from(a: $f) -> Self {
a.bytes
}
}
};
}
from_primitive_impl!(u8, 16, Uint128);
from_primitive_impl!(u8, 32, Uint256);
from_primitive_impl!(u8, 20, Bytes20);
from_primitive_impl!(u8, 32, Bytes32);
from_primitive_ref_impl!(u8, 16, Uint128);
from_primitive_ref_impl!(u8, 32, Uint256);
from_primitive_ref_impl!(u8, 20, Bytes20);
from_primitive_ref_impl!(u8, 32, Bytes32);
from_type_for_primitive_impl!(Uint128, 16, u8);
from_type_for_primitive_impl!(Uint256, 32, u8);
from_type_for_primitive_impl!(Bytes20, 20, u8);
from_type_for_primitive_impl!(Bytes32, 32, u8);
#[cfg(test)]
mod tests {
use super::{Bytes20, Bytes32, Uint128, Uint256};
macro_rules! test_conversions {
($type: ident, $size: expr, $test_name: ident) => {
#[test]
fn $test_name() {
let raw = [1; $size];
let uint = $type::from(raw);
assert_eq!(uint.bytes[$size - 1], 1);
let uint = $type::from(&raw);
assert_eq!(uint.bytes[$size - 1], 1);
let uint: $type = raw.into();
assert_eq!(uint.bytes[$size - 1], 1);
let uint: $type = (&raw).into();
assert_eq!(uint.bytes[$size - 1], 1);
let r: [u8; $size] = uint.into();
assert_eq!(r[$size - 1], 1);
}
};
}
test_conversions!(Uint128, 16, test_uint128);
test_conversions!(Uint256, 32, test_uint256);
test_conversions!(Bytes20, 20, test_bytes20);
test_conversions!(Bytes32, 32, test_bytes32);
}
//! General utility functions.
/// Allocate an owned buffer using the global allocator.
/// Only enabled with `std`.
#[cfg(feature = "std")]
pub fn unsafe_alloc_buffer(len: usize) -> Vec<u8> {
let mut ret: Vec<u8> = Vec::with_capacity(len);
unsafe {
ret.set_len(len);
}
ret
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn smoke() {
let ret = unsafe_alloc_buffer(42);
assert_eq!(ret.len(), 42);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment