Skip to content

Commit

Permalink
feat(bridge-ui-v2): Update encoded signal proof in BridgeProver.ts (#…
Browse files Browse the repository at this point in the history
…15348)

Co-authored-by: David <david@taiko.xyz>
Co-authored-by: Daniel Wang <99078276+dantaik@users.noreply.github.com>
  • Loading branch information
3 people committed Dec 11, 2023
1 parent 8edcb3c commit 5e06cd9
Show file tree
Hide file tree
Showing 8 changed files with 73 additions and 41 deletions.
2 changes: 1 addition & 1 deletion packages/bridge-ui-v2/src/libs/bridge/ERC1155Bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ export class ERC1155Bridge extends Bridge {
log('Claiming ERC721 token with message', message);
log('Message status', messageStatus);
if (messageStatus === MessageStatus.NEW) {
const proof = await this._prover.generateProofToProcessMessage(msgHash, srcChainId, destChainId);
const proof = await this._prover.encodedSignalProof(msgHash, srcChainId, destChainId);

try {
if (message.gasLimit > bridgeService.erc1155GasLimitThreshold) {
Expand Down
2 changes: 1 addition & 1 deletion packages/bridge-ui-v2/src/libs/bridge/ERC20Bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ export class ERC20Bridge extends Bridge {
const destChainId = Number(message.destChainId);

if (messageStatus === MessageStatus.NEW) {
const proof = await this._prover.generateProofToProcessMessage(msgHash, srcChainId, destChainId);
const proof = await this._prover.encodedSignalProof(msgHash, srcChainId, destChainId);

try {
if (message.gasLimit > bridgeService.erc20GasLimitThreshold) {
Expand Down
2 changes: 1 addition & 1 deletion packages/bridge-ui-v2/src/libs/bridge/ERC721Bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ export class ERC721Bridge extends Bridge {
log('Claiming ERC721 token with message', message);
log('Message status', messageStatus);
if (messageStatus === MessageStatus.NEW) {
const proof = await this._prover.generateProofToProcessMessage(msgHash, srcChainId, destChainId);
const proof = await this._prover.encodedSignalProof(msgHash, srcChainId, destChainId);

try {
if (message.gasLimit > bridgeService.erc721GasLimitThreshold) {
Expand Down
2 changes: 1 addition & 1 deletion packages/bridge-ui-v2/src/libs/bridge/ETHBridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ export class ETHBridge extends Bridge {
const destChainId = Number(message.destChainId);

if (messageStatus === MessageStatus.NEW) {
const proof = await this._prover.generateProofToProcessMessage(msgHash, srcChainId, destChainId);
const proof = await this._prover.encodedSignalProof(msgHash, srcChainId, destChainId);

try {
txHash = await destBridgeContract.write.processMessage([message, proof]);
Expand Down
2 changes: 1 addition & 1 deletion packages/bridge-ui-v2/src/libs/bridge/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export type RelayerMessage = {
From: Address;
SrcChainId: number | string | bigint;
DestChainId: number | string | bigint;
User: Address;
Owner: Address;
To: Address;
RefundTo: Address;
Value: bigint;
Expand Down
70 changes: 46 additions & 24 deletions packages/bridge-ui-v2/src/libs/proof/BridgeProver.ts
Original file line number Diff line number Diff line change
@@ -1,68 +1,90 @@
import { encodeAbiParameters, type Hash, toHex, toRlp } from 'viem';
import { type Address, encodeAbiParameters, type Hash, type Hex, toHex } from 'viem';

import { routingContractsMap } from '$bridgeConfig';
import { MessageStatus } from '$libs/bridge';
import { InvalidProofError } from '$libs/error';

import { Prover } from './Prover';
import type { EthGetProofResponse } from './types';

export class BridgeProver extends Prover {
constructor() {
super();
}

private _getSignalProof(proof: EthGetProofResponse, blockHeight: bigint) {
// RLP encode the proof together for LibTrieProof to decode
const encodedProof = toRlp(proof.storageProof[0].proof);
// Reference: EncodedSignalProof in relayer/proof/encoded_signal_proof.go
// protocol/contracts/signal/SignalService.sol
private _encodedSignalProof(crossChainSyncAddress: Address, rlpEncodedStorageProof: Hex, blockHeight: bigint) {
type Hop = {
signalRootRelay: Address;
signalRoot: Hex;
storageProof: Hex;
};

// Encode the SignalProof struct:
// struct SignalProof {
// uint256 height;
// bytes proof;
// }
const hops: Hop[] = [];
const signalProof = encodeAbiParameters(
// ['tuple(uint256 height, bytes proof)'],
[
{
type: 'tuple',
components: [
{ name: 'height', type: 'uint256' },
{ name: 'proof', type: 'bytes' },
{ name: 'crossChainSync', type: 'address' },
{ name: 'height', type: 'uint64' },
{ name: 'storageProof', type: 'bytes' },
{
type: 'tuple[]',
name: 'hops',
components: [
{
type: 'address',
name: 'signalRootRelay',
},
{
type: 'bytes32',
name: 'signalRoot',
},
{
type: 'bytes',
name: 'storageProof',
},
],
},
],
},
],
[{ height: blockHeight, proof: encodedProof }],
[
{
crossChainSync: crossChainSyncAddress,
height: blockHeight,
storageProof: rlpEncodedStorageProof,
hops: hops,
},
],
);

return signalProof;
}

async generateProofToProcessMessage(msgHash: Hash, srcChainId: number, destChainId: number) {
async encodedSignalProof(msgHash: Hash, srcChainId: number, destChainId: number) {
const srcBridgeAddress = routingContractsMap[srcChainId][destChainId].bridgeAddress;
const srcSignalServiceAddress = routingContractsMap[srcChainId][destChainId].signalServiceAddress;
const destCrossChainSyncAddress = routingContractsMap[destChainId][srcChainId].crossChainSyncAddress;

const { proof, block } = await this.generateProof({
const { rlpEncodedStorageProof, block } = await this.encodedStorageProof({
msgHash,
clientChainId: srcChainId,
contractAddress: srcBridgeAddress,
crossChainSyncChainId: destChainId,
proofForAccountAddress: srcSignalServiceAddress,
});

// Value must be 0x1 => isSignalSent
if (proof.storageProof[0].value !== toHex(true)) {
throw new InvalidProofError('storage proof value is not 1');
}

return this._getSignalProof(proof, block.number as bigint);
return this._encodedSignalProof(destCrossChainSyncAddress, rlpEncodedStorageProof, block.number as bigint);
}

async generateProofToRelease(msgHash: Hash, srcChainId: number, destChainId: number) {
const srcBridgeAddress = routingContractsMap[srcChainId][destChainId].bridgeAddress;
const destBridgeAddress = routingContractsMap[destChainId][srcChainId].bridgeAddress;
const srcCrossChainSyncAddress = routingContractsMap[srcChainId][destChainId].crossChainSyncAddress;

const { proof, block } = await this.generateProof({
const { proof, rlpEncodedStorageProof, block } = await this.encodedStorageProof({
msgHash,
clientChainId: destChainId,
contractAddress: srcBridgeAddress,
Expand All @@ -75,6 +97,6 @@ export class BridgeProver extends Prover {
throw new InvalidProofError('storage proof value is not FAILED');
}

return this._getSignalProof(proof, block.number as bigint);
return this._encodedSignalProof(srcCrossChainSyncAddress, rlpEncodedStorageProof, block.number as bigint);
}
}
32 changes: 21 additions & 11 deletions packages/bridge-ui-v2/src/libs/proof/Prover.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { getContract, type GetContractResult, type PublicClient } from '@wagmi/core';
import { type Address, encodePacked, type Hex, keccak256 } from 'viem';
import { type Address, encodePacked, type Hex, keccak256, toHex, toRlp } from 'viem';

import { crossChainSyncABI } from '$abi';
import { routingContractsMap } from '$bridgeConfig';
import { PendingBlockError } from '$libs/error';
import { InvalidProofError, PendingBlockError } from '$libs/error';
import { getLogger } from '$libs/util/logger';
import { publicClient } from '$libs/wagmi';

Expand All @@ -12,19 +12,22 @@ import type { ClientWithEthGetProofRequest, GenerateProofArgs } from './types';
const log = getLogger('proof:Prover');

export class Prover {
protected async _getKey(contractAddress: Address, msgHash: Hex) {
return keccak256(encodePacked(['address', 'bytes32'], [contractAddress, msgHash]));
async getSignalSlot(chainId: number, contractAddress: Address, msgHash: Hex) {
return keccak256(
encodePacked(['string', 'uint64', 'address', 'bytes32'], ['SIGNAL', BigInt(chainId), contractAddress, msgHash]),
);
}

protected async _getLatestBlock(
protected async getLatestBlockFromGetSyncedSnippet(
client: PublicClient,
crossChainSyncContract: GetContractResult<typeof crossChainSyncABI>,
) {
const { blockHash } = await crossChainSyncContract.read.getSyncedSnippet([BigInt(0)]);
return client.getBlock({ blockHash });
const syncedSnippet = await crossChainSyncContract.read.getSyncedSnippet([BigInt(0)]);
const latestBlockHash = syncedSnippet['blockHash'];
return client.getBlock({ blockHash: latestBlockHash });
}

async generateProof(args: GenerateProofArgs) {
async encodedStorageProof(args: GenerateProofArgs) {
const { msgHash, clientChainId, contractAddress, crossChainSyncChainId, proofForAccountAddress } = args;

const crossChainSyncAddress = routingContractsMap[crossChainSyncChainId][clientChainId].crossChainSyncAddress;
Expand All @@ -38,13 +41,13 @@ export class Prover {
});

const client = publicClient({ chainId: clientChainId });
const block = await this._getLatestBlock(client, crossChainSyncContract);
const block = await this.getLatestBlockFromGetSyncedSnippet(client, crossChainSyncContract);

if (block.hash === null || block.number === null) {
throw new PendingBlockError('block is pending');
}

const key = await this._getKey(contractAddress, msgHash);
const key = await this.getSignalSlot(clientChainId, contractAddress, msgHash);

// Unfortunately, since this method is stagnant, it hasn't been included into Viem lib
// as supported methods. Still stupported by Alchmey, Infura and others.
Expand All @@ -68,6 +71,13 @@ export class Prover {

log('Proof from eth_getProof', proof);

return { proof, block };
if (proof.storageProof[0].value !== toHex(true)) {
throw new InvalidProofError('storage proof value is not 1');
}

// RLP encode the proof together for LibTrieProof to decode
const rlpEncodedStorageProof = toRlp(proof.storageProof[0].proof);

return { proof, rlpEncodedStorageProof, block };
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ export class RelayerAPIService {
to: tx.data.Message.To,
data,
memo: tx.data.Message.Memo,
owner: tx.data.Message.User,
owner: tx.data.Message.Owner,
from: tx.data.Message.From,
gasLimit: BigInt(tx.data.Message.GasLimit),
value: BigInt(tx.data.Message.Value),
Expand Down

0 comments on commit 5e06cd9

Please sign in to comment.