-
Notifications
You must be signed in to change notification settings - Fork 33
Remove dependency on primitive_types for Ethereum types such as Address and TransactionHash
#2405
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,21 +2,141 @@ | |
| #![forbid(unsafe_code)] | ||
|
|
||
| pub use ethbloom::{Bloom as H2048, Input}; | ||
| pub use primitive_types::{H160, H256, U128, U256}; | ||
| use hex::FromHexError; | ||
| pub use primitive_types::U256; | ||
| use serde::{Deserialize, Serialize}; | ||
| use serde_hex::{CompactPfx, SerHex, SerHexSeq, StrictPfx}; | ||
|
|
||
| pub type Address = H160; | ||
| use std::{ | ||
| fmt, | ||
| fmt::{Display, Formatter, LowerHex}, | ||
| str::FromStr, | ||
| }; | ||
|
|
||
| #[derive(Debug, Default, Copy, Clone, PartialEq, Deserialize)] | ||
| pub struct H64(#[serde(with = "SerHex::<StrictPfx>")] [u8; 8]); | ||
|
|
||
| #[derive( | ||
| Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, | ||
| )] | ||
| pub struct Address(#[serde(with = "SerHex::<StrictPfx>")] [u8; 20]); | ||
|
|
||
| impl Address { | ||
| pub fn from_slice(src: &[u8]) -> Self { | ||
| let mut address = Address([0u8; 20]); | ||
| address.0.copy_from_slice(src); | ||
| address | ||
| } | ||
| } | ||
|
|
||
| impl From<[u8; 20]> for Address { | ||
| fn from(bytes: [u8; 20]) -> Self { | ||
| Address(bytes) | ||
| } | ||
| } | ||
|
|
||
| impl From<Address> for [u8; 20] { | ||
| fn from(s: Address) -> Self { | ||
| s.0 | ||
| } | ||
| } | ||
|
|
||
| impl FromStr for Address { | ||
| type Err = FromHexError; | ||
|
|
||
| fn from_str(s: &str) -> Result<Self, Self::Err> { | ||
| hex::decode(s).map(|v| Address::from_slice(v.as_slice())) | ||
| } | ||
| } | ||
|
|
||
| impl From<Address> for Hash { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That looks weird, why do we need to convert between an Address and a Hash?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we currently do this in production code and removing that is non-trivial, can you please record a follow-up issue?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Okay interesting, I would put this functionality on the specific topic then and not as a generall conversion between addresses and hashes. |
||
| fn from(address: Address) -> Self { | ||
| let mut h256 = Hash([0u8; 32]); | ||
| h256.0[(32 - 20)..32].copy_from_slice(&address.0); | ||
| h256 | ||
| } | ||
| } | ||
|
|
||
| impl LowerHex for Address { | ||
| fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { | ||
| if f.alternate() { | ||
| write!(f, "0x")?; | ||
| } | ||
| for i in &self.0[..] { | ||
| write!(f, "{:02x}", i)?; | ||
| } | ||
| Ok(()) | ||
| } | ||
| } | ||
|
|
||
| #[derive( | ||
| Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, | ||
| )] | ||
| pub struct Hash(#[serde(with = "SerHex::<StrictPfx>")] [u8; 32]); | ||
|
|
||
| impl From<[u8; 32]> for Hash { | ||
| fn from(bytes: [u8; 32]) -> Self { | ||
| Hash(bytes) | ||
| } | ||
| } | ||
|
|
||
| impl From<Hash> for [u8; 32] { | ||
| fn from(s: Hash) -> Self { | ||
| s.0 | ||
| } | ||
| } | ||
|
|
||
| impl Hash { | ||
| pub fn from_slice(src: &[u8]) -> Self { | ||
| let mut h256 = Hash([0u8; 32]); | ||
| h256.0.copy_from_slice(src); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as above, this can panic and we should avoid using this function.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. follow up: #2412 |
||
| h256 | ||
| } | ||
|
|
||
| pub fn as_bytes(&self) -> &[u8] { | ||
| &self.0 | ||
| } | ||
| } | ||
|
|
||
| impl FromStr for Hash { | ||
| type Err = FromHexError; | ||
|
|
||
| fn from_str(s: &str) -> Result<Self, Self::Err> { | ||
| hex::decode(s).map(|v| Hash::from_slice(v.as_slice())) | ||
| } | ||
| } | ||
|
|
||
| impl LowerHex for Hash { | ||
| fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { | ||
| if f.alternate() { | ||
| write!(f, "0x")?; | ||
| } | ||
| for i in &self.0[..] { | ||
| write!(f, "{:02x}", i)?; | ||
| } | ||
| Ok(()) | ||
| } | ||
| } | ||
|
|
||
| impl Display for Hash { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Eventually, I would like this This will likely require a few changes to log statements in our code where we currently use Can be done as a follow-up ticket :)
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. recorded: #2413 |
||
| fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { | ||
| write!(f, "0x")?; | ||
| for i in &self.0[0..2] { | ||
| write!(f, "{:02x}", i)?; | ||
| } | ||
| write!(f, "…")?; | ||
| for i in &self.0[32 - 2..32] { | ||
| write!(f, "{:02x}", i)?; | ||
| } | ||
| Ok(()) | ||
| } | ||
| } | ||
|
|
||
| /// "Receipt" of an executed transaction: details of its execution. | ||
| #[derive(Debug, Default, Clone, PartialEq, Deserialize)] | ||
| pub struct TransactionReceipt { | ||
| /// Contract address created, or `None` if not a deployment. | ||
| #[serde(rename = "contractAddress")] | ||
| pub contract_address: Option<H160>, | ||
| pub contract_address: Option<Address>, | ||
| /// Logs generated within this transaction. | ||
| pub logs: Vec<Log>, | ||
| /// Status: either 1 (success) or 0 (failure). | ||
|
|
@@ -34,9 +154,9 @@ impl TransactionReceipt { | |
| #[derive(Debug, Default, Clone, PartialEq, Deserialize)] | ||
| pub struct Transaction { | ||
| /// Hash | ||
| pub hash: H256, | ||
| pub hash: Hash, | ||
| /// Recipient (None when contract creation) | ||
| pub to: Option<H160>, | ||
| pub to: Option<Address>, | ||
| /// Transfered value | ||
| pub value: U256, | ||
| /// Input data | ||
|
|
@@ -47,9 +167,9 @@ pub struct Transaction { | |
| #[derive(Debug, Clone, PartialEq, Deserialize)] | ||
| pub struct Log { | ||
| /// H160 | ||
| pub address: H160, | ||
| pub address: Address, | ||
| /// Topics | ||
| pub topics: Vec<H256>, | ||
| pub topics: Vec<Hash>, | ||
| /// Data | ||
| pub data: Bytes, | ||
| } | ||
|
|
@@ -60,10 +180,10 @@ pub struct Log { | |
| #[derive(Debug, Default, Clone, PartialEq, Deserialize)] | ||
| pub struct Block { | ||
| /// Hash of the block | ||
| pub hash: Option<H256>, | ||
| pub hash: Option<Hash>, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you record a follow-up issue for making this not an
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. recorded: #2414 |
||
| /// Hash of the parent | ||
| #[serde(rename = "parentHash")] | ||
| pub parent_hash: H256, | ||
| pub parent_hash: Hash, | ||
| /// Logs bloom | ||
| #[serde(rename = "logsBloom")] | ||
| pub logs_bloom: H2048, | ||
|
|
@@ -85,8 +205,70 @@ impl<T: Into<Vec<u8>>> From<T> for Bytes { | |
|
|
||
| #[cfg(test)] | ||
| mod tests { | ||
| use super::Log; | ||
| use crate::ethereum::TransactionReceipt; | ||
| use super::*; | ||
|
|
||
| #[test] | ||
| fn deserialise_address() { | ||
| let json = | ||
| serde_json::Value::String("0xc5549e335b2786520f4c5d706c76c9ee69d0a028".to_owned()); | ||
| let _: Address = Address::deserialize(&json).unwrap(); | ||
| } | ||
|
|
||
| #[test] | ||
| fn deserialise_address_when_not_using_reference_to_deserialize_fails() { | ||
| // This is due to a bug in serde-jex, keep this test until https://github.com/fspmarshall/serde-hex/pull/8 | ||
| // is fixed. | ||
| let json = | ||
| serde_json::Value::String("0xc5549e335b2786520f4c5d706c76c9ee69d0a028".to_owned()); | ||
|
|
||
| let deserialized = serde_json::from_value::<Address>(json); | ||
| matches!(deserialized, Err(_)); | ||
| } | ||
|
|
||
| #[test] | ||
| fn from_string_address() { | ||
| let json = | ||
| serde_json::Value::String("0xc5549e335b2786520f4c5d706c76c9ee69d0a028".to_owned()); | ||
| let deserialized: Address = Address::deserialize(&json).unwrap(); | ||
|
|
||
| let from_string = Address::from_str("c5549e335b2786520f4c5d706c76c9ee69d0a028").unwrap(); | ||
|
|
||
| assert_eq!(from_string, deserialized); | ||
| } | ||
|
|
||
| #[test] | ||
| fn deserialise_hash() { | ||
| let json = serde_json::Value::String( | ||
| "0x3ae3b6ffb04204f52dee42000e8b971c0f7c2b4aa8dd9455e41a30ee4b31e8a9".to_owned(), | ||
| ); | ||
| let _: Hash = Hash::deserialize(&json).unwrap(); | ||
| } | ||
|
|
||
| #[test] | ||
| fn deserialise_hash_when_not_using_reference_to_deserialize_fails() { | ||
| // This is due to a bug in serde-jex, keep this test until https://github.com/fspmarshall/serde-hex/pull/8 | ||
| // is fixed. | ||
| let json = serde_json::Value::String( | ||
| "0x3ae3b6ffb04204f52dee42000e8b971c0f7c2b4aa8dd9455e41a30ee4b31e8a9".to_owned(), | ||
| ); | ||
|
|
||
| let deserialized = serde_json::from_value::<Hash>(json); | ||
| matches!(deserialized, Err(_)); | ||
| } | ||
|
|
||
| #[test] | ||
| fn from_string_hash() { | ||
| let json = serde_json::Value::String( | ||
| "0x3ae3b6ffb04204f52dee42000e8b971c0f7c2b4aa8dd9455e41a30ee4b31e8a9".to_owned(), | ||
| ); | ||
| let deserialized: Hash = Hash::deserialize(&json).unwrap(); | ||
|
|
||
| let from_string = | ||
| Hash::from_str("3ae3b6ffb04204f52dee42000e8b971c0f7c2b4aa8dd9455e41a30ee4b31e8a9") | ||
| .unwrap(); | ||
|
|
||
| assert_eq!(from_string, deserialized); | ||
| } | ||
|
|
||
| #[test] | ||
| fn deserialise_log() { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this can panic if the slice is not long enough, i.e. < 20 bytes. Can you please create a follow-up ticket for auditing the usages of this function with the goal of removing it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
follow up: #2412