Skip to content

Commit

Permalink
Add workaround for tx batches
Browse files Browse the repository at this point in the history
  • Loading branch information
alekseysidorov committed Jan 26, 2021
1 parent 4a35d8d commit d3c9a8a
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 40 deletions.
128 changes: 88 additions & 40 deletions core/bin/zksync_api/src/api_server/tx_sender.rs
Original file line number Diff line number Diff line change
Expand Up @@ -295,36 +295,17 @@ impl TxSender {
let mut verified_txs = Vec::new();
let mut verified_signature = None;

let mut messages_to_sign = vec![];
for tx in &txs {
messages_to_sign.push(self.tx_message_to_sign(&tx.0).await?);
}

if let Some(signature) = eth_signature {
// User provided the signature for the whole batch.
let (verified_batch, sign_data) = verify_txs_batch_signature(
txs,
signature,
messages_to_sign,
self.sign_verify_requests.clone(),
)
.await?
.unwrap_batch();
let (verified_batch, sign_data) =
self.glm_verify_txs_batch_info(txs, signature).await?;

verified_signature = Some(sign_data.signature);
verified_txs.extend(verified_batch.into_iter());
} else {
// Otherwise, we process every transaction in turn.
for (tx, msg_to_sign) in txs.into_iter().zip(messages_to_sign.into_iter()) {
let verified_tx = verify_tx_info_message_signature(
&tx.0,
tx.1.clone(),
msg_to_sign,
self.sign_verify_requests.clone(),
)
.await?
.unwrap_tx();

for (tx, signature) in txs {
let verified_tx = self.glm_verify_tx_info(&tx, signature).await?;
verified_txs.push(verified_tx);
}
}
Expand Down Expand Up @@ -382,6 +363,7 @@ impl TxSender {
/// Returns a message that user has to sign to send the transaction.
/// If the transaction doesn't need a message signature, returns `None`.
/// If any error is encountered during the message generation, returns `jsonrpc_core::Error`.
#[allow(dead_code)]
async fn tx_message_to_sign(&self, tx: &ZkSyncTx) -> Result<Option<Vec<u8>>, SubmitError> {
Ok(match tx {
ZkSyncTx::Transfer(tx) => {
Expand Down Expand Up @@ -504,6 +486,55 @@ impl TxSender {
})
}

fn glm_force_tx_message_to_sign(&self, tx: &ZkSyncTx, decimals: u8) -> Option<Vec<u8>> {
match tx {
ZkSyncTx::Transfer(tx) => {
let msg = tx.get_ethereum_sign_message("tGLM", decimals).into_bytes();
Some(msg)
}

ZkSyncTx::Withdraw(tx) => {
let msg = tx.get_ethereum_sign_message("tGLM", decimals).into_bytes();
Some(msg)
}

_ => None,
}
}

fn glm_force_txs_batch_message_to_sign(
&self,
txs: &[(ZkSyncTx, Option<TxEthSignature>)],
decimals: u8,
) -> Vec<Option<Vec<u8>>> {
txs.iter()
.map(|(tx, _)| self.glm_force_tx_message_to_sign(tx, decimals))
.collect()
}

async fn glm_txs_batch_message_to_sign(
&self,
txs: &[(ZkSyncTx, Option<TxEthSignature>)],
) -> Result<(Option<Token>, Vec<Option<Vec<u8>>>), SubmitError> {
let mut messages_to_sign = vec![];
let mut batch_token = None;

for tx in txs {
let msg = match self.glm_tx_message_to_sign(&tx.0).await? {
Some((token, msg)) => {
if batch_token.is_none() {
batch_token = Some(token.clone());
}
Some(msg)
}
None => None,
};
messages_to_sign.push(msg);
}

Ok((batch_token, messages_to_sign))
}

async fn glm_verify_tx_info(
&self,
tx: &ZkSyncTx,
Expand All @@ -522,23 +553,7 @@ impl TxSender {
Ok(tx) => Ok(tx.unwrap_tx()),
Err(err) => {
if token.symbol == "GNT" {
let msg_to_sign = match tx {
ZkSyncTx::Transfer(tx) => {
let msg = tx
.get_ethereum_sign_message("tGLM", token.decimals)
.into_bytes();
Some(msg)
}

ZkSyncTx::Withdraw(tx) => {
let msg = tx
.get_ethereum_sign_message("tGLM", token.decimals)
.into_bytes();
Some(msg)
}

_ => unreachable!(),
};
let msg_to_sign = self.glm_force_tx_message_to_sign(tx, token.decimals);

let verified_tx = verify_tx_info_message_signature(
&tx,
Expand Down Expand Up @@ -568,6 +583,39 @@ impl TxSender {
Ok(verified_tx)
}
}

async fn glm_verify_txs_batch_info(
&self,
batch: Vec<(ZkSyncTx, Option<TxEthSignature>)>,
signature: TxEthSignature,
) -> Result<(Vec<SignedZkSyncTx>, EthSignData), SubmitError> {
let (batch_token, messages_to_sign) = self.glm_txs_batch_message_to_sign(&batch).await?;

let verify_result = verify_txs_batch_signature(
batch.clone(),
signature.clone(),
messages_to_sign,
self.sign_verify_requests.clone(),
)
.await;

match (verify_result, batch_token) {
(Ok(tx), _) => Ok(tx.unwrap_batch()),
(Err(_), Some(token)) if token.symbol == "GNT" => {
let messages_to_sign =
self.glm_force_txs_batch_message_to_sign(&batch, token.decimals);
Ok(verify_txs_batch_signature(
batch,
signature,
messages_to_sign,
self.sign_verify_requests.clone(),
)
.await?
.unwrap_batch())
}
(Err(e), _) => Err(e),
}
}
}

async fn send_verify_request_and_recv(
Expand Down
36 changes: 36 additions & 0 deletions sdk/zksync-rs/tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,42 @@ async fn comprehensive_test() -> Result<(), anyhow::Error> {
)
.await?;

// Test batch with single transaction
let total_fee = alice_wallet2
.provider
.get_tx_fee(TxFeeTypes::Transfer, bob_wallet1.address(), "tGLM")
.await?
.total_fee;

let mut nonce = alice_wallet2.account_info().await?.committed.nonce;
let (transfer, signature) = alice_wallet2
.signer
.sign_transfer(
token_tglm.clone(),
20_000_000_000_000_000_000u128.into(),
total_fee,
bob_wallet1.address(),
nonce,
)
.await?;
*nonce += 1;

let signed_transfers = vec![(
ZkSyncTx::Transfer(Box::new(transfer.clone())),
signature.clone(),
)];

let tx_hashes = alice_wallet2
.provider
.send_txs_batch(signed_transfers, None)
.await?;
let batch_handle = SyncTransactionHandle::new(tx_hashes[0], alice_wallet2.provider.clone());

batch_handle
.commit_timeout(Duration::from_secs(180))
.wait_for_commit()
.await?;

Ok(())
}

Expand Down

0 comments on commit d3c9a8a

Please sign in to comment.