Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: update acp-103 complexities #919

Merged
merged 8 commits into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,
erictaylor marked this conversation as resolved.
Show resolved Hide resolved
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