Address checksum (#85)
* feat: addr checksum encoding * fix: replace format! with encode to hex * chore: Fix celo tests * perf(checksum): convert loop to iterator Co-authored-by: Rohit Narurkar <rohit.narurkar@protonmail.com>
This commit is contained in:
parent
ac93c0bc65
commit
938972e9cc
|
@ -77,7 +77,7 @@ pub struct Block<TX> {
|
||||||
#[cfg(feature = "celo")]
|
#[cfg(feature = "celo")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "celo")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "celo")))]
|
||||||
#[serde(rename = "epochSnarkData", default)]
|
#[serde(rename = "epochSnarkData", default)]
|
||||||
pub epoch_snark_data: EpochSnarkData,
|
pub epoch_snark_data: Option<EpochSnarkData>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize)]
|
#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
|
@ -214,7 +214,7 @@ mod celo_tests {
|
||||||
fn block_without_snark_data() {
|
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 = 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();
|
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]
|
#[test]
|
||||||
|
|
|
@ -23,10 +23,13 @@ where
|
||||||
|
|
||||||
/// Compute the Keccak-256 hash of input bytes.
|
/// Compute the Keccak-256 hash of input bytes.
|
||||||
// TODO: Add Solidity Keccak256 packing support
|
// 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 output = [0u8; 32];
|
||||||
let mut hasher = Keccak::v256();
|
let mut hasher = Keccak::v256();
|
||||||
hasher.update(bytes);
|
hasher.update(bytes.as_ref());
|
||||||
hasher.finalize(&mut output);
|
hasher.finalize(&mut output);
|
||||||
output
|
output
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ pub use rlp;
|
||||||
|
|
||||||
use crate::types::{Address, Bytes, U256};
|
use crate::types::{Address, Bytes, U256};
|
||||||
use k256::{ecdsa::SigningKey, EncodedPoint as K256PublicKey};
|
use k256::{ecdsa::SigningKey, EncodedPoint as K256PublicKey};
|
||||||
|
use rustc_hex::ToHex;
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
|
|
||||||
/// 1 Ether = 1e18 Wei == 0x0de0b6b3a7640000 Wei
|
/// 1 Ether = 1e18 Wei == 0x0de0b6b3a7640000 Wei
|
||||||
|
@ -111,6 +112,32 @@ pub fn secret_key_to_address(secret_key: &SigningKey) -> Address {
|
||||||
Address::from_slice(&hash[12..])
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -121,6 +148,94 @@ mod tests {
|
||||||
assert_eq!(WEI_IN_ETHER.as_u64(), 1e18 as u64);
|
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]
|
#[test]
|
||||||
fn contract_address() {
|
fn contract_address() {
|
||||||
// http://ethereum.stackexchange.com/questions/760/how-is-the-address-of-an-ethereum-contract-computed
|
// http://ethereum.stackexchange.com/questions/760/how-is-the-address-of-an-ethereum-contract-computed
|
||||||
|
|
Loading…
Reference in New Issue