Skip to content

Commit

Permalink
fix: transform and withdraw spearate the fee and the non-fee amounts
Browse files Browse the repository at this point in the history
  • Loading branch information
Westlad committed Sep 7, 2023
1 parent 92a60f5 commit f2f85f7
Show file tree
Hide file tree
Showing 9 changed files with 117 additions and 28 deletions.
38 changes: 29 additions & 9 deletions nightfall-deployer/circuits/burn.circom
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pragma circom 2.1.2;
include "./common/utils/calculate_keys.circom";
include "./common/utils/array_uint32_to_bits.circom";
include "./common/verifiers/verify_duplicates.circom";
include "./common/verifiers/commitments/verify_commitments.circom";
include "./common/verifiers/commitments/verify_commitments_optional.circom";
include "./common/verifiers/nullifiers/verify_nullifiers.circom";
include "./common/verifiers/nullifiers/verify_nullifiers_optional.circom";
Expand Down Expand Up @@ -94,18 +95,18 @@ template Burn(N,C) {
n1 === 0;

// Convert the nullifiers values to numbers and calculate its sum
var nullifiersSum = 0;
// var nullifiersSum = 0;
for(var i = 0; i < N; i++) {
nullifiersSum += nullifiersValues[i];
// nullifiersSum += nullifiersValues[i];
var nullifierValueBits[254] = Num2Bits(254)(nullifiersValues[i]);
nullifierValueBits[253] === 0;
nullifierValueBits[252] === 0;
}

// Convert the commitment values to numbers and calculate its sum
var commitmentsSum = 0;
// var commitmentsSum = 0;
for(var i = 0; i < C; i++) {
commitmentsSum += commitmentsValues[i];
// commitmentsSum += commitmentsValues[i];
var commitmentValueBits[254] = Num2Bits(254)(commitmentsValues[i]);
commitmentValueBits[253] === 0;
commitmentValueBits[252] === 0;
Expand All @@ -117,7 +118,7 @@ template Burn(N,C) {
valuePrivateBits[252] === 0;

// Check that the value holds
nullifiersSum === commitmentsSum + fee + valuePrivate;
//nullifiersSum === commitmentsSum + fee + valuePrivate;


// Calculate the nullifierKeys and the zkpPublicKeys from the root key
Expand Down Expand Up @@ -150,13 +151,32 @@ template Burn(N,C) {
}
checkFeeNullifiers.valid === 1;

// Check that the first commitment is valid either with packedErcAddress or if value is zero
var checkCommitment = VerifyCommitmentsOptional(1)(packedErcAddressPrivate, idRemainderPrivate, [commitments[0]],[commitmentsValues[0]], [commitmentsSalts[0]], [recipientPublicKey[0]]);
// Check that the first commitment is valid with packedErcAddress
var checkCommitment = VerifyCommitments(1)(packedErcAddressPrivate, idRemainderPrivate, [commitments[0]],[commitmentsValues[0]], [commitmentsSalts[0]], [recipientPublicKey[0]]);
checkCommitment === 1;

// Check that the second commitment is valid either with feeAddress or if value is zero
var checkCommitmentFee = VerifyCommitmentsOptional(1)(feeAddress, 0, [commitments[1]],[commitmentsValues[1]], [commitmentsSalts[1]], [recipientPublicKey[1]]);
checkCommitmentFee === 1;
// var checkCommitmentFee = VerifyCommitmentsOptional(1)(feeAddress, 0, [commitments[1]],[commitmentsValues[1]], [commitmentsSalts[1]], [recipientPublicKey[1]]);
// checkCommitmentFee === 1;

// Check that the other commitments are valid either using the feeAddress or if value is zero
// note that this means we only support burning a single token for simplicity all other commitments are expected to be fees
component checkOptionalCommitments = VerifyCommitmentsOptional(C -1);
checkOptionalCommitments.packedErcAddress <== feeAddress;
checkOptionalCommitments.idRemainder <== 0;
for(var i = 1; i < C; i++) {
checkOptionalCommitments.commitmentsHashes[i-1] <== commitments[i];
checkOptionalCommitments.newCommitmentsValues[i-1] <== commitmentsValues[i];
checkOptionalCommitments.newCommitmentsSalts[i-1] <== commitmentsSalts[i];
checkOptionalCommitments.recipientPublicKey[i-1][0] <== recipientPublicKey[i][0];
checkOptionalCommitments.recipientPublicKey[i-1][1] <== recipientPublicKey[i][1];
}
checkOptionalCommitments.valid === 1;

// constrain the fees
checkFeeNullifiers.total === checkOptionalCommitments.total + fee;
// constrain the L2 value
nullifiersValues[0] === commitmentsValues[0] + value;

// assert(commitmentsValues[0] == 0 || (
// zkpPublicKeys[0] == recipientPublicKey[0][0] && zkpPublicKeys[1] == recipientPublicKey[0][1]));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,15 @@ template VerifyCommitmentsGeneric(C) {
signal input feeAddress;

signal output valid;
signal output feeTotal;
signal output nonFeeTotal;

signal r[C];
signal f[C];
signal s[C];

var feeSum = 0;
var nonFeeSum = 0;
for(var i=0; i < C; i++) {
// Check if the commitment value is zero
var isCommitmentValueZero = IsZero()(newCommitmentsValues[i]);
Expand All @@ -52,7 +58,15 @@ template VerifyCommitmentsGeneric(C) {
// assert(commitment == commitmentsHashes[i] || commitmentFee == commitmentsHashes[i]);
r[i] <== OR()(IsEqual()([commitment, commitmentsHashes[i]]), IsEqual()([commitmentFee, commitmentsHashes[i]]));
r[i] === 1;
}

// add up the fees and the commitment values. These can be different coins and so can't always be validly added together
// in the case where the address is the same, the two values will end up equal
var IsEqualCommitment = IsEqual()([commitment, commitmentsHashes[i]]);
var IsEqualCommitmentFee = IsEqual()([commitmentFee, commitmentsHashes[i]]);
feeSum += IsEqualCommitmentFee * newCommitmentsValues[i];
nonFeeSum += IsEqualCommitment * newCommitmentsValues[i];
}
valid <== 1;
feeTotal <-- feeSum;
nonFeeTotal <-- nonFeeSum;
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ template VerifyCommitmentsOptional(C) {
signal input recipientPublicKey[C][2];

signal output valid;
signal output total;


var sum = 0;
for(var i=0; i < C; i++) {

// Check if the commitment value is zero
Expand All @@ -39,7 +40,9 @@ template VerifyCommitmentsOptional(C) {

// Check that the reconstructed commitment hash is equal to the public transaction commitment hash
commitment === commitmentsHashes[i];
sum += newCommitmentsValues[i];
}

valid <== 1;
total <-- sum;
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,15 @@ template VerifyNullifiersGeneric(N) {
signal input feeAddress;

signal output valid;
signal output feeTotal;
signal output nonFeeTotal;


signal r[N];

var nonFeeSum = 0;
var feeSum = 0;

for(var i=0; i < N; i++) {
// Check if the commitment value is zero
var isNullifierValueZero = IsZero()(oldCommitmentValues[i]);
Expand Down Expand Up @@ -85,7 +91,13 @@ template VerifyNullifiersGeneric(N) {
// Check that the roots are equal. If nullifierValue is zero it will directly be considered valid
var isValidRoot = Mux1()([IsEqualRoots, 1], isNullifierValueZero);
isValidRoot === 1;
}

// add up the fees and the commitment values. These can be different coins and so can't be validly added together
// In the casde where they have the same address, these values will end up equal
feeSum += IsEqualNullifierFee * oldCommitmentValues[i];
nonFeeSum += IsEqualNullifier * oldCommitmentValues[i];
}
valid <== 1;
feeTotal <-- feeSum;
nonFeeTotal <-- nonFeeSum;
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ template VerifyNullifiersOptional(N) {
signal input orders[N];

signal output valid;
signal output total;

var sum = 0;
for(var i=0; i < N; i++) {
// Check if the commitment value is zero
var isNullifierValueZero = IsZero()(oldCommitmentValues[i]);
Expand All @@ -60,7 +62,9 @@ template VerifyNullifiersOptional(N) {
// Check that the root is valid
var isValidRoot = Mux1()([IsEqualRoots, 1], isNullifierValueZero);
isValidRoot === 1;
sum += oldCommitmentValues[i];
}

valid <== 1;
total <-- sum;
}
17 changes: 13 additions & 4 deletions nightfall-deployer/circuits/depositfee.circom
Original file line number Diff line number Diff line change
Expand Up @@ -131,18 +131,27 @@ template DepositFee(N,C) {
var checkCommitments = VerifyCommitments(1)(packedErcAddress, idRemainder.out, [commitments[0]], [commitmentsValues[0]], [commitmentsSalts[0]], [recipientPublicKey[0]]);
checkCommitments === 1;

var checkCommitmentsFee = VerifyCommitmentsOptional(1)(feeAddress, 0, [commitments[1]], [commitmentsValues[1]], [commitmentsSalts[1]], [recipientPublicKey[1]]);
checkCommitmentsFee === 1;
component checkCommitmentsFee = VerifyCommitmentsOptional(C-1);
checkCommitmentsFee.packedErcAddress <== feeAddress;
checkCommitmentsFee.idRemainder <== 0;
for(var i = 1; i < C; i++) {
checkCommitmentsFee.commitmentsHashes[i-1] <== commitments[i];
checkCommitmentsFee.newCommitmentsValues[i-1] <== commitmentsValues[i];
checkCommitmentsFee.newCommitmentsSalts[i-1] <== commitmentsSalts[i];
checkCommitmentsFee.recipientPublicKey[i-1][0] <== recipientPublicKey[i][0];
checkCommitmentsFee.recipientPublicKey[i-1][1] <== recipientPublicKey[i][1];
}
checkCommitmentsFee.valid === 1;

// Calculate the nullifierKeys and the zkpPublicKeys from the root key
var nullifierKeys, zkpPublicKeys[2];
(nullifierKeys, zkpPublicKeys) = CalculateKeys()(rootKey);


// Check that the nullifiers are valid either using the feeAddress or if value is zero
var checkNullifier = VerifyNullifiersOptional(N)(feeAddress, 0, nullifierKeys, zkpPublicKeys, nullifiers, roots,
component checkNullifier = VerifyNullifiersOptional(N)(feeAddress, 0, nullifierKeys, zkpPublicKeys, nullifiers, roots,
nullifiersValues, nullifiersSalts, paths, orders);
checkNullifier === 1;
checkNullifier.valid === 1;

// Verify the fee change
// assert(commitmentsValues[1] == 0 || (
Expand Down
16 changes: 12 additions & 4 deletions nightfall-deployer/circuits/transfer.circom
Original file line number Diff line number Diff line change
Expand Up @@ -103,23 +103,23 @@ template Transfer(N,C) {
// Convert the nullifiers values to numbers and calculate its sum
var nullifiersSum = 0;
for(var i = 0; i < N; i++) {
nullifiersSum += nullifiersValues[i];
// nullifiersSum += nullifiersValues[i];
var nullifierValueBits[254] = Num2Bits(254)(nullifiersValues[i]);
nullifierValueBits[253] === 0;
nullifierValueBits[252] === 0;
}

// Convert the commitment values to numbers and calculate its sum
var commitmentsSum = 0;
// var commitmentsSum = 0;
for(var i = 0; i < C; i++) {
commitmentsSum += commitmentsValues[i];
// commitmentsSum += commitmentsValues[i];
var commitmentValueBits[254] = Num2Bits(254)(commitmentsValues[i]);
commitmentValueBits[253] === 0;
commitmentValueBits[252] === 0;
}

// Check that the value holds
nullifiersSum === commitmentsSum + fee;
// nullifiersSum === commitmentsSum + fee;

// Calculate the nullifierKeys and the zkpPublicKeys from the root key
var nullifierKeys, zkpPublicKeys[2];
Expand Down Expand Up @@ -166,6 +166,14 @@ template Transfer(N,C) {
}
checkGenericCommitments.valid === 1;

// constrain input and output values to be equal for both the fee and transaction values.
// We have to count differently if the feeAddress and the packedERCAddress are the same and the idRemainder is zero because
// then we have no way to differentiate fee and non-fee commitments - in this case we just sum all the values, noting that the fee and non-fee
// values are the same becuase they both contain a copy of everything. In this 'degenerate' case, the two constraints become identical.
var isDegenerate = AND()(IsEqual()([packedErcAddressPrivate, feeAddress]), IsZero()(idRemainderPrivate));
checkGenericNullifiers.nonFeeTotal + nullifiersValues[0] === checkGenericCommitments.nonFeeTotal + commitmentsValues[0] + fee * isDegenerate;
checkGenericNullifiers.feeTotal + (nullifiersValues[0] - commitmentsValues[0]) * isDegenerate === checkGenericCommitments.feeTotal + fee;

// Verify the withdraw change
//assert(commitmentsValues[C - 2] == 0 || (
// zkpPublicKeys[0] == recipientPublicKey[C - 2][0] && zkpPublicKeys[1] == recipientPublicKey[C - 2][1]));
Expand Down
10 changes: 6 additions & 4 deletions nightfall-deployer/circuits/transform.circom
Original file line number Diff line number Diff line change
Expand Up @@ -116,18 +116,20 @@ template Transform(N,C) {
// check the fee nullifiers and fee commitments by looking at the input addresses
var feeNullifiersSum = 0;
for (var i = 0; i < N; i++) {
var isFee = inputPackedAddressesPrivate[i] == feeAddress;
var isFee = IsEqual()([inputPackedAddressesPrivate[i],feeAddress]);
feeNullifiersSum += nullifiersValues[i] * isFee;
}

var feeCommitmentSum = 0;
for (var i = 0; i < C; i++) {
var isFee = outputPackedAddressesPrivate[i] == feeAddress;
var isFee = IsEqual()([outputPackedAddressesPrivate[i], feeAddress]);
feeCommitmentSum += commitmentsValues[i] * isFee;
}
signal in <-- feeNullifiersSum;
signal out <-- feeCommitmentSum;

// Check that the value holds
var feeIsCovered = feeNullifiersSum - feeCommitmentSum - fee;
// Constrain the fees so that new 'fees' can't be added from an L2 commitment
in === fee + out;

// Calculate the nullifierKeys and the zkpPublicKeys from the root key
var nullifierKeys, zkpPublicKeys[2];
Expand Down
25 changes: 21 additions & 4 deletions nightfall-deployer/circuits/withdraw.circom
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ template Withdraw(N,C) {


// Check that the value holds
nullifiersSum === commitmentsSum + fee + value;
// nullifiersSum === commitmentsSum + fee + value;

// Calculate the token Id remainder without the 4 top bytes
component idRemainder = Bits2Num(224);
Expand Down Expand Up @@ -160,8 +160,26 @@ template Withdraw(N,C) {
checkGenericNullifiers.valid === 1;

// Check that the commimtents are valid either using the ercAddress, the feeAddress or if value is zero
var checkCommitments = VerifyCommitmentsGeneric(C)(packedErcAddress, idRemainder.out, commitments, commitmentsValues, commitmentsSalts, recipientPublicKey, feeAddress);
checkCommitments === 1;
component checkGenericCommitments = VerifyCommitmentsGeneric(C);
checkGenericCommitments.packedErcAddress <== packedErcAddress;
checkGenericCommitments.idRemainder <== idRemainder.out;
checkGenericCommitments.feeAddress <== feeAddress;
for(var i = 0; i < C; i++) {
checkGenericCommitments.commitmentsHashes[i] <== commitments[i];
checkGenericCommitments.newCommitmentsValues[i] <== commitmentsValues[i];
checkGenericCommitments.newCommitmentsSalts[i] <== commitmentsSalts[i];
checkGenericCommitments.recipientPublicKey[i][0] <== recipientPublicKey[i][0];
checkGenericCommitments.recipientPublicKey[i][1] <== recipientPublicKey[i][1];
}
checkGenericCommitments.valid === 1;

// constrain input and output values to be equal for both the fee and transaction values.
// We have to count differently if the feeAddress and the packedERCAddress are the same and the idRemainder is zero because
// then we have no way to differentiate fee and non-fee commitments - in this case we just sum all the values, noting that the fee and non-fee
// values are the same becuase they both contain a copy of everything. In this 'degenerate' case, the two constraints become identical.
var isDegenerate = AND()(IsEqual()([packedErcAddress, feeAddress]), IsZero()(idRemainder.out));
checkGenericNullifiers.nonFeeTotal + nullifiersValues[0] === checkGenericCommitments.nonFeeTotal + fee * isDegenerate + value;
checkGenericNullifiers.feeTotal + (nullifiersValues[0] - value) * isDegenerate === checkGenericCommitments.feeTotal + fee;

// Verify the withdraw change
// assert(commitmentsValues[C - 2] == 0 ||
Expand All @@ -172,7 +190,6 @@ template Withdraw(N,C) {
signal t <== OR()(f1, AND()(f2, f3));
t === 1;


// Verify the fee change
// assert(commitmentsValues[C - 1] == 0 ||
// (zkpPublicKeys[0] == recipientPublicKey[C - 1][0] && zkpPublicKeys[1] == recipientPublicKey[C - 1][1]));
Expand Down

0 comments on commit f2f85f7

Please sign in to comment.