This commit is contained in:
Georgios Konstantopoulos 2020-05-26 12:52:15 +03:00
parent 3f313ede01
commit e7c9e19409
No known key found for this signature in database
GPG Key ID: FA607837CD26EDBC
16 changed files with 1032 additions and 154 deletions

895
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -5,10 +5,10 @@ members = [
# "./crates/ethers-abi", # "./crates/ethers-abi",
# "./crates/ethers-contract", # "./crates/ethers-contract",
# "./crates/ethers-derive", # "./crates/ethers-derive",
# "./crates/ethers-providers", "./crates/ethers-providers",
# "./crates/ethers-signers", # "./crates/ethers-signers",
"./crates/ethers-types", "./crates/ethers-types",
# "./crates/ethers-utils", "./crates/ethers-utils",
] ]
# [dependencies] # [dependencies]

View File

@ -4,6 +4,13 @@ version = "0.1.0"
authors = ["Georgios Konstantopoulos <me@gakonst.com>"] authors = ["Georgios Konstantopoulos <me@gakonst.com>"]
edition = "2018" edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
ethers-types = { path = "../ethers-types" }
ethers-utils = { path = "../ethers-utils" }
async-trait = { version = "0.1.31", default-features = false }
reqwest = { version = "0.10.4", default-features = false, features = ["json", "rustls-tls"] }
serde = { version = "1.0.110", default-features = false, features = ["derive"] }
serde_json = { version = "1.0.53", default-features = false }
thiserror = { version = "1.0.19", default-features = false }
url = { version = "2.1.1", default-features = false }

View File

@ -1,6 +1,6 @@
//! Minimal HTTP JSON-RPC 2.0 Client //! Minimal HTTP JSON-RPC 2.0 Client
//! The request/response code is taken from [here](https://github.com/althea-net/guac_rs/blob/master/web3/src/jsonrpc) //! The request/response code is taken from [here](https://github.com/althea-net/guac_rs/blob/master/web3/src/jsonrpc)
use crate::providers::JsonRpcClient; use crate::JsonRpcClient;
use async_trait::async_trait; use async_trait::async_trait;
use reqwest::{Client, Error as ReqwestError}; use reqwest::{Client, Error as ReqwestError};

View File

@ -1,7 +1,29 @@
#[cfg(test)] //! Ethereum compatible providers
mod tests { //! Currently supported:
#[test] //! - Raw HTTP POST requests
fn it_works() { //!
assert_eq!(2 + 2, 4); //! TODO: WebSockets, multiple backends, popular APIs etc.
}
mod provider;
mod http;
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use std::{error::Error, fmt::Debug};
/// An HTTP provider for interacting with an Ethereum-compatible blockchain
pub type HttpProvider = Provider<http::Provider>;
#[async_trait]
/// Implement this trait in order to plug in different backends
pub trait JsonRpcClient: Debug {
type Error: Error;
/// Sends a request with the provided method and the params serialized as JSON
async fn request<T: Serialize + Send + Sync, R: for<'a> Deserialize<'a>>(
&self,
method: &str,
params: Option<T>,
) -> Result<R, Self::Error>;
} }

View File

