Skip to content

Commit

Permalink
Extract EthereumSigner into a trait
Browse files Browse the repository at this point in the history
  • Loading branch information
popzxc committed Oct 20, 2020
1 parent 7e02186 commit c70acdd
Show file tree
Hide file tree
Showing 20 changed files with 230 additions and 226 deletions.
15 changes: 15 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions core/bin/zksync_eth_sender/src/ethereum_interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use web3::contract::tokens::Tokenize;
use web3::contract::Options;
use web3::transports::Http;
use zksync_basic_types::{TransactionReceipt, H256, U256};
use zksync_eth_signer::EthereumSigner;
use zksync_eth_signer::PrivateKeySigner;
// Workspace uses
use super::ExecutedTxStatus;
use std::time::Duration;
Expand Down Expand Up @@ -62,13 +62,13 @@ pub(super) trait EthereumInterface {
/// Supposed to be an actual Ethereum intermediator for the `ETHSender`.
#[derive(Debug)]
pub struct EthereumHttpClient {
eth_client: ETHClient<Http>,
eth_client: ETHClient<Http, PrivateKeySigner>,
}

impl EthereumHttpClient {
pub fn new(options: &ConfigurationOptions) -> Result<Self, anyhow::Error> {
let transport = Http::new(&options.web3_url)?;
let ethereum_signer = EthereumSigner::from_key(
let ethereum_signer = PrivateKeySigner::new(
options
.operator_private_key
.expect("Operator private key is required for eth_sender"),
Expand Down
10 changes: 5 additions & 5 deletions core/lib/eth_client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ use zksync_eth_signer::{raw_ethereum_tx::RawTransaction, EthereumSigner};
const FALLBACK_GAS_LIMIT: u64 = 3_000_000;

#[derive(Clone)]
pub struct ETHClient<T: Transport> {
eth_signer: EthereumSigner,
pub struct ETHClient<T: Transport, S: EthereumSigner> {
eth_signer: S,
pub sender_account: Address,
pub contract_addr: H160,
pub contract: ethabi::Contract,
Expand All @@ -28,7 +28,7 @@ pub struct ETHClient<T: Transport> {
pub web3: Web3<T>,
}

impl<T: Transport> fmt::Debug for ETHClient<T> {
impl<T: Transport, S: EthereumSigner> fmt::Debug for ETHClient<T, S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// We do not want to have a private key in the debug representation.

Expand All @@ -49,12 +49,12 @@ pub struct SignedCallResult {
pub hash: H256,
}

impl<T: Transport> ETHClient<T> {
impl<T: Transport, S: EthereumSigner> ETHClient<T, S> {
pub fn new(
transport: T,
contract: ethabi::Contract,
operator_eth_addr: H160,
eth_signer: EthereumSigner,
eth_signer: S,
contract_eth_addr: H160,
chain_id: u8,
gas_price_factor: f64,
Expand Down
1 change: 1 addition & 0 deletions core/lib/eth_signer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@ reqwest = { version = "0.10", features = ["json", "blocking"] }
thiserror = "1.0"

jsonrpc-core = "14.0.3"
async-trait = "0.1"
160 changes: 84 additions & 76 deletions core/lib/eth_signer/src/json_rpc_signer.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::error::{RpcSignerError, SignerError};
use crate::json_rpc_signer::messages::JsonRpcRequest;
use crate::EthereumSigner;
use crate::RawTransaction;

use jsonrpc_core::types::response::Output;
Expand All @@ -17,28 +18,103 @@ pub fn recover_eth_signer(signature: &Signature, msg: &[u8]) -> Result<Address,
Ok(public_to_address(&public_key))
}

#[derive(Clone)]
#[derive(Debug, Clone)]
pub enum AddressOrIndex {
Address(Address),
Index(usize),
}

/// Describes whether to add a prefix `\x19Ethereum Signed Message:\n`
/// when requesting a message signature.
#[derive(Clone)]
#[derive(Debug, Clone)]
pub enum SignerType {
NotNeedPrefix,
NeedPrefix,
}

#[derive(Clone)]
#[derive(Debug, Clone)]
pub struct JsonRpcSigner {
rpc_addr: String,
client: reqwest::Client,
address: Option<Address>,
signer_type: Option<SignerType>,
}

#[async_trait::async_trait]
impl EthereumSigner for JsonRpcSigner {
/// The sign method calculates an Ethereum specific signature with:
/// checks if the server adds a prefix if not then adds
/// return sign(keccak256("\x19Ethereum Signed Message:\n" + len(message) + message))).
async fn sign_message(&self, msg: &[u8]) -> Result<TxEthSignature, SignerError> {
let signature: PackedEthSignature = {
let msg = match &self.signer_type {
Some(SignerType::NotNeedPrefix) => msg.to_vec(),
Some(SignerType::NeedPrefix) => {
let prefix = format!("\x19Ethereum Signed Message:\n{}", msg.len());
let mut bytes = Vec::with_capacity(prefix.len() + msg.len());
bytes.extend_from_slice(prefix.as_bytes());
bytes.extend_from_slice(msg);

bytes
}
None => {
return Err(SignerError::MissingEthSigner);
}
};

let message = JsonRpcRequest::sign_message(self.address()?, &msg);
let ret = self
.post(&message)
.await
.map_err(|err| SignerError::SigningFailed(err.to_string()))?;
serde_json::from_value(ret)
.map_err(|err| SignerError::SigningFailed(err.to_string()))?
};

// Checks the correctness of the message signature without a prefix
if signature
.signature_recover_signer(msg)
.map_err(|_| SignerError::DefineAddress)?
== self.address()?
{
Ok(TxEthSignature::EthereumSignature(signature))
} else {
Err(SignerError::SigningFailed(
"Invalid signature from JsonRpcSigner".to_string(),
))
}
}

/// Signs and returns the RLP-encoded transaction.
async fn sign_transaction(&self, raw_tx: RawTransaction) -> Result<Vec<u8>, SignerError> {
let msg = JsonRpcRequest::sign_transaction(self.address()?, raw_tx);

let ret = self
.post(&msg)
.await
.map_err(|err| SignerError::SigningFailed(err.to_string()))?;

// get Json object and parse it to get raw Transaction
let json: Value = serde_json::from_value(ret)
.map_err(|err| SignerError::SigningFailed(err.to_string()))?;

let raw_tx: Option<&str> = json
.get("raw")
.and_then(|value| value.as_str())
.map(|value| &value["0x".len()..]);

if let Some(raw_tx) = raw_tx {
hex::decode(raw_tx).map_err(|err| SignerError::DecodeRawTxFailed(err.to_string()))
} else {
Err(SignerError::DefineAddress)
}
}

async fn get_address(&self) -> Result<Address, SignerError> {
self.address()
}
}

impl JsonRpcSigner {
pub async fn new(
rpc_addr: impl Into<String>,
Expand Down Expand Up @@ -78,6 +154,11 @@ impl JsonRpcSigner {
Ok(signer)
}

/// Get Ethereum address.
pub fn address(&self) -> Result<Address, SignerError> {
self.address.ok_or(SignerError::DefineAddress)
}

/// Specifies the Ethreum address which sets the address for which all other requests will be processed.
/// If the address has already been set, then it will all the same change to a new one.
pub async fn detect_address(
Expand Down Expand Up @@ -145,11 +226,6 @@ impl JsonRpcSigner {
}
}

/// Get Ethereum address.
pub fn address(&self) -> Result<Address, SignerError> {
self.address.ok_or(SignerError::DefineAddress)
}

/// Unlocks the current account, after that the server can sign messages and transactions.
pub async fn unlock(&self, password: &str) -> Result<(), SignerError> {
let message = JsonRpcRequest::unlock_account(self.address()?, password);
Expand All @@ -170,74 +246,6 @@ impl JsonRpcSigner {
}
}

/// The sign method calculates an Ethereum specific signature with:
/// checks if the server adds a prefix if not then adds
/// return sign(keccak256("\x19Ethereum Signed Message:\n" + len(message) + message))).
pub async fn sign_message(&self, msg: &[u8]) -> Result<TxEthSignature, SignerError> {
let signature: PackedEthSignature = {
let msg = match &self.signer_type {
Some(SignerType::NotNeedPrefix) => msg.to_vec(),
Some(SignerType::NeedPrefix) => {
let prefix = format!("\x19Ethereum Signed Message:\n{}", msg.len());
let mut bytes = Vec::with_capacity(prefix.len() + msg.len());
bytes.extend_from_slice(prefix.as_bytes());
bytes.extend_from_slice(msg);

bytes
}
None => {
return Err(SignerError::MissingEthSigner);
}
};

let message = JsonRpcRequest::sign_message(self.address()?, &msg);
let ret = self
.post(&message)
.await
.map_err(|err| SignerError::SigningFailed(err.to_string()))?;
serde_json::from_value(ret)
.map_err(|err| SignerError::SigningFailed(err.to_string()))?
};

// Checks the correctness of the message signature without a prefix
if signature
.signature_recover_signer(msg)
.map_err(|_| SignerError::DefineAddress)?
== self.address()?
{
Ok(TxEthSignature::EthereumSignature(signature))
} else {
Err(SignerError::SigningFailed(
"Invalid signature from JsonRpcSigner".to_string(),
))
}
}

/// Signs and returns the RLP-encoded transaction.
pub async fn sign_transaction(&self, raw_tx: RawTransaction) -> Result<Vec<u8>, SignerError> {
let msg = JsonRpcRequest::sign_transaction(self.address()?, raw_tx);

let ret = self
.post(&msg)
.await
.map_err(|err| SignerError::SigningFailed(err.to_string()))?;

// get Json object and parse it to get raw Transaction
let json: Value = serde_json::from_value(ret)
.map_err(|err| SignerError::SigningFailed(err.to_string()))?;

let raw_tx: Option<&str> = json
.get("raw")
.and_then(|value| value.as_str())
.map(|value| &value["0x".len()..]);

if let Some(raw_tx) = raw_tx {
hex::decode(raw_tx).map_err(|err| SignerError::DecodeRawTxFailed(err.to_string()))
} else {
Err(SignerError::DefineAddress)
}
}

/// Performs a POST query to the JSON RPC endpoint,
/// and decodes the response, returning the decoded `serde_json::Value`.
/// `Ok` is returned only for successful calls, for any kind of error
Expand Down
54 changes: 7 additions & 47 deletions core/lib/eth_signer/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
#[macro_use]
extern crate serde_derive;

use async_trait::async_trait;
use error::SignerError;
use zksync_types::tx::TxEthSignature;
use zksync_types::{Address, H256};
use zksync_types::Address;

pub use json_rpc_signer::JsonRpcSigner;
pub use pk_signer::PrivateKeySigner;
Expand All @@ -14,50 +15,9 @@ pub mod json_rpc_signer;
pub mod pk_signer;
pub mod raw_ethereum_tx;

#[derive(Clone)]
pub enum EthereumSigner {
PrivateKey(PrivateKeySigner),
JsonRpc(JsonRpcSigner),
}

impl EthereumSigner {
/// Creates a signer from a private key.
pub fn from_key(private_key: H256) -> Self {
let signer = PrivateKeySigner::new(private_key);
Self::PrivateKey(signer)
}

/// Creates a signer with JsonRpcSigner
/// who does not disclose the private key
/// while signing the necessary messages and transactions.
pub fn from_rpc(rpc_signer: JsonRpcSigner) -> Self {
Self::JsonRpc(rpc_signer)
}

/// The sign method calculates an Ethereum specific signature with:
/// sign(keccak256("\x19Ethereum Signed Message:\n" + len(message) + message))).
pub async fn sign_message(&self, message: &[u8]) -> Result<TxEthSignature, SignerError> {
match self {
EthereumSigner::PrivateKey(pk_signer) => pk_signer.sign_message(message),
EthereumSigner::JsonRpc(json_rpc_signer) => json_rpc_signer.sign_message(message).await,
}
}

/// Signs and returns the RLP-encoded transaction.
pub async fn sign_transaction(&self, raw_tx: RawTransaction) -> Result<Vec<u8>, SignerError> {
match self {
EthereumSigner::PrivateKey(pk_signer) => pk_signer.sign_transaction(raw_tx),
EthereumSigner::JsonRpc(json_rpc_signer) => {
json_rpc_signer.sign_transaction(raw_tx).await
}
}
}

/// Get Ethereum address.
pub fn get_address(&self) -> Result<Address, SignerError> {
match self {
EthereumSigner::PrivateKey(pk_signer) => pk_signer.address(),
EthereumSigner::JsonRpc(json_rpc_signer) => Ok(json_rpc_signer.address()?),
}
}
#[async_trait]
pub trait EthereumSigner {
async fn sign_message(&self, message: &[u8]) -> Result<TxEthSignature, SignerError>;
async fn sign_transaction(&self, raw_tx: RawTransaction) -> Result<Vec<u8>, SignerError>;
async fn get_address(&self) -> Result<Address, SignerError>;
}
Loading

0 comments on commit c70acdd

Please sign in to comment.