Skip to content

Commit fe6f66a

Browse files
Merge pull request #974 from ava-labs/raj/warp-l1-validator
feat: warp codec for l1 validator messages
2 parents b5e0877 + d8c1de1 commit fe6f66a

28 files changed

+898
-4
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { utils } from '../../src';
2+
import { pvmSerial } from '../../src/serializable';
3+
4+
/**
5+
* Structure of the message:
6+
* WarpSignedMessage
7+
* - WarpUnsignedMessage
8+
* - NetworkId
9+
* - SourceChainId
10+
* - AddressedCallPayload
11+
* - SourceAddress
12+
* - L1ValidatorWeightMessage
13+
* - BitSet Signatures
14+
*/
15+
16+
// Ref: See tx hash (on Fuji - PChain) zphUhqvXtj8dkYxg2BqCdTPDUPWBJUQkb1XTp38u8zDZ9VjMW
17+
const signedWarpMsgHex =
18+
'0000000000056b804f574b890cf9e0cb0f0f68591a394bba1696cf62b4e576e793d8509cc88600000058000000000001000000140feedc0de0000000000000000000000000000000000000360000000000038ccf9ef520784d2fa5d97fbf098b8b4e82ff19408ec423c2970a522ab04b3a0400000000000000040000000000000029000000000000000106a8206d76cf3fa7d65fec8464b0311dce9283d05bcf0ca7987cdf03a3a2f764691e01df4f6aaa3ff6b52e5b92fd3291e519f3fb50bad5d9697a39e34e2c3e99ea585f0332e9d13b4b6db7ecc58eee44c7f96e64371b1eebaa6f7c45bbf0937e68';
19+
20+
const warpManager = pvmSerial.warp.getWarpManager();
21+
22+
const parsedWarpMsg = warpManager.unpack(
23+
utils.hexToBuffer(signedWarpMsgHex),
24+
pvmSerial.warp.WarpMessage,
25+
);
26+
27+
console.log('Network ID:', parsedWarpMsg.unsignedMessage.networkId.value());
28+
console.log(
29+
'Source Chain ID:',
30+
parsedWarpMsg.unsignedMessage.sourceChainId.value(),
31+
);
32+
33+
const addressedCall = warpManager.unpack(
34+
parsedWarpMsg.unsignedMessage.payload.bytes,
35+
pvmSerial.warp.AddressedCallPayloads.AddressedCall,
36+
);
37+
38+
console.log('Source Address:', addressedCall.getSourceAddress());
39+
40+
const l1VldrWeightMsg = warpManager.unpack(
41+
addressedCall.payload.bytes,
42+
pvmSerial.warp.AddressedCallPayloads.L1ValidatorWeightMessage,
43+
);
44+
45+
console.log(l1VldrWeightMsg);
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { utils } from '../../src';
2+
import { pvmSerial } from '../../src/serializable';
3+
4+
/**
5+
* Structure of the message:
6+
* WarpSignedMessage
7+
* - WarpUnsignedMessage
8+
* - NetworkId
9+
* - SourceChainId
10+
* - AddressedCallPayload
11+
* - SourceAddress
12+
* - RegisterL1ValidatorMessage
13+
* - BitSet Signatures
14+
*/
15+
16+
// Ref: See tx hash (on Fuji - PChain) qKx5qy1zriGsWGhnRfHibE9kuDSUMfQV7gPQVCQ8iBKT5ZriV
17+
const signedWarpMsgHex =
18+
'0x0000000000057f78fe8ca06cefa186ef29c15231e45e1056cd8319ceca0695ca61099e610355000000d80000000000010000001433b9785e20ec582d5009965fb3346f1716e8a423000000b60000000000015e8b6e2e8155e93739f2fa6a7f8a32c6bb2e1dce2e471b56dcc60aac49bf34350000001447b37278e32917ffc6d2861b50dd9751b4016dd1b0d305fd70c376b0f5d4e6b9184728dcacb7390f477015690133a5632affab5701e9ebe61038d2e41373de53f4569fd60000000067d1ac310000000100000001380c1fb1db38f176b50e77eca240258e31a5b5e80000000100000001380c1fb1db38f176b50e77eca240258e31a5b5e80000000000004e200000000000000003c4411899be0450aee4dcc1be90a8802bdbd12821a5025a74cb094ff0033982e7f3951d6c4b882a6ce39bd2aa835b31accd09c60f26bc75308af4e05c4237df9b72b04c2697c5a0a7fb0f05f7b09358743a4a2df8cd4eda61f0dea0312a7014baa8a5c1';
19+
20+
const warpManager = pvmSerial.warp.getWarpManager();
21+
22+
const parsedWarpMsg = warpManager.unpack(
23+
utils.hexToBuffer(signedWarpMsgHex),
24+
pvmSerial.warp.WarpMessage,
25+
);
26+
27+
console.log('Network ID:', parsedWarpMsg.unsignedMessage.networkId.value());
28+
console.log(
29+
'Source Chain ID:',
30+
parsedWarpMsg.unsignedMessage.sourceChainId.value(),
31+
);
32+
33+
const addressedCall = warpManager.unpack(
34+
parsedWarpMsg.unsignedMessage.payload.bytes,
35+
pvmSerial.warp.AddressedCallPayloads.AddressedCall,
36+
);
37+
38+
console.log('Source Address:', addressedCall.getSourceAddress());
39+
40+
const registerL1ValidatorMsg = warpManager.unpack(
41+
addressedCall.payload.bytes,
42+
pvmSerial.warp.AddressedCallPayloads.RegisterL1ValidatorMessage,
43+
);
44+
45+
console.log(registerL1ValidatorMsg);

