Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions contracts/test/ProtocolAdapterMock.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,10 @@ contract ProtocolAdapterMockVerifierTest is Test {
_mockPa.execute(txn);
}

function test_random_transactions_execute(TxGen.TransactionParams memory params) public {
_mockPa.execute(vm.transaction(_mockVerifier, params));
}

function testFuzz_execute_reverts_on_pre_existing_nullifier(bool aggregated) public {
TxGen.ActionConfig[] memory configs = TxGen.generateActionConfigs({actionCount: 1, complianceUnitCount: 1});

Expand Down
175 changes: 171 additions & 4 deletions contracts/test/libs/TxGen.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ library TxGen {
using Logic for Logic.VerifierInput[];
using Logic for Logic.VerifierInput;

uint256 public constant MAX_ACTIONS = 4;
uint256 public constant MAX_RESOURCES = 5;
Comment on lines +24 to +25
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really want to hardcode these parameters?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but only in the context of the approach taken by this PR. If there are no limits on action and resource counts, the generator will start sometimes generating transactions with 256 actions, each containing 256 resources (so 65536 resources total), and the fuzz tests, each of which has a 1000 iterations, take impractical amounts of time to complete. But maybe another approach could be taken like removing the limits only for (non-fuzzing) unit tests or something...


struct ActionConfig {
uint256 complianceUnitCount;
}
Expand All @@ -35,6 +38,19 @@ library TxGen {
ResourceAndAppData[] created;
}

struct ActionParams {
Resource[2][MAX_RESOURCES] resources;
uint256[MAX_RESOURCES] bijection;
uint256 targetResourcesLen;
uint256[2][MAX_RESOURCES] valueCommitmentRandomness;
}

struct TransactionParams {
ActionParams[MAX_ACTIONS] actionParams;
uint256 targetActionsLen;
bool isProofAggregated;
}

error ConsumedCreatedCountMismatch(uint256 nConsumed, uint256 nCreated);
error NonExistingTag(bytes32 tag);
error TransactionTagCountMismatch();
Expand All @@ -44,7 +60,9 @@ library TxGen {
RiscZeroMockVerifier mockVerifier,
bytes32 commitmentTreeRoot, // historical root
Resource memory consumed,
Resource memory created
uint256 consumedValueCommitmentRandomness,
Resource memory created,
uint256 createdValueCommitmentRandomness
) internal returns (Compliance.VerifierInput memory unit) {
bytes32 nf = nullifier(consumed, 0);
bytes32 cm = commitment(created);
Expand All @@ -56,7 +74,7 @@ library TxGen {
kind: kind(consumed),
quantity: consumed.quantity,
consumed: true,
valueCommitmentRandomness: 1
valueCommitmentRandomness: consumedValueCommitmentRandomness
})
);
// Construct the delta for creation based on kind and quantity
Expand All @@ -68,7 +86,7 @@ library TxGen {
kind: kind(created),
quantity: created.quantity,
consumed: false,
valueCommitmentRandomness: 1
valueCommitmentRandomness: createdValueCommitmentRandomness
})
)
);
Expand Down Expand Up @@ -139,7 +157,9 @@ library TxGen {
mockVerifier: mockVerifier,
commitmentTreeRoot: initialRoot(),
consumed: consumed[i].resource,
created: created[i].resource
consumedValueCommitmentRandomness: 1,
created: created[i].resource,
createdValueCommitmentRandomness: 1
});
}
action = Action({logicVerifierInputs: logicVerifierInputs, complianceVerifierInputs: complianceVerifierInputs});
Expand Down Expand Up @@ -287,6 +307,114 @@ library TxGen {
}).seal;
}

