Skip to content

Commit

Permalink
Merge pull request #83 from hyperledger-labs/more-inputs
Browse files Browse the repository at this point in the history
supports larger sized input & output utxos
  • Loading branch information
jimthematrix authored Oct 2, 2024
2 parents 82f611a + a79aa0f commit b5fa621
Show file tree
Hide file tree
Showing 138 changed files with 13,004 additions and 3,244 deletions.
3 changes: 1 addition & 2 deletions go-sdk/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,10 @@ lint: ${LINT}
GOGC=20 $(LINT) run -v --timeout 5m
${LINT}:
$(VGO) install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.54.0

go-mod-tidy: .ALWAYS
$(VGO) mod tidy
e2e: test
$(VGO) test -v ./integration-test
$(VGO) test -count=1 -v ./integration-test
.ALWAYS: ;
clean:
$(VGO) clean
Expand Down
92 changes: 88 additions & 4 deletions go-sdk/integration-test/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ func (s *E2ETestSuite) TestZeto_2_SuccessfulProving() {
outputCommitments := []*big.Int{output1, output2}

encryptionNonce := crypto.NewEncryptionNonce()
ephemeralKeypair := testutils.NewKeypair()

witnessInputs := map[string]interface{}{
"inputCommitments": inputCommitments,
Expand All @@ -228,6 +229,7 @@ func (s *E2ETestSuite) TestZeto_2_SuccessfulProving() {
"outputSalts": []*big.Int{salt3, salt4},
"outputOwnerPublicKeys": [][]*big.Int{{receiver.PublicKey.X, receiver.PublicKey.Y}, {sender.PublicKey.X, sender.PublicKey.Y}},
"encryptionNonce": encryptionNonce,
"ecdhPrivateKey": ephemeralKeypair.PrivateKey.Scalar().BigInt(),
}

startTime := time.Now()
Expand All @@ -242,21 +244,21 @@ func (s *E2ETestSuite) TestZeto_2_SuccessfulProving() {
assert.Equal(s.T(), 3, len(proof.Proof.A))
assert.Equal(s.T(), 3, len(proof.Proof.B))
assert.Equal(s.T(), 3, len(proof.Proof.C))
assert.Equal(s.T(), 9, len(proof.PubSignals))
assert.Equal(s.T(), 15, len(proof.PubSignals))

// the receiver would be able to get the encrypted values and salts
// from the transaction events
encryptedValues := make([]*big.Int, 4)
for i := 0; i < 4; i++ {
v, ok := new(big.Int).SetString(proof.PubSignals[i], 10)
v, ok := new(big.Int).SetString(proof.PubSignals[i+2], 10)
assert.True(s.T(), ok)
encryptedValues[i] = v
}

// the first two elements in the public signals are the encrypted value and salt
// for the first output. decrypt using the receiver's private key and compare with
// the UTXO hash
secret := crypto.GenerateECDHSharedSecret(receiver.PrivateKey, sender.PublicKey)
secret := crypto.GenerateECDHSharedSecret(receiver.PrivateKey, ephemeralKeypair.PublicKey)
decrypted, err := crypto.PoseidonDecrypt(encryptedValues, []*big.Int{secret.X, secret.Y}, encryptionNonce, 2)
assert.NoError(s.T(), err)
assert.Equal(s.T(), outputValues[0].String(), decrypted[0].String())
Expand Down Expand Up @@ -400,6 +402,7 @@ func (s *E2ETestSuite) TestZeto_4_SuccessfulProving() {
outputCommitments := []*big.Int{output1, output2}

encryptionNonce := crypto.NewEncryptionNonce()
ephemeralKeypair := testutils.NewKeypair()

proof1Siblings := make([]*big.Int, len(circomProof1.Siblings)-1)
for i, s := range circomProof1.Siblings[0 : len(circomProof1.Siblings)-1] {
Expand All @@ -423,6 +426,7 @@ func (s *E2ETestSuite) TestZeto_4_SuccessfulProving() {
"outputSalts": []*big.Int{salt3, salt4},
"outputOwnerPublicKeys": [][]*big.Int{{receiver.PublicKey.X, receiver.PublicKey.Y}, {sender.PublicKey.X, sender.PublicKey.Y}},
"encryptionNonce": encryptionNonce,
"ecdhPrivateKey": ephemeralKeypair.PrivateKey.Scalar().BigInt(),
}

startTime := time.Now()
Expand All @@ -437,7 +441,7 @@ func (s *E2ETestSuite) TestZeto_4_SuccessfulProving() {
assert.Equal(s.T(), 3, len(proof.Proof.A))
assert.Equal(s.T(), 3, len(proof.Proof.B))
assert.Equal(s.T(), 3, len(proof.Proof.C))
assert.Equal(s.T(), 12, len(proof.PubSignals))
assert.Equal(s.T(), 18, len(proof.PubSignals))
}

func (s *E2ETestSuite) TestZeto_5_SuccessfulProving() {
Expand Down Expand Up @@ -496,6 +500,86 @@ func (s *E2ETestSuite) TestZeto_5_SuccessfulProving() {
assert.Equal(s.T(), 3, len(proof.Proof.B))
assert.Equal(s.T(), 3, len(proof.Proof.C))
assert.Equal(s.T(), 2, len(proof.PubSignals))

}

func (s *E2ETestSuite) TestZeto_5_SuccessfulProvingWithConcurrency() {
concurrency := 10
resultChan := make(chan struct{}, concurrency)

for i := 0; i < concurrency; i++ {
index := i
go func() {
defer func() {
resultChan <- struct{}{}
}()
calc, provingKey, err := loadCircuit("nf_anon")
assert.NoError(s.T(), err)
assert.NotNil(s.T(), calc)

sender := testutils.NewKeypair()
receiver := testutils.NewKeypair()

tokenId := big.NewInt(int64(index + 1)) // ensure different token uris for each run
tokenUri, err := utxo.HashTokenUri("https://example.com/token/" + tokenId.String())
assert.NoError(s.T(), err)

salt1 := crypto.NewSalt()
input1, err := poseidon.Hash([]*big.Int{tokenId, tokenUri, salt1, sender.PublicKey.X, sender.PublicKey.Y})
assert.NoError(s.T(), err)

salt3 := crypto.NewSalt()
output1, err := poseidon.Hash([]*big.Int{tokenId, tokenUri, salt3, receiver.PublicKey.X, receiver.PublicKey.Y})
assert.NoError(s.T(), err)

witnessInputs := map[string]interface{}{
"tokenIds": []*big.Int{tokenId},
"tokenUris": []*big.Int{tokenUri},
"inputCommitments": []*big.Int{input1},
"inputSalts": []*big.Int{salt1},
"inputOwnerPrivateKey": sender.PrivateKeyBigInt,
"outputCommitments": []*big.Int{output1},
"outputSalts": []*big.Int{salt3},
"outputOwnerPublicKeys": [][]*big.Int{{receiver.PublicKey.X, receiver.PublicKey.Y}},
}
// calculate the witness object for checking correctness
witness, err := calc.CalculateWitness(witnessInputs, true)
assert.NoError(s.T(), err)
assert.NotNil(s.T(), witness)

assert.Equal(s.T(), 0, witness[0].Cmp(big.NewInt(1)))
assert.Equal(s.T(), 0, witness[1].Cmp(input1))
assert.Equal(s.T(), 0, witness[2].Cmp(output1))
assert.Equal(s.T(), 0, witness[3].Cmp(tokenId))
assert.Equal(s.T(), 0, witness[4].Cmp(tokenUri))

// generate the witness binary to feed into the prover
startTime := time.Now()
witnessBin, err := calc.CalculateWTNSBin(witnessInputs, true)
assert.NoError(s.T(), err)
assert.NotNil(s.T(), witnessBin)

proof, err := prover.Groth16Prover(provingKey, witnessBin)
elapsedTime := time.Since(startTime)
fmt.Printf("Proving time: %s\n", elapsedTime)
fmt.Printf("token uri from witness: %s\n", witness[3])
assert.NoError(s.T(), err)
assert.Equal(s.T(), 3, len(proof.Proof.A))
assert.Equal(s.T(), 3, len(proof.Proof.B))
assert.Equal(s.T(), 3, len(proof.Proof.C))
assert.Equal(s.T(), 2, len(proof.PubSignals))

}()
}
count := 0
for {
<-resultChan
count++
if count == concurrency {
break
}
}

}

func (s *E2ETestSuite) TestZeto_6_SuccessfulProving() {
Expand Down
8 changes: 7 additions & 1 deletion solidity/contracts/factory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ contract ZetoTokenFactory is Ownable {
address depositVerifier;
address withdrawVerifier;
address verifier;
address batchVerifier;
}

event ZetoTokenDeployed(address indexed zetoToken);
Expand Down Expand Up @@ -74,6 +75,10 @@ contract ZetoTokenFactory is Ownable {
args.withdrawVerifier != address(0),
"Factory: withdrawVerifier address is required"
);
require(
args.batchVerifier != address(0),
"Factory: batchVerifier address is required"
);
address instance = Clones.clone(args.implementation);
require(
instance != address(0),
Expand All @@ -83,7 +88,8 @@ contract ZetoTokenFactory is Ownable {
initialOwner,
args.verifier,
args.depositVerifier,
args.withdrawVerifier
args.withdrawVerifier,
args.batchVerifier
);
emit ZetoTokenDeployed(instance);
return instance;
Expand Down
18 changes: 18 additions & 0 deletions solidity/contracts/lib/common.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,24 @@ library Commonlib {
uint[2] pC;
}

function padUintArray(
uint256[] memory arr,
uint256 targetLength,
uint256 padValue
) internal pure returns (uint256[] memory) {
if (arr.length == targetLength) {
return arr;
}
uint256[] memory paddedArray = new uint256[](targetLength);
for (uint256 i = 0; i < arr.length; i++) {
paddedArray[i] = arr[i];
}
for (uint256 i = arr.length; i < targetLength; i++) {
paddedArray[i] = padValue;
}
return paddedArray;
}

function getProofHash(
Proof calldata proof
) internal pure returns (bytes32) {
Expand Down
1 change: 1 addition & 0 deletions solidity/contracts/lib/interfaces/izeto_encrypted.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ interface IZetoEncrypted is IZetoBase {
uint256[] inputs,
uint256[] outputs,
uint256 encryptionNonce,
uint256[2] ecdhPublicKey,
uint256[] encryptedValues,
address indexed submitter,
bytes data
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ interface IZetoFungibleInitializable {
address initialOwner,
address _depositVerifier,
address _withdrawVerifier,
address _verifier
address _verifier,
address _batchVerifier
) external;
}
Loading

0 comments on commit b5fa621

Please sign in to comment.