Skip to content

Commit

Permalink
Merge pull request #64 from base-org/jack/handle-hashi-mock-proofs
Browse files Browse the repository at this point in the history
update ts-filler to handle mock proofs for hashi
  • Loading branch information
jackchuma authored Dec 27, 2024
2 parents 4636ae0 + dc9adfc commit 21ad0a7
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 8 deletions.
2 changes: 1 addition & 1 deletion services/ts-filler/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ bun run index.ts

## Docker Support

The `ts-filler` service can also be run inside a Docker container. The provided `docker-compose.tml` file sets up the environment and installs the necessary dependencies.
The `ts-filler` service can also be run inside a Docker container. The provided `docker-compose.yml` file sets up the environment and installs the necessary dependencies.

To build and run the Docker container, use the following command:

Expand Down
23 changes: 23 additions & 0 deletions services/ts-filler/src/abis/ShoyuBashi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
export default [
{
type: "function",
name: "getThresholdHash",
inputs: [
{ name: "domain", type: "uint256", internalType: "uint256" },
{ name: "id", type: "uint256", internalType: "uint256" },
],
outputs: [{ name: "", type: "bytes32", internalType: "bytes32" }],
stateMutability: "view",
},
{
type: "function",
name: "setHash",
inputs: [
{ name: "domain", type: "uint256", internalType: "uint256" },
{ name: "id", type: "uint256", internalType: "uint256" },
{ name: "hash", type: "bytes32", internalType: "bytes32" },
],
outputs: [],
stateMutability: "nonpayable",
},
] as const;
14 changes: 10 additions & 4 deletions services/ts-filler/src/chain/chain.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,10 +179,16 @@ export default class ChainService {
}