src/fixtures/primitives.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
import { BlsPublicKey } from '../serializable/fxs/common/blsPublicKey';
12
import { BlsSignature } from '../serializable/fxs/common/blsSignature';
23
import {
34
BigIntPr,
5+
Bool,
46
Byte,
57
Bytes,
68
Int,
@@ -19,6 +21,10 @@ export const intsBytes = () =>
1921

2022
export const ints = () => [new Int(3), new Int(2), new Int(1)];
2123

24+
export const boolBytes = (val = true) => new Uint8Array([val ? 0x01 : 0x00]);
25+
26+
export const bool = (val = true) => new Bool(val);
27+
2228
export const byteByte = () => new Uint8Array([0x01]);
2329

2430
export const byte = () => new Byte(new Uint8Array([0x01]));
@@ -50,6 +56,9 @@ export const blsPublicKeyBytes = () =>
5056
0xde, 0xc9, 0x50, 0x40, 0x30, 0x9a, 0xd1, 0xf1, 0x58, 0x95, 0x30, 0x67,
5157
]);
5258

59+
export const blsPublicKey = () =>
60+
BlsPublicKey.fromBytes(blsPublicKeyBytes())[0];
61+
5362
export const blsSignatureBytes = () =>
5463
new Uint8Array([
5564
0x8b, 0x1d, 0x61, 0x33, 0xd1, 0x7e, 0x34, 0x83, 0x22, 0x0a, 0xd9, 0x60,

src/fixtures/warp.ts

Lines changed: 114 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,42 @@
1+
import { Int } from '../serializable/primitives';
12
import {
23
WarpMessage,
34
WarpSignature,
45
WarpUnsignedMessage,
56
} from '../serializable/pvm/warp';
7+
import {
8+
AddressedCall,
9+
L1ValidatorWeightMessage,
10+
} from '../serializable/pvm/warp/addressedCall';
11+
import { L1ValidatorRegistrationMessage } from '../serializable/pvm/warp/addressedCall/messages/l1ValidatorRegistrationMessage';
12+
import { RegisterL1ValidatorMessage } from '../serializable/pvm/warp/addressedCall/messages/registerL1ValidatorMessage';
13+
import { SubnetToL1ConversionMessage } from '../serializable/pvm/warp/addressedCall/messages/subnetToL1ConversionMessage';
14+
import { ConversionData } from '../serializable/pvm/warp/addressedCall/utils/conversionData';
15+
import { ValidatorData } from '../serializable/pvm/warp/addressedCall/utils/validatorData';
616
import { concatBytes } from '../utils';
7-
import { id, idBytes } from './common';
817
import {
18+
address,
19+
addressBytes,
20+
id,
21+
idBytes,
22+
nodeId,
23+
nodeIdBytes,
24+
} from './common';
25+
import {
26+
bigIntPr,
27+
bigIntPrBytes,
28+
blsPublicKey,
29+
blsPublicKeyBytes,
930
blsSignature,
1031
blsSignatureBytes,
32+
bool,
33+
boolBytes,
1134
bytes,
1235
bytesBytes,
1336
int,
1437
intBytes,
1538
} from './primitives';
39+
import { pChainOwner, pChainOwnerBytes } from './pvm';
1640
import { bytesForInt } from './utils/bytesFor';
1741

1842
export const warpUnsignedMessage = () =>
@@ -31,3 +55,92 @@ export const warpMessage = () =>
3155

3256
export const warpMessageBytes = () =>
3357
concatBytes(warpUnsignedMessageBytes(), bytesForInt(0), warpSignatureBytes());
58+
59+
export const registerL1ValidatorMessage = () =>
60+
new RegisterL1ValidatorMessage(
61+
id(),
62+
nodeId(),
63+
blsPublicKey(),
64+
bigIntPr(),
65+
pChainOwner(),
66+
pChainOwner(),
67+
bigIntPr(),
68+
);
69+
70+
export const registerL1ValidatorMessageBytes = () =>
71+
concatBytes(
72+
new Int(1).toBytes(), // typeId
73+
idBytes(),
74+
bytesForInt(nodeIdBytes().length),
75+
nodeIdBytes(),
76+
blsPublicKeyBytes(),
77+
bigIntPrBytes(),
78+
pChainOwnerBytes(),
79+
pChainOwnerBytes(),
80+
bigIntPrBytes(),
81+
);
82+
83+
export const l1ValidatorWeightMessage = () =>
84+
new L1ValidatorWeightMessage(id(), bigIntPr(), bigIntPr());
85+
86+
export const l1ValidatorWeightMessageBytes = () =>
87+
concatBytes(
88+
new Int(3).toBytes(), // typeId
89+
idBytes(),
90+
bigIntPrBytes(),
91+
bigIntPrBytes(),
92+
);
93+
94+
export const addressedCall = () => new AddressedCall(address(), bytes());
95+
96+
export const addressedCallBytes = () =>
97+
concatBytes(
98+
new Int(1).toBytes(), // typeId
99+
bytesForInt(addressBytes().length),
100+
addressBytes(),
101+
bytesBytes(),
102+
);
103+
104+
export const l1ValidatorRegistrationMessage = () =>
105+
new L1ValidatorRegistrationMessage(id(), bool());
106+
107+
export const l1ValidatorRegistrationMessageBytes = () =>
108+
concatBytes(
109+
new Int(2).toBytes(), // typeId
110+
idBytes(),
111+
boolBytes(),
112+
);
113+
114+
export const subnetToL1ConversionMessage = () =>
115+
new SubnetToL1ConversionMessage(id());
116+
117+
export const subnetToL1ConversionMessageBytes = () =>
118+
concatBytes(
119+
new Int(0).toBytes(), // typeId
120+
idBytes(),
121+
);
122+
123+
export const validatorData = () =>
124+
new ValidatorData(nodeId(), blsPublicKey(), bigIntPr());
125+
126+
export const validatorDataBytes = () =>
127+
concatBytes(
128+
bytesForInt(nodeIdBytes().length),
129+
nodeIdBytes(),
130+
blsPublicKeyBytes(),
131+
bigIntPrBytes(),
132+
);
133+
134+
export const conversionData = () =>
135+
new ConversionData(id(), id(), address(), [validatorData()]);
136+
137+
export const conversionDataBytes = () =>
138+
concatBytes(
139+
idBytes(),
140+
idBytes(),
141+
bytesForInt(addressBytes().length),
142+
addressBytes(),
143+
bytesForInt(validatorDataBytes().length + 4), // size of validators + length of numValidators
144+
bytesForInt(1), // numValidators
145+
validatorDataBytes(),
146+
);

src/serializable/constants.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export enum TypeSymbols {
2121
Address = 'common.Address',
2222
NodeId = 'common.NodeId',
2323
BlsSignature = 'common.BlsSignature',
24+
BlsPublicKey = 'common.BlsPublicKey',
2425

2526
// PRIMITIVES
2627
Int = 'primitives.Int',
@@ -29,7 +30,7 @@ export enum TypeSymbols {
2930
Byte = 'primitives.Byte',
3031
Bytes = 'primitives.Bytes',
3132
Short = 'primitives.Short',
32-
33+
Bool = 'primitives.Bool',
3334
// SECP256k1FX
3435
Input = 'secp256k1fx.Input',
3536
TransferInput = 'secp256k1fx.TransferInput',
@@ -100,4 +101,13 @@ export enum TypeSymbols {
100101
WarpMessage = 'warp.Message',
101102
WarpUnsignedMessage = 'warp.UnsignedMessage',
102103
WarpSignature = 'warp.Signature',
104+
105+
// Warp AddressedCall Payloads
106+
AddressedCall = 'warp.AddressedCall',
107+
RegisterL1ValidatorMessage = 'warp.RegisterL1ValidatorMessage',
108+
L1ValidatorWeightMessage = 'warp.L1ValidatorWeightMessage',
109+
SubnetToL1ConversionMessage = 'warp.SubnetToL1ConversionMessage',
110+
L1ValidatorRegistrationMessage = 'warp.L1ValidatorRegistrationMessage',
111+
ValidatorData = 'warp.ValidatorData',
112+
ConversionData = 'warp.ConversionData',
103113
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { blsPublicKey, blsPublicKeyBytes } from '../../../fixtures/primitives';
2+
import { testSerialization } from '../../../fixtures/utils/serializable';
3+
import { BlsPublicKey } from './blsPublicKey';
4+
5+
testSerialization(
6+
'BlsPublicKey',
7+
BlsPublicKey,
8+
blsPublicKey,
9+
blsPublicKeyBytes,
10+
);
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import type { PublicKey } from '../../../crypto/bls';
2+
import {
3+
PUBLIC_KEY_LENGTH,
4+
publicKeyFromBytes,
5+
publicKeyToBytes,
6+
} from '../../../crypto/bls';
7+
import { hexToBuffer } from '../../../utils/buffer';
8+
import { serializable } from '../../common/types';
9+
import { TypeSymbols } from '../../constants';
10+
import { Primitives } from '../../primitives/primatives';
11+
12+
@serializable()
13+
export class BlsPublicKey extends Primitives {
14+
_type = TypeSymbols.BlsPublicKey;
15+
16+
constructor(public readonly publicKey: PublicKey) {
17+
super();
18+
}
19+
20+
static fromPublicKeyBytes(publicKeyBytes: Uint8Array): BlsPublicKey {
21+
return new BlsPublicKey(publicKeyFromBytes(publicKeyBytes));
22+
}
23+
24+
static fromBytes(
25+
bytes: Uint8Array,
26+
): [blsPublicKey: BlsPublicKey, rest: Uint8Array] {
27+
const blsPublicKeyBytes = bytes.slice(0, PUBLIC_KEY_LENGTH);
28+
const publicKey = publicKeyFromBytes(blsPublicKeyBytes);
29+
const rest = bytes.slice(PUBLIC_KEY_LENGTH);
30+
31+
return [new BlsPublicKey(publicKey), rest];
32+
}
33+
34+
static fromHex(hex: string): BlsPublicKey {
35+
return new BlsPublicKey(publicKeyFromBytes(hexToBuffer(hex)));
36+
}
37+
38+
toBytes() {
39+
return publicKeyToBytes(this.publicKey);
40+
}
41+
42+
toString() {
43+
return this.publicKey.toHex();
44+
}
45+
46+
toJSON() {
47+
return this.toString();
48+
}
49+
}

src/serializable/fxs/common/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Address } from './address';
22
import { BlsSignature } from './blsSignature';
3+
import { BlsPublicKey } from './blsPublicKey';
34
import { Id } from './id';
45
import { NodeId } from './nodeId';
56

6-
export { Address, BlsSignature, Id, NodeId };
7+
export { Address, BlsSignature, BlsPublicKey, Id, NodeId };
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { bufferToBool, hexToBuffer, padLeft } from '../../utils/buffer';
2+
import { serializable } from '../common/types';
3+
import { Primitives } from './primatives';
4+
import { TypeSymbols } from '../constants';
5+
6+
/**
7+
* Number of bytes per bool.
8+
*/
9+
export const BOOL_LEN = 1;
10+
11+
@serializable()
12+
export class Bool extends Primitives {
13+
_type = TypeSymbols.Bool;
14+
constructor(private readonly bool: boolean) {
15+
super();
16+
}
17+
18+
static fromBytes(buf: Uint8Array): [Bool, Uint8Array] {
19+
return [
20+
new Bool(bufferToBool(buf.slice(0, BOOL_LEN))),
21+
buf.slice(BOOL_LEN),
22+
];
23+
}
24+
25+
toJSON() {
26+
return this.bool.toString();
27+
}
28+
29+
toBytes() {
30+
return padLeft(hexToBuffer(this.bool ? '1' : '0'), BOOL_LEN);
31+
}
32+
33+
value() {
34+
return this.bool;
35+
}
36+
}

0 commit comments

Comments
 (0)