Skip to content

Commit

Permalink
Test aggregation metadata builder
Browse files Browse the repository at this point in the history
  • Loading branch information
yorhodes committed May 3, 2024
1 parent ca1dd22 commit 33eba47
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 67 deletions.
58 changes: 58 additions & 0 deletions typescript/sdk/src/ism/metadata/aggregation.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { expect } from 'chai';

import {
AggregationIsmMetadata,
AggregationIsmMetadataBuilder,
} from './aggregation.js';

type Fixture = {
decoded: AggregationIsmMetadata;
encoded: string;
};

const fixtures: Fixture[] = [
{
decoded: {
submoduleMetadata: [
'290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563',
'510e4e770828ddbf7f7b00ab00a9f6adaf81c0dc9cc85f1f8249c256942d61d9',
'356e5a2cc1eba076e650ac7473fccc37952b46bc2e419a200cec0c451dce2336',
],
count: 3,
},
encoded:
'000000180000003800000038000000580000005800000078290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563510e4e770828ddbf7f7b00ab00a9f6adaf81c0dc9cc85f1f8249c256942d61d9356e5a2cc1eba076e650ac7473fccc37952b46bc2e419a200cec0c451dce2336',
},
{
decoded: {
count: 5,
submoduleMetadata: [
'290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563',
'510e4e770828ddbf7f7b00ab00a9f6adaf81c0dc9cc85f1f8249c256942d61d9',
'356e5a2cc1eba076e650ac7473fccc37952b46bc2e419a200cec0c451dce2336',
'f2e59013a0a379837166b59f871b20a8a0d101d1c355ea85d35329360e69c000',
],
},
encoded:
'000000280000004800000048000000680000006800000088000000000000000000000088000000a8290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563510e4e770828ddbf7f7b00ab00a9f6adaf81c0dc9cc85f1f8249c256942d61d9356e5a2cc1eba076e650ac7473fccc37952b46bc2e419a200cec0c451dce2336f2e59013a0a379837166b59f871b20a8a0d101d1c355ea85d35329360e69c000',
},
];

describe('AggregationMetadataBuilder', () => {
fixtures.forEach((fixture, i) => {
it(`should encode fixture ${i}`, () => {
expect(AggregationIsmMetadataBuilder.encode(fixture.decoded)).to.equal(
fixture.encoded,
);
});

it(`should decode fixture ${i}`, () => {
expect(
AggregationIsmMetadataBuilder.decode(
fixture.encoded,
fixture.decoded.count,
),
).to.deep.equal(fixture.decoded);
});
});
});
82 changes: 35 additions & 47 deletions typescript/sdk/src/ism/metadata/aggregation.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import { defaultAbiCoder } from '@ethersproject/abi';

import { WithAddress, assert } from '@hyperlane-xyz/utils';
import { WithAddress } from '@hyperlane-xyz/utils';

import { DispatchedMessage } from '../../core/types.js';
import { DerivedIsmConfigWithAddress } from '../read.js';
Expand All @@ -10,17 +8,13 @@ import { BaseMetadataBuilder, MetadataBuilder } from './builder.js';

export interface AggregationIsmMetadata {
submoduleMetadata: string[];
}

interface Range {
start: number;
end: number;
count: number;
}

const RANGE_SIZE = 4;

export class AggregationIsmMetadataBuilder
implements MetadataBuilder<AggregationIsmConfig, AggregationIsmMetadata>
implements MetadataBuilder<WithAddress<AggregationIsmConfig>>
{
constructor(protected readonly base: BaseMetadataBuilder) {}

Expand All @@ -33,57 +27,51 @@ export class AggregationIsmMetadataBuilder
this.base.build(message, module as DerivedIsmConfigWithAddress),
),
);
return this.encode({ submoduleMetadata: metadatas });
return AggregationIsmMetadataBuilder.encode({
submoduleMetadata: metadatas,
count: ismConfig.threshold,
});
}

