Skip to content

Commit 7fca80c

Browse files
authored
feat: update acp-103 complexities (#919)
* 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
1 parent ce826b8 commit 7fca80c

24 files changed

+525
-157
lines changed

src/fixtures/codec.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { getAVMManager } from '../serializable/avm/codec';
22
import { codec as EVMCodec } from '../serializable/evm/codec';
33
import { Short } from '../serializable/primitives';
44
import { codec } from '../serializable/pvm/codec';
5+
import { codec as WarpCodec } from '../serializable/pvm/warp/codec';
56

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

1516
export const testEVMCodec = () => EVMCodec;
17+
18+
export const testWarpCodec = () => WarpCodec;

src/fixtures/primitives.ts

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -65,25 +65,41 @@ export const blsSignatureBytes = () =>
6565
export const blsSignature = () =>
6666
BlsSignature.fromBytes(blsSignatureBytes())[0];
6767

68+
/**
69+
* Example WarpMessage bytes.
70+
*
71+
* WarpUnsignedMessage:
72+
* - networkId: 12345
73+
* - sourceChainId: rVPmsy4tQrQhQpRE6embdYnmYtwQi8kQAg1ZdaZGzeVdjc4qT
74+
*
75+
* One "signer" on the WarpSignature.
76+
*/
6877
export const warpMessageBytes = () =>
6978
new Uint8Array([
70-
0x00, 0x00, 0x00, 0xd7, 0x00, 0x00, 0x00, 0x00, 0x30, 0x39, 0x70, 0x5f,
71-
0x3d, 0x44, 0x15, 0xf9, 0x90, 0x22, 0x5d, 0x3d, 0xf5, 0xce, 0x43, 0x7d,
72-
0x7a, 0xf2, 0xaa, 0x32, 0x4b, 0x1b, 0xbc, 0xe8, 0x54, 0xee, 0x34, 0xab,
73-
0x6f, 0x39, 0x88, 0x22, 0x50, 0xd2, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00,
74-
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36,
75-
0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x38, 0xe6, 0xe9, 0xfe, 0x31, 0xc6,
76-
0xd0, 0x70, 0xa8, 0xc7, 0x92, 0xdb, 0xac, 0xf6, 0xd0, 0xae, 0xfb, 0x8e,
77-
0xac, 0x2a, 0xde, 0xd4, 0x9c, 0xc0, 0xaa, 0x9f, 0x42, 0x2d, 0x1f, 0xdd,
78-
0x9e, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
79-
0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
80-
0x00, 0x01, 0x01, 0x87, 0xf4, 0xbb, 0x2c, 0x42, 0x86, 0x9c, 0x56, 0xf0,
81-
0x23, 0xa1, 0xca, 0x81, 0x04, 0x5a, 0xff, 0x03, 0x4a, 0xcd, 0x49, 0x0b,
82-
0x8f, 0x15, 0xb5, 0x06, 0x90, 0x25, 0xf9, 0x82, 0xe6, 0x05, 0xe0, 0x77,
83-
0x00, 0x7f, 0xc5, 0x88, 0xf7, 0xd5, 0x63, 0x69, 0xa6, 0x5d, 0xf7, 0x57,
84-
0x4d, 0xf3, 0xb7, 0x0f, 0xf0, 0x28, 0xea, 0x17, 0x37, 0x39, 0xc7, 0x89,
85-
0x52, 0x5a, 0xb7, 0xee, 0xbf, 0xcb, 0x5c, 0x11, 0x5b, 0x13, 0xcc, 0xa8,
86-
0xf0, 0x2b, 0x36, 0x21, 0x04, 0xb7, 0x00, 0xc7, 0x5b, 0xc9, 0x52, 0x34,
87-
0x10, 0x9f, 0x3f, 0x13, 0x60, 0xdd, 0xcb, 0x4e, 0xc3, 0xca, 0xf6, 0xb0,
88-
0xe8, 0x21, 0xcb,
79+
0x00, 0x00, 0x00, 0x00, 0x30, 0x39, 0x70, 0x5f, 0x3d, 0x44, 0x15, 0xf9,
80+
0x90, 0x22, 0x5d, 0x3d, 0xf5, 0xce, 0x43, 0x7d, 0x7a, 0xf2, 0xaa, 0x32,
81+
0x4b, 0x1b, 0xbc, 0xe8, 0x54, 0xee, 0x34, 0xab, 0x6f, 0x39, 0x88, 0x22,
82+
0x50, 0xd2, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
83+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, 0x00,
84+
0x00, 0x01, 0xa0, 0x67, 0x3b, 0x4e, 0xe5, 0xec, 0x44, 0xe5, 0x7c, 0x8a,
85+
0xb2, 0x50, 0xdd, 0x7c, 0xd7, 0xb6, 0x8d, 0x04, 0x42, 0x1f, 0x64, 0xbd,
86+
0x65, 0x59, 0xa4, 0x28, 0x4a, 0x3e, 0xe3, 0x58, 0xff, 0x2b, 0x00, 0x00,
87+
0x00, 0x14, 0x5e, 0xfc, 0x86, 0xa1, 0x1c, 0x5b, 0x12, 0xcc, 0x95, 0xb2,
88+
0xcf, 0x52, 0x7c, 0x02, 0x3f, 0x9c, 0xf6, 0xe0, 0xe8, 0xf6, 0xb6, 0x20,
89+
0x34, 0x31, 0x5c, 0x5d, 0x11, 0xce, 0xa4, 0x19, 0x0f, 0x6e, 0xa8, 0x99,
90+
0x78, 0x21, 0xc0, 0x24, 0x83, 0xd2, 0x9a, 0xdb, 0x5e, 0x45, 0x67, 0x84,
91+
0x3f, 0x7a, 0x44, 0xc3, 0x9b, 0x2f, 0xfa, 0x20, 0xc8, 0x52, 0x0d, 0xc3,
92+
0x58, 0x70, 0x2f, 0xb1, 0xec, 0x29, 0xf2, 0x74, 0x6d, 0xcc, 0x00, 0x00,
93+
0x00, 0x00, 0x67, 0x05, 0xaf, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
94+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
95+
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
96+
0x00, 0x01, 0x01, 0x8e, 0x99, 0xdc, 0x6e, 0xd7, 0x36, 0x08, 0x9c, 0x03,
97+
0xb9, 0xa1, 0x27, 0x5e, 0x0c, 0xf8, 0x01, 0x52, 0x4e, 0xd3, 0x41, 0xfb,
98+
0x10, 0x11, 0x1f, 0x29, 0xc0, 0x39, 0x0f, 0xa2, 0xf9, 0x6c, 0xf6, 0xaa,
99+
0x78, 0x53, 0x9e, 0xc7, 0x67, 0xe5, 0xcd, 0x52, 0x3c, 0x60, 0x6c, 0x7e,
100+
0xde, 0x50, 0xe6, 0x0b, 0xa6, 0x06, 0x5a, 0x36, 0x85, 0xe7, 0x70, 0xd9,
101+
0x79, 0xb0, 0xdf, 0x74, 0xe3, 0x54, 0x1b, 0x61, 0xed, 0x63, 0xf0, 0x37,
102+
0x46, 0x37, 0x76, 0x09, 0x85, 0x76, 0xe3, 0x85, 0x76, 0x7a, 0x69, 0x5d,
103+
0xe5, 0x93, 0x52, 0xb4, 0x4e, 0x51, 0x58, 0x31, 0xc5, 0xee, 0x7a, 0x8c,
104+
0xc7, 0x28, 0xf9,
89105
]);

src/fixtures/warp.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import {
2+
WarpMessage,
3+
WarpSignature,
4+
WarpUnsignedMessage,
5+
} from '../serializable/pvm/warp';
6+
import { concatBytes } from '../utils';
7+
import { id, idBytes } from './common';
8+
import {
9+
blsSignature,
10+
blsSignatureBytes,
11+
bytes,
12+
bytesBytes,
13+
int,
14+
intBytes,
15+
} from './primitives';
16+
import { bytesForInt } from './utils/bytesFor';
17+
18+
export const warpUnsignedMessage = () =>
19+
new WarpUnsignedMessage(int(), id(), bytes());
20+
21+
export const warpUnsignedMessageBytes = () =>
22+
concatBytes(intBytes(), idBytes(), bytesBytes());
23+
24+
export const warpSignature = () => new WarpSignature(bytes(), blsSignature());
25+
26+
export const warpSignatureBytes = () =>
27+
concatBytes(bytesBytes(), blsSignatureBytes());
28+
29+
export const warpMessage = () =>
30+
new WarpMessage(warpUnsignedMessage(), warpSignature());
31+
32+
export const warpMessageBytes = () =>
33+
concatBytes(warpUnsignedMessageBytes(), bytesForInt(0), warpSignatureBytes());

src/serializable/constants.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,4 +95,9 @@ export enum TypeSymbols {
9595
EvmInput = 'evm.Input',
9696
EvmOutput = 'evm.Output',
9797
EvmImportTx = 'evm.ImportTx',
98+
99+
// Warp
100+
WarpMessage = 'warp.Message',
101+
WarpUnsignedMessage = 'warp.UnsignedMessage',
102+
WarpSignature = 'warp.Signature',
98103
}

src/serializable/primitives/bytes.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { TypeSymbols } from '../constants';
99
@serializable()
1010
export class Bytes extends Primitives {
1111
_type = TypeSymbols.Bytes;
12-
constructor(private readonly bytes: Uint8Array) {
12+
constructor(public readonly bytes: Uint8Array) {
1313
super();
1414
}
1515

src/serializable/pvm/registerL1ValidatorTx.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export class RegisterL1ValidatorTx extends PVMTx {
2929
[BaseTx, BigIntPr, BlsSignature, Bytes],
3030
codec,
3131
);
32+
3233
return [
3334
new RegisterL1ValidatorTx(baseTx, balance, blsSignature, message),
3435
rest,

src/serializable/pvm/warp/codec.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { Codec, Manager } from '../../codec';
2+
import { WarpSignature } from './signature';
3+
4+
/**
5+
* @see https://github.com/ava-labs/avalanchego/blob/master/vms/platformvm/warp/codec.go
6+
*/
7+
export const codec = new Codec([WarpSignature]);
8+
9+
let manager: Manager;
10+
export const getWarpManager = () => {
11+
if (manager) return manager;
12+
manager = new Manager();
13+
manager.RegisterCodec(0, codec);
14+
return manager;
15+
};

src/serializable/pvm/warp/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export { codec, getWarpManager } from './codec';
2+
export { WarpMessage } from './message';
3+
export { WarpSignature } from './signature';
4+
export { WarpUnsignedMessage } from './unsignedMessage';
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { testWarpCodec } from '../../../fixtures/codec';
2+
import { testSerialization } from '../../../fixtures/utils/serializable';
3+
import { warpMessage, warpMessageBytes } from '../../../fixtures/warp';
4+
import { WarpMessage } from './message';
5+
6+
testSerialization(
7+
'WarpMessage',
8+
WarpMessage,
9+
warpMessage,
10+
warpMessageBytes,
11+
testWarpCodec,
12+
);

src/serializable/pvm/warp/message.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { concatBytes } from '../../../utils/buffer';
2+
import { unpack } from '../../../utils/struct';
3+
import type { Codec } from '../../codec';
4+
import { serializable } from '../../common/types';
5+
import { TypeSymbols } from '../../constants';
6+
import type { WarpSignature } from './signature';
7+
import { WarpUnsignedMessage } from './unsignedMessage';
8+
9+
@serializable()
10+
export class WarpMessage {
11+
_type = TypeSymbols.WarpMessage;
12+
13+
constructor(
14+
public readonly unsignedMessage: WarpUnsignedMessage,
15+
public readonly signature: WarpSignature,
16+
) {}
17+
18+
static fromBytes(bytes: Uint8Array, codec: Codec): [WarpMessage, Uint8Array] {
19+
const [unsignedMessage, signatureBytes] = unpack(
20+
bytes,
21+
[WarpUnsignedMessage],
22+
codec,
23+
);
24+
25+
const [signature, rest] = codec.UnpackPrefix<WarpSignature>(signatureBytes);
26+
27+
return [new WarpMessage(unsignedMessage, signature), rest];
28+
}
29+
30+
toBytes(codec: Codec) {
31+
return concatBytes(
32+
this.unsignedMessage.toBytes(codec),
33+
codec.PackPrefix(this.signature),
34+
);
35+
}
36+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { testWarpCodec } from '../../../fixtures/codec';
2+
import { testSerialization } from '../../../fixtures/utils/serializable';
3+
import { warpSignature, warpSignatureBytes } from '../../../fixtures/warp';
4+
import { WarpSignature } from './signature';
5+
6+
testSerialization(
7+
'WarpSignature',
8+
WarpSignature,
9+
warpSignature,
10+
warpSignatureBytes,
11+
testWarpCodec,
12+
);
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { hammingWeight } from '../../../utils/buffer';
2+
import { pack, unpack } from '../../../utils/struct';
3+
import type { Codec } from '../../codec';
4+
import { serializable } from '../../common/types';
5+
import { TypeSymbols } from '../../constants';
6+
import { BlsSignature } from '../../fxs/common';
7+
import { Bytes } from '../../primitives';
8+
9+
@serializable()
10+
export class WarpSignature {
11+
_type = TypeSymbols.WarpSignature;
12+
13+
constructor(
14+
public readonly signers: Bytes,
15+
public readonly signature: BlsSignature,
16+
) {}
17+
18+
static fromBytes(
19+
bytes: Uint8Array,
20+
codec: Codec,
21+
): [WarpSignature, Uint8Array] {
22+
const [signers, signature, rest] = unpack(
23+
bytes,
24+
[Bytes, BlsSignature],
25+
codec,
26+
);
27+
28+
return [new WarpSignature(signers, signature), rest];
29+
}
30+
31+
toBytes(codec: Codec) {
32+
return pack([this.signers, this.signature], codec);
33+
}
34+
35+
/**
36+
* Number of BLS public keys that participated in the
37+
* {@linkcode BlsSignature}. This is exposed because users of the signatures
38+
* typically impose a verification fee that is a function of the number of signers.
39+
*
40+
* This is used to calculate the Warp complexity in transactions.
41+
*/
42+
numOfSigners(): number {
43+
return hammingWeight(this.signers.bytes);
44+
}
45+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { testWarpCodec } from '../../../fixtures/codec';
2+
import { testSerialization } from '../../../fixtures/utils/serializable';
3+
import {
4+
warpUnsignedMessage,
5+
warpUnsignedMessageBytes,
6+
} from '../../../fixtures/warp';
7+
import { WarpUnsignedMessage } from './unsignedMessage';
8+
9+
testSerialization(
10+
'WarpUnsignedMessage',
11+
WarpUnsignedMessage,
12+
warpUnsignedMessage,
13+
warpUnsignedMessageBytes,
14+
testWarpCodec,
15+
);
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { pack, unpack } from '../../../utils/struct';
2+
import type { Codec } from '../../codec';
3+
import { serializable } from '../../common/types';
4+
import { TypeSymbols } from '../../constants';
5+
import { Id } from '../../fxs/common';
6+
import { Bytes, Int } from '../../primitives';
7+
8+
@serializable()
9+
export class WarpUnsignedMessage {
10+
_type = TypeSymbols.WarpUnsignedMessage;
11+
12+
constructor(
13+
public readonly networkId: Int,
14+
public readonly sourceChainId: Id,
15+
public readonly payload: Bytes,
16+
) {}
17+
18+
static fromBytes(
19+
bytes: Uint8Array,
20+
codec: Codec,
21+
): [WarpUnsignedMessage, Uint8Array] {
22+
const [networkId, sourceChainId, payload, rest] = unpack(
23+
bytes,
24+
[Int, Id, Bytes],
25+
codec,
26+
);
27+
28+
return [new WarpUnsignedMessage(networkId, sourceChainId, payload), rest];
29+
}
30+
31+
toBytes(codec: Codec) {
32+
return pack([this.networkId, this.sourceChainId, this.payload], codec);
33+
}
34+
}

src/utils/buffer.test.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import { bufferToBigInt, bufferToNumber, padLeft } from './buffer';
1+
import {
2+
bufferToBigInt,
3+
bufferToNumber,
4+
hammingWeight,
5+
padLeft,
6+
} from './buffer';
27
import { describe, it, expect } from 'vitest';
38

49
describe('bufferToBigInt', () => {
@@ -79,3 +84,31 @@ describe('padLeft', () => {
7984
expect(res).toStrictEqual(new Uint8Array([0xaf, 0x72, 0x72]));
8085
});
8186
});
87+
88+
describe('hammingWeight()', () => {
89+
it('should return expected number of `1` bits from bytes', () => {
90+
expect(hammingWeight(new Uint8Array([0]))).toBe(0);
91+
expect(hammingWeight(new Uint8Array([1]))).toBe(1);
92+
expect(hammingWeight(new Uint8Array([2]))).toBe(1);
93+
expect(hammingWeight(new Uint8Array([3]))).toBe(2);
94+
expect(hammingWeight(new Uint8Array([4]))).toBe(1);
95+
expect(hammingWeight(new Uint8Array([5]))).toBe(2);
96+
expect(hammingWeight(new Uint8Array([6]))).toBe(2);
97+
expect(hammingWeight(new Uint8Array([7]))).toBe(3);
98+
expect(hammingWeight(new Uint8Array([8]))).toBe(1);
99+
expect(hammingWeight(new Uint8Array([9]))).toBe(2);
100+
101+
expect(hammingWeight(new Uint8Array([0, 0]))).toBe(0);
102+
expect(hammingWeight(new Uint8Array([0, 1]))).toBe(1);
103+
expect(hammingWeight(new Uint8Array([0, 2]))).toBe(1);
104+
expect(hammingWeight(new Uint8Array([0, 3]))).toBe(2);
105+
106+
expect(hammingWeight(new Uint8Array([1, 1]))).toBe(2);
107+
expect(hammingWeight(new Uint8Array([1, 2]))).toBe(2);
108+
expect(hammingWeight(new Uint8Array([1, 3]))).toBe(3);
109+
110+
expect(hammingWeight(new Uint8Array([3, 1]))).toBe(3);
111+
expect(hammingWeight(new Uint8Array([3, 2]))).toBe(3);
112+
expect(hammingWeight(new Uint8Array([3, 3]))).toBe(4);
113+
});
114+
});

src/utils/buffer.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,30 @@ export function padLeft(bytes: Uint8Array, length: number) {
3333
return out;
3434
}
3535

36+
/**
37+
* Calculates the number of `1`s (set bits) in the binary
38+
* representation a big-endian byte slice.
39+
*
40+
* @param input A Uint8Array
41+
* @returns The number of bits set to 1 in the binary representation of the input
42+
*
43+
* @example
44+
* ```ts
45+
* hammingWeight(new Uint8Array([0, 1, 2, 3, 4, 5])); // 7
46+
* ```
47+
*/
48+
export const hammingWeight = (input: Uint8Array): number => {
49+
let count = 0;
50+
51+
for (let i = 0; i < input.length; i++) {
52+
let num = input[i];
53+
while (num !== 0) {
54+
count += num & 1;
55+
num >>= 1;
56+
}
57+
}
58+
59+
return count;
60+
};
61+
3662
export { concatBytes, strip0x, add0x };

0 commit comments

Comments
 (0)