function generateAction(VmSafe vm, RiscZeroMockVerifier mockVerifier, ActionParams memory params)
internal
returns (Action memory action, uint256 totalValueCommitmentRandomness)
{
Resource[2][] memory truncatedResources =
truncateResources(params.resources, params.targetResourcesLen % MAX_RESOURCES);
action.logicVerifierInputs = new Logic.VerifierInput[](truncatedResources.length * 2);
action.complianceVerifierInputs = new Compliance.VerifierInput[](truncatedResources.length);
// Created empty app data for all the resources
Logic.AppData memory appData = Logic.AppData({
resourcePayload: new Logic.ExpirableBlob[](0),
discoveryPayload: new Logic.ExpirableBlob[](0),
externalPayload: new Logic.ExpirableBlob[](0),
applicationPayload: new Logic.ExpirableBlob[](0)
});
// Match the created and consumed resources
uint256[] memory bijection = generateBijection(params.bijection, truncatedResources.length);
for (uint256 i = 0; i < truncatedResources.length; ++i) {
truncatedResources[bijection[i]][1].quantity = truncatedResources[i][0].quantity;
truncatedResources[bijection[i]][1].logicRef = truncatedResources[i][0].logicRef;
truncatedResources[bijection[i]][1].labelRef = truncatedResources[i][0].labelRef;
}
// Compute action tree tags and action tree root
bytes32[] memory actionTreeTags = new bytes32[](2 * truncatedResources.length);
totalValueCommitmentRandomness = 0;
for (uint256 i = 0; i < truncatedResources.length; ++i) {
uint256 index = (i * 2);

actionTreeTags[index] = nullifier(truncatedResources[i][0], 0);
actionTreeTags[index + 1] = commitment(truncatedResources[i][1]);
// Adjust and accumulate the value randomness commitments
params.valueCommitmentRandomness[i][0] =
1 + (params.valueCommitmentRandomness[i][0] % (DeltaGen.SECP256K1_ORDER - 1));
params.valueCommitmentRandomness[i][1] =
1 + (params.valueCommitmentRandomness[i][1] % (DeltaGen.SECP256K1_ORDER - 1));
totalValueCommitmentRandomness =
addmod(totalValueCommitmentRandomness, params.valueCommitmentRandomness[i][0], DeltaGen.SECP256K1_ORDER);
totalValueCommitmentRandomness =
addmod(totalValueCommitmentRandomness, params.valueCommitmentRandomness[i][1], DeltaGen.SECP256K1_ORDER);
}
bytes32 actionTreeRoot = actionTreeTags.computeRoot();
// Create logic and compliance verifier inputs
for (uint256 i = 0; i < truncatedResources.length; i++) {
uint256 index = (i * 2);

Resource memory consumedResource = truncatedResources[i][0];
Resource memory createdResource = truncatedResources[i][1];
// Created logic verifier input for a consumed resource
action.logicVerifierInputs[index] = logicVerifierInput({
mockVerifier: mockVerifier,
actionTreeRoot: actionTreeRoot,
resource: consumedResource,
isConsumed: true,
appData: appData
});
// Create logic verifier input for a created resource
action.logicVerifierInputs[index + 1] = logicVerifierInput({
mockVerifier: mockVerifier,
actionTreeRoot: actionTreeRoot,
resource: createdResource,
isConsumed: false,
appData: appData
});
// Create compliance verifier input for the resource pairs
action.complianceVerifierInputs[i] = complianceVerifierInput({
vm: vm,
mockVerifier: mockVerifier,
commitmentTreeRoot: initialRoot(),
consumed: consumedResource,
consumedValueCommitmentRandomness: params.valueCommitmentRandomness[i][0],
created: createdResource,
createdValueCommitmentRandomness: params.valueCommitmentRandomness[i][1]
});
}
}

function transaction(VmSafe vm, RiscZeroMockVerifier mockVerifier, TransactionParams memory params)
internal
returns (Transaction memory txn)
{
// Generate actions
Action[] memory actions = new Action[](params.targetActionsLen % MAX_ACTIONS);
uint256 totalValueCommitmentRandomness = 0;
for (uint256 i = 0; i < actions.length; i++) {
uint256 valueCommitmentRandomness;
(actions[i], valueCommitmentRandomness) = generateAction(vm, mockVerifier, params.actionParams[i]);
totalValueCommitmentRandomness =
addmod(totalValueCommitmentRandomness, valueCommitmentRandomness, DeltaGen.SECP256K1_ORDER);
}
// Generate delta proof
bytes memory proof = "";
bytes32[] memory tags = TxGen.collectTags(actions);
if (tags.length != 0) {
proof = DeltaGen.generateProof(
vm,
DeltaGen.ProofInputs({
valueCommitmentRandomness: totalValueCommitmentRandomness,
verifyingKey: Delta.computeVerifyingKey(tags)
})
);
}
// Generate transaction
txn = Transaction({actions: actions, deltaProof: proof, aggregationProof: ""});
if (params.isProofAggregated) {
txn = transactionAggregation(mockVerifier, txn);
}
}

function logicVerifierInput(
RiscZeroMockVerifier mockVerifier,
bytes32 actionTreeRoot,
Expand Down Expand Up @@ -479,4 +607,43 @@ library TxGen {

root = actionTreeTags.computeRoot();
}

function truncateResources(Resource[2][MAX_RESOURCES] memory resources, uint256 len)
internal
pure
returns (Resource[2][] memory truncatedResources)
{
truncatedResources = new Resource[2][](len);
for (uint256 i = 0; i < len; i++) {
truncatedResources[i][0] = resources[i][0];
truncatedResources[i][1] = resources[i][1];
}
}

function generateBijection(uint256[MAX_RESOURCES] memory input, uint256 len)
internal
pure
returns (uint256[] memory output)
{
output = new uint256[](len);
uint256[] memory duplicates = new uint256[](len);
uint256 duplicateCount = 0;
for (uint256 i = 0; i < len; i++) {
output[i] = duplicates[i] = len;
input[i] %= len;
}
for (uint256 i = 0; i < len; i++) {
if (output[input[i]] == len) {
output[input[i]] = i;
} else {
duplicates[duplicateCount++] = i;
}
}
duplicateCount = 0;
for (uint256 i = 0; i < len; i++) {
if (output[i] == len) {
output[i] = duplicates[duplicateCount++];
}
}
}
}
Loading