pulsar-contracts
Version:
180 lines • 8.82 kB
JavaScript
import { Field, Poseidon, PrivateKey, Signature, UInt64, } from 'o1js';
import { BlockList, MultisigVerifierProgram, } from '../SettlementProof.js';
import { GenerateValidateReduceProof, GenerateSettlementPublicInput, MergeSettlementProofs, GeneratePulsarBlock, } from './generateFunctions.js';
import { ValidateReducePublicInput } from '../ValidateReduce.js';
import { SignaturePublicKeyList, SignaturePublicKeyMatrix, } from '../types/signaturePubKeyList.js';
import { List } from '../types/common.js';
import { PulsarAction } from '../types/PulsarAction.js';
import { ProofGenerators } from '../types/proofGenerators.js';
import { actionListAdd, emptyActionListHash, merkleActionsAdd, } from '../types/actionHelpers.js';
import { BATCH_SIZE, MAX_DEPOSIT_PER_BATCH, MAX_SETTLEMENT_PER_BATCH, MAX_WITHDRAWAL_PER_BATCH, SETTLEMENT_MATRIX_SIZE, } from './constants.js';
export const TestUtils = {
GenerateSignaturePubKeyList,
GenerateSignaturePubKeyMatrix,
GenerateReducerSignatureList,
GenerateTestSettlementProof,
MockReducerVerifierProof,
GenerateTestActions,
CalculateActionRoot,
GenerateTestBlocks,
CreateValidatorMerkleList,
CalculateFromMockActions,
};
function GenerateSignaturePubKeyList(signatureMessage, signerSet) {
const signatures = [];
for (let i = 0; i < signerSet.length; i++) {
signatures.push(Signature.create(signerSet[i][0], signatureMessage));
}
return SignaturePublicKeyList.fromArray(signatures.map((signature, i) => [signature, signerSet[i][1]]));
}
function GenerateSignaturePubKeyMatrix(blocks, signerSet) {
const signatureMatrix = [];
for (let i = 0; i < SETTLEMENT_MATRIX_SIZE; i++) {
signatureMatrix.push(GenerateSignaturePubKeyList(blocks[i].hash().toFields(), signerSet[i]));
}
return SignaturePublicKeyMatrix.fromArray(signatureMatrix.map((list) => list.list.map((item) => [item.signature, item.publicKey])));
}
function GenerateReducerSignatureList(publicInput, proofGeneratorsList) {
const signatures = [];
const message = publicInput.hash().toFields();
for (let i = 0; i < proofGeneratorsList.length; i++) {
signatures.push(Signature.create(proofGeneratorsList[i][0], message));
}
return SignaturePublicKeyList.fromArray(signatures.map((signature, i) => [signature, proofGeneratorsList[i][1]]));
}
function CreateValidatorMerkleList(validatorSet) {
const merkleList = List.empty();
for (let i = 0; i < validatorSet.length; i++) {
const [, publicKey] = validatorSet[i];
merkleList.push(Poseidon.hash(publicKey.toFields()));
}
return merkleList;
}
async function GenerateTestSettlementProof(validatorSet, initialBlockHeight, newBlockHeight, initialStateRoot = initialBlockHeight, newStateRoot = newBlockHeight) {
if (newBlockHeight - initialBlockHeight <= 0 ||
(newBlockHeight - initialBlockHeight) % SETTLEMENT_MATRIX_SIZE !== 0) {
throw new Error(`newBlockHeight must be greater than initialBlockHeight and difference must be a multiple of ${SETTLEMENT_MATRIX_SIZE}`);
}
const settlementProofs = [];
const merkleList = CreateValidatorMerkleList(validatorSet);
let blocks = [];
let index = 1;
for (let i = initialBlockHeight; i < newBlockHeight; i++, index++) {
const block = GeneratePulsarBlock(merkleList.hash, Field.from(i == initialBlockHeight
? initialStateRoot
: blocks[i - initialBlockHeight - 1].NewStateRoot), Field.from(i), merkleList.hash, Field.from(i == newBlockHeight - 1 ? newStateRoot : Field.random()), Field.from(i + 1));
blocks.push(block);
if (index % SETTLEMENT_MATRIX_SIZE === 0) {
const publicInput = GenerateSettlementPublicInput(merkleList.hash, blocks[blocks.length - SETTLEMENT_MATRIX_SIZE].InitialStateRoot, blocks[blocks.length - SETTLEMENT_MATRIX_SIZE].InitialBlockHeight, blocks[blocks.length - 1].NewMerkleListRoot, blocks[blocks.length - 1].NewStateRoot, blocks[blocks.length - 1].NewBlockHeight, [validatorSet[0][1]]);
const signatureMatrix = GenerateSignaturePubKeyMatrix(blocks.slice(-SETTLEMENT_MATRIX_SIZE), Array.from({ length: SETTLEMENT_MATRIX_SIZE }, () => validatorSet));
const proof = (await MultisigVerifierProgram.verifySignatures(publicInput, signatureMatrix, validatorSet[0][1], BlockList.fromArray(blocks.slice(-SETTLEMENT_MATRIX_SIZE)))).proof;
settlementProofs.push(proof);
}
}
let mergedProof = await MergeSettlementProofs(settlementProofs);
return mergedProof;
}
async function MockReducerVerifierProof(publicInput, validatorSet) {
const signatureList = GenerateReducerSignatureList(publicInput, validatorSet);
return {
validateReduceProof: await GenerateValidateReduceProof(publicInput, signatureList),
};
}
function GenerateTestActions(numActions, merkleListRoot, initialStateRoot = Field(0)) {
const actions = [];
let blockHeight = 1;
for (let i = 0; i < numActions; i++) {
const randomType = Math.ceil(Math.random() * 3);
if (randomType === 1) {
actions.push(PulsarAction.settlement(i == 0 ? initialStateRoot : Field.random(), Field.random(), merkleListRoot, merkleListRoot, Field.from(blockHeight++), Field.from(blockHeight++), ProofGenerators.empty().insertAt(Field.from(0), PrivateKey.random().toPublicKey())));
}
else if (randomType === 2) {
actions.push(PulsarAction.deposit(PrivateKey.random().toPublicKey(), UInt64.from(Math.floor(Math.random() * 2 ** 32)).value));
}
else if (randomType === 3) {
actions.push(PulsarAction.withdrawal(PrivateKey.random().toPublicKey(), UInt64.from(Math.floor(Math.random() * 2 ** 32)).value));
}
}
return actions;
}
function CalculateActionRoot(initialRoot, actions) {
let actionRoot = initialRoot;
for (const action of actions) {
actionRoot = merkleActionsAdd(actionRoot, actionListAdd(emptyActionListHash, action));
}
return actionRoot;
}
function GenerateTestBlocks(initialBlockHeight, initialMerkleListRoot, initialStateRoot = Field(0)) {
const blocks = [];
for (let i = 0; i < SETTLEMENT_MATRIX_SIZE; i++) {
blocks.push(GeneratePulsarBlock(initialMerkleListRoot, initialStateRoot, initialBlockHeight, initialMerkleListRoot, initialStateRoot.add(Field(1)), initialBlockHeight.add(Field(1))));
initialBlockHeight = initialBlockHeight.add(Field(1));
initialStateRoot = initialStateRoot.add(Field(1));
}
return blocks;
}
function CalculateFromMockActions(initialState, packedActions) {
let withdrawals = 0;
let deposits = 0;
let settlements = 0;
const batchActions = [];
let endActionState = 0n;
let publicInput = initialState;
for (const [, pack] of packedActions.entries()) {
if (batchActions.length === BATCH_SIZE) {
break;
}
if (PulsarAction.isSettlement(pack.action).toBoolean()) {
if (settlements === MAX_SETTLEMENT_PER_BATCH) {
break;
}
settlements++;
publicInput = new ValidateReducePublicInput({
...publicInput,
stateRoot: pack.action.newState,
merkleListRoot: pack.action.newMerkleListRoot,
blockHeight: pack.action.newBlockHeight,
rewardListHash: Poseidon.hash([
publicInput.rewardListHash,
pack.action.rewardListUpdateHash,
]),
});
}
else if (PulsarAction.isDeposit(pack.action).toBoolean()) {
if (deposits === MAX_DEPOSIT_PER_BATCH) {
break;
}
deposits++;
publicInput = new ValidateReducePublicInput({
...publicInput,
depositListHash: Poseidon.hash([
publicInput.depositListHash,
...pack.action.account.toFields(),
pack.action.amount,
]),
});
}
else if (PulsarAction.isWithdrawal(pack.action).toBoolean()) {
if (withdrawals === MAX_WITHDRAWAL_PER_BATCH) {
break;
}
withdrawals++;
publicInput = new ValidateReducePublicInput({
...publicInput,
withdrawalListHash: Poseidon.hash([
publicInput.withdrawalListHash,
...pack.action.account.toFields(),
pack.action.amount,
]),
});
}
batchActions.push(pack.action);
endActionState = BigInt(pack.hash);
}
return {
endActionState,
batchActions,
publicInput,
};
}
//# sourceMappingURL=testUtils.js.map