Skip to content

Commit

Permalink
Adds ethAddress type (FuelLabs#1452)
Browse files Browse the repository at this point in the history
* eth address type

* document MemoryOverflow

* ecr for eth_address working.

* eth -> evm

* reference std::vm in sway book

* add reference to ethereum address recovery to cryptography docs

* make evm specific ecrecover explicit. formatting

* Update docs/src/blockchain-development/hashing_and_cryptography.md

Co-authored-by: John Adler <[email protected]>

* Update docs/src/introduction/standard_library.md

Co-authored-by: John Adler <[email protected]>

* Update sway-lib-std/src/vm/evm/ecr.sw

Co-authored-by: John Adler <[email protected]>

* Update sway-lib-std/src/vm/evm/ecr.sw

Co-authored-by: John Adler <[email protected]>

* Update sway-lib-std/src/vm/evm/evm_address.sw

Co-authored-by: John Adler <[email protected]>

Co-authored-by: simonr0204 <[email protected]>
Co-authored-by: Simon Roberts <[email protected]>
Co-authored-by: John Adler <[email protected]>
  • Loading branch information
4 people authored May 24, 2022
1 parent 38a857f commit d73d9d2
Show file tree
Hide file tree
Showing 10 changed files with 60 additions and 39 deletions.
2 changes: 2 additions & 0 deletions docs/src/blockchain-development/hashing_and_cryptography.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ The Sway standard library provides easy access to a selection of cryptographic h
```sway
{{#include ../../../examples/signatures/src/main.sw}}
```

> **Note** Recovery of EVM addresses is also supported via `std::vm::evm`.
2 changes: 1 addition & 1 deletion docs/src/introduction/standard_library.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Similar to Rust, Sway comes with its own standard library.

The Sway Standard Library is the foundation of portable Sway software, a set of minimal shared abstractions for the broader Sway ecosystem. It offers core types, like `Result<T, E>` and `Option<T>`, library-defined operations on language primitives, native asset management, blockchain contextual operations, access control and storage management, among many other things.
The Sway Standard Library is the foundation of portable Sway software, a set of minimal shared abstractions for the broader Sway ecosystem. It offers core types, like `Result<T, E>` and `Option<T>`, library-defined operations on language primitives, native asset management, blockchain contextual operations, access control, storage management, and support for types from other VMs, among many other things.

The standard library is made implicitly available to all Forc projects created using [`forc init`](../forc/commands/forc_init.md). Importing items from the standard library can be done using the `use` keyword. Example:

Expand Down
2 changes: 1 addition & 1 deletion sway-lib-std/src/chain.sw
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
library chain;
dep chain/auth;
dep chain/auth;
2 changes: 1 addition & 1 deletion sway-lib-std/src/logging.sw
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use ::intrinsics::{is_reference_type, size_of};
/// If the type is a reference type, `log` is used.
/// Otherwise `logd` is used.'
pub fn log<T>(value: T) {
if ! is_reference_type::<T>() {
if !is_reference_type::<T>() {
asm(r1: value) {
log r1 zero zero zero;
}
Expand Down
18 changes: 6 additions & 12 deletions sway-lib-std/src/vm/evm/ecr.sw
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
library ecr;

use ::address::Address;
use ::b512::B512;
use ::context::registers::error;
use ::ecr::{EcRecoverError, ec_recover};
use ::vm::evm::evm_address::EvmAddress;
use ::hash::keccak256;
use ::result::*;

/// Recover the address derived from the private key used to sign a message.
/// Recover the EVM address derived from the private key used to sign a message.
/// Returns a `Result` to let the caller choose an error handling strategy.
/// Ethereum addresses are 20 bytes long, so these are left-padded to fit in a 32 byte Address type.
pub fn ec_recover_address(signature: B512, msg_hash: b256) -> Result<Address, EcRecoverError> {
pub fn ec_recover_evm_address(signature: B512, msg_hash: b256) -> Result<EvmAddress, EcRecoverError> {
let pub_key_result = ec_recover(signature, msg_hash);

if let Result::Err(e) = pub_key_result {
Expand All @@ -19,14 +18,9 @@ pub fn ec_recover_address(signature: B512, msg_hash: b256) -> Result<Address, Ec
} else {
let pub_key = pub_key_result.unwrap();

// Note that Ethereum addresses are derived from the Keccak256 hash of the pubkey (not sha256)
let address = keccak256(((pub_key.bytes)[0], (pub_key.bytes)[1]));
// Note that EVM addresses are derived from the Keccak256 hash of the pubkey (not sha256)
let pubkey_hash = keccak256(((pub_key.bytes)[0], (pub_key.bytes)[1]));

// Zero out first 12 bytes for ethereum address
asm(r1: address) {
mcli r1 i12;
};

Result::Ok(~Address::from(address))
Result::Ok(~EvmAddress::from(pubkey_hash))
}
}
40 changes: 40 additions & 0 deletions sway-lib-std/src/vm/evm/evm_address.sw
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
library evm_address;

//! A wrapper around the b256 type to help enhance type-safety.
/// The Address type, a struct wrappper around the inner `value`.
pub struct EvmAddress {
value: b256,
}

impl core::ops::Eq for EvmAddress {
fn eq(self, other: Self) -> bool {
// An `Address` in Sway is 32 bytes
asm(r1: self, r2: other, result, bytes_to_compare: 32) {
meq result r1 r2 bytes_to_compare;
result: bool
}
}
}

pub trait From {
fn from(b: b256) -> Self;
} {
fn into(addr: EvmAddress) -> b256 {
addr.value
}
}

/// Functions for casting between the b256 and Address types.
impl From for EvmAddress {
fn from(bits: b256) -> EvmAddress {
// An EVM address is only 20 bytes, so the first 12 are set to zero
asm(r1: bits) {
mcli r1 i12;
};

EvmAddress {
value: bits,
}
}
}
1 change: 1 addition & 0 deletions sway-lib-std/src/vm/evm/mod.sw
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
library evm;

dep evm_address;
dep ecr;
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
contract;

use std::{
chain::auth::*,
context::call_frames::contract_id,
contract_id::ContractId,
result::*,
revert::revert,
};
use std::{chain::auth::*, context::call_frames::contract_id, contract_id::ContractId, result::*, revert::revert};

use reentrancy_target_abi::Target;
use reentrancy_attacker_abi::Attacker;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,6 @@
contract;

use std::{
assert::assert,
chain::auth::*,
context::{call_frames::contract_id, gas},
contract_id::ContractId,
reentrancy::*,
result::*,
revert::revert,
};
use std::{assert::assert, chain::auth::*, context::{call_frames::contract_id, gas}, contract_id::ContractId, reentrancy::*, result::*, revert::revert};

use reentrancy_attacker_abi::Attacker;
use reentrancy_target_abi::Target;
Expand Down
14 changes: 6 additions & 8 deletions test/src/sdk-harness/test_projects/evm_ecr/src/main.sw
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
script;

use std::address::Address;
use std::assert::assert;
use std::b512::B512;
use std::ecr::EcRecoverError;
use std::result::*;
use std::vm::evm::ecr::ec_recover_address;
use std::vm::evm::evm_address::EvmAddress;
use std::vm::evm::ecr::ec_recover_evm_address;

fn main() -> bool {
//======================================================
Expand All @@ -19,11 +19,9 @@ fn main() -> bool {
Signature: 82115ed208d8fe8dd522d88ca77812b34d270d6bb6326ff511297766a3af1166c07204f554a00e49a2ee69f0979dc4feef07f7dba8d779d388fb2a53bc9bcde4
*/

// Get the expected ethereum pubkeyhash
let pubkey: B512 = ~B512::from(0x1d152307c6b72b0ed0418b0e70cd80e7f5295b8d86f5722d3f5213fbd2394f36, 0xb7ce9c3e45905178455900b44abb308f3ef480481a4b2ee3f70aca157fde396a);
let ethereum_pubkeyhash: Address = ~Address::from(0xe4eab8f844a8d11b205fd137a1b7ea5ede26f651909505d99cf8b5c0d4c8e9c1);
// Manually zero the first 12 bytes.
let ethereum_address: Address = ~Address::from(0x000000000000000000000000a1b7ea5ede26f651909505d99cf8b5c0d4c8e9c1);
// Get the expected ethereum address
let pubkeyhash = 0xe4eab8f844a8d11b205fd137a1b7ea5ede26f651909505d99cf8b5c0d4c8e9c1;
let ethereum_address = ~EvmAddress::from(pubkeyhash);

let msg_hash = 0x8ddb13a2ab58f413bd3121e1ddc8b83a328f3b830d19a7c471f0be652d23bb0e;

Expand All @@ -33,7 +31,7 @@ fn main() -> bool {
let signature: B512 = ~B512::from(sig_hi, sig_lo);

// recover the address:
let result: Result<Address, EcRecoverError> = ec_recover_address(signature, msg_hash);
let result: Result<EvmAddress, EcRecoverError> = ec_recover_evm_address(signature, msg_hash);
let recovered_address = result.unwrap();

recovered_address == ethereum_address
Expand Down

0 comments on commit d73d9d2

Please sign in to comment.