-
-
Notifications
You must be signed in to change notification settings - Fork 266
/
blobsAssertion.ts
105 lines (90 loc) 路 3.69 KB
/
blobsAssertion.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import {randomBytes} from "node:crypto";
import {ApiError} from "@lodestar/api";
import {fromHex, toHex} from "@lodestar/utils";
import {SimulationAssertion, AssertionMatch, AssertionResult, NodePair} from "../interfaces.js";
import {EL_GENESIS_ACCOUNT, EL_GENESIS_SECRET_KEY, SIM_ENV_CHAIN_ID} from "../constants.js";
import {generateBlobsForTransaction} from "../utils/blobs.js";
import {BlobsEIP4844Transaction} from "../web3js/blobsEIP4844Transaction.js";
const numberOfBlobs = 6;
const sentBlobs: Uint8Array[] = [];
export function createBlobsAssertion(
nodes: NodePair[],
{sendBlobsAtSlot, validateBlobsAt}: {sendBlobsAtSlot: number; validateBlobsAt: number}
): SimulationAssertion<string, Uint8Array[]> {
return {
id: `blobs-${nodes.map((n) => n.id).join("-")}`,
match: ({slot}) => {
// Run capture every sendBlobsAtSlot -> validateBlobsAt and validate only at validateBlobsAt
return slot === validateBlobsAt
? AssertionMatch.Capture | AssertionMatch.Assert
: slot >= sendBlobsAtSlot && slot <= validateBlobsAt
? AssertionMatch.Capture
: AssertionMatch.None;
},
async capture({slot, node}) {
// TODO: Add feature to detect the node index during capture process
// slot starts from 1, so we add up 1
if (slot <= numberOfBlobs + 1 && node.id === "node-1") {
const {blobs, kzgCommitments, blobVersionedHashes, kzgProofs} = generateBlobsForTransaction(1);
const nonce = await node.execution.provider?.eth.getTransactionCount(EL_GENESIS_ACCOUNT);
const tx = BlobsEIP4844Transaction.fromTxData({
chainId: `0x${SIM_ENV_CHAIN_ID.toString(16)}`,
to: `0x${randomBytes(20).toString("hex")}`,
gasLimit: "0xc350",
maxPriorityFeePerGas: "0x3b9aca00",
maxFeePerGas: "0x3ba26b20",
maxFeePerBlobGas: "0x3e8",
value: "0x10000",
nonce: `0x${(nonce ?? 0).toString(16)}`,
blobVersionedHashes,
blobs,
kzgCommitments,
kzgProofs,
});
const signedTx = tx.sign(fromHex(`0x${EL_GENESIS_SECRET_KEY}`));
await node.execution.provider?.extended.sendRawTransaction(toHex(signedTx.serialize()));
sentBlobs.push(...blobs.map((b) => fromHex(b)));
}
const blobSideCars = await node.beacon.api.beacon.getBlobSidecars(slot);
ApiError.assert(blobSideCars);
return blobSideCars.response.data.map((b) => b.blob);
},
assert: async ({store}) => {
const errors: AssertionResult[] = [];
const blobs: Uint8Array[] = [];
for (let slot = sendBlobsAtSlot; slot <= validateBlobsAt; slot++) {
blobs.push(...(store[slot] ?? []));
}
if (blobs.length !== numberOfBlobs) {
errors.push([
"Node does not have right number of blobs",
{
expectedBlobs: numberOfBlobs,
currentBlobs: blobs.length,
},
]);
}
for (let i = 0; i < blobs.length; i++) {
if (!Buffer.from(blobs[i]).equals(Buffer.from(sentBlobs[i]))) {
errors.push(["Node does not have the correct blobs", {index: i}]);
}
}
return errors;
},
async dump({store, nodes}) {
const result: Record<string, string> = {
"expectedBlobs.txt": sentBlobs.map(toHex).join("\n"),
};
for (const node of nodes) {
const blobs: Uint8Array[] = [];
for (let slot = sendBlobsAtSlot; slot <= validateBlobsAt; slot++) {
if (store[node.beacon.id] !== undefined) {
blobs.push(...(store[node.beacon.id][slot] ?? []));
}
}
result[`blobs-${node.beacon.id}.txt`] = blobs.map(toHex).join("\n");
}
return result;
},
};
}