Skip to content

Commit d103364

Browse files
committed
Merge branch 'aip-61-adex-v5' into channel-list-filter-by-chain
2 parents e50c489 + e616efd commit d103364

File tree

8 files changed

+386
-627
lines changed

8 files changed

+386
-627
lines changed

Cargo.lock

Lines changed: 243 additions & 531 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

adapter/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ primitives = { path = "../primitives" }
2020

2121
# For Ethereum client
2222
web3 = { version = "0.18", features = ["http-tls", "signing"] }
23-
ethstore = { git = "https://github.com/openethereum/openethereum", tag = "v3.1.1-rc.1" }
23+
ethsign = "0.8"
2424
create2 = "0.0.2"
2525

2626
# For Dummy client

adapter/src/client.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@ pub trait Locked: Sync + Send {
1616
/// Get Adapter whoami
1717
fn whoami(&self) -> ValidatorId;
1818

19-
/// Verify, based on the signature & state_root, that the signer is the same
19+
/// Verify, based on the `signature` & `state_root`, that the `signer` is the same.
20+
///
21+
/// `signature` should be a `0x` prefixed hex string.
22+
///
23+
/// `state_root` should be a hex string, with no `0x` prefix.
2024
fn verify(
2125
&self,
2226
signer: ValidatorId,

adapter/src/ethereum.rs

Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
//! The [`Ethereum`] client for the [`Adapter`].
22
3-
use ethstore::{ethkey::Password, SafeAccount};
3+
use ethsign::{KeyFile, Protected, SecretKey, Signature};
44

55
use once_cell::sync::Lazy;
6-
use serde_json::Value;
76
use web3::signing::keccak256;
87

98
use crate::{Adapter, LockedState, UnlockedState};
@@ -65,15 +64,67 @@ fn to_ethereum_signed(message: &[u8]) -> [u8; 32] {
6564
keccak256(&bytes)
6665
}
6766

67+
/// Trait for encoding a signature into RSV array
68+
/// and V altered to be in "Electrum" notation, i.e. `v += 27`
69+
pub trait Electrum {
70+
/// Encode the signature into RSV array (V altered to be in "Electrum" notation).
71+
///`{r}{s}{v}`
72+
///
73+
///
74+
fn to_electrum(&self) -> [u8; 65];
75+
76+
/// Parse bytes as a signature encoded as RSV (V in "Electrum" notation).
77+
/// `{r}{s}{v}`
78+
///
79+
/// Will return `None` if given data has invalid length or
80+
/// if `V` (byte 64) component is not in electrum notation (`< 27`)
81+
fn from_electrum(data: &[u8]) -> Option<Self>
82+
where
83+
Self: Sized;
84+
}
85+
86+
impl Electrum for Signature {
87+
fn to_electrum(&self) -> [u8; 65] {
88+
let mut electrum_array = [0_u8; 65];
89+
90+
// R
91+
electrum_array[0..32].copy_from_slice(&self.r);
92+
// S
93+
electrum_array[32..64].copy_from_slice(&self.s);
94+
// V altered to be in "Electrum" notation
95+
electrum_array[64] = self.v + 27;
96+
97+
electrum_array
98+
}
99+
100+
fn from_electrum(sig: &[u8]) -> Option<Self> {
101+
if sig.len() != 65 || sig[64] < 27 {
102+
return None;
103+
}
104+
105+
let mut r = [0u8; 32];
106+
r.copy_from_slice(&sig[0..32]);
107+
108+
let mut s = [0u8; 32];
109+
s.copy_from_slice(&sig[32..64]);
110+
111+
let v = sig[64] - 27;
112+
113+
Some(Signature { v, r, s })
114+
}
115+
}
116+
68117
#[derive(Debug, Clone)]
69118
pub struct UnlockedWallet {
70-
wallet: SafeAccount,
71-
password: Password,
119+
wallet: SecretKey,
72120
}
73121

74122
#[derive(Debug, Clone)]
75123
pub enum LockedWallet {
76-
KeyStore { keystore: Value, password: Password },
124+
KeyStore {
125+
keystore: KeyFile,
126+
password: Protected,
127+
},
77128
PrivateKey(String),
78129
}
79130

adapter/src/ethereum/client.rs

Lines changed: 35 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,16 @@ use crate::{
77
use async_trait::async_trait;
88
use chrono::Utc;
99
use create2::calc_addr;
10-
use ethstore::{
11-
ethkey::{verify_address, Message, Signature},
12-
SafeAccount,
13-
};
10+
use ethsign::{KeyFile, Signature};
1411
use primitives::{Address, BigNum, Chain, ChainId, ChainOf, Channel, Config, ValidatorId};
1512

1613
use super::{
1714
channel::EthereumChannel,
1815
error::{Error, EwtSigningError, KeystoreError, VerifyError},
1916
ewt::{self, Payload},
20-
to_ethereum_signed, LockedWallet, UnlockedWallet, WalletState, DEPOSITOR_BYTECODE_DECODED,
21-
ERC20_ABI, IDENTITY_ABI, OUTPACE_ABI, SWEEPER_ABI,
17+
to_ethereum_signed, Electrum, LockedWallet, UnlockedWallet, WalletState,
18+
DEPOSITOR_BYTECODE_DECODED, ERC20_ABI, IDENTITY_ABI, OUTPACE_ABI, SWEEPER_ABI,
2219
};
23-
use serde_json::Value;
2420
use web3::{
2521
contract::{Contract, Options as ContractOptions},
2622
ethabi::{encode, Token},
@@ -80,22 +76,18 @@ impl Ethereum<LockedWallet> {
8076
pub fn init(opts: Options, config: &Config) -> Result<Self, Error> {
8177
let keystore_contents =
8278
fs::read_to_string(&opts.keystore_file).map_err(KeystoreError::ReadingFile)?;
83-
let keystore_json: Value =
79+
let keystore_json: KeyFile =
8480
serde_json::from_str(&keystore_contents).map_err(KeystoreError::Deserialization)?;
8581

86-
let address = {
87-
let keystore_address = keystore_json
88-
.get("address")
89-
.and_then(|value| value.as_str())
90-
.ok_or(KeystoreError::AddressMissing)?;
82+
let address_bytes = keystore_json
83+
.address
84+
.clone()
85+
.ok_or(KeystoreError::AddressMissing)?;
9186

92-
keystore_address
93-
.parse()
94-
.map_err(KeystoreError::AddressInvalid)
95-
}?;
87+
let address = Address::from_slice(&address_bytes.0).ok_or(KeystoreError::AddressLength)?;
9688

9789
Ok(Self {
98-
address,
90+
address: ValidatorId::from(address),
9991
config: config.to_owned(),
10092
state: LockedWallet::KeyStore {
10193
keystore: keystore_json,
@@ -160,16 +152,9 @@ impl Unlockable for Ethereum<LockedWallet> {
160152
fn unlock(&self) -> Result<Ethereum<UnlockedWallet>, <Self::Unlocked as Locked>::Error> {
161153
let unlocked_wallet = match &self.state {
162154
LockedWallet::KeyStore { keystore, password } => {
163-
let json = serde_json::from_value(keystore.clone())
164-
.map_err(KeystoreError::Deserialization)?;
165-
166-
let wallet = SafeAccount::from_file(json, None, &Some(password.clone()))
167-
.map_err(|err| Error::WalletUnlock(err.to_string()))?;
155+
let wallet = keystore.to_secret_key(password)?;
168156

169-
UnlockedWallet {
170-
wallet,
171-
password: password.clone(),
172-
}
157+
UnlockedWallet { wallet }
173158
}
174159
LockedWallet::PrivateKey(_priv_key) => todo!(),
175160
};
@@ -201,15 +186,19 @@ impl<S: WalletState> Locked for Ethereum<S> {
201186
}
202187
let decoded_signature =
203188
hex::decode(&signature[2..]).map_err(VerifyError::SignatureDecoding)?;
204-
let address = ethstore::ethkey::Address::from(*signer.as_bytes());
205-
let signature = Signature::from_electrum(&decoded_signature);
189+
190+
let signature =
191+
Signature::from_electrum(&decoded_signature).ok_or(VerifyError::SignatureInvalid)?;
206192
let state_root = hex::decode(state_root).map_err(VerifyError::StateRootDecoding)?;
207-
let message = Message::from(to_ethereum_signed(&state_root));
208193

209-
let verify_address = verify_address(&address, &signature, &message)
210-
.map_err(VerifyError::PublicKeyRecovery)?;
194+
let message = to_ethereum_signed(&state_root);
211195

212-
Ok(verify_address)
196+
// recover the public key using the signature and the eth sign message
197+
let public_key = signature
198+
.recover(&message)
199+
.map_err(|ec_err| VerifyError::PublicKeyRecovery(ec_err.to_string()))?;
200+
201+
Ok(public_key.address() == signer.as_bytes())
213202
}
214203

215204
/// Creates a `Session` from a provided Token by calling the Contract.
@@ -358,16 +347,16 @@ impl<S: WalletState> Locked for Ethereum<S> {
358347
impl Unlocked for Ethereum<UnlockedWallet> {
359348
fn sign(&self, state_root: &str) -> Result<String, Error> {
360349
let state_root = hex::decode(state_root).map_err(VerifyError::StateRootDecoding)?;
361-
let message = Message::from(to_ethereum_signed(&state_root));
350+
let message = to_ethereum_signed(&state_root);
351+
362352
let wallet_sign = self
363353
.state
364354
.wallet
365-
.sign(&self.state.password, &message)
355+
.sign(&message)
366356
// TODO: This is not entirely true, we do not sign an Ethereum Web Token but Outpace state_root
367357
.map_err(|err| EwtSigningError::SigningMessage(err.to_string()))?;
368-
let signature: Signature = wallet_sign.into_electrum().into();
369358

370-
Ok(format!("0x{}", signature))
359+
Ok(format!("0x{}", hex::encode(wallet_sign.to_electrum())))
371360
}
372361

373362
fn get_auth(&self, for_chain: ChainId, intended_for: ValidatorId) -> Result<String, Error> {
@@ -380,8 +369,7 @@ impl Unlocked for Ethereum<UnlockedWallet> {
380369
chain_id: for_chain,
381370
};
382371

383-
let token = ewt::Token::sign(&self.state.wallet, &self.state.password, payload)
384-
.map_err(Error::SignMessage)?;
372+
let token = ewt::Token::sign(&self.state.wallet, payload).map_err(Error::SignMessage)?;
385373

386374
Ok(token.to_string())
387375
}
@@ -395,15 +383,14 @@ mod test {
395383
ewt::{self, Payload},
396384
get_counterfactual_address,
397385
test_util::*,
398-
to_ethereum_signed,
386+
to_ethereum_signed, Electrum,
399387
};
400388

401389
use crate::{
402390
prelude::*,
403391
primitives::{Deposit, Session},
404392
};
405393
use chrono::Utc;
406-
use ethstore::ethkey::Message;
407394

408395
use primitives::{
409396
channel::Nonce,
@@ -509,14 +496,13 @@ mod test {
509496

510497
let signature_actual = {
511498
let ethers_sign_message = to_ethereum_signed(&msg_hash_actual);
512-
let message = Message::from(ethers_sign_message);
513499

514500
let mut signature = user_adapter
515501
.state
516502
.wallet
517-
.sign(&user_adapter.state.password, &message)
503+
.sign(&ethers_sign_message)
518504
.expect("Should sign message")
519-
.into_electrum()
505+
.to_electrum()
520506
.to_vec();
521507

522508
signature.extend(ETH_SIGN_SUFFIX.as_slice());
@@ -549,14 +535,13 @@ mod test {
549535

550536
let signature_actual = {
551537
let ethers_sign_message = to_ethereum_signed(&msg_hash_actual);
552-
let message = Message::from(ethers_sign_message);
553538

554539
let mut signature = evil_adapter
555540
.state
556541
.wallet
557-
.sign(&evil_adapter.state.password, &message)
542+
.sign(&ethers_sign_message)
558543
.expect("Should sign message")
559-
.into_electrum()
544+
.to_electrum()
560545
.to_vec();
561546

562547
signature.extend(ETH_SIGN_SUFFIX.as_slice());
@@ -631,7 +616,7 @@ mod test {
631616
chain_id: ganache_chain.chain_id,
632617
};
633618

634-
let auth_token = ewt::Token::sign(&adapter.state.wallet, &adapter.state.password, payload)
619+
let auth_token = ewt::Token::sign(&adapter.state.wallet, payload)
635620
.expect("Should sign successfully the Payload");
636621

637622
let has_privileges = adapter
@@ -684,12 +669,8 @@ mod test {
684669
chain_id: ganache_chain.chain_id,
685670
};
686671

687-
let token = ewt::Token::sign(
688-
&signer_adapter.state.wallet,
689-
&signer_adapter.state.password,
690-
payload,
691-
)
692-
.expect("Should sign successfully the Payload");
672+
let token = ewt::Token::sign(&signer_adapter.state.wallet, payload)
673+
.expect("Should sign successfully the Payload");
693674

694675
// double check that we have privileges for _Who Am I_
695676
assert!(adapter

adapter/src/ethereum/error.rs

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,7 @@ pub enum Error {
3737
#[error("Keystore: {0}")]
3838
Keystore(#[from] KeystoreError),
3939
#[error("Wallet unlocking: {0}")]
40-
/// Since [`ethstore::Error`] is not [`Sync`] we must use a [`String`] instead.
41-
WalletUnlock(String),
40+
WalletUnlock(#[from] ethsign::Error),
4241
#[error("Web3: {0}")]
4342
Web3(#[from] web3::Error),
4443
/// When the ChannelId that we get from hashing the EthereumChannel with the contract address
@@ -82,13 +81,16 @@ pub enum Error {
8281
/// (signer, state_root, signature) **doesn't align**.
8382
pub enum VerifyError {
8483
#[error("Recovering the public key from the signature: {0}")]
85-
PublicKeyRecovery(#[from] ethstore::ethkey::Error),
84+
/// `secp256k1` error
85+
PublicKeyRecovery(String),
8686
#[error("Decoding state root: {0}")]
8787
StateRootDecoding(#[source] hex::FromHexError),
8888
#[error("Decoding signature: {0}")]
8989
SignatureDecoding(#[source] hex::FromHexError),
9090
#[error("Signature is not prefixed with `0x`")]
9191
SignatureNotPrefixed,
92+
#[error("Signature length or V component of the Signature was incorrect")]
93+
SignatureInvalid,
9294
}
9395

9496
#[derive(Debug, Error)]
@@ -97,8 +99,8 @@ pub enum KeystoreError {
9799
#[error("\"address\" key missing in keystore file")]
98100
AddressMissing,
99101
/// The `address` key in the keystore file is not a valid `ValidatorId`
100-
#[error("\"address\" is invalid: {0}")]
101-
AddressInvalid(#[source] primitives::address::Error),
102+
#[error("\"address\" length should be 20 bytes")]
103+
AddressLength,
102104
/// reading the keystore file failed
103105
#[error("Reading keystore file: {0}")]
104106
ReadingFile(#[source] std::io::Error),
@@ -113,7 +115,8 @@ pub enum EwtSigningError {
113115
HeaderSerialization(#[source] serde_json::Error),
114116
#[error("Payload serialization: {0}")]
115117
PayloadSerialization(#[source] serde_json::Error),
116-
/// Since [`ethstore::Error`] is not [`Sync`] we must use a [`String`] instead.
118+
/// we must use a [`String`] since [`ethsign`] does not export the error from
119+
/// the `secp256k1` crate which is returned in the [`ethsign::SecretKey::sign()`] method.
117120
#[error("Signing message: {0}")]
118121
SigningMessage(String),
119122
#[error("Decoding hex of Signature: {0}")]
@@ -128,13 +131,15 @@ pub enum EwtVerifyError {
128131
InvalidTokenLength,
129132
#[error("The token does not comply to the format of header.payload.signature")]
130133
InvalidToken,
134+
/// We use a `String` because [`ethsign::Signature::recover()`] returns `secp256k1` error
135+
/// which is not exported in `ethsign`.
131136
#[error("Address recovery: {0}")]
132-
AddressRecovery(#[from] ethstore::ethkey::Error),
137+
AddressRecovery(String),
133138
#[error("Signature decoding: {0}")]
134139
SignatureDecoding(#[source] base64::DecodeError),
135-
/// When token is decoded but creating a Signature results in empty Signature.
136-
/// Signature is encoded as RSV (V in "Electrum" notation)
137-
/// See [`Signature::from_electrum`]
140+
/// If there is no suffix in the signature for the mode
141+
/// or if Signature length is not 65 bytes
142+
/// or if Signature V component is not in "Electrum" notation (`< 27`).
138143
#[error("Error when decoding token signature")]
139144
InvalidSignature,
140145
#[error("Payload decoding: {0}")]

0 commit comments

Comments
 (0)