Skip to content

Commit

Permalink
feat: update acp-103 complexities (#919)
Browse files Browse the repository at this point in the history
* test: update complexity tests with compute

* feat: acp-103 complexity updates

* fix: update export name

* feat: add updated acp-103 complexities

* feat: add warp message serialization/deserialization

* test: fix warp message bytes fixture

* docs: update jsdoc comment

* docs: update jsdoc comment
  • Loading branch information
erictaylor authored Nov 20, 2024
1 parent ce826b8 commit 7fca80c
Show file tree
Hide file tree
Showing 24 changed files with 525 additions and 157 deletions.
3 changes: 3 additions & 0 deletions src/fixtures/codec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { getAVMManager } from '../serializable/avm/codec';
import { codec as EVMCodec } from '../serializable/evm/codec';
import { Short } from '../serializable/primitives';
import { codec } from '../serializable/pvm/codec';
import { codec as WarpCodec } from '../serializable/pvm/warp/codec';

// Check for circular imports in the fx type
// registries if tests are throwing errors
Expand All @@ -13,3 +14,5 @@ export const testCodec = () => testManager().getCodecForVersion(new Short(0));
export const testPVMCodec = () => codec;

export const testEVMCodec = () => EVMCodec;

export const testWarpCodec = () => WarpCodec;
54 changes: 35 additions & 19 deletions src/fixtures/primitives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,25 +65,41 @@ export const blsSignatureBytes = () =>
export const blsSignature = () =>
BlsSignature.fromBytes(blsSignatureBytes())[0];

/**
* Example WarpMessage bytes.
*
* WarpUnsignedMessage:
* - networkId: 12345
* - sourceChainId: rVPmsy4tQrQhQpRE6embdYnmYtwQi8kQAg1ZdaZGzeVdjc4qT
*
* One "signer" on the WarpSignature.
*/
export const warpMessageBytes = () =>
new Uint8Array([
0x00, 0x00, 0x00, 0xd7, 0x00, 0x00, 0x00, 0x00, 0x30, 0x39, 0x70, 0x5f,
0x3d, 0x44, 0x15, 0xf9, 0x90, 0x22, 0x5d, 0x3d, 0xf5, 0xce, 0x43, 0x7d,
0x7a, 0xf2, 0xaa, 0x32, 0x4b, 0x1b, 0xbc, 0xe8, 0x54, 0xee, 0x34, 0xab,
0x6f, 0x39, 0x88, 0x22, 0x50, 0xd2, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36,
0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x38, 0xe6, 0xe9, 0xfe, 0x31, 0xc6,
0xd0, 0x70, 0xa8, 0xc7, 0x92, 0xdb, 0xac, 0xf6, 0xd0, 0xae, 0xfb, 0x8e,
0xac, 0x2a, 0xde, 0xd4, 0x9c, 0xc0, 0xaa, 0x9f, 0x42, 0x2d, 0x1f, 0xdd,
0x9e, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x01, 0x87, 0xf4, 0xbb, 0x2c, 0x42, 0x86, 0x9c, 0x56, 0xf0,
0x23, 0xa1, 0xca, 0x81, 0x04, 0x5a, 0xff, 0x03, 0x4a, 0xcd, 0x49, 0x0b,
0x8f, 0x15, 0xb5, 0x06, 0x90, 0x25, 0xf9, 0x82, 0xe6, 0x05, 0xe0, 0x77,
0x00, 0x7f, 0xc5, 0x88, 0xf7, 0xd5, 0x63, 0x69, 0xa6, 0x5d, 0xf7, 0x57,
0x4d, 0xf3, 0xb7, 0x0f, 0xf0, 0x28, 0xea, 0x17, 0x37, 0x39, 0xc7, 0x89,
0x52, 0x5a, 0xb7, 0xee, 0xbf, 0xcb, 0x5c, 0x11, 0x5b, 0x13, 0xcc, 0xa8,
0xf0, 0x2b, 0x36, 0x21, 0x04, 0xb7, 0x00, 0xc7, 0x5b, 0xc9, 0x52, 0x34,
0x10, 0x9f, 0x3f, 0x13, 0x60, 0xdd, 0xcb, 0x4e, 0xc3, 0xca, 0xf6, 0xb0,
0xe8, 0x21, 0xcb,
0x00, 0x00, 0x00, 0x00, 0x30, 0x39, 0x70, 0x5f, 0x3d, 0x44, 0x15, 0xf9,
0x90, 0x22, 0x5d, 0x3d, 0xf5, 0xce, 0x43, 0x7d, 0x7a, 0xf2, 0xaa, 0x32,
0x4b, 0x1b, 0xbc, 0xe8, 0x54, 0xee, 0x34, 0xab, 0x6f, 0x39, 0x88, 0x22,
0x50, 0xd2, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0xa0, 0x67, 0x3b, 0x4e, 0xe5, 0xec, 0x44, 0xe5, 0x7c, 0x8a,
0xb2, 0x50, 0xdd, 0x7c, 0xd7, 0xb6, 0x8d, 0x04, 0x42, 0x1f, 0x64, 0xbd,
0x65, 0x59, 0xa4, 0x28, 0x4a, 0x3e, 0xe3, 0x58, 0xff, 0x2b, 0x00, 0x00,
0x00, 0x14, 0x5e, 0xfc, 0x86, 0xa1, 0x1c, 0x5b, 0x12, 0xcc, 0x95, 0xb2,
0xcf, 0x52, 0x7c, 0x02, 0x3f, 0x9c, 0xf6, 0xe0, 0xe8, 0xf6, 0xb6, 0x20,
0x34, 0x31, 0x5c, 0x5d, 0x11, 0xce, 0xa4, 0x19, 0x0f, 0x6e, 0xa8, 0x99,
0x78, 0x21, 0xc0, 0x24, 0x83, 0xd2, 0x9a, 0xdb, 0x5e, 0x45, 0x67, 0x84,
0x3f, 0x7a, 0x44, 0xc3, 0x9b, 0x2f, 0xfa, 0x20, 0xc8, 0x52, 0x0d, 0xc3,
0x58, 0x70, 0x2f, 0xb1, 0xec, 0x29, 0xf2, 0x74, 0x6d, 0xcc, 0x00, 0x00,
0x00, 0x00, 0x67, 0x05, 0xaf, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x01, 0x8e, 0x99, 0xdc, 0x6e, 0xd7, 0x36, 0x08, 0x9c, 0x03,
0xb9, 0xa1, 0x27, 0x5e, 0x0c, 0xf8, 0x01, 0x52, 0x4e, 0xd3, 0x41, 0xfb,
0x10, 0x11, 0x1f, 0x29, 0xc0, 0x39, 0x0f, 0xa2, 0xf9, 0x6c, 0xf6, 0xaa,
0x78, 0x53, 0x9e, 0xc7, 0x67, 0xe5, 0xcd, 0x52, 0x3c, 0x60, 0x6c, 0x7e,
0xde, 0x50, 0xe6, 0x0b, 0xa6, 0x06, 0x5a, 0x36, 0x85, 0xe7, 0x70, 0xd9,
0x79, 0xb0, 0xdf, 0x74, 0xe3, 0x54, 0x1b, 0x61, 0xed, 0x63, 0xf0, 0x37,
0x46, 0x37, 0x76, 0x09, 0x85, 0x76, 0xe3, 0x85, 0x76, 0x7a, 0x69, 0x5d,
0xe5, 0x93, 0x52, 0xb4, 0x4e, 0x51, 0x58, 0x31, 0xc5, 0xee, 0x7a, 0x8c,
0xc7, 0x28, 0xf9,
]);
33 changes: 33 additions & 0 deletions src/fixtures/warp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import {
WarpMessage,
WarpSignature,
WarpUnsignedMessage,
} from '../serializable/pvm/warp';
import { concatBytes } from '../utils';
import { id, idBytes } from './common';
import {
blsSignature,
blsSignatureBytes,
bytes,
bytesBytes,
int,
intBytes,
} from './primitives';
import { bytesForInt } from './utils/bytesFor';

export const warpUnsignedMessage = () =>
new WarpUnsignedMessage(int(), id(), bytes());

export const warpUnsignedMessageBytes = () =>
concatBytes(intBytes(), idBytes(), bytesBytes());

export const warpSignature = () => new WarpSignature(bytes(), blsSignature());

export const warpSignatureBytes = () =>
concatBytes(bytesBytes(), blsSignatureBytes());

export const warpMessage = () =>
new WarpMessage(warpUnsignedMessage(), warpSignature());

export const warpMessageBytes = () =>
concatBytes(warpUnsignedMessageBytes(), bytesForInt(0), warpSignatureBytes());
5 changes: 5 additions & 0 deletions src/serializable/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,9 @@ export enum TypeSymbols {
EvmInput = 'evm.Input',
EvmOutput = 'evm.Output',
EvmImportTx = 'evm.ImportTx',

// Warp
WarpMessage = 'warp.Message',
WarpUnsignedMessage = 'warp.UnsignedMessage',
WarpSignature = 'warp.Signature',
}
2 changes: 1 addition & 1 deletion src/serializable/primitives/bytes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { TypeSymbols } from '../constants';
@serializable()
export class Bytes extends Primitives {
_type = TypeSymbols.Bytes;
constructor(private readonly bytes: Uint8Array) {
constructor(public readonly bytes: Uint8Array) {
super();
}

Expand Down
1 change: 1 addition & 0 deletions src/serializable/pvm/registerL1ValidatorTx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export class RegisterL1ValidatorTx extends PVMTx {
[BaseTx, BigIntPr, BlsSignature, Bytes],
codec,
);

return [
new RegisterL1ValidatorTx(baseTx, balance, blsSignature, message),
rest,
Expand Down
15 changes: 15 additions & 0 deletions src/serializable/pvm/warp/codec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Codec, Manager } from '../../codec';
import { WarpSignature } from './signature';

/**
* @see https://github.com/ava-labs/avalanchego/blob/master/vms/platformvm/warp/codec.go
*/
export const codec = new Codec([WarpSignature]);

let manager: Manager;
export const getWarpManager = () => {
if (manager) return manager;
manager = new Manager();
manager.RegisterCodec(0, codec);
return manager;
};
4 changes: 4 additions & 0 deletions src/serializable/pvm/warp/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export { codec, getWarpManager } from './codec';
export { WarpMessage } from './message';
export { WarpSignature } from './signature';
export { WarpUnsignedMessage } from './unsignedMessage';
12 changes: 12 additions & 0 deletions src/serializable/pvm/warp/message.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { testWarpCodec } from '../../../fixtures/codec';
import { testSerialization } from '../../../fixtures/utils/serializable';
import { warpMessage, warpMessageBytes } from '../../../fixtures/warp';
import { WarpMessage } from './message';

testSerialization(
'WarpMessage',
WarpMessage,
warpMessage,
warpMessageBytes,
testWarpCodec,
);
36 changes: 36 additions & 0 deletions src/serializable/pvm/warp/message.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { concatBytes } from '../../../utils/buffer';
import { unpack } from '../../../utils/struct';
import type { Codec } from '../../codec';
import { serializable } from '../../common/types';
import { TypeSymbols } from '../../constants';
import type { WarpSignature } from './signature';
import { WarpUnsignedMessage } from './unsignedMessage';

@serializable()
export class WarpMessage {
_type = TypeSymbols.WarpMessage;

constructor(
public readonly unsignedMessage: WarpUnsignedMessage,
public readonly signature: WarpSignature,
) {}

static fromBytes(bytes: Uint8Array, codec: Codec): [WarpMessage, Uint8Array] {
const [unsignedMessage, signatureBytes] = unpack(
bytes,
[WarpUnsignedMessage],
codec,
);

const [signature, rest] = codec.UnpackPrefix<WarpSignature>(signatureBytes);

return [new WarpMessage(unsignedMessage, signature), rest];
}

toBytes(codec: Codec) {
return concatBytes(
this.unsignedMessage.toBytes(codec),
codec.PackPrefix(this.signature),
);
}
}
12 changes: 12 additions & 0 deletions src/serializable/pvm/warp/signature.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { testWarpCodec } from '../../../fixtures/codec';
import { testSerialization } from '../../../fixtures/utils/serializable';
import { warpSignature, warpSignatureBytes } from '../../../fixtures/warp';
import { WarpSignature } from './signature';

testSerialization(
'WarpSignature',
WarpSignature,
warpSignature,
warpSignatureBytes,
testWarpCodec,
);
45 changes: 45 additions & 0 deletions src/serializable/pvm/warp/signature.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { hammingWeight } from '../../../utils/buffer';
import { pack, unpack } from '../../../utils/struct';
import type { Codec } from '../../codec';
import { serializable } from '../../common/types';
import { TypeSymbols } from '../../constants';
import { BlsSignature } from '../../fxs/common';
import { Bytes } from '../../primitives';

@serializable()
export class WarpSignature {
_type = TypeSymbols.WarpSignature;

constructor(
public readonly signers: Bytes,
public readonly signature: BlsSignature,
) {}

static fromBytes(
bytes: Uint8Array,
codec: Codec,
): [WarpSignature, Uint8Array] {
const [signers, signature, rest] = unpack(
bytes,
[Bytes, BlsSignature],
codec,
);

return [new WarpSignature(signers, signature), rest];
}

toBytes(codec: Codec) {
return pack([this.signers, this.signature], codec);
}

/**
* Number of BLS public keys that participated in the
* {@linkcode BlsSignature}. This is exposed because users of the signatures
* typically impose a verification fee that is a function of the number of signers.
*
* This is used to calculate the Warp complexity in transactions.
*/
numOfSigners(): number {
return hammingWeight(this.signers.bytes);
}
}
15 changes: 15 additions & 0 deletions src/serializable/pvm/warp/unsignedMessage.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { testWarpCodec } from '../../../fixtures/codec';
import { testSerialization } from '../../../fixtures/utils/serializable';
import {
warpUnsignedMessage,
warpUnsignedMessageBytes,
} from '../../../fixtures/warp';
import { WarpUnsignedMessage } from './unsignedMessage';

testSerialization(
'WarpUnsignedMessage',
WarpUnsignedMessage,
warpUnsignedMessage,
warpUnsignedMessageBytes,
testWarpCodec,
);
34 changes: 34 additions & 0 deletions src/serializable/pvm/warp/unsignedMessage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { pack, unpack } from '../../../utils/struct';
import type { Codec } from '../../codec';
import { serializable } from '../../common/types';
import { TypeSymbols } from '../../constants';
import { Id } from '../../fxs/common';
import { Bytes, Int } from '../../primitives';

@serializable()
export class WarpUnsignedMessage {
_type = TypeSymbols.WarpUnsignedMessage;

constructor(
public readonly networkId: Int,
public readonly sourceChainId: Id,
public readonly payload: Bytes,
) {}

static fromBytes(
bytes: Uint8Array,
codec: Codec,
): [WarpUnsignedMessage, Uint8Array] {
const [networkId, sourceChainId, payload, rest] = unpack(
bytes,
[Int, Id, Bytes],
codec,
);

return [new WarpUnsignedMessage(networkId, sourceChainId, payload), rest];
}

toBytes(codec: Codec) {
return pack([this.networkId, this.sourceChainId, this.payload], codec);
}
}
35 changes: 34 additions & 1 deletion src/utils/buffer.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { bufferToBigInt, bufferToNumber, padLeft } from './buffer';
import {
bufferToBigInt,
bufferToNumber,
hammingWeight,
padLeft,
} from './buffer';
import { describe, it, expect } from 'vitest';

describe('bufferToBigInt', () => {
Expand Down Expand Up @@ -79,3 +84,31 @@ describe('padLeft', () => {
expect(res).toStrictEqual(new Uint8Array([0xaf, 0x72, 0x72]));
});
});

describe('hammingWeight()', () => {
it('should return expected number of `1` bits from bytes', () => {
expect(hammingWeight(new Uint8Array([0]))).toBe(0);
expect(hammingWeight(new Uint8Array([1]))).toBe(1);
expect(hammingWeight(new Uint8Array([2]))).toBe(1);
expect(hammingWeight(new Uint8Array([3]))).toBe(2);
expect(hammingWeight(new Uint8Array([4]))).toBe(1);
expect(hammingWeight(new Uint8Array([5]))).toBe(2);
expect(hammingWeight(new Uint8Array([6]))).toBe(2);
expect(hammingWeight(new Uint8Array([7]))).toBe(3);
expect(hammingWeight(new Uint8Array([8]))).toBe(1);
expect(hammingWeight(new Uint8Array([9]))).toBe(2);

expect(hammingWeight(new Uint8Array([0, 0]))).toBe(0);
expect(hammingWeight(new Uint8Array([0, 1]))).toBe(1);
expect(hammingWeight(new Uint8Array([0, 2]))).toBe(1);
expect(hammingWeight(new Uint8Array([0, 3]))).toBe(2);

expect(hammingWeight(new Uint8Array([1, 1]))).toBe(2);
expect(hammingWeight(new Uint8Array([1, 2]))).toBe(2);
expect(hammingWeight(new Uint8Array([1, 3]))).toBe(3);

expect(hammingWeight(new Uint8Array([3, 1]))).toBe(3);
expect(hammingWeight(new Uint8Array([3, 2]))).toBe(3);
expect(hammingWeight(new Uint8Array([3, 3]))).toBe(4);
});
});
26 changes: 26 additions & 0 deletions src/utils/buffer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,30 @@ export function padLeft(bytes: Uint8Array, length: number) {
return out;
}

/**
* Calculates the number of `1`s (set bits) in the binary
* representation a big-endian byte slice.
*
* @param input A Uint8Array
* @returns The number of bits set to 1 in the binary representation of the input
*
* @example
* ```ts
* hammingWeight(new Uint8Array([0, 1, 2, 3, 4, 5])); // 7
* ```
*/
export const hammingWeight = (input: Uint8Array): number => {
let count = 0;

for (let i = 0; i < input.length; i++) {
let num = input[i];
while (num !== 0) {
count += num & 1;
num >>= 1;
}
}

return count;
};

export { concatBytes, strip0x, add0x };
Loading

0 comments on commit 7fca80c

Please sign in to comment.