@ -1,39 +1,13 @@
//! Ethereum compatible providers use ethers_types::{
//! Currently supported: Address, Block, BlockId, BlockNumber, Filter, Log, Transaction, TransactionReceipt,
//! - Raw HTTP POST requests TransactionRequest, TxHash, U256,
//!
//! TODO: WebSockets, multiple backends, popular APIs etc.
mod http;
use crate::{
signers::{Client, Signer},
types::{
Address, Block, BlockId, BlockNumber, Filter, Log, Transaction, TransactionReceipt,
TransactionRequest, TxHash, U256,
},
utils,
}; };
use ethers_utils as utils;
use async_trait::async_trait; use async_trait::async_trait;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{error::Error, fmt::Debug}; use std::{error::Error, fmt::Debug};
/// An HTTP provider for interacting with an Ethereum-compatible blockchain
pub type HttpProvider = Provider<http::Provider>;
#[async_trait]
/// Implement this trait in order to plug in different backends
pub trait JsonRpcClient: Debug {
type Error: Error;
/// Sends a request with the provided method and the params serialized as JSON
async fn request<T: Serialize + Send + Sync, R: for<'a> Deserialize<'a>>(
&self,
method: &str,
params: Option<T>,
) -> Result<R, Self::Error>;
}
/// An abstract provider for interacting with the [Ethereum JSON RPC /// An abstract provider for interacting with the [Ethereum JSON RPC
/// API](https://github.com/ethereum/wiki/wiki/JSON-RPC) /// API](https://github.com/ethereum/wiki/wiki/JSON-RPC)
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -41,16 +15,6 @@ pub struct Provider<P>(P);
// JSON RPC bindings // JSON RPC bindings
impl<P: JsonRpcClient> Provider<P> { impl<P: JsonRpcClient> Provider<P> {
/// Connects to a signer and returns a client
pub fn connect<S: Signer>(&self, signer: S) -> Client<S, P> {
Client {
signer: Some(signer),
provider: self,
}
}
// Cost related
/// Gets the current gas price as estimated by the node /// Gets the current gas price as estimated by the node
pub async fn get_gas_price(&self) -> Result<U256, P::Error> { pub async fn get_gas_price(&self) -> Result<U256, P::Error> {
self.0.request("eth_gasPrice", None::<()>).await self.0.request("eth_gasPrice", None::<()>).await

View File

@ -7,6 +7,8 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
ethers-utils = { path = "../ethers-utils" }
ethereum-types = { version = "0.9.2", default-features = false, features = ["serialize"] } ethereum-types = { version = "0.9.2", default-features = false, features = ["serialize"] }
serde = { version = "1.0.110", default-features = false, features = ["derive"] } serde = { version = "1.0.110", default-features = false, features = ["derive"] }
rlp = { version = "0.4.5", default-features = false } rlp = { version = "0.4.5", default-features = false }

View File

@ -1,4 +1,4 @@
use crate::types::{Address, Bloom, Bytes, H256, U256, U64}; use crate::{Address, Bloom, Bytes, H256, U256, U64};
use serde::{ser::SerializeStruct, Deserialize, Serialize, Serializer}; use serde::{ser::SerializeStruct, Deserialize, Serialize, Serializer};
/// The block type returned from RPC calls. /// The block type returned from RPC calls.

View File

@ -1,3 +1,6 @@
use crate::{Address, Signature, Transaction, TransactionRequest, H256, U256, U64};
use ethers_utils::{hash_message, keccak256};
use rand::Rng; use rand::Rng;
use secp256k1::{ use secp256k1::{
key::ONE_KEY, Error as SecpError, Message, PublicKey as PubKey, Secp256k1, SecretKey, key::ONE_KEY, Error as SecpError, Message, PublicKey as PubKey, Secp256k1, SecretKey,
@ -7,11 +10,6 @@ use std::str::FromStr;
use thiserror::Error; use thiserror::Error;
use zeroize::DefaultIsZeroes; use zeroize::DefaultIsZeroes;
use crate::{
types::{Address, Signature, Transaction, TransactionRequest, H256, U256, U64},
utils::{hash_message, keccak256},
};
/// A private key on Secp256k1 /// A private key on Secp256k1
#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct PrivateKey(pub(super) SecretKey); pub struct PrivateKey(pub(super) SecretKey);

View File

@ -1,7 +1,5 @@
use crate::{ use crate::{Address, BlockNumber, Bytes, H256, U256, U64};
types::{Address, BlockNumber, Bytes, H256, U256, U64}, use ethers_utils::keccak256;
utils::keccak256,
};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::str::FromStr; use std::str::FromStr;

View File

@ -1,8 +1,6 @@
// Code adapted from: https://github.com/tomusdrw/rust-web3/blob/master/src/api/accounts.rs // Code adapted from: https://github.com/tomusdrw/rust-web3/blob/master/src/api/accounts.rs
use crate::{ use crate::{Address, PublicKey, H256};
types::{Address, PublicKey, H256}, use ethers_utils::hash_message;
utils::hash_message,
};
use rustc_hex::ToHex; use rustc_hex::ToHex;
use secp256k1::{ use secp256k1::{

View File

@ -1,8 +1,7 @@
//! Transaction types //! Transaction types
use crate::{ use crate::{Address, Bloom, Bytes, Log, Signature, H256, U256, U64};
types::{Address, Bloom, Bytes, Log, Signature, H256, U256, U64}, use ethers_utils::keccak256;
utils::keccak256,
};
use rlp::RlpStream; use rlp::RlpStream;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::str::FromStr; use std::str::FromStr;

View File

@ -4,6 +4,8 @@ version = "0.1.0"
authors = ["Georgios Konstantopoulos <me@gakonst.com>"] authors = ["Georgios Konstantopoulos <me@gakonst.com>"]
edition = "2018" edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
ethereum-types = { version = "0.9.2", default-features = false, features = ["serialize"] }
tiny-keccak = { version = "2.0.2", default-features = false }
serde = { version = "1.0.110", default-features = false }
serde_json = { version = "1.0.53", default-features = false, features = ["alloc"] }

View File

@ -1,7 +1,81 @@
//! Various utilities for manipulating Ethereum related dat
use ethereum_types::H256;
use tiny_keccak::{Hasher, Keccak};
const PREFIX: &str = "\x19Ethereum Signed Message:\n";
/// Hash a message according to EIP-191.
///
/// The data is a UTF-8 encoded string and will enveloped as follows:
/// `"\x19Ethereum Signed Message:\n" + message.length + message` and hashed
/// using keccak256.
pub fn hash_message<S>(message: S) -> H256
where
S: AsRef<[u8]>,
{
let message = message.as_ref();
let mut eth_message = format!("{}{}", PREFIX, message.len()).into_bytes();
eth_message.extend_from_slice(message);
keccak256(&eth_message).into()
}
/// Compute the Keccak-256 hash of input bytes.
// TODO: Add Solidity Keccak256 packing support
pub fn keccak256(bytes: &[u8]) -> [u8; 32] {
let mut output = [0u8; 32];
let mut hasher = Keccak::v256();
hasher.update(bytes);
hasher.finalize(&mut output);
output
}
/// Calculate the function selector as per the contract ABI specification. This
/// is defined as the first 4 bytes of the Keccak256 hash of the function
/// signature.
pub fn id<S: AsRef<str>>(signature: S) -> [u8; 4] {
let mut output = [0u8; 4];
let mut hasher = Keccak::v256();
hasher.update(signature.as_ref().as_bytes());
hasher.finalize(&mut output);
output
}
/// Serialize a type. Panics if the type is returns error during serialization.
pub fn serialize<T: serde::Serialize>(t: &T) -> serde_json::Value {
serde_json::to_value(t).expect("Types never fail to serialize.")
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*;
// test vector taken from:
// https://web3js.readthedocs.io/en/v1.2.2/web3-eth-accounts.html#hashmessage
#[test] #[test]
fn it_works() { fn test_hash_message() {
assert_eq!(2 + 2, 4); let hash = hash_message("Hello World");
assert_eq!(
hash,
"a1de988600a42c4b4ab089b619297c17d53cffae5d5120d82d8a92d0bb3b78f2"
.parse()
.unwrap()
);
}
#[test]
fn simple_function_signature() {
// test vector retrieved from
// https://web3js.readthedocs.io/en/v1.2.4/web3-eth-abi.html#encodefunctionsignature
assert_eq!(id("myMethod(uint256,string)"), [0x24, 0xee, 0x00, 0x97],);
}
#[test]
fn revert_function_signature() {
assert_eq!(id("Error(string)"), [0x08, 0xc3, 0x79, 0xa0]);
} }
} }

View File

@ -1,81 +0,0 @@
//! Various utilities for manipulating Ethereum related dat
use crate::types::{Selector, H256};
use tiny_keccak::{Hasher, Keccak};
const PREFIX: &str = "\x19Ethereum Signed Message:\n";
/// Hash a message according to EIP-191.
///
/// The data is a UTF-8 encoded string and will enveloped as follows:
/// `"\x19Ethereum Signed Message:\n" + message.length + message` and hashed
/// using keccak256.
pub fn hash_message<S>(message: S) -> H256
where
S: AsRef<[u8]>,
{
let message = message.as_ref();
let mut eth_message = format!("{}{}", PREFIX, message.len()).into_bytes();
eth_message.extend_from_slice(message);
keccak256(&eth_message).into()
}
/// Compute the Keccak-256 hash of input bytes.
// TODO: Add Solidity Keccak256 packing support
pub fn keccak256(bytes: &[u8]) -> [u8; 32] {
let mut output = [0u8; 32];
let mut hasher = Keccak::v256();
hasher.update(bytes);
hasher.finalize(&mut output);
output
}
/// Calculate the function selector as per the contract ABI specification. This
/// is defined as the first 4 bytes of the Keccak256 hash of the function
/// signature.
pub fn id<S: AsRef<str>>(signature: S) -> Selector {
let mut output = [0u8; 4];
let mut hasher = Keccak::v256();
hasher.update(signature.as_ref().as_bytes());
hasher.finalize(&mut output);
output
}
/// Serialize a type. Panics if the type is returns error during serialization.
pub fn serialize<T: serde::Serialize>(t: &T) -> serde_json::Value {
serde_json::to_value(t).expect("Types never fail to serialize.")
}
#[cfg(test)]
mod tests {
use super::*;
// test vector taken from:
// https://web3js.readthedocs.io/en/v1.2.2/web3-eth-accounts.html#hashmessage
#[test]
fn test_hash_message() {
let hash = hash_message("Hello World");
assert_eq!(
hash,
"a1de988600a42c4b4ab089b619297c17d53cffae5d5120d82d8a92d0bb3b78f2"
.parse()
.unwrap()
);
}
#[test]
fn simple_function_signature() {
// test vector retrieved from
// https://web3js.readthedocs.io/en/v1.2.4/web3-eth-abi.html#encodefunctionsignature
assert_eq!(id("myMethod(uint256,string)"), [0x24, 0xee, 0x00, 0x97],);
}
#[test]
fn revert_function_signature() {
assert_eq!(id("Error(string)"), [0x08, 0xc3, 0x79, 0xa0]);
}
}