private async getL2BlockNumber(l1BlockNumber?: bigint): Promise<bigint> {
if (!this.usingHashi) {
if (!l1BlockNumber) {
throw new Error("Block number is required");
}
if (this.usingHashi) {
// NOTE: This is only for a proof of concept. We have a mock shoyu bashi contract that allows us to directly set the block hash for the l2 block number.
// In production, more sophisticated logic will be needed to determine the latest block number accounted for in the Hashi system.
return await exponentialBackoff(
async () => await this.activeChains.dst.publicClient.getBlockNumber()
);
}

if (!l1BlockNumber) {
throw new Error("Block number is required");
}

const config = this.activeChains.l1;
Expand Down
12 changes: 12 additions & 0 deletions services/ts-filler/src/common/utils/attributes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const REWARD_ATTRIBUTE_SELECTOR = "0xa362e5db";
const DELAY_ATTRIBUTE_SELECTOR = "0x84f550e0";
const FULFILLER_ATTRIBUTE_SELECTOR = "0x138a03fc";
const L2_ORACLE_ATTRIBUTE_SELECTOR = "0x7ff7245a";
const SHOYU_BASHI_ATTRIBUTE_SELECTOR = "0xda07e15d";

export default class Attributes {
constructor(private attributes: Hex[]) {}
Expand Down Expand Up @@ -51,6 +52,17 @@ export default class Attributes {
};
}

getShoyuBashi(): Hex {
const attribute = this.getAttribute(SHOYU_BASHI_ATTRIBUTE_SELECTOR);

const [hash] = decodeAbiParameters(
[{ type: "bytes32" }],
("0x" + attribute.slice(10)) as Hex
);

return hash;
}

setFulfiller(fulfiller: Address): void {
this.attributes.push(
`${FULFILLER_ATTRIBUTE_SELECTOR}${fulfiller.slice(2).padStart(64, "0")}`
Expand Down
28 changes: 27 additions & 1 deletion services/ts-filler/src/prover/prover.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ export default class ProverService {
}

async generateProof(requestHash: Address): Promise<ProofType> {
const { proof } = await this.generateProofWithL2Block(requestHash);
return proof;
}

async generateProofWithL2Block(
requestHash: Address
): Promise<{ proof: ProofType; l2Block: Block }> {
let beaconData: GetBeaconRootAndL2TimestampReturnType | undefined;
let l1BlockNumber: bigint | undefined;
let stateRootInclusion: StateRootProofReturnType | undefined;
Expand Down Expand Up @@ -89,7 +96,7 @@ export default class ProverService {
};
const storageProofs = await this.getStorageProofs(storageProofOpts);

return this.storeProofObj(
const proof = this.storeProofObj(
storageProofs,
l2Block,
beaconData,
Expand All @@ -98,6 +105,8 @@ export default class ProverService {
parentAssertionHash,
afterInboxBatchAcc
);

return { proof, l2Block };
}

private getExecutionStateRootProof(block: any): StateRootProofReturnType {
Expand Down Expand Up @@ -336,6 +345,13 @@ export default class ProverService {
throw new Error("Storage proof is required for Arbitrum proofs");
}

if (proofs.storageProof.storageProof[0].value === 0n) {
throw new Error("Storage proof value is 0");
}
if (proofs.l2StorageProof.storageProof[0].value === 0n) {
throw new Error("L2 storage proof value is 0");
}

return {
stateProofParams: {
beaconRoot: beaconData.beaconRoot,
Expand Down Expand Up @@ -380,6 +396,12 @@ export default class ProverService {
if (!proofs.storageProof) {
throw new Error("Storage proof is required for OPStack proofs");
}
if (proofs.storageProof.storageProof[0].value === 0n) {
throw new Error("Storage proof value is 0");
}
if (proofs.l2StorageProof.storageProof[0].value === 0n) {
throw new Error("L2 storage proof value is 0");
}

return {
l2MessagePasserStorageRoot:
Expand Down Expand Up @@ -411,6 +433,10 @@ export default class ProverService {
}

private buildHashiProof(proofs: Proofs, l2Block: Block): HashiProofType {
if (proofs.l2StorageProof.storageProof[0].value === 0n) {
throw new Error("L2 storage proof value is 0");
}

return {
rlpEncodedBlockHeader: this.getEncodedBlockArray(l2Block),
dstAccountProofParams: {
Expand Down
23 changes: 21 additions & 2 deletions services/ts-filler/src/rewards/monitor.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import type {
import CAIP10 from "../common/utils/caip10";
import HashiProof from "../abis/HashiProof";
import Attributes from "../common/utils/attributes";
import ShoyuBashi from "../abis/ShoyuBashi";
import bytes32ToAddress from "../common/utils/bytes32ToAddress";

export default class RewardMonitorService {
private processing = false;
Expand Down Expand Up @@ -69,11 +71,28 @@ export default class RewardMonitorService {
);
const { requestHash, sender, receiver, payload, attributes } = job;

const proof = await proverService.generateProof(requestHash);
const payTo = signerService.getFulfillerAddress();
const { proof, l2Block } = await proverService.generateProofWithL2Block(
requestHash
);
const attributesClass = new Attributes(attributes);
attributesClass.removeFulfiller();

const usingHashi =
!activeChains.src.exposesL1State || !activeChains.dst.sharesStateWithL1;

if (usingHashi) {
// NOTE: This is only for a proof of concept. We have a mock shoyu bashi contract that allows us to directly set the block hash for the l2 block number.
// In production, more sophisticated logic will be needed to determine the latest block number accounted for in the Hashi system.
const shoyuBashi = attributesClass.getShoyuBashi();
await signerService.sendTransaction(
bytes32ToAddress(shoyuBashi),
ShoyuBashi,
"setHash",
[activeChains.dst.chainId, l2Block.number as bigint, l2Block.hash]
);
}

const payTo = signerService.getFulfillerAddress();
const encodedProof = this.encodeProof(proof);

const functionName = "claimReward";
Expand Down

0 comments on commit 21ad0a7

Please sign in to comment.