@aztec/stdlib 5.0.0-nightly.20260429 → 5.0.0-nightly.20260430
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dest/avm/avm.d.ts +300 -300
- package/dest/block/attestation_info.d.ts +3 -2
- package/dest/block/attestation_info.d.ts.map +1 -1
- package/dest/block/attestation_info.js +7 -5
- package/dest/block/block_data.d.ts +290 -1
- package/dest/block/block_data.d.ts.map +1 -1
- package/dest/block/block_data.js +14 -0
- package/dest/block/block_parameter.d.ts +30 -3
- package/dest/block/block_parameter.d.ts.map +1 -1
- package/dest/block/block_parameter.js +36 -2
- package/dest/block/l2_block_source.d.ts +39 -4
- package/dest/block/l2_block_source.d.ts.map +1 -1
- package/dest/block/proposal/attestations_and_signers.d.ts +13 -6
- package/dest/block/proposal/attestations_and_signers.d.ts.map +1 -1
- package/dest/block/proposal/attestations_and_signers.js +26 -18
- package/dest/checkpoint/checkpoint_data.d.ts +7 -1
- package/dest/checkpoint/checkpoint_data.d.ts.map +1 -1
- package/dest/checkpoint/checkpoint_data.js +2 -0
- package/dest/config/chain-config.d.ts +2 -2
- package/dest/config/chain-config.d.ts.map +1 -1
- package/dest/config/chain-config.js +2 -2
- package/dest/config/sequencer-config.d.ts +2 -2
- package/dest/config/sequencer-config.d.ts.map +1 -1
- package/dest/config/sequencer-config.js +6 -6
- package/dest/ha-signing/local_config.d.ts +1 -1
- package/dest/ha-signing/local_config.d.ts.map +1 -1
- package/dest/ha-signing/local_config.js +2 -2
- package/dest/interfaces/archiver.d.ts +1 -1
- package/dest/interfaces/archiver.d.ts.map +1 -1
- package/dest/interfaces/archiver.js +7 -3
- package/dest/interfaces/aztec-node-admin.d.ts +11 -1
- package/dest/interfaces/aztec-node-admin.d.ts.map +1 -1
- package/dest/interfaces/aztec-node-admin.js +2 -1
- package/dest/interfaces/aztec-node.d.ts +45 -55
- package/dest/interfaces/aztec-node.d.ts.map +1 -1
- package/dest/interfaces/aztec-node.js +18 -16
- package/dest/interfaces/block_response.d.ts +156 -0
- package/dest/interfaces/block_response.d.ts.map +1 -0
- package/dest/interfaces/block_response.js +24 -0
- package/dest/interfaces/chain_tips.d.ts +304 -0
- package/dest/interfaces/chain_tips.d.ts.map +1 -0
- package/dest/interfaces/chain_tips.js +11 -0
- package/dest/interfaces/checkpoint_parameter.d.ts +27 -0
- package/dest/interfaces/checkpoint_parameter.d.ts.map +1 -0
- package/dest/interfaces/checkpoint_parameter.js +20 -0
- package/dest/interfaces/checkpoint_response.d.ts +312 -0
- package/dest/interfaces/checkpoint_response.d.ts.map +1 -0
- package/dest/interfaces/checkpoint_response.js +26 -0
- package/dest/interfaces/client.d.ts +6 -1
- package/dest/interfaces/client.d.ts.map +1 -1
- package/dest/interfaces/client.js +5 -0
- package/dest/interfaces/configs.d.ts +7 -2
- package/dest/interfaces/configs.d.ts.map +1 -1
- package/dest/interfaces/configs.js +2 -1
- package/dest/interfaces/l1_publish_info.d.ts +43 -0
- package/dest/interfaces/l1_publish_info.d.ts.map +1 -0
- package/dest/interfaces/l1_publish_info.js +26 -0
- package/dest/interfaces/proving-job.d.ts +166 -166
- package/dest/interfaces/server.d.ts +6 -1
- package/dest/interfaces/server.d.ts.map +1 -1
- package/dest/interfaces/server.js +5 -0
- package/dest/interfaces/validator.d.ts +10 -1
- package/dest/interfaces/validator.d.ts.map +1 -1
- package/dest/interfaces/validator.js +1 -0
- package/dest/p2p/block_proposal.d.ts +19 -9
- package/dest/p2p/block_proposal.d.ts.map +1 -1
- package/dest/p2p/block_proposal.js +42 -32
- package/dest/p2p/checkpoint_attestation.d.ts +7 -3
- package/dest/p2p/checkpoint_attestation.d.ts.map +1 -1
- package/dest/p2p/checkpoint_attestation.js +15 -17
- package/dest/p2p/checkpoint_proposal.d.ts +15 -7
- package/dest/p2p/checkpoint_proposal.d.ts.map +1 -1
- package/dest/p2p/checkpoint_proposal.js +31 -29
- package/dest/p2p/consensus_payload.d.ts +18 -7
- package/dest/p2p/consensus_payload.d.ts.map +1 -1
- package/dest/p2p/consensus_payload.js +31 -19
- package/dest/p2p/signature_utils.d.ts +28 -19
- package/dest/p2p/signature_utils.d.ts.map +1 -1
- package/dest/p2p/signature_utils.js +118 -21
- package/dest/p2p/signed_txs.d.ts +15 -13
- package/dest/p2p/signed_txs.d.ts.map +1 -1
- package/dest/p2p/signed_txs.js +26 -24
- package/dest/tests/mocks.d.ts +7 -1
- package/dest/tests/mocks.d.ts.map +1 -1
- package/dest/tests/mocks.js +28 -14
- package/dest/timetable/index.d.ts +1 -1
- package/dest/timetable/index.d.ts.map +1 -1
- package/dest/timetable/index.js +25 -11
- package/dest/tx/profiling.js +4 -4
- package/package.json +8 -8
- package/src/block/attestation_info.ts +11 -11
- package/src/block/block_data.ts +17 -0
- package/src/block/block_parameter.ts +35 -2
- package/src/block/l2_block_source.ts +43 -3
- package/src/block/proposal/attestations_and_signers.ts +32 -17
- package/src/checkpoint/checkpoint_data.ts +4 -0
- package/src/config/chain-config.ts +2 -3
- package/src/config/sequencer-config.ts +10 -6
- package/src/ha-signing/local_config.ts +2 -2
- package/src/interfaces/archiver.ts +13 -3
- package/src/interfaces/aztec-node-admin.ts +3 -1
- package/src/interfaces/aztec-node.ts +105 -95
- package/src/interfaces/block_response.ts +79 -0
- package/src/interfaces/chain_tips.ts +24 -0
- package/src/interfaces/checkpoint_parameter.ts +22 -0
- package/src/interfaces/checkpoint_response.ts +84 -0
- package/src/interfaces/client.ts +5 -0
- package/src/interfaces/configs.ts +5 -1
- package/src/interfaces/l1_publish_info.ts +40 -0
- package/src/interfaces/server.ts +5 -0
- package/src/interfaces/validator.ts +5 -0
- package/src/p2p/block_proposal.ts +84 -28
- package/src/p2p/checkpoint_attestation.ts +15 -20
- package/src/p2p/checkpoint_proposal.ts +69 -37
- package/src/p2p/consensus_payload.ts +50 -28
- package/src/p2p/signature_utils.ts +110 -25
- package/src/p2p/signed_txs.ts +46 -28
- package/src/tests/mocks.ts +46 -26
- package/src/timetable/index.ts +26 -11
- package/src/tx/profiling.ts +4 -4
|
@@ -10,10 +10,21 @@ import { z } from 'zod';
|
|
|
10
10
|
import type { Checkpoint } from '../checkpoint/checkpoint.js';
|
|
11
11
|
import { CheckpointHeader } from '../rollup/checkpoint_header.js';
|
|
12
12
|
import type { CheckpointProposal, CheckpointProposalCore } from './checkpoint_proposal.js';
|
|
13
|
-
import
|
|
13
|
+
import {
|
|
14
|
+
type CoordinationSignatureContext,
|
|
15
|
+
type CoordinationSignatureType,
|
|
16
|
+
EMPTY_COORDINATION_SIGNATURE_CONTEXT,
|
|
17
|
+
type Signable,
|
|
18
|
+
coordinationSignatureContextEquals,
|
|
19
|
+
coordinationSignatureContextSchema,
|
|
20
|
+
readCoordinationSignatureContext,
|
|
21
|
+
serializeCoordinationSignatureContext,
|
|
22
|
+
} from './signature_utils.js';
|
|
14
23
|
|
|
15
24
|
/** Checkpoint consensus payload as signed by validators and verified on L1. */
|
|
16
25
|
export class ConsensusPayload implements Signable {
|
|
26
|
+
readonly primaryType: CoordinationSignatureType = 'CheckpointAttestation';
|
|
27
|
+
|
|
17
28
|
private size: number | undefined;
|
|
18
29
|
|
|
19
30
|
constructor(
|
|
@@ -22,7 +33,9 @@ export class ConsensusPayload implements Signable {
|
|
|
22
33
|
/** The archive root after the block is added */
|
|
23
34
|
public readonly archive: Fr,
|
|
24
35
|
/** The fee asset price modifier in basis points (from oracle) */
|
|
25
|
-
public readonly feeAssetPriceModifier: bigint
|
|
36
|
+
public readonly feeAssetPriceModifier: bigint,
|
|
37
|
+
/** The signing domain (chainId + rollupAddress) the signature is bound to */
|
|
38
|
+
public readonly signatureContext: CoordinationSignatureContext,
|
|
26
39
|
) {}
|
|
27
40
|
|
|
28
41
|
static get schema() {
|
|
@@ -31,36 +44,38 @@ export class ConsensusPayload implements Signable {
|
|
|
31
44
|
header: CheckpointHeader.schema,
|
|
32
45
|
archive: schemas.Fr,
|
|
33
46
|
feeAssetPriceModifier: schemas.BigInt,
|
|
47
|
+
signatureContext: coordinationSignatureContextSchema,
|
|
34
48
|
})
|
|
35
|
-
.transform(obj => new ConsensusPayload(obj.header, obj.archive, obj.feeAssetPriceModifier));
|
|
49
|
+
.transform(obj => new ConsensusPayload(obj.header, obj.archive, obj.feeAssetPriceModifier, obj.signatureContext));
|
|
36
50
|
}
|
|
37
51
|
|
|
38
|
-
static getFields(fields: FieldsOf<ConsensusPayload>) {
|
|
39
|
-
return [fields.header, fields.archive, fields.feeAssetPriceModifier] as const;
|
|
52
|
+
static getFields(fields: Omit<FieldsOf<ConsensusPayload>, 'primaryType'>) {
|
|
53
|
+
return [fields.header, fields.archive, fields.feeAssetPriceModifier, fields.signatureContext] as const;
|
|
40
54
|
}
|
|
41
55
|
|
|
42
|
-
getPayloadToSign(
|
|
56
|
+
getPayloadToSign(): Buffer {
|
|
57
|
+
// Matches the L1 ProposePayload struct in ProposeLib.sol.
|
|
43
58
|
const abi = parseAbiParameters(
|
|
44
|
-
'
|
|
45
|
-
'(' +
|
|
59
|
+
'(' +
|
|
46
60
|
'bytes32, ' + // archive
|
|
47
61
|
'(int256), ' + // oracleInput
|
|
48
62
|
'bytes32' + // headerHash
|
|
49
63
|
')',
|
|
50
64
|
);
|
|
51
65
|
const archiveRoot = this.archive.toString();
|
|
52
|
-
|
|
53
66
|
const headerHash = this.header.hash().toString();
|
|
54
|
-
const encodedData = encodeAbiParameters(abi, [
|
|
55
|
-
domainSeparator,
|
|
56
|
-
[archiveRoot, [this.feeAssetPriceModifier], headerHash],
|
|
57
|
-
] as const);
|
|
67
|
+
const encodedData = encodeAbiParameters(abi, [[archiveRoot, [this.feeAssetPriceModifier], headerHash]] as const);
|
|
58
68
|
|
|
59
69
|
return hexToBuffer(encodedData);
|
|
60
70
|
}
|
|
61
71
|
|
|
62
72
|
toBuffer(): Buffer {
|
|
63
|
-
return serializeToBuffer([
|
|
73
|
+
return serializeToBuffer([
|
|
74
|
+
this.header,
|
|
75
|
+
this.archive,
|
|
76
|
+
serializeSignedBigInt(this.feeAssetPriceModifier),
|
|
77
|
+
serializeCoordinationSignatureContext(this.signatureContext),
|
|
78
|
+
]);
|
|
64
79
|
}
|
|
65
80
|
|
|
66
81
|
public equals(other: ConsensusPayload | CheckpointProposal | CheckpointProposalCore): boolean {
|
|
@@ -69,34 +84,39 @@ export class ConsensusPayload implements Signable {
|
|
|
69
84
|
return (
|
|
70
85
|
this.header.equals(otherHeader) &&
|
|
71
86
|
this.archive.equals(other.archive) &&
|
|
72
|
-
this.feeAssetPriceModifier === otherModifier
|
|
87
|
+
this.feeAssetPriceModifier === otherModifier &&
|
|
88
|
+
coordinationSignatureContextEquals(this.signatureContext, other.signatureContext)
|
|
73
89
|
);
|
|
74
90
|
}
|
|
75
91
|
|
|
76
92
|
static fromBuffer(buf: Buffer | BufferReader): ConsensusPayload {
|
|
77
93
|
const reader = BufferReader.asReader(buf);
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
);
|
|
83
|
-
return payload;
|
|
94
|
+
const header = reader.readObject(CheckpointHeader);
|
|
95
|
+
const archive = reader.readObject(Fr);
|
|
96
|
+
const feeAssetPriceModifier = reader.readInt256();
|
|
97
|
+
const signatureContext = readCoordinationSignatureContext(reader);
|
|
98
|
+
return new ConsensusPayload(header, archive, feeAssetPriceModifier, signatureContext);
|
|
84
99
|
}
|
|
85
100
|
|
|
86
|
-
static fromFields(fields: FieldsOf<ConsensusPayload>): ConsensusPayload {
|
|
87
|
-
return new ConsensusPayload(fields.header, fields.archive, fields.feeAssetPriceModifier);
|
|
101
|
+
static fromFields(fields: Omit<FieldsOf<ConsensusPayload>, 'primaryType'>): ConsensusPayload {
|
|
102
|
+
return new ConsensusPayload(fields.header, fields.archive, fields.feeAssetPriceModifier, fields.signatureContext);
|
|
88
103
|
}
|
|
89
104
|
|
|
90
|
-
static fromCheckpoint(checkpoint: Checkpoint): ConsensusPayload {
|
|
91
|
-
return new ConsensusPayload(
|
|
105
|
+
static fromCheckpoint(checkpoint: Checkpoint, signatureContext: CoordinationSignatureContext): ConsensusPayload {
|
|
106
|
+
return new ConsensusPayload(
|
|
107
|
+
checkpoint.header,
|
|
108
|
+
checkpoint.archive.root,
|
|
109
|
+
checkpoint.feeAssetPriceModifier,
|
|
110
|
+
signatureContext,
|
|
111
|
+
);
|
|
92
112
|
}
|
|
93
113
|
|
|
94
114
|
static empty(): ConsensusPayload {
|
|
95
|
-
return new ConsensusPayload(CheckpointHeader.empty(), Fr.ZERO, 0n);
|
|
115
|
+
return new ConsensusPayload(CheckpointHeader.empty(), Fr.ZERO, 0n, EMPTY_COORDINATION_SIGNATURE_CONTEXT);
|
|
96
116
|
}
|
|
97
117
|
|
|
98
118
|
static random(): ConsensusPayload {
|
|
99
|
-
return new ConsensusPayload(CheckpointHeader.random(), Fr.random(), 0n);
|
|
119
|
+
return new ConsensusPayload(CheckpointHeader.random(), Fr.random(), 0n, EMPTY_COORDINATION_SIGNATURE_CONTEXT);
|
|
100
120
|
}
|
|
101
121
|
|
|
102
122
|
/**
|
|
@@ -117,10 +137,12 @@ export class ConsensusPayload implements Signable {
|
|
|
117
137
|
header: this.header.toInspect(),
|
|
118
138
|
archive: this.archive.toString(),
|
|
119
139
|
feeAssetPriceModifier: this.feeAssetPriceModifier.toString(),
|
|
140
|
+
chainId: this.signatureContext.chainId,
|
|
141
|
+
rollupAddress: this.signatureContext.rollupAddress.toString(),
|
|
120
142
|
};
|
|
121
143
|
}
|
|
122
144
|
|
|
123
145
|
toString() {
|
|
124
|
-
return `header: ${this.header.toString()}, archive: ${this.archive.toString()}, feeAssetPriceModifier: ${this.feeAssetPriceModifier}}`;
|
|
146
|
+
return `header: ${this.header.toString()}, archive: ${this.archive.toString()}, feeAssetPriceModifier: ${this.feeAssetPriceModifier}, chainId: ${this.signatureContext.chainId}, rollupAddress: ${this.signatureContext.rollupAddress.toString()}`;
|
|
125
147
|
}
|
|
126
148
|
}
|
|
@@ -1,37 +1,122 @@
|
|
|
1
1
|
import { Buffer32 } from '@aztec/foundation/buffer';
|
|
2
2
|
import { keccak256 } from '@aztec/foundation/crypto/keccak';
|
|
3
|
-
import {
|
|
3
|
+
import { tryRecoverAddress } from '@aztec/foundation/crypto/secp256k1-signer';
|
|
4
|
+
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
5
|
+
import type { Signature } from '@aztec/foundation/eth-signature';
|
|
6
|
+
import { type BufferReader, serializeToBuffer } from '@aztec/foundation/serialize';
|
|
4
7
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
import { type TypedDataDefinition, hashTypedData } from 'viem';
|
|
9
|
+
import { z } from 'zod';
|
|
10
|
+
|
|
11
|
+
import type { ZodFor } from '../schemas/index.js';
|
|
12
|
+
|
|
13
|
+
export type CoordinationSignatureType =
|
|
14
|
+
| 'BlockProposal'
|
|
15
|
+
| 'CheckpointProposal'
|
|
16
|
+
| 'CheckpointAttestation'
|
|
17
|
+
| 'AttestationsAndSigners'
|
|
18
|
+
| 'SignedTxs';
|
|
19
|
+
|
|
20
|
+
export type CoordinationSignatureContext = {
|
|
21
|
+
chainId: number;
|
|
22
|
+
rollupAddress: EthAddress;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const EMPTY_COORDINATION_SIGNATURE_CONTEXT: CoordinationSignatureContext = {
|
|
26
|
+
chainId: 0,
|
|
27
|
+
rollupAddress: EthAddress.ZERO,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const coordinationSignatureContextSchema: ZodFor<CoordinationSignatureContext> = z.object({
|
|
31
|
+
chainId: z.number(),
|
|
32
|
+
rollupAddress: EthAddress.schema,
|
|
33
|
+
});
|
|
12
34
|
|
|
13
35
|
export interface Signable {
|
|
14
|
-
|
|
36
|
+
readonly primaryType: CoordinationSignatureType;
|
|
37
|
+
readonly signatureContext: CoordinationSignatureContext;
|
|
38
|
+
getPayloadToSign(): Buffer;
|
|
15
39
|
}
|
|
16
40
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
41
|
+
export function coordinationSignatureContextEquals(
|
|
42
|
+
a: CoordinationSignatureContext,
|
|
43
|
+
b: CoordinationSignatureContext,
|
|
44
|
+
): boolean {
|
|
45
|
+
return a.chainId === b.chainId && a.rollupAddress.equals(b.rollupAddress);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function serializeCoordinationSignatureContext(ctx: CoordinationSignatureContext): Buffer {
|
|
49
|
+
return serializeToBuffer([ctx.chainId, ctx.rollupAddress]);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function readCoordinationSignatureContext(reader: BufferReader): CoordinationSignatureContext {
|
|
53
|
+
const chainId = reader.readNumber();
|
|
54
|
+
const rollupAddress = reader.readObject(EthAddress);
|
|
55
|
+
return { chainId, rollupAddress };
|
|
24
56
|
}
|
|
25
57
|
|
|
26
58
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
59
|
+
* Returns true if the signable carries a context matching the node's expected context.
|
|
60
|
+
* Use this at the P2P ingress boundary to reject foreign-chain messages cheaply before
|
|
61
|
+
* performing any signature recovery.
|
|
30
62
|
*/
|
|
31
|
-
export function
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
63
|
+
export function hasValidSignatureContext(signable: Signable, expected: CoordinationSignatureContext): boolean {
|
|
64
|
+
return coordinationSignatureContextEquals(signable.signatureContext, expected);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const COORDINATION_SIGNATURE_NAME = 'Aztec Rollup';
|
|
68
|
+
const COORDINATION_SIGNATURE_VERSION = '1';
|
|
69
|
+
|
|
70
|
+
const EIP712_DOMAIN_FIELDS = [
|
|
71
|
+
{ name: 'name', type: 'string' },
|
|
72
|
+
{ name: 'version', type: 'string' },
|
|
73
|
+
{ name: 'chainId', type: 'uint256' },
|
|
74
|
+
{ name: 'verifyingContract', type: 'address' },
|
|
75
|
+
] as const;
|
|
76
|
+
|
|
77
|
+
const COORDINATION_SIGNATURE_TYPES = {
|
|
78
|
+
EIP712Domain: EIP712_DOMAIN_FIELDS,
|
|
79
|
+
BlockProposal: [{ name: 'payloadHash', type: 'bytes32' }],
|
|
80
|
+
CheckpointProposal: [{ name: 'payloadHash', type: 'bytes32' }],
|
|
81
|
+
CheckpointAttestation: [{ name: 'payloadHash', type: 'bytes32' }],
|
|
82
|
+
AttestationsAndSigners: [{ name: 'payloadHash', type: 'bytes32' }],
|
|
83
|
+
SignedTxs: [{ name: 'payloadHash', type: 'bytes32' }],
|
|
84
|
+
} as const;
|
|
85
|
+
|
|
86
|
+
export function getCoordinationSignatureTypedDataForPayloadHash(
|
|
87
|
+
payloadHash: Buffer32,
|
|
88
|
+
type: CoordinationSignatureType,
|
|
89
|
+
context: CoordinationSignatureContext,
|
|
90
|
+
): TypedDataDefinition {
|
|
91
|
+
return {
|
|
92
|
+
domain: {
|
|
93
|
+
name: COORDINATION_SIGNATURE_NAME,
|
|
94
|
+
version: COORDINATION_SIGNATURE_VERSION,
|
|
95
|
+
chainId: context.chainId,
|
|
96
|
+
verifyingContract: context.rollupAddress.toString() as `0x${string}`,
|
|
97
|
+
},
|
|
98
|
+
types: COORDINATION_SIGNATURE_TYPES,
|
|
99
|
+
primaryType: type,
|
|
100
|
+
message: {
|
|
101
|
+
payloadHash: payloadHash.toString() as `0x${string}`,
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function getCoordinationSignatureTypedData(signable: Signable): TypedDataDefinition {
|
|
107
|
+
const payloadHash = getHashedSignaturePayload(signable);
|
|
108
|
+
return getCoordinationSignatureTypedDataForPayloadHash(payloadHash, signable.primaryType, signable.signatureContext);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function getHashedSignaturePayloadTypedData(signable: Signable): Buffer32 {
|
|
112
|
+
return Buffer32.fromString(hashTypedData(getCoordinationSignatureTypedData(signable)));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function recoverCoordinationSigner(signable: Signable, signature: Signature): EthAddress | undefined {
|
|
116
|
+
const digest = getHashedSignaturePayloadTypedData(signable);
|
|
117
|
+
return tryRecoverAddress(digest, signature, { allowYParityAsV: true });
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function getHashedSignaturePayload(s: Signable): Buffer32 {
|
|
121
|
+
return Buffer32.fromBuffer(keccak256(s.getPayloadToSign()));
|
|
37
122
|
}
|
package/src/p2p/signed_txs.ts
CHANGED
|
@@ -1,15 +1,20 @@
|
|
|
1
|
-
import { Buffer32 } from '@aztec/foundation/buffer';
|
|
2
|
-
import { tryRecoverAddress } from '@aztec/foundation/crypto/secp256k1-signer';
|
|
3
1
|
import type { EthAddress } from '@aztec/foundation/eth-address';
|
|
4
2
|
import { Signature } from '@aztec/foundation/eth-signature';
|
|
5
3
|
import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize';
|
|
6
4
|
|
|
5
|
+
import type { TypedDataDefinition } from 'viem';
|
|
6
|
+
|
|
7
7
|
import { MAX_TXS_PER_BLOCK } from '../deserialization/index.js';
|
|
8
8
|
import { Tx } from '../tx/tx.js';
|
|
9
9
|
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
type CoordinationSignatureContext,
|
|
11
|
+
type CoordinationSignatureType,
|
|
12
|
+
EMPTY_COORDINATION_SIGNATURE_CONTEXT,
|
|
13
|
+
type Signable,
|
|
14
|
+
getCoordinationSignatureTypedData,
|
|
15
|
+
readCoordinationSignatureContext,
|
|
16
|
+
recoverCoordinationSigner,
|
|
17
|
+
serializeCoordinationSignatureContext,
|
|
13
18
|
} from './signature_utils.js';
|
|
14
19
|
|
|
15
20
|
/**
|
|
@@ -17,50 +22,56 @@ import {
|
|
|
17
22
|
* The signature is over the transaction objects themselves, providing
|
|
18
23
|
* data availability guarantees beyond just the transaction hashes.
|
|
19
24
|
*/
|
|
20
|
-
export class SignedTxs {
|
|
21
|
-
|
|
25
|
+
export class SignedTxs implements Signable {
|
|
26
|
+
readonly primaryType: CoordinationSignatureType = 'SignedTxs';
|
|
27
|
+
|
|
28
|
+
private cachedSender: EthAddress | undefined | null = undefined;
|
|
22
29
|
|
|
23
30
|
constructor(
|
|
24
31
|
/** The transactions */
|
|
25
32
|
public readonly txs: Tx[],
|
|
26
33
|
/** The proposer's signature over the transactions */
|
|
27
34
|
public readonly signature: Signature,
|
|
35
|
+
/** The signing domain (chainId + rollupAddress) the signature is bound to */
|
|
36
|
+
public readonly signatureContext: CoordinationSignatureContext,
|
|
28
37
|
) {}
|
|
29
38
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
*/
|
|
33
|
-
getPayloadToSign(domainSeparator: SignatureDomainSeparator): Buffer {
|
|
34
|
-
return serializeToBuffer([domainSeparator, this.txs.length, this.txs]);
|
|
39
|
+
getPayloadToSign(): Buffer {
|
|
40
|
+
return serializeToBuffer([this.txs.length, this.txs]);
|
|
35
41
|
}
|
|
36
42
|
|
|
37
43
|
/**
|
|
38
|
-
* Lazily evaluate the sender of the signed txs; result is cached
|
|
44
|
+
* Lazily evaluate the sender of the signed txs; result is cached.
|
|
39
45
|
* @returns The sender address, or undefined if signature recovery fails
|
|
40
46
|
*/
|
|
41
47
|
getSender(): EthAddress | undefined {
|
|
42
|
-
if (
|
|
43
|
-
|
|
44
|
-
this.sender = tryRecoverAddress(hashed, this.signature);
|
|
48
|
+
if (this.cachedSender === undefined) {
|
|
49
|
+
this.cachedSender = recoverCoordinationSigner(this, this.signature) ?? null;
|
|
45
50
|
}
|
|
46
|
-
return this.
|
|
51
|
+
return this.cachedSender ?? undefined;
|
|
47
52
|
}
|
|
48
53
|
|
|
49
54
|
/**
|
|
50
|
-
* Create SignedTxs from a signer function
|
|
55
|
+
* Create SignedTxs from a typed-data signer function
|
|
51
56
|
*/
|
|
52
57
|
static async createFromSigner(
|
|
53
58
|
txs: Tx[],
|
|
54
|
-
|
|
59
|
+
signatureContext: CoordinationSignatureContext,
|
|
60
|
+
typedDataSigner: (typedData: TypedDataDefinition) => Promise<Signature>,
|
|
55
61
|
): Promise<SignedTxs> {
|
|
56
|
-
const tempSignedTxs = new SignedTxs(txs, Signature.empty());
|
|
57
|
-
const
|
|
58
|
-
const signature = await
|
|
59
|
-
return new SignedTxs(txs, signature);
|
|
62
|
+
const tempSignedTxs = new SignedTxs(txs, Signature.empty(), signatureContext);
|
|
63
|
+
const typedData = getCoordinationSignatureTypedData(tempSignedTxs);
|
|
64
|
+
const signature = await typedDataSigner(typedData);
|
|
65
|
+
return new SignedTxs(txs, signature, signatureContext);
|
|
60
66
|
}
|
|
61
67
|
|
|
62
68
|
toBuffer(): Buffer {
|
|
63
|
-
return serializeToBuffer([
|
|
69
|
+
return serializeToBuffer([
|
|
70
|
+
this.txs.length,
|
|
71
|
+
this.txs,
|
|
72
|
+
this.signature,
|
|
73
|
+
serializeCoordinationSignatureContext(this.signatureContext),
|
|
74
|
+
]);
|
|
64
75
|
}
|
|
65
76
|
|
|
66
77
|
static fromBuffer(buf: Buffer | BufferReader): SignedTxs {
|
|
@@ -71,18 +82,25 @@ export class SignedTxs {
|
|
|
71
82
|
}
|
|
72
83
|
const txs = reader.readArray(txCount, Tx);
|
|
73
84
|
const signature = reader.readObject(Signature);
|
|
74
|
-
|
|
85
|
+
const signatureContext = readCoordinationSignatureContext(reader);
|
|
86
|
+
return new SignedTxs(txs, signature, signatureContext);
|
|
75
87
|
}
|
|
76
88
|
|
|
77
89
|
getSize(): number {
|
|
78
|
-
return
|
|
90
|
+
return (
|
|
91
|
+
4 /* txs.length */ +
|
|
92
|
+
this.txs.reduce((acc, tx) => acc + tx.getSize(), 0) +
|
|
93
|
+
this.signature.getSize() +
|
|
94
|
+
4 /* chainId */ +
|
|
95
|
+
20 /* rollupAddress */
|
|
96
|
+
);
|
|
79
97
|
}
|
|
80
98
|
|
|
81
99
|
static empty(): SignedTxs {
|
|
82
|
-
return new SignedTxs([], Signature.empty());
|
|
100
|
+
return new SignedTxs([], Signature.empty(), EMPTY_COORDINATION_SIGNATURE_CONTEXT);
|
|
83
101
|
}
|
|
84
102
|
|
|
85
103
|
static random(): SignedTxs {
|
|
86
|
-
return new SignedTxs([Tx.random(), Tx.random()], Signature.random());
|
|
104
|
+
return new SignedTxs([Tx.random(), Tx.random()], Signature.random(), EMPTY_COORDINATION_SIGNATURE_CONTEXT);
|
|
87
105
|
}
|
|
88
106
|
}
|
package/src/tests/mocks.ts
CHANGED
|
@@ -14,8 +14,11 @@ import { padArrayEnd, times } from '@aztec/foundation/collection';
|
|
|
14
14
|
import { randomBytes } from '@aztec/foundation/crypto/random';
|
|
15
15
|
import { Secp256k1Signer } from '@aztec/foundation/crypto/secp256k1-signer';
|
|
16
16
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
17
|
+
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
17
18
|
import { Signature } from '@aztec/foundation/eth-signature';
|
|
18
19
|
|
|
20
|
+
import { type TypedDataDefinition, hashTypedData } from 'viem';
|
|
21
|
+
|
|
19
22
|
import type { ContractArtifact } from '../abi/abi.js';
|
|
20
23
|
import { PublicTxEffect } from '../avm/avm.js';
|
|
21
24
|
import type { AvmAccumulatedData } from '../avm/avm_accumulated_data.js';
|
|
@@ -51,7 +54,7 @@ import { BlockProposal } from '../p2p/block_proposal.js';
|
|
|
51
54
|
import { CheckpointAttestation } from '../p2p/checkpoint_attestation.js';
|
|
52
55
|
import { CheckpointProposal } from '../p2p/checkpoint_proposal.js';
|
|
53
56
|
import { ConsensusPayload } from '../p2p/consensus_payload.js';
|
|
54
|
-
import {
|
|
57
|
+
import { type CoordinationSignatureContext, getHashedSignaturePayloadTypedData } from '../p2p/signature_utils.js';
|
|
55
58
|
import { ChonkProof } from '../proofs/chonk_proof.js';
|
|
56
59
|
import { ProvingRequestType } from '../proofs/proving_request_type.js';
|
|
57
60
|
import { CheckpointHeader } from '../rollup/checkpoint_header.js';
|
|
@@ -86,6 +89,15 @@ import {
|
|
|
86
89
|
makePublicDataWrite,
|
|
87
90
|
} from './factories.js';
|
|
88
91
|
|
|
92
|
+
export const TEST_COORDINATION_SIGNATURE_CONTEXT: CoordinationSignatureContext = {
|
|
93
|
+
chainId: 31337,
|
|
94
|
+
rollupAddress: EthAddress.fromNumber(1),
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
function signTypedData(signer: Secp256k1Signer, typedData: TypedDataDefinition): Signature {
|
|
98
|
+
return signer.sign(Buffer32.fromString(hashTypedData(typedData)));
|
|
99
|
+
}
|
|
100
|
+
|
|
89
101
|
export const randomTxHash = (): TxHash => TxHash.random();
|
|
90
102
|
|
|
91
103
|
export const mockTx = async (
|
|
@@ -519,6 +531,7 @@ export interface MakeConsensusPayloadOptions {
|
|
|
519
531
|
txHashes?: TxHash[];
|
|
520
532
|
txs?: Tx[];
|
|
521
533
|
feeAssetPriceModifier?: bigint;
|
|
534
|
+
signatureContext?: CoordinationSignatureContext;
|
|
522
535
|
}
|
|
523
536
|
|
|
524
537
|
export interface MakeBlockProposalOptions {
|
|
@@ -529,6 +542,7 @@ export interface MakeBlockProposalOptions {
|
|
|
529
542
|
archiveRoot?: Fr;
|
|
530
543
|
txHashes?: TxHash[];
|
|
531
544
|
txs?: Tx[];
|
|
545
|
+
signatureContext?: CoordinationSignatureContext;
|
|
532
546
|
}
|
|
533
547
|
|
|
534
548
|
export interface MakeCheckpointProposalOptions {
|
|
@@ -536,6 +550,7 @@ export interface MakeCheckpointProposalOptions {
|
|
|
536
550
|
checkpointHeader?: CheckpointHeader;
|
|
537
551
|
archiveRoot?: Fr;
|
|
538
552
|
feeAssetPriceModifier?: bigint;
|
|
553
|
+
signatureContext?: CoordinationSignatureContext;
|
|
539
554
|
/** Options for the lastBlock - if undefined, no lastBlock is included */
|
|
540
555
|
lastBlock?: {
|
|
541
556
|
blockHeader?: BlockHeader;
|
|
@@ -546,10 +561,7 @@ export interface MakeCheckpointProposalOptions {
|
|
|
546
561
|
}
|
|
547
562
|
|
|
548
563
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
549
|
-
const makeAndSignConsensusPayload = (
|
|
550
|
-
domainSeparator: SignatureDomainSeparator,
|
|
551
|
-
options?: MakeConsensusPayloadOptions,
|
|
552
|
-
) => {
|
|
564
|
+
const makeAndSignConsensusPayload = (options?: MakeConsensusPayloadOptions) => {
|
|
553
565
|
const header = options?.header ?? makeCheckpointHeader(1);
|
|
554
566
|
const { signer = Secp256k1Signer.random(), archive = Fr.random(), feeAssetPriceModifier = 0n } = options ?? {};
|
|
555
567
|
|
|
@@ -557,9 +569,10 @@ const makeAndSignConsensusPayload = (
|
|
|
557
569
|
header,
|
|
558
570
|
archive,
|
|
559
571
|
feeAssetPriceModifier,
|
|
572
|
+
signatureContext: TEST_COORDINATION_SIGNATURE_CONTEXT,
|
|
560
573
|
});
|
|
561
574
|
|
|
562
|
-
const hash =
|
|
575
|
+
const hash = getHashedSignaturePayloadTypedData(payload);
|
|
563
576
|
const signature = signer.sign(hash);
|
|
564
577
|
|
|
565
578
|
return { blockNumber: header.slotNumber, payload, signature };
|
|
@@ -569,10 +582,7 @@ export const makeAndSignCommitteeAttestationsAndSigners = (
|
|
|
569
582
|
attestationsAndSigners: CommitteeAttestationsAndSigners,
|
|
570
583
|
signer: Secp256k1Signer = Secp256k1Signer.random(),
|
|
571
584
|
) => {
|
|
572
|
-
const hash =
|
|
573
|
-
attestationsAndSigners,
|
|
574
|
-
SignatureDomainSeparator.attestationsAndSigners,
|
|
575
|
-
);
|
|
585
|
+
const hash = getHashedSignaturePayloadTypedData(attestationsAndSigners);
|
|
576
586
|
return signer.sign(hash);
|
|
577
587
|
};
|
|
578
588
|
|
|
@@ -584,6 +594,7 @@ export const makeBlockProposal = (options?: MakeBlockProposalOptions): Promise<B
|
|
|
584
594
|
const txHashes = options?.txHashes ?? [0, 1, 2, 3, 4, 5].map(() => TxHash.random());
|
|
585
595
|
const txs = options?.txs;
|
|
586
596
|
const signer = options?.signer ?? Secp256k1Signer.random();
|
|
597
|
+
const signatureContext = options?.signatureContext ?? TEST_COORDINATION_SIGNATURE_CONTEXT;
|
|
587
598
|
|
|
588
599
|
return BlockProposal.createProposalFromSigner(
|
|
589
600
|
blockHeader,
|
|
@@ -593,7 +604,9 @@ export const makeBlockProposal = (options?: MakeBlockProposalOptions): Promise<B
|
|
|
593
604
|
archiveRoot,
|
|
594
605
|
txHashes,
|
|
595
606
|
txs,
|
|
596
|
-
|
|
607
|
+
signatureContext,
|
|
608
|
+
(typedData, _context) => Promise.resolve(signTypedData(signer, typedData)),
|
|
609
|
+
(typedData, _context) => Promise.resolve(signTypedData(signer, typedData)),
|
|
597
610
|
);
|
|
598
611
|
};
|
|
599
612
|
|
|
@@ -602,6 +615,7 @@ export const makeCheckpointProposal = async (options?: MakeCheckpointProposalOpt
|
|
|
602
615
|
const archiveRoot = options?.archiveRoot ?? Fr.random();
|
|
603
616
|
const feeAssetPriceModifier = options?.feeAssetPriceModifier ?? 0n;
|
|
604
617
|
const signer = options?.signer ?? Secp256k1Signer.random();
|
|
618
|
+
const signatureContext = options?.signatureContext ?? TEST_COORDINATION_SIGNATURE_CONTEXT;
|
|
605
619
|
|
|
606
620
|
// Build a signed block proposal if lastBlock options are provided
|
|
607
621
|
const lastBlockProposal = options?.lastBlock
|
|
@@ -613,6 +627,7 @@ export const makeCheckpointProposal = async (options?: MakeCheckpointProposalOpt
|
|
|
613
627
|
txHashes: options.lastBlock.txHashes,
|
|
614
628
|
txs: options.lastBlock.txs,
|
|
615
629
|
signer,
|
|
630
|
+
signatureContext,
|
|
616
631
|
})
|
|
617
632
|
: undefined;
|
|
618
633
|
|
|
@@ -622,7 +637,8 @@ export const makeCheckpointProposal = async (options?: MakeCheckpointProposalOpt
|
|
|
622
637
|
CheckpointNumber(1),
|
|
623
638
|
feeAssetPriceModifier,
|
|
624
639
|
lastBlockProposal,
|
|
625
|
-
|
|
640
|
+
signatureContext,
|
|
641
|
+
typedData => Promise.resolve(signTypedData(signer, typedData)),
|
|
626
642
|
);
|
|
627
643
|
};
|
|
628
644
|
|
|
@@ -636,6 +652,7 @@ export type MakeCheckpointAttestationOptions = {
|
|
|
636
652
|
attesterSigner?: Secp256k1Signer;
|
|
637
653
|
proposerSigner?: Secp256k1Signer;
|
|
638
654
|
signer?: Secp256k1Signer;
|
|
655
|
+
signatureContext?: CoordinationSignatureContext;
|
|
639
656
|
};
|
|
640
657
|
|
|
641
658
|
/**
|
|
@@ -645,26 +662,27 @@ export const makeCheckpointAttestation = (options: MakeCheckpointAttestationOpti
|
|
|
645
662
|
const header = options.header ?? makeCheckpointHeader(1);
|
|
646
663
|
const archive = options.archive ?? Fr.random();
|
|
647
664
|
const feeAssetPriceModifier = options.feeAssetPriceModifier ?? 0n;
|
|
665
|
+
const signatureContext = options.signatureContext ?? TEST_COORDINATION_SIGNATURE_CONTEXT;
|
|
648
666
|
const { signer, attesterSigner = signer, proposerSigner = signer } = options;
|
|
649
667
|
|
|
650
|
-
const payload = new ConsensusPayload(header, archive, feeAssetPriceModifier);
|
|
668
|
+
const payload = new ConsensusPayload(header, archive, feeAssetPriceModifier, signatureContext);
|
|
651
669
|
|
|
652
670
|
// Sign as attester
|
|
653
|
-
const attestationHash =
|
|
654
|
-
payload,
|
|
655
|
-
SignatureDomainSeparator.checkpointAttestation,
|
|
656
|
-
);
|
|
671
|
+
const attestationHash = getHashedSignaturePayloadTypedData(payload);
|
|
657
672
|
const attestationSigner = attesterSigner ?? Secp256k1Signer.random();
|
|
658
673
|
const attestationSignature = attestationSigner.sign(attestationHash);
|
|
659
674
|
|
|
660
675
|
// Sign as proposer - use CheckpointProposal's payload format (serializeToBuffer)
|
|
661
676
|
// This is different from ConsensusPayload's format (ABI encoding)
|
|
662
677
|
const proposalSignerToUse = proposerSigner ?? Secp256k1Signer.random();
|
|
663
|
-
const tempProposal = new CheckpointProposal(
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
678
|
+
const tempProposal = new CheckpointProposal(
|
|
679
|
+
header,
|
|
680
|
+
archive,
|
|
681
|
+
feeAssetPriceModifier,
|
|
682
|
+
Signature.empty(),
|
|
683
|
+
signatureContext,
|
|
667
684
|
);
|
|
685
|
+
const proposalHash = getHashedSignaturePayloadTypedData(tempProposal);
|
|
668
686
|
const proposerSignature = proposalSignerToUse.sign(proposalHash);
|
|
669
687
|
|
|
670
688
|
return new CheckpointAttestation(payload, attestationSignature, proposerSignature);
|
|
@@ -677,13 +695,15 @@ export const makeCheckpointAttestationFromProposal = (
|
|
|
677
695
|
proposal: CheckpointProposal,
|
|
678
696
|
attesterSigner?: Secp256k1Signer,
|
|
679
697
|
): CheckpointAttestation => {
|
|
680
|
-
const payload = new ConsensusPayload(
|
|
698
|
+
const payload = new ConsensusPayload(
|
|
699
|
+
proposal.checkpointHeader,
|
|
700
|
+
proposal.archive,
|
|
701
|
+
proposal.feeAssetPriceModifier,
|
|
702
|
+
proposal.signatureContext,
|
|
703
|
+
);
|
|
681
704
|
|
|
682
705
|
// Sign as attester
|
|
683
|
-
const attestationHash =
|
|
684
|
-
payload,
|
|
685
|
-
SignatureDomainSeparator.checkpointAttestation,
|
|
686
|
-
);
|
|
706
|
+
const attestationHash = getHashedSignaturePayloadTypedData(payload);
|
|
687
707
|
const attestationSigner = attesterSigner ?? Secp256k1Signer.random();
|
|
688
708
|
const attestationSignature = attestationSigner.sign(attestationHash);
|
|
689
709
|
|