From c272a9c0f51a37313b35b5491439f17c7b16669b Mon Sep 17 00:00:00 2001 From: muji Date: Wed, 1 May 2024 11:05:56 +0800 Subject: [PATCH 1/2] Import address and signature modules. Use zero address for global preferences. --- Cargo.lock | 51 +--- crates/sdk/Cargo.toml | 4 +- crates/sdk/src/account/archive/backup.rs | 2 +- crates/sdk/src/account/archive/zip.rs | 3 +- crates/sdk/src/account/preferences.rs | 2 + crates/sdk/src/error.rs | 20 +- crates/sdk/src/signer/address.rs | 218 +++++++++++++++++ crates/sdk/src/{signer.rs => signer/mod.rs} | 7 +- crates/sdk/src/signer/signature.rs | 257 ++++++++++++++++++++ 9 files changed, 510 insertions(+), 54 deletions(-) create mode 100644 crates/sdk/src/signer/address.rs rename crates/sdk/src/{signer.rs => signer/mod.rs} (98%) create mode 100644 crates/sdk/src/signer/signature.rs diff --git a/Cargo.lock b/Cargo.lock index 8fb527b25c..422cd20ef0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1470,9 +1470,9 @@ checksum = "a0474425d51df81997e2f90a21591180b38eccf27292d755f3e30750225c175b" [[package]] name = "ethbloom" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11da94e443c60508eb62cf256243a64da87304c2802ac2528847f79d750007ef" +checksum = "c22d4b5885b6aa2fe5e8b9329fb8d232bf739e434e6b87347c63bdd00c120f60" dependencies = [ "crunchy", "fixed-hash", @@ -1483,9 +1483,9 @@ dependencies = [ [[package]] name = "ethereum-types" -version = "0.13.1" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2827b94c556145446fcce834ca86b7abf0c39a805883fe20e72c5bfdb5a0dc6" +checksum = "02d215cbf040552efcbe99a38372fe80ab9d00268e20012b79fcd0f073edd8ee" dependencies = [ "ethbloom", "fixed-hash", @@ -1581,9 +1581,9 @@ dependencies = [ [[package]] name = "fixed-hash" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" dependencies = [ "byteorder", "rand", @@ -2252,9 +2252,9 @@ dependencies = [ [[package]] name = "impl-serde" -version = "0.3.2" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4551f042f3438e64dbd6226b20527fc84a6e1fe65688b58746a2f53623f25f5c" +checksum = "ebc88fc67028ae3db0c853baa36269d398d5f45b6982f95549ff5def78c935cd" dependencies = [ "serde", ] @@ -3115,9 +3115,9 @@ dependencies = [ [[package]] name = "primitive-types" -version = "0.11.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28720988bff275df1f51b171e1b2a18c30d194c4d2b61defdacecd625a5d94a" +checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" dependencies = [ "fixed-hash", "impl-codec", @@ -4216,6 +4216,7 @@ dependencies = [ "csv-async", "ed25519-dalek", "enum-iterator", + "ethereum-types", "filetime", "futures", "futures-util", @@ -4245,6 +4246,7 @@ dependencies = [ "sha2", "sha3", "sos-vfs 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle", "tempfile", "thiserror", "time", @@ -4261,8 +4263,6 @@ dependencies = [ "vcard4", "vsss-rs", "walkdir", - "web3-address", - "web3-signature", "whoami", "zxcvbn", ] @@ -5385,33 +5385,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "web3-address" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b03bbbdd6d7ccf5eb05509f7913344ecff52e1f0fa1e4daa1d046a9de75d4a8d" -dependencies = [ - "hex", - "k256", - "serde", - "sha3", - "subtle", - "thiserror", -] - -[[package]] -name = "web3-signature" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66c1aab5ddd1557cca9710fee8ff8ab44a8034dfc83b767be3abcb5d48f08489" -dependencies = [ - "ethereum-types", - "hex", - "k256", - "serde", - "thiserror", -] - [[package]] name = "webpki-roots" version = "0.26.1" diff --git a/crates/sdk/Cargo.toml b/crates/sdk/Cargo.toml index c60e3a8df2..5664638e71 100644 --- a/crates/sdk/Cargo.toml +++ b/crates/sdk/Cargo.toml @@ -61,14 +61,14 @@ bs58.workspace = true indexmap.workspace = true bitflags.workspace = true enum-iterator.workspace = true +subtle = { version = "2.5" } +ethereum-types = "0.14" async_zip = { version = "0.0.17", default-features = false, features = ["deflate", "tokio"], optional = true } csv-async = { version = "1", features = ["tokio", "with_serde"], optional = true } sos-vfs = { version = "0.2.2" } aes-gcm = { version = "0.10.1", features = ["std"] } chacha20poly1305 = { version = "0.10.1", features = ["std"] } filetime = "0.2" -web3-signature = { version = "0.4", features = ["single-party"] } -web3-address = { version = "0.6", features = ["ethereum"] } argon2 = { version = "0.5", features = ["std"]} balloon-hash = { version = "0.4", features = ["std"]} app_dirs2 = "2" diff --git a/crates/sdk/src/account/archive/backup.rs b/crates/sdk/src/account/archive/backup.rs index 0eb74ab959..09e3f1999f 100644 --- a/crates/sdk/src/account/archive/backup.rs +++ b/crates/sdk/src/account/archive/backup.rs @@ -7,7 +7,6 @@ use std::{ use serde::{Deserialize, Serialize}; use tokio::io::{AsyncBufRead, AsyncSeek, BufReader}; -use web3_address::ethereum::Address; use uuid::Uuid; use walkdir::WalkDir; @@ -20,6 +19,7 @@ use crate::{ events::{EventLogExt, FolderEventLog, WriteEvent}, identity::{Identity, MemoryIdentityFolder, PublicIdentity}, sha2::{Digest, Sha256}, + signer::ecdsa::Address, vault::{ secret::SecretId, Summary, Vault, VaultAccess, VaultId, VaultWriter, }, diff --git a/crates/sdk/src/account/archive/zip.rs b/crates/sdk/src/account/archive/zip.rs index 2740816789..b7c19ae942 100644 --- a/crates/sdk/src/account/archive/zip.rs +++ b/crates/sdk/src/account/archive/zip.rs @@ -14,10 +14,9 @@ use async_zip::{ use tokio::io::{AsyncBufRead, AsyncSeek, AsyncWrite}; use tokio_util::compat::{Compat, TokioAsyncWriteCompatExt}; -use web3_address::ethereum::Address; - use crate::{ constants::{ARCHIVE_MANIFEST, FILES_DIR, VAULT_EXT}, + signer::ecdsa::Address, vault::{Header as VaultHeader, Summary, VaultId}, vfs::{self, File}, Error, Result, diff --git a/crates/sdk/src/account/preferences.rs b/crates/sdk/src/account/preferences.rs index 3bc2191040..387a26b643 100644 --- a/crates/sdk/src/account/preferences.rs +++ b/crates/sdk/src/account/preferences.rs @@ -25,6 +25,8 @@ impl CachedPreferences { accounts: &[PublicIdentity], data_dir: Option, ) -> Result<()> { + // Zero address can be used for cross-account global preferences + Self::new_account(&Address::zero(), data_dir.clone()).await?; for account in accounts { Self::new_account(account.address(), data_dir.clone()).await?; } diff --git a/crates/sdk/src/error.rs b/crates/sdk/src/error.rs index 7afec1c288..1d6670f293 100644 --- a/crates/sdk/src/error.rs +++ b/crates/sdk/src/error.rs @@ -458,6 +458,18 @@ pub enum Error { #[error("no system message for key '{0}'")] NoSysMessage(String), + /// Error generated when an address has the wrong prefix. + #[error("address must begin with 0x")] + BadAddressPrefix, + + /// Invalid length, secp256k1 signatures are 65 bytes + #[error("invalid signature length, got {0}, expected 65")] + InvalidLength(usize), + + /// Expected a recovery identifier. + #[error("recovery identifier is expected")] + RecoveryId, + /// Generic boxed error. #[error(transparent)] Boxed(#[from] Box), @@ -526,10 +538,6 @@ pub enum Error { #[error(transparent)] Json(#[from] serde_json::Error), - /// Error generated by the address library. - #[error(transparent)] - Address(#[from] web3_address::Error), - /// Error generated by the crypto library. #[error(transparent)] ChaCha(#[from] chacha20poly1305::Error), @@ -538,10 +546,6 @@ pub enum Error { #[error(transparent)] Urn(#[from] urn::Error), - /// Error generated by the signature library. - #[error(transparent)] - Signature(#[from] web3_signature::SignatureError), - /// Error generated by the password entropy library. #[error(transparent)] Zxcvbn(#[from] zxcvbn::ZxcvbnError), diff --git a/crates/sdk/src/signer/address.rs b/crates/sdk/src/signer/address.rs new file mode 100644 index 0000000000..a53da05d4c --- /dev/null +++ b/crates/sdk/src/signer/address.rs @@ -0,0 +1,218 @@ +//! Ethereum style address. +use crate::{Error, Result}; +use k256::{ + ecdsa::VerifyingKey, + elliptic_curve::{ + point::DecompressPoint, scalar::ScalarPrimitive, sec1::ToEncodedPoint, + }, + AffinePoint, EncodedPoint, FieldBytes, Scalar, Secp256k1, +}; + +use serde::{Deserialize, Serialize}; +use sha3::{Digest, Keccak256}; +use std::{fmt, str::FromStr}; +use subtle::Choice; + +/// Ethereum public address that may be converted to and from +/// a string. +/// +/// It must begin with 0x and be followed with 20 bytes hex-encoded. +#[derive(Debug, Serialize, Deserialize, Clone, Copy, Hash, Eq, PartialEq)] +#[serde(try_from = "String", into = "String")] +pub struct Address([u8; 20]); + +impl Address { + /// Zero address. + pub fn zero() -> Self { + Self([0; 20]) + } +} + +impl fmt::Display for Address { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "0x{}", hex::encode(self.0)) + } +} + +impl Default for Address { + fn default() -> Self { + Self([0u8; 20]) + } +} + +impl AsRef<[u8]> for Address { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl From<[u8; 20]> for Address { + fn from(value: [u8; 20]) -> Self { + Self(value) + } +} + +impl FromStr for Address { + type Err = Error; + + fn from_str(s: &str) -> std::result::Result { + if !s.starts_with("0x") { + return Err(Error::BadAddressPrefix); + } + let bytes = hex::decode(&s[2..])?; + let buffer: [u8; 20] = bytes.as_slice().try_into()?; + Ok(Address(buffer)) + } +} + +impl From
for String { + fn from(value: Address) -> String { + value.to_string() + } +} + +impl From
for [u8; 20] { + fn from(value: Address) -> [u8; 20] { + value.0 + } +} + +impl TryFrom for Address { + type Error = Error; + fn try_from(value: String) -> Result { +
::from_str(&value) + } +} + +impl<'a> From<&'a [u8; 64]> for Address { + fn from(bytes: &'a [u8; 64]) -> Self { + let digest = Keccak256::digest(bytes); + let final_bytes = &digest[12..]; + Self(final_bytes.try_into().unwrap()) + } +} + +impl From<[u8; 64]> for Address { + fn from(bytes: [u8; 64]) -> Self { + (&bytes).into() + } +} + +impl<'a> TryFrom<&'a VerifyingKey> for Address { + type Error = Error; + + fn try_from(key: &'a VerifyingKey) -> Result { + let point = key.to_encoded_point(true); + let bytes: [u8; 33] = point.as_bytes().try_into()?; + (&bytes).try_into() + //let bytes: [u8; 33] = key. + //to_encoded_point(true).as_bytes().try_into()?; + //bytes.into() + } +} + +impl TryFrom for Address { + type Error = Error; + fn try_from(key: VerifyingKey) -> Result { + (&key).try_into() + } +} + +impl<'a> TryFrom<&'a [u8; 33]> for Address { + type Error = Error; + fn try_from( + bytes: &'a [u8; 33], + ) -> std::result::Result { + let point = decompress(bytes)?; + let x: [u8; 32] = *point.x().unwrap().as_ref(); + let y: [u8; 32] = *point.y().unwrap().as_ref(); + let bytes: [u8; 64] = [x, y].concat().as_slice().try_into()?; + Ok((&bytes).into()) + } +} + +// FIXME: handle panics / unwrap here! + +/// Decompress the bytes for a compressed public key into a point on the secp256k1 curve. +fn decompress(compressed_bytes: &[u8; 33]) -> Result { + let y_is_odd = if compressed_bytes[0] == 3 { + Choice::from(1) + } else { + Choice::from(0) + }; + let x: &[u8; 32] = &compressed_bytes[1..].try_into()?; + let scalar_core = ScalarPrimitive::::from_slice(x)?; + let scalar = Scalar::from(scalar_core); + let x_bytes = FieldBytes::from(scalar); + let point = AffinePoint::decompress(&x_bytes, y_is_odd).unwrap(); + let point = point.to_encoded_point(false); + Ok(point) +} + +/// Compute the public address for a compressed public key. +fn address_compressed(compressed_bytes: &[u8; 33]) -> Result { + let decompressed = decompress(compressed_bytes)?; + let x: [u8; 32] = *decompressed.x().unwrap().as_ref(); + let y: [u8; 32] = *decompressed.y().unwrap().as_ref(); + let bytes: [u8; 64] = [x, y].concat().as_slice().try_into()?; + address(&bytes) +} + +/// Compute the public address for a decompressed public key. +fn address_decompressed(bytes: &[u8; 65]) -> Result { + let bytes: [u8; 64] = bytes[1..].try_into()?; + address(&bytes) +} + +/// Compute the public address for the bytes representing the x / y coordinate pair. +fn address(bytes: &[u8; 64]) -> Result { + let digest = Keccak256::digest(bytes); + let final_bytes = &digest[12..]; + Ok(format!("0x{}", hex::encode(&final_bytes))) +} + +#[cfg(test)] +mod tests { + use super::*; + use anyhow::Result; + use k256::ecdsa::SigningKey; + + const COMPRESSED_PUBLIC_KEY: &str = + "025f37d20e5b18909361e0ead7ed17c69b417bee70746c9e9c2bcb1394d921d4ae"; + const COMPRESSED_ADDRESS: &str = + "0xd09d3103ccabfb769edc3e9b01500ca7241d470a"; + + const PUBLIC_KEY: [u8; 33] = [ + 3, 191, 74, 169, 115, 14, 12, 199, 99, 221, 125, 5, 13, 247, 115, + 157, 30, 185, 140, 2, 20, 153, 10, 245, 177, 145, 111, 188, 103, 92, + 61, 227, 121, + ]; + + #[test] + fn decompress_test() -> Result<()> { + let decompressed = decompress(&PUBLIC_KEY)?; + let x = decompressed.x(); + let y = decompressed.y(); + assert!(x.is_some()); + assert!(y.is_some()); + Ok(()) + } + + #[test] + fn address_test() -> Result<()> { + let compressed_bytes = hex::decode(COMPRESSED_PUBLIC_KEY)?; + let mut bytes: [u8; 33] = [0; 33]; + bytes.copy_from_slice(&compressed_bytes[..]); + let address = address_compressed(&bytes)?; + assert_eq!(COMPRESSED_ADDRESS, address); + Ok(()) + } + + #[test] + fn verifying_key() -> Result<()> { + let key = SigningKey::random(&mut rand::thread_rng()); + let public_key = key.verifying_key(); + let address: Address = public_key.try_into()?; + Ok(()) + } +} diff --git a/crates/sdk/src/signer.rs b/crates/sdk/src/signer/mod.rs similarity index 98% rename from crates/sdk/src/signer.rs rename to crates/sdk/src/signer/mod.rs index 7079674f49..2a6517ed00 100644 --- a/crates/sdk/src/signer.rs +++ b/crates/sdk/src/signer/mod.rs @@ -3,6 +3,9 @@ use async_trait::async_trait; use crate::Result; +mod address; +mod signature; + /// Boxed signer. type BoxedSigner = Box< dyn Signer @@ -60,9 +63,9 @@ pub mod ecdsa { use sha2::Sha256; use sha3::{Digest, Keccak256}; + pub use super::address::Address; + pub use super::signature::Signature; pub use k256::ecdsa::{hazmat::SignPrimitive, SigningKey, VerifyingKey}; - pub use web3_address::ethereum::Address; - pub use web3_signature::Signature; use super::{BoxedSigner, Signer}; use crate::Result; diff --git a/crates/sdk/src/signer/signature.rs b/crates/sdk/src/signer/signature.rs new file mode 100644 index 0000000000..3036474560 --- /dev/null +++ b/crates/sdk/src/signer/signature.rs @@ -0,0 +1,257 @@ +//! Type to represent an ECDSA signature with a recovery identifier. +//! +//! Supports serde and single party signatures. +//! +//! Support for signatures generated by MPC crates will be coming soon. +#![deny(missing_docs)] +use crate::Error; +use ethereum_types::U256; +use k256::{ecdsa::RecoveryId, FieldBytes}; +use serde::{Deserialize, Serialize}; +use std::str::FromStr; +use thiserror::Error; + +/// An ECDSA signature with a recovery identifier. +/// +/// The recovery identifier may be normalized, in Electrum notation +/// or have EIP155 chain replay protection applied. +#[derive( + Default, Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Copy, +)] +pub struct Signature { + /// R value + pub r: U256, + /// S value + pub s: U256, + /// V value for the recovery identifier + pub v: u64, +} + +impl Signature { + /// Create a signature with normalized recovery identifier. + pub fn new_normalized(r: U256, s: U256, v: u64) -> Self { + debug_assert!(v == 0 || v == 1); + Self { r, s, v } + } + + /// Create a signature with electrum recovery identifier. + pub fn new_electrum(r: U256, s: U256, v: u64) -> Self { + debug_assert!(v == 27 || v == 28); + Self { r, s, v } + } + + /// Create a signature with EIP155 chain replay protection. + pub fn new_eip155(r: U256, s: U256, v: u64) -> Self { + debug_assert!(v >= 35); + Self { r, s, v } + } + + /// Is the recovery identifier for this signature in + /// the normalized form (`0` or `1`). + pub fn is_normalized(&self) -> bool { + self.v == 0 || self.v == 1 + } + + /// Is the recovery identifier for this signature in + /// the electrum form (`27` or `28`). + pub fn is_electrum(&self) -> bool { + self.v == 27 || self.v == 28 + } + + /// Is the recovery identifier for this signature in + /// the EIP155 form. + pub fn is_eip155(&self) -> bool { + self.v >= 35 + } + + /// Converts this signature into normalized form from an Electrum + /// signature. + /// + /// Panics if this signature is not in Electrum format. + pub fn normalize(self) -> Self { + assert!(self.is_electrum()); + Self { + r: self.r, + s: self.s, + v: self.v - 27, + } + } + + /// Converts this signature into normalized form from an EIP155 + /// signature. + /// + /// Panics if the signature could not be safely normalized for + /// example if a `chain_id` was supplied that would cause the + /// existing `v` value to become negative. + pub fn normalize_eip155(self, chain_id: u64) -> Self { + if self.v >= 35 + (chain_id * 2) { + Self { + r: self.r, + s: self.s, + v: self.v - chain_id * 2 - 35, + } + } else { + panic!("cannot safely normalize signature recovery identifier") + } + } + + /// Converts this signature into Electrum form. + /// + /// Panics if this signature is not in it's normalized form. + pub fn into_electrum(self) -> Self { + assert!(self.is_normalized()); + Self { + r: self.r, + s: self.s, + v: self.v + 27, + } + } + + /// Converts this signature applying + /// [EIP155](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md) + /// chain replay protection. + /// + /// Panics if this signature is not in it's normalized form. + pub fn into_eip155(self, chain_id: u64) -> Self { + assert!(self.is_normalized()); + Self { + r: self.r, + s: self.s, + v: self.v + 35 + chain_id * 2, + } + } + + /// Get the bytes for the r, s and v values. + /// + /// Panics if this signature is not normalized. + pub fn to_bytes(&self) -> [u8; 65] { + if !self.is_normalized() { + panic!("signature must be normalized to convert to byte array"); + } + + let mut out = [0u8; 64]; + let mut r: [u8; 32] = [0u8; 32]; + let mut s: [u8; 32] = [0u8; 32]; + self.r.to_big_endian(&mut r); + self.s.to_big_endian(&mut s); + let (left, right) = out.split_at_mut(32); + left.copy_from_slice(&r); + right.copy_from_slice(&s); + + let mut result = [0u8; 65]; + let (left, right) = result.split_at_mut(64); + left.copy_from_slice(&out); + right[0] = self.v as u8; + result + } +} + +impl<'a> TryFrom<&'a [u8]> for Signature { + type Error = Error; + + /// Parses a raw signature which is expected to be 65 bytes long where + /// the first 32 bytes is the `r` value, the second 32 bytes the `s` value + /// and the final byte is the `v` value in 'Electrum' notation. + fn try_from(bytes: &'a [u8]) -> Result { + if bytes.len() != 65 { + return Err(Error::InvalidLength(bytes.len())); + } + + let v = bytes[64]; + let r = U256::from_big_endian(&bytes[0..32]); + let s = U256::from_big_endian(&bytes[32..64]); + + Ok(Signature { r, s, v: v.into() }) + } +} + +impl FromStr for Signature { + type Err = Error; + + fn from_str(s: &str) -> Result { + let s = s.strip_prefix("0x").unwrap_or(s); + let bytes = hex::decode(s)?; + Signature::try_from(&bytes[..]) + } +} + +impl From<[u8; 65]> for Signature { + fn from(value: [u8; 65]) -> Self { + let r = &value[0..32]; + let s = &value[32..64]; + let v = &value[64]; + Self { + r: U256::from_big_endian(r), + s: U256::from_big_endian(s), + v: *v as u64, + } + } +} + +impl TryFrom<(k256::ecdsa::Signature, Option)> for Signature { + type Error = Error; + + fn try_from( + sig: (k256::ecdsa::Signature, Option), + ) -> Result { + let r_bytes: FieldBytes = sig.0.r().into(); + let s_bytes: FieldBytes = sig.0.s().into(); + let v: u8 = sig.1.ok_or(Error::RecoveryId)?.into(); + Ok(Self { + r: U256::from_big_endian(r_bytes.as_slice()), + s: U256::from_big_endian(s_bytes.as_slice()), + v: v as u64, + }) + } +} + +impl TryFrom for (k256::ecdsa::Signature, RecoveryId) { + type Error = Error; + fn try_from(value: Signature) -> Result { + let mut r: [u8; 32] = [0u8; 32]; + let mut s: [u8; 32] = [0u8; 32]; + value.r.to_big_endian(&mut r); + value.s.to_big_endian(&mut s); + let signature = k256::ecdsa::Signature::from_scalars(r, s)?; + let recid = RecoveryId::try_from(value.v as u8)?; + Ok((signature, recid)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn signature_into_electrum() { + let sig = Signature { + r: Default::default(), + s: Default::default(), + v: 1, + }; + let electrum = sig.into_electrum(); + assert_eq!(28, electrum.v); + } + + #[test] + fn signature_from_electrum() { + let electrum = Signature { + r: Default::default(), + s: Default::default(), + v: 37, + }; + let sig = electrum.normalize_eip155(1); + assert_eq!(0, sig.v); + } + + #[test] + fn signature_into_eip155() { + let sig = Signature { + r: Default::default(), + s: Default::default(), + v: 1, + }; + let eip155 = sig.into_eip155(1337u64); + assert_eq!(2710, eip155.v); + } +} From cf215e7326b1f5724adc874b999c16d1abd8b1a5 Mon Sep 17 00:00:00 2001 From: muji Date: Wed, 1 May 2024 11:12:02 +0800 Subject: [PATCH 2/2] Do not use zero preferences. --- crates/sdk/src/account/preferences.rs | 2 -- crates/sdk/src/signer/address.rs | 7 ------- 2 files changed, 9 deletions(-) diff --git a/crates/sdk/src/account/preferences.rs b/crates/sdk/src/account/preferences.rs index 387a26b643..3bc2191040 100644 --- a/crates/sdk/src/account/preferences.rs +++ b/crates/sdk/src/account/preferences.rs @@ -25,8 +25,6 @@ impl CachedPreferences { accounts: &[PublicIdentity], data_dir: Option, ) -> Result<()> { - // Zero address can be used for cross-account global preferences - Self::new_account(&Address::zero(), data_dir.clone()).await?; for account in accounts { Self::new_account(account.address(), data_dir.clone()).await?; } diff --git a/crates/sdk/src/signer/address.rs b/crates/sdk/src/signer/address.rs index a53da05d4c..b741aac40e 100644 --- a/crates/sdk/src/signer/address.rs +++ b/crates/sdk/src/signer/address.rs @@ -21,13 +21,6 @@ use subtle::Choice; #[serde(try_from = "String", into = "String")] pub struct Address([u8; 20]); -impl Address { - /// Zero address. - pub fn zero() -> Self { - Self([0; 20]) - } -} - impl fmt::Display for Address { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "0x{}", hex::encode(self.0))