Skip to content

Commit 13f4698

Browse files
authored
Moved and exported merkle module + logic adjustments (#55)
1 parent a8bc6b6 commit 13f4698

File tree

9 files changed

+69
-42
lines changed

9 files changed

+69
-42
lines changed

package-lock.json

Lines changed: 10 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@gluwa/cc-next-query-builder",
3-
"version": "0.7.0",
3+
"version": "0.8.0",
44
"repository": {
55
"type": "git",
66
"url": "https://github.com/gluwa/cc-next-query-builder"
@@ -34,7 +34,8 @@
3434
"dependencies": {
3535
"axios": "^1.13.2",
3636
"dotenv": "^17.2.3",
37-
"ethers": "^6.15.0"
37+
"ethers": "^6.15.0",
38+
"exponential-backoff": "^3.1.3"
3839
},
3940
"author": "Gluwa Inc.",
4041
"license": "MIT"

src/block-prover/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { Contract, ContractMethod, InterfaceAbi, JsonRpcApiProvider } from 'ethers';
22

33
import BlockProverABI from './block_prover.json';
4-
import { ContinuityProof, TransactionMerkleProof } from '../proof-generator';
4+
import { ContinuityProof } from '../proof-generator';
5+
import { TransactionMerkleProof } from '../proof-generator/merkle';
56

67
const contractABI = BlockProverABI as unknown as InterfaceAbi;
78

src/chain-info/index.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Contract, InterfaceAbi, JsonRpcApiProvider } from 'ethers';
2+
import { backOff, BackoffOptions } from 'exponential-backoff';
23

34
import ChainInfoABI from './chain_info.json';
45

@@ -408,12 +409,23 @@ export class PrecompileChainInfoProvider implements ChainInfoProvider {
408409
): Promise<void> {
409410
const startTime = Date.now();
410411

412+
const backOffOptions = {
413+
delayFirstAttempt: true,
414+
jitter: 'full',
415+
numOfAttempts: 5,
416+
startingDelay: 100, // 100ms
417+
} as BackoffOptions;
418+
411419
while (true) {
412-
const heightHash = await this.getLatestAttestedHeightAndHash(chainKey);
420+
const heightHash = await backOff(() => this.getLatestAttestedHeightAndHash(chainKey), backOffOptions);
413421
if (heightHash.exists && heightHash.height >= targetHeight) {
414422
return;
415423
}
416424

425+
console.debug(
426+
`Height ${targetHeight} not yet attested on chain key ${chainKey}. Latest attested height is ${heightHash.exists ? heightHash.height : 'N/A'}. Retrying in ${pollIntervalMs}ms...`,
427+
);
428+
417429
if (Date.now() - startTime > waitTimeoutMs) {
418430
throw new Error(`Timeout waiting for height ${targetHeight} to be attested on chain key ${chainKey}`);
419431
}

src/proof-generator/index.ts

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
export * as raw from './raw-generator';
22
export * as api from './api-generator';
3+
export * as merkle from './merkle';
4+
5+
import { TransactionMerkleProof } from './merkle';
36

47
/**
58
* A continuity proof used to prove block continuity in the attestation chain.
@@ -72,26 +75,6 @@ export function mergeProofs(proofs: [number, ContinuityProof][]): ContinuityProo
7275
}
7376
}
7477

75-
export class TransactionMerkleProof {
76-
public root: string;
77-
public siblings: MerkleProofEntry[];
78-
79-
constructor(root: string, siblings: MerkleProofEntry[]) {
80-
this.root = root;
81-
this.siblings = siblings;
82-
}
83-
}
84-
85-
export class MerkleProofEntry {
86-
public hash: string;
87-
public isLeft: boolean;
88-
89-
constructor(hash: string, isLeft: boolean) {
90-
this.hash = hash;
91-
this.isLeft = isLeft;
92-
}
93-
}
94-
9578
export interface ContinuityResponse {
9679
chainKey: number;
9780
headerNumber: number;

src/proof-generator/raw-generator/merkle.ts renamed to src/proof-generator/merkle.ts

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,26 @@
1-
import { Block, TransactionResponse, TransactionReceipt, keccak256, solidityPacked } from 'ethers';
1+
import { TransactionReceipt, keccak256, solidityPacked } from 'ethers';
22

3-
import { MerkleProofEntry, TransactionMerkleProof } from '..';
3+
import { abiEncode, EncodingVersion, TransactionWithRaw } from '../encoding';
44

5-
import { abiEncode, EncodingVersion, TransactionWithRaw } from '../../encoding';
5+
export class TransactionMerkleProof {
6+
public root: string;
7+
public siblings: MerkleProofEntry[];
8+
9+
constructor(root: string, siblings: MerkleProofEntry[]) {
10+
this.root = root;
11+
this.siblings = siblings;
12+
}
13+
}
14+
15+
export class MerkleProofEntry {
16+
public hash: string;
17+
public isLeft: boolean;
18+
19+
constructor(hash: string, isLeft: boolean) {
20+
this.hash = hash;
21+
this.isLeft = isLeft;
22+
}
23+
}
624

725
/**
826
* Hashes two child nodes to produce their parent node in the Merkle tree.
@@ -11,7 +29,7 @@ import { abiEncode, EncodingVersion, TransactionWithRaw } from '../../encoding';
1129
* @param right A 32-byte hex string representing the right child hash
1230
* @returns A 32-byte hex string representing the parent node hash
1331
*/
14-
function hashInner(left: string, right: string): string {
32+
export function hashInner(left: string, right: string): string {
1533
// Use solidityPacked to exactly match Solidity's abi.encodePacked(uint8(0x01), bytes32, bytes32)
1634
return keccak256(solidityPacked(['uint8', 'bytes32', 'bytes32'], [0x01, left, right]));
1735
}
@@ -22,7 +40,7 @@ function hashInner(left: string, right: string): string {
2240
* @param leaf A 32-byte hex string representing the data to be hashed as a leaf
2341
* @returns A 32-byte hex string representing the leaf node hash
2442
*/
25-
function hashLeaf(leaf: string): string {
43+
export function hashLeaf(leaf: string): string {
2644
// Use solidityPacked to exactly match Solidity's abi.encodePacked(uint8(0x00), bytes)
2745
return keccak256(solidityPacked(['uint8', 'bytes'], [0x00, leaf]));
2846
}
@@ -87,12 +105,13 @@ export function computeMerkleRootOfBlock(
87105
* @returns The computed digest as a hex string
88106
*/
89107
export function computeDigestOf(blockNumber: number, merkleRoot: string, prevDigest: string | null): string {
90-
const prevDigestNonNull = prevDigest || ZERO_HASH;
91-
const digest = keccak256(
92-
solidityPacked(['uint64', 'bytes32', 'bytes32'], [blockNumber, merkleRoot, prevDigestNonNull]),
93-
);
108+
// If prevDigest is provided, include it in the packed encoding
109+
if (prevDigest) {
110+
return keccak256(solidityPacked(['uint64', 'bytes32', 'bytes32'], [blockNumber, merkleRoot, prevDigest]));
111+
}
94112

95-
return digest;
113+
// Otherwise, only use blockNumber and merkleRoot
114+
return keccak256(solidityPacked(['uint64', 'bytes32'], [blockNumber, merkleRoot]));
96115
}
97116

98117
export class KeccakMerkleTree {

src/proof-generator/raw-generator/continuity-proof.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { ChainInfoProvider, ContinuityBounds } from '../../chain-info';
33

44
import { EncodingVersion } from '../../encoding';
55

6-
import { computeDigestOf, computeMerkleRootOfBlock } from './merkle';
6+
import { computeDigestOf, computeMerkleRootOfBlock } from '../merkle';
77
import { BlockProvider } from './block-provider';
88

99
export class AttestationBlock {

src/proof-generator/raw-generator/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { ProofGenerationResult, ProofGenerator } from '..';
22
import { ChainInfoProvider } from '../../chain-info';
33

4-
import { abiEncode, EncodingVersion, TransactionWithRaw } from '../../encoding';
4+
import { abiEncode, EncodingVersion } from '../../encoding';
55

66
import { ContinuityProofBuilder } from './continuity-proof';
7-
import { KeccakMerkleTree } from './merkle';
7+
import { KeccakMerkleTree } from '../merkle';
88
import { BlockProvider } from './block-provider';
99

1010
// Re-export for easier access

tests/smoke/merkle.test.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import { test, expect } from '@jest/globals';
22

3-
import { MerkleProofEntry, TransactionMerkleProof } from '../../src/proof-generator';
4-
import { KeccakMerkleTree, ZERO_HASH } from '../../src/proof-generator/raw-generator/merkle';
3+
import {
4+
MerkleProofEntry,
5+
TransactionMerkleProof,
6+
KeccakMerkleTree,
7+
ZERO_HASH,
8+
} from '../../src/proof-generator/merkle';
59

610
test('MerkleTree should fail to generate proof for non-existent leaf', async () => {
711
const tree = new KeccakMerkleTree([]);

0 commit comments

Comments
 (0)