Golden Gate — Trustless-Bridging Ethereum (EVM) Blockchains — Part 2: Transaction Replay

struct Transaction {
uint256 nonce;
uint256 gasPrice;
uint256 gasLimit;
address to;
uint256 value;
bytes data;
uint8 v;
bytes32 r;
bytes32 s;
}
  1. same contract address (transaction’s to field)
  2. same account address (transaction’s from field)
  3. same account nonce (number of transactions sent for EOAs / number of contracts created for contracts), checked by miners
  4. with EIP-155, same chain id, which is included in the signed transaction data
  • it will need to know how to interface with the Proxy
  • msg.sender will now be the Proxy address, so the original msg.sender must be additionally provided by the Proxy
  • the Proxy will be given administrative rights to change state and it will be the only mechanism through which state is changed
  • get the transaction and receipt proofs, along with the header of the block in which the transaction was mined.
  • send the proofs to the Proxy-Prover smart contract. You can see the entire logic of the forwardAndVerify function here.
function forwardAndVerify (
EthereumDecoder.BlockHeader memory header,
MPT.MerkleProof memory accountdata,
MPT.MerkleProof memory txdata,
MPT.MerkleProof memory receiptdata,
uint256 chainId
) public returns (bytes memory);
  • check the block header & hash validity as shown in Part 1
(bool valid, string memory reason) = verifyHeader(header);
if (!valid) revert(reason);
(valid, reason) = verifyTransaction(header, txdata);
if (!valid) revert(reason);
(valid, reason) = verifyReceipt(header, receiptdata);
if (!valid) revert(reason);
(valid, reason) = verifyAccount(header, accountdata);
if (!valid) revert(reason);
  • check transaction and receipt have the same index (the key used for each of the proofs) and therefore are directly correlated.
require(
keccak256(txdata.key) == keccak256(receiptdata.key),
"Transaction & receipt index must be the same."
);
  • Decode RLP-encoded transaction, receipt, and account data. The transaction data is needed for retrieving the to field — the address of the contract to which we forward the transaction. The receipt contains the status field, which tells us if the transaction must be successful or not. The account data contains the account nonce.
Account memory account = toAccount(
accountdata.expectedValue
);
Transaction memory transaction = toTransaction(
txdata.expectedValue
);
TransactionReceipt memory receipt = toReceipt(
receiptdata.expectedValue
);
  • recover the sender address and account nonce from the signed transaction data and verify that the nonce is what we expect. We are effectively synchronizing the account nonce from chain A, with chain B in order to protect from replaying the same transaction multiple times. This nonce can be kept per each account, per each chain we are synchronizing with. It is updated regardless of the success/fail status of the transaction.
address sender = getTransactionSender(txdata, chainId);
if (accountNonces[sender] > 0) {
require(
account.nonce == accountNonces[sender] + 1,
"Account nonce out of sync"
);
}
accountNonces[sender] = account.nonce;
  • We can even check that the receiving contract has the same code on chain B as it has on chain A, by comparing the account’s codeHash with the target contract codeHash
bytes32 codeHash;
address target = transaction.to;
assembly {
codeHash := extcodehash(target)
}
require(account.codeHash == codeHash);
  • execute the call and check that it has the same status as the receipt
(bool success, bytes memory data) = transaction.to.call{
value: transaction.value,
gas: transaction.gasLimit
}(transaction.data);
uint8 _success = success ? uint8(1) : uint8(0);
require(
_success == receipt.status,
"Diverged transaction status"
);
return data;

Transaction hygiene

Shadow payments

  • lower value chain payment precedes higher value chain payment. One can pay on a testnet as a promise to buy a product.
  • lower value chain payment postcedes higher value chain payment. One can send a payment on the mainnet, use the proof for that payment on a testnet, and process further logic.

Other mechanisms

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store