Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Non repudiation #37

Merged
merged 9 commits into from
Aug 15, 2024
58 changes: 58 additions & 0 deletions go-sdk/internal/sparse-merkle-tree/smt/smt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,18 @@ import (
"fmt"
"math/big"
"math/rand"
"os"
"testing"

"github.com/hyperledger-labs/zeto/go-sdk/internal/sparse-merkle-tree/node"
"github.com/hyperledger-labs/zeto/go-sdk/internal/sparse-merkle-tree/storage"
"github.com/hyperledger-labs/zeto/go-sdk/internal/testutils"
"github.com/hyperledger-labs/zeto/go-sdk/pkg/sparse-merkle-tree/core"
"github.com/hyperledger-labs/zeto/go-sdk/pkg/utxo"
"github.com/iden3/go-iden3-crypto/babyjub"
"github.com/stretchr/testify/assert"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)

func TestNewMerkleTree(t *testing.T) {
Expand Down Expand Up @@ -191,3 +195,57 @@ func TestVerifyProof(t *testing.T) {

fmt.Println("All done")
}

type testSqlProvider struct {
db *gorm.DB
}

func (s *testSqlProvider) DB() *gorm.DB {
return s.db
}

func (s *testSqlProvider) Close() {}

func TestSqliteStorage(t *testing.T) {
dbfile, err := os.CreateTemp("", "gorm.db")
assert.NoError(t, err)
defer func() {
os.Remove(dbfile.Name())
}()
db, err := gorm.Open(sqlite.Open(dbfile.Name()), &gorm.Config{})
assert.NoError(t, err)
err = db.Table(core.TreeRootsTable).AutoMigrate(&core.SMTRoot{})
assert.NoError(t, err)
err = db.Table(core.NodesTablePrefix + "test_1").AutoMigrate(&core.SMTNode{})
assert.NoError(t, err)

provider := &testSqlProvider{db: db}
s := storage.NewSqlStorage(provider, "test_1")
assert.NoError(t, err)

mt, err := NewMerkleTree(s, 10)
assert.NoError(t, err)

tokenId := big.NewInt(1001)
uriString := "https://example.com/token/1001"
assert.NoError(t, err)
sender := testutils.NewKeypair()
salt1 := utxo.NewSalt()

utxo1 := node.NewNonFungible(tokenId, uriString, sender.PublicKey, salt1)
n1, err := node.NewLeafNode(utxo1)
assert.NoError(t, err)
err = mt.AddLeaf(n1)
assert.NoError(t, err)

root := mt.Root()
dbRoot := core.SMTRoot{Name: "test_1"}
err = db.Table(core.TreeRootsTable).First(&dbRoot).Error
assert.NoError(t, err)
assert.Equal(t, root.Hex(), dbRoot.RootIndex)

dbNode := core.SMTNode{RefKey: n1.Ref().Hex()}
err = db.Table(core.NodesTablePrefix + "test_1").First(&dbNode).Error
assert.NoError(t, err)
assert.Equal(t, n1.Ref().Hex(), dbNode.RefKey)
}
345 changes: 345 additions & 0 deletions solidity/contracts/lib/verifier_anon_enc_nullifier_non_repudiation.sol

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions solidity/contracts/zeto_anon_enc_nullifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ contract Zeto_AnonEncNullifier is
for (uint256 i = 0; i < nullifiers.length; ++i) {
nullifierArray[i] = nullifiers[i];
outputArray[i] = outputs[i];
}
for (uint256 i = 0; i < encryptedValues.length; ++i) {
encryptedValuesArray[i] = encryptedValues[i];
}

Expand Down
197 changes: 197 additions & 0 deletions solidity/contracts/zeto_anon_enc_nullifier_non_repudiation.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
// Copyright © 2024 Kaleido, Inc.
//
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
pragma solidity ^0.8.20;

import {Groth16Verifier_CheckHashesValue} from "./lib/verifier_check_hashes_value.sol";
import {Groth16Verifier_CheckNullifierValue} from "./lib/verifier_check_nullifier_value.sol";
import {Groth16Verifier_AnonEncNullifierNonRepudiation} from "./lib/verifier_anon_enc_nullifier_non_repudiation.sol";
import {ZetoNullifier} from "./lib/zeto_nullifier.sol";
import {ZetoFungibleWithdrawWithNullifiers} from "./lib/zeto_fungible_withdraw_nullifier.sol";
import {Registry} from "./lib/registry.sol";
import {Commonlib} from "./lib/common.sol";
import "hardhat/console.sol";

/// @title A sample implementation of a Zeto based fungible token with anonymity, encryption and history masking
/// @author Kaleido, Inc.
/// @dev The proof has the following statements:
/// - each value in the output commitments must be a positive number in the range 0 ~ (2\*\*40 - 1)
/// - the sum of the nullified values match the sum of output values
/// - the hashes in the input and output match the hash(value, salt, owner public key) formula
/// - the sender possesses the private BabyJubjub key, whose public key is part of the pre-image of the input commitment hashes, which match the corresponding nullifiers
/// - the encrypted value in the input is derived from the receiver's UTXO value and encrypted with a shared secret using the ECDH protocol between the sender and receiver (this guarantees data availability for the receiver)
/// - the nullifiers represent input commitments that are included in a Sparse Merkle Tree represented by the root hash
contract Zeto_AnonEncNullifierNonRepudiation is
ZetoNullifier,
ZetoFungibleWithdrawWithNullifiers
{
event UTXOTransferNonRepudiation(
uint256[] inputs,
uint256[] outputs,
uint256 encryptionNonce,
uint256[] encryptedValuesForReceiver,
uint256[] encryptedValuesForAuthority,
address indexed submitter
);

Groth16Verifier_AnonEncNullifierNonRepudiation verifier;
// the authority public key that must be used to
// encrypt the secrets of every transaction
uint256[2] private authority;

constructor(
Groth16Verifier_CheckHashesValue _depositVerifier,
Groth16Verifier_CheckNullifierValue _withdrawVerifier,
Groth16Verifier_AnonEncNullifierNonRepudiation _verifier
)
ZetoNullifier()
ZetoFungibleWithdrawWithNullifiers(_depositVerifier, _withdrawVerifier)
{
verifier = _verifier;
}

function setAuthority(uint256[2] memory _authority) public onlyOwner {
authority = _authority;
}

function getAuthority() public view returns (uint256[2] memory) {
return authority;
}

/**
* @dev the main function of the contract, which transfers values from one account (represented by Babyjubjub public keys)
* to one or more receiver accounts (also represented by Babyjubjub public keys). One of the two nullifiers may be zero
* if the transaction only needs one UTXO to be spent. Equally one of the two outputs may be zero if the transaction
* only needs to create one new UTXO.
*
* @param nullifiers Array of nullifiers that are secretly bound to UTXOs to be spent by the transaction.
* @param outputs Array of new UTXOs to generate, for future transactions to spend.
* @param root The root hash of the Sparse Merkle Tree that contains the nullifiers.
* @param encryptionNonce The nonce used to derive the shared secret for encryption by the receiver.
* @param encryptedValuesForReceiver Array of encrypted values, salts and public keys for the receiver UTXO
* @param encryptedValuesForAuthority Array of encrypted values, salts and public keys for the input UTXOs and output UTXOs.
* @param proof A zero knowledge proof that the submitter is authorized to spend the inputs, and
* that the outputs are valid in terms of obeying mass conservation rules.
*
* Emits a {UTXOTransferNonRepudiation} event.
*/
function transfer(
uint256[2] memory nullifiers,
uint256[2] memory outputs,
uint256 root,
uint256 encryptionNonce,
uint256[2] memory encryptedValuesForReceiver,
uint256[14] memory encryptedValuesForAuthority,
Commonlib.Proof calldata proof
) public returns (bool) {
require(
validateTransactionProposal(nullifiers, outputs, root),
"Invalid transaction proposal"
);

// construct the public inputs
uint256[26] memory publicInputs;
publicInputs[0] = encryptedValuesForReceiver[0]; // encrypted value for the receiver UTXO
publicInputs[1] = encryptedValuesForReceiver[1]; // encrypted salt for the receiver UTXO
publicInputs[2] = encryptedValuesForAuthority[0]; // encrypted input owner public key[0]
publicInputs[3] = encryptedValuesForAuthority[1]; // encrypted input owner public key[1]
publicInputs[4] = encryptedValuesForAuthority[2]; // encrypted input value[0]
publicInputs[5] = encryptedValuesForAuthority[3]; // encrypted input salt[0]
publicInputs[6] = encryptedValuesForAuthority[4]; // encrypted input value[1]
publicInputs[7] = encryptedValuesForAuthority[5]; // encrypted input salt[1]
publicInputs[8] = encryptedValuesForAuthority[6]; // encrypted first output owner public key[0]
publicInputs[9] = encryptedValuesForAuthority[7]; // encrypted first output owner public key[1]
publicInputs[10] = encryptedValuesForAuthority[8]; // encrypted second output owner public key[0]
publicInputs[11] = encryptedValuesForAuthority[9]; // encrypted second output owner public key[1]
publicInputs[12] = encryptedValuesForAuthority[10]; // encrypted output value[0]
publicInputs[13] = encryptedValuesForAuthority[11]; // encrypted output salt[0]
publicInputs[14] = encryptedValuesForAuthority[12]; // encrypted output value[1]
publicInputs[15] = encryptedValuesForAuthority[13]; // encrypted output salt[1]
publicInputs[16] = nullifiers[0];
publicInputs[17] = nullifiers[1];
publicInputs[18] = root;
publicInputs[19] = (nullifiers[0] == 0) ? 0 : 1; // if the first nullifier is empty, disable its MT proof verification
publicInputs[20] = (nullifiers[1] == 0) ? 0 : 1; // if the second nullifier is empty, disable its MT proof verification
publicInputs[21] = outputs[0];
publicInputs[22] = outputs[1];
publicInputs[23] = encryptionNonce;
publicInputs[24] = authority[0];
publicInputs[25] = authority[1];

// // Check the proof
require(
verifier.verifyProof(proof.pA, proof.pB, proof.pC, publicInputs),
"Invalid proof"
);

// accept the transaction to consume the input UTXOs and produce new UTXOs
processInputsAndOutputs(nullifiers, outputs);

uint256[] memory nullifierArray = new uint256[](nullifiers.length);
uint256[] memory outputArray = new uint256[](outputs.length);
uint256[] memory encryptedValuesReceiverArray = new uint256[](
encryptedValuesForReceiver.length
);
uint256[] memory encryptedValuesAuthorityArray = new uint256[](
encryptedValuesForAuthority.length
);
for (uint256 i = 0; i < nullifiers.length; ++i) {
nullifierArray[i] = nullifiers[i];
outputArray[i] = outputs[i];
}
for (uint256 i = 0; i < encryptedValuesForReceiver.length; ++i) {
encryptedValuesReceiverArray[i] = encryptedValuesForReceiver[i];
}
for (uint256 i = 0; i < encryptedValuesForAuthority.length; ++i) {
encryptedValuesAuthorityArray[i] = encryptedValuesForAuthority[i];
}

emit UTXOTransferNonRepudiation(
nullifierArray,
outputArray,
encryptionNonce,
encryptedValuesReceiverArray,
encryptedValuesAuthorityArray,
msg.sender
);
return true;
}

function deposit(
uint256 amount,
uint256 utxo,
Commonlib.Proof calldata proof
) public {
_deposit(amount, utxo, proof);
uint256[] memory utxos = new uint256[](1);
utxos[0] = utxo;
_mint(utxos);
}

function withdraw(
uint256 amount,
uint256[2] memory nullifiers,
uint256 output,
uint256 root,
Commonlib.Proof calldata proof
) public {
_withdrawWithNullifiers(amount, nullifiers, output, root, proof);
processInputsAndOutputs(nullifiers, [output, 0]);
}

function mint(uint256[] memory utxos) public onlyOwner {
_mint(utxos);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright © 2024 Kaleido, Inc.
//
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";
import { SmtLibModule, DepositVerifierModule, WithdrawNullifierVerifierModule } from "./lib/deps";

const VerifierModule = buildModule("Groth16Verifier_AnonEncNullifierNonRepudiation", (m) => {
const verifier = m.contract('Groth16Verifier_AnonEncNullifierNonRepudiation', []);
return { verifier };
});

export default buildModule("Zeto_AnonEncNullifierNonRepudiation", (m) => {
const { smtLib, poseidon3 } = m.useModule(SmtLibModule);
const { verifier } = m.useModule(VerifierModule);
const { verifier: depositVerifier } = m.useModule(DepositVerifierModule);
const { verifier: withdrawVerifier } = m.useModule(WithdrawNullifierVerifierModule);
const commonlib = m.library('Commonlib');

const zeto = m.contract('Zeto_AnonEncNullifierNonRepudiation', [depositVerifier, withdrawVerifier, verifier], {
libraries: {
SmtLib: smtLib,
PoseidonUnit3L: poseidon3,
Commonlib: commonlib,
},
});

return { zeto };
});
10 changes: 10 additions & 0 deletions solidity/test/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,16 @@ export function parseUTXOEvents(zetoTokenContract: any, result: ContractTransact
submitter: event?.args.submitter
};
returnValues.push(transfer);
} else if (event?.name === 'UTXOTransferNonRepudiation') {
const transfer = {
inputs: event?.args.inputs,
outputs: event?.args.outputs,
encryptedValuesForReceiver: event?.args.encryptedValuesForReceiver,
encryptedValuesForAuthority: event?.args.encryptedValuesForAuthority,
encryptionNonce: event?.args.encryptionNonce,
submitter: event?.args.submitter
};
returnValues.push(transfer);
} else if (event?.name === 'UTXOMint') {
const mint = {
outputs: event?.args.outputs,
Expand Down
Loading
Loading