encode(metadata: AggregationIsmMetadata): string {
const lengths = metadata.submoduleMetadata.map((m) => m.length / 2);
const ranges: Range[] = [];
static encode(metadata: AggregationIsmMetadata): string {
const rangeSize = 2 * RANGE_SIZE * metadata.count;

let offset = 0;
for (const length of lengths) {
ranges.push({ start: offset, end: offset + length });
offset += length;
}
let encoded = Buffer.alloc(rangeSize, 0);
metadata.submoduleMetadata.forEach((meta, index) => {
if (meta.length === 0) {
return;
}

let encoded = '';
for (const range of ranges) {
const encodedRange = defaultAbiCoder.encode(
['uint32', 'uint32'],
[range.start, range.end],
);
assert(encodedRange.length === RANGE_SIZE * 2);
encoded += encodedRange;
}
const start = encoded.length;
encoded = Buffer.concat([encoded, Buffer.from(meta)]);
// TODO: FIX THIS, IDK WHY ITS NOT WORKING
const end = encoded.length;

for (const m of metadata.submoduleMetadata) {
encoded += m;
}
const rangeStart = 2 * RANGE_SIZE * index;
encoded.writeUint32BE(start, rangeStart);
encoded.writeUint32BE(end, rangeStart + RANGE_SIZE);
});

return encoded;
return encoded.toString('hex');
}

metadataRange(metadata: string, index: number): Range {
const start = index * RANGE_SIZE * 2;
const mid = start + RANGE_SIZE;
const end = mid + RANGE_SIZE;
return {
start: parseInt(metadata.slice(start, mid)),
end: parseInt(metadata.slice(mid, end)),
};
static metadataRange(metadata: string, index: number): string {
const rangeStart = index * 2 * RANGE_SIZE;
const encoded = Buffer.from(metadata, 'hex');
const start = encoded.readUint32BE(rangeStart);
const end = encoded.readUint32BE(rangeStart + 1);
return encoded.subarray(start, end).toString('hex');
}

hasMetadata(metadata: string, index: number): boolean {
const { start } = this.metadataRange(metadata, index);
return start > 0;
static hasMetadata(metadata: string, index: number): boolean {
return this.metadataRange(metadata, index).length > 0;
}

decode(metadata: string): AggregationIsmMetadata {
static decode(metadata: string, count: number): AggregationIsmMetadata {
const submoduleMetadata = [];
for (let i = 0; this.hasMetadata(metadata, i); i++) {
const { start, end } = this.metadataRange(metadata, i);
submoduleMetadata.push(metadata.slice(start, end));
for (let i = 0; i < count; i++) {
submoduleMetadata.push(this.metadataRange(metadata, i));
}
return { submoduleMetadata };
return { submoduleMetadata, count };
}
}
27 changes: 18 additions & 9 deletions typescript/sdk/src/ism/metadata/builder.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,35 @@
import { WithAddress, eqAddress } from '@hyperlane-xyz/utils';
import { eqAddress } from '@hyperlane-xyz/utils';

import { HyperlaneCore } from '../../core/HyperlaneCore.js';
import { DispatchedMessage } from '../../core/types.js';
import { DerivedIsmConfigWithAddress } from '../read.js';
import { IsmConfig, IsmType } from '../types.js';
import { IsmType } from '../types.js';

import {
AggregationIsmMetadata,
AggregationIsmMetadataBuilder,
} from './aggregation.js';
import { MultisigMetadata, MultisigMetadataBuilder } from './multisig.js';

export interface MetadataBuilder<T extends IsmConfig, M> {
build(message: DispatchedMessage, ismConfig: WithAddress<T>): Promise<string>;
encode?(metadata: M): string;
decode?(metadata: string): M;
}
type NullMetadata = {
type:
| IsmType.PAUSABLE
| IsmType.TEST_ISM
| IsmType.OP_STACK
| IsmType.TRUSTED_RELAYER;
};

export type StructuredMetadata =
| AggregationIsmMetadata
| MultisigMetadata
| NullMetadata;

type StructuredMetadata = AggregationIsmMetadata | MultisigMetadata;
export interface MetadataBuilder<T extends DerivedIsmConfigWithAddress> {
build(message: DispatchedMessage, ismConfig: T): Promise<string>;
}

export class BaseMetadataBuilder
implements MetadataBuilder<DerivedIsmConfigWithAddress, StructuredMetadata>
implements MetadataBuilder<DerivedIsmConfigWithAddress>
{
constructor(protected readonly core: HyperlaneCore) {}

Expand Down
27 changes: 16 additions & 11 deletions typescript/sdk/src/ism/metadata/multisig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,14 @@ import { IsmType, MultisigIsmConfig } from '../types.js';
import { MetadataBuilder } from './builder.js';

interface MessageIdMultisigMetadata {
type: IsmType.MESSAGE_ID_MULTISIG;
checkpoint: Omit<Checkpoint, 'mailbox_domain'>;
signatures: SignatureLike[];
}

interface MerkleRootMultisigMetadata extends MessageIdMultisigMetadata {
interface MerkleRootMultisigMetadata
extends Omit<MessageIdMultisigMetadata, 'type'> {
type: IsmType.MERKLE_ROOT_MULTISIG;
proof: MerkleProof;
}

Expand All @@ -34,7 +37,7 @@ export type MultisigMetadata =
| MerkleRootMultisigMetadata;

export class MultisigMetadataBuilder
implements MetadataBuilder<MultisigIsmConfig, MultisigMetadata>
implements MetadataBuilder<WithAddress<MultisigIsmConfig>>
{
constructor(protected readonly core: HyperlaneCore) {}

Expand Down Expand Up @@ -81,19 +84,21 @@ export class MultisigMetadataBuilder
);

const metadata: MessageIdMultisigMetadata = {
type: IsmType.MESSAGE_ID_MULTISIG,
checkpoint: matching.value.checkpoint,
signatures: checkpoints
.filter((c): c is S3CheckpointWithId => c !== undefined)
.map((c) => c.signature),
};

return this.encode(metadata);
return MultisigMetadataBuilder.encode(metadata);
}

encode(metadata: MultisigMetadata): string {
if ('proof' in metadata) {
throw new Error('Merkle proofs are not yet supported');
}
static encode(metadata: MultisigMetadata): string {
assert(
metadata.type === IsmType.MESSAGE_ID_MULTISIG,
'Merkle proofs are not yet supported',
);

let encoded = defaultAbiCoder.encode(
['bytes32', 'bytes32', 'uint32'],
Expand All @@ -114,13 +119,13 @@ export class MultisigMetadataBuilder
return encoded;
}

signatureAt(metadata: string, index: number): SignatureLike {
static signatureAt(metadata: string, index: number): SignatureLike {
const start = 68 + index * 65;
const end = start + 65;
return metadata.slice(start, end);
}

hasSignature(metadata: string, index: number): boolean {
static hasSignature(metadata: string, index: number): boolean {
try {
this.signatureAt(metadata, index);
return true;
Expand All @@ -129,7 +134,7 @@ export class MultisigMetadataBuilder
}
}

decode(metadata: string): MessageIdMultisigMetadata {
static decode(metadata: string): MessageIdMultisigMetadata {
const checkpoint = {
merkle_tree_hook_address: metadata.slice(0, 32),
root: metadata.slice(32, 64),
Expand All @@ -141,6 +146,6 @@ export class MultisigMetadataBuilder
signatures.push(this.signatureAt(metadata, i));
}

return { checkpoint, signatures };
return { type: IsmType.MESSAGE_ID_MULTISIG, checkpoint, signatures };
}
}

0 comments on commit 33eba47

Please sign in to comment.