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 = []
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
# 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]));
}
}
use {encode, decode, Token, ParamType};
macro_rules! test_encode_decode {
(name: $name:tt, types: $types:expr, tokens: $tokens:expr, data: $data:tt) => {
$crate::paste::item! {
#[test]
fn [<encode_ $name>]() {
let encoded = encode(&$tokens);
let expected = $crate::hex_literal::hex!($data).to_vec();
assert_eq!(encoded, expected);
}
#[test]
fn [<decode_ $name>]() {
let encoded = hex!($data);
let expected = $tokens;
let decoded = decode(&$types, &encoded).unwrap();
assert_eq!(decoded, expected);
}
}
}
}
// test address
test_encode_decode! {
name: address,
types: [ParamType::Address],
tokens: [Token::Address([0x11u8; 20].into())],
data: "0000000000000000000000001111111111111111111111111111111111111111"
}
test_encode_decode! {
name: addresses,
types: [
ParamType::Address,
ParamType::Address
],
tokens: [
Token::Address([0x11u8; 20].into()),
Token::Address([0x22u8; 20].into())
],
data: "
0000000000000000000000001111111111111111111111111111111111111111
0000000000000000000000002222222222222222222222222222222222222222"
}
// test bytes
test_encode_decode! {
name: bytes,
types: [ParamType::Bytes],
tokens: [Token::Bytes(vec![0x12, 0x34])],
data: "
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000002
1234000000000000000000000000000000000000000000000000000000000000"
}
test_encode_decode! {
name: bytes2,
types: [ParamType::Bytes],
tokens: [Token::Bytes(hex!("10000000000000000000000000000000000000000000000000000000000002").to_vec())],
data: "
0000000000000000000000000000000000000000000000000000000000000020
000000000000000000000000000000000000000000000000000000000000001f
1000000000000000000000000000000000000000000000000000000000000200"
}
test_encode_decode! {
name: bytes3,
types: [ParamType::Bytes],
tokens: [
Token::Bytes(hex!("
1000000000000000000000000000000000000000000000000000000000000000
1000000000000000000000000000000000000000000000000000000000000000
").to_vec())
],
data: "
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000040
1000000000000000000000000000000000000000000000000000000000000000
1000000000000000000000000000000000000000000000000000000000000000"
}
test_encode_decode! {
name: two_bytes,
types: [ParamType::Bytes, ParamType::Bytes],
tokens: [
Token::Bytes(hex!("10000000000000000000000000000000000000000000000000000000000002").to_vec()),
Token::Bytes(hex!("0010000000000000000000000000000000000000000000000000000000000002").to_vec())
],
data: "
0000000000000000000000000000000000000000000000000000000000000040
0000000000000000000000000000000000000000000000000000000000000080
000000000000000000000000000000000000000000000000000000000000001f
1000000000000000000000000000000000000000000000000000000000000200
0000000000000000000000000000000000000000000000000000000000000020
0010000000000000000000000000000000000000000000000000000000000002"
}
// test int
test_encode_decode! {
name: int,
types: [ParamType::Int(32)],
tokens: [Token::Int([0x11u8; 32].into())],
data: "1111111111111111111111111111111111111111111111111111111111111111"
}
test_encode_decode! {
name: int2,
types: [ParamType::Int(32)],
tokens: {
let mut int = [0u8; 32];
int[31] = 4;
[Token::Int(int.into())]
},
data: "0000000000000000000000000000000000000000000000000000000000000004"
}
// test uint
test_encode_decode! {
name: uint,
types: [ParamType::Uint(32)],
tokens: [Token::Uint([0x11u8; 32].into())],
data: "1111111111111111111111111111111111111111111111111111111111111111"
}
test_encode_decode! {
name: uint2,
types: [ParamType::Uint(32)],
tokens: {
let mut uint = [0u8; 32];
uint[31] = 4;
[Token::Uint(uint.into())]
},
data: "0000000000000000000000000000000000000000000000000000000000000004"
}
// test bool
test_encode_decode! {
name: bool,
types: [ParamType::Bool],
tokens: [Token::Bool(true)],
data: "0000000000000000000000000000000000000000000000000000000000000001"
}
test_encode_decode! {
name: bool2,
types: [ParamType::Bool],
tokens: [Token::Bool(false)],
data: "0000000000000000000000000000000000000000000000000000000000000000"
}
// test string
test_encode_decode! {
name: string,
types: [ParamType::String],
tokens: [Token::String("gavofyork".to_owned())],
data: "
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000009
6761766f66796f726b0000000000000000000000000000000000000000000000"
}
// test array
test_encode_decode! {
name: dynamic_array_of_addresses,
types: [ParamType::Array(Box::new(ParamType::Address))],
tokens: {
let address1 = Token::Address([0x11u8; 20].into());
let address2 = Token::Address([0x22u8; 20].into());
[Token::Array(vec![address1, address2])]
},
data: "
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000001111111111111111111111111111111111111111
0000000000000000000000002222222222222222222222222222222222222222"
}
test_encode_decode! {
name: dynamic_array_of_fixed_arrays_of_addresses,
types: [
ParamType::Array(Box::new(
ParamType::FixedArray(Box::new(ParamType::Address), 2)
))
],
tokens: {
let address1 = Token::Address([0x11u8; 20].into());
let address2 = Token::Address([0x22u8; 20].into());
let address3 = Token::Address([0x33u8; 20].into());
let address4 = Token::Address([0x44u8; 20].into());
let array0 = Token::FixedArray(vec![address1, address2]);
let array1 = Token::FixedArray(vec![address3, address4]);
[Token::Array(vec![array0, array1])]
},
data: "
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000001111111111111111111111111111111111111111
0000000000000000000000002222222222222222222222222222222222222222
0000000000000000000000003333333333333333333333333333333333333333
0000000000000000000000004444444444444444444444444444444444444444"
}
test_encode_decode! {
name: dynamic_array_of_fixed_arrays_of_dynamic_array,
types: [
ParamType::Array(Box::new(
ParamType::FixedArray(Box::new(ParamType::Array(Box::new(ParamType::Address))), 2)
))
],
tokens: {
let address1 = Token::Address([0x11u8; 20].into());
let address2 = Token::Address([0x22u8; 20].into());
let address3 = Token::Address([0x33u8; 20].into());
let address4 = Token::Address([0x44u8; 20].into());
let address5 = Token::Address([0x55u8; 20].into());
let address6 = Token::Address([0x66u8; 20].into());
let address7 = Token::Address([0x77u8; 20].into());
let address8 = Token::Address([0x88u8; 20].into());
let array0 = Token::FixedArray(vec![
Token::Array(vec![address1, address2]),
Token::Array(vec![address3, address4]),
]);
let array1 = Token::FixedArray(vec![
Token::Array(vec![address5, address6]),
Token::Array(vec![address7, address8]),
]);
[Token::Array(vec![array0, array1])]
},
data: "
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000000000000000000000000000000000000000000040
0000000000000000000000000000000000000000000000000000000000000140
0000000000000000000000000000000000000000000000000000000000000040
00000000000000000000000000000000000000000000000000000000000000a0
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000001111111111111111111111111111111111111111
0000000000000000000000002222222222222222222222222222222222222222
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000003333333333333333333333333333333333333333
0000000000000000000000004444444444444444444444444444444444444444
0000000000000000000000000000000000000000000000000000000000000040
00000000000000000000000000000000000000000000000000000000000000a0
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000005555555555555555555555555555555555555555
0000000000000000000000006666666666666666666666666666666666666666
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000007777777777777777777777777777777777777777
0000000000000000000000008888888888888888888888888888888888888888"
}
test_encode_decode! {
name: dynamic_array_of_dynamic_arrays,
types: [
ParamType::Array(Box::new(
ParamType::Array(Box::new(ParamType::Address))
))
],
tokens: {
let address1 = Token::Address([0x11u8; 20].into());
let address2 = Token::Address([0x22u8; 20].into());
let array0 = Token::Array(vec![address1]);
let array1 = Token::Array(vec![address2]);
let dynamic = Token::Array(vec![array0, array1]);
[dynamic]
},
data: "
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000000000000000000000000000000000000000000040
0000000000000000000000000000000000000000000000000000000000000080
0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000001111111111111111111111111111111111111111
0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000002222222222222222222222222222222222222222"
}
test_encode_decode! {
name: dynamic_array_of_dynamic_arrays2,
types: [
ParamType::Array(Box::new(
ParamType::Array(Box::new(ParamType::Address))
))
],
tokens: {
let address1 = Token::Address([0x11u8; 20].into());
let address2 = Token::Address([0x22u8; 20].into());
let address3 = Token::Address([0x33u8; 20].into());
let address4 = Token::Address([0x44u8; 20].into());
let array0 = Token::Array(vec![address1, address2]);
let array1 = Token::Array(vec![address3, address4]);
let dynamic = Token::Array(vec![array0, array1]);
[dynamic]
},
data: "
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000000000000000000000000000000000000000000040
00000000000000000000000000000000000000000000000000000000000000a0
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000001111111111111111111111111111111111111111
0000000000000000000000002222222222222222222222222222222222222222
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000003333333333333333333333333333333333333333
0000000000000000000000004444444444444444444444444444444444444444"
}
test_encode_decode! {
name: dynamic_array_of_bytes,
types: [ParamType::Array(Box::new(ParamType::Bytes))],
tokens: {
let bytes = hex!("019c80031b20d5e69c8093a571162299032018d913930d93ab320ae5ea44a4218a274f00d607").to_vec();
[Token::Array(vec![Token::Bytes(bytes)])]
},
data: "
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000026
019c80031b20d5e69c8093a571162299032018d913930d93ab320ae5ea44a421
8a274f00d6070000000000000000000000000000000000000000000000000000"
}
test_encode_decode! {
name: dynamic_array_of_bytes2,
types: [ParamType::Array(Box::new(ParamType::Bytes))],
tokens: [
Token::Array(vec![
Token::Bytes(hex!("4444444444444444444444444444444444444444444444444444444444444444444444444444").to_vec()),
Token::Bytes(hex!("6666666666666666666666666666666666666666666666666666666666666666666666666666").to_vec()),
])
],
data: "
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000000000000000000000000000000000000000000040
00000000000000000000000000000000000000000000000000000000000000a0
0000000000000000000000000000000000000000000000000000000000000026
4444444444444444444444444444444444444444444444444444444444444444
4444444444440000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000026
6666666666666666666666666666666666666666666666666666666666666666
6666666666660000000000000000000000000000000000000000000000000000"
}
test_encode_decode! {
name: empty_dynamic_array,
types: [
ParamType::Array(Box::new(ParamType::Bool)),
ParamType::Array(Box::new(ParamType::Bool)),
],
tokens: [
Token::Array(vec![]),
Token::Array(vec![])
],
data: "
0000000000000000000000000000000000000000000000000000000000000040
0000000000000000000000000000000000000000000000000000000000000060
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000"
}
test_encode_decode! {
name: dynamic_array_of_empty_dynamic_array,
types: [
ParamType::Array(Box::new(ParamType::Array(Box::new(ParamType::Bool)))),
ParamType::Array(Box::new(ParamType::Array(Box::new(ParamType::Bool)))),
],
tokens: [
Token::Array(vec![Token::Array(vec![])]),
Token::Array(vec![Token::Array(vec![])]),
],
data: "
0000000000000000000000000000000000000000000000000000000000000040
00000000000000000000000000000000000000000000000000000000000000a0
0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000000"
}
// test fixed array
test_encode_decode! {
name: fixed_array_of_addresses,
types: [ParamType::FixedArray(Box::new(ParamType::Address), 2)],
tokens: {
let address1 = Token::Address([0x11u8; 20].into());
let address2 = Token::Address([0x22u8; 20].into());
[Token::FixedArray(vec![address1, address2])]
},
data: "
0000000000000000000000001111111111111111111111111111111111111111
0000000000000000000000002222222222222222222222222222222222222222"
}
test_encode_decode! {
name: fixed_array_of_fixed_arrays,
types: [
ParamType::FixedArray(
Box::new(ParamType::FixedArray(Box::new(ParamType::Address), 2)),
2
)
],
tokens: {
let address1 = Token::Address([0x11u8; 20].into());
let address2 = Token::Address([0x22u8; 20].into());
let address3 = Token::Address([0x33u8; 20].into());
let address4 = Token::Address([0x44u8; 20].into());
let array0 = Token::FixedArray(vec![address1, address2]);
let array1 = Token::FixedArray(vec![address3, address4]);
let fixed = Token::FixedArray(vec![array0, array1]);
[fixed]
},
data: "
0000000000000000000000001111111111111111111111111111111111111111
0000000000000000000000002222222222222222222222222222222222222222
0000000000000000000000003333333333333333333333333333333333333333
0000000000000000000000004444444444444444444444444444444444444444"
}
test_encode_decode! {
name: fixed_array_of_dynamic_array_of_addresses,
types: [
ParamType::FixedArray(
Box::new(ParamType::Array(Box::new(ParamType::Address))),
2
)
],
tokens: {
let address1 = Token::Address([0x11u8; 20].into());
let address2 = Token::Address([0x22u8; 20].into());
let address3 = Token::Address([0x33u8; 20].into());
let address4 = Token::Address([0x44u8; 20].into());
let array0 = Token::Array(vec![address1, address2]);
let array1 = Token::Array(vec![address3, address4]);
[Token::FixedArray(vec![array0, array1])]
},
data: "
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000040
00000000000000000000000000000000000000000000000000000000000000a0
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000001111111111111111111111111111111111111111
0000000000000000000000002222222222222222222222222222222222222222
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000003333333333333333333333333333333333333333
0000000000000000000000004444444444444444444444444444444444444444"
}
// test fixed bytes
test_encode_decode! {
name: fixed_bytes,
types: [ParamType::FixedBytes(2)],
tokens: [Token::FixedBytes(vec![0x12, 0x34])],
data: "1234000000000000000000000000000000000000000000000000000000000000"
}
// comprehensive test
test_encode_decode! {
name: comprehensive_test,
types: [
ParamType::Int(32),
ParamType::Bytes,
ParamType::Int(32),
ParamType::Bytes,
],
tokens: {
let bytes = hex!("
131a3afc00d1b1e3461b955e53fc866dcf303b3eb9f4c16f89e388930f48134b
131a3afc00d1b1e3461b955e53fc866dcf303b3eb9f4c16f89e388930f48134b
").to_vec();
[
Token::Int(5.into()),
Token::Bytes(bytes.clone()),
Token::Int(3.into()),
Token::Bytes(bytes),
]
},
data: "
0000000000000000000000000000000000000000000000000000000000000005
0000000000000000000000000000000000000000000000000000000000000080
0000000000000000000000000000000000000000000000000000000000000003
00000000000000000000000000000000000000000000000000000000000000e0
0000000000000000000000000000000000000000000000000000000000000040
131a3afc00d1b1e3461b955e53fc866dcf303b3eb9f4c16f89e388930f48134b
131a3afc00d1b1e3461b955e53fc866dcf303b3eb9f4c16f89e388930f48134b
0000000000000000000000000000000000000000000000000000000000000040
131a3afc00d1b1e3461b955e53fc866dcf303b3eb9f4c16f89e388930f48134b
131a3afc00d1b1e3461b955e53fc866dcf303b3eb9f4c16f89e388930f48134b"
}
test_encode_decode! {
name: comprehensive_test2,
types: [
ParamType::Int(32),
ParamType::String,
ParamType::Int(32),
ParamType::Int(32),
ParamType::Int(32),
ParamType::Array(Box::new(ParamType::Int(32))),
],
tokens: [
Token::Int(1.into()),
Token::String("gavofyork".to_owned()),
Token::Int(2.into()),
Token::Int(3.into()),
Token::Int(4.into()),
Token::Array(vec![
Token::Int(5.into()),
Token::Int(6.into()),
Token::Int(7.into()),
])
],
data: "
0000000000000000000000000000000000000000000000000000000000000001
00000000000000000000000000000000000000000000000000000000000000c0
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000000000000000000000000000000000000000000003
0000000000000000000000000000000000000000000000000000000000000004
0000000000000000000000000000000000000000000000000000000000000100
0000000000000000000000000000000000000000000000000000000000000009
6761766f66796f726b0000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000003
0000000000000000000000000000000000000000000000000000000000000005
0000000000000000000000000000000000000000000000000000000000000006
0000000000000000000000000000000000000000000000000000000000000007"
}
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) }
}
//! ewasm_api is a library used to interface with Ethereum's EEI in [Ewasm](https://github.com/ewasm/design), a set of enhancements to
//! the Ethereum smart contract platform.
//! ewasm_api exposes both a set of unsafe "native" functions representing the actual EEI
//! functions, and a set of safe wrappers around them. It is recommended not to use the native
//! functions as they do not perform bounds-checking.
//!
//! To use ewasm_api, simply include it as a dependency in your project.
//! ewasm_api can be built 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.
//!
//! # Examples
//! ```ignore
//! extern crate ewasm_api;
//!
//! use ewasm_api::{Hash, block_hash, finish_data};
//!
//! #[cfg(target_arch = "wasm32")]
//! #[no_mangle]
//! pub extern "C" fn main() {
//! let a: Hash = block_hash(1);
//! finish_data(&a.bytes);
//! }
//! ```
#[macro_use]
extern crate cfg_if;
cfg_if! {
if #[cfg(feature = "wee_alloc")] {
extern crate wee_alloc;
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
} else if #[cfg(feature = "qimalloc")] {
extern crate qimalloc;
#[global_allocator]
static ALLOC: qimalloc::QIMalloc = qimalloc::QIMalloc::INIT;
}
}
mod native;
mod utils;
pub extern crate ethabi as metavmabi;
//pub extern crate rustc_hex;
pub mod metavm;
pub mod types;
#[cfg(feature = "debug")]
pub mod debug;
#[cfg(feature = "experimental")]
pub mod bignum;
#[cfg(not(feature = "std"))]
pub mod convert;
#[cfg(feature = "std")]
use std::vec::Vec;
use types::*;
use utils::*;
/// Re-export of all the basic features.
pub mod prelude {
pub use crate::*;
pub use crate::types::*;
#[cfg(not(feature = "std"))]
pub use crate::convert::*;
#[cfg(feature = "debug")]
pub use crate::debug;
#[cfg(feature = "experimental")]
pub use crate::bignum;
}
/// Enum representing an error code for EEI calls. Currently used by `codeCopy`, `callDataCopy`,
/// `externalCodeCopy`, and `returnDataCopy`.
pub enum Error {
OutOfBoundsCopy,
}
/// Enum describing the result of a call. Used by `call`, `callCode`, `callDelegate`, and
/// `callStatic`.
pub enum CallResult {
Successful,
Failure,
Revert,
Unknown,
}
/// Enum describing the result of `create`. On success, the data contained is the address of the
/// newly created contract.
pub enum CreateResult {
Successful(Address),
Failure,
Revert,
Unknown,
}
/// Subtracts the given amount from the VM's gas counter. This is usually injected by the metering
/// contract at deployment time, and hence is unneeded in most cases.
pub fn consume_gas(amount: u64) {
unsafe {
native::ethereum_useGas(amount);
}
}
/// Returns the gas left in the current call.
pub fn gas_left() -> u64 {
unsafe { native::ethereum_getGasLeft() }
}
/// Returns the executing address.
pub fn current_address() -> Address {
let mut ret = Address::default();
unsafe {
native::ethereum_getAddress(ret.bytes.as_mut_ptr() as *const u32);
}
ret
}
/// Returns the balance of the address given.
pub fn external_balance(address: &Address) -> EtherValue {
let mut ret = EtherValue::default();
unsafe {
native::ethereum_getExternalBalance(
address.bytes.as_ptr() as *const u32,
ret.bytes.as_mut_ptr() as *const u32,
);
}
ret
}
/// Returns the beneficiary address for the block this transaction is in (current block)
pub fn block_coinbase() -> Address {
let mut ret = Address::default();
unsafe {
native::ethereum_getBlockCoinbase(ret.bytes.as_mut_ptr() as *const u32);
}
ret
}
/// Returns the difficulty of the most recent block.
pub fn block_difficulty() -> Difficulty {
let mut ret = Difficulty::default();
unsafe {
native::ethereum_getBlockDifficulty(ret.bytes.as_mut_ptr() as *const u32);
}
ret
}
/// Returns the gas limit of the most recent block.
pub fn block_gas_limit() -> u64 {
unsafe { native::ethereum_getBlockGasLimit() }
}
/// Returns the hash of the `number`th most recent block.
pub fn block_hash(number: u64) -> Hash {
let mut ret = Hash::default();
unsafe {
native::ethereum_getBlockHash(number, ret.bytes.as_mut_ptr() as *const u32);
}
ret
}
/// Returns the number of the most recent block.
pub fn block_number() -> u64 {
unsafe { native::ethereum_getBlockNumber() }
}
/// Returns the timestamp of the most recent block.
pub fn block_timestamp() -> u64 {
unsafe { native::ethereum_getBlockTimestamp() }
}
/// Returns the gas price of the currently executing call.
pub fn tx_gas_price() -> EtherValue {
let mut ret = EtherValue::default();
unsafe {
native::ethereum_getTxGasPrice(ret.bytes.as_mut_ptr() as *const u32);
}
ret
}
/// Returns the address of the original transaction sender.
pub fn tx_origin() -> Address {
let mut ret = Address::default();
unsafe {
native::ethereum_getTxOrigin(ret.bytes.as_mut_ptr() as *const u32);
}
ret
}
/// Appends log data to the transaction receipt, with a variable number of topics.
fn log(
data: &[u8],
topic_count: usize,
topic1: *const u8,
topic2: *const u8,
topic3: *const u8,
topic4: *const u8,
) {
unsafe {
native::ethereum_log(
data.as_ptr() as *const u32,
data.len() as u32,
topic_count as u32,
topic1 as *const u32,
topic2 as *const u32,
topic3 as *const u32,
topic4 as *const u32,
);
}
}
/// Appends log data without a topic.
pub fn log0(data: &[u8]) {
log(
data,
0,
0 as *const u8,
0 as *const u8,
0 as *const u8,
0 as *const u8,
)
}
/// Appends log data with one topic.
pub fn log1(data: &[u8], topic1: &LogTopic) {
log(
data,
1,
topic1.bytes.as_ptr() as *const u8,
0 as *const u8,
0 as *const u8,
0 as *const u8,
)
}
/// Appends log data with two topics.
pub fn log2(data: &[u8], topic1: &LogTopic, topic2: &LogTopic) {
log(
data,
2,
topic1.bytes.as_ptr() as *const u8,
topic2.bytes.as_ptr() as *const u8,
0 as *const u8,
0 as *const u8,
)
}
/// Appends log data with three topics.
pub fn log3(data: &[u8], topic1: &LogTopic, topic2: &LogTopic, topic3: &LogTopic) {
log(
data,
3,
topic1.bytes.as_ptr() as *const u8,
topic2.bytes.as_ptr() as *const u8,
topic3.bytes.as_ptr() as *const u8,
0 as *const u8,
)
}
/// Appends log data with four topics.
pub fn log4(
data: &[u8],
topic1: &LogTopic,
topic2: &LogTopic,
topic3: &LogTopic,
topic4: &LogTopic,
) {
log(
data,
4,
topic1.bytes.as_ptr() as *const u8,
topic2.bytes.as_ptr() as *const u8,
topic3.bytes.as_ptr() as *const u8,
topic4.bytes.as_ptr() as *const u8,
)
}
/// Executes a standard call to the specified address with the given gas limit, ether value, and
/// data.
pub fn call_mutable(
gas_limit: u64,
address: &Address,
value: &EtherValue,
data: &[u8],
) -> CallResult {
let ret = unsafe {
native::ethereum_call(
gas_limit,
address.bytes.as_ptr() as *const u32,
value.bytes.as_ptr() as *const u32,
data.as_ptr() as *const u32,
data.len() as u32,
)
};
match ret {
0 => CallResult::Successful,
1 => CallResult::Failure,
2 => CallResult::Revert,
_ => CallResult::Unknown,
}
}
/// Executes another account's code in the context of the caller.
pub fn call_code(gas_limit: u64, address: &Address, value: &EtherValue, data: &[u8]) -> CallResult {
let ret = unsafe {
native::ethereum_callCode(
gas_limit,
address.bytes.as_ptr() as *const u32,
value.bytes.as_ptr() as *const u32,
data.as_ptr() as *const u32,
data.len() as u32,
)
};
match ret {
0 => CallResult::Successful,
1 => CallResult::Failure,
2 => CallResult::Revert,
_ => CallResult::Unknown,
}
}
/// Executes a call similar to `call_code`, but retaining the currently executing call's sender
/// and value.
pub fn call_delegate(gas_limit: u64, address: &Address, data: &[u8]) -> CallResult {
let ret = unsafe {
native::ethereum_callDelegate(
gas_limit,
address.bytes.as_ptr() as *const u32,
data.as_ptr() as *const u32,
data.len() as u32,
)
};
match ret {
0 => CallResult::Successful,
1 => CallResult::Failure,
2 => CallResult::Revert,
_ => CallResult::Unknown,
}
}
/// Executes a static call which cannot mutate the state.
pub fn call_static(gas_limit: u64, address: &Address, data: &[u8]) -> CallResult {
let ret = unsafe {
native::ethereum_callStatic(
gas_limit,
address.bytes.as_ptr() as *const u32,
data.as_ptr() as *const u32,
data.len() as u32,
)
};
match ret {
0 => CallResult::Successful,
1 => CallResult::Failure,
2 => CallResult::Revert,
_ => CallResult::Unknown,
}
}
/// Creates a contract with the the given code, sending the specified ether value to its address.
pub fn create(value: &EtherValue, data: &[u8]) -> CreateResult {
let mut address = Address::default();
let ret = unsafe {
native::ethereum_create(
value.bytes.as_ptr() as *const u32,
data.as_ptr() as *const u32,
data.len() as u32,
address.bytes.as_mut_ptr() as *const u32,
)
};
match ret {
0 => CreateResult::Successful(address),
1 => CreateResult::Failure,
2 => CreateResult::Revert,
_ => CreateResult::Unknown,
}
}
/// Executes callDataCopy, but does not check for overflow.
pub fn unsafe_calldata_copy(from: usize, length: usize, ret: &mut [u8]) {
unsafe {
native::ethereum_callDataCopy(ret.as_mut_ptr() as *const u32, from as u32, length as u32);
}
}
#[cfg(feature = "std")]
/// Returns a vector containing all data passed with the currently executing call.
pub fn calldata_acquire() -> Vec<u8> {
let length = calldata_size();
let mut ret: Vec<u8> = unsafe_alloc_buffer(length);
unsafe_calldata_copy(0, length, &mut ret);
ret
}
/// Returns the segment of call data beginning at `from`, and continuing for `length` bytes.
pub fn calldata_copy(from: usize, length: usize, ret: &mut [u8]) -> Result<(), Error> {
let size = calldata_size();
if (size < from) || ((size - from) < length) || (ret.len() < length) {
Err(Error::OutOfBoundsCopy)
} else {
unsafe_calldata_copy(from, length, ret);
Ok(())
}
}
/// Returns the length of the call data supplied with the currently executing call.
pub fn calldata_size() -> usize {
unsafe { native::ethereum_getCallDataSize() as usize }
}
/// Returns the sender of the currently executing call.
pub fn caller() -> Address {
let mut ret = Address::default();
unsafe {
native::ethereum_getCaller(ret.bytes.as_mut_ptr() as *const u32);
}
ret
}
/// Returns the value sent with the currently executing call.
pub fn callvalue() -> EtherValue {
let mut ret = EtherValue::default();
unsafe {
native::ethereum_getCallValue(ret.bytes.as_mut_ptr() as *const u32);
}
ret
}
/// Executes codeCopy, but does not check for overflow.
pub fn unsafe_code_copy(from: usize, length: usize, ret: &mut [u8]) {
unsafe {
native::ethereum_codeCopy(ret.as_mut_ptr() as *const u32, from as u32, length as u32);
}
}
#[cfg(feature = "std")]
/// Returns the currently executing code.
pub fn code_acquire() -> Vec<u8> {
let length = code_size();
let mut ret: Vec<u8> = unsafe_alloc_buffer(length);
unsafe_code_copy(0, length, &mut ret);
ret
}
/// Copies the segment of running code beginning at `from` and continuing for `length` bytes.
pub fn code_copy(from: usize, length: usize, ret: &mut [u8]) -> Result<(), Error> {
let size = code_size();
if (size < from) || ((size - from) < length) || (ret.len() < length) {
Err(Error::OutOfBoundsCopy)
} else {
unsafe_code_copy(from, length, ret);
Ok(())
}
}
/// Returns the size of the currently executing code.
pub fn code_size() -> usize {
unsafe { native::ethereum_getCodeSize() as usize }
}
/// Executes externalCodeCopy, but does not check for overflow.
pub fn unsafe_external_code_copy(address: &Address, from: usize, length: usize, ret: &mut [u8]) {
unsafe {
native::ethereum_externalCodeCopy(
address.bytes.as_ptr() as *const u32,
ret.as_mut_ptr() as *const u32,
from as u32,
length as u32,
);
}
}
#[cfg(feature = "std")]
/// Returns the code at the specified address.
pub fn external_code_acquire(address: &Address) -> Vec<u8> {
let length = external_code_size(address);
let mut ret: Vec<u8> = unsafe_alloc_buffer(length);
unsafe_external_code_copy(address, 0, length, &mut ret);
ret
}
/// Returns the segment of code at `address` beginning at `from` and continuing for `length` bytes.
pub fn external_code_copy(
address: &Address,
from: usize,
length: usize,
ret: &mut [u8],
) -> Result<(), Error> {
let size = external_code_size(address);
if (size < from) || ((size - from) < length) || (ret.len() < length) {
Err(Error::OutOfBoundsCopy)
} else {
unsafe_external_code_copy(address, from, length, ret);
Ok(())
}
}
/// Returns the size of the code at the specified address.
pub fn external_code_size(address: &Address) -> usize {
unsafe { native::ethereum_getExternalCodeSize(address.bytes.as_ptr() as *const u32) as usize }
}
/// Executes returnDataCopy, but does not check for overflow.
pub fn unsafe_returndata_copy(from: usize, length: usize, ret: &mut [u8]) {
unsafe {
native::ethereum_returnDataCopy(ret.as_mut_ptr() as *const u32, from as u32, length as u32);
}
}
#[cfg(feature = "std")]
/// Returns the data in the VM's return buffer.
pub fn returndata_acquire() -> Vec<u8> {
let length = returndata_size();
let mut ret: Vec<u8> = unsafe_alloc_buffer(length);
unsafe_returndata_copy(0, length, &mut ret);
ret
}
/// Returns the segment of return buffer data beginning at `from` and continuing for `length` bytes.
pub fn returndata_copy(from: usize, length: usize, ret: &mut [u8]) -> Result<(), Error> {
let size = returndata_size();
if (size < from) || ((size - from) < length) || (ret.len() < length) {
Err(Error::OutOfBoundsCopy)
} else {
unsafe_returndata_copy(from, length, ret);
Ok(())
}
}
/// Returns the length of the data in the VM's return buffer.
pub fn returndata_size() -> usize {
unsafe { native::ethereum_getReturnDataSize() as usize }
}
/// Halts execution, reverts all changes to the state and consumes all gas.
pub fn abort() -> ! {
// TODO: use assembly block with `unreachable`
panic!()
}
/// Halts execution and reverts all changes to the state.
pub fn revert() -> ! {
unsafe {
native::ethereum_revert(0 as *const u32, 0 as u32);
}
}
/// Fills the return buffer with the given data and halts execution, reverting all state changes.
pub fn revert_data(data: &[u8]) -> ! {
unsafe {
native::ethereum_revert(data.as_ptr() as *const u32, data.len() as u32);
}
}
/// Ends execution, signalling success.
pub fn finish() -> ! {
unsafe {
native::ethereum_finish(0 as *const u32, 0 as u32);
}
}
/// Fills the return buffer with the given data and halts execution, signalling success.
pub fn finish_data(data: &[u8]) -> ! {
unsafe {
native::ethereum_finish(data.as_ptr() as *const u32, data.len() as u32);
}
}
/// Accesses the storage data at the specified key.
pub fn storage_load(key: &StorageKey) -> StorageValue {
let mut ret = StorageValue::default();
unsafe {
native::ethereum_storageLoad(
key.bytes.as_ptr() as *const u32,
ret.bytes.as_mut_ptr() as *const u32,
);
}
ret
}
/// Sets the storage data at the specified key.
pub fn storage_store(key: &StorageKey, value: &StorageValue) {
unsafe {
native::ethereum_storageStore(
key.bytes.as_ptr() as *const u32,
value.bytes.as_ptr() as *const u32,
);
}
}
/// Self-destructs the running contract, sending all its ether to a specified beneficiary address.
pub fn selfdestruct(address: &Address) -> ! {
unsafe {
native::ethereum_selfDestruct(address.bytes.as_ptr() as *const u32);
}
}
/*
// 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_store2(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_load2(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,
);
}
//TODO 执行完上面的请求,好像有一个阻塞
let vl = metavmutils::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 metavmutils {
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 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
*/
// 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