Skip to content
This repository has been archived by the owner on Oct 19, 2024. It is now read-only.

Address checksum #85

Merged
merged 4 commits into from
Oct 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions ethers-core/src/types/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ pub struct Block<TX> {
#[cfg(feature = "celo")]
#[cfg_attr(docsrs, doc(cfg(feature = "celo")))]
#[serde(rename = "epochSnarkData", default)]
pub epoch_snark_data: EpochSnarkData,
pub epoch_snark_data: Option<EpochSnarkData>,
}

#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize)]
Expand Down Expand Up @@ -214,7 +214,7 @@ mod celo_tests {
fn block_without_snark_data() {
let block = r#"{"extraData":"0xd983010000846765746889676f312e31332e3130856c696e7578000000000000f8b2c0c080b841cfa11585812ec794c4baa46178690971b3c72e367211d68a9ea318ff500c5aeb7099cafc965240e3b57cf7355341cf76bdca74530334658370d2df7b2e030ab200f582027db017810fa05b4f35927f968f6be1a61e322d4ace3563feb8a489690a91c031fda640c55c216f6712a7bdde994338a5610080f58203ffb093cd643f5154979952791ff714eb885df0f18012f2211fb8c29a8947130dc3adf4ecb48a3c4a142a0faa51e5c60b048180","gasUsed":"0xbef6","hash":"0x37ac2818e50e61f0566caea102ed98677f2552fa86fed53443315ed11fe0eaad","logsBloom":"0x00000800000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000100000000000000000000000000000000000000000000000000000000000000080000000000001020000400000000000000000000000000000000000000000000000000000000000080000000000000000000000000400000000000000000000000000000000000000100000040004000000000000800000000000000000084000000000000000000000000000000000020000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000","miner":"0xcda518f6b5a797c3ec45d37c65b83e0b0748edca","number":"0x1b4","parentHash":"0xa6b4775f600c2981f9142cbc1361db02c7ba8c185a1110537b255356876301a2","randomness":{"committed":"0x049e84c89f1aa0e3a770b2545b05a30eb814dae322e7247fd2bf27e6cacb1f51","revealed":"0x5a8826bf59a7ed1ee86a9d6464fa9c1fcece78ffa7cf32b11a03ad251ddcefe6"},"receiptsRoot":"0x1724dc3e7c2bfa03974c1deedf5ea20ad30b72e25f3c62fbb5fd06fc107068d7","size":"0x3a0","stateRoot":"0xc45fa03e69dccb54b4981d23d77328ab8906ddd7a0d8238b9c54ae1a14df4d1c","timestamp":"0x5e90166d","totalDifficulty":"0x1b5","transactions":[{"blockHash":"0x37ac2818e50e61f0566caea102ed98677f2552fa86fed53443315ed11fe0eaad","blockNumber":"0x1b4","from":"0x456f41406b32c45d59e539e4bba3d7898c3584da","gas":"0x1312d00","gasPrice":"0x174876e800","feeCurrency":null,"gatewayFeeRecipient":null,"gatewayFee":"0x0","hash":"0xf7b1b588b1fc03305f556805812273d80fb61fc0ba7f812de27189e95c5ecfc5","input":"0xed385274000000000000000000000000b9ff7ab50a2f0fd3e2fb2814b016ac90c91df98f03386ba30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000be951906eba2aa800000","nonce":"0x147","to":"0xa12a699c641cc875a7ca57495861c79c33d293b4","transactionIndex":"0x0","value":"0x0","v":"0x15e08","r":"0x5787d040d09a34cb2b9ffcd096be7fe66aa6a3ed0632f182d1f3045640a9ef8b","s":"0x7897f58740f2a1c645826579106a620c306fc56381520ae2f28880bb284c4abd"}],"transactionsRoot":"0xbc8cb40b809914b9cd735b12e9b1802cf5d85de5223a22bbdb249a7e8b45ec93"}"#;
let block: Block<Transaction> = serde_json::from_str(&block).unwrap();
assert_eq!(block.epoch_snark_data, EpochSnarkData::default());
assert_eq!(block.epoch_snark_data, None);
}

#[test]
Expand Down
7 changes: 5 additions & 2 deletions ethers-core/src/utils/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,13 @@ where

/// Compute the Keccak-256 hash of input bytes.
// TODO: Add Solidity Keccak256 packing support
pub fn keccak256(bytes: &[u8]) -> [u8; 32] {
pub fn keccak256<S>(bytes: S) -> [u8; 32]
where
S: AsRef<[u8]>,
{
let mut output = [0u8; 32];
let mut hasher = Keccak::v256();
hasher.update(bytes);
hasher.update(bytes.as_ref());
hasher.finalize(&mut output);
output
}
Expand Down
115 changes: 115 additions & 0 deletions ethers-core/src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub use rlp;

use crate::types::{Address, Bytes, U256};
use k256::{ecdsa::SigningKey, EncodedPoint as K256PublicKey};
use rustc_hex::ToHex;
use std::convert::TryInto;

/// 1 Ether = 1e18 Wei == 0x0de0b6b3a7640000 Wei
Expand Down Expand Up @@ -111,6 +112,32 @@ pub fn secret_key_to_address(secret_key: &SigningKey) -> Address {
Address::from_slice(&hash[12..])
}

/// Converts an Ethereum address to the checksum encoding
/// Ref: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-55.md
pub fn to_checksum(addr: &Address, chain_id: Option<u8>) -> String {
let prefixed_addr = match chain_id {
Some(chain_id) => format!("{}0x{:x}", chain_id, addr),
None => format!("{:x}", addr),
};
let hash = keccak256(&prefixed_addr).to_hex::<String>();
let hash = hash.as_bytes();

let addr_hex = addr.as_bytes().to_hex::<String>();
let addr_hex = addr_hex.as_bytes();

addr_hex
.into_iter()
.zip(hash)
.fold("0x".to_owned(), |mut encoded, (addr, hash)| {
encoded.push(if *hash >= 56 {
addr.to_ascii_uppercase() as char
} else {
addr.to_ascii_lowercase() as char
});
encoded
})
}

#[cfg(test)]
mod tests {
use super::*;
Expand All @@ -121,6 +148,94 @@ mod tests {
assert_eq!(WEI_IN_ETHER.as_u64(), 1e18 as u64);
}

#[test]
fn addr_checksum() {
let addr_list = vec![
// mainnet
(
None,
"27b1fdb04752bbc536007a920d24acb045561c26",
"0x27b1fdb04752bbc536007a920d24acb045561c26",
),
(
None,
"3599689e6292b81b2d85451025146515070129bb",
"0x3599689E6292b81B2d85451025146515070129Bb",
),
(
None,
"42712d45473476b98452f434e72461577d686318",
"0x42712D45473476b98452f434e72461577D686318",
),
(
None,
"52908400098527886e0f7030069857d2e4169ee7",
"0x52908400098527886E0F7030069857D2E4169EE7",
),
(
None,
"5aaeb6053f3e94c9b9a09f33669435e7ef1beaed",
"0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed",
),
(
None,
"6549f4939460de12611948b3f82b88c3c8975323",
"0x6549f4939460DE12611948b3f82b88C3C8975323",
),
(
None,
"66f9664f97f2b50f62d13ea064982f936de76657",
"0x66f9664f97F2b50F62D13eA064982f936dE76657",
),
(
None,
"88021160c5c792225e4e5452585947470010289d",
"0x88021160C5C792225E4E5452585947470010289D",
),
// rsk mainnet
(
Some(30),
"27b1fdb04752bbc536007a920d24acb045561c26",
"0x27b1FdB04752BBc536007A920D24ACB045561c26",
),
(
Some(30),
"3599689e6292b81b2d85451025146515070129bb",
"0x3599689E6292B81B2D85451025146515070129Bb",
),
(
Some(30),
"42712d45473476b98452f434e72461577d686318",
"0x42712D45473476B98452f434E72461577d686318",
),
(
Some(30),
"52908400098527886e0f7030069857d2e4169ee7",
"0x52908400098527886E0F7030069857D2E4169ee7",
),
(
Some(30),
"5aaeb6053f3e94c9b9a09f33669435e7ef1beaed",
"0x5aaEB6053f3e94c9b9a09f33669435E7ef1bEAeD",
),
(
Some(30),
"6549f4939460de12611948b3f82b88c3c8975323",
"0x6549F4939460DE12611948B3F82B88C3C8975323",
),
(
Some(30),
"66f9664f97f2b50f62d13ea064982f936de76657",
"0x66F9664f97f2B50F62d13EA064982F936de76657",
),
];

for (chain_id, addr, checksummed_addr) in addr_list {
let addr = addr.parse::<Address>().unwrap();
assert_eq!(to_checksum(&addr, chain_id), String::from(checksummed_addr));
}
}

#[test]
fn contract_address() {
// http://ethereum.stackexchange.com/questions/760/how-is-the-address-of-an-ethereum-contract-computed
Expand Down