From 6be8893a15b5f3d1281dc61bd81fd38c3801a052 Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Tue, 26 Nov 2024 15:59:42 -0500 Subject: [PATCH 01/36] deploy ERC20 in e2e test Signed-off-by: Jim Zhang --- domains/zeto/integration-test/e2e_test.go | 104 ++++++++++++++++++---- domains/zeto/pkg/types/abi.go | 7 ++ 2 files changed, 94 insertions(+), 17 deletions(-) diff --git a/domains/zeto/integration-test/e2e_test.go b/domains/zeto/integration-test/e2e_test.go index 5fe41b667..8761b6ea8 100644 --- a/domains/zeto/integration-test/e2e_test.go +++ b/domains/zeto/integration-test/e2e_test.go @@ -19,6 +19,8 @@ import ( "context" _ "embed" "encoding/json" + "fmt" + "os" "testing" "time" @@ -30,11 +32,14 @@ import ( "github.com/kaleido-io/paladin/domains/zeto/pkg/types" "github.com/kaleido-io/paladin/domains/zeto/pkg/zeto" "github.com/kaleido-io/paladin/domains/zeto/pkg/zetosigner/zetosignerapi" + "github.com/kaleido-io/paladin/toolkit/pkg/algorithms" "github.com/kaleido-io/paladin/toolkit/pkg/log" "github.com/kaleido-io/paladin/toolkit/pkg/pldapi" "github.com/kaleido-io/paladin/toolkit/pkg/plugintk" "github.com/kaleido-io/paladin/toolkit/pkg/query" + "github.com/kaleido-io/paladin/toolkit/pkg/solutils" "github.com/kaleido-io/paladin/toolkit/pkg/tktypes" + "github.com/kaleido-io/paladin/toolkit/pkg/verifiers" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" @@ -87,26 +92,26 @@ func (s *zetoDomainTestSuite) TestZeto_Anon() { s.testZetoFungible(s.T(), constants.TOKEN_ANON, false, false) } -func (s *zetoDomainTestSuite) TestZeto_AnonBatch() { - s.T().Skip() - s.testZetoFungible(s.T(), constants.TOKEN_ANON, true, false) -} +// func (s *zetoDomainTestSuite) TestZeto_AnonBatch() { +// s.T().Skip() +// s.testZetoFungible(s.T(), constants.TOKEN_ANON, true, false) +// } -func (s *zetoDomainTestSuite) TestZeto_AnonEnc() { - s.testZetoFungible(s.T(), constants.TOKEN_ANON_ENC, false, false) -} +// func (s *zetoDomainTestSuite) TestZeto_AnonEnc() { +// s.testZetoFungible(s.T(), constants.TOKEN_ANON_ENC, false, false) +// } -func (s *zetoDomainTestSuite) TestZeto_AnonEncBatch() { - s.testZetoFungible(s.T(), constants.TOKEN_ANON_ENC, true, false) -} +// func (s *zetoDomainTestSuite) TestZeto_AnonEncBatch() { +// s.testZetoFungible(s.T(), constants.TOKEN_ANON_ENC, true, false) +// } -func (s *zetoDomainTestSuite) TestZeto_AnonNullifier() { - s.testZetoFungible(s.T(), constants.TOKEN_ANON_NULLIFIER, false, true) -} +// func (s *zetoDomainTestSuite) TestZeto_AnonNullifier() { +// s.testZetoFungible(s.T(), constants.TOKEN_ANON_NULLIFIER, false, true) +// } -func (s *zetoDomainTestSuite) TestZeto_AnonNullifierBatch() { - s.testZetoFungible(s.T(), constants.TOKEN_ANON_NULLIFIER, true, true) -} +// func (s *zetoDomainTestSuite) TestZeto_AnonNullifierBatch() { +// s.testZetoFungible(s.T(), constants.TOKEN_ANON_NULLIFIER, true, true) +// } func (s *zetoDomainTestSuite) testZetoFungible(t *testing.T, tokenName string, useBatch bool, isNullifiersToken bool) { ctx := context.Background() @@ -122,8 +127,33 @@ func (s *zetoDomainTestSuite) testZetoFungible(t *testing.T, tokenName string, u } log.L(ctx).Infof("Zeto instance deployed to %s", zetoAddress) + var controllerEthAddr string + rpcerr = s.rpc.CallRPC(ctx, &controllerEthAddr, "ptx_resolveVerifier", controllerName, algorithms.ECDSA_SECP256K1, verifiers.ETH_ADDRESS) + require.Nil(t, rpcerr) + + log.L(ctx).Infof("Deploying the sample ERC20 with initialOwner %s", controllerEthAddr) + erc20Address, err := deployERC20(ctx, s.rpc, controllerEthAddr) + require.NoError(t, err) + + log.L(ctx).Infof("Setting the ERC20 contract (%s) to the Zeto instance", erc20Address) + paramsJson, err := json.Marshal(&map[string]string{"_erc20": erc20Address.String()}) + require.NoError(t, err) + var invokeResult testbed.TransactionResult + rpcerr = s.rpc.CallRPC(ctx, &invokeResult, "testbed_invoke", &pldapi.TransactionInput{ + TransactionBase: pldapi.TransactionBase{ + From: controllerName, + To: &zetoAddress, + Function: "setERC20", + Data: paramsJson, + }, + ABI: types.ZetoABI, + }, true) + if rpcerr != nil { + require.NoError(t, rpcerr.Error()) + } + log.L(ctx).Infof("Mint two UTXOs (10, 20) from controller to controller") - _, err := s.mint(ctx, zetoAddress, controllerName, []int64{10, 20}) + _, err = s.mint(ctx, zetoAddress, controllerName, []int64{10, 20}) require.NoError(t, err) var controllerAddr tktypes.Bytes32 @@ -191,6 +221,9 @@ func (s *zetoDomainTestSuite) testZetoFungible(t *testing.T, tokenName string, u assert.Equal(t, int64(5), coins[1].Data.Amount.Int().Int64()) // change for controller assert.Equal(t, controllerAddr.String(), coins[1].Data.Owner.String()) } + + log.L(ctx).Infof("Deposit from ERC20 balance to Zeto") + } func (s *zetoDomainTestSuite) setupContractsAbi(t *testing.T, ctx context.Context, tokenName string) { @@ -265,6 +298,27 @@ func (s *zetoDomainTestSuite) transfer(ctx context.Context, zetoAddress tktypes. return &invokeResult, nil } +func (s *zetoDomainTestSuite) deposit(ctx context.Context, zetoAddress tktypes.EthAddress, sender string, amount int64) (*testbed.TransactionResult, error) { + var invokeResult testbed.TransactionResult + paramsJson, err := json.Marshal(&map[string]int64{"amount": amount}) + if err != nil { + return nil, err + } + rpcerr := s.rpc.CallRPC(ctx, &invokeResult, "testbed_invoke", &pldapi.TransactionInput{ + TransactionBase: pldapi.TransactionBase{ + From: sender, + To: &zetoAddress, + Function: "deposit", + Data: paramsJson, + }, + ABI: types.ZetoABI, + }, true) + if rpcerr != nil { + return nil, rpcerr.Error() + } + return &invokeResult, nil +} + func findAvailableCoins(t *testing.T, ctx context.Context, rpc rpcbackend.Backend, zeto zeto.Zeto, address tktypes.EthAddress, jq *query.QueryJSON, useNullifiers bool) []*types.ZetoCoinState { if jq == nil { jq = query.NewQueryBuilder().Limit(100).Query() @@ -314,3 +368,19 @@ func newTestbed(t *testing.T, hdWalletSeed *testbed.UTInitFunction, domains map[ rpc := rpcbackend.NewRPCClient(resty.New().SetBaseURL(url)) return done, tb, rpc } + +func deployERC20(ctx context.Context, rpc rpcbackend.Backend, controllerAddr string) (*tktypes.EthAddress, error) { + bytes, err := os.ReadFile("./abis/SampleERC20.json") + if err != nil { + return nil, err + } + build := solutils.MustLoadBuildResolveLinks(bytes, map[string]*tktypes.EthAddress{}) + params := fmt.Sprintf(`{"initialOwner":"%s"}`, controllerAddr) + var addr string + rpcerr := rpc.CallRPC(ctx, &addr, "testbed_deployBytecode", controllerName, build.ABI, build.Bytecode.String(), tktypes.RawJSON(params)) + if rpcerr != nil { + return nil, rpcerr.Error() + } + return tktypes.MustEthAddress(addr), nil + +} diff --git a/domains/zeto/pkg/types/abi.go b/domains/zeto/pkg/types/abi.go index 37e203f35..c81ba35e3 100644 --- a/domains/zeto/pkg/types/abi.go +++ b/domains/zeto/pkg/types/abi.go @@ -66,6 +66,13 @@ var ZetoABI = abi.ABI{ {Name: "call", Type: "bytes"}, // assumed to be an encoded "transfer" }, }, + { + Name: "setERC20", + Type: abi.Function, + Inputs: abi.ParameterArray{ + {Name: "_erc20", Type: "address"}, + }, + }, } type InitializerParams struct { From 24695ba95e0163ab3dd7eb251357368a7f61e4dc Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Tue, 3 Dec 2024 14:33:36 -0500 Subject: [PATCH 02/36] add download files as inputs to avoid re-downloading Signed-off-by: Jim Zhang --- domains/zeto/build.gradle | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/domains/zeto/build.gradle b/domains/zeto/build.gradle index 6f98f3022..00ba7a67f 100644 --- a/domains/zeto/build.gradle +++ b/domains/zeto/build.gradle @@ -32,7 +32,7 @@ ext { "github.com/kaleido-io/paladin/domains/zeto/pkg/zetosigner", ] - zetoVersion = "v0.0.7" + zetoVersion = "v0.0.9" zetoHost = "hyperledger-labs" zkpOut = "${projectDir}/zkp" toolsOut = "${projectDir}/tools" @@ -78,6 +78,7 @@ task downloadZetoProver { def outname = "zeto-wasm-${zetoVersion}.tar.gz" def url = "https://github.com/${zetoHost}/zeto/releases/download/${zetoVersion}/${outname}" def f = new File(toolsOut, outname) + inputs.file(f) doFirst { mkdir(f.parent) new URL(url).withInputStream{ i -> f.withOutputStream{ it << i }} @@ -89,6 +90,7 @@ task downloadZetoTestProvingKeys { def outname = "zeto-test-proving-keys-${zetoVersion}.tar.gz" def url = "https://github.com/${zetoHost}/zeto/releases/download/${zetoVersion}/${outname}" def f = new File(toolsOut, outname) + inputs.file(f) doFirst { mkdir(f.parent) new URL(url).withInputStream{ i -> f.withOutputStream{ it << i }} @@ -100,6 +102,7 @@ task downloadZetoCompiledContracts { def outname = "zeto-contracts-${zetoVersion}.tar.gz" def url = "https://github.com/${zetoHost}/zeto/releases/download/${zetoVersion}/${outname}" def f = new File(toolsOut, outname) + inputs.file(f) doFirst { mkdir(f.parent) new URL(url).withInputStream{ i -> f.withOutputStream{ it << i }} From 4c085f82ef22a5f89d7f57076cab5865762a4054 Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Tue, 3 Dec 2024 14:35:04 -0500 Subject: [PATCH 03/36] make deposit and withdraw verifier contracts per-token configuration Signed-off-by: Jim Zhang --- .../integration-test/config-for-deploy.yaml | 17 ++ domains/zeto/integration-test/contracts.go | 27 ++- domains/zeto/integration-test/e2e_test.go | 170 ++++++++++++++---- domains/zeto/integration-test/util.go | 17 +- 4 files changed, 180 insertions(+), 51 deletions(-) diff --git a/domains/zeto/integration-test/config-for-deploy.yaml b/domains/zeto/integration-test/config-for-deploy.yaml index f356dae3d..571b4b77d 100644 --- a/domains/zeto/integration-test/config-for-deploy.yaml +++ b/domains/zeto/integration-test/config-for-deploy.yaml @@ -17,6 +17,14 @@ contracts: - name: Groth16Verifier_CheckInputsOutputsValueBatch abiAndBytecode: path: ./abis/Groth16Verifier_CheckInputsOutputsValueBatch.json + # for the withdraw transaction with nullifiers + - name: Groth16Verifier_CheckNullifierValue + abiAndBytecode: + path: ./abis/Groth16Verifier_CheckNullifierValue.json + # for the withdraw transaction with batch inputs and nullifiers + - name: Groth16Verifier_CheckNullifierValueBatch + abiAndBytecode: + path: ./abis/Groth16Verifier_CheckNullifierValueBatch.json # for the Zeto_Anon token - name: Groth16Verifier_Anon abiAndBytecode: @@ -27,6 +35,9 @@ contracts: - name: Zeto_Anon verifier: Groth16Verifier_Anon batchVerifier: Groth16Verifier_AnonBatch + depositVerifier: Groth16Verifier_CheckHashesValue + withdrawVerifier: Groth16Verifier_CheckInputsOutputsValue + batchWithdrawVerifier: Groth16Verifier_CheckInputsOutputsValueBatch circuitId: anon cloneable: true abiAndBytecode: @@ -41,6 +52,9 @@ contracts: - name: Zeto_AnonEnc verifier: Groth16Verifier_AnonEnc batchVerifier: Groth16Verifier_AnonEncBatch + depositVerifier: Groth16Verifier_CheckHashesValue + withdrawVerifier: Groth16Verifier_CheckInputsOutputsValue + batchWithdrawVerifier: Groth16Verifier_CheckInputsOutputsValueBatch circuitId: anon_enc cloneable: true abiAndBytecode: @@ -64,6 +78,9 @@ contracts: - name: Zeto_AnonNullifier verifier: Groth16Verifier_AnonNullifier batchVerifier: Groth16Verifier_AnonNullifierBatch + depositVerifier: Groth16Verifier_CheckHashesValue + withdrawVerifier: Groth16Verifier_CheckNullifierValue + batchWithdrawVerifier: Groth16Verifier_CheckNullifierValueBatch circuitId: anon_nullifier cloneable: true abiAndBytecode: diff --git a/domains/zeto/integration-test/contracts.go b/domains/zeto/integration-test/contracts.go index e75c75e13..480540e60 100644 --- a/domains/zeto/integration-test/contracts.go +++ b/domains/zeto/integration-test/contracts.go @@ -40,9 +40,12 @@ type ZetoDomainContracts struct { } type cloneableContract struct { - circuitId string - verifier string - batchVerifier string + circuitId string + verifier string + batchVerifier string + depositVerifier string + withdrawVerifier string + batchWithdrawVerifier string } func newZetoDomainContracts() *ZetoDomainContracts { @@ -88,9 +91,12 @@ func findCloneableContracts(config *domainConfig) map[string]cloneableContract { for _, contract := range config.DomainContracts.Implementations { if contract.Cloneable { cloneableContracts[contract.Name] = cloneableContract{ - circuitId: contract.CircuitId, - verifier: contract.Verifier, - batchVerifier: contract.BatchVerifier, + circuitId: contract.CircuitId, + verifier: contract.Verifier, + batchVerifier: contract.BatchVerifier, + depositVerifier: contract.DepositVerifier, + withdrawVerifier: contract.WithdrawVerifier, + batchWithdrawVerifier: contract.BatchWithdrawVerifier, } } } @@ -165,6 +171,9 @@ func registerImpl(ctx context.Context, name string, domainContracts *ZetoDomainC log.L(ctx).Infof("Registering implementation %s", name) verifierName := domainContracts.cloneableContracts[name].verifier batchVerifierName := domainContracts.cloneableContracts[name].batchVerifier + depositVerifierName := domainContracts.cloneableContracts[name].depositVerifier + withdrawVerifierName := domainContracts.cloneableContracts[name].withdrawVerifier + batchWithdrawVerifierName := domainContracts.cloneableContracts[name].batchWithdrawVerifier implAddr, ok := domainContracts.deployedContracts[name] if !ok { return fmt.Errorf("implementation contract %s not found among the deployed contracts", name) @@ -177,15 +186,15 @@ func registerImpl(ctx context.Context, name string, domainContracts *ZetoDomainC if !ok { return fmt.Errorf("batch verifier contract %s not found among the deployed contracts", batchVerifierName) } - depositVerifierAddr, ok := domainContracts.deployedContracts["Groth16Verifier_CheckHashesValue"] + depositVerifierAddr, ok := domainContracts.deployedContracts[depositVerifierName] if !ok { return fmt.Errorf("deposit verifier contract not found among the deployed contracts") } - withdrawVerifierAddr, ok := domainContracts.deployedContracts["Groth16Verifier_CheckInputsOutputsValue"] + withdrawVerifierAddr, ok := domainContracts.deployedContracts[withdrawVerifierName] if !ok { return fmt.Errorf("withdraw verifier contract not found among the deployed contracts") } - batchWithdrawVerifierAddr, ok := domainContracts.deployedContracts["Groth16Verifier_CheckInputsOutputsValueBatch"] + batchWithdrawVerifierAddr, ok := domainContracts.deployedContracts[batchWithdrawVerifierName] if !ok { return fmt.Errorf("batch withdraw verifier contract not found among the deployed contracts") } diff --git a/domains/zeto/integration-test/e2e_test.go b/domains/zeto/integration-test/e2e_test.go index 8761b6ea8..369d24dad 100644 --- a/domains/zeto/integration-test/e2e_test.go +++ b/domains/zeto/integration-test/e2e_test.go @@ -22,7 +22,6 @@ import ( "fmt" "os" "testing" - "time" "github.com/go-resty/resty/v2" "github.com/hyperledger/firefly-signer/pkg/rpcbackend" @@ -62,6 +61,7 @@ type zetoDomainTestSuite struct { domainName string domain zeto.Zeto rpc rpcbackend.Backend + tb testbed.Testbed done func() } @@ -75,12 +75,13 @@ func (s *zetoDomainTestSuite) SetupSuite() { log.L(ctx).Infof("Domain name = %s", domainName) config := PrepareZetoConfig(s.T(), s.deployedContracts, "../zkp") zeto, zetoTestbed := newZetoDomain(s.T(), domainContracts, config) - done, _, rpc := newTestbed(s.T(), s.hdWalletSeed, map[string]*testbed.TestbedDomain{ + done, tb, rpc := newTestbed(s.T(), s.hdWalletSeed, map[string]*testbed.TestbedDomain{ domainName: zetoTestbed, }) s.domainName = domainName s.domain = zeto s.rpc = rpc + s.tb = tb s.done = done } @@ -92,30 +93,31 @@ func (s *zetoDomainTestSuite) TestZeto_Anon() { s.testZetoFungible(s.T(), constants.TOKEN_ANON, false, false) } -// func (s *zetoDomainTestSuite) TestZeto_AnonBatch() { -// s.T().Skip() -// s.testZetoFungible(s.T(), constants.TOKEN_ANON, true, false) -// } +func (s *zetoDomainTestSuite) TestZeto_AnonBatch() { + s.testZetoFungible(s.T(), constants.TOKEN_ANON, true, false) +} -// func (s *zetoDomainTestSuite) TestZeto_AnonEnc() { -// s.testZetoFungible(s.T(), constants.TOKEN_ANON_ENC, false, false) -// } +func (s *zetoDomainTestSuite) TestZeto_AnonEnc() { + s.testZetoFungible(s.T(), constants.TOKEN_ANON_ENC, false, false) +} -// func (s *zetoDomainTestSuite) TestZeto_AnonEncBatch() { -// s.testZetoFungible(s.T(), constants.TOKEN_ANON_ENC, true, false) -// } +func (s *zetoDomainTestSuite) TestZeto_AnonEncBatch() { + s.testZetoFungible(s.T(), constants.TOKEN_ANON_ENC, true, false) +} -// func (s *zetoDomainTestSuite) TestZeto_AnonNullifier() { -// s.testZetoFungible(s.T(), constants.TOKEN_ANON_NULLIFIER, false, true) -// } +func (s *zetoDomainTestSuite) TestZeto_AnonNullifier() { + s.testZetoFungible(s.T(), constants.TOKEN_ANON_NULLIFIER, false, true) +} -// func (s *zetoDomainTestSuite) TestZeto_AnonNullifierBatch() { -// s.testZetoFungible(s.T(), constants.TOKEN_ANON_NULLIFIER, true, true) -// } +func (s *zetoDomainTestSuite) TestZeto_AnonNullifierBatch() { + s.testZetoFungible(s.T(), constants.TOKEN_ANON_NULLIFIER, true, true) +} func (s *zetoDomainTestSuite) testZetoFungible(t *testing.T, tokenName string, useBatch bool, isNullifiersToken bool) { ctx := context.Background() + log.L(ctx).Info("*************************************") log.L(ctx).Infof("Deploying an instance of the %s token", tokenName) + log.L(ctx).Info("*************************************") s.setupContractsAbi(t, ctx, tokenName) var zetoAddress tktypes.EthAddress rpcerr := s.rpc.CallRPC(ctx, &zetoAddress, "testbed_deploy", @@ -138,21 +140,21 @@ func (s *zetoDomainTestSuite) testZetoFungible(t *testing.T, tokenName string, u log.L(ctx).Infof("Setting the ERC20 contract (%s) to the Zeto instance", erc20Address) paramsJson, err := json.Marshal(&map[string]string{"_erc20": erc20Address.String()}) require.NoError(t, err) - var invokeResult testbed.TransactionResult - rpcerr = s.rpc.CallRPC(ctx, &invokeResult, "testbed_invoke", &pldapi.TransactionInput{ + _, err = s.tb.ExecTransactionSync(ctx, &pldapi.TransactionInput{ TransactionBase: pldapi.TransactionBase{ + Type: pldapi.TransactionTypePublic.Enum(), From: controllerName, To: &zetoAddress, Function: "setERC20", Data: paramsJson, }, ABI: types.ZetoABI, - }, true) - if rpcerr != nil { - require.NoError(t, rpcerr.Error()) - } + }) + require.NoError(t, err) + log.L(ctx).Info("*************************************") log.L(ctx).Infof("Mint two UTXOs (10, 20) from controller to controller") + log.L(ctx).Info("*************************************") _, err = s.mint(ctx, zetoAddress, controllerName, []int64{10, 20}) require.NoError(t, err) @@ -169,7 +171,9 @@ func (s *zetoDomainTestSuite) testZetoFungible(t *testing.T, tokenName string, u // for testing the batch circuits, we mint the 3rd UTXO if useBatch { + log.L(ctx).Info("*************************************") log.L(ctx).Infof("Mint 30 from controller to controller") + log.L(ctx).Info("*************************************") _, err = s.mint(ctx, zetoAddress, controllerName, []int64{30}) require.NoError(t, err) } @@ -183,12 +187,16 @@ func (s *zetoDomainTestSuite) testZetoFungible(t *testing.T, tokenName string, u // for testing the batch circuits, we transfer 50 which would require 3 UTXOs (>2) amount1 := 10 amount2 := 40 + log.L(ctx).Info("*************************************") log.L(ctx).Infof("Transfer %d from controller to recipient1 (%d) and recipient2 (%d)", amount1+amount2, amount1, amount2) + log.L(ctx).Info("*************************************") _, err = s.transfer(ctx, zetoAddress, controllerName, []string{recipient1Name, recipient2Name}, []int64{int64(amount1), int64(amount2)}) require.NoError(t, err) } else { amount := 25 + log.L(ctx).Info("*************************************") log.L(ctx).Infof("Transfer %d from controller to recipient1", amount) + log.L(ctx).Info("*************************************") _, err = s.transfer(ctx, zetoAddress, controllerName, []string{recipient1Name}, []int64{int64(amount)}) require.NoError(t, err) } @@ -201,14 +209,6 @@ func (s *zetoDomainTestSuite) testZetoFungible(t *testing.T, tokenName string, u if useBatch { expectedCoins = 3 } - - // some tokens like Zeto_AnonNullifier with batch can take some time to process - // so we need to retry at least once - if len(coins) != expectedCoins { - time.Sleep(1 * time.Second) - coins = findAvailableCoins(t, ctx, s.rpc, s.domain, zetoAddress, nil, isNullifiersToken) - } - require.Len(t, coins, expectedCoins) if useBatch { @@ -222,8 +222,29 @@ func (s *zetoDomainTestSuite) testZetoFungible(t *testing.T, tokenName string, u assert.Equal(t, controllerAddr.String(), coins[1].Data.Owner.String()) } + log.L(ctx).Infof("Mint 100 in ERC20 to controller") + err = s.mintERC20(ctx, *erc20Address, 100, controllerName, controllerEthAddr) + require.NoError(t, err) + + log.L(ctx).Infof("Approve Zeto (%s) to spend from the controller account (%s)", zetoAddress.String(), controllerEthAddr) + err = s.approveERC20(ctx, *erc20Address, zetoAddress, 100, controllerName) + require.NoError(t, err) + + log.L(ctx).Info("*************************************") log.L(ctx).Infof("Deposit from ERC20 balance to Zeto") + log.L(ctx).Info("*************************************") + _, err = s.deposit(ctx, zetoAddress, controllerName, 100) + require.NoError(t, err) + expectedCoins += 2 // the deposit call produces 2 output UTXOs for the receiver + coins = findAvailableCoins(t, ctx, s.rpc, s.domain, zetoAddress, nil, isNullifiersToken) + require.Len(t, coins, expectedCoins) + + log.L(ctx).Info("*************************************") + log.L(ctx).Infof("Withdraw back to ERC20 balance from Zeto") + log.L(ctx).Info("*************************************") + _, err = s.withdraw(ctx, zetoAddress, controllerName, 100) + require.NoError(t, err) } func (s *zetoDomainTestSuite) setupContractsAbi(t *testing.T, ctx context.Context, tokenName string) { @@ -299,11 +320,14 @@ func (s *zetoDomainTestSuite) transfer(ctx context.Context, zetoAddress tktypes. } func (s *zetoDomainTestSuite) deposit(ctx context.Context, zetoAddress tktypes.EthAddress, sender string, amount int64) (*testbed.TransactionResult, error) { - var invokeResult testbed.TransactionResult - paramsJson, err := json.Marshal(&map[string]int64{"amount": amount}) + params := &types.DepositParams{ + Amount: tktypes.Int64ToInt256(amount), + } + paramsJson, err := json.Marshal(params) if err != nil { return nil, err } + var invokeResult testbed.TransactionResult rpcerr := s.rpc.CallRPC(ctx, &invokeResult, "testbed_invoke", &pldapi.TransactionInput{ TransactionBase: pldapi.TransactionBase{ From: sender, @@ -319,6 +343,74 @@ func (s *zetoDomainTestSuite) deposit(ctx context.Context, zetoAddress tktypes.E return &invokeResult, nil } +func (s *zetoDomainTestSuite) withdraw(ctx context.Context, zetoAddress tktypes.EthAddress, sender string, amount int64) (*testbed.TransactionResult, error) { + params := &types.WithdrawParams{ + Amount: tktypes.Int64ToInt256(amount), + } + paramsJson, err := json.Marshal(params) + if err != nil { + return nil, err + } + var invokeResult testbed.TransactionResult + rpcerr := s.rpc.CallRPC(ctx, &invokeResult, "testbed_invoke", &pldapi.TransactionInput{ + TransactionBase: pldapi.TransactionBase{ + From: sender, + To: &zetoAddress, + Function: "withdraw", + Data: paramsJson, + }, + ABI: types.ZetoABI, + }, true) + if rpcerr != nil { + return nil, rpcerr.Error() + } + return &invokeResult, nil +} + +func (s *zetoDomainTestSuite) mintERC20(ctx context.Context, erc20Address tktypes.EthAddress, amount int64, from, to string) error { + paramsJson, err := json.Marshal(&map[string]any{"amount": amount, "to": to}) + if err != nil { + return err + } + build, err := getERC20Spec() + if err != nil { + return err + } + _, err = s.tb.ExecTransactionSync(ctx, &pldapi.TransactionInput{ + TransactionBase: pldapi.TransactionBase{ + Type: pldapi.TransactionTypePublic.Enum(), + From: from, + To: &erc20Address, + Function: "mint", + Data: paramsJson, + }, + ABI: build.ABI, + }) + return err +} + +func (s *zetoDomainTestSuite) approveERC20(ctx context.Context, erc20Address, zetoAddress tktypes.EthAddress, amount int64, from string) error { + paramsJson, err := json.Marshal(&map[string]any{"spender": zetoAddress.String(), "value": amount}) + if err != nil { + return err + } + build, err := getERC20Spec() + if err != nil { + return err + } + _, err = s.tb.ExecTransactionSync(ctx, &pldapi.TransactionInput{ + TransactionBase: pldapi.TransactionBase{ + Type: pldapi.TransactionTypePublic.Enum(), + From: from, + To: &erc20Address, + Function: "approve", + Data: paramsJson, + }, + ABI: build.ABI, + }) + return err +} + func findAvailableCoins(t *testing.T, ctx context.Context, rpc rpcbackend.Backend, zeto zeto.Zeto, address tktypes.EthAddress, jq *query.QueryJSON, useNullifiers bool) []*types.ZetoCoinState { if jq == nil { jq = query.NewQueryBuilder().Limit(100).Query() @@ -369,12 +461,20 @@ func newTestbed(t *testing.T, hdWalletSeed *testbed.UTInitFunction, domains map[ return done, tb, rpc } -func deployERC20(ctx context.Context, rpc rpcbackend.Backend, controllerAddr string) (*tktypes.EthAddress, error) { +func getERC20Spec() (*solutils.SolidityBuild, error) { bytes, err := os.ReadFile("./abis/SampleERC20.json") if err != nil { return nil, err } build := solutils.MustLoadBuildResolveLinks(bytes, map[string]*tktypes.EthAddress{}) + return build, nil +} + +func deployERC20(ctx context.Context, rpc rpcbackend.Backend, controllerAddr string) (*tktypes.EthAddress, error) { + build, err := getERC20Spec() + if err != nil { + return nil, err + } params := fmt.Sprintf(`{"initialOwner":"%s"}`, controllerAddr) var addr string rpcerr := rpc.CallRPC(ctx, &addr, "testbed_deployBytecode", controllerName, build.ABI, build.Bytecode.String(), tktypes.RawJSON(params)) diff --git a/domains/zeto/integration-test/util.go b/domains/zeto/integration-test/util.go index 9772eb7a9..10ffd554f 100644 --- a/domains/zeto/integration-test/util.go +++ b/domains/zeto/integration-test/util.go @@ -41,13 +41,16 @@ type domainContracts struct { } type domainContract struct { - Name string `yaml:"name"` - Verifier string `yaml:"verifier"` - BatchVerifier string `yaml:"batchVerifier"` - CircuitId string `yaml:"circuitId"` - AbiAndBytecode abiAndBytecode `yaml:"abiAndBytecode"` - Libraries []string `yaml:"libraries"` - Cloneable bool `yaml:"cloneable"` + Name string `yaml:"name"` + Verifier string `yaml:"verifier"` + BatchVerifier string `yaml:"batchVerifier"` + DepositVerifier string `yaml:"depositVerifier"` + WithdrawVerifier string `yaml:"withdrawVerifier"` + BatchWithdrawVerifier string `yaml:"batchWithdrawVerifier"` + CircuitId string `yaml:"circuitId"` + AbiAndBytecode abiAndBytecode `yaml:"abiAndBytecode"` + Libraries []string `yaml:"libraries"` + Cloneable bool `yaml:"cloneable"` } type abiAndBytecode struct { From 4d196f28c1939127834b4c794657640c0b5331f8 Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Tue, 3 Dec 2024 14:35:51 -0500 Subject: [PATCH 04/36] Add handler for deposit and withdraw requests Signed-off-by: Jim Zhang --- domains/zeto/internal/zeto/common/utils.go | 73 ++++ domains/zeto/internal/zeto/handler_deposit.go | 222 ++++++++++++ domains/zeto/internal/zeto/handler_events.go | 33 +- domains/zeto/internal/zeto/handler_mint.go | 5 +- .../zeto/internal/zeto/handler_transfer.go | 127 ++----- .../internal/zeto/handler_transfer_test.go | 12 +- .../zeto/internal/zeto/handler_withdraw.go | 317 ++++++++++++++++++ domains/zeto/internal/zeto/handlers.go | 31 -- domains/zeto/internal/zeto/handlers_test.go | 32 -- domains/zeto/internal/zeto/signer/decoder.go | 6 +- .../internal/zeto/signer/inputs_assembler.go | 92 ++++- .../zeto/internal/zeto/signer/snark_prover.go | 24 +- .../internal/zeto/signer/snark_prover_test.go | 36 -- domains/zeto/internal/zeto/states.go | 85 ++++- domains/zeto/internal/zeto/states_test.go | 8 +- domains/zeto/internal/zeto/utils.go | 144 ++++++-- domains/zeto/internal/zeto/zeto.go | 34 +- domains/zeto/internal/zeto/zeto_test.go | 10 + domains/zeto/pkg/constants/constants.go | 17 +- domains/zeto/pkg/proto/zeto.proto | 7 +- domains/zeto/pkg/types/abi.go | 22 ++ 21 files changed, 1033 insertions(+), 304 deletions(-) create mode 100644 domains/zeto/internal/zeto/common/utils.go create mode 100644 domains/zeto/internal/zeto/handler_deposit.go create mode 100644 domains/zeto/internal/zeto/handler_withdraw.go delete mode 100644 domains/zeto/internal/zeto/handlers.go delete mode 100644 domains/zeto/internal/zeto/handlers_test.go diff --git a/domains/zeto/internal/zeto/common/utils.go b/domains/zeto/internal/zeto/common/utils.go new file mode 100644 index 000000000..b6b9fb206 --- /dev/null +++ b/domains/zeto/internal/zeto/common/utils.go @@ -0,0 +1,73 @@ +/* + * Copyright © 2024 Kaleido, Inc. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package common + +import ( + "github.com/kaleido-io/paladin/domains/zeto/pkg/constants" +) + +func IsNullifiersCircuit(circuitId string) bool { + nullifierCircuits := []string{ + constants.CIRCUIT_ANON_NULLIFIER, + constants.CIRCUIT_ANON_NULLIFIER_BATCH, + constants.CIRCUIT_WITHDRAW_NULLIFIER, + constants.CIRCUIT_WITHDRAW_NULLIFIER_BATCH, + } + for _, c := range nullifierCircuits { + if circuitId == c { + return true + } + } + return false +} + +func IsEncryptionCircuit(circuitId string) bool { + encryptionCircuits := []string{ + constants.CIRCUIT_ANON_ENC, + constants.CIRCUIT_ANON_ENC_BATCH, + } + for _, c := range encryptionCircuits { + if circuitId == c { + return true + } + } + return false +} + +func IsBatchCircuit(sizeOfEndorsableStates int) bool { + if sizeOfEndorsableStates <= 2 { + return false + } + return true +} + +func IsNullifiersToken(tokenName string) bool { + return tokenName == constants.TOKEN_ANON_NULLIFIER +} + +func IsEncryptionToken(tokenName string) bool { + return tokenName == constants.TOKEN_ANON_ENC +} + +// the Zeto implementations support two input/output sizes for the circuits: 2 and 10, +// if the input or output size is larger than 2, then the batch circuit is used with +// input/output size 10 +func GetInputSize(sizeOfEndorsableStates int) int { + if sizeOfEndorsableStates <= 2 { + return 2 + } + return 10 +} diff --git a/domains/zeto/internal/zeto/handler_deposit.go b/domains/zeto/internal/zeto/handler_deposit.go new file mode 100644 index 000000000..ff339e41c --- /dev/null +++ b/domains/zeto/internal/zeto/handler_deposit.go @@ -0,0 +1,222 @@ +/* + * Copyright © 2024 Kaleido, Inc. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package zeto + +import ( + "context" + "encoding/json" + + "github.com/hyperledger/firefly-common/pkg/i18n" + "github.com/hyperledger/firefly-signer/pkg/abi" + "github.com/kaleido-io/paladin/domains/zeto/internal/msgs" + "github.com/kaleido-io/paladin/domains/zeto/internal/zeto/common" + "github.com/kaleido-io/paladin/domains/zeto/pkg/constants" + corepb "github.com/kaleido-io/paladin/domains/zeto/pkg/proto" + "github.com/kaleido-io/paladin/domains/zeto/pkg/types" + "github.com/kaleido-io/paladin/domains/zeto/pkg/zetosigner/zetosignerapi" + "github.com/kaleido-io/paladin/toolkit/pkg/algorithms" + "github.com/kaleido-io/paladin/toolkit/pkg/domain" + pb "github.com/kaleido-io/paladin/toolkit/pkg/prototk" + "github.com/kaleido-io/paladin/toolkit/pkg/tktypes" + "github.com/kaleido-io/paladin/toolkit/pkg/verifiers" + "google.golang.org/protobuf/proto" +) + +type depositHandler struct { + zeto *Zeto +} + +var depositABI = &abi.Entry{ + Type: abi.Function, + Name: "deposit", + Inputs: abi.ParameterArray{ + {Name: "amount", Type: "uint256"}, + {Name: "outputs", Type: "uint256[]"}, + {Name: "proof", Type: "tuple", InternalType: "struct Commonlib.Proof", Components: proofComponents}, + {Name: "data", Type: "bytes"}, + }, +} + +func (h *depositHandler) ValidateParams(ctx context.Context, config *types.DomainInstanceConfig, params string) (interface{}, error) { + var depositParams types.DepositParams + if err := json.Unmarshal([]byte(params), &depositParams); err != nil { + return nil, err + } + + if err := validateAmountParam(ctx, depositParams.Amount, 0); err != nil { + return nil, err + } + + return depositParams.Amount, nil +} + +func (h *depositHandler) Init(ctx context.Context, tx *types.ParsedTransaction, req *pb.InitTransactionRequest) (*pb.InitTransactionResponse, error) { + res := &pb.InitTransactionResponse{ + RequiredVerifiers: []*pb.ResolveVerifierRequest{ + { + Lookup: tx.Transaction.From, + Algorithm: h.zeto.getAlgoZetoSnarkBJJ(), + VerifierType: zetosignerapi.IDEN3_PUBKEY_BABYJUBJUB_COMPRESSED_0X, + }, + }, + } + return res, nil +} + +func (h *depositHandler) Assemble(ctx context.Context, tx *types.ParsedTransaction, req *pb.AssembleTransactionRequest) (*pb.AssembleTransactionResponse, error) { + amount := tx.Params.(*tktypes.HexUint256) + + resolvedSender := domain.FindVerifier(tx.Transaction.From, h.zeto.getAlgoZetoSnarkBJJ(), zetosignerapi.IDEN3_PUBKEY_BABYJUBJUB_COMPRESSED_0X, req.ResolvedVerifiers) + if resolvedSender == nil { + return nil, i18n.NewError(ctx, msgs.MsgErrorResolveVerifier, tx.Transaction.From) + } + + useNullifiers := common.IsNullifiersToken(tx.DomainConfig.TokenName) + outputCoins, outputStates, err := h.zeto.prepareOutputsForDeposit(ctx, useNullifiers, amount, req.ResolvedVerifiers) + if err != nil { + return nil, i18n.NewError(ctx, msgs.MsgErrorPrepTxOutputs, err) + } + + payloadBytes, err := h.formatProvingRequest(ctx, outputCoins) + if err != nil { + return nil, i18n.NewError(ctx, msgs.MsgErrorFormatProvingReq, err) + } + + amountStr := amount.Int().Text(10) + return &pb.AssembleTransactionResponse{ + AssemblyResult: pb.AssembleTransactionResponse_OK, + AssembledTransaction: &pb.AssembledTransaction{ + OutputStates: outputStates, + DomainData: &amountStr, + }, + AttestationPlan: []*pb.AttestationRequest{ + { + Name: "sender", + AttestationType: pb.AttestationType_SIGN, + Algorithm: h.zeto.getAlgoZetoSnarkBJJ(), + VerifierType: zetosignerapi.IDEN3_PUBKEY_BABYJUBJUB_COMPRESSED_0X, + PayloadType: zetosignerapi.PAYLOAD_DOMAIN_ZETO_SNARK, + Payload: payloadBytes, + Parties: []string{tx.Transaction.From}, + }, + { + Name: "submitter", + AttestationType: pb.AttestationType_ENDORSE, + Algorithm: algorithms.ECDSA_SECP256K1, + VerifierType: verifiers.ETH_ADDRESS, + Parties: []string{tx.Transaction.From}, + }, + }, + }, nil +} + +func (h *depositHandler) Endorse(ctx context.Context, tx *types.ParsedTransaction, req *pb.EndorseTransactionRequest) (*pb.EndorseTransactionResponse, error) { + return &pb.EndorseTransactionResponse{ + EndorsementResult: pb.EndorseTransactionResponse_ENDORSER_SUBMIT, + }, nil +} + +func (h *depositHandler) Prepare(ctx context.Context, tx *types.ParsedTransaction, req *pb.PrepareTransactionRequest) (*pb.PrepareTransactionResponse, error) { + var proofRes corepb.ProvingResponse + result := domain.FindAttestation("sender", req.AttestationResult) + if result == nil { + return nil, i18n.NewError(ctx, msgs.MsgErrorFindSenderAttestation) + } + if err := proto.Unmarshal(result.Payload, &proofRes); err != nil { + return nil, i18n.NewError(ctx, msgs.MsgErrorUnmarshalProvingRes, err) + } + + outputSize := common.GetInputSize(len(req.OutputStates)) + outputs := make([]string, outputSize) + for i := 0; i < outputSize; i++ { + if i < len(req.OutputStates) { + state := req.OutputStates[i] + coin, err := h.zeto.makeCoin(state.StateDataJson) + if err != nil { + return nil, i18n.NewError(ctx, msgs.MsgErrorParseOutputStates, err) + } + hash, err := coin.Hash(ctx) + if err != nil { + return nil, i18n.NewError(ctx, msgs.MsgErrorHashOutputState, err) + } + outputs[i] = hash.String() + } else { + outputs[i] = "0" + } + } + + data, err := encodeTransactionData(ctx, req.Transaction) + if err != nil { + return nil, i18n.NewError(ctx, msgs.MsgErrorEncodeTxData, err) + } + amount := tktypes.MustParseHexUint256(*req.DomainData) + params := map[string]any{ + "amount": amount.Int().Text(10), + "outputs": outputs, + "proof": encodeProof(proofRes.Proof), + "data": data, + } + depositFunction := depositABI + paramsJSON, err := json.Marshal(params) + if err != nil { + return nil, i18n.NewError(ctx, msgs.MsgErrorMarshalPrepedParams, err) + } + functionJSON, err := json.Marshal(depositFunction) + if err != nil { + return nil, err + } + + return &pb.PrepareTransactionResponse{ + Transaction: &pb.PreparedTransaction{ + FunctionAbiJson: string(functionJSON), + ParamsJson: string(paramsJSON), + }, + }, nil +} + +func (h *depositHandler) formatProvingRequest(ctx context.Context, outputCoins []*types.ZetoCoin) ([]byte, error) { + outputSize := common.GetInputSize(len(outputCoins)) + outputCommitments := make([]string, outputSize) + outputValueInts := make([]uint64, outputSize) + outputSalts := make([]string, outputSize) + outputOwners := make([]string, outputSize) + for i := 0; i < outputSize; i++ { + if i < len(outputCoins) { + coin := outputCoins[i] + hash, err := coin.Hash(ctx) + if err != nil { + return nil, i18n.NewError(ctx, msgs.MsgErrorHashInputState, err) + } + outputCommitments[i] = hash.Int().Text(16) + outputValueInts[i] = coin.Amount.Int().Uint64() + outputSalts[i] = coin.Salt.Int().Text(16) + outputOwners[i] = coin.Owner.String() + } else { + outputSalts[i] = "0" + } + } + + payload := &corepb.ProvingRequest{ + CircuitId: constants.CIRCUIT_DEPOSIT, + Common: &corepb.ProvingRequestCommon{ + OutputCommitments: outputCommitments, + OutputValues: outputValueInts, + OutputSalts: outputSalts, + OutputOwners: outputOwners, + }, + } + return proto.Marshal(payload) +} diff --git a/domains/zeto/internal/zeto/handler_events.go b/domains/zeto/internal/zeto/handler_events.go index c876d081d..04e3d5e40 100644 --- a/domains/zeto/internal/zeto/handler_events.go +++ b/domains/zeto/internal/zeto/handler_events.go @@ -9,6 +9,7 @@ import ( "github.com/hyperledger-labs/zeto/go-sdk/pkg/sparse-merkle-tree/node" "github.com/hyperledger/firefly-common/pkg/i18n" "github.com/kaleido-io/paladin/domains/zeto/internal/msgs" + "github.com/kaleido-io/paladin/domains/zeto/internal/zeto/common" "github.com/kaleido-io/paladin/domains/zeto/internal/zeto/smt" "github.com/kaleido-io/paladin/toolkit/pkg/log" "github.com/kaleido-io/paladin/toolkit/pkg/prototk" @@ -28,7 +29,7 @@ func (z *Zeto) handleMintEvent(ctx context.Context, tree core.SparseMerkleTree, Location: ev.Location, }) res.ConfirmedStates = append(res.ConfirmedStates, parseStatesFromEvent(txID, mint.Outputs)...) - if isNullifiersToken(tokenName) { + if common.IsNullifiersToken(tokenName) { err := z.updateMerkleTree(ctx, tree, storage, txID, mint.Outputs) if err != nil { return i18n.NewError(ctx, msgs.MsgErrorUpdateSMT, "UTXOMint", err) @@ -54,7 +55,7 @@ func (z *Zeto) handleTransferEvent(ctx context.Context, tree core.SparseMerkleTr }) res.SpentStates = append(res.SpentStates, parseStatesFromEvent(txID, transfer.Inputs)...) res.ConfirmedStates = append(res.ConfirmedStates, parseStatesFromEvent(txID, transfer.Outputs)...) - if isNullifiersToken(tokenName) { + if common.IsNullifiersToken(tokenName) { err := z.updateMerkleTree(ctx, tree, storage, txID, transfer.Outputs) if err != nil { return i18n.NewError(ctx, msgs.MsgErrorUpdateSMT, "UTXOTransfer", err) @@ -80,7 +81,7 @@ func (z *Zeto) handleTransferWithEncryptionEvent(ctx context.Context, tree core. }) res.SpentStates = append(res.SpentStates, parseStatesFromEvent(txID, transfer.Inputs)...) res.ConfirmedStates = append(res.ConfirmedStates, parseStatesFromEvent(txID, transfer.Outputs)...) - if isNullifiersToken(tokenName) { + if common.IsNullifiersToken(tokenName) { err := z.updateMerkleTree(ctx, tree, storage, txID, transfer.Outputs) if err != nil { return i18n.NewError(ctx, msgs.MsgErrorUpdateSMT, "UTXOTransferWithEncryptedValues", err) @@ -92,6 +93,32 @@ func (z *Zeto) handleTransferWithEncryptionEvent(ctx context.Context, tree core. return nil } +func (z *Zeto) handleWithdrawEvent(ctx context.Context, tree core.SparseMerkleTree, storage smt.StatesStorage, ev *prototk.OnChainEvent, tokenName string, res *prototk.HandleEventBatchResponse) error { + var withdraw WithdrawEvent + if err := json.Unmarshal([]byte(ev.DataJson), &withdraw); err == nil { + txID := decodeTransactionData(withdraw.Data) + if txID == nil { + log.L(ctx).Errorf("Failed to decode transaction data for withdraw event: %s. Skip to the next event", withdraw.Data) + return nil + } + res.TransactionsComplete = append(res.TransactionsComplete, &prototk.CompletedTransaction{ + TransactionId: txID.String(), + Location: ev.Location, + }) + res.SpentStates = append(res.SpentStates, parseStatesFromEvent(txID, withdraw.Inputs)...) + res.ConfirmedStates = append(res.ConfirmedStates, parseStatesFromEvent(txID, []tktypes.HexUint256{withdraw.Output})...) + if common.IsNullifiersToken(tokenName) { + err := z.updateMerkleTree(ctx, tree, storage, txID, []tktypes.HexUint256{withdraw.Output}) + if err != nil { + return i18n.NewError(ctx, msgs.MsgErrorUpdateSMT, "UTXOWithdraw", err) + } + } + } else { + log.L(ctx).Errorf("Failed to unmarshal withdraw event: %s", err) + } + return nil +} + func (z *Zeto) updateMerkleTree(ctx context.Context, tree core.SparseMerkleTree, storage smt.StatesStorage, txID tktypes.HexBytes, outputs []tktypes.HexUint256) error { storage.SetTransactionId(txID.HexString0xPrefix()) for _, out := range outputs { diff --git a/domains/zeto/internal/zeto/handler_mint.go b/domains/zeto/internal/zeto/handler_mint.go index c8f9e2f6c..ea7af068d 100644 --- a/domains/zeto/internal/zeto/handler_mint.go +++ b/domains/zeto/internal/zeto/handler_mint.go @@ -22,6 +22,7 @@ import ( "github.com/hyperledger/firefly-common/pkg/i18n" "github.com/hyperledger/firefly-signer/pkg/abi" "github.com/kaleido-io/paladin/domains/zeto/internal/msgs" + "github.com/kaleido-io/paladin/domains/zeto/internal/zeto/common" "github.com/kaleido-io/paladin/domains/zeto/pkg/types" "github.com/kaleido-io/paladin/domains/zeto/pkg/zetosigner/zetosignerapi" "github.com/kaleido-io/paladin/toolkit/pkg/algorithms" @@ -76,8 +77,8 @@ func (h *mintHandler) Init(ctx context.Context, tx *types.ParsedTransaction, req func (h *mintHandler) Assemble(ctx context.Context, tx *types.ParsedTransaction, req *pb.AssembleTransactionRequest) (*pb.AssembleTransactionResponse, error) { params := tx.Params.([]*types.TransferParamEntry) - useNullifiers := isNullifiersToken(tx.DomainConfig.TokenName) - _, outputStates, err := h.zeto.prepareOutputs(ctx, useNullifiers, params, req.ResolvedVerifiers) + useNullifiers := common.IsNullifiersToken(tx.DomainConfig.TokenName) + _, outputStates, err := h.zeto.prepareOutputsForTransfer(ctx, useNullifiers, params, req.ResolvedVerifiers) if err != nil { return nil, err } diff --git a/domains/zeto/internal/zeto/handler_transfer.go b/domains/zeto/internal/zeto/handler_transfer.go index 8d790519d..dd99362c5 100644 --- a/domains/zeto/internal/zeto/handler_transfer.go +++ b/domains/zeto/internal/zeto/handler_transfer.go @@ -18,18 +18,15 @@ package zeto import ( "context" "encoding/json" - "math/big" "strings" - "github.com/hyperledger-labs/zeto/go-sdk/pkg/sparse-merkle-tree/core" - "github.com/hyperledger-labs/zeto/go-sdk/pkg/sparse-merkle-tree/node" "github.com/hyperledger/firefly-common/pkg/i18n" "github.com/hyperledger/firefly-signer/pkg/abi" "github.com/kaleido-io/paladin/domains/zeto/internal/msgs" + "github.com/kaleido-io/paladin/domains/zeto/internal/zeto/common" "github.com/kaleido-io/paladin/domains/zeto/internal/zeto/smt" corepb "github.com/kaleido-io/paladin/domains/zeto/pkg/proto" "github.com/kaleido-io/paladin/domains/zeto/pkg/types" - "github.com/kaleido-io/paladin/domains/zeto/pkg/zetosigner" "github.com/kaleido-io/paladin/domains/zeto/pkg/zetosigner/zetosignerapi" "github.com/kaleido-io/paladin/toolkit/pkg/algorithms" "github.com/kaleido-io/paladin/toolkit/pkg/domain" @@ -145,12 +142,12 @@ func (h *transferHandler) Assemble(ctx context.Context, tx *types.ParsedTransact return nil, i18n.NewError(ctx, msgs.MsgErrorResolveVerifier, tx.Transaction.From) } - useNullifiers := isNullifiersToken(tx.DomainConfig.TokenName) - inputCoins, inputStates, _, remainder, err := h.zeto.prepareInputs(ctx, useNullifiers, req.StateQueryContext, resolvedSender.Verifier, params) + useNullifiers := common.IsNullifiersToken(tx.DomainConfig.TokenName) + inputCoins, inputStates, _, remainder, err := h.zeto.prepareInputsForTransfer(ctx, useNullifiers, req.StateQueryContext, resolvedSender.Verifier, params) if err != nil { return nil, i18n.NewError(ctx, msgs.MsgErrorPrepTxInputs, err) } - outputCoins, outputStates, err := h.zeto.prepareOutputs(ctx, useNullifiers, params, req.ResolvedVerifiers) + outputCoins, outputStates, err := h.zeto.prepareOutputsForTransfer(ctx, useNullifiers, params, req.ResolvedVerifiers) if err != nil { return nil, i18n.NewError(ctx, msgs.MsgErrorPrepTxOutputs, err) } @@ -163,7 +160,7 @@ func (h *transferHandler) Assemble(ctx context.Context, tx *types.ParsedTransact Amount: &remainderHex, }, } - returnedCoins, returnedStates, err := h.zeto.prepareOutputs(ctx, useNullifiers, remainderParams, req.ResolvedVerifiers) + returnedCoins, returnedStates, err := h.zeto.prepareOutputsForTransfer(ctx, useNullifiers, remainderParams, req.ResolvedVerifiers) if err != nil { return nil, i18n.NewError(ctx, msgs.MsgErrorPrepTxChange, err) } @@ -213,19 +210,6 @@ func (h *transferHandler) Endorse(ctx context.Context, tx *types.ParsedTransacti }, nil } -func getTransferABI(tokenName string) *abi.Entry { - transferFunction := transferABI - if isEncryptionToken(tokenName) { - transferFunction = transferABI_withEncryption - if isNullifiersToken(tokenName) { - transferFunction = transferABI_withEncryption_nullifiers - } - } else if isNullifiersToken(tokenName) { - transferFunction = transferABI_nullifiers - } - return transferFunction -} - func (h *transferHandler) Prepare(ctx context.Context, tx *types.ParsedTransaction, req *pb.PrepareTransactionRequest) (*pb.PrepareTransactionResponse, error) { var proofRes corepb.ProvingResponse result := domain.FindAttestation("sender", req.AttestationResult) @@ -236,7 +220,7 @@ func (h *transferHandler) Prepare(ctx context.Context, tx *types.ParsedTransacti return nil, i18n.NewError(ctx, msgs.MsgErrorUnmarshalProvingRes, err) } - inputSize := getInputSize(len(req.InputStates)) + inputSize := common.GetInputSize(len(req.InputStates)) inputs := make([]string, inputSize) for i := 0; i < inputSize; i++ { if i < len(req.InputStates) { @@ -279,16 +263,16 @@ func (h *transferHandler) Prepare(ctx context.Context, tx *types.ParsedTransacti params := map[string]any{ "inputs": inputs, "outputs": outputs, - "proof": h.encodeProof(proofRes.Proof), + "proof": encodeProof(proofRes.Proof), "data": data, } transferFunction := getTransferABI(tx.DomainConfig.TokenName) - if isEncryptionToken(tx.DomainConfig.TokenName) { + if common.IsEncryptionToken(tx.DomainConfig.TokenName) { params["ecdhPublicKey"] = strings.Split(proofRes.PublicInputs["ecdhPublicKey"], ",") params["encryptionNonce"] = proofRes.PublicInputs["encryptionNonce"] params["encryptedValues"] = strings.Split(proofRes.PublicInputs["encryptedValues"], ",") } - if isNullifiersToken(tx.DomainConfig.TokenName) { + if common.IsNullifiersToken(tx.DomainConfig.TokenName) { delete(params, "inputs") params["nullifiers"] = strings.Split(proofRes.PublicInputs["nullifiers"], ",") params["root"] = proofRes.PublicInputs["root"] @@ -311,7 +295,7 @@ func (h *transferHandler) Prepare(ctx context.Context, tx *types.ParsedTransacti } func (h *transferHandler) formatProvingRequest(ctx context.Context, inputCoins, outputCoins []*types.ZetoCoin, circuitId, tokenName, stateQueryContext string, contractAddress *tktypes.EthAddress) ([]byte, error) { - inputSize := getInputSize(len(inputCoins)) + inputSize := common.GetInputSize(len(inputCoins)) inputCommitments := make([]string, inputSize) inputValueInts := make([]uint64, inputSize) inputSalts := make([]string, inputSize) @@ -347,8 +331,8 @@ func (h *transferHandler) formatProvingRequest(ctx context.Context, inputCoins, } var extras []byte - if isNullifiersCircuit(circuitId) { - proofs, extrasObj, err := h.generateMerkleProofs(ctx, tokenName, stateQueryContext, contractAddress, inputCoins) + if common.IsNullifiersCircuit(circuitId) { + proofs, extrasObj, err := generateMerkleProofs(ctx, h.zeto, tokenName, stateQueryContext, contractAddress, inputCoins) if err != nil { return nil, i18n.NewError(ctx, msgs.MsgErrorGenerateMTP, err) } @@ -381,84 +365,15 @@ func (h *transferHandler) formatProvingRequest(ctx context.Context, inputCoins, return proto.Marshal(payload) } -func (h *transferHandler) encodeProof(proof *corepb.SnarkProof) map[string]interface{} { - // Convert the proof json to the format that the Solidity verifier expects - return map[string]interface{}{ - "pA": []string{proof.A[0], proof.A[1]}, - "pB": [][]string{ - {proof.B[0].Items[1], proof.B[0].Items[0]}, - {proof.B[1].Items[1], proof.B[1].Items[0]}, - }, - "pC": []string{proof.C[0], proof.C[1]}, - } -} - -func (h *transferHandler) generateMerkleProofs(ctx context.Context, tokenName string, stateQueryContext string, contractAddress *tktypes.EthAddress, inputCoins []*types.ZetoCoin) ([]core.Proof, *corepb.ProvingRequestExtras_Nullifiers, error) { - smtName := smt.MerkleTreeName(tokenName, contractAddress) - storage := smt.NewStatesStorage(h.zeto.Callbacks, smtName, stateQueryContext, h.zeto.merkleTreeRootSchema.Id, h.zeto.merkleTreeNodeSchema.Id) - mt, err := smt.NewSmt(storage) - if err != nil { - return nil, nil, i18n.NewError(ctx, msgs.MsgErrorNewSmt, smtName, err) - } - // verify that the input UTXOs have been indexed by the Merkle tree DB - // and generate a merkle proof for each - var indexes []*big.Int - for _, coin := range inputCoins { - pubKey, err := zetosigner.DecodeBabyJubJubPublicKey(coin.Owner.String()) - if err != nil { - return nil, nil, i18n.NewError(ctx, msgs.MsgErrorLoadOwnerPubKey, err) - } - idx := node.NewFungible(coin.Amount.Int(), pubKey, coin.Salt.Int()) - leaf, err := node.NewLeafNode(idx) - if err != nil { - return nil, nil, i18n.NewError(ctx, msgs.MsgErrorNewLeafNode, err) - } - n, err := mt.GetNode(leaf.Ref()) - if err != nil { - // TODO: deal with when the node is not found in the DB tables for the tree - // e.g because the transaction event hasn't been processed yet - return nil, nil, i18n.NewError(ctx, msgs.MsgErrorQueryLeafNode, leaf.Ref().Hex(), err) - } - hash, err := coin.Hash(ctx) - if err != nil { - return nil, nil, i18n.NewError(ctx, msgs.MsgErrorHashInputState, err) - } - if n.Index().BigInt().Cmp(hash.Int()) != 0 { - expectedIndex, err := node.NewNodeIndexFromBigInt(hash.Int()) - if err != nil { - return nil, nil, i18n.NewError(ctx, msgs.MsgErrorNewNodeIndex, err) - } - return nil, nil, i18n.NewError(ctx, msgs.MsgErrorHashMismatch, leaf.Ref().Hex(), n.Index().BigInt().Text(16), n.Index().Hex(), hash.HexString0xPrefix(), expectedIndex.Hex()) - } - indexes = append(indexes, n.Index().BigInt()) - } - mtRoot := mt.Root() - proofs, _, err := mt.GenerateProofs(indexes, mtRoot) - if err != nil { - return nil, nil, i18n.NewError(ctx, msgs.MsgErrorGenerateMTP, err) - } - var mps []*corepb.MerkleProof - var enabled []bool - for i, proof := range proofs { - cp, err := proof.ToCircomVerifierProof(indexes[i], indexes[i], mtRoot, smt.SMT_HEIGHT_UTXO) - if err != nil { - return nil, nil, i18n.NewError(ctx, msgs.MsgErrorConvertToCircomProof, err) - } - proofSiblings := make([]string, len(cp.Siblings)-1) - for i, s := range cp.Siblings[0 : len(cp.Siblings)-1] { - proofSiblings[i] = s.BigInt().Text(16) - } - p := corepb.MerkleProof{ - Nodes: proofSiblings, +func getTransferABI(tokenName string) *abi.Entry { + transferFunction := transferABI + if common.IsEncryptionToken(tokenName) { + transferFunction = transferABI_withEncryption + if common.IsNullifiersToken(tokenName) { + transferFunction = transferABI_withEncryption_nullifiers } - mps = append(mps, &p) - enabled = append(enabled, true) - } - extrasObj := corepb.ProvingRequestExtras_Nullifiers{ - Root: mt.Root().BigInt().Text(16), - MerkleProofs: mps, - Enabled: enabled, + } else if common.IsNullifiersToken(tokenName) { + transferFunction = transferABI_nullifiers } - - return proofs, &extrasObj, nil + return transferFunction } diff --git a/domains/zeto/internal/zeto/handler_transfer_test.go b/domains/zeto/internal/zeto/handler_transfer_test.go index 72c3b0790..8453befa7 100644 --- a/domains/zeto/internal/zeto/handler_transfer_test.go +++ b/domains/zeto/internal/zeto/handler_transfer_test.go @@ -420,7 +420,7 @@ func TestGenerateMerkleProofs(t *testing.T) { } ctx := context.Background() queryContext := "queryContext" - _, _, err = h.generateMerkleProofs(ctx, "Zeto_Anon", queryContext, addr, inputCoins) + _, _, err = generateMerkleProofs(ctx, h.zeto, "Zeto_Anon", queryContext, addr, inputCoins) assert.EqualError(t, err, "PD210019: Failed to create Merkle tree for smt_Zeto_Anon_0x1234567890123456789012345678901234567890: PD210065: Failed to find available states for the merkle tree. test error") testCallbacks.returnFunc = func() (*prototk.FindAvailableStatesResponse, error) { @@ -432,11 +432,11 @@ func TestGenerateMerkleProofs(t *testing.T) { }, }, nil } - _, _, err = h.generateMerkleProofs(ctx, "Zeto_Anon", queryContext, addr, inputCoins) + _, _, err = generateMerkleProofs(ctx, h.zeto, "Zeto_Anon", queryContext, addr, inputCoins) assert.EqualError(t, err, "PD210037: Failed load owner public key. PD210072: Invalid compressed public key length: 2") inputCoins[0].Owner = tktypes.MustParseHexBytes("0x7cdd539f3ed6c283494f47d8481f84308a6d7043087fb6711c9f1df04e2b8025") - _, _, err = h.generateMerkleProofs(ctx, "Zeto_Anon", queryContext, addr, inputCoins) + _, _, err = generateMerkleProofs(ctx, h.zeto, "Zeto_Anon", queryContext, addr, inputCoins) assert.EqualError(t, err, "PD210054: Failed to create new leaf node. inputs values not inside Finite Field") inputCoins[0].Salt = tktypes.MustParseHexUint256("0x042fac32983b19d76425cc54dd80e8a198f5d477c6a327cb286eb81a0c2b95ec") @@ -457,7 +457,7 @@ func TestGenerateMerkleProofs(t *testing.T) { }, nil } } - _, _, err = h.generateMerkleProofs(ctx, "Zeto_Anon", queryContext, addr, inputCoins) + _, _, err = generateMerkleProofs(ctx, h.zeto, "Zeto_Anon", queryContext, addr, inputCoins) assert.EqualError(t, err, "PD210055: Failed to query the smt DB for leaf node (ref=789c99b9a2196addb3ac11567135877e8b86bc9b5f7725808a79757fd36b2a2a). key not found") testCallbacks.returnFunc = func() (*prototk.FindAvailableStatesResponse, error) { @@ -480,7 +480,7 @@ func TestGenerateMerkleProofs(t *testing.T) { }, nil } } - _, _, err = h.generateMerkleProofs(ctx, "Zeto_Anon", queryContext, addr, inputCoins) + _, _, err = generateMerkleProofs(ctx, h.zeto, "Zeto_Anon", queryContext, addr, inputCoins) assert.EqualError(t, err, "PD210057: Coin (ref=789c99b9a2196addb3ac11567135877e8b86bc9b5f7725808a79757fd36b2a2a) found in the merkle tree but the persisted hash 26e3879b46b15a4ddbaca5d96af1bd2743f67f13f0bb85c40782950a2a700138 (index=3801702a0a958207c485bbf0137ff64327bdf16ad9a5acdb4d5ab1469b87e326) did not match the expected hash 0x303eb034d22aacc5dff09647928d757017a35e64e696d48609a250a6505e5d5f (index=5f5d5e50a650a20986d496e6645ea31770758d924796f0dfc5ac2ad234b03e30)") testCallbacks.returnFunc = func() (*prototk.FindAvailableStatesResponse, error) { @@ -503,7 +503,7 @@ func TestGenerateMerkleProofs(t *testing.T) { }, nil } } - _, _, err = h.generateMerkleProofs(ctx, "Zeto_Anon", queryContext, addr, inputCoins) + _, _, err = generateMerkleProofs(ctx, h.zeto, "Zeto_Anon", queryContext, addr, inputCoins) assert.NoError(t, err) } diff --git a/domains/zeto/internal/zeto/handler_withdraw.go b/domains/zeto/internal/zeto/handler_withdraw.go new file mode 100644 index 000000000..c6134f50b --- /dev/null +++ b/domains/zeto/internal/zeto/handler_withdraw.go @@ -0,0 +1,317 @@ +/* + * Copyright © 2024 Kaleido, Inc. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package zeto + +import ( + "context" + "encoding/json" + "strings" + + "github.com/hyperledger/firefly-common/pkg/i18n" + "github.com/hyperledger/firefly-signer/pkg/abi" + "github.com/kaleido-io/paladin/domains/zeto/internal/msgs" + "github.com/kaleido-io/paladin/domains/zeto/internal/zeto/common" + "github.com/kaleido-io/paladin/domains/zeto/internal/zeto/smt" + "github.com/kaleido-io/paladin/domains/zeto/pkg/constants" + corepb "github.com/kaleido-io/paladin/domains/zeto/pkg/proto" + "github.com/kaleido-io/paladin/domains/zeto/pkg/types" + "github.com/kaleido-io/paladin/domains/zeto/pkg/zetosigner/zetosignerapi" + "github.com/kaleido-io/paladin/toolkit/pkg/algorithms" + "github.com/kaleido-io/paladin/toolkit/pkg/domain" + pb "github.com/kaleido-io/paladin/toolkit/pkg/prototk" + "github.com/kaleido-io/paladin/toolkit/pkg/tktypes" + "github.com/kaleido-io/paladin/toolkit/pkg/verifiers" + "google.golang.org/protobuf/proto" +) + +type withdrawHandler struct { + zeto *Zeto +} + +var withdrawABI = &abi.Entry{ + Type: abi.Function, + Name: "withdraw", + Inputs: abi.ParameterArray{ + {Name: "amount", Type: "uint256"}, + {Name: "inputs", Type: "uint256[]"}, + {Name: "output", Type: "uint256"}, + {Name: "proof", Type: "tuple", InternalType: "struct Commonlib.Proof", Components: proofComponents}, + {Name: "data", Type: "bytes"}, + }, +} + +var withdrawABI_nullifiers = &abi.Entry{ + Type: abi.Function, + Name: "withdraw", + Inputs: abi.ParameterArray{ + {Name: "amount", Type: "uint256"}, + {Name: "nullifiers", Type: "uint256[]"}, + {Name: "output", Type: "uint256"}, + {Name: "root", Type: "uint256"}, + {Name: "proof", Type: "tuple", InternalType: "struct Commonlib.Proof", Components: proofComponents}, + {Name: "data", Type: "bytes"}, + }, +} + +func (h *withdrawHandler) ValidateParams(ctx context.Context, config *types.DomainInstanceConfig, params string) (interface{}, error) { + var withdrawParams types.WithdrawParams + if err := json.Unmarshal([]byte(params), &withdrawParams); err != nil { + return nil, err + } + + if err := validateAmountParam(ctx, withdrawParams.Amount, 0); err != nil { + return nil, err + } + + return withdrawParams.Amount, nil +} + +func (h *withdrawHandler) Init(ctx context.Context, tx *types.ParsedTransaction, req *pb.InitTransactionRequest) (*pb.InitTransactionResponse, error) { + res := &pb.InitTransactionResponse{ + RequiredVerifiers: []*pb.ResolveVerifierRequest{ + { + Lookup: tx.Transaction.From, + Algorithm: h.zeto.getAlgoZetoSnarkBJJ(), + VerifierType: zetosignerapi.IDEN3_PUBKEY_BABYJUBJUB_COMPRESSED_0X, + }, + }, + } + return res, nil +} + +func (h *withdrawHandler) Assemble(ctx context.Context, tx *types.ParsedTransaction, req *pb.AssembleTransactionRequest) (*pb.AssembleTransactionResponse, error) { + amount := tx.Params.(*tktypes.HexUint256) + + resolvedSender := domain.FindVerifier(tx.Transaction.From, h.zeto.getAlgoZetoSnarkBJJ(), zetosignerapi.IDEN3_PUBKEY_BABYJUBJUB_COMPRESSED_0X, req.ResolvedVerifiers) + if resolvedSender == nil { + return nil, i18n.NewError(ctx, msgs.MsgErrorResolveVerifier, tx.Transaction.From) + } + + useNullifiers := common.IsNullifiersToken(tx.DomainConfig.TokenName) + inputCoins, inputStates, _, remainder, err := h.zeto.prepareInputsForWithdraw(ctx, useNullifiers, req.StateQueryContext, resolvedSender.Verifier, amount) + if err != nil { + return nil, i18n.NewError(ctx, msgs.MsgErrorPrepTxInputs, err) + } + + outputCoin, outputState, err := h.zeto.prepareOutputForWithdraw(ctx, tktypes.MustParseHexUint256(remainder.Text(10)), req.ResolvedVerifiers) + if err != nil { + return nil, i18n.NewError(ctx, msgs.MsgErrorPrepTxOutputs, err) + } + + contractAddress, err := tktypes.ParseEthAddress(req.Transaction.ContractInfo.ContractAddress) + if err != nil { + return nil, i18n.NewError(ctx, msgs.MsgErrorDecodeContractAddress, err) + } + payloadBytes, err := h.formatProvingRequest(ctx, inputCoins, outputCoin, tx.DomainConfig.CircuitId, tx.DomainConfig.TokenName, req.StateQueryContext, contractAddress) + if err != nil { + return nil, i18n.NewError(ctx, msgs.MsgErrorFormatProvingReq, err) + } + + amountStr := amount.Int().Text(10) + return &pb.AssembleTransactionResponse{ + AssemblyResult: pb.AssembleTransactionResponse_OK, + AssembledTransaction: &pb.AssembledTransaction{ + InputStates: inputStates, + OutputStates: []*pb.NewState{outputState}, + DomainData: &amountStr, + }, + AttestationPlan: []*pb.AttestationRequest{ + { + Name: "sender", + AttestationType: pb.AttestationType_SIGN, + Algorithm: h.zeto.getAlgoZetoSnarkBJJ(), + VerifierType: zetosignerapi.IDEN3_PUBKEY_BABYJUBJUB_COMPRESSED_0X, + PayloadType: zetosignerapi.PAYLOAD_DOMAIN_ZETO_SNARK, + Payload: payloadBytes, + Parties: []string{tx.Transaction.From}, + }, + { + Name: "submitter", + AttestationType: pb.AttestationType_ENDORSE, + Algorithm: algorithms.ECDSA_SECP256K1, + VerifierType: verifiers.ETH_ADDRESS, + Parties: []string{tx.Transaction.From}, + }, + }, + }, nil +} + +func (h *withdrawHandler) Endorse(ctx context.Context, tx *types.ParsedTransaction, req *pb.EndorseTransactionRequest) (*pb.EndorseTransactionResponse, error) { + return &pb.EndorseTransactionResponse{ + EndorsementResult: pb.EndorseTransactionResponse_ENDORSER_SUBMIT, + }, nil +} + +func (h *withdrawHandler) Prepare(ctx context.Context, tx *types.ParsedTransaction, req *pb.PrepareTransactionRequest) (*pb.PrepareTransactionResponse, error) { + var proofRes corepb.ProvingResponse + result := domain.FindAttestation("sender", req.AttestationResult) + if result == nil { + return nil, i18n.NewError(ctx, msgs.MsgErrorFindSenderAttestation) + } + if err := proto.Unmarshal(result.Payload, &proofRes); err != nil { + return nil, i18n.NewError(ctx, msgs.MsgErrorUnmarshalProvingRes, err) + } + + inputSize := common.GetInputSize(len(req.InputStates)) + inputs := make([]string, inputSize) + for i := 0; i < inputSize; i++ { + if i < len(req.InputStates) { + state := req.InputStates[i] + coin, err := h.zeto.makeCoin(state.StateDataJson) + if err != nil { + return nil, i18n.NewError(ctx, msgs.MsgErrorParseInputStates, err) + } + hash, err := coin.Hash(ctx) + if err != nil { + return nil, i18n.NewError(ctx, msgs.MsgErrorHashInputState, err) + } + inputs[i] = hash.String() + } else { + inputs[i] = "0" + } + } + + outputCoin, err := h.zeto.makeCoin(req.OutputStates[0].StateDataJson) + if err != nil { + return nil, i18n.NewError(ctx, msgs.MsgErrorParseOutputStates, err) + } + hash, err := outputCoin.Hash(ctx) + if err != nil { + return nil, i18n.NewError(ctx, msgs.MsgErrorHashOutputState, err) + } + output := hash.String() + + data, err := encodeTransactionData(ctx, req.Transaction) + if err != nil { + return nil, i18n.NewError(ctx, msgs.MsgErrorEncodeTxData, err) + } + amount := tktypes.MustParseHexUint256(*req.DomainData) + params := map[string]any{ + "amount": amount.Int().Text(10), + "inputs": inputs, + "output": output, + "proof": encodeProof(proofRes.Proof), + "data": data, + } + if common.IsNullifiersToken(tx.DomainConfig.TokenName) { + delete(params, "inputs") + params["nullifiers"] = strings.Split(proofRes.PublicInputs["nullifiers"], ",") + params["root"] = proofRes.PublicInputs["root"] + } + withdrawFunction := getWithdrawABI(tx.DomainConfig.TokenName) + paramsJSON, err := json.Marshal(params) + if err != nil { + return nil, i18n.NewError(ctx, msgs.MsgErrorMarshalPrepedParams, err) + } + functionJSON, err := json.Marshal(withdrawFunction) + if err != nil { + return nil, err + } + + return &pb.PrepareTransactionResponse{ + Transaction: &pb.PreparedTransaction{ + FunctionAbiJson: string(functionJSON), + ParamsJson: string(paramsJSON), + }, + }, nil +} + +func (h *withdrawHandler) formatProvingRequest(ctx context.Context, inputCoins []*types.ZetoCoin, outputCoin *types.ZetoCoin, circuitId, tokenName, stateQueryContext string, contractAddress *tktypes.EthAddress) ([]byte, error) { + inputSize := common.GetInputSize(len(inputCoins)) + inputCommitments := make([]string, inputSize) + inputValueInts := make([]uint64, inputSize) + inputSalts := make([]string, inputSize) + inputOwner := inputCoins[0].Owner.String() + for i := 0; i < inputSize; i++ { + if i < len(inputCoins) { + coin := inputCoins[i] + hash, err := coin.Hash(ctx) + if err != nil { + return nil, i18n.NewError(ctx, msgs.MsgErrorHashInputState, err) + } + inputCommitments[i] = hash.Int().Text(16) + inputValueInts[i] = coin.Amount.Int().Uint64() + inputSalts[i] = coin.Salt.Int().Text(16) + } else { + inputCommitments[i] = "0" + inputSalts[i] = "0" + } + } + + hash, err := outputCoin.Hash(ctx) + if err != nil { + return nil, i18n.NewError(ctx, msgs.MsgErrorHashInputState, err) + } + outputCommitment := hash.Int().Text(16) + outputValueInt := outputCoin.Amount.Int().Uint64() + outputSalt := outputCoin.Salt.Int().Text(16) + outputOwner := outputCoin.Owner.String() + + var extras []byte + if common.IsNullifiersCircuit(circuitId) { + proofs, extrasObj, err := generateMerkleProofs(ctx, h.zeto, tokenName, stateQueryContext, contractAddress, inputCoins) + if err != nil { + return nil, i18n.NewError(ctx, msgs.MsgErrorGenerateMTP, err) + } + for i := len(proofs); i < inputSize; i++ { + extrasObj.MerkleProofs = append(extrasObj.MerkleProofs, &smt.Empty_Proof) + extrasObj.Enabled = append(extrasObj.Enabled, false) + } + protoExtras, err := proto.Marshal(extrasObj) + if err != nil { + return nil, i18n.NewError(ctx, msgs.MsgErrorMarshalExtraObj, err) + } + extras = protoExtras + } + + payload := &corepb.ProvingRequest{ + CircuitId: getCircuitId(tokenName), + Common: &corepb.ProvingRequestCommon{ + InputCommitments: inputCommitments, + InputValues: inputValueInts, + InputSalts: inputSalts, + InputOwner: inputOwner, + OutputCommitments: []string{outputCommitment}, + OutputValues: []uint64{outputValueInt}, + OutputSalts: []string{outputSalt}, + OutputOwners: []string{outputOwner}, + }, + } + + if extras != nil { + payload.Extras = extras + } + + return proto.Marshal(payload) +} + +func getCircuitId(tokenName string) string { + isNullifier := common.IsNullifiersToken(tokenName) + + if isNullifier { + return constants.CIRCUIT_WITHDRAW_NULLIFIER + } else { + return constants.CIRCUIT_WITHDRAW + } +} + +func getWithdrawABI(tokenName string) *abi.Entry { + withdrawFunction := withdrawABI + if common.IsNullifiersToken(tokenName) { + withdrawFunction = withdrawABI_nullifiers + } + return withdrawFunction +} diff --git a/domains/zeto/internal/zeto/handlers.go b/domains/zeto/internal/zeto/handlers.go deleted file mode 100644 index 791756b97..000000000 --- a/domains/zeto/internal/zeto/handlers.go +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright © 2024 Kaleido, Inc. - * - * 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. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -package zeto - -import "github.com/kaleido-io/paladin/domains/zeto/pkg/types" - -func (z *Zeto) GetHandler(method string) types.DomainHandler { - switch method { - case "mint": - return &mintHandler{zeto: z} - case "transfer": - return &transferHandler{zeto: z} - case "lockProof": - return &lockHandler{zeto: z} - default: - return nil - } -} diff --git a/domains/zeto/internal/zeto/handlers_test.go b/domains/zeto/internal/zeto/handlers_test.go deleted file mode 100644 index 2fc7f6e93..000000000 --- a/domains/zeto/internal/zeto/handlers_test.go +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright © 2024 Kaleido, Inc. - * - * 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. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -package zeto - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestGetHandler(t *testing.T) { - z := &Zeto{ - name: "test1", - } - assert.NotNil(t, z.GetHandler("mint")) - assert.NotNil(t, z.GetHandler("transfer")) - assert.NotNil(t, z.GetHandler("lockProof")) - assert.Nil(t, z.GetHandler("bad")) -} diff --git a/domains/zeto/internal/zeto/signer/decoder.go b/domains/zeto/internal/zeto/signer/decoder.go index a0bebb5aa..9ae3b30bc 100644 --- a/domains/zeto/internal/zeto/signer/decoder.go +++ b/domains/zeto/internal/zeto/signer/decoder.go @@ -20,7 +20,7 @@ import ( "github.com/hyperledger/firefly-common/pkg/i18n" "github.com/kaleido-io/paladin/domains/zeto/internal/msgs" - "github.com/kaleido-io/paladin/domains/zeto/pkg/constants" + "github.com/kaleido-io/paladin/domains/zeto/internal/zeto/common" pb "github.com/kaleido-io/paladin/domains/zeto/pkg/proto" "google.golang.org/protobuf/proto" ) @@ -32,7 +32,7 @@ func decodeProvingRequest(ctx context.Context, payload []byte) (*pb.ProvingReque if err != nil { return nil, nil, err } - if inputs.CircuitId == constants.CIRCUIT_ANON_ENC { + if common.IsEncryptionCircuit(inputs.CircuitId) { encExtras := pb.ProvingRequestExtras_Encryption{ EncryptionNonce: "", } @@ -43,7 +43,7 @@ func decodeProvingRequest(ctx context.Context, payload []byte) (*pb.ProvingReque } } return &inputs, &encExtras, nil - } else if inputs.CircuitId == constants.CIRCUIT_ANON_NULLIFIER { + } else if common.IsNullifiersCircuit(inputs.CircuitId) { var nullifierExtras pb.ProvingRequestExtras_Nullifiers err := proto.Unmarshal(inputs.Extras, &nullifierExtras) if err != nil { diff --git a/domains/zeto/internal/zeto/signer/inputs_assembler.go b/domains/zeto/internal/zeto/signer/inputs_assembler.go index 367f0664f..20619e60c 100644 --- a/domains/zeto/internal/zeto/signer/inputs_assembler.go +++ b/domains/zeto/internal/zeto/signer/inputs_assembler.go @@ -78,6 +78,76 @@ func assembleInputs_anon_enc(ctx context.Context, inputs *commonWitnessInputs, e } func assembleInputs_anon_nullifier(ctx context.Context, inputs *commonWitnessInputs, extras *pb.ProvingRequestExtras_Nullifiers, keyEntry *core.KeyEntry) (map[string]any, error) { + nullifiers, root, proofs, enabled, err := prepareInputsForNullifiers(ctx, inputs, extras, keyEntry) + if err != nil { + return nil, err + } + + witnessInputs := map[string]interface{}{ + "nullifiers": nullifiers, + "root": root, + "merkleProof": proofs, + "enabled": enabled, + "inputCommitments": inputs.inputCommitments, + "inputValues": inputs.inputValues, + "inputSalts": inputs.inputSalts, + "inputOwnerPrivateKey": keyEntry.PrivateKeyForZkp, + "outputCommitments": inputs.outputCommitments, + "outputValues": inputs.outputValues, + "outputSalts": inputs.outputSalts, + "outputOwnerPublicKeys": inputs.outputOwnerPublicKeys, + } + return witnessInputs, nil +} + +func assembleInputs_deposit(inputs *commonWitnessInputs) map[string]interface{} { + witnessInputs := map[string]interface{}{ + "outputCommitments": inputs.outputCommitments, + "outputValues": inputs.outputValues, + "outputSalts": inputs.outputSalts, + "outputOwnerPublicKeys": inputs.outputOwnerPublicKeys, + } + return witnessInputs +} + +func assembleInputs_withdraw(inputs *commonWitnessInputs, keyEntry *core.KeyEntry) map[string]interface{} { + witnessInputs := map[string]interface{}{ + "inputCommitments": inputs.inputCommitments, + "inputValues": inputs.inputValues, + "inputSalts": inputs.inputSalts, + "inputOwnerPrivateKey": keyEntry.PrivateKeyForZkp, + "outputCommitments": inputs.outputCommitments, + "outputValues": inputs.outputValues, + "outputSalts": inputs.outputSalts, + "outputOwnerPublicKeys": inputs.outputOwnerPublicKeys, + } + return witnessInputs +} + +func assembleInputs_withdraw_nullifier(ctx context.Context, inputs *commonWitnessInputs, extras *pb.ProvingRequestExtras_Nullifiers, keyEntry *core.KeyEntry) (map[string]interface{}, error) { + nullifiers, root, proofs, enabled, err := prepareInputsForNullifiers(ctx, inputs, extras, keyEntry) + if err != nil { + return nil, err + } + + witnessInputs := map[string]interface{}{ + "nullifiers": nullifiers, + "root": root, + "merkleProof": proofs, + "enabled": enabled, + "inputCommitments": inputs.inputCommitments, + "inputValues": inputs.inputValues, + "inputSalts": inputs.inputSalts, + "inputOwnerPrivateKey": keyEntry.PrivateKeyForZkp, + "outputCommitments": inputs.outputCommitments, + "outputValues": inputs.outputValues, + "outputSalts": inputs.outputSalts, + "outputOwnerPublicKeys": inputs.outputOwnerPublicKeys, + } + return witnessInputs, nil +} + +func prepareInputsForNullifiers(ctx context.Context, inputs *commonWitnessInputs, extras *pb.ProvingRequestExtras_Nullifiers, keyEntry *core.KeyEntry) ([]*big.Int, *big.Int, [][]*big.Int, []*big.Int, error) { // calculate the nullifiers for the input UTXOs nullifiers := make([]*big.Int, len(inputs.inputCommitments)) for i := 0; i < len(inputs.inputCommitments); i++ { @@ -88,13 +158,13 @@ func assembleInputs_anon_nullifier(ctx context.Context, inputs *commonWitnessInp } nullifier, err := CalculateNullifier(inputs.inputValues[i], inputs.inputSalts[i], keyEntry.PrivateKeyForZkp) if err != nil { - return nil, i18n.NewError(ctx, msgs.MsgErrorCalcNullifier, err) + return nil, nil, nil, nil, i18n.NewError(ctx, msgs.MsgErrorCalcNullifier, err) } nullifiers[i] = nullifier } root, ok := new(big.Int).SetString(extras.Root, 16) if !ok { - return nil, i18n.NewError(ctx, msgs.MsgErrorDecodeRootExtras) + return nil, nil, nil, nil, i18n.NewError(ctx, msgs.MsgErrorDecodeRootExtras) } var proofs [][]*big.Int for _, proof := range extras.MerkleProofs { @@ -102,7 +172,7 @@ func assembleInputs_anon_nullifier(ctx context.Context, inputs *commonWitnessInp for _, node := range proof.Nodes { n, ok := new(big.Int).SetString(node, 16) if !ok { - return nil, i18n.NewError(ctx, msgs.MsgErrorDecodeMTPNodeExtras) + return nil, nil, nil, nil, i18n.NewError(ctx, msgs.MsgErrorDecodeMTPNodeExtras) } mp = append(mp, n) } @@ -117,19 +187,5 @@ func assembleInputs_anon_nullifier(ctx context.Context, inputs *commonWitnessInp } } - witnessInputs := map[string]interface{}{ - "nullifiers": nullifiers, - "root": root, - "merkleProof": proofs, - "enabled": enabled, - "inputCommitments": inputs.inputCommitments, - "inputValues": inputs.inputValues, - "inputSalts": inputs.inputSalts, - "inputOwnerPrivateKey": keyEntry.PrivateKeyForZkp, - "outputCommitments": inputs.outputCommitments, - "outputValues": inputs.outputValues, - "outputSalts": inputs.outputSalts, - "outputOwnerPublicKeys": inputs.outputOwnerPublicKeys, - } - return witnessInputs, nil + return nullifiers, root, proofs, enabled, nil } diff --git a/domains/zeto/internal/zeto/signer/snark_prover.go b/domains/zeto/internal/zeto/signer/snark_prover.go index fc3c9ccae..bcb788fd5 100644 --- a/domains/zeto/internal/zeto/signer/snark_prover.go +++ b/domains/zeto/internal/zeto/signer/snark_prover.go @@ -205,15 +205,6 @@ func getCircuitId(inputs *pb.ProvingRequest) string { } func validateInputs(ctx context.Context, inputs *pb.ProvingRequestCommon) error { - if len(inputs.InputCommitments) == 0 { - return i18n.NewError(ctx, msgs.MsgErrorMissingInputCommitments) - } - if len(inputs.InputValues) == 0 { - return i18n.NewError(ctx, msgs.MsgErrorMissingInputValues) - } - if len(inputs.InputSalts) == 0 { - return i18n.NewError(ctx, msgs.MsgErrorMissingInputSalts) - } if len(inputs.InputCommitments) != len(inputs.InputValues) || len(inputs.InputCommitments) != len(inputs.InputSalts) { return i18n.NewError(ctx, msgs.MsgErrorInputsDiffLength) } @@ -256,6 +247,12 @@ func serializeProofResponse(circuitId string, proof *types.ZKProof) ([]byte, err case constants.CIRCUIT_ANON_NULLIFIER_BATCH: publicInputs["nullifiers"] = strings.Join(proof.PubSignals[:10], ",") publicInputs["root"] = proof.PubSignals[10] + case constants.CIRCUIT_WITHDRAW_NULLIFIER: + publicInputs["nullifiers"] = strings.Join(proof.PubSignals[1:3], ",") + publicInputs["root"] = proof.PubSignals[3] + case constants.CIRCUIT_WITHDRAW_NULLIFIER_BATCH: + publicInputs["nullifiers"] = strings.Join(proof.PubSignals[1:11], ",") + publicInputs["root"] = proof.PubSignals[11] } res := pb.ProvingResponse{ @@ -286,6 +283,15 @@ func calculateWitness(ctx context.Context, circuitId string, commonInputs *pb.Pr if err != nil { return nil, i18n.NewError(ctx, msgs.MsgErrorAssembleInputs, err) } + case constants.CIRCUIT_DEPOSIT: + witnessInputs = assembleInputs_deposit(inputs) + case constants.CIRCUIT_WITHDRAW, constants.CIRCUIT_WITHDRAW_BATCH: + witnessInputs = assembleInputs_withdraw(inputs, keyEntry) + case constants.CIRCUIT_WITHDRAW_NULLIFIER, constants.CIRCUIT_WITHDRAW_NULLIFIER_BATCH: + witnessInputs, err = assembleInputs_withdraw_nullifier(ctx, inputs, extras.(*pb.ProvingRequestExtras_Nullifiers), keyEntry) + if err != nil { + return nil, i18n.NewError(ctx, msgs.MsgErrorAssembleInputs, err) + } } wtns, err := circuit.CalculateWTNSBin(witnessInputs, true) diff --git a/domains/zeto/internal/zeto/signer/snark_prover_test.go b/domains/zeto/internal/zeto/signer/snark_prover_test.go index cdc8af77f..70562b038 100644 --- a/domains/zeto/internal/zeto/signer/snark_prover_test.go +++ b/domains/zeto/internal/zeto/signer/snark_prover_test.go @@ -265,42 +265,6 @@ func TestSnarkProveErrorInputs(t *testing.T) { payload, err := proto.Marshal(&req) require.NoError(t, err) _, err = prover.Sign(context.Background(), zetosignerapi.AlgoDomainZetoSnarkBJJ("zeto"), zetosignerapi.PAYLOAD_DOMAIN_ZETO_SNARK, alice.PrivateKey[:], payload) - assert.ErrorContains(t, err, "input commitments are required") - - req = pb.ProvingRequest{ - CircuitId: constants.CIRCUIT_ANON, - Common: &pb.ProvingRequestCommon{ - InputCommitments: []string{"input1", "input2"}, - }, - } - payload, err = proto.Marshal(&req) - require.NoError(t, err) - _, err = prover.Sign(context.Background(), zetosignerapi.AlgoDomainZetoSnarkBJJ("zeto"), zetosignerapi.PAYLOAD_DOMAIN_ZETO_SNARK, alice.PrivateKey[:], payload) - assert.ErrorContains(t, err, "input values are required") - - req = pb.ProvingRequest{ - CircuitId: constants.CIRCUIT_ANON, - Common: &pb.ProvingRequestCommon{ - InputCommitments: []string{"input1", "input2"}, - InputValues: []uint64{30, 40}, - }, - } - payload, err = proto.Marshal(&req) - require.NoError(t, err) - _, err = prover.Sign(context.Background(), zetosignerapi.AlgoDomainZetoSnarkBJJ("zeto"), zetosignerapi.PAYLOAD_DOMAIN_ZETO_SNARK, alice.PrivateKey[:], payload) - assert.ErrorContains(t, err, "input salts are required") - - req = pb.ProvingRequest{ - CircuitId: constants.CIRCUIT_ANON, - Common: &pb.ProvingRequestCommon{ - InputCommitments: []string{"input1", "input2"}, - InputValues: []uint64{30, 40}, - InputSalts: []string{"salt1", "salt2"}, - }, - } - payload, err = proto.Marshal(&req) - require.NoError(t, err) - _, err = prover.Sign(context.Background(), zetosignerapi.AlgoDomainZetoSnarkBJJ("zeto"), zetosignerapi.PAYLOAD_DOMAIN_ZETO_SNARK, alice.PrivateKey[:], payload) assert.ErrorContains(t, err, "output values are required") req = pb.ProvingRequest{ diff --git a/domains/zeto/internal/zeto/states.go b/domains/zeto/internal/zeto/states.go index 5e724653e..dc7635e25 100644 --- a/domains/zeto/internal/zeto/states.go +++ b/domains/zeto/internal/zeto/states.go @@ -19,6 +19,7 @@ import ( "context" "encoding/json" "math/big" + "math/rand/v2" "github.com/hyperledger-labs/zeto/go-sdk/pkg/crypto" "github.com/hyperledger/firefly-common/pkg/i18n" @@ -94,17 +95,25 @@ func (z *Zeto) makeNewState(ctx context.Context, useNullifiers bool, coin *types return newState, nil } -func (z *Zeto) prepareInputs(ctx context.Context, useNullifiers bool, stateQueryContext, senderKey string, params []*types.TransferParamEntry) ([]*types.ZetoCoin, []*pb.StateRef, *big.Int, *big.Int, error) { - var lastStateTimestamp int64 - total := big.NewInt(0) - stateRefs := []*pb.StateRef{} - coins := []*types.ZetoCoin{} - +func (z *Zeto) prepareInputsForTransfer(ctx context.Context, useNullifiers bool, stateQueryContext, senderKey string, params []*types.TransferParamEntry) ([]*types.ZetoCoin, []*pb.StateRef, *big.Int, *big.Int, error) { expectedTotal := big.NewInt(0) for _, param := range params { expectedTotal = expectedTotal.Add(expectedTotal, param.Amount.Int()) } + return z.buildInputsForExpectedTotal(ctx, useNullifiers, stateQueryContext, senderKey, expectedTotal) +} + +func (z *Zeto) prepareInputsForWithdraw(ctx context.Context, useNullifiers bool, stateQueryContext, senderKey string, amount *tktypes.HexUint256) ([]*types.ZetoCoin, []*pb.StateRef, *big.Int, *big.Int, error) { + expectedTotal := amount.Int() + return z.buildInputsForExpectedTotal(ctx, useNullifiers, stateQueryContext, senderKey, expectedTotal) +} + +func (z *Zeto) buildInputsForExpectedTotal(ctx context.Context, useNullifiers bool, stateQueryContext, senderKey string, expectedTotal *big.Int) ([]*types.ZetoCoin, []*pb.StateRef, *big.Int, *big.Int, error) { + var lastStateTimestamp int64 + total := big.NewInt(0) + stateRefs := []*pb.StateRef{} + coins := []*types.ZetoCoin{} for { queryBuilder := query.NewQueryBuilder(). Limit(10). @@ -144,7 +153,7 @@ func (z *Zeto) prepareInputs(ctx context.Context, useNullifiers bool, stateQuery } } -func (z *Zeto) prepareOutputs(ctx context.Context, useNullifiers bool, params []*types.TransferParamEntry, resolvedVerifiers []*pb.ResolvedVerifier) ([]*types.ZetoCoin, []*pb.NewState, error) { +func (z *Zeto) prepareOutputsForTransfer(ctx context.Context, useNullifiers bool, params []*types.TransferParamEntry, resolvedVerifiers []*pb.ResolvedVerifier) ([]*types.ZetoCoin, []*pb.NewState, error) { var coins []*types.ZetoCoin var newStates []*pb.NewState for _, param := range params { @@ -175,6 +184,64 @@ func (z *Zeto) prepareOutputs(ctx context.Context, useNullifiers bool, params [] return coins, newStates, nil } +func (z *Zeto) prepareOutputsForDeposit(ctx context.Context, useNullifiers bool, amount *tktypes.HexUint256, resolvedVerifiers []*pb.ResolvedVerifier) ([]*types.ZetoCoin, []*pb.NewState, error) { + var coins []*types.ZetoCoin + // the token implementation allows up to 2 output states, we will use one of them + // to bear the deposit amount, and set the other to value of 0. we randomize + // which one to use and which one to set to 0 + var newStates []*pb.NewState + amounts := make([]*tktypes.HexUint256, 2) + size := 2 + randomIdx := randomSlot(size) + amounts[randomIdx] = amount + amounts[size-randomIdx-1] = tktypes.MustParseHexUint256("0x0") + for _, amt := range amounts { + resolvedRecipient := resolvedVerifiers[0] + recipientKey, err := loadBabyJubKey([]byte(resolvedRecipient.Verifier)) + if err != nil { + return nil, nil, i18n.NewError(ctx, msgs.MsgErrorLoadOwnerPubKey, err) + } + + salt := crypto.NewSalt() + compressedKeyStr := zetosigner.EncodeBabyJubJubPublicKey(recipientKey) + newCoin := &types.ZetoCoin{ + Salt: (*tktypes.HexUint256)(salt), + Owner: tktypes.MustParseHexBytes(compressedKeyStr), + Amount: amt, + } + + newState, err := z.makeNewState(ctx, useNullifiers, newCoin, resolvedRecipient.Lookup) + if err != nil { + return nil, nil, i18n.NewError(ctx, msgs.MsgErrorCreateNewState, err) + } + coins = append(coins, newCoin) + newStates = append(newStates, newState) + } + return coins, newStates, nil +} + +func (z *Zeto) prepareOutputForWithdraw(ctx context.Context, amount *tktypes.HexUint256, resolvedVerifiers []*pb.ResolvedVerifier) (*types.ZetoCoin, *pb.NewState, error) { + resolvedRecipient := resolvedVerifiers[0] + recipientKey, err := loadBabyJubKey([]byte(resolvedRecipient.Verifier)) + if err != nil { + return nil, nil, i18n.NewError(ctx, msgs.MsgErrorLoadOwnerPubKey, err) + } + + salt := crypto.NewSalt() + compressedKeyStr := zetosigner.EncodeBabyJubJubPublicKey(recipientKey) + newCoin := &types.ZetoCoin{ + Salt: (*tktypes.HexUint256)(salt), + Owner: tktypes.MustParseHexBytes(compressedKeyStr), + Amount: amount, + } + + newState, err := z.makeNewState(ctx, false, newCoin, resolvedRecipient.Lookup) + if err != nil { + return nil, nil, i18n.NewError(ctx, msgs.MsgErrorCreateNewState, err) + } + return newCoin, newState, nil +} + func (z *Zeto) findAvailableStates(ctx context.Context, useNullifiers bool, stateQueryContext, query string) ([]*pb.StoredState, error) { req := &pb.FindAvailableStatesRequest{ StateQueryContext: stateQueryContext, @@ -188,3 +255,7 @@ func (z *Zeto) findAvailableStates(ctx context.Context, useNullifiers bool, stat } return res.States, nil } + +func randomSlot(size int) int { + return rand.IntN(size) +} diff --git a/domains/zeto/internal/zeto/states_test.go b/domains/zeto/internal/zeto/states_test.go index 00bd4f497..5370aa4ca 100644 --- a/domains/zeto/internal/zeto/states_test.go +++ b/domains/zeto/internal/zeto/states_test.go @@ -54,13 +54,13 @@ func TestPrepareInputs(t *testing.T) { stateQueryContext := "test" ctx := context.Background() - _, _, _, _, err := zeto.prepareInputs(ctx, false, stateQueryContext, "Alice", []*types.TransferParamEntry{{Amount: tktypes.Uint64ToUint256(100)}}) + _, _, _, _, err := zeto.prepareInputsForTransfer(ctx, false, stateQueryContext, "Alice", []*types.TransferParamEntry{{Amount: tktypes.Uint64ToUint256(100)}}) assert.EqualError(t, err, "PD210032: Failed to query the state store for available coins. test error") testCallbacks.returnFunc = func() (*prototk.FindAvailableStatesResponse, error) { return &prototk.FindAvailableStatesResponse{}, nil } - _, _, _, _, err = zeto.prepareInputs(ctx, false, stateQueryContext, "Alice", []*types.TransferParamEntry{{Amount: tktypes.Uint64ToUint256(100)}}) + _, _, _, _, err = zeto.prepareInputsForTransfer(ctx, false, stateQueryContext, "Alice", []*types.TransferParamEntry{{Amount: tktypes.Uint64ToUint256(100)}}) assert.EqualError(t, err, "PD210033: Insufficient funds (available=0)") testCallbacks.returnFunc = func() (*prototk.FindAvailableStatesResponse, error) { @@ -73,7 +73,7 @@ func TestPrepareInputs(t *testing.T) { }, }, nil } - _, _, _, _, err = zeto.prepareInputs(ctx, false, stateQueryContext, "Alice", []*types.TransferParamEntry{{Amount: tktypes.Uint64ToUint256(100)}}) + _, _, _, _, err = zeto.prepareInputsForTransfer(ctx, false, stateQueryContext, "Alice", []*types.TransferParamEntry{{Amount: tktypes.Uint64ToUint256(100)}}) assert.EqualError(t, err, "PD210034: Coin state-1 is invalid: invalid character 'b' looking for beginning of value") testCallbacks.returnFunc = func() (*prototk.FindAvailableStatesResponse, error) { @@ -92,6 +92,6 @@ func TestPrepareInputs(t *testing.T) { }, }, nil } - _, _, _, _, err = zeto.prepareInputs(ctx, false, stateQueryContext, "Alice", []*types.TransferParamEntry{{Amount: tktypes.Uint64ToUint256(200)}}) + _, _, _, _, err = zeto.prepareInputsForTransfer(ctx, false, stateQueryContext, "Alice", []*types.TransferParamEntry{{Amount: tktypes.Uint64ToUint256(200)}}) assert.EqualError(t, err, "PD210035: Need more than maximum number (10) of coins to fulfill the transfer amount total") } diff --git a/domains/zeto/internal/zeto/utils.go b/domains/zeto/internal/zeto/utils.go index e143775c3..b642efe93 100644 --- a/domains/zeto/internal/zeto/utils.go +++ b/domains/zeto/internal/zeto/utils.go @@ -17,46 +17,21 @@ package zeto import ( "context" + "math/big" + "github.com/hyperledger-labs/zeto/go-sdk/pkg/sparse-merkle-tree/core" + "github.com/hyperledger-labs/zeto/go-sdk/pkg/sparse-merkle-tree/node" "github.com/hyperledger/firefly-common/pkg/i18n" "github.com/iden3/go-iden3-crypto/babyjub" "github.com/kaleido-io/paladin/domains/zeto/internal/msgs" - "github.com/kaleido-io/paladin/domains/zeto/pkg/constants" + "github.com/kaleido-io/paladin/domains/zeto/internal/zeto/smt" + corepb "github.com/kaleido-io/paladin/domains/zeto/pkg/proto" "github.com/kaleido-io/paladin/domains/zeto/pkg/types" + "github.com/kaleido-io/paladin/domains/zeto/pkg/zetosigner" "github.com/kaleido-io/paladin/toolkit/pkg/prototk" "github.com/kaleido-io/paladin/toolkit/pkg/tktypes" ) -func isNullifiersCircuit(circuitId string) bool { - return circuitId == constants.CIRCUIT_ANON_NULLIFIER || circuitId == constants.CIRCUIT_ANON_NULLIFIER_BATCH -} - -func isNullifiersToken(tokenName string) bool { - return tokenName == constants.TOKEN_ANON_NULLIFIER -} - -func isEncryptionToken(tokenName string) bool { - return tokenName == constants.TOKEN_ANON_ENC -} - -// the Zeto implementations support two input/output sizes for the circuits: 2 and 10, -// if the input or output size is larger than 2, then the batch circuit is used with -// input/output size 10 -func getInputSize(sizeOfEndorsableStates int) int { - if sizeOfEndorsableStates <= 2 { - return 2 - } - return 10 -} - -func loadBabyJubKey(payload []byte) (*babyjub.PublicKey, error) { - var keyCompressed babyjub.PublicKeyComp - if err := keyCompressed.UnmarshalText(payload); err != nil { - return nil, err - } - return keyCompressed.Decompress() -} - func validateTransferParams(ctx context.Context, params []*types.TransferParamEntry) error { if len(params) == 0 { return i18n.NewError(ctx, msgs.MsgNoTransferParams) @@ -65,16 +40,23 @@ func validateTransferParams(ctx context.Context, params []*types.TransferParamEn if param.To == "" { return i18n.NewError(ctx, msgs.MsgNoParamTo, i) } - if param.Amount == nil { - return i18n.NewError(ctx, msgs.MsgNoParamAmount, i) - } - if param.Amount.Int().Sign() != 1 { - return i18n.NewError(ctx, msgs.MsgParamAmountGtZero, i) + if err := validateAmountParam(ctx, param.Amount, i); err != nil { + return err } } return nil } +func validateAmountParam(ctx context.Context, amount *tktypes.HexUint256, i int) error { + if amount == nil { + return i18n.NewError(ctx, msgs.MsgNoParamAmount, i) + } + if amount.Int().Sign() != 1 { + return i18n.NewError(ctx, msgs.MsgParamAmountGtZero, i) + } + return nil +} + func encodeTransactionData(ctx context.Context, transaction *prototk.TransactionSpecification) (tktypes.HexBytes, error) { txID, err := tktypes.ParseHexBytes(ctx, transaction.TransactionId) if err != nil { @@ -96,3 +78,93 @@ func decodeTransactionData(data tktypes.HexBytes) (txID tktypes.HexBytes) { } return data[4:] } + +func encodeProof(proof *corepb.SnarkProof) map[string]interface{} { + // Convert the proof json to the format that the Solidity verifier expects + return map[string]interface{}{ + "pA": []string{proof.A[0], proof.A[1]}, + "pB": [][]string{ + {proof.B[0].Items[1], proof.B[0].Items[0]}, + {proof.B[1].Items[1], proof.B[1].Items[0]}, + }, + "pC": []string{proof.C[0], proof.C[1]}, + } +} + +func loadBabyJubKey(payload []byte) (*babyjub.PublicKey, error) { + var keyCompressed babyjub.PublicKeyComp + if err := keyCompressed.UnmarshalText(payload); err != nil { + return nil, err + } + return keyCompressed.Decompress() +} + +func generateMerkleProofs(ctx context.Context, zeto *Zeto, tokenName string, stateQueryContext string, contractAddress *tktypes.EthAddress, inputCoins []*types.ZetoCoin) ([]core.Proof, *corepb.ProvingRequestExtras_Nullifiers, error) { + smtName := smt.MerkleTreeName(tokenName, contractAddress) + storage := smt.NewStatesStorage(zeto.Callbacks, smtName, stateQueryContext, zeto.merkleTreeRootSchema.Id, zeto.merkleTreeNodeSchema.Id) + mt, err := smt.NewSmt(storage) + if err != nil { + return nil, nil, i18n.NewError(ctx, msgs.MsgErrorNewSmt, smtName, err) + } + // verify that the input UTXOs have been indexed by the Merkle tree DB + // and generate a merkle proof for each + var indexes []*big.Int + for _, coin := range inputCoins { + pubKey, err := zetosigner.DecodeBabyJubJubPublicKey(coin.Owner.String()) + if err != nil { + return nil, nil, i18n.NewError(ctx, msgs.MsgErrorLoadOwnerPubKey, err) + } + idx := node.NewFungible(coin.Amount.Int(), pubKey, coin.Salt.Int()) + leaf, err := node.NewLeafNode(idx) + if err != nil { + return nil, nil, i18n.NewError(ctx, msgs.MsgErrorNewLeafNode, err) + } + n, err := mt.GetNode(leaf.Ref()) + if err != nil { + // TODO: deal with when the node is not found in the DB tables for the tree + // e.g because the transaction event hasn't been processed yet + return nil, nil, i18n.NewError(ctx, msgs.MsgErrorQueryLeafNode, leaf.Ref().Hex(), err) + } + hash, err := coin.Hash(ctx) + if err != nil { + return nil, nil, i18n.NewError(ctx, msgs.MsgErrorHashInputState, err) + } + if n.Index().BigInt().Cmp(hash.Int()) != 0 { + expectedIndex, err := node.NewNodeIndexFromBigInt(hash.Int()) + if err != nil { + return nil, nil, i18n.NewError(ctx, msgs.MsgErrorNewNodeIndex, err) + } + return nil, nil, i18n.NewError(ctx, msgs.MsgErrorHashMismatch, leaf.Ref().Hex(), n.Index().BigInt().Text(16), n.Index().Hex(), hash.HexString0xPrefix(), expectedIndex.Hex()) + } + indexes = append(indexes, n.Index().BigInt()) + } + mtRoot := mt.Root() + proofs, _, err := mt.GenerateProofs(indexes, mtRoot) + if err != nil { + return nil, nil, i18n.NewError(ctx, msgs.MsgErrorGenerateMTP, err) + } + var mps []*corepb.MerkleProof + var enabled []bool + for i, proof := range proofs { + cp, err := proof.ToCircomVerifierProof(indexes[i], indexes[i], mtRoot, smt.SMT_HEIGHT_UTXO) + if err != nil { + return nil, nil, i18n.NewError(ctx, msgs.MsgErrorConvertToCircomProof, err) + } + proofSiblings := make([]string, len(cp.Siblings)-1) + for i, s := range cp.Siblings[0 : len(cp.Siblings)-1] { + proofSiblings[i] = s.BigInt().Text(16) + } + p := corepb.MerkleProof{ + Nodes: proofSiblings, + } + mps = append(mps, &p) + enabled = append(enabled, true) + } + extrasObj := corepb.ProvingRequestExtras_Nullifiers{ + Root: mt.Root().BigInt().Text(16), + MerkleProofs: mps, + Enabled: enabled, + } + + return proofs, &extrasObj, nil +} diff --git a/domains/zeto/internal/zeto/zeto.go b/domains/zeto/internal/zeto/zeto.go index c654d1fd2..a73133abc 100644 --- a/domains/zeto/internal/zeto/zeto.go +++ b/domains/zeto/internal/zeto/zeto.go @@ -27,6 +27,7 @@ import ( "github.com/hyperledger/firefly-signer/pkg/ethtypes" "github.com/iden3/go-iden3-crypto/babyjub" "github.com/kaleido-io/paladin/domains/zeto/internal/msgs" + "github.com/kaleido-io/paladin/domains/zeto/internal/zeto/common" "github.com/kaleido-io/paladin/domains/zeto/internal/zeto/signer" "github.com/kaleido-io/paladin/domains/zeto/internal/zeto/smt" "github.com/kaleido-io/paladin/domains/zeto/pkg/types" @@ -53,6 +54,7 @@ type Zeto struct { mintSignature string transferSignature string transferWithEncSignature string + withdrawSignature string snarkProver signerapi.InMemorySigner } @@ -75,6 +77,13 @@ type TransferWithEncryptedValuesEvent struct { EncryptedValues []tktypes.HexUint256 `json:"encryptedValues"` } +type WithdrawEvent struct { + Amount tktypes.HexUint256 `json:"amount"` + Inputs []tktypes.HexUint256 `json:"inputs"` + Output tktypes.HexUint256 `json:"output"` + Data tktypes.HexBytes `json:"data"` +} + var factoryDeployABI = &abi.Entry{ Type: abi.Function, Name: "deploy", @@ -274,6 +283,23 @@ func (z *Zeto) PrepareTransaction(ctx context.Context, req *prototk.PrepareTrans return handler.Prepare(ctx, tx, req) } +func (z *Zeto) GetHandler(method string) types.DomainHandler { + switch method { + case "mint": + return &mintHandler{zeto: z} + case "transfer": + return &transferHandler{zeto: z} + case "lockProof": + return &lockHandler{zeto: z} + case "deposit": + return &depositHandler{zeto: z} + case "withdraw": + return &withdrawHandler{zeto: z} + default: + return nil + } +} + func (z *Zeto) decodeDomainConfig(ctx context.Context, domainConfig []byte) (*types.DomainInstanceConfig, error) { configValues, err := types.DomainInstanceConfigABI.DecodeABIDataCtx(ctx, domainConfig, 0) if err != nil { @@ -345,6 +371,8 @@ func (z *Zeto) registerEventSignatures(eventAbis abi.ABI) { z.transferSignature = event.SolString() case "UTXOTransferWithEncryptedValues": z.transferWithEncSignature = event.SolString() + case "UTXOWithdraw": + z.withdrawSignature = event.SolString() } } } @@ -366,7 +394,7 @@ func (z *Zeto) HandleEventBatch(ctx context.Context, req *prototk.HandleEventBat var smtName string var storage smt.StatesStorage var tree core.SparseMerkleTree - if isNullifiersCircuit(domainConfig.CircuitId) { + if common.IsNullifiersToken(domainConfig.TokenName) { smtName = smt.MerkleTreeName(domainConfig.TokenName, contractAddress) storage = smt.NewStatesStorage(z.Callbacks, smtName, req.StateQueryContext, z.merkleTreeRootSchema.Id, z.merkleTreeNodeSchema.Id) tree, err = smt.NewSmt(storage) @@ -383,6 +411,8 @@ func (z *Zeto) HandleEventBatch(ctx context.Context, req *prototk.HandleEventBat err = z.handleTransferEvent(ctx, tree, storage, ev, domainConfig.TokenName, &res) case z.transferWithEncSignature: err = z.handleTransferWithEncryptionEvent(ctx, tree, storage, ev, domainConfig.TokenName, &res) + case z.withdrawSignature: + err = z.handleWithdrawEvent(ctx, tree, storage, ev, domainConfig.TokenName, &res) } if err != nil { errors = append(errors, err.Error()) @@ -391,7 +421,7 @@ func (z *Zeto) HandleEventBatch(ctx context.Context, req *prototk.HandleEventBat if len(errors) > 0 { return &res, i18n.NewError(ctx, msgs.MsgErrorHandleEvents, formatErrors(errors)) } - if isNullifiersCircuit(domainConfig.CircuitId) { + if common.IsNullifiersToken(domainConfig.TokenName) { newStatesForSMT, err := storage.GetNewStates() if err != nil { return nil, i18n.NewError(ctx, msgs.MsgErrorGetNewSmtStates, smtName, err) diff --git a/domains/zeto/internal/zeto/zeto_test.go b/domains/zeto/internal/zeto/zeto_test.go index 6b1a11916..208b26798 100644 --- a/domains/zeto/internal/zeto/zeto_test.go +++ b/domains/zeto/internal/zeto/zeto_test.go @@ -642,3 +642,13 @@ func findCoins(ctx context.Context, z *Zeto, useNullifiers bool, contractAddress } return coins, err } + +func TestGetHandler(t *testing.T) { + z := &Zeto{ + name: "test1", + } + assert.NotNil(t, z.GetHandler("mint")) + assert.NotNil(t, z.GetHandler("transfer")) + assert.NotNil(t, z.GetHandler("lockProof")) + assert.Nil(t, z.GetHandler("bad")) +} diff --git a/domains/zeto/pkg/constants/constants.go b/domains/zeto/pkg/constants/constants.go index a5795544a..452c0d4bb 100644 --- a/domains/zeto/pkg/constants/constants.go +++ b/domains/zeto/pkg/constants/constants.go @@ -17,14 +17,19 @@ package constants const ( // the base circuits support inputs and outputs up to size 2 - CIRCUIT_ANON = "anon" - CIRCUIT_ANON_ENC = "anon_enc" - CIRCUIT_ANON_NULLIFIER = "anon_nullifier" + CIRCUIT_ANON = "anon" + CIRCUIT_ANON_ENC = "anon_enc" + CIRCUIT_ANON_NULLIFIER = "anon_nullifier" + CIRCUIT_DEPOSIT = "check_hashes_value" + CIRCUIT_WITHDRAW = "check_inputs_outputs_value" + CIRCUIT_WITHDRAW_NULLIFIER = "check_nullifier_value" // the batch circuits support inputs and outputs from size 3 up to size 10 - CIRCUIT_ANON_BATCH = "anon_batch" - CIRCUIT_ANON_ENC_BATCH = "anon_enc_batch" - CIRCUIT_ANON_NULLIFIER_BATCH = "anon_nullifier_batch" + CIRCUIT_ANON_BATCH = "anon_batch" + CIRCUIT_ANON_ENC_BATCH = "anon_enc_batch" + CIRCUIT_ANON_NULLIFIER_BATCH = "anon_nullifier_batch" + CIRCUIT_WITHDRAW_BATCH = "check_inputs_outputs_value_batch" + CIRCUIT_WITHDRAW_NULLIFIER_BATCH = "check_nullifier_value_batch" TOKEN_ANON = "Zeto_Anon" TOKEN_ANON_ENC = "Zeto_AnonEnc" diff --git a/domains/zeto/pkg/proto/zeto.proto b/domains/zeto/pkg/proto/zeto.proto index d01232b28..464890977 100644 --- a/domains/zeto/pkg/proto/zeto.proto +++ b/domains/zeto/pkg/proto/zeto.proto @@ -29,9 +29,10 @@ message ProvingRequestCommon { repeated uint64 inputValues = 2; repeated string inputSalts = 3; string inputOwner = 4; - repeated uint64 outputValues = 5; - repeated string outputSalts = 6; - repeated string outputOwners = 7; + repeated string outputCommitments = 5; + repeated uint64 outputValues = 6; + repeated string outputSalts = 7; + repeated string outputOwners = 8; } message ProvingRequestExtras_Encryption { diff --git a/domains/zeto/pkg/types/abi.go b/domains/zeto/pkg/types/abi.go index c81ba35e3..32aaf279c 100644 --- a/domains/zeto/pkg/types/abi.go +++ b/domains/zeto/pkg/types/abi.go @@ -73,6 +73,20 @@ var ZetoABI = abi.ABI{ {Name: "_erc20", Type: "address"}, }, }, + { + Name: "deposit", + Type: abi.Function, + Inputs: abi.ParameterArray{ + {Name: "amount", Type: "uint256"}, + }, + }, + { + Name: "withdraw", + Type: abi.Function, + Inputs: abi.ParameterArray{ + {Name: "amount", Type: "uint256"}, + }, + }, } type InitializerParams struct { @@ -104,3 +118,11 @@ type LockParams struct { Delegate *tktypes.EthAddress `json:"delegate"` Call tktypes.HexBytes `json:"call"` } + +type DepositParams struct { + Amount *tktypes.HexUint256 `json:"amount"` +} + +type WithdrawParams struct { + Amount *tktypes.HexUint256 `json:"amount"` +} From 09e1cf80d72512208fe89f7dd394f1266588cc6e Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Tue, 3 Dec 2024 14:38:22 -0500 Subject: [PATCH 05/36] formatting Signed-off-by: Jim Zhang --- toolkit/go/pkg/rpcserver/rpcmethod.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toolkit/go/pkg/rpcserver/rpcmethod.go b/toolkit/go/pkg/rpcserver/rpcmethod.go index 3bb98e00f..faa312311 100644 --- a/toolkit/go/pkg/rpcserver/rpcmethod.go +++ b/toolkit/go/pkg/rpcserver/rpcmethod.go @@ -22,8 +22,8 @@ import ( "github.com/hyperledger/firefly-common/pkg/fftypes" "github.com/hyperledger/firefly-common/pkg/i18n" - "github.com/kaleido-io/paladin/toolkit/pkg/tkmsgs" "github.com/kaleido-io/paladin/toolkit/pkg/rpcclient" + "github.com/kaleido-io/paladin/toolkit/pkg/tkmsgs" ) // RPCHandler should not be implemented directly - use RPCMethod0 ... RPCMethod5 to implement your function From bd19b1ada5e4eb88ac1d85c895beef007765c332 Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Thu, 5 Dec 2024 12:22:36 -0500 Subject: [PATCH 06/36] upgrade to zeto v0.0.10 Signed-off-by: Jim Zhang --- domains/zeto/build.gradle | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/domains/zeto/build.gradle b/domains/zeto/build.gradle index 00ba7a67f..758c828bf 100644 --- a/domains/zeto/build.gradle +++ b/domains/zeto/build.gradle @@ -32,7 +32,7 @@ ext { "github.com/kaleido-io/paladin/domains/zeto/pkg/zetosigner", ] - zetoVersion = "v0.0.9" + zetoVersion = "v0.0.10" zetoHost = "hyperledger-labs" zkpOut = "${projectDir}/zkp" toolsOut = "${projectDir}/tools" @@ -74,11 +74,10 @@ dependencies { coreGo project(path: ":core:go", configuration: "goSource") } -task downloadZetoProver { +task downloadZetoProver(dependsOn: "protoc") { def outname = "zeto-wasm-${zetoVersion}.tar.gz" def url = "https://github.com/${zetoHost}/zeto/releases/download/${zetoVersion}/${outname}" def f = new File(toolsOut, outname) - inputs.file(f) doFirst { mkdir(f.parent) new URL(url).withInputStream{ i -> f.withOutputStream{ it << i }} @@ -86,11 +85,10 @@ task downloadZetoProver { outputs.file(f) } -task downloadZetoTestProvingKeys { +task downloadZetoTestProvingKeys(dependsOn: "protoc") { def outname = "zeto-test-proving-keys-${zetoVersion}.tar.gz" def url = "https://github.com/${zetoHost}/zeto/releases/download/${zetoVersion}/${outname}" def f = new File(toolsOut, outname) - inputs.file(f) doFirst { mkdir(f.parent) new URL(url).withInputStream{ i -> f.withOutputStream{ it << i }} @@ -98,11 +96,10 @@ task downloadZetoTestProvingKeys { outputs.file(f) } -task downloadZetoCompiledContracts { +task downloadZetoCompiledContracts(dependsOn: "protoc") { def outname = "zeto-contracts-${zetoVersion}.tar.gz" def url = "https://github.com/${zetoHost}/zeto/releases/download/${zetoVersion}/${outname}" def f = new File(toolsOut, outname) - inputs.file(f) doFirst { mkdir(f.parent) new URL(url).withInputStream{ i -> f.withOutputStream{ it << i }} @@ -220,7 +217,7 @@ task testE2E(type: Exec, dependsOn: [protoc, copySolidity, copySolidityForTest, helpers.dumpLogsOnFailure(it, ':testinfra:startTestInfra') } -task unitTests(type: Exec, dependsOn: [protoc, ":core:go:makeMocks"]) { +task unitTests(type: Exec, dependsOn: [protoc, ":core:go:makeMocks", downloadZetoCompiledContracts]) { inputs.files(configurations.toolkitGo) inputs.files(configurations.coreGo) inputs.files(goFiles) From dbbfa7aee93258adb6c33924f1b66d2ee90997d0 Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Thu, 5 Dec 2024 12:23:42 -0500 Subject: [PATCH 07/36] unit tests for deposit and withdraw Signed-off-by: Jim Zhang --- domains/zeto/internal/msgs/en_errors.go | 1 + domains/zeto/internal/msgs/en_errors_test.go | 13 + .../zeto/internal/zeto/common/utils_test.go | 12 + domains/zeto/internal/zeto/events_abi_test.go | 2 +- domains/zeto/internal/zeto/handler_deposit.go | 24 +- .../internal/zeto/handler_deposit_test.go | 230 ++++++++++++++++++ .../zeto/internal/zeto/handler_events_test.go | 30 +++ .../internal/zeto/handler_withdraw_test.go | 230 ++++++++++++++++++ .../internal/zeto/signer/circuits_test.go | 24 +- .../zeto/signer/inputs_assembler_test.go | 41 ++++ .../internal/zeto/signer/snark_prover_test.go | 76 ++++++ domains/zeto/internal/zeto/states.go | 4 +- domains/zeto/internal/zeto/states_test.go | 41 ++++ domains/zeto/internal/zeto/zeto_test.go | 19 ++ 14 files changed, 726 insertions(+), 21 deletions(-) create mode 100644 domains/zeto/internal/msgs/en_errors_test.go create mode 100644 domains/zeto/internal/zeto/common/utils_test.go create mode 100644 domains/zeto/internal/zeto/handler_deposit_test.go create mode 100644 domains/zeto/internal/zeto/handler_withdraw_test.go diff --git a/domains/zeto/internal/msgs/en_errors.go b/domains/zeto/internal/msgs/en_errors.go index e6c788766..08cd5e789 100644 --- a/domains/zeto/internal/msgs/en_errors.go +++ b/domains/zeto/internal/msgs/en_errors.go @@ -144,4 +144,5 @@ var ( MsgNoDomainReceipt = ffe("PD210102", "Not implemented. See state receipt for coin transfers") MsgUnknownSignPayload = ffe("PD210103", "Sign payload type '%s' not recognized") MsgNullifierGenerationFailed = ffe("PD210104", "Failed to generate nullifier for coin") + MsgErrorDecodeDepositCall = ffe("PD210105", "Failed to decode the deposit call. %s") ) diff --git a/domains/zeto/internal/msgs/en_errors_test.go b/domains/zeto/internal/msgs/en_errors_test.go new file mode 100644 index 000000000..df9e25c37 --- /dev/null +++ b/domains/zeto/internal/msgs/en_errors_test.go @@ -0,0 +1,13 @@ +package msgs + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestFfeError(t *testing.T) { + assert.Panics(t, func() { + ffe("notvalid", "") + }) +} diff --git a/domains/zeto/internal/zeto/common/utils_test.go b/domains/zeto/internal/zeto/common/utils_test.go new file mode 100644 index 000000000..ee395e360 --- /dev/null +++ b/domains/zeto/internal/zeto/common/utils_test.go @@ -0,0 +1,12 @@ +package common + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestIsBatchCircuit(t *testing.T) { + assert.False(t, IsBatchCircuit(2)) + assert.True(t, IsBatchCircuit(5)) +} diff --git a/domains/zeto/internal/zeto/events_abi_test.go b/domains/zeto/internal/zeto/events_abi_test.go index 28c36f615..a0cae18c4 100644 --- a/domains/zeto/internal/zeto/events_abi_test.go +++ b/domains/zeto/internal/zeto/events_abi_test.go @@ -24,7 +24,7 @@ import ( func TestGetAllZetoEventAbis(t *testing.T) { events := getAllZetoEventAbis() - assert.Equal(t, 3, len(events)) + assert.Equal(t, 4, len(events)) } func TestBuildEvents(t *testing.T) { diff --git a/domains/zeto/internal/zeto/handler_deposit.go b/domains/zeto/internal/zeto/handler_deposit.go index ff339e41c..08ebeb55c 100644 --- a/domains/zeto/internal/zeto/handler_deposit.go +++ b/domains/zeto/internal/zeto/handler_deposit.go @@ -53,7 +53,7 @@ var depositABI = &abi.Entry{ func (h *depositHandler) ValidateParams(ctx context.Context, config *types.DomainInstanceConfig, params string) (interface{}, error) { var depositParams types.DepositParams if err := json.Unmarshal([]byte(params), &depositParams); err != nil { - return nil, err + return nil, i18n.NewError(ctx, msgs.MsgErrorDecodeDepositCall, err) } if err := validateAmountParam(ctx, depositParams.Amount, 0); err != nil { @@ -85,7 +85,7 @@ func (h *depositHandler) Assemble(ctx context.Context, tx *types.ParsedTransacti } useNullifiers := common.IsNullifiersToken(tx.DomainConfig.TokenName) - outputCoins, outputStates, err := h.zeto.prepareOutputsForDeposit(ctx, useNullifiers, amount, req.ResolvedVerifiers) + outputCoins, outputStates, err := h.zeto.prepareOutputsForDeposit(ctx, useNullifiers, amount, resolvedSender) if err != nil { return nil, i18n.NewError(ctx, msgs.MsgErrorPrepTxOutputs, err) } @@ -194,19 +194,15 @@ func (h *depositHandler) formatProvingRequest(ctx context.Context, outputCoins [ outputSalts := make([]string, outputSize) outputOwners := make([]string, outputSize) for i := 0; i < outputSize; i++ { - if i < len(outputCoins) { - coin := outputCoins[i] - hash, err := coin.Hash(ctx) - if err != nil { - return nil, i18n.NewError(ctx, msgs.MsgErrorHashInputState, err) - } - outputCommitments[i] = hash.Int().Text(16) - outputValueInts[i] = coin.Amount.Int().Uint64() - outputSalts[i] = coin.Salt.Int().Text(16) - outputOwners[i] = coin.Owner.String() - } else { - outputSalts[i] = "0" + coin := outputCoins[i] + hash, err := coin.Hash(ctx) + if err != nil { + return nil, i18n.NewError(ctx, msgs.MsgErrorHashInputState, err) } + outputCommitments[i] = hash.Int().Text(16) + outputValueInts[i] = coin.Amount.Int().Uint64() + outputSalts[i] = coin.Salt.Int().Text(16) + outputOwners[i] = coin.Owner.String() } payload := &corepb.ProvingRequest{ diff --git a/domains/zeto/internal/zeto/handler_deposit_test.go b/domains/zeto/internal/zeto/handler_deposit_test.go new file mode 100644 index 000000000..11650787d --- /dev/null +++ b/domains/zeto/internal/zeto/handler_deposit_test.go @@ -0,0 +1,230 @@ +package zeto + +import ( + "context" + "errors" + "testing" + + "github.com/kaleido-io/paladin/domains/zeto/pkg/constants" + corepb "github.com/kaleido-io/paladin/domains/zeto/pkg/proto" + "github.com/kaleido-io/paladin/domains/zeto/pkg/types" + "github.com/kaleido-io/paladin/domains/zeto/pkg/zetosigner/zetosignerapi" + "github.com/kaleido-io/paladin/toolkit/pkg/prototk" + "github.com/kaleido-io/paladin/toolkit/pkg/tktypes" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" +) + +func TestDepositValidateParams(t *testing.T) { + h := &depositHandler{} + ctx := context.Background() + config := &types.DomainInstanceConfig{} + v, err := h.ValidateParams(ctx, config, "{\"amount\":100}") + require.NoError(t, err) + require.Equal(t, "0x64", v.(*tktypes.HexUint256).String()) + + _, err = h.ValidateParams(ctx, config, "bad json") + require.ErrorContains(t, err, "PD210105: Failed to decode the deposit call.") + + _, err = h.ValidateParams(ctx, config, "{\"amount\":-100}") + require.ErrorContains(t, err, "PD210027: Parameter 'amount' must be greater than 0 (index=0)") +} + +func TestDepositInit(t *testing.T) { + h := depositHandler{ + zeto: &Zeto{ + name: "test1", + }, + } + ctx := context.Background() + tx := &types.ParsedTransaction{ + Transaction: &prototk.TransactionSpecification{ + From: "Alice", + }, + } + req := &prototk.InitTransactionRequest{} + res, err := h.Init(ctx, tx, req) + assert.NoError(t, err) + assert.Len(t, res.RequiredVerifiers, 1) + assert.Equal(t, "Alice", res.RequiredVerifiers[0].Lookup) + assert.Equal(t, zetosignerapi.IDEN3_PUBKEY_BABYJUBJUB_COMPRESSED_0X, res.RequiredVerifiers[0].VerifierType) + assert.Equal(t, zetosignerapi.AlgoDomainZetoSnarkBJJ("test1"), res.RequiredVerifiers[0].Algorithm) +} + +func TestDepositAssemble(t *testing.T) { + h := depositHandler{ + zeto: &Zeto{ + name: "test1", + coinSchema: &prototk.StateSchema{ + Id: "coin", + }, + merkleTreeRootSchema: &prototk.StateSchema{ + Id: "merkle_tree_root", + }, + merkleTreeNodeSchema: &prototk.StateSchema{ + Id: "merkle_tree_node", + }, + }, + } + ctx := context.Background() + txSpec := &prototk.TransactionSpecification{ + From: "Bob", + ContractInfo: &prototk.ContractInfo{ + ContractAddress: "0x1234567890123456789012345678901234567890", + }, + } + tx := &types.ParsedTransaction{ + Params: tktypes.MustParseHexUint256("100"), + Transaction: txSpec, + DomainConfig: &types.DomainInstanceConfig{ + TokenName: "tokenContract1", + CircuitId: "circuit1", + }, + } + req := &prototk.AssembleTransactionRequest{ + ResolvedVerifiers: []*prototk.ResolvedVerifier{ + { + Lookup: "Alice", + Verifier: "0x19d2ee6b9770a4f8d7c3b7906bc7595684509166fa42d718d1d880b62bcb7922", + Algorithm: h.zeto.getAlgoZetoSnarkBJJ(), + VerifierType: zetosignerapi.IDEN3_PUBKEY_BABYJUBJUB_COMPRESSED_0X, + }, + }, + Transaction: txSpec, + } + _, err := h.Assemble(ctx, tx, req) + assert.EqualError(t, err, "PD210036: Failed to resolve verifier: Bob") + + req.ResolvedVerifiers = append(req.ResolvedVerifiers, &prototk.ResolvedVerifier{ + Lookup: "Bob", + Verifier: "0x1234567890123456789012345678901234567890", + Algorithm: h.zeto.getAlgoZetoSnarkBJJ(), + VerifierType: zetosignerapi.IDEN3_PUBKEY_BABYJUBJUB_COMPRESSED_0X, + }) + testCallbacks := &testDomainCallbacks{ + returnFunc: func() (*prototk.FindAvailableStatesResponse, error) { + return nil, errors.New("test error") + }, + } + h.zeto.Callbacks = testCallbacks + _, err = h.Assemble(ctx, tx, req) + assert.ErrorContains(t, err, "PD210040: Failed to prepare transaction outputs. PD210037: Failed load owner public key") + + req.ResolvedVerifiers[1].Verifier = "0x19d2ee6b9770a4f8d7c3b7906bc7595684509166fa42d718d1d880b62bcb7922" + res, err := h.Assemble(ctx, tx, req) + require.NoError(t, err) + assert.Equal(t, "100", *res.AssembledTransaction.DomainData) +} + +func TestDepositEndorse(t *testing.T) { + h := depositHandler{} + ctx := context.Background() + tx := &types.ParsedTransaction{ + Params: tktypes.MustParseHexUint256("100"), + Transaction: &prototk.TransactionSpecification{ + From: "Bob", + }, + } + + req := &prototk.EndorseTransactionRequest{} + res, err := h.Endorse(ctx, tx, req) + assert.NoError(t, err) + assert.Equal(t, prototk.EndorseTransactionResponse_ENDORSER_SUBMIT, res.EndorsementResult) +} + +func TestDepositPrepare(t *testing.T) { + z := &Zeto{ + name: "test1", + } + h := depositHandler{ + zeto: z, + } + txSpec := &prototk.TransactionSpecification{ + TransactionId: "bad hex", + From: "Bob", + } + tx := &types.ParsedTransaction{ + Params: tktypes.MustParseHexUint256("100"), + Transaction: txSpec, + DomainConfig: &types.DomainInstanceConfig{ + TokenName: constants.TOKEN_ANON_ENC, + }, + } + amountStr := "100" + req := &prototk.PrepareTransactionRequest{ + InputStates: []*prototk.EndorsableState{ + { + SchemaId: "coin", + StateDataJson: "bad json", + }, + }, + OutputStates: []*prototk.EndorsableState{ + { + SchemaId: "coin", + StateDataJson: "bad json", + }, + }, + Transaction: txSpec, + DomainData: &amountStr, + } + ctx := context.Background() + _, err := h.Prepare(ctx, tx, req) + assert.EqualError(t, err, "PD210043: Did not find 'sender' attestation") + + at := zetosignerapi.PAYLOAD_DOMAIN_ZETO_SNARK + req.AttestationResult = []*prototk.AttestationResult{ + { + Name: "sender", + AttestationType: prototk.AttestationType_ENDORSE, + PayloadType: &at, + Payload: []byte("bad payload"), + }, + } + _, err = h.Prepare(ctx, tx, req) + assert.ErrorContains(t, err, "PD210044: Failed to unmarshal proving response") + + proofReq := corepb.ProvingResponse{ + Proof: &corepb.SnarkProof{ + A: []string{"0x1234567890", "0x1234567890"}, + B: []*corepb.B_Item{ + { + Items: []string{"0x1234567890", "0x1234567890"}, + }, + { + Items: []string{"0x1234567890", "0x1234567890"}, + }, + }, + C: []string{"0x1234567890", "0x1234567890"}, + }, + PublicInputs: map[string]string{ + "encryptionNonce": "0x1234567890", + "encryptedValues": "0x1234567890,0x1234567890", + }, + } + payload, err := proto.Marshal(&proofReq) + assert.NoError(t, err) + req.AttestationResult[0].Payload = payload + _, err = h.Prepare(ctx, tx, req) + assert.ErrorContains(t, err, "PD210047: Failed to parse output states.") + + req.OutputStates[0].StateDataJson = "{\"salt\":\"0x042fac32983b19d76425cc54dd80e8a198f5d477c6a327cb286eb81a0c2b95ec\",\"owner\":\"0x7cdd539f3ed6c283494f47d8481f84308a6d7043087fb6711c9f1df04e2b8025\",\"amount\":\"0x0f\",\"hash\":\"0x303eb034d22aacc5dff09647928d757017a35e64e696d48609a250a6505e5d5f\"}" + _, err = h.Prepare(ctx, tx, req) + assert.ErrorContains(t, err, "PD210049: Failed to encode transaction data. PD210028: Failed to parse transaction id. PD020007: Invalid hex:") + + txSpec.TransactionId = "0x1234567890123456789012345678901234567890123456789012345678901234" + z.config = &types.DomainFactoryConfig{ + DomainContracts: types.DomainConfigContracts{ + Implementations: []*types.DomainContract{}, + }, + } + z.config.DomainContracts.Implementations = []*types.DomainContract{ + { + Name: constants.TOKEN_ANON_ENC, + }, + } + + res, err := h.Prepare(ctx, tx, req) + assert.NoError(t, err) + assert.Equal(t, "{\"amount\":\"100\",\"data\":\"0x000100001234567890123456789012345678901234567890123456789012345678901234\",\"outputs\":[\"0x303eb034d22aacc5dff09647928d757017a35e64e696d48609a250a6505e5d5f\",\"0\"],\"proof\":{\"pA\":[\"0x1234567890\",\"0x1234567890\"],\"pB\":[[\"0x1234567890\",\"0x1234567890\"],[\"0x1234567890\",\"0x1234567890\"]],\"pC\":[\"0x1234567890\",\"0x1234567890\"]}}", res.Transaction.ParamsJson) +} diff --git a/domains/zeto/internal/zeto/handler_events_test.go b/domains/zeto/internal/zeto/handler_events_test.go index db4001577..5c877b37d 100644 --- a/domains/zeto/internal/zeto/handler_events_test.go +++ b/domains/zeto/internal/zeto/handler_events_test.go @@ -168,3 +168,33 @@ func TestUpdateMerkleTree(t *testing.T) { err = z.updateMerkleTree(ctx, merkleTree, storage, tktypes.HexBytes("0x1234"), []tktypes.HexUint256{*tktypes.MustParseHexUint256("0x1234"), *tktypes.MustParseHexUint256("0x0")}) assert.NoError(t, err) } + +func TestHandleWithdrawEvent(t *testing.T) { + z, testCallbacks := newTestZeto() + storage := smt.NewStatesStorage(testCallbacks, "testToken1", "context1", "merkle_tree_root", "merkle_tree_node") + merkleTree, err := smt.NewSmt(storage) + require.NoError(t, err) + ctx := context.Background() + + ev := &prototk.OnChainEvent{ + DataJson: "bad json", + SoliditySignature: "event UTXOWithdraw(uint256 amount, uint256[] inputs, uint256 output, address indexed submitter, bytes data)", + } + res := &prototk.HandleEventBatchResponse{} + + // bad data for the withdraw event - should be logged and move on + err = z.handleWithdrawEvent(ctx, merkleTree, storage, ev, "Zeto_Anon", res) + assert.NoError(t, err) + + ev.DataJson = "{\"data\":\"0x0001\",\"inputs\":[\"7980718117603030807695495350922077879582656644717071592146865497574198464253\"],\"output\":\"7980718117603030807695495350922077879582656644717071592146865497574198464253\",\"submitter\":\"0x74e71b05854ee819cb9397be01c82570a178d019\"}" + err = z.handleWithdrawEvent(ctx, merkleTree, storage, ev, "Zeto_Anon", res) + assert.NoError(t, err) + + ev.DataJson = "{\"data\":\"0x0001ffff\",\"inputs\":[\"7980718117603030807695495350922077879582656644717071592146865497574198464253\"],\"output\":\"7980718117603030807695495350922077879582656644717071592146865497574198464253\",\"submitter\":\"0x74e71b05854ee819cb9397be01c82570a178d019\"}" + err = z.handleWithdrawEvent(ctx, merkleTree, storage, ev, "Zeto_Anon", res) + assert.NoError(t, err) + + ev.DataJson = "{\"data\":\"0x0001000030e43028afbb41d6887444f4c2b4ed6d00000000000000000000000000000000\",\"output\":\"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\",\"submitter\":\"0x74e71b05854ee819cb9397be01c82570a178d019\"}" + err = z.handleWithdrawEvent(ctx, merkleTree, storage, ev, "Zeto_AnonNullifier", res) + assert.ErrorContains(t, err, "PD210061: Failed to update merkle tree for the UTXOWithdraw event. PD210056: Failed to create new node index from hash. 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") +} diff --git a/domains/zeto/internal/zeto/handler_withdraw_test.go b/domains/zeto/internal/zeto/handler_withdraw_test.go new file mode 100644 index 000000000..2dee1ed92 --- /dev/null +++ b/domains/zeto/internal/zeto/handler_withdraw_test.go @@ -0,0 +1,230 @@ +package zeto + +import ( + "context" + "errors" + "testing" + + "github.com/kaleido-io/paladin/domains/zeto/pkg/constants" + corepb "github.com/kaleido-io/paladin/domains/zeto/pkg/proto" + "github.com/kaleido-io/paladin/domains/zeto/pkg/types" + "github.com/kaleido-io/paladin/domains/zeto/pkg/zetosigner/zetosignerapi" + "github.com/kaleido-io/paladin/toolkit/pkg/prototk" + "github.com/kaleido-io/paladin/toolkit/pkg/tktypes" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" +) + +func TestWithdrawValidateParams(t *testing.T) { + h := &depositHandler{} + ctx := context.Background() + config := &types.DomainInstanceConfig{} + v, err := h.ValidateParams(ctx, config, "{\"amount\":100}") + require.NoError(t, err) + require.Equal(t, "0x64", v.(*tktypes.HexUint256).String()) + + _, err = h.ValidateParams(ctx, config, "bad json") + require.ErrorContains(t, err, "PD210105: Failed to decode the deposit call.") + + _, err = h.ValidateParams(ctx, config, "{\"amount\":-100}") + require.ErrorContains(t, err, "PD210027: Parameter 'amount' must be greater than 0 (index=0)") +} + +func TestWithdrawInit(t *testing.T) { + h := depositHandler{ + zeto: &Zeto{ + name: "test1", + }, + } + ctx := context.Background() + tx := &types.ParsedTransaction{ + Transaction: &prototk.TransactionSpecification{ + From: "Alice", + }, + } + req := &prototk.InitTransactionRequest{} + res, err := h.Init(ctx, tx, req) + assert.NoError(t, err) + assert.Len(t, res.RequiredVerifiers, 1) + assert.Equal(t, "Alice", res.RequiredVerifiers[0].Lookup) + assert.Equal(t, zetosignerapi.IDEN3_PUBKEY_BABYJUBJUB_COMPRESSED_0X, res.RequiredVerifiers[0].VerifierType) + assert.Equal(t, zetosignerapi.AlgoDomainZetoSnarkBJJ("test1"), res.RequiredVerifiers[0].Algorithm) +} + +func TestWithdrawAssemble(t *testing.T) { + h := depositHandler{ + zeto: &Zeto{ + name: "test1", + coinSchema: &prototk.StateSchema{ + Id: "coin", + }, + merkleTreeRootSchema: &prototk.StateSchema{ + Id: "merkle_tree_root", + }, + merkleTreeNodeSchema: &prototk.StateSchema{ + Id: "merkle_tree_node", + }, + }, + } + ctx := context.Background() + txSpec := &prototk.TransactionSpecification{ + From: "Bob", + ContractInfo: &prototk.ContractInfo{ + ContractAddress: "0x1234567890123456789012345678901234567890", + }, + } + tx := &types.ParsedTransaction{ + Params: tktypes.MustParseHexUint256("100"), + Transaction: txSpec, + DomainConfig: &types.DomainInstanceConfig{ + TokenName: "tokenContract1", + CircuitId: "circuit1", + }, + } + req := &prototk.AssembleTransactionRequest{ + ResolvedVerifiers: []*prototk.ResolvedVerifier{ + { + Lookup: "Alice", + Verifier: "0x19d2ee6b9770a4f8d7c3b7906bc7595684509166fa42d718d1d880b62bcb7922", + Algorithm: h.zeto.getAlgoZetoSnarkBJJ(), + VerifierType: zetosignerapi.IDEN3_PUBKEY_BABYJUBJUB_COMPRESSED_0X, + }, + }, + Transaction: txSpec, + } + _, err := h.Assemble(ctx, tx, req) + assert.EqualError(t, err, "PD210036: Failed to resolve verifier: Bob") + + req.ResolvedVerifiers = append(req.ResolvedVerifiers, &prototk.ResolvedVerifier{ + Lookup: "Bob", + Verifier: "0x1234567890123456789012345678901234567890", + Algorithm: h.zeto.getAlgoZetoSnarkBJJ(), + VerifierType: zetosignerapi.IDEN3_PUBKEY_BABYJUBJUB_COMPRESSED_0X, + }) + testCallbacks := &testDomainCallbacks{ + returnFunc: func() (*prototk.FindAvailableStatesResponse, error) { + return nil, errors.New("test error") + }, + } + h.zeto.Callbacks = testCallbacks + _, err = h.Assemble(ctx, tx, req) + assert.ErrorContains(t, err, "PD210040: Failed to prepare transaction outputs. PD210037: Failed load owner public key") + + req.ResolvedVerifiers[1].Verifier = "0x19d2ee6b9770a4f8d7c3b7906bc7595684509166fa42d718d1d880b62bcb7922" + res, err := h.Assemble(ctx, tx, req) + require.NoError(t, err) + assert.Equal(t, "100", *res.AssembledTransaction.DomainData) +} + +func TestWithdrawEndorse(t *testing.T) { + h := depositHandler{} + ctx := context.Background() + tx := &types.ParsedTransaction{ + Params: tktypes.MustParseHexUint256("100"), + Transaction: &prototk.TransactionSpecification{ + From: "Bob", + }, + } + + req := &prototk.EndorseTransactionRequest{} + res, err := h.Endorse(ctx, tx, req) + assert.NoError(t, err) + assert.Equal(t, prototk.EndorseTransactionResponse_ENDORSER_SUBMIT, res.EndorsementResult) +} + +func TestWithdrawPrepare(t *testing.T) { + z := &Zeto{ + name: "test1", + } + h := depositHandler{ + zeto: z, + } + txSpec := &prototk.TransactionSpecification{ + TransactionId: "bad hex", + From: "Bob", + } + tx := &types.ParsedTransaction{ + Params: tktypes.MustParseHexUint256("100"), + Transaction: txSpec, + DomainConfig: &types.DomainInstanceConfig{ + TokenName: constants.TOKEN_ANON_ENC, + }, + } + amountStr := "100" + req := &prototk.PrepareTransactionRequest{ + InputStates: []*prototk.EndorsableState{ + { + SchemaId: "coin", + StateDataJson: "bad json", + }, + }, + OutputStates: []*prototk.EndorsableState{ + { + SchemaId: "coin", + StateDataJson: "bad json", + }, + }, + Transaction: txSpec, + DomainData: &amountStr, + } + ctx := context.Background() + _, err := h.Prepare(ctx, tx, req) + assert.EqualError(t, err, "PD210043: Did not find 'sender' attestation") + + at := zetosignerapi.PAYLOAD_DOMAIN_ZETO_SNARK + req.AttestationResult = []*prototk.AttestationResult{ + { + Name: "sender", + AttestationType: prototk.AttestationType_ENDORSE, + PayloadType: &at, + Payload: []byte("bad payload"), + }, + } + _, err = h.Prepare(ctx, tx, req) + assert.ErrorContains(t, err, "PD210044: Failed to unmarshal proving response") + + proofReq := corepb.ProvingResponse{ + Proof: &corepb.SnarkProof{ + A: []string{"0x1234567890", "0x1234567890"}, + B: []*corepb.B_Item{ + { + Items: []string{"0x1234567890", "0x1234567890"}, + }, + { + Items: []string{"0x1234567890", "0x1234567890"}, + }, + }, + C: []string{"0x1234567890", "0x1234567890"}, + }, + PublicInputs: map[string]string{ + "encryptionNonce": "0x1234567890", + "encryptedValues": "0x1234567890,0x1234567890", + }, + } + payload, err := proto.Marshal(&proofReq) + assert.NoError(t, err) + req.AttestationResult[0].Payload = payload + _, err = h.Prepare(ctx, tx, req) + assert.ErrorContains(t, err, "PD210047: Failed to parse output states.") + + req.OutputStates[0].StateDataJson = "{\"salt\":\"0x042fac32983b19d76425cc54dd80e8a198f5d477c6a327cb286eb81a0c2b95ec\",\"owner\":\"0x7cdd539f3ed6c283494f47d8481f84308a6d7043087fb6711c9f1df04e2b8025\",\"amount\":\"0x0f\",\"hash\":\"0x303eb034d22aacc5dff09647928d757017a35e64e696d48609a250a6505e5d5f\"}" + _, err = h.Prepare(ctx, tx, req) + assert.ErrorContains(t, err, "PD210049: Failed to encode transaction data. PD210028: Failed to parse transaction id. PD020007: Invalid hex:") + + txSpec.TransactionId = "0x1234567890123456789012345678901234567890123456789012345678901234" + z.config = &types.DomainFactoryConfig{ + DomainContracts: types.DomainConfigContracts{ + Implementations: []*types.DomainContract{}, + }, + } + z.config.DomainContracts.Implementations = []*types.DomainContract{ + { + Name: constants.TOKEN_ANON_ENC, + }, + } + + res, err := h.Prepare(ctx, tx, req) + assert.NoError(t, err) + assert.Equal(t, "{\"amount\":\"100\",\"data\":\"0x000100001234567890123456789012345678901234567890123456789012345678901234\",\"outputs\":[\"0x303eb034d22aacc5dff09647928d757017a35e64e696d48609a250a6505e5d5f\",\"0\"],\"proof\":{\"pA\":[\"0x1234567890\",\"0x1234567890\"],\"pB\":[[\"0x1234567890\",\"0x1234567890\"],[\"0x1234567890\",\"0x1234567890\"]],\"pC\":[\"0x1234567890\",\"0x1234567890\"]}}", res.Transaction.ParamsJson) +} diff --git a/domains/zeto/internal/zeto/signer/circuits_test.go b/domains/zeto/internal/zeto/signer/circuits_test.go index 9ddb62984..73d7214c7 100644 --- a/domains/zeto/internal/zeto/signer/circuits_test.go +++ b/domains/zeto/internal/zeto/signer/circuits_test.go @@ -22,6 +22,7 @@ import ( "path" "testing" + "github.com/iden3/go-rapidsnark/witness/v2" "github.com/kaleido-io/paladin/domains/zeto/pkg/zetosigner/zetosignerapi" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -38,6 +39,24 @@ func mockWASMModule() []byte { )`) } +func loadTestCircuit(t *testing.T) (witness.Calculator, []byte) { + tmpDir := t.TempDir() + err := os.Mkdir(path.Join(tmpDir, "test_js"), 0755) + require.NoError(t, err) + err = os.WriteFile(path.Join(tmpDir, "test_js", "test.wasm"), testWasm, 0644) + require.NoError(t, err) + err = os.WriteFile(path.Join(tmpDir, "test.zkey"), []byte("test"), 0644) + require.NoError(t, err) + + config := &zetosignerapi.SnarkProverConfig{} + config.CircuitsDir = tmpDir + config.ProvingKeysDir = tmpDir + + circuit, provingKey, err := loadCircuit(context.Background(), "test", config) + require.NoError(t, err) + return circuit, provingKey +} + func TestLoadCircuit(t *testing.T) { tmpDir := t.TempDir() err := os.Mkdir(path.Join(tmpDir, "test_js"), 0755) @@ -57,10 +76,7 @@ func TestLoadCircuit(t *testing.T) { assert.Nil(t, circuit) assert.Equal(t, []byte{}, provingKey) - err = os.WriteFile(path.Join(tmpDir, "test_js", "test.wasm"), testWasm, 0644) - require.NoError(t, err) - circuit, provingKey, err = loadCircuit(ctx, "test", config) - require.NoError(t, err) + circuit, provingKey = loadTestCircuit(t) assert.NotNil(t, circuit) assert.Equal(t, []byte("test"), provingKey) } diff --git a/domains/zeto/internal/zeto/signer/inputs_assembler_test.go b/domains/zeto/internal/zeto/signer/inputs_assembler_test.go index 187ffb8aa..d21b25bd9 100644 --- a/domains/zeto/internal/zeto/signer/inputs_assembler_test.go +++ b/domains/zeto/internal/zeto/signer/inputs_assembler_test.go @@ -122,3 +122,44 @@ func TestAssembleInputsAnonNullifier_fail(t *testing.T) { _, err = assembleInputs_anon_nullifier(ctx, &inputs, &extras, &key) assert.EqualError(t, err, "PD210079: Failed to calculate nullifier. inputs values not inside Finite Field") } + +func TestAssembleInputsDeposit(t *testing.T) { + inputs := commonWitnessInputs{outputCommitments: []*big.Int{big.NewInt(100)}} + result := assembleInputs_deposit(&inputs) + assert.Equal(t, "100", result["outputCommitments"].([]*big.Int)[0].Text(10)) +} + +func TestAssembleInputsWithdrawNullifier(t *testing.T) { + inputs := commonWitnessInputs{ + inputCommitments: []*big.Int{big.NewInt(100)}, + inputValues: []*big.Int{big.NewInt(100)}, + inputSalts: []*big.Int{big.NewInt(200)}, + outputCommitments: []*big.Int{big.NewInt(100)}, + outputValues: []*big.Int{big.NewInt(200)}, + outputSalts: []*big.Int{big.NewInt(300)}, + } + privKey, pubKey, zkpKey := newKeypair() + key := core.KeyEntry{ + PrivateKey: privKey, + PublicKey: pubKey, + PrivateKeyForZkp: zkpKey, + } + extras := proto.ProvingRequestExtras_Nullifiers{ + Root: "123", + MerkleProofs: []*proto.MerkleProof{ + { + Nodes: []string{"1", "2", "3"}, + }, + { + Nodes: []string{"0", "0", "0"}, + }, + }, + Enabled: []bool{true, false}, + } + ctx := context.Background() + privateInputs, err := assembleInputs_withdraw_nullifier(ctx, &inputs, &extras, &key) + assert.NoError(t, err) + assert.Equal(t, "123", privateInputs["root"].(*big.Int).Text(16)) + assert.Len(t, privateInputs["nullifiers"], 1) + assert.NotEqual(t, "0", privateInputs["nullifiers"].([]*big.Int)[0].Text(10)) +} diff --git a/domains/zeto/internal/zeto/signer/snark_prover_test.go b/domains/zeto/internal/zeto/signer/snark_prover_test.go index 70562b038..e1f4e4dfc 100644 --- a/domains/zeto/internal/zeto/signer/snark_prover_test.go +++ b/domains/zeto/internal/zeto/signer/snark_prover_test.go @@ -24,6 +24,7 @@ import ( "time" "github.com/hyperledger-labs/zeto/go-sdk/pkg/crypto" + "github.com/hyperledger-labs/zeto/go-sdk/pkg/key-manager/core" "github.com/iden3/go-iden3-crypto/poseidon" "github.com/iden3/go-rapidsnark/types" "github.com/iden3/go-rapidsnark/witness/v2" @@ -522,6 +523,19 @@ func TestSerializeProofResponse(t *testing.T) { bytes, err = serializeProofResponse(constants.CIRCUIT_ANON_NULLIFIER_BATCH, &snark) assert.NoError(t, err) assert.Equal(t, 84, len(bytes)) + + bytes, err = serializeProofResponse(constants.CIRCUIT_WITHDRAW_NULLIFIER, &snark) + assert.NoError(t, err) + assert.Equal(t, 66, len(bytes)) + + snark.PubSignals = []string{ + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", + "1", "2", "3"} + bytes, err = serializeProofResponse(constants.CIRCUIT_WITHDRAW_NULLIFIER_BATCH, &snark) + assert.NoError(t, err) + assert.Equal(t, 85, len(bytes)) } func TestZKPProverInvalidAlgos(t *testing.T) { @@ -570,3 +584,65 @@ func TestGetCircuitId(t *testing.T) { circuitId = getCircuitId(inputs) assert.Equal(t, constants.CIRCUIT_ANON_ENC_BATCH, circuitId) } + +func TestCalculateWitness(t *testing.T) { + extras1 := &pb.ProvingRequestExtras_Encryption{ + EncryptionNonce: "bad number", + } + inputs := &pb.ProvingRequestCommon{ + InputCommitments: []string{"1234567890123456789012345678901234567890123456789012345678901234", "1234567890123456789012345678901234567890123456789012345678901234"}, + InputValues: []uint64{10, 20}, + InputSalts: []string{"1234567890123456789012345678901234567890123456789012345678901234", "1234567890123456789012345678901234567890123456789012345678901234"}, + InputOwner: "7cdd539f3ed6c283494f47d8481f84308a6d7043087fb6711c9f1df04e2b8025", + OutputValues: []uint64{30, 0}, + OutputSalts: []string{"1234567890123456789012345678901234567890123456789012345678901234", "1234567890123456789012345678901234567890123456789012345678901234"}, + OutputOwners: []string{"7cdd539f3ed6c283494f47d8481f84308a6d7043087fb6711c9f1df04e2b8025", "7cdd539f3ed6c283494f47d8481f84308a6d7043087fb6711c9f1df04e2b8025"}, + } + ctx := context.Background() + _, err := calculateWitness(ctx, constants.CIRCUIT_ANON_ENC, inputs, extras1, nil, nil) + assert.EqualError(t, err, "PD210099: failed to assemble private inputs for witness calculation. PD210077: Failed to parse encryption nonce") + + extras2 := &pb.ProvingRequestExtras_Nullifiers{ + Root: "123456", + MerkleProofs: []*pb.MerkleProof{ + { + Nodes: []string{"1", "2", "3"}, + }, + { + Nodes: []string{"0", "0", "0"}, + }, + }, + Enabled: []bool{true, false}, + } + privKey, ok := new(big.Int).SetString("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16) + require.True(t, ok) + keyEntry := &core.KeyEntry{ + PrivateKeyForZkp: privKey, + } + _, err = calculateWitness(ctx, constants.CIRCUIT_ANON_NULLIFIER, inputs, extras2, keyEntry, nil) + assert.EqualError(t, err, "PD210099: failed to assemble private inputs for witness calculation. PD210079: Failed to calculate nullifier. inputs values not inside Finite Field") + + inputs = &pb.ProvingRequestCommon{ + OutputValues: []uint64{30, 0}, + OutputSalts: []string{"1234567890123456789012345678901234567890123456789012345678901234", "1234567890123456789012345678901234567890123456789012345678901234"}, + OutputOwners: []string{"7cdd539f3ed6c283494f47d8481f84308a6d7043087fb6711c9f1df04e2b8025", "7cdd539f3ed6c283494f47d8481f84308a6d7043087fb6711c9f1df04e2b8025"}, + } + circuit, _ := loadTestCircuit(t) + _, err = calculateWitness(ctx, constants.CIRCUIT_DEPOSIT, inputs, nil, keyEntry, circuit) + assert.ErrorContains(t, err, "PD210100: failed to calculate the witness") + + inputs = &pb.ProvingRequestCommon{ + InputCommitments: []string{"1234567890123456789012345678901234567890123456789012345678901234", "1234567890123456789012345678901234567890123456789012345678901234"}, + InputValues: []uint64{10, 20}, + InputSalts: []string{"1234567890123456789012345678901234567890123456789012345678901234", "1234567890123456789012345678901234567890123456789012345678901234"}, + InputOwner: "7cdd539f3ed6c283494f47d8481f84308a6d7043087fb6711c9f1df04e2b8025", + OutputValues: []uint64{30, 0}, + OutputSalts: []string{"1234567890123456789012345678901234567890123456789012345678901234", "1234567890123456789012345678901234567890123456789012345678901234"}, + OutputOwners: []string{"7cdd539f3ed6c283494f47d8481f84308a6d7043087fb6711c9f1df04e2b8025", "7cdd539f3ed6c283494f47d8481f84308a6d7043087fb6711c9f1df04e2b8025"}, + } + _, err = calculateWitness(ctx, constants.CIRCUIT_WITHDRAW, inputs, nil, keyEntry, circuit) + assert.ErrorContains(t, err, "PD210100: failed to calculate the witness") + + _, err = calculateWitness(ctx, constants.CIRCUIT_WITHDRAW_NULLIFIER, inputs, extras2, keyEntry, circuit) + assert.EqualError(t, err, "PD210099: failed to assemble private inputs for witness calculation. PD210079: Failed to calculate nullifier. inputs values not inside Finite Field") +} diff --git a/domains/zeto/internal/zeto/states.go b/domains/zeto/internal/zeto/states.go index dc7635e25..ef145d304 100644 --- a/domains/zeto/internal/zeto/states.go +++ b/domains/zeto/internal/zeto/states.go @@ -184,7 +184,7 @@ func (z *Zeto) prepareOutputsForTransfer(ctx context.Context, useNullifiers bool return coins, newStates, nil } -func (z *Zeto) prepareOutputsForDeposit(ctx context.Context, useNullifiers bool, amount *tktypes.HexUint256, resolvedVerifiers []*pb.ResolvedVerifier) ([]*types.ZetoCoin, []*pb.NewState, error) { +func (z *Zeto) prepareOutputsForDeposit(ctx context.Context, useNullifiers bool, amount *tktypes.HexUint256, resolvedSender *pb.ResolvedVerifier) ([]*types.ZetoCoin, []*pb.NewState, error) { var coins []*types.ZetoCoin // the token implementation allows up to 2 output states, we will use one of them // to bear the deposit amount, and set the other to value of 0. we randomize @@ -196,7 +196,7 @@ func (z *Zeto) prepareOutputsForDeposit(ctx context.Context, useNullifiers bool, amounts[randomIdx] = amount amounts[size-randomIdx-1] = tktypes.MustParseHexUint256("0x0") for _, amt := range amounts { - resolvedRecipient := resolvedVerifiers[0] + resolvedRecipient := resolvedSender recipientKey, err := loadBabyJubKey([]byte(resolvedRecipient.Verifier)) if err != nil { return nil, nil, i18n.NewError(ctx, msgs.MsgErrorLoadOwnerPubKey, err) diff --git a/domains/zeto/internal/zeto/states_test.go b/domains/zeto/internal/zeto/states_test.go index 5370aa4ca..35c27380a 100644 --- a/domains/zeto/internal/zeto/states_test.go +++ b/domains/zeto/internal/zeto/states_test.go @@ -94,4 +94,45 @@ func TestPrepareInputs(t *testing.T) { } _, _, _, _, err = zeto.prepareInputsForTransfer(ctx, false, stateQueryContext, "Alice", []*types.TransferParamEntry{{Amount: tktypes.Uint64ToUint256(200)}}) assert.EqualError(t, err, "PD210035: Need more than maximum number (10) of coins to fulfill the transfer amount total") + + _, _, _, _, err = zeto.prepareInputsForWithdraw(ctx, false, stateQueryContext, "Alice", tktypes.Uint64ToUint256(100)) + assert.NoError(t, err) +} + +func TestPrepareOutputs(t *testing.T) { + testCallbacks := &testDomainCallbacks{ + returnFunc: func() (*prototk.FindAvailableStatesResponse, error) { + return nil, errors.New("test error") + }, + } + zeto := &Zeto{ + name: "test1", + Callbacks: testCallbacks, + coinSchema: &prototk.StateSchema{ + Id: "coin", + }, + merkleTreeRootSchema: &prototk.StateSchema{ + Id: "merkle_tree_root", + }, + merkleTreeNodeSchema: &prototk.StateSchema{ + Id: "merkle_tree_node", + }, + } + + ctx := context.Background() + sender := &prototk.ResolvedVerifier{ + Lookup: "Alice", + Verifier: "7cdd539f3ed6c283494f47d8481f84308a6d7043087fb6711c9f1df04e2b8025", + } + + _, _, err := zeto.prepareOutputsForDeposit(ctx, false, tktypes.Uint64ToUint256(100), sender) + assert.NoError(t, err) + + sender.Verifier = "bad key" + _, _, err = zeto.prepareOutputForWithdraw(ctx, tktypes.Uint64ToUint256(100), []*prototk.ResolvedVerifier{sender}) + assert.ErrorContains(t, err, "PD210037: Failed load owner public key.") + + sender.Verifier = "7cdd539f3ed6c283494f47d8481f84308a6d7043087fb6711c9f1df04e2b8025" + _, _, err = zeto.prepareOutputForWithdraw(ctx, tktypes.Uint64ToUint256(100), []*prototk.ResolvedVerifier{sender}) + assert.NoError(t, err) } diff --git a/domains/zeto/internal/zeto/zeto_test.go b/domains/zeto/internal/zeto/zeto_test.go index 208b26798..6b8a7468f 100644 --- a/domains/zeto/internal/zeto/zeto_test.go +++ b/domains/zeto/internal/zeto/zeto_test.go @@ -479,6 +479,11 @@ func TestHandleEventBatch(t *testing.T) { assert.NoError(t, err) assert.Len(t, res4.TransactionsComplete, 1) assert.Len(t, res4.NewStates, 2) + + req.Events[0].SoliditySignature = "event UTXOWithdraw(uint256 amount, uint256[] inputs, uint256 output, address indexed submitter, bytes data)" + req.Events[0].DataJson = "{\"data\":\"0x0001000030e43028afbb41d6887444f4c2b4ed6d00000000000000000000000000000000\",\"output\":\"7980718117603030807695495350922077879582656644717071592146865497574198464253\",\"submitter\":\"0x74e71b05854ee819cb9397be01c82570a178d019\"}" + _, err = z.HandleEventBatch(ctx, req) + assert.NoError(t, err) } func TestGetVerifier(t *testing.T) { @@ -650,5 +655,19 @@ func TestGetHandler(t *testing.T) { assert.NotNil(t, z.GetHandler("mint")) assert.NotNil(t, z.GetHandler("transfer")) assert.NotNil(t, z.GetHandler("lockProof")) + assert.NotNil(t, z.GetHandler("deposit")) + assert.NotNil(t, z.GetHandler("withdraw")) assert.Nil(t, z.GetHandler("bad")) } + +func TestUnimplementedMethods(t *testing.T) { + z := &Zeto{} + _, err := z.InitCall(context.Background(), nil) + assert.ErrorContains(t, err, "PD210085: Not implemented") + + _, err = z.ExecCall(context.Background(), nil) + assert.ErrorContains(t, err, "PD210085: Not implemented") + + _, err = z.BuildReceipt(context.Background(), nil) + assert.ErrorContains(t, err, "PD210102: Not implemented") +} From 5fd4e487747578d37dc64d89e43620e24f54d92a Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Thu, 5 Dec 2024 15:03:49 -0500 Subject: [PATCH 08/36] unit tests coverage Signed-off-by: Jim Zhang --- domains/zeto/build.gradle | 2 +- domains/zeto/internal/msgs/en_errors.go | 1 + .../internal/zeto/handler_transfer_test.go | 1 + .../zeto/internal/zeto/handler_withdraw.go | 6 +- .../internal/zeto/handler_withdraw_test.go | 130 ++++++++++++++++-- domains/zeto/internal/zeto/states.go | 3 +- domains/zeto/internal/zeto/states_test.go | 4 +- 7 files changed, 128 insertions(+), 19 deletions(-) diff --git a/domains/zeto/build.gradle b/domains/zeto/build.gradle index 758c828bf..20e490e4f 100644 --- a/domains/zeto/build.gradle +++ b/domains/zeto/build.gradle @@ -24,7 +24,7 @@ ext { goFilesE2E = fileTree(".") { include "integration-test/**/*.go" } - targetCoverage = 93.5 + targetCoverage = 94.5 maxCoverageBarGap = 1 coveragePackages = [ "github.com/kaleido-io/paladin/domains/zeto/internal/...", diff --git a/domains/zeto/internal/msgs/en_errors.go b/domains/zeto/internal/msgs/en_errors.go index 08cd5e789..3801c9274 100644 --- a/domains/zeto/internal/msgs/en_errors.go +++ b/domains/zeto/internal/msgs/en_errors.go @@ -145,4 +145,5 @@ var ( MsgUnknownSignPayload = ffe("PD210103", "Sign payload type '%s' not recognized") MsgNullifierGenerationFailed = ffe("PD210104", "Failed to generate nullifier for coin") MsgErrorDecodeDepositCall = ffe("PD210105", "Failed to decode the deposit call. %s") + MsgErrorDecodeWithdrawCall = ffe("PD210106", "Failed to decode the withdraw call. %s") ) diff --git a/domains/zeto/internal/zeto/handler_transfer_test.go b/domains/zeto/internal/zeto/handler_transfer_test.go index 8453befa7..295d9aeac 100644 --- a/domains/zeto/internal/zeto/handler_transfer_test.go +++ b/domains/zeto/internal/zeto/handler_transfer_test.go @@ -247,6 +247,7 @@ func TestTransferAssemble(t *testing.T) { }, nil } + h.zeto.Callbacks = testCallbacks res, err = h.Assemble(ctx, tx, req) assert.NoError(t, err) assert.Len(t, res.AssembledTransaction.OutputStates, 2) diff --git a/domains/zeto/internal/zeto/handler_withdraw.go b/domains/zeto/internal/zeto/handler_withdraw.go index c6134f50b..faa688541 100644 --- a/domains/zeto/internal/zeto/handler_withdraw.go +++ b/domains/zeto/internal/zeto/handler_withdraw.go @@ -69,7 +69,7 @@ var withdrawABI_nullifiers = &abi.Entry{ func (h *withdrawHandler) ValidateParams(ctx context.Context, config *types.DomainInstanceConfig, params string) (interface{}, error) { var withdrawParams types.WithdrawParams if err := json.Unmarshal([]byte(params), &withdrawParams); err != nil { - return nil, err + return nil, i18n.NewError(ctx, msgs.MsgErrorDecodeWithdrawCall, err) } if err := validateAmountParam(ctx, withdrawParams.Amount, 0); err != nil { @@ -106,7 +106,7 @@ func (h *withdrawHandler) Assemble(ctx context.Context, tx *types.ParsedTransact return nil, i18n.NewError(ctx, msgs.MsgErrorPrepTxInputs, err) } - outputCoin, outputState, err := h.zeto.prepareOutputForWithdraw(ctx, tktypes.MustParseHexUint256(remainder.Text(10)), req.ResolvedVerifiers) + outputCoin, outputState, err := h.zeto.prepareOutputForWithdraw(ctx, tktypes.MustParseHexUint256(remainder.Text(10)), resolvedSender) if err != nil { return nil, i18n.NewError(ctx, msgs.MsgErrorPrepTxOutputs, err) } @@ -253,7 +253,7 @@ func (h *withdrawHandler) formatProvingRequest(ctx context.Context, inputCoins [ hash, err := outputCoin.Hash(ctx) if err != nil { - return nil, i18n.NewError(ctx, msgs.MsgErrorHashInputState, err) + return nil, i18n.NewError(ctx, msgs.MsgErrorHashOutputState, err) } outputCommitment := hash.Int().Text(16) outputValueInt := outputCoin.Amount.Int().Uint64() diff --git a/domains/zeto/internal/zeto/handler_withdraw_test.go b/domains/zeto/internal/zeto/handler_withdraw_test.go index 2dee1ed92..a49661639 100644 --- a/domains/zeto/internal/zeto/handler_withdraw_test.go +++ b/domains/zeto/internal/zeto/handler_withdraw_test.go @@ -17,7 +17,7 @@ import ( ) func TestWithdrawValidateParams(t *testing.T) { - h := &depositHandler{} + h := &withdrawHandler{} ctx := context.Background() config := &types.DomainInstanceConfig{} v, err := h.ValidateParams(ctx, config, "{\"amount\":100}") @@ -25,14 +25,14 @@ func TestWithdrawValidateParams(t *testing.T) { require.Equal(t, "0x64", v.(*tktypes.HexUint256).String()) _, err = h.ValidateParams(ctx, config, "bad json") - require.ErrorContains(t, err, "PD210105: Failed to decode the deposit call.") + require.ErrorContains(t, err, "PD210106: Failed to decode the withdraw call.") _, err = h.ValidateParams(ctx, config, "{\"amount\":-100}") require.ErrorContains(t, err, "PD210027: Parameter 'amount' must be greater than 0 (index=0)") } func TestWithdrawInit(t *testing.T) { - h := depositHandler{ + h := withdrawHandler{ zeto: &Zeto{ name: "test1", }, @@ -53,7 +53,7 @@ func TestWithdrawInit(t *testing.T) { } func TestWithdrawAssemble(t *testing.T) { - h := depositHandler{ + h := withdrawHandler{ zeto: &Zeto{ name: "test1", coinSchema: &prototk.StateSchema{ @@ -71,7 +71,7 @@ func TestWithdrawAssemble(t *testing.T) { txSpec := &prototk.TransactionSpecification{ From: "Bob", ContractInfo: &prototk.ContractInfo{ - ContractAddress: "0x1234567890123456789012345678901234567890", + ContractAddress: "bad address", }, } tx := &types.ParsedTransaction{ @@ -109,16 +109,100 @@ func TestWithdrawAssemble(t *testing.T) { } h.zeto.Callbacks = testCallbacks _, err = h.Assemble(ctx, tx, req) - assert.ErrorContains(t, err, "PD210040: Failed to prepare transaction outputs. PD210037: Failed load owner public key") + assert.ErrorContains(t, err, "PD210039: Failed to prepare transaction inputs. PD210032: Failed to query the state store for available coins. test error") + + h.zeto.Callbacks = &testDomainCallbacks{ + returnFunc: func() (*prototk.FindAvailableStatesResponse, error) { + return &prototk.FindAvailableStatesResponse{ + States: []*prototk.StoredState{ + { + DataJson: "{\"salt\":\"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\",\"owner\":\"0x19d2ee6b9770a4f8d7c3b7906bc7595684509166fa42d718d1d880b62bcb7922\",\"amount\":\"0x0f\"}", + }, + }, + }, nil + }, + } + req.ResolvedVerifiers[1].Verifier = "bad key" + _, err = h.Assemble(ctx, tx, req) + require.ErrorContains(t, err, "PD210040: Failed to prepare transaction outputs. PD210037: Failed load owner public key.") req.ResolvedVerifiers[1].Verifier = "0x19d2ee6b9770a4f8d7c3b7906bc7595684509166fa42d718d1d880b62bcb7922" + _, err = h.Assemble(ctx, tx, req) + assert.ErrorContains(t, err, "PD210017: Failed to decode contract address.") + + txSpec.ContractInfo.ContractAddress = "0x1234567890123456789012345678901234567890" + _, err = h.Assemble(ctx, tx, req) + assert.ErrorContains(t, err, "PD210042: Failed to format proving request.") + + h.zeto.Callbacks = &testDomainCallbacks{ + returnFunc: func() (*prototk.FindAvailableStatesResponse, error) { + return &prototk.FindAvailableStatesResponse{ + States: []*prototk.StoredState{ + { + DataJson: "{\"salt\":\"0x042fac32983b19d76425cc54dd80e8a198f5d477c6a327cb286eb81a0c2b95ec\",\"owner\":\"0x19d2ee6b9770a4f8d7c3b7906bc7595684509166fa42d718d1d880b62bcb7922\",\"amount\":\"0x0f\"}", + }, + }, + }, nil + }, + } res, err := h.Assemble(ctx, tx, req) require.NoError(t, err) assert.Equal(t, "100", *res.AssembledTransaction.DomainData) + + tx.DomainConfig.TokenName = constants.TOKEN_ANON_NULLIFIER + tx.DomainConfig.CircuitId = constants.CIRCUIT_ANON_NULLIFIER + called := 0 + h.zeto.Callbacks = &testDomainCallbacks{ + returnFunc: func() (*prototk.FindAvailableStatesResponse, error) { + var dataJson string + if called == 0 { + dataJson = "{\"salt\":\"0x13de02d64a5736a56b2d35d2a83dd60397ba70aae6f8347629f0960d4fee5d58\",\"owner\":\"0xc1d218cf8993f940e75eabd3fee23dadc4e89cd1de479f03a61e91727959281b\",\"amount\":\"0x65\"}" + } else if called == 1 { + dataJson = "{\"rootIndex\": \"0x28025a624a1e83687e84451d04190f081d79d470f9d50a7059508476be02d401\"}" + } else { + dataJson = "{\"index\":\"bad index\",\"leftChild\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"refKey\":\"0x89ea7fc1e5e9722566083823f288a45d6dc7ef30b68094f006530dfe9f5cf90f\",\"rightChild\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"type\":\"0x02\"}" + } + called++ + return &prototk.FindAvailableStatesResponse{ + States: []*prototk.StoredState{ + { + DataJson: dataJson, + }, + }, + }, nil + }, + } + res, err = h.Assemble(ctx, tx, req) + require.ErrorContains(t, err, "PD210042: Failed to format proving request. PD210052: Failed to generate merkle proofs.") + + called = 0 + h.zeto.Callbacks = &testDomainCallbacks{ + returnFunc: func() (*prototk.FindAvailableStatesResponse, error) { + var dataJson string + if called == 0 { + dataJson = "{\"salt\":\"0x13de02d64a5736a56b2d35d2a83dd60397ba70aae6f8347629f0960d4fee5d58\",\"owner\":\"0xc1d218cf8993f940e75eabd3fee23dadc4e89cd1de479f03a61e91727959281b\",\"amount\":\"0x65\"}" + } else if called == 1 { + dataJson = "{\"rootIndex\": \"0x28025a624a1e83687e84451d04190f081d79d470f9d50a7059508476be02d401\"}" + } else { + dataJson = "{\"index\":\"0xb6025832e11338c178467dda6472d74c15aac53d0781f51681df082840e2ca25\",\"leftChild\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"refKey\":\"0x89ea7fc1e5e9722566083823f288a45d6dc7ef30b68094f006530dfe9f5cf90f\",\"rightChild\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"type\":\"0x02\"}" + } + called++ + return &prototk.FindAvailableStatesResponse{ + States: []*prototk.StoredState{ + { + DataJson: dataJson, + }, + }, + }, nil + }, + } + res, err = h.Assemble(ctx, tx, req) + require.NoError(t, err) + assert.Equal(t, "100", *res.AssembledTransaction.DomainData) } func TestWithdrawEndorse(t *testing.T) { - h := depositHandler{} + h := withdrawHandler{} ctx := context.Background() tx := &types.ParsedTransaction{ Params: tktypes.MustParseHexUint256("100"), @@ -137,7 +221,7 @@ func TestWithdrawPrepare(t *testing.T) { z := &Zeto{ name: "test1", } - h := depositHandler{ + h := withdrawHandler{ zeto: z, } txSpec := &prototk.TransactionSpecification{ @@ -206,11 +290,23 @@ func TestWithdrawPrepare(t *testing.T) { assert.NoError(t, err) req.AttestationResult[0].Payload = payload _, err = h.Prepare(ctx, tx, req) + assert.ErrorContains(t, err, "PD210045: Failed to parse input states.") + + req.InputStates[0].StateDataJson = "{\"salt\":\"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\",\"owner\":\"0x7cdd539f3ed6c283494f47d8481f84308a6d7043087fb6711c9f1df04e2b8025\",\"amount\":\"0x0f\",\"hash\":\"0x303eb034d22aacc5dff09647928d757017a35e64e696d48609a250a6505e5d5f\"}" + _, err = h.Prepare(ctx, tx, req) + assert.ErrorContains(t, err, "PD210046: Failed to create Poseidon hash for an input coin.") + + req.InputStates[0].StateDataJson = "{\"salt\":\"0x042fac32983b19d76425cc54dd80e8a198f5d477c6a327cb286eb81a0c2b95ec\",\"owner\":\"0x7cdd539f3ed6c283494f47d8481f84308a6d7043087fb6711c9f1df04e2b8025\",\"amount\":\"0x0f\",\"hash\":\"0x303eb034d22aacc5dff09647928d757017a35e64e696d48609a250a6505e5d5f\"}" + _, err = h.Prepare(ctx, tx, req) assert.ErrorContains(t, err, "PD210047: Failed to parse output states.") + req.OutputStates[0].StateDataJson = "{\"salt\":\"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\",\"owner\":\"0x7cdd539f3ed6c283494f47d8481f84308a6d7043087fb6711c9f1df04e2b8025\",\"amount\":\"0x0f\",\"hash\":\"0x303eb034d22aacc5dff09647928d757017a35e64e696d48609a250a6505e5d5f\"}" + _, err = h.Prepare(ctx, tx, req) + assert.ErrorContains(t, err, "PD210048: Failed to create Poseidon hash for an output coin.") + req.OutputStates[0].StateDataJson = "{\"salt\":\"0x042fac32983b19d76425cc54dd80e8a198f5d477c6a327cb286eb81a0c2b95ec\",\"owner\":\"0x7cdd539f3ed6c283494f47d8481f84308a6d7043087fb6711c9f1df04e2b8025\",\"amount\":\"0x0f\",\"hash\":\"0x303eb034d22aacc5dff09647928d757017a35e64e696d48609a250a6505e5d5f\"}" _, err = h.Prepare(ctx, tx, req) - assert.ErrorContains(t, err, "PD210049: Failed to encode transaction data. PD210028: Failed to parse transaction id. PD020007: Invalid hex:") + assert.ErrorContains(t, err, "PD210049: Failed to encode transaction data. PD210028: Failed to parse transaction id.") txSpec.TransactionId = "0x1234567890123456789012345678901234567890123456789012345678901234" z.config = &types.DomainFactoryConfig{ @@ -223,8 +319,20 @@ func TestWithdrawPrepare(t *testing.T) { Name: constants.TOKEN_ANON_ENC, }, } - res, err := h.Prepare(ctx, tx, req) assert.NoError(t, err) - assert.Equal(t, "{\"amount\":\"100\",\"data\":\"0x000100001234567890123456789012345678901234567890123456789012345678901234\",\"outputs\":[\"0x303eb034d22aacc5dff09647928d757017a35e64e696d48609a250a6505e5d5f\",\"0\"],\"proof\":{\"pA\":[\"0x1234567890\",\"0x1234567890\"],\"pB\":[[\"0x1234567890\",\"0x1234567890\"],[\"0x1234567890\",\"0x1234567890\"]],\"pC\":[\"0x1234567890\",\"0x1234567890\"]}}", res.Transaction.ParamsJson) + assert.Equal(t, "{\"amount\":\"100\",\"data\":\"0x000100001234567890123456789012345678901234567890123456789012345678901234\",\"inputs\":[\"0x303eb034d22aacc5dff09647928d757017a35e64e696d48609a250a6505e5d5f\",\"0\"],\"output\":\"0x303eb034d22aacc5dff09647928d757017a35e64e696d48609a250a6505e5d5f\",\"proof\":{\"pA\":[\"0x1234567890\",\"0x1234567890\"],\"pB\":[[\"0x1234567890\",\"0x1234567890\"],[\"0x1234567890\",\"0x1234567890\"]],\"pC\":[\"0x1234567890\",\"0x1234567890\"]}}", res.Transaction.ParamsJson) + + tx.DomainConfig.TokenName = constants.TOKEN_ANON_NULLIFIER + tx.DomainConfig.CircuitId = constants.CIRCUIT_ANON_NULLIFIER + proofReq.PublicInputs = map[string]string{ + "nullifiers": "0x1234567890,0x1234567890", + "root": "0x1234567890", + } + payload, err = proto.Marshal(&proofReq) + require.NoError(t, err) + req.AttestationResult[0].Payload = payload + res, err = h.Prepare(ctx, tx, req) + assert.NoError(t, err) + assert.Equal(t, "{\"amount\":\"100\",\"data\":\"0x000100001234567890123456789012345678901234567890123456789012345678901234\",\"nullifiers\":[\"0x1234567890\",\"0x1234567890\"],\"output\":\"0x303eb034d22aacc5dff09647928d757017a35e64e696d48609a250a6505e5d5f\",\"proof\":{\"pA\":[\"0x1234567890\",\"0x1234567890\"],\"pB\":[[\"0x1234567890\",\"0x1234567890\"],[\"0x1234567890\",\"0x1234567890\"]],\"pC\":[\"0x1234567890\",\"0x1234567890\"]},\"root\":\"0x1234567890\"}", res.Transaction.ParamsJson) } diff --git a/domains/zeto/internal/zeto/states.go b/domains/zeto/internal/zeto/states.go index ef145d304..b7d49297c 100644 --- a/domains/zeto/internal/zeto/states.go +++ b/domains/zeto/internal/zeto/states.go @@ -220,8 +220,7 @@ func (z *Zeto) prepareOutputsForDeposit(ctx context.Context, useNullifiers bool, return coins, newStates, nil } -func (z *Zeto) prepareOutputForWithdraw(ctx context.Context, amount *tktypes.HexUint256, resolvedVerifiers []*pb.ResolvedVerifier) (*types.ZetoCoin, *pb.NewState, error) { - resolvedRecipient := resolvedVerifiers[0] +func (z *Zeto) prepareOutputForWithdraw(ctx context.Context, amount *tktypes.HexUint256, resolvedRecipient *pb.ResolvedVerifier) (*types.ZetoCoin, *pb.NewState, error) { recipientKey, err := loadBabyJubKey([]byte(resolvedRecipient.Verifier)) if err != nil { return nil, nil, i18n.NewError(ctx, msgs.MsgErrorLoadOwnerPubKey, err) diff --git a/domains/zeto/internal/zeto/states_test.go b/domains/zeto/internal/zeto/states_test.go index 35c27380a..7703706fb 100644 --- a/domains/zeto/internal/zeto/states_test.go +++ b/domains/zeto/internal/zeto/states_test.go @@ -129,10 +129,10 @@ func TestPrepareOutputs(t *testing.T) { assert.NoError(t, err) sender.Verifier = "bad key" - _, _, err = zeto.prepareOutputForWithdraw(ctx, tktypes.Uint64ToUint256(100), []*prototk.ResolvedVerifier{sender}) + _, _, err = zeto.prepareOutputForWithdraw(ctx, tktypes.Uint64ToUint256(100), sender) assert.ErrorContains(t, err, "PD210037: Failed load owner public key.") sender.Verifier = "7cdd539f3ed6c283494f47d8481f84308a6d7043087fb6711c9f1df04e2b8025" - _, _, err = zeto.prepareOutputForWithdraw(ctx, tktypes.Uint64ToUint256(100), []*prototk.ResolvedVerifier{sender}) + _, _, err = zeto.prepareOutputForWithdraw(ctx, tktypes.Uint64ToUint256(100), sender) assert.NoError(t, err) } From 928228fcdd032a91893e3a9ca9951834f1cb6dd1 Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Thu, 5 Dec 2024 15:05:23 -0500 Subject: [PATCH 09/36] update zeto example Signed-off-by: Jim Zhang --- example/zeto/.gitignore | 1 + example/zeto/build.gradle | 11 + example/zeto/src/abis/SampleERC20.json | 422 +++++++++++++++++++++++++ example/zeto/src/index.ts | 42 ++- go.work.sum | 8 + sdk/typescript/src/domains/zeto.ts | 30 ++ 6 files changed, 513 insertions(+), 1 deletion(-) create mode 100644 example/zeto/src/abis/SampleERC20.json diff --git a/example/zeto/.gitignore b/example/zeto/.gitignore index b38db2f29..4c79bef56 100644 --- a/example/zeto/.gitignore +++ b/example/zeto/.gitignore @@ -1,2 +1,3 @@ node_modules/ build/ +src/abis/*.json \ No newline at end of file diff --git a/example/zeto/build.gradle b/example/zeto/build.gradle index 890da0296..7314f0f34 100644 --- a/example/zeto/build.gradle +++ b/example/zeto/build.gradle @@ -29,6 +29,17 @@ dependencies { buildSDK project(path: ":sdk:typescript", configuration: "buildSDK") } +task copyContracts(type: Copy, dependsOn: [":domains:zeto:extractZetoArtifacts"]) { + from fileTree("../../domains/zeto/zkp/artifacts/contracts") { + include '**/SampleERC20.json' + } + into './src/abis' + + // Flatten all paths into the destination folder + eachFile { path = name } + includeEmptyDirs = false +} + task install(type: Exec) { executable 'npm' args 'install' diff --git a/example/zeto/src/abis/SampleERC20.json b/example/zeto/src/abis/SampleERC20.json new file mode 100644 index 000000000..969b87b8f --- /dev/null +++ b/example/zeto/src/abis/SampleERC20.json @@ -0,0 +1,422 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "SampleERC20", + "sourceName": "contracts/erc20.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "initialOwner", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "allowance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "needed", + "type": "uint256" + } + ], + "name": "ERC20InsufficientAllowance", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "needed", + "type": "uint256" + } + ], + "name": "ERC20InsufficientBalance", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "approver", + "type": "address" + } + ], + "name": "ERC20InvalidApprover", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + } + ], + "name": "ERC20InvalidReceiver", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "ERC20InvalidSender", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "ERC20InvalidSpender", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "OwnableInvalidOwner", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "OwnableUnauthorizedAccount", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x608060405234801561001057600080fd5b50604051611b2f380380611b2f83398181016040528101906100329190610539565b806040518060400160405280601281526020017f53616d706c6520455243323020746f6b656e00000000000000000000000000008152506040518060400160405280600b81526020017f53616d706c65455243323000000000000000000000000000000000000000000081525081600390816100ae91906107b6565b5080600490816100be91906107b6565b505050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16036101335760006040517f1e4fbdf700000000000000000000000000000000000000000000000000000000815260040161012a9190610897565b60405180910390fd5b6101428161016360201b60201c565b5061015d3369d3c21bcecceda100000061022960201b60201c565b50610976565b6000600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905081600560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361029b5760006040517fec442f050000000000000000000000000000000000000000000000000000000081526004016102929190610897565b60405180910390fd5b6102ad600083836102b160201b60201c565b5050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036103035780600260008282546102f791906108e1565b925050819055506103d6565b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490508181101561038f578381836040517fe450d38c00000000000000000000000000000000000000000000000000000000815260040161038693929190610924565b60405180910390fd5b8181036000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550505b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361041f578060026000828254039250508190555061046c565b806000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055505b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040516104c9919061095b565b60405180910390a3505050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610506826104db565b9050919050565b610516816104fb565b811461052157600080fd5b50565b6000815190506105338161050d565b92915050565b60006020828403121561054f5761054e6104d6565b5b600061055d84828501610524565b91505092915050565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806105e757607f821691505b6020821081036105fa576105f96105a0565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b6000600883026106627fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82610625565b61066c8683610625565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b60006106b36106ae6106a984610684565b61068e565b610684565b9050919050565b6000819050919050565b6106cd83610698565b6106e16106d9826106ba565b848454610632565b825550505050565b600090565b6106f66106e9565b6107018184846106c4565b505050565b5b818110156107255761071a6000826106ee565b600181019050610707565b5050565b601f82111561076a5761073b81610600565b61074484610615565b81016020851015610753578190505b61076761075f85610615565b830182610706565b50505b505050565b600082821c905092915050565b600061078d6000198460080261076f565b1980831691505092915050565b60006107a6838361077c565b9150826002028217905092915050565b6107bf82610566565b67ffffffffffffffff8111156107d8576107d7610571565b5b6107e282546105cf565b6107ed828285610729565b600060209050601f831160018114610820576000841561080e578287015190505b610818858261079a565b865550610880565b601f19841661082e86610600565b60005b8281101561085657848901518255600182019150602085019450602081019050610831565b86831015610873578489015161086f601f89168261077c565b8355505b6001600288020188555050505b505050505050565b610891816104fb565b82525050565b60006020820190506108ac6000830184610888565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006108ec82610684565b91506108f783610684565b925082820190508082111561090f5761090e6108b2565b5b92915050565b61091e81610684565b82525050565b60006060820190506109396000830186610888565b6109466020830185610915565b6109536040830184610915565b949350505050565b60006020820190506109706000830184610915565b92915050565b6111aa806109856000396000f3fe608060405234801561001057600080fd5b50600436106100df5760003560e01c806370a082311161008c57806395d89b411161006657806395d89b4114610212578063a9059cbb14610230578063dd62ed3e14610260578063f2fde38b14610290576100df565b806370a08231146101ba578063715018a6146101ea5780638da5cb5b146101f4576100df565b806323b872dd116100bd57806323b872dd14610150578063313ce5671461018057806340c10f191461019e576100df565b806306fdde03146100e4578063095ea7b31461010257806318160ddd14610132575b600080fd5b6100ec6102ac565b6040516100f99190610dfe565b60405180910390f35b61011c60048036038101906101179190610eb9565b61033e565b6040516101299190610f14565b60405180910390f35b61013a610361565b6040516101479190610f3e565b60405180910390f35b61016a60048036038101906101659190610f59565b61036b565b6040516101779190610f14565b60405180910390f35b61018861039a565b6040516101959190610fc8565b60405180910390f35b6101b860048036038101906101b39190610eb9565b6103a3565b005b6101d460048036038101906101cf9190610fe3565b6103b9565b6040516101e19190610f3e565b60405180910390f35b6101f2610401565b005b6101fc610415565b604051610209919061101f565b60405180910390f35b61021a61043f565b6040516102279190610dfe565b60405180910390f35b61024a60048036038101906102459190610eb9565b6104d1565b6040516102579190610f14565b60405180910390f35b61027a6004803603810190610275919061103a565b6104f4565b6040516102879190610f3e565b60405180910390f35b6102aa60048036038101906102a59190610fe3565b61057b565b005b6060600380546102bb906110a9565b80601f01602080910402602001604051908101604052809291908181526020018280546102e7906110a9565b80156103345780601f1061030957610100808354040283529160200191610334565b820191906000526020600020905b81548152906001019060200180831161031757829003601f168201915b5050505050905090565b600080610349610601565b9050610356818585610609565b600191505092915050565b6000600254905090565b600080610376610601565b905061038385828561061b565b61038e8585856106af565b60019150509392505050565b60006012905090565b6103ab6107a3565b6103b5828261082a565b5050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b6104096107a3565b61041360006108ac565b565b6000600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60606004805461044e906110a9565b80601f016020809104026020016040519081016040528092919081815260200182805461047a906110a9565b80156104c75780601f1061049c576101008083540402835291602001916104c7565b820191906000526020600020905b8154815290600101906020018083116104aa57829003601f168201915b5050505050905090565b6000806104dc610601565b90506104e98185856106af565b600191505092915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b6105836107a3565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16036105f55760006040517f1e4fbdf70000000000000000000000000000000000000000000000000000000081526004016105ec919061101f565b60405180910390fd5b6105fe816108ac565b50565b600033905090565b6106168383836001610972565b505050565b600061062784846104f4565b90507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146106a95781811015610699578281836040517ffb8f41b2000000000000000000000000000000000000000000000000000000008152600401610690939291906110da565b60405180910390fd5b6106a884848484036000610972565b5b50505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036107215760006040517f96c6fd1e000000000000000000000000000000000000000000000000000000008152600401610718919061101f565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036107935760006040517fec442f0500000000000000000000000000000000000000000000000000000000815260040161078a919061101f565b60405180910390fd5b61079e838383610b49565b505050565b6107ab610601565b73ffffffffffffffffffffffffffffffffffffffff166107c9610415565b73ffffffffffffffffffffffffffffffffffffffff1614610828576107ec610601565b6040517f118cdaa700000000000000000000000000000000000000000000000000000000815260040161081f919061101f565b60405180910390fd5b565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361089c5760006040517fec442f05000000000000000000000000000000000000000000000000000000008152600401610893919061101f565b60405180910390fd5b6108a860008383610b49565b5050565b6000600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905081600560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16036109e45760006040517fe602df050000000000000000000000000000000000000000000000000000000081526004016109db919061101f565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603610a565760006040517f94280d62000000000000000000000000000000000000000000000000000000008152600401610a4d919061101f565b60405180910390fd5b81600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508015610b43578273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92584604051610b3a9190610f3e565b60405180910390a35b50505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603610b9b578060026000828254610b8f9190611140565b92505081905550610c6e565b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905081811015610c27578381836040517fe450d38c000000000000000000000000000000000000000000000000000000008152600401610c1e939291906110da565b60405180910390fd5b8181036000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550505b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610cb75780600260008282540392505081905550610d04565b806000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055505b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051610d619190610f3e565b60405180910390a3505050565b600081519050919050565b600082825260208201905092915050565b60005b83811015610da8578082015181840152602081019050610d8d565b60008484015250505050565b6000601f19601f8301169050919050565b6000610dd082610d6e565b610dda8185610d79565b9350610dea818560208601610d8a565b610df381610db4565b840191505092915050565b60006020820190508181036000830152610e188184610dc5565b905092915050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610e5082610e25565b9050919050565b610e6081610e45565b8114610e6b57600080fd5b50565b600081359050610e7d81610e57565b92915050565b6000819050919050565b610e9681610e83565b8114610ea157600080fd5b50565b600081359050610eb381610e8d565b92915050565b60008060408385031215610ed057610ecf610e20565b5b6000610ede85828601610e6e565b9250506020610eef85828601610ea4565b9150509250929050565b60008115159050919050565b610f0e81610ef9565b82525050565b6000602082019050610f296000830184610f05565b92915050565b610f3881610e83565b82525050565b6000602082019050610f536000830184610f2f565b92915050565b600080600060608486031215610f7257610f71610e20565b5b6000610f8086828701610e6e565b9350506020610f9186828701610e6e565b9250506040610fa286828701610ea4565b9150509250925092565b600060ff82169050919050565b610fc281610fac565b82525050565b6000602082019050610fdd6000830184610fb9565b92915050565b600060208284031215610ff957610ff8610e20565b5b600061100784828501610e6e565b91505092915050565b61101981610e45565b82525050565b60006020820190506110346000830184611010565b92915050565b6000806040838503121561105157611050610e20565b5b600061105f85828601610e6e565b925050602061107085828601610e6e565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806110c157607f821691505b6020821081036110d4576110d361107a565b5b50919050565b60006060820190506110ef6000830186611010565b6110fc6020830185610f2f565b6111096040830184610f2f565b949350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061114b82610e83565b915061115683610e83565b925082820190508082111561116e5761116d611111565b5b9291505056fea264697066735822122087fee3dee55412e98926c508cd3f02869c1f308399901f683e021879b2aed14264736f6c634300081b0033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100df5760003560e01c806370a082311161008c57806395d89b411161006657806395d89b4114610212578063a9059cbb14610230578063dd62ed3e14610260578063f2fde38b14610290576100df565b806370a08231146101ba578063715018a6146101ea5780638da5cb5b146101f4576100df565b806323b872dd116100bd57806323b872dd14610150578063313ce5671461018057806340c10f191461019e576100df565b806306fdde03146100e4578063095ea7b31461010257806318160ddd14610132575b600080fd5b6100ec6102ac565b6040516100f99190610dfe565b60405180910390f35b61011c60048036038101906101179190610eb9565b61033e565b6040516101299190610f14565b60405180910390f35b61013a610361565b6040516101479190610f3e565b60405180910390f35b61016a60048036038101906101659190610f59565b61036b565b6040516101779190610f14565b60405180910390f35b61018861039a565b6040516101959190610fc8565b60405180910390f35b6101b860048036038101906101b39190610eb9565b6103a3565b005b6101d460048036038101906101cf9190610fe3565b6103b9565b6040516101e19190610f3e565b60405180910390f35b6101f2610401565b005b6101fc610415565b604051610209919061101f565b60405180910390f35b61021a61043f565b6040516102279190610dfe565b60405180910390f35b61024a60048036038101906102459190610eb9565b6104d1565b6040516102579190610f14565b60405180910390f35b61027a6004803603810190610275919061103a565b6104f4565b6040516102879190610f3e565b60405180910390f35b6102aa60048036038101906102a59190610fe3565b61057b565b005b6060600380546102bb906110a9565b80601f01602080910402602001604051908101604052809291908181526020018280546102e7906110a9565b80156103345780601f1061030957610100808354040283529160200191610334565b820191906000526020600020905b81548152906001019060200180831161031757829003601f168201915b5050505050905090565b600080610349610601565b9050610356818585610609565b600191505092915050565b6000600254905090565b600080610376610601565b905061038385828561061b565b61038e8585856106af565b60019150509392505050565b60006012905090565b6103ab6107a3565b6103b5828261082a565b5050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b6104096107a3565b61041360006108ac565b565b6000600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60606004805461044e906110a9565b80601f016020809104026020016040519081016040528092919081815260200182805461047a906110a9565b80156104c75780601f1061049c576101008083540402835291602001916104c7565b820191906000526020600020905b8154815290600101906020018083116104aa57829003601f168201915b5050505050905090565b6000806104dc610601565b90506104e98185856106af565b600191505092915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b6105836107a3565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16036105f55760006040517f1e4fbdf70000000000000000000000000000000000000000000000000000000081526004016105ec919061101f565b60405180910390fd5b6105fe816108ac565b50565b600033905090565b6106168383836001610972565b505050565b600061062784846104f4565b90507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146106a95781811015610699578281836040517ffb8f41b2000000000000000000000000000000000000000000000000000000008152600401610690939291906110da565b60405180910390fd5b6106a884848484036000610972565b5b50505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036107215760006040517f96c6fd1e000000000000000000000000000000000000000000000000000000008152600401610718919061101f565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036107935760006040517fec442f0500000000000000000000000000000000000000000000000000000000815260040161078a919061101f565b60405180910390fd5b61079e838383610b49565b505050565b6107ab610601565b73ffffffffffffffffffffffffffffffffffffffff166107c9610415565b73ffffffffffffffffffffffffffffffffffffffff1614610828576107ec610601565b6040517f118cdaa700000000000000000000000000000000000000000000000000000000815260040161081f919061101f565b60405180910390fd5b565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361089c5760006040517fec442f05000000000000000000000000000000000000000000000000000000008152600401610893919061101f565b60405180910390fd5b6108a860008383610b49565b5050565b6000600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905081600560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16036109e45760006040517fe602df050000000000000000000000000000000000000000000000000000000081526004016109db919061101f565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603610a565760006040517f94280d62000000000000000000000000000000000000000000000000000000008152600401610a4d919061101f565b60405180910390fd5b81600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508015610b43578273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92584604051610b3a9190610f3e565b60405180910390a35b50505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603610b9b578060026000828254610b8f9190611140565b92505081905550610c6e565b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905081811015610c27578381836040517fe450d38c000000000000000000000000000000000000000000000000000000008152600401610c1e939291906110da565b60405180910390fd5b8181036000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550505b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610cb75780600260008282540392505081905550610d04565b806000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055505b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051610d619190610f3e565b60405180910390a3505050565b600081519050919050565b600082825260208201905092915050565b60005b83811015610da8578082015181840152602081019050610d8d565b60008484015250505050565b6000601f19601f8301169050919050565b6000610dd082610d6e565b610dda8185610d79565b9350610dea818560208601610d8a565b610df381610db4565b840191505092915050565b60006020820190508181036000830152610e188184610dc5565b905092915050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610e5082610e25565b9050919050565b610e6081610e45565b8114610e6b57600080fd5b50565b600081359050610e7d81610e57565b92915050565b6000819050919050565b610e9681610e83565b8114610ea157600080fd5b50565b600081359050610eb381610e8d565b92915050565b60008060408385031215610ed057610ecf610e20565b5b6000610ede85828601610e6e565b9250506020610eef85828601610ea4565b9150509250929050565b60008115159050919050565b610f0e81610ef9565b82525050565b6000602082019050610f296000830184610f05565b92915050565b610f3881610e83565b82525050565b6000602082019050610f536000830184610f2f565b92915050565b600080600060608486031215610f7257610f71610e20565b5b6000610f8086828701610e6e565b9350506020610f9186828701610e6e565b9250506040610fa286828701610ea4565b9150509250925092565b600060ff82169050919050565b610fc281610fac565b82525050565b6000602082019050610fdd6000830184610fb9565b92915050565b600060208284031215610ff957610ff8610e20565b5b600061100784828501610e6e565b91505092915050565b61101981610e45565b82525050565b60006020820190506110346000830184611010565b92915050565b6000806040838503121561105157611050610e20565b5b600061105f85828601610e6e565b925050602061107085828601610e6e565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806110c157607f821691505b6020821081036110d4576110d361107a565b5b50919050565b60006060820190506110ef6000830186611010565b6110fc6020830185610f2f565b6111096040830184610f2f565b949350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061114b82610e83565b915061115683610e83565b925082820190508082111561116e5761116d611111565b5b9291505056fea264697066735822122087fee3dee55412e98926c508cd3f02869c1f308399901f683e021879b2aed14264736f6c634300081b0033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/example/zeto/src/index.ts b/example/zeto/src/index.ts index b9eb1c0d8..194f99bc7 100644 --- a/example/zeto/src/index.ts +++ b/example/zeto/src/index.ts @@ -5,6 +5,7 @@ import PaladinClient, { TransactionType, Verifiers, } from "paladin-sdk"; +import erc20Abi from "./abis/SampleERC20.json"; const logger = console; @@ -33,7 +34,46 @@ async function main() { logger.error("Failed!"); return; } - logger.log(`Success! address: ${zetoCBDC.address}`); + logger.log(`Success! Zeto deployed at: ${zetoCBDC.address}`); + + logger.log("Deploying ERC20 token..."); + const cbdcIssuerAddress = await paladin1.resolveVerifier( + cbdcIssuer, + Algorithms.ECDSA_SECP256K1, + Verifiers.ETH_ADDRESS + ); + + const txId = await paladin3.sendTransaction({ + type: TransactionType.PUBLIC, + from: cbdcIssuer, + data: { + "initialOwner": cbdcIssuerAddress, + }, + function: "", + abi: erc20Abi.abi, + bytecode: erc20Abi.bytecode, + }); + if (txId === undefined) { + logger.error("Failed!"); + return; + } + const result1 = await paladin3.pollForReceipt(txId, 5000); + if (result1 === undefined) { + logger.error("Failed!"); + return; + } + const erc20Address = result1.contractAddress; + logger.log(`Success! ERC20 deployed at: ${erc20Address}`); + + logger.log("Setting ERC20 to the Zeto token contract ..."); + const result2 = await zetoCBDC.setERC20(cbdcIssuer, { + _erc20: erc20Address as string + }); + if (result1 === undefined) { + logger.error("Failed!"); + return; + } + logger.log(`Success! ERC20 configured on the Zeto contract`); // Issue some cash logger.log("Issuing CBDC to bank1 and bank2 ..."); diff --git a/go.work.sum b/go.work.sum index e20278034..91b1419da 100644 --- a/go.work.sum +++ b/go.work.sum @@ -503,6 +503,9 @@ github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47 github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20240312041847-bd984b5ce465/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= +github.com/iden3/go-iden3-crypto v0.0.15/go.mod h1:dLpM4vEPJ3nDHzhWFXDjzkn1qHoBeOT/3UEhXsEsP3E= +github.com/iden3/go-rapidsnark/prover v0.0.10/go.mod h1:wgDsmKOGCuWGtgVtuW9ARWNguNr4NJAIyg2G7+uTax0= +github.com/iden3/go-rapidsnark/types v0.0.2/go.mod h1:ApgcaUxKIgSRA6fAeFxK7p+lgXXfG4oA2HN5DhFlfF4= github.com/iden3/go-rapidsnark/witness/wasmer v0.0.0-20230524142950-0986cf057d4e/go.mod h1:WUtPVKXrhfZHJXavwId2+8J/fKMHQ92N0MZDxt8sfEA= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= @@ -596,6 +599,7 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/ktrysmt/go-bitbucket v0.6.4/go.mod h1:9u0v3hsd2rqCHRIpbir1oP7F58uo5dq19sBYvuMoyQ4= @@ -776,6 +780,7 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.5.1/go.mod h1:/iHQpkQwBD6DLUmQ4pE+s1TXdob1mORJ4/UFdrifcy0= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= @@ -1321,6 +1326,7 @@ google.golang.org/grpc v1.62.0/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJai google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0= +google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= @@ -1335,10 +1341,12 @@ google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHh google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/sdk/typescript/src/domains/zeto.ts b/sdk/typescript/src/domains/zeto.ts index 2aedc9b64..24700b38a 100644 --- a/sdk/typescript/src/domains/zeto.ts +++ b/sdk/typescript/src/domains/zeto.ts @@ -56,6 +56,20 @@ const zetoPrivateAbi = [ }, ]; +const zetoPublicAbi = [{ + "inputs": [ + { + "internalType": "contract IERC20", + "name": "_erc20", + "type": "address" + } + ], + "name": "setERC20", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" +}]; + export const zetoConstructorABI = { type: "constructor", inputs: [{ name: "tokenName", type: "string" }], @@ -73,6 +87,10 @@ export interface ZetoTransferParams { transfers: ZetoTransfer[]; } +export interface ZetoSetERC20Params { + _erc20: string; +} + export interface ZetoTransfer { to: string; amount: string | number; @@ -156,4 +174,16 @@ export class ZetoInstance { }); return this.paladin.pollForReceipt(txID, this.options.pollTimeout); } + + async setERC20(from: string, data: ZetoSetERC20Params) { + const txID = await this.paladin.sendTransaction({ + type: TransactionType.PUBLIC, + abi: zetoPublicAbi, + function: "setERC20", + to: this.address, + from, + data, + }); + return this.paladin.pollForReceipt(txID, DEFAULT_POLL_TIMEOUT); + } } From 0e400b4c94b895734f6cbc7cbcf8973af36c2b8c Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Thu, 5 Dec 2024 15:23:56 -0500 Subject: [PATCH 10/36] Revert "update zeto example" This reverts commit 928228fcdd032a91893e3a9ca9951834f1cb6dd1. Signed-off-by: Jim Zhang --- example/zeto/.gitignore | 1 - example/zeto/build.gradle | 11 - example/zeto/src/abis/SampleERC20.json | 422 ------------------------- example/zeto/src/index.ts | 42 +-- go.work.sum | 8 - sdk/typescript/src/domains/zeto.ts | 30 -- 6 files changed, 1 insertion(+), 513 deletions(-) delete mode 100644 example/zeto/src/abis/SampleERC20.json diff --git a/example/zeto/.gitignore b/example/zeto/.gitignore index 4c79bef56..b38db2f29 100644 --- a/example/zeto/.gitignore +++ b/example/zeto/.gitignore @@ -1,3 +1,2 @@ node_modules/ build/ -src/abis/*.json \ No newline at end of file diff --git a/example/zeto/build.gradle b/example/zeto/build.gradle index 7314f0f34..890da0296 100644 --- a/example/zeto/build.gradle +++ b/example/zeto/build.gradle @@ -29,17 +29,6 @@ dependencies { buildSDK project(path: ":sdk:typescript", configuration: "buildSDK") } -task copyContracts(type: Copy, dependsOn: [":domains:zeto:extractZetoArtifacts"]) { - from fileTree("../../domains/zeto/zkp/artifacts/contracts") { - include '**/SampleERC20.json' - } - into './src/abis' - - // Flatten all paths into the destination folder - eachFile { path = name } - includeEmptyDirs = false -} - task install(type: Exec) { executable 'npm' args 'install' diff --git a/example/zeto/src/abis/SampleERC20.json b/example/zeto/src/abis/SampleERC20.json deleted file mode 100644 index 969b87b8f..000000000 --- a/example/zeto/src/abis/SampleERC20.json +++ /dev/null @@ -1,422 +0,0 @@ -{ - "_format": "hh-sol-artifact-1", - "contractName": "SampleERC20", - "sourceName": "contracts/erc20.sol", - "abi": [ - { - "inputs": [ - { - "internalType": "address", - "name": "initialOwner", - "type": "address" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "internalType": "uint256", - "name": "allowance", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "needed", - "type": "uint256" - } - ], - "name": "ERC20InsufficientAllowance", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "internalType": "uint256", - "name": "balance", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "needed", - "type": "uint256" - } - ], - "name": "ERC20InsufficientBalance", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "approver", - "type": "address" - } - ], - "name": "ERC20InvalidApprover", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "receiver", - "type": "address" - } - ], - "name": "ERC20InvalidReceiver", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "sender", - "type": "address" - } - ], - "name": "ERC20InvalidSender", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "spender", - "type": "address" - } - ], - "name": "ERC20InvalidSpender", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "owner", - "type": "address" - } - ], - "name": "OwnableInvalidOwner", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "OwnableUnauthorizedAccount", - "type": "error" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "Approval", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "previousOwner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "OwnershipTransferred", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "Transfer", - "type": "event" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "internalType": "address", - "name": "spender", - "type": "address" - } - ], - "name": "allowance", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "approve", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "balanceOf", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "decimals", - "outputs": [ - { - "internalType": "uint8", - "name": "", - "type": "uint8" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "mint", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "name", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "owner", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "renounceOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "symbol", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "totalSupply", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "transfer", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "transferFrom", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "transferOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - } - ], - "bytecode": "0x608060405234801561001057600080fd5b50604051611b2f380380611b2f83398181016040528101906100329190610539565b806040518060400160405280601281526020017f53616d706c6520455243323020746f6b656e00000000000000000000000000008152506040518060400160405280600b81526020017f53616d706c65455243323000000000000000000000000000000000000000000081525081600390816100ae91906107b6565b5080600490816100be91906107b6565b505050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16036101335760006040517f1e4fbdf700000000000000000000000000000000000000000000000000000000815260040161012a9190610897565b60405180910390fd5b6101428161016360201b60201c565b5061015d3369d3c21bcecceda100000061022960201b60201c565b50610976565b6000600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905081600560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361029b5760006040517fec442f050000000000000000000000000000000000000000000000000000000081526004016102929190610897565b60405180910390fd5b6102ad600083836102b160201b60201c565b5050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036103035780600260008282546102f791906108e1565b925050819055506103d6565b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490508181101561038f578381836040517fe450d38c00000000000000000000000000000000000000000000000000000000815260040161038693929190610924565b60405180910390fd5b8181036000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550505b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361041f578060026000828254039250508190555061046c565b806000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055505b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040516104c9919061095b565b60405180910390a3505050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610506826104db565b9050919050565b610516816104fb565b811461052157600080fd5b50565b6000815190506105338161050d565b92915050565b60006020828403121561054f5761054e6104d6565b5b600061055d84828501610524565b91505092915050565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806105e757607f821691505b6020821081036105fa576105f96105a0565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b6000600883026106627fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82610625565b61066c8683610625565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b60006106b36106ae6106a984610684565b61068e565b610684565b9050919050565b6000819050919050565b6106cd83610698565b6106e16106d9826106ba565b848454610632565b825550505050565b600090565b6106f66106e9565b6107018184846106c4565b505050565b5b818110156107255761071a6000826106ee565b600181019050610707565b5050565b601f82111561076a5761073b81610600565b61074484610615565b81016020851015610753578190505b61076761075f85610615565b830182610706565b50505b505050565b600082821c905092915050565b600061078d6000198460080261076f565b1980831691505092915050565b60006107a6838361077c565b9150826002028217905092915050565b6107bf82610566565b67ffffffffffffffff8111156107d8576107d7610571565b5b6107e282546105cf565b6107ed828285610729565b600060209050601f831160018114610820576000841561080e578287015190505b610818858261079a565b865550610880565b601f19841661082e86610600565b60005b8281101561085657848901518255600182019150602085019450602081019050610831565b86831015610873578489015161086f601f89168261077c565b8355505b6001600288020188555050505b505050505050565b610891816104fb565b82525050565b60006020820190506108ac6000830184610888565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006108ec82610684565b91506108f783610684565b925082820190508082111561090f5761090e6108b2565b5b92915050565b61091e81610684565b82525050565b60006060820190506109396000830186610888565b6109466020830185610915565b6109536040830184610915565b949350505050565b60006020820190506109706000830184610915565b92915050565b6111aa806109856000396000f3fe608060405234801561001057600080fd5b50600436106100df5760003560e01c806370a082311161008c57806395d89b411161006657806395d89b4114610212578063a9059cbb14610230578063dd62ed3e14610260578063f2fde38b14610290576100df565b806370a08231146101ba578063715018a6146101ea5780638da5cb5b146101f4576100df565b806323b872dd116100bd57806323b872dd14610150578063313ce5671461018057806340c10f191461019e576100df565b806306fdde03146100e4578063095ea7b31461010257806318160ddd14610132575b600080fd5b6100ec6102ac565b6040516100f99190610dfe565b60405180910390f35b61011c60048036038101906101179190610eb9565b61033e565b6040516101299190610f14565b60405180910390f35b61013a610361565b6040516101479190610f3e565b60405180910390f35b61016a60048036038101906101659190610f59565b61036b565b6040516101779190610f14565b60405180910390f35b61018861039a565b6040516101959190610fc8565b60405180910390f35b6101b860048036038101906101b39190610eb9565b6103a3565b005b6101d460048036038101906101cf9190610fe3565b6103b9565b6040516101e19190610f3e565b60405180910390f35b6101f2610401565b005b6101fc610415565b604051610209919061101f565b60405180910390f35b61021a61043f565b6040516102279190610dfe565b60405180910390f35b61024a60048036038101906102459190610eb9565b6104d1565b6040516102579190610f14565b60405180910390f35b61027a6004803603810190610275919061103a565b6104f4565b6040516102879190610f3e565b60405180910390f35b6102aa60048036038101906102a59190610fe3565b61057b565b005b6060600380546102bb906110a9565b80601f01602080910402602001604051908101604052809291908181526020018280546102e7906110a9565b80156103345780601f1061030957610100808354040283529160200191610334565b820191906000526020600020905b81548152906001019060200180831161031757829003601f168201915b5050505050905090565b600080610349610601565b9050610356818585610609565b600191505092915050565b6000600254905090565b600080610376610601565b905061038385828561061b565b61038e8585856106af565b60019150509392505050565b60006012905090565b6103ab6107a3565b6103b5828261082a565b5050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b6104096107a3565b61041360006108ac565b565b6000600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60606004805461044e906110a9565b80601f016020809104026020016040519081016040528092919081815260200182805461047a906110a9565b80156104c75780601f1061049c576101008083540402835291602001916104c7565b820191906000526020600020905b8154815290600101906020018083116104aa57829003601f168201915b5050505050905090565b6000806104dc610601565b90506104e98185856106af565b600191505092915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b6105836107a3565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16036105f55760006040517f1e4fbdf70000000000000000000000000000000000000000000000000000000081526004016105ec919061101f565b60405180910390fd5b6105fe816108ac565b50565b600033905090565b6106168383836001610972565b505050565b600061062784846104f4565b90507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146106a95781811015610699578281836040517ffb8f41b2000000000000000000000000000000000000000000000000000000008152600401610690939291906110da565b60405180910390fd5b6106a884848484036000610972565b5b50505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036107215760006040517f96c6fd1e000000000000000000000000000000000000000000000000000000008152600401610718919061101f565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036107935760006040517fec442f0500000000000000000000000000000000000000000000000000000000815260040161078a919061101f565b60405180910390fd5b61079e838383610b49565b505050565b6107ab610601565b73ffffffffffffffffffffffffffffffffffffffff166107c9610415565b73ffffffffffffffffffffffffffffffffffffffff1614610828576107ec610601565b6040517f118cdaa700000000000000000000000000000000000000000000000000000000815260040161081f919061101f565b60405180910390fd5b565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361089c5760006040517fec442f05000000000000000000000000000000000000000000000000000000008152600401610893919061101f565b60405180910390fd5b6108a860008383610b49565b5050565b6000600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905081600560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16036109e45760006040517fe602df050000000000000000000000000000000000000000000000000000000081526004016109db919061101f565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603610a565760006040517f94280d62000000000000000000000000000000000000000000000000000000008152600401610a4d919061101f565b60405180910390fd5b81600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508015610b43578273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92584604051610b3a9190610f3e565b60405180910390a35b50505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603610b9b578060026000828254610b8f9190611140565b92505081905550610c6e565b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905081811015610c27578381836040517fe450d38c000000000000000000000000000000000000000000000000000000008152600401610c1e939291906110da565b60405180910390fd5b8181036000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550505b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610cb75780600260008282540392505081905550610d04565b806000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055505b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051610d619190610f3e565b60405180910390a3505050565b600081519050919050565b600082825260208201905092915050565b60005b83811015610da8578082015181840152602081019050610d8d565b60008484015250505050565b6000601f19601f8301169050919050565b6000610dd082610d6e565b610dda8185610d79565b9350610dea818560208601610d8a565b610df381610db4565b840191505092915050565b60006020820190508181036000830152610e188184610dc5565b905092915050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610e5082610e25565b9050919050565b610e6081610e45565b8114610e6b57600080fd5b50565b600081359050610e7d81610e57565b92915050565b6000819050919050565b610e9681610e83565b8114610ea157600080fd5b50565b600081359050610eb381610e8d565b92915050565b60008060408385031215610ed057610ecf610e20565b5b6000610ede85828601610e6e565b9250506020610eef85828601610ea4565b9150509250929050565b60008115159050919050565b610f0e81610ef9565b82525050565b6000602082019050610f296000830184610f05565b92915050565b610f3881610e83565b82525050565b6000602082019050610f536000830184610f2f565b92915050565b600080600060608486031215610f7257610f71610e20565b5b6000610f8086828701610e6e565b9350506020610f9186828701610e6e565b9250506040610fa286828701610ea4565b9150509250925092565b600060ff82169050919050565b610fc281610fac565b82525050565b6000602082019050610fdd6000830184610fb9565b92915050565b600060208284031215610ff957610ff8610e20565b5b600061100784828501610e6e565b91505092915050565b61101981610e45565b82525050565b60006020820190506110346000830184611010565b92915050565b6000806040838503121561105157611050610e20565b5b600061105f85828601610e6e565b925050602061107085828601610e6e565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806110c157607f821691505b6020821081036110d4576110d361107a565b5b50919050565b60006060820190506110ef6000830186611010565b6110fc6020830185610f2f565b6111096040830184610f2f565b949350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061114b82610e83565b915061115683610e83565b925082820190508082111561116e5761116d611111565b5b9291505056fea264697066735822122087fee3dee55412e98926c508cd3f02869c1f308399901f683e021879b2aed14264736f6c634300081b0033", - "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100df5760003560e01c806370a082311161008c57806395d89b411161006657806395d89b4114610212578063a9059cbb14610230578063dd62ed3e14610260578063f2fde38b14610290576100df565b806370a08231146101ba578063715018a6146101ea5780638da5cb5b146101f4576100df565b806323b872dd116100bd57806323b872dd14610150578063313ce5671461018057806340c10f191461019e576100df565b806306fdde03146100e4578063095ea7b31461010257806318160ddd14610132575b600080fd5b6100ec6102ac565b6040516100f99190610dfe565b60405180910390f35b61011c60048036038101906101179190610eb9565b61033e565b6040516101299190610f14565b60405180910390f35b61013a610361565b6040516101479190610f3e565b60405180910390f35b61016a60048036038101906101659190610f59565b61036b565b6040516101779190610f14565b60405180910390f35b61018861039a565b6040516101959190610fc8565b60405180910390f35b6101b860048036038101906101b39190610eb9565b6103a3565b005b6101d460048036038101906101cf9190610fe3565b6103b9565b6040516101e19190610f3e565b60405180910390f35b6101f2610401565b005b6101fc610415565b604051610209919061101f565b60405180910390f35b61021a61043f565b6040516102279190610dfe565b60405180910390f35b61024a60048036038101906102459190610eb9565b6104d1565b6040516102579190610f14565b60405180910390f35b61027a6004803603810190610275919061103a565b6104f4565b6040516102879190610f3e565b60405180910390f35b6102aa60048036038101906102a59190610fe3565b61057b565b005b6060600380546102bb906110a9565b80601f01602080910402602001604051908101604052809291908181526020018280546102e7906110a9565b80156103345780601f1061030957610100808354040283529160200191610334565b820191906000526020600020905b81548152906001019060200180831161031757829003601f168201915b5050505050905090565b600080610349610601565b9050610356818585610609565b600191505092915050565b6000600254905090565b600080610376610601565b905061038385828561061b565b61038e8585856106af565b60019150509392505050565b60006012905090565b6103ab6107a3565b6103b5828261082a565b5050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b6104096107a3565b61041360006108ac565b565b6000600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60606004805461044e906110a9565b80601f016020809104026020016040519081016040528092919081815260200182805461047a906110a9565b80156104c75780601f1061049c576101008083540402835291602001916104c7565b820191906000526020600020905b8154815290600101906020018083116104aa57829003601f168201915b5050505050905090565b6000806104dc610601565b90506104e98185856106af565b600191505092915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b6105836107a3565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16036105f55760006040517f1e4fbdf70000000000000000000000000000000000000000000000000000000081526004016105ec919061101f565b60405180910390fd5b6105fe816108ac565b50565b600033905090565b6106168383836001610972565b505050565b600061062784846104f4565b90507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146106a95781811015610699578281836040517ffb8f41b2000000000000000000000000000000000000000000000000000000008152600401610690939291906110da565b60405180910390fd5b6106a884848484036000610972565b5b50505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036107215760006040517f96c6fd1e000000000000000000000000000000000000000000000000000000008152600401610718919061101f565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036107935760006040517fec442f0500000000000000000000000000000000000000000000000000000000815260040161078a919061101f565b60405180910390fd5b61079e838383610b49565b505050565b6107ab610601565b73ffffffffffffffffffffffffffffffffffffffff166107c9610415565b73ffffffffffffffffffffffffffffffffffffffff1614610828576107ec610601565b6040517f118cdaa700000000000000000000000000000000000000000000000000000000815260040161081f919061101f565b60405180910390fd5b565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361089c5760006040517fec442f05000000000000000000000000000000000000000000000000000000008152600401610893919061101f565b60405180910390fd5b6108a860008383610b49565b5050565b6000600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905081600560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16036109e45760006040517fe602df050000000000000000000000000000000000000000000000000000000081526004016109db919061101f565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603610a565760006040517f94280d62000000000000000000000000000000000000000000000000000000008152600401610a4d919061101f565b60405180910390fd5b81600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508015610b43578273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92584604051610b3a9190610f3e565b60405180910390a35b50505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603610b9b578060026000828254610b8f9190611140565b92505081905550610c6e565b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905081811015610c27578381836040517fe450d38c000000000000000000000000000000000000000000000000000000008152600401610c1e939291906110da565b60405180910390fd5b8181036000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550505b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610cb75780600260008282540392505081905550610d04565b806000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055505b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051610d619190610f3e565b60405180910390a3505050565b600081519050919050565b600082825260208201905092915050565b60005b83811015610da8578082015181840152602081019050610d8d565b60008484015250505050565b6000601f19601f8301169050919050565b6000610dd082610d6e565b610dda8185610d79565b9350610dea818560208601610d8a565b610df381610db4565b840191505092915050565b60006020820190508181036000830152610e188184610dc5565b905092915050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610e5082610e25565b9050919050565b610e6081610e45565b8114610e6b57600080fd5b50565b600081359050610e7d81610e57565b92915050565b6000819050919050565b610e9681610e83565b8114610ea157600080fd5b50565b600081359050610eb381610e8d565b92915050565b60008060408385031215610ed057610ecf610e20565b5b6000610ede85828601610e6e565b9250506020610eef85828601610ea4565b9150509250929050565b60008115159050919050565b610f0e81610ef9565b82525050565b6000602082019050610f296000830184610f05565b92915050565b610f3881610e83565b82525050565b6000602082019050610f536000830184610f2f565b92915050565b600080600060608486031215610f7257610f71610e20565b5b6000610f8086828701610e6e565b9350506020610f9186828701610e6e565b9250506040610fa286828701610ea4565b9150509250925092565b600060ff82169050919050565b610fc281610fac565b82525050565b6000602082019050610fdd6000830184610fb9565b92915050565b600060208284031215610ff957610ff8610e20565b5b600061100784828501610e6e565b91505092915050565b61101981610e45565b82525050565b60006020820190506110346000830184611010565b92915050565b6000806040838503121561105157611050610e20565b5b600061105f85828601610e6e565b925050602061107085828601610e6e565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806110c157607f821691505b6020821081036110d4576110d361107a565b5b50919050565b60006060820190506110ef6000830186611010565b6110fc6020830185610f2f565b6111096040830184610f2f565b949350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061114b82610e83565b915061115683610e83565b925082820190508082111561116e5761116d611111565b5b9291505056fea264697066735822122087fee3dee55412e98926c508cd3f02869c1f308399901f683e021879b2aed14264736f6c634300081b0033", - "linkReferences": {}, - "deployedLinkReferences": {} -} diff --git a/example/zeto/src/index.ts b/example/zeto/src/index.ts index 194f99bc7..b9eb1c0d8 100644 --- a/example/zeto/src/index.ts +++ b/example/zeto/src/index.ts @@ -5,7 +5,6 @@ import PaladinClient, { TransactionType, Verifiers, } from "paladin-sdk"; -import erc20Abi from "./abis/SampleERC20.json"; const logger = console; @@ -34,46 +33,7 @@ async function main() { logger.error("Failed!"); return; } - logger.log(`Success! Zeto deployed at: ${zetoCBDC.address}`); - - logger.log("Deploying ERC20 token..."); - const cbdcIssuerAddress = await paladin1.resolveVerifier( - cbdcIssuer, - Algorithms.ECDSA_SECP256K1, - Verifiers.ETH_ADDRESS - ); - - const txId = await paladin3.sendTransaction({ - type: TransactionType.PUBLIC, - from: cbdcIssuer, - data: { - "initialOwner": cbdcIssuerAddress, - }, - function: "", - abi: erc20Abi.abi, - bytecode: erc20Abi.bytecode, - }); - if (txId === undefined) { - logger.error("Failed!"); - return; - } - const result1 = await paladin3.pollForReceipt(txId, 5000); - if (result1 === undefined) { - logger.error("Failed!"); - return; - } - const erc20Address = result1.contractAddress; - logger.log(`Success! ERC20 deployed at: ${erc20Address}`); - - logger.log("Setting ERC20 to the Zeto token contract ..."); - const result2 = await zetoCBDC.setERC20(cbdcIssuer, { - _erc20: erc20Address as string - }); - if (result1 === undefined) { - logger.error("Failed!"); - return; - } - logger.log(`Success! ERC20 configured on the Zeto contract`); + logger.log(`Success! address: ${zetoCBDC.address}`); // Issue some cash logger.log("Issuing CBDC to bank1 and bank2 ..."); diff --git a/go.work.sum b/go.work.sum index 91b1419da..e20278034 100644 --- a/go.work.sum +++ b/go.work.sum @@ -503,9 +503,6 @@ github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47 github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20240312041847-bd984b5ce465/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= -github.com/iden3/go-iden3-crypto v0.0.15/go.mod h1:dLpM4vEPJ3nDHzhWFXDjzkn1qHoBeOT/3UEhXsEsP3E= -github.com/iden3/go-rapidsnark/prover v0.0.10/go.mod h1:wgDsmKOGCuWGtgVtuW9ARWNguNr4NJAIyg2G7+uTax0= -github.com/iden3/go-rapidsnark/types v0.0.2/go.mod h1:ApgcaUxKIgSRA6fAeFxK7p+lgXXfG4oA2HN5DhFlfF4= github.com/iden3/go-rapidsnark/witness/wasmer v0.0.0-20230524142950-0986cf057d4e/go.mod h1:WUtPVKXrhfZHJXavwId2+8J/fKMHQ92N0MZDxt8sfEA= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= @@ -599,7 +596,6 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/ktrysmt/go-bitbucket v0.6.4/go.mod h1:9u0v3hsd2rqCHRIpbir1oP7F58uo5dq19sBYvuMoyQ4= @@ -780,7 +776,6 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.5.1/go.mod h1:/iHQpkQwBD6DLUmQ4pE+s1TXdob1mORJ4/UFdrifcy0= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= @@ -1326,7 +1321,6 @@ google.golang.org/grpc v1.62.0/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJai google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0= -google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= @@ -1341,12 +1335,10 @@ google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHh google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/sdk/typescript/src/domains/zeto.ts b/sdk/typescript/src/domains/zeto.ts index 24700b38a..2aedc9b64 100644 --- a/sdk/typescript/src/domains/zeto.ts +++ b/sdk/typescript/src/domains/zeto.ts @@ -56,20 +56,6 @@ const zetoPrivateAbi = [ }, ]; -const zetoPublicAbi = [{ - "inputs": [ - { - "internalType": "contract IERC20", - "name": "_erc20", - "type": "address" - } - ], - "name": "setERC20", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" -}]; - export const zetoConstructorABI = { type: "constructor", inputs: [{ name: "tokenName", type: "string" }], @@ -87,10 +73,6 @@ export interface ZetoTransferParams { transfers: ZetoTransfer[]; } -export interface ZetoSetERC20Params { - _erc20: string; -} - export interface ZetoTransfer { to: string; amount: string | number; @@ -174,16 +156,4 @@ export class ZetoInstance { }); return this.paladin.pollForReceipt(txID, this.options.pollTimeout); } - - async setERC20(from: string, data: ZetoSetERC20Params) { - const txID = await this.paladin.sendTransaction({ - type: TransactionType.PUBLIC, - abi: zetoPublicAbi, - function: "setERC20", - to: this.address, - from, - data, - }); - return this.paladin.pollForReceipt(txID, DEFAULT_POLL_TIMEOUT); - } } From e790cc88d64718920fdc7ba5db5e45826446b5cd Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Thu, 5 Dec 2024 15:45:28 -0500 Subject: [PATCH 11/36] Update zeto API reference Signed-off-by: Jim Zhang --- doc-site/docs/architecture/zeto.md | 52 ++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/doc-site/docs/architecture/zeto.md b/doc-site/docs/architecture/zeto.md index 30673a452..6ba5100d6 100644 --- a/doc-site/docs/architecture/zeto.md +++ b/doc-site/docs/architecture/zeto.md @@ -111,6 +111,58 @@ Inputs: - **to** - lookup string for the identity that will receive transferred value - **amount** - amount of value to transfer +### deposit + +The Zeto token implementations support interaction with an ERC20 token, to control the value supply publicly. With this paradigm, the token issuer, such as a central bank for digital currencies, can control the total supply in the ERC20 contract. This makes the supply of the tokens public. + +The Zeto token contract can be configured to allow balances from a designated ERC20 contract to be "swapped" for Zeto tokens, by calling the `deposit` API. This allows any accounts that have a balance in the ERC20 contract to swap them for Zeto tokens. The exchange rate between the ERC20 and Zeto tokens is 1:1. On successful deposit, the ERC20 balance is transferred to the Zeto contract. + +Typically in this paradigm, the `mint` API on the Zeto domain should be locked down (disabled) so that the only way to mint Zeto tokens is by depositing. + +```json +{ + "type": "function", + "name": "deposit", + "inputs": [ + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": null +} +``` + +Inputs: + +- **amount** - amount of value to deposit + +### withdraw + +Opposite to the "deposit" operation, users can swap Zeto tokens back to ERC20 balances. + +On successful withdrawal, the ERC20 balance is released by the Zeto contract and transferred back to the user account. + +```json +{ + "type": "function", + "name": "withdraw", + "inputs": [ + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": null +} +``` + +Inputs: + +- **amount** - amount of value to withdraw + ### lockProof This is a special purpose function used in coordinating multi-party transactions, such as [Delivery-vs-Payment (DvP) contracts](https://github.com/hyperledger-labs/zeto/blob/main/solidity/contracts/zkDvP.sol). When a party commits to the trade first by uploading the ZK proof to the orchestration contract, they must be protected from a malicious party seeing the proof and using it to unilaterally execute the token transfer. The `lockProof()` function allows an account, which can be a smart contract address, to designate the finaly submitter of the proof, thus protecting anybody else from abusing the proof outside of the atomic settlement of the multi-leg trade. From ee93a2c1ef9dbd001056711575e254ab96798503 Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Fri, 6 Dec 2024 13:09:34 -0500 Subject: [PATCH 12/36] add missing zkp circuit artifacts for zeto Signed-off-by: Jim Zhang --- build.gradle | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/build.gradle b/build.gradle index 1a138fd31..8a2bc10e9 100644 --- a/build.gradle +++ b/build.gradle @@ -196,17 +196,45 @@ task copyZetoZKPFiles(type: Copy) { // Likely if the range of circuits grows, a separate docker tag will be required for builds // of Paladin that have a wider range of pre-built circuits included. from fileTree('domains/zeto/zkp') { + // anon include 'anon.zkey' include 'anon-vkey.json' include 'anon_js/anon.wasm' + include 'anon_batch.zkey' + include 'anon_batch-vkey.json' + include 'anon_batch_js/anon_batch.wasm' // anon_enc include 'anon_enc.zkey' include 'anon_enc-vkey.json' include 'anon_enc_js/anon_enc.wasm' + include 'anon_enc_batch.zkey' + include 'anon_enc_batch-vkey.json' + include 'anon_enc_batch_js/anon_enc_batch.wasm' // anon_nullifier include 'anon_nullifier.zkey' include 'anon_nullifier-vkey.json' include 'anon_nullifier_js/anon_nullifier.wasm' + include 'anon_nullifier_batch.zkey' + include 'anon_nullifier_batch-vkey.json' + include 'anon_nullifier_batch_js/anon_nullifier_batch.wasm' + // deposit + include 'check_hashes_value.zkey' + include 'check_hashes_value-vkey.json' + include 'check_hashes_value_js/check_hashes_value.wasm' + // withdraw + include 'check_inputs_outputs_value.zkey' + include 'check_inputs_outputs_value-vkey.json' + include 'check_inputs_outputs_value_js/check_inputs_outputs_value.wasm' + include 'check_inputs_outputs_value_batch.zkey' + include 'check_inputs_outputs_value_batch-vkey.json' + include 'check_inputs_outputs_value_batch_js/check_inputs_outputs_value_batch.wasm' + // withdraw_nullifier + include 'check_nullifier_value.zkey' + include 'check_nullifier_value-vkey.json' + include 'check_nullifier_value_js/check_nullifier_value.wasm' + include 'check_nullifier_value_batch.zkey' + include 'check_nullifier_value_batch-vkey.json' + include 'check_nullifier_value_batch_js/check_nullifier_value_batch.wasm' } into zetoZkpDir From ba26066df4131f0db286587d788cdaef4eb00a7e Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Fri, 6 Dec 2024 13:16:27 -0500 Subject: [PATCH 13/36] update the withdraw verifiers for nullifer tokens Signed-off-by: Jim Zhang --- ...a1_transactioninvoke_zeto_register_anon_nullifier.yaml | 8 ++++---- operator/config/samples/kustomization.yaml | 2 ++ operator/contractpkg/contract_map.json | 6 ++++++ 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/operator/config/samples/core_v1alpha1_transactioninvoke_zeto_register_anon_nullifier.yaml b/operator/config/samples/core_v1alpha1_transactioninvoke_zeto_register_anon_nullifier.yaml index f33af4c69..4b1dfabab 100644 --- a/operator/config/samples/core_v1alpha1_transactioninvoke_zeto_register_anon_nullifier.yaml +++ b/operator/config/samples/core_v1alpha1_transactioninvoke_zeto_register_anon_nullifier.yaml @@ -14,8 +14,8 @@ spec: - "zeto-factory" - "zeto-impl-anon-nullifier" - "zeto-g16-check-hashes-value" - - "zeto-g16-check-inputs-outputs" - - "zeto-g16-check-inputs-outputs-batch" + - "zeto-g16-check-nullifier" + - "zeto-g16-check-nullifier-batch" - "zeto-g16-verifier-anon-nullifier" - "zeto-g16-verifier-anon-nullifier-batch" function: registerImplementation @@ -25,8 +25,8 @@ spec: "implementation": { "implementation": "{{ index .status.resolvedContractAddresses "zeto-impl-anon-nullifier" }}", "depositVerifier": "{{ index .status.resolvedContractAddresses "zeto-g16-check-hashes-value" }}", - "withdrawVerifier": "{{ index .status.resolvedContractAddresses "zeto-g16-check-inputs-outputs" }}", - "batchWithdrawVerifier": "{{ index .status.resolvedContractAddresses "zeto-g16-check-inputs-outputs-batch" }}", + "withdrawVerifier": "{{ index .status.resolvedContractAddresses "zeto-g16-check-nullifier" }}", + "batchWithdrawVerifier": "{{ index .status.resolvedContractAddresses "zeto-g16-check-nullifier-batch" }}", "verifier": "{{ index .status.resolvedContractAddresses "zeto-g16-verifier-anon-nullifier" }}", "batchVerifier": "{{ index .status.resolvedContractAddresses "zeto-g16-verifier-anon-nullifier-batch" }}" } diff --git a/operator/config/samples/kustomization.yaml b/operator/config/samples/kustomization.yaml index c18cedf49..13f488780 100644 --- a/operator/config/samples/kustomization.yaml +++ b/operator/config/samples/kustomization.yaml @@ -25,6 +25,8 @@ resources: - core_v1alpha1_smartcontractdeployment_zeto_factory.yaml - core_v1alpha1_smartcontractdeployment_zeto_g16_check_inputs_outputs.yaml - core_v1alpha1_smartcontractdeployment_zeto_g16_check_inputs_outputs_batch.yaml +- core_v1alpha1_smartcontractdeployment_zeto_g16_check_nullifier.yaml +- core_v1alpha1_smartcontractdeployment_zeto_g16_check_nullifier_batch.yaml - core_v1alpha1_smartcontractdeployment_zeto_g16_check_hashes_value.yaml - core_v1alpha1_smartcontractdeployment_zeto_g16_verifier_anon.yaml - core_v1alpha1_smartcontractdeployment_zeto_g16_verifier_anon_batch.yaml diff --git a/operator/contractpkg/contract_map.json b/operator/contractpkg/contract_map.json index d1465a10e..e11fcefac 100644 --- a/operator/contractpkg/contract_map.json +++ b/operator/contractpkg/contract_map.json @@ -35,6 +35,12 @@ "zeto_g16_check_inputs_outputs_batch": { "filename": "test/e2e/abis/zeto/Groth16Verifier_CheckInputsOutputsValueBatch.json" }, + "zeto_g16_check_nullifier": { + "filename": "test/e2e/abis/zeto/Groth16Verifier_CheckNullifierValue.json" + }, + "zeto_g16_check_nullifier_batch": { + "filename": "test/e2e/abis/zeto/Groth16Verifier_CheckNullifierValueBatch.json" + }, "zeto_poseidon_unit2l": { "filename": "test/e2e/abis/zeto/Poseidon2.json" }, From d79e4974034c8f275be535820206483dea7ef520 Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Fri, 6 Dec 2024 13:24:39 -0500 Subject: [PATCH 14/36] Update zeto example to include the deposit/withdraw flow Signed-off-by: Jim Zhang --- domains/zeto/internal/zeto/handler_deposit.go | 47 ++++--- domains/zeto/internal/zeto/handler_mint.go | 3 +- .../zeto/internal/zeto/handler_transfer.go | 97 +++++++++++-- domains/zeto/internal/zeto/states.go | 47 ++----- domains/zeto/internal/zeto/utils.go | 114 +++++----------- domains/zeto/internal/zeto/zeto.go | 19 +-- domains/zeto/internal/zeto/zeto_test.go | 19 --- domains/zeto/pkg/types/abi.go | 26 +--- example/zeto/.gitignore | 1 + example/zeto/build.gradle | 11 ++ example/zeto/src/index.ts | 42 +++++- go.work.sum | 8 ++ sdk/typescript/src/domains/zeto.ts | 129 +++++++++++------- 13 files changed, 297 insertions(+), 266 deletions(-) diff --git a/domains/zeto/internal/zeto/handler_deposit.go b/domains/zeto/internal/zeto/handler_deposit.go index 08ebeb55c..fb2c64870 100644 --- a/domains/zeto/internal/zeto/handler_deposit.go +++ b/domains/zeto/internal/zeto/handler_deposit.go @@ -22,8 +22,6 @@ import ( "github.com/hyperledger/firefly-common/pkg/i18n" "github.com/hyperledger/firefly-signer/pkg/abi" "github.com/kaleido-io/paladin/domains/zeto/internal/msgs" - "github.com/kaleido-io/paladin/domains/zeto/internal/zeto/common" - "github.com/kaleido-io/paladin/domains/zeto/pkg/constants" corepb "github.com/kaleido-io/paladin/domains/zeto/pkg/proto" "github.com/kaleido-io/paladin/domains/zeto/pkg/types" "github.com/kaleido-io/paladin/domains/zeto/pkg/zetosigner/zetosignerapi" @@ -53,7 +51,7 @@ var depositABI = &abi.Entry{ func (h *depositHandler) ValidateParams(ctx context.Context, config *types.DomainInstanceConfig, params string) (interface{}, error) { var depositParams types.DepositParams if err := json.Unmarshal([]byte(params), &depositParams); err != nil { - return nil, i18n.NewError(ctx, msgs.MsgErrorDecodeDepositCall, err) + return nil, err } if err := validateAmountParam(ctx, depositParams.Amount, 0); err != nil { @@ -84,23 +82,26 @@ func (h *depositHandler) Assemble(ctx context.Context, tx *types.ParsedTransacti return nil, i18n.NewError(ctx, msgs.MsgErrorResolveVerifier, tx.Transaction.From) } - useNullifiers := common.IsNullifiersToken(tx.DomainConfig.TokenName) - outputCoins, outputStates, err := h.zeto.prepareOutputsForDeposit(ctx, useNullifiers, amount, resolvedSender) + outputCoins, outputStates, err := h.zeto.prepareOutputsForDeposit(ctx, amount, req.ResolvedVerifiers) if err != nil { return nil, i18n.NewError(ctx, msgs.MsgErrorPrepTxOutputs, err) } - payloadBytes, err := h.formatProvingRequest(ctx, outputCoins) + contractAddress, err := tktypes.ParseEthAddress(req.Transaction.ContractInfo.ContractAddress) + if err != nil { + return nil, i18n.NewError(ctx, msgs.MsgErrorDecodeContractAddress, err) + } + payloadBytes, err := h.formatProvingRequest(ctx, outputCoins, tx.DomainConfig.CircuitId, tx.DomainConfig.TokenName, req.StateQueryContext, contractAddress) if err != nil { return nil, i18n.NewError(ctx, msgs.MsgErrorFormatProvingReq, err) } - amountStr := amount.Int().Text(10) + amountStr := amount.String() return &pb.AssembleTransactionResponse{ AssemblyResult: pb.AssembleTransactionResponse_OK, AssembledTransaction: &pb.AssembledTransaction{ OutputStates: outputStates, - DomainData: &amountStr, + ExtraData: &amountStr, }, AttestationPlan: []*pb.AttestationRequest{ { @@ -139,7 +140,7 @@ func (h *depositHandler) Prepare(ctx context.Context, tx *types.ParsedTransactio return nil, i18n.NewError(ctx, msgs.MsgErrorUnmarshalProvingRes, err) } - outputSize := common.GetInputSize(len(req.OutputStates)) + outputSize := getInputSize(len(req.OutputStates)) outputs := make([]string, outputSize) for i := 0; i < outputSize; i++ { if i < len(req.OutputStates) { @@ -162,7 +163,7 @@ func (h *depositHandler) Prepare(ctx context.Context, tx *types.ParsedTransactio if err != nil { return nil, i18n.NewError(ctx, msgs.MsgErrorEncodeTxData, err) } - amount := tktypes.MustParseHexUint256(*req.DomainData) + amount := tktypes.MustParseHexUint256(*req.ExtraData) params := map[string]any{ "amount": amount.Int().Text(10), "outputs": outputs, @@ -187,26 +188,30 @@ func (h *depositHandler) Prepare(ctx context.Context, tx *types.ParsedTransactio }, nil } -func (h *depositHandler) formatProvingRequest(ctx context.Context, outputCoins []*types.ZetoCoin) ([]byte, error) { - outputSize := common.GetInputSize(len(outputCoins)) +func (h *depositHandler) formatProvingRequest(ctx context.Context, outputCoins []*types.ZetoCoin, circuitId, tokenName, stateQueryContext string, contractAddress *tktypes.EthAddress) ([]byte, error) { + outputSize := getInputSize(len(outputCoins)) outputCommitments := make([]string, outputSize) outputValueInts := make([]uint64, outputSize) outputSalts := make([]string, outputSize) outputOwners := make([]string, outputSize) for i := 0; i < outputSize; i++ { - coin := outputCoins[i] - hash, err := coin.Hash(ctx) - if err != nil { - return nil, i18n.NewError(ctx, msgs.MsgErrorHashInputState, err) + if i < len(outputCoins) { + coin := outputCoins[i] + hash, err := coin.Hash(ctx) + if err != nil { + return nil, i18n.NewError(ctx, msgs.MsgErrorHashInputState, err) + } + outputCommitments[i] = hash.Int().Text(16) + outputValueInts[i] = coin.Amount.Int().Uint64() + outputSalts[i] = coin.Salt.Int().Text(16) + outputOwners[i] = coin.Owner.String() + } else { + outputSalts[i] = "0" } - outputCommitments[i] = hash.Int().Text(16) - outputValueInts[i] = coin.Amount.Int().Uint64() - outputSalts[i] = coin.Salt.Int().Text(16) - outputOwners[i] = coin.Owner.String() } payload := &corepb.ProvingRequest{ - CircuitId: constants.CIRCUIT_DEPOSIT, + CircuitId: circuitId, Common: &corepb.ProvingRequestCommon{ OutputCommitments: outputCommitments, OutputValues: outputValueInts, diff --git a/domains/zeto/internal/zeto/handler_mint.go b/domains/zeto/internal/zeto/handler_mint.go index ea7af068d..0e6265c09 100644 --- a/domains/zeto/internal/zeto/handler_mint.go +++ b/domains/zeto/internal/zeto/handler_mint.go @@ -22,7 +22,6 @@ import ( "github.com/hyperledger/firefly-common/pkg/i18n" "github.com/hyperledger/firefly-signer/pkg/abi" "github.com/kaleido-io/paladin/domains/zeto/internal/msgs" - "github.com/kaleido-io/paladin/domains/zeto/internal/zeto/common" "github.com/kaleido-io/paladin/domains/zeto/pkg/types" "github.com/kaleido-io/paladin/domains/zeto/pkg/zetosigner/zetosignerapi" "github.com/kaleido-io/paladin/toolkit/pkg/algorithms" @@ -77,7 +76,7 @@ func (h *mintHandler) Init(ctx context.Context, tx *types.ParsedTransaction, req func (h *mintHandler) Assemble(ctx context.Context, tx *types.ParsedTransaction, req *pb.AssembleTransactionRequest) (*pb.AssembleTransactionResponse, error) { params := tx.Params.([]*types.TransferParamEntry) - useNullifiers := common.IsNullifiersToken(tx.DomainConfig.TokenName) + useNullifiers := isNullifiersToken(tx.DomainConfig.TokenName) _, outputStates, err := h.zeto.prepareOutputsForTransfer(ctx, useNullifiers, params, req.ResolvedVerifiers) if err != nil { return nil, err diff --git a/domains/zeto/internal/zeto/handler_transfer.go b/domains/zeto/internal/zeto/handler_transfer.go index dd99362c5..aa36f524e 100644 --- a/domains/zeto/internal/zeto/handler_transfer.go +++ b/domains/zeto/internal/zeto/handler_transfer.go @@ -18,15 +18,18 @@ package zeto import ( "context" "encoding/json" + "math/big" "strings" + "github.com/hyperledger-labs/zeto/go-sdk/pkg/sparse-merkle-tree/core" + "github.com/hyperledger-labs/zeto/go-sdk/pkg/sparse-merkle-tree/node" "github.com/hyperledger/firefly-common/pkg/i18n" "github.com/hyperledger/firefly-signer/pkg/abi" "github.com/kaleido-io/paladin/domains/zeto/internal/msgs" - "github.com/kaleido-io/paladin/domains/zeto/internal/zeto/common" "github.com/kaleido-io/paladin/domains/zeto/internal/zeto/smt" corepb "github.com/kaleido-io/paladin/domains/zeto/pkg/proto" "github.com/kaleido-io/paladin/domains/zeto/pkg/types" + "github.com/kaleido-io/paladin/domains/zeto/pkg/zetosigner" "github.com/kaleido-io/paladin/domains/zeto/pkg/zetosigner/zetosignerapi" "github.com/kaleido-io/paladin/toolkit/pkg/algorithms" "github.com/kaleido-io/paladin/toolkit/pkg/domain" @@ -142,8 +145,8 @@ func (h *transferHandler) Assemble(ctx context.Context, tx *types.ParsedTransact return nil, i18n.NewError(ctx, msgs.MsgErrorResolveVerifier, tx.Transaction.From) } - useNullifiers := common.IsNullifiersToken(tx.DomainConfig.TokenName) - inputCoins, inputStates, _, remainder, err := h.zeto.prepareInputsForTransfer(ctx, useNullifiers, req.StateQueryContext, resolvedSender.Verifier, params) + useNullifiers := isNullifiersToken(tx.DomainConfig.TokenName) + inputCoins, inputStates, _, remainder, err := h.zeto.prepareInputs(ctx, useNullifiers, req.StateQueryContext, resolvedSender.Verifier, params) if err != nil { return nil, i18n.NewError(ctx, msgs.MsgErrorPrepTxInputs, err) } @@ -220,7 +223,7 @@ func (h *transferHandler) Prepare(ctx context.Context, tx *types.ParsedTransacti return nil, i18n.NewError(ctx, msgs.MsgErrorUnmarshalProvingRes, err) } - inputSize := common.GetInputSize(len(req.InputStates)) + inputSize := getInputSize(len(req.InputStates)) inputs := make([]string, inputSize) for i := 0; i < inputSize; i++ { if i < len(req.InputStates) { @@ -267,12 +270,12 @@ func (h *transferHandler) Prepare(ctx context.Context, tx *types.ParsedTransacti "data": data, } transferFunction := getTransferABI(tx.DomainConfig.TokenName) - if common.IsEncryptionToken(tx.DomainConfig.TokenName) { + if isEncryptionToken(tx.DomainConfig.TokenName) { params["ecdhPublicKey"] = strings.Split(proofRes.PublicInputs["ecdhPublicKey"], ",") params["encryptionNonce"] = proofRes.PublicInputs["encryptionNonce"] params["encryptedValues"] = strings.Split(proofRes.PublicInputs["encryptedValues"], ",") } - if common.IsNullifiersToken(tx.DomainConfig.TokenName) { + if isNullifiersToken(tx.DomainConfig.TokenName) { delete(params, "inputs") params["nullifiers"] = strings.Split(proofRes.PublicInputs["nullifiers"], ",") params["root"] = proofRes.PublicInputs["root"] @@ -295,7 +298,7 @@ func (h *transferHandler) Prepare(ctx context.Context, tx *types.ParsedTransacti } func (h *transferHandler) formatProvingRequest(ctx context.Context, inputCoins, outputCoins []*types.ZetoCoin, circuitId, tokenName, stateQueryContext string, contractAddress *tktypes.EthAddress) ([]byte, error) { - inputSize := common.GetInputSize(len(inputCoins)) + inputSize := getInputSize(len(inputCoins)) inputCommitments := make([]string, inputSize) inputValueInts := make([]uint64, inputSize) inputSalts := make([]string, inputSize) @@ -331,8 +334,8 @@ func (h *transferHandler) formatProvingRequest(ctx context.Context, inputCoins, } var extras []byte - if common.IsNullifiersCircuit(circuitId) { - proofs, extrasObj, err := generateMerkleProofs(ctx, h.zeto, tokenName, stateQueryContext, contractAddress, inputCoins) + if isNullifiersCircuit(circuitId) { + proofs, extrasObj, err := h.generateMerkleProofs(ctx, tokenName, stateQueryContext, contractAddress, inputCoins) if err != nil { return nil, i18n.NewError(ctx, msgs.MsgErrorGenerateMTP, err) } @@ -365,14 +368,84 @@ func (h *transferHandler) formatProvingRequest(ctx context.Context, inputCoins, return proto.Marshal(payload) } +func (h *transferHandler) generateMerkleProofs(ctx context.Context, tokenName string, stateQueryContext string, contractAddress *tktypes.EthAddress, inputCoins []*types.ZetoCoin) ([]core.Proof, *corepb.ProvingRequestExtras_Nullifiers, error) { + smtName := smt.MerkleTreeName(tokenName, contractAddress) + storage := smt.NewStatesStorage(h.zeto.Callbacks, smtName, stateQueryContext, h.zeto.merkleTreeRootSchema.Id, h.zeto.merkleTreeNodeSchema.Id) + mt, err := smt.NewSmt(storage) + if err != nil { + return nil, nil, i18n.NewError(ctx, msgs.MsgErrorNewSmt, smtName, err) + } + // verify that the input UTXOs have been indexed by the Merkle tree DB + // and generate a merkle proof for each + var indexes []*big.Int + for _, coin := range inputCoins { + pubKey, err := zetosigner.DecodeBabyJubJubPublicKey(coin.Owner.String()) + if err != nil { + return nil, nil, i18n.NewError(ctx, msgs.MsgErrorLoadOwnerPubKey, err) + } + idx := node.NewFungible(coin.Amount.Int(), pubKey, coin.Salt.Int()) + leaf, err := node.NewLeafNode(idx) + if err != nil { + return nil, nil, i18n.NewError(ctx, msgs.MsgErrorNewLeafNode, err) + } + n, err := mt.GetNode(leaf.Ref()) + if err != nil { + // TODO: deal with when the node is not found in the DB tables for the tree + // e.g because the transaction event hasn't been processed yet + return nil, nil, i18n.NewError(ctx, msgs.MsgErrorQueryLeafNode, leaf.Ref().Hex(), err) + } + hash, err := coin.Hash(ctx) + if err != nil { + return nil, nil, i18n.NewError(ctx, msgs.MsgErrorHashInputState, err) + } + if n.Index().BigInt().Cmp(hash.Int()) != 0 { + expectedIndex, err := node.NewNodeIndexFromBigInt(hash.Int()) + if err != nil { + return nil, nil, i18n.NewError(ctx, msgs.MsgErrorNewNodeIndex, err) + } + return nil, nil, i18n.NewError(ctx, msgs.MsgErrorHashMismatch, leaf.Ref().Hex(), n.Index().BigInt().Text(16), n.Index().Hex(), hash.HexString0xPrefix(), expectedIndex.Hex()) + } + indexes = append(indexes, n.Index().BigInt()) + } + mtRoot := mt.Root() + proofs, _, err := mt.GenerateProofs(indexes, mtRoot) + if err != nil { + return nil, nil, i18n.NewError(ctx, msgs.MsgErrorGenerateMTP, err) + } + var mps []*corepb.MerkleProof + var enabled []bool + for i, proof := range proofs { + cp, err := proof.ToCircomVerifierProof(indexes[i], indexes[i], mtRoot, smt.SMT_HEIGHT_UTXO) + if err != nil { + return nil, nil, i18n.NewError(ctx, msgs.MsgErrorConvertToCircomProof, err) + } + proofSiblings := make([]string, len(cp.Siblings)-1) + for i, s := range cp.Siblings[0 : len(cp.Siblings)-1] { + proofSiblings[i] = s.BigInt().Text(16) + } + p := corepb.MerkleProof{ + Nodes: proofSiblings, + } + mps = append(mps, &p) + enabled = append(enabled, true) + } + extrasObj := corepb.ProvingRequestExtras_Nullifiers{ + Root: mt.Root().BigInt().Text(16), + MerkleProofs: mps, + Enabled: enabled, + } + + return proofs, &extrasObj, nil +} + func getTransferABI(tokenName string) *abi.Entry { transferFunction := transferABI - if common.IsEncryptionToken(tokenName) { + if isEncryptionToken(tokenName) { transferFunction = transferABI_withEncryption - if common.IsNullifiersToken(tokenName) { + if isNullifiersToken(tokenName) { transferFunction = transferABI_withEncryption_nullifiers } - } else if common.IsNullifiersToken(tokenName) { + } else if isNullifiersToken(tokenName) { transferFunction = transferABI_nullifiers } return transferFunction diff --git a/domains/zeto/internal/zeto/states.go b/domains/zeto/internal/zeto/states.go index b7d49297c..cc991b337 100644 --- a/domains/zeto/internal/zeto/states.go +++ b/domains/zeto/internal/zeto/states.go @@ -95,25 +95,17 @@ func (z *Zeto) makeNewState(ctx context.Context, useNullifiers bool, coin *types return newState, nil } -func (z *Zeto) prepareInputsForTransfer(ctx context.Context, useNullifiers bool, stateQueryContext, senderKey string, params []*types.TransferParamEntry) ([]*types.ZetoCoin, []*pb.StateRef, *big.Int, *big.Int, error) { +func (z *Zeto) prepareInputs(ctx context.Context, useNullifiers bool, stateQueryContext, senderKey string, params []*types.TransferParamEntry) ([]*types.ZetoCoin, []*pb.StateRef, *big.Int, *big.Int, error) { + var lastStateTimestamp int64 + total := big.NewInt(0) + stateRefs := []*pb.StateRef{} + coins := []*types.ZetoCoin{} + expectedTotal := big.NewInt(0) for _, param := range params { expectedTotal = expectedTotal.Add(expectedTotal, param.Amount.Int()) } - return z.buildInputsForExpectedTotal(ctx, useNullifiers, stateQueryContext, senderKey, expectedTotal) -} - -func (z *Zeto) prepareInputsForWithdraw(ctx context.Context, useNullifiers bool, stateQueryContext, senderKey string, amount *tktypes.HexUint256) ([]*types.ZetoCoin, []*pb.StateRef, *big.Int, *big.Int, error) { - expectedTotal := amount.Int() - return z.buildInputsForExpectedTotal(ctx, useNullifiers, stateQueryContext, senderKey, expectedTotal) -} - -func (z *Zeto) buildInputsForExpectedTotal(ctx context.Context, useNullifiers bool, stateQueryContext, senderKey string, expectedTotal *big.Int) ([]*types.ZetoCoin, []*pb.StateRef, *big.Int, *big.Int, error) { - var lastStateTimestamp int64 - total := big.NewInt(0) - stateRefs := []*pb.StateRef{} - coins := []*types.ZetoCoin{} for { queryBuilder := query.NewQueryBuilder(). Limit(10). @@ -184,7 +176,7 @@ func (z *Zeto) prepareOutputsForTransfer(ctx context.Context, useNullifiers bool return coins, newStates, nil } -func (z *Zeto) prepareOutputsForDeposit(ctx context.Context, useNullifiers bool, amount *tktypes.HexUint256, resolvedSender *pb.ResolvedVerifier) ([]*types.ZetoCoin, []*pb.NewState, error) { +func (z *Zeto) prepareOutputsForDeposit(ctx context.Context, amount *tktypes.HexUint256, resolvedVerifiers []*pb.ResolvedVerifier) ([]*types.ZetoCoin, []*pb.NewState, error) { var coins []*types.ZetoCoin // the token implementation allows up to 2 output states, we will use one of them // to bear the deposit amount, and set the other to value of 0. we randomize @@ -196,7 +188,7 @@ func (z *Zeto) prepareOutputsForDeposit(ctx context.Context, useNullifiers bool, amounts[randomIdx] = amount amounts[size-randomIdx-1] = tktypes.MustParseHexUint256("0x0") for _, amt := range amounts { - resolvedRecipient := resolvedSender + resolvedRecipient := resolvedVerifiers[0] recipientKey, err := loadBabyJubKey([]byte(resolvedRecipient.Verifier)) if err != nil { return nil, nil, i18n.NewError(ctx, msgs.MsgErrorLoadOwnerPubKey, err) @@ -210,7 +202,7 @@ func (z *Zeto) prepareOutputsForDeposit(ctx context.Context, useNullifiers bool, Amount: amt, } - newState, err := z.makeNewState(ctx, useNullifiers, newCoin, resolvedRecipient.Lookup) + newState, err := z.makeNewState(ctx, false, newCoin, resolvedRecipient.Lookup) if err != nil { return nil, nil, i18n.NewError(ctx, msgs.MsgErrorCreateNewState, err) } @@ -220,27 +212,6 @@ func (z *Zeto) prepareOutputsForDeposit(ctx context.Context, useNullifiers bool, return coins, newStates, nil } -func (z *Zeto) prepareOutputForWithdraw(ctx context.Context, amount *tktypes.HexUint256, resolvedRecipient *pb.ResolvedVerifier) (*types.ZetoCoin, *pb.NewState, error) { - recipientKey, err := loadBabyJubKey([]byte(resolvedRecipient.Verifier)) - if err != nil { - return nil, nil, i18n.NewError(ctx, msgs.MsgErrorLoadOwnerPubKey, err) - } - - salt := crypto.NewSalt() - compressedKeyStr := zetosigner.EncodeBabyJubJubPublicKey(recipientKey) - newCoin := &types.ZetoCoin{ - Salt: (*tktypes.HexUint256)(salt), - Owner: tktypes.MustParseHexBytes(compressedKeyStr), - Amount: amount, - } - - newState, err := z.makeNewState(ctx, false, newCoin, resolvedRecipient.Lookup) - if err != nil { - return nil, nil, i18n.NewError(ctx, msgs.MsgErrorCreateNewState, err) - } - return newCoin, newState, nil -} - func (z *Zeto) findAvailableStates(ctx context.Context, useNullifiers bool, stateQueryContext, query string) ([]*pb.StoredState, error) { req := &pb.FindAvailableStatesRequest{ StateQueryContext: stateQueryContext, diff --git a/domains/zeto/internal/zeto/utils.go b/domains/zeto/internal/zeto/utils.go index b642efe93..6995a95ce 100644 --- a/domains/zeto/internal/zeto/utils.go +++ b/domains/zeto/internal/zeto/utils.go @@ -17,21 +17,47 @@ package zeto import ( "context" - "math/big" - "github.com/hyperledger-labs/zeto/go-sdk/pkg/sparse-merkle-tree/core" - "github.com/hyperledger-labs/zeto/go-sdk/pkg/sparse-merkle-tree/node" "github.com/hyperledger/firefly-common/pkg/i18n" "github.com/iden3/go-iden3-crypto/babyjub" "github.com/kaleido-io/paladin/domains/zeto/internal/msgs" - "github.com/kaleido-io/paladin/domains/zeto/internal/zeto/smt" + "github.com/kaleido-io/paladin/domains/zeto/pkg/constants" corepb "github.com/kaleido-io/paladin/domains/zeto/pkg/proto" "github.com/kaleido-io/paladin/domains/zeto/pkg/types" - "github.com/kaleido-io/paladin/domains/zeto/pkg/zetosigner" "github.com/kaleido-io/paladin/toolkit/pkg/prototk" "github.com/kaleido-io/paladin/toolkit/pkg/tktypes" ) +func isNullifiersCircuit(circuitId string) bool { + return circuitId == constants.CIRCUIT_ANON_NULLIFIER || circuitId == constants.CIRCUIT_ANON_NULLIFIER_BATCH +} + +func isNullifiersToken(tokenName string) bool { + return tokenName == constants.TOKEN_ANON_NULLIFIER +} + +func isEncryptionToken(tokenName string) bool { + return tokenName == constants.TOKEN_ANON_ENC +} + +// the Zeto implementations support two input/output sizes for the circuits: 2 and 10, +// if the input or output size is larger than 2, then the batch circuit is used with +// input/output size 10 +func getInputSize(sizeOfEndorsableStates int) int { + if sizeOfEndorsableStates <= 2 { + return 2 + } + return 10 +} + +func loadBabyJubKey(payload []byte) (*babyjub.PublicKey, error) { + var keyCompressed babyjub.PublicKeyComp + if err := keyCompressed.UnmarshalText(payload); err != nil { + return nil, err + } + return keyCompressed.Decompress() +} + func validateTransferParams(ctx context.Context, params []*types.TransferParamEntry) error { if len(params) == 0 { return i18n.NewError(ctx, msgs.MsgNoTransferParams) @@ -90,81 +116,3 @@ func encodeProof(proof *corepb.SnarkProof) map[string]interface{} { "pC": []string{proof.C[0], proof.C[1]}, } } - -func loadBabyJubKey(payload []byte) (*babyjub.PublicKey, error) { - var keyCompressed babyjub.PublicKeyComp - if err := keyCompressed.UnmarshalText(payload); err != nil { - return nil, err - } - return keyCompressed.Decompress() -} - -func generateMerkleProofs(ctx context.Context, zeto *Zeto, tokenName string, stateQueryContext string, contractAddress *tktypes.EthAddress, inputCoins []*types.ZetoCoin) ([]core.Proof, *corepb.ProvingRequestExtras_Nullifiers, error) { - smtName := smt.MerkleTreeName(tokenName, contractAddress) - storage := smt.NewStatesStorage(zeto.Callbacks, smtName, stateQueryContext, zeto.merkleTreeRootSchema.Id, zeto.merkleTreeNodeSchema.Id) - mt, err := smt.NewSmt(storage) - if err != nil { - return nil, nil, i18n.NewError(ctx, msgs.MsgErrorNewSmt, smtName, err) - } - // verify that the input UTXOs have been indexed by the Merkle tree DB - // and generate a merkle proof for each - var indexes []*big.Int - for _, coin := range inputCoins { - pubKey, err := zetosigner.DecodeBabyJubJubPublicKey(coin.Owner.String()) - if err != nil { - return nil, nil, i18n.NewError(ctx, msgs.MsgErrorLoadOwnerPubKey, err) - } - idx := node.NewFungible(coin.Amount.Int(), pubKey, coin.Salt.Int()) - leaf, err := node.NewLeafNode(idx) - if err != nil { - return nil, nil, i18n.NewError(ctx, msgs.MsgErrorNewLeafNode, err) - } - n, err := mt.GetNode(leaf.Ref()) - if err != nil { - // TODO: deal with when the node is not found in the DB tables for the tree - // e.g because the transaction event hasn't been processed yet - return nil, nil, i18n.NewError(ctx, msgs.MsgErrorQueryLeafNode, leaf.Ref().Hex(), err) - } - hash, err := coin.Hash(ctx) - if err != nil { - return nil, nil, i18n.NewError(ctx, msgs.MsgErrorHashInputState, err) - } - if n.Index().BigInt().Cmp(hash.Int()) != 0 { - expectedIndex, err := node.NewNodeIndexFromBigInt(hash.Int()) - if err != nil { - return nil, nil, i18n.NewError(ctx, msgs.MsgErrorNewNodeIndex, err) - } - return nil, nil, i18n.NewError(ctx, msgs.MsgErrorHashMismatch, leaf.Ref().Hex(), n.Index().BigInt().Text(16), n.Index().Hex(), hash.HexString0xPrefix(), expectedIndex.Hex()) - } - indexes = append(indexes, n.Index().BigInt()) - } - mtRoot := mt.Root() - proofs, _, err := mt.GenerateProofs(indexes, mtRoot) - if err != nil { - return nil, nil, i18n.NewError(ctx, msgs.MsgErrorGenerateMTP, err) - } - var mps []*corepb.MerkleProof - var enabled []bool - for i, proof := range proofs { - cp, err := proof.ToCircomVerifierProof(indexes[i], indexes[i], mtRoot, smt.SMT_HEIGHT_UTXO) - if err != nil { - return nil, nil, i18n.NewError(ctx, msgs.MsgErrorConvertToCircomProof, err) - } - proofSiblings := make([]string, len(cp.Siblings)-1) - for i, s := range cp.Siblings[0 : len(cp.Siblings)-1] { - proofSiblings[i] = s.BigInt().Text(16) - } - p := corepb.MerkleProof{ - Nodes: proofSiblings, - } - mps = append(mps, &p) - enabled = append(enabled, true) - } - extrasObj := corepb.ProvingRequestExtras_Nullifiers{ - Root: mt.Root().BigInt().Text(16), - MerkleProofs: mps, - Enabled: enabled, - } - - return proofs, &extrasObj, nil -} diff --git a/domains/zeto/internal/zeto/zeto.go b/domains/zeto/internal/zeto/zeto.go index a73133abc..eb1f8c0c7 100644 --- a/domains/zeto/internal/zeto/zeto.go +++ b/domains/zeto/internal/zeto/zeto.go @@ -27,7 +27,6 @@ import ( "github.com/hyperledger/firefly-signer/pkg/ethtypes" "github.com/iden3/go-iden3-crypto/babyjub" "github.com/kaleido-io/paladin/domains/zeto/internal/msgs" - "github.com/kaleido-io/paladin/domains/zeto/internal/zeto/common" "github.com/kaleido-io/paladin/domains/zeto/internal/zeto/signer" "github.com/kaleido-io/paladin/domains/zeto/internal/zeto/smt" "github.com/kaleido-io/paladin/domains/zeto/pkg/types" @@ -54,7 +53,6 @@ type Zeto struct { mintSignature string transferSignature string transferWithEncSignature string - withdrawSignature string snarkProver signerapi.InMemorySigner } @@ -77,13 +75,6 @@ type TransferWithEncryptedValuesEvent struct { EncryptedValues []tktypes.HexUint256 `json:"encryptedValues"` } -type WithdrawEvent struct { - Amount tktypes.HexUint256 `json:"amount"` - Inputs []tktypes.HexUint256 `json:"inputs"` - Output tktypes.HexUint256 `json:"output"` - Data tktypes.HexBytes `json:"data"` -} - var factoryDeployABI = &abi.Entry{ Type: abi.Function, Name: "deploy", @@ -293,8 +284,6 @@ func (z *Zeto) GetHandler(method string) types.DomainHandler { return &lockHandler{zeto: z} case "deposit": return &depositHandler{zeto: z} - case "withdraw": - return &withdrawHandler{zeto: z} default: return nil } @@ -371,8 +360,6 @@ func (z *Zeto) registerEventSignatures(eventAbis abi.ABI) { z.transferSignature = event.SolString() case "UTXOTransferWithEncryptedValues": z.transferWithEncSignature = event.SolString() - case "UTXOWithdraw": - z.withdrawSignature = event.SolString() } } } @@ -394,7 +381,7 @@ func (z *Zeto) HandleEventBatch(ctx context.Context, req *prototk.HandleEventBat var smtName string var storage smt.StatesStorage var tree core.SparseMerkleTree - if common.IsNullifiersToken(domainConfig.TokenName) { + if isNullifiersCircuit(domainConfig.CircuitId) { smtName = smt.MerkleTreeName(domainConfig.TokenName, contractAddress) storage = smt.NewStatesStorage(z.Callbacks, smtName, req.StateQueryContext, z.merkleTreeRootSchema.Id, z.merkleTreeNodeSchema.Id) tree, err = smt.NewSmt(storage) @@ -411,8 +398,6 @@ func (z *Zeto) HandleEventBatch(ctx context.Context, req *prototk.HandleEventBat err = z.handleTransferEvent(ctx, tree, storage, ev, domainConfig.TokenName, &res) case z.transferWithEncSignature: err = z.handleTransferWithEncryptionEvent(ctx, tree, storage, ev, domainConfig.TokenName, &res) - case z.withdrawSignature: - err = z.handleWithdrawEvent(ctx, tree, storage, ev, domainConfig.TokenName, &res) } if err != nil { errors = append(errors, err.Error()) @@ -421,7 +406,7 @@ func (z *Zeto) HandleEventBatch(ctx context.Context, req *prototk.HandleEventBat if len(errors) > 0 { return &res, i18n.NewError(ctx, msgs.MsgErrorHandleEvents, formatErrors(errors)) } - if common.IsNullifiersToken(domainConfig.TokenName) { + if isNullifiersCircuit(domainConfig.CircuitId) { newStatesForSMT, err := storage.GetNewStates() if err != nil { return nil, i18n.NewError(ctx, msgs.MsgErrorGetNewSmtStates, smtName, err) diff --git a/domains/zeto/internal/zeto/zeto_test.go b/domains/zeto/internal/zeto/zeto_test.go index 6b8a7468f..208b26798 100644 --- a/domains/zeto/internal/zeto/zeto_test.go +++ b/domains/zeto/internal/zeto/zeto_test.go @@ -479,11 +479,6 @@ func TestHandleEventBatch(t *testing.T) { assert.NoError(t, err) assert.Len(t, res4.TransactionsComplete, 1) assert.Len(t, res4.NewStates, 2) - - req.Events[0].SoliditySignature = "event UTXOWithdraw(uint256 amount, uint256[] inputs, uint256 output, address indexed submitter, bytes data)" - req.Events[0].DataJson = "{\"data\":\"0x0001000030e43028afbb41d6887444f4c2b4ed6d00000000000000000000000000000000\",\"output\":\"7980718117603030807695495350922077879582656644717071592146865497574198464253\",\"submitter\":\"0x74e71b05854ee819cb9397be01c82570a178d019\"}" - _, err = z.HandleEventBatch(ctx, req) - assert.NoError(t, err) } func TestGetVerifier(t *testing.T) { @@ -655,19 +650,5 @@ func TestGetHandler(t *testing.T) { assert.NotNil(t, z.GetHandler("mint")) assert.NotNil(t, z.GetHandler("transfer")) assert.NotNil(t, z.GetHandler("lockProof")) - assert.NotNil(t, z.GetHandler("deposit")) - assert.NotNil(t, z.GetHandler("withdraw")) assert.Nil(t, z.GetHandler("bad")) } - -func TestUnimplementedMethods(t *testing.T) { - z := &Zeto{} - _, err := z.InitCall(context.Background(), nil) - assert.ErrorContains(t, err, "PD210085: Not implemented") - - _, err = z.ExecCall(context.Background(), nil) - assert.ErrorContains(t, err, "PD210085: Not implemented") - - _, err = z.BuildReceipt(context.Background(), nil) - assert.ErrorContains(t, err, "PD210102: Not implemented") -} diff --git a/domains/zeto/pkg/types/abi.go b/domains/zeto/pkg/types/abi.go index 32aaf279c..10e41eb8f 100644 --- a/domains/zeto/pkg/types/abi.go +++ b/domains/zeto/pkg/types/abi.go @@ -66,13 +66,6 @@ var ZetoABI = abi.ABI{ {Name: "call", Type: "bytes"}, // assumed to be an encoded "transfer" }, }, - { - Name: "setERC20", - Type: abi.Function, - Inputs: abi.ParameterArray{ - {Name: "_erc20", Type: "address"}, - }, - }, { Name: "deposit", Type: abi.Function, @@ -80,13 +73,6 @@ var ZetoABI = abi.ABI{ {Name: "amount", Type: "uint256"}, }, }, - { - Name: "withdraw", - Type: abi.Function, - Inputs: abi.ParameterArray{ - {Name: "amount", Type: "uint256"}, - }, - }, } type InitializerParams struct { @@ -109,6 +95,10 @@ type TransferParams struct { Transfers []*TransferParamEntry `json:"transfers"` } +type DepositParams struct { + Amount *tktypes.HexUint256 `json:"amount"` +} + type TransferParamEntry struct { To string `json:"to"` Amount *tktypes.HexUint256 `json:"amount"` @@ -118,11 +108,3 @@ type LockParams struct { Delegate *tktypes.EthAddress `json:"delegate"` Call tktypes.HexBytes `json:"call"` } - -type DepositParams struct { - Amount *tktypes.HexUint256 `json:"amount"` -} - -type WithdrawParams struct { - Amount *tktypes.HexUint256 `json:"amount"` -} diff --git a/example/zeto/.gitignore b/example/zeto/.gitignore index b38db2f29..4c79bef56 100644 --- a/example/zeto/.gitignore +++ b/example/zeto/.gitignore @@ -1,2 +1,3 @@ node_modules/ build/ +src/abis/*.json \ No newline at end of file diff --git a/example/zeto/build.gradle b/example/zeto/build.gradle index 890da0296..7314f0f34 100644 --- a/example/zeto/build.gradle +++ b/example/zeto/build.gradle @@ -29,6 +29,17 @@ dependencies { buildSDK project(path: ":sdk:typescript", configuration: "buildSDK") } +task copyContracts(type: Copy, dependsOn: [":domains:zeto:extractZetoArtifacts"]) { + from fileTree("../../domains/zeto/zkp/artifacts/contracts") { + include '**/SampleERC20.json' + } + into './src/abis' + + // Flatten all paths into the destination folder + eachFile { path = name } + includeEmptyDirs = false +} + task install(type: Exec) { executable 'npm' args 'install' diff --git a/example/zeto/src/index.ts b/example/zeto/src/index.ts index b9eb1c0d8..194f99bc7 100644 --- a/example/zeto/src/index.ts +++ b/example/zeto/src/index.ts @@ -5,6 +5,7 @@ import PaladinClient, { TransactionType, Verifiers, } from "paladin-sdk"; +import erc20Abi from "./abis/SampleERC20.json"; const logger = console; @@ -33,7 +34,46 @@ async function main() { logger.error("Failed!"); return; } - logger.log(`Success! address: ${zetoCBDC.address}`); + logger.log(`Success! Zeto deployed at: ${zetoCBDC.address}`); + + logger.log("Deploying ERC20 token..."); + const cbdcIssuerAddress = await paladin1.resolveVerifier( + cbdcIssuer, + Algorithms.ECDSA_SECP256K1, + Verifiers.ETH_ADDRESS + ); + + const txId = await paladin3.sendTransaction({ + type: TransactionType.PUBLIC, + from: cbdcIssuer, + data: { + "initialOwner": cbdcIssuerAddress, + }, + function: "", + abi: erc20Abi.abi, + bytecode: erc20Abi.bytecode, + }); + if (txId === undefined) { + logger.error("Failed!"); + return; + } + const result1 = await paladin3.pollForReceipt(txId, 5000); + if (result1 === undefined) { + logger.error("Failed!"); + return; + } + const erc20Address = result1.contractAddress; + logger.log(`Success! ERC20 deployed at: ${erc20Address}`); + + logger.log("Setting ERC20 to the Zeto token contract ..."); + const result2 = await zetoCBDC.setERC20(cbdcIssuer, { + _erc20: erc20Address as string + }); + if (result1 === undefined) { + logger.error("Failed!"); + return; + } + logger.log(`Success! ERC20 configured on the Zeto contract`); // Issue some cash logger.log("Issuing CBDC to bank1 and bank2 ..."); diff --git a/go.work.sum b/go.work.sum index e20278034..91b1419da 100644 --- a/go.work.sum +++ b/go.work.sum @@ -503,6 +503,9 @@ github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47 github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20240312041847-bd984b5ce465/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= +github.com/iden3/go-iden3-crypto v0.0.15/go.mod h1:dLpM4vEPJ3nDHzhWFXDjzkn1qHoBeOT/3UEhXsEsP3E= +github.com/iden3/go-rapidsnark/prover v0.0.10/go.mod h1:wgDsmKOGCuWGtgVtuW9ARWNguNr4NJAIyg2G7+uTax0= +github.com/iden3/go-rapidsnark/types v0.0.2/go.mod h1:ApgcaUxKIgSRA6fAeFxK7p+lgXXfG4oA2HN5DhFlfF4= github.com/iden3/go-rapidsnark/witness/wasmer v0.0.0-20230524142950-0986cf057d4e/go.mod h1:WUtPVKXrhfZHJXavwId2+8J/fKMHQ92N0MZDxt8sfEA= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= @@ -596,6 +599,7 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/ktrysmt/go-bitbucket v0.6.4/go.mod h1:9u0v3hsd2rqCHRIpbir1oP7F58uo5dq19sBYvuMoyQ4= @@ -776,6 +780,7 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.5.1/go.mod h1:/iHQpkQwBD6DLUmQ4pE+s1TXdob1mORJ4/UFdrifcy0= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= @@ -1321,6 +1326,7 @@ google.golang.org/grpc v1.62.0/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJai google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0= +google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= @@ -1335,10 +1341,12 @@ google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHh google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/sdk/typescript/src/domains/zeto.ts b/sdk/typescript/src/domains/zeto.ts index 2aedc9b64..88d1d40b8 100644 --- a/sdk/typescript/src/domains/zeto.ts +++ b/sdk/typescript/src/domains/zeto.ts @@ -1,60 +1,71 @@ import { TransactionType } from "../interfaces"; import PaladinClient from "../paladin"; -const DEFAULT_POLL_TIMEOUT = 10000; +const POLL_TIMEOUT_MS = 10000; export interface ZetoOptions { pollTimeout?: number; } -const zetoPrivateAbi = [ - { - name: "mint", - type: "function", - inputs: [ - { - name: "mints", - type: "tuple[]", - components: [ - { - name: "to", - type: "string", - internalType: "string", - }, - { - name: "amount", - type: "uint256", - internalType: "uint256", - }, - ], - }, - ], - outputs: [], - }, - { - type: "function", - name: "transfer", - inputs: [ - { - name: "transfers", - type: "tuple[]", - components: [ - { - name: "to", - type: "string", - internalType: "string", - }, - { - name: "amount", - type: "uint256", - internalType: "uint256", - }, - ], - }, - ], - outputs: [], - }, -]; +const zetoPrivateAbi = [{ + "name": "mint", + "type": "function", + "inputs": [ + { + "name": "mints", + "type": "tuple[]", + "components": [ + { + "name": "to", + "type": "string", + "internalType": "string" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + } + ] + } + ], + "outputs": [], +}, { + "type": "function", + "name": "transfer", + "inputs": [ + { + "name": "transfers", + "type": "tuple[]", + "components": [ + { + "name": "to", + "type": "string", + "internalType": "string" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + } + ] + } + ], + "outputs": [] +}]; + +const zetoPublicAbi = [{ + "inputs": [ + { + "internalType": "contract IERC20", + "name": "_erc20", + "type": "address" + } + ], + "name": "setERC20", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" +}]; export const zetoConstructorABI = { type: "constructor", @@ -73,6 +84,10 @@ export interface ZetoTransferParams { transfers: ZetoTransfer[]; } +export interface ZetoSetERC20Params { + _erc20: string; +} + export interface ZetoTransfer { to: string; amount: string | number; @@ -87,7 +102,7 @@ export class ZetoFactory { options?: ZetoOptions ) { this.options = { - pollTimeout: DEFAULT_POLL_TIMEOUT, + pollTimeout: POLL_TIMEOUT_MS, ...options, }; } @@ -124,7 +139,7 @@ export class ZetoInstance { options?: ZetoOptions ) { this.options = { - pollTimeout: DEFAULT_POLL_TIMEOUT, + pollTimeout: POLL_TIMEOUT_MS, ...options, }; } @@ -156,4 +171,16 @@ export class ZetoInstance { }); return this.paladin.pollForReceipt(txID, this.options.pollTimeout); } + + async setERC20(from: string, data: ZetoSetERC20Params) { + const txID = await this.paladin.sendTransaction({ + type: TransactionType.PUBLIC, + abi: zetoPublicAbi, + function: "setERC20", + to: this.address, + from, + data, + }); + return this.paladin.pollForReceipt(txID, POLL_TIMEOUT_MS); + } } From 402cc4d69aa3702125d592f1efd8cdfd06127425 Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Fri, 6 Dec 2024 13:29:43 -0500 Subject: [PATCH 15/36] restore domains/zeto from zeto-erc20 Signed-off-by: Jim Zhang --- domains/zeto/internal/zeto/handler_deposit.go | 47 ++++---- domains/zeto/internal/zeto/handler_mint.go | 3 +- .../zeto/internal/zeto/handler_transfer.go | 97 ++------------- domains/zeto/internal/zeto/states.go | 47 ++++++-- domains/zeto/internal/zeto/utils.go | 114 +++++++++++++----- domains/zeto/internal/zeto/zeto.go | 19 ++- domains/zeto/internal/zeto/zeto_test.go | 19 +++ domains/zeto/pkg/types/abi.go | 26 +++- 8 files changed, 214 insertions(+), 158 deletions(-) diff --git a/domains/zeto/internal/zeto/handler_deposit.go b/domains/zeto/internal/zeto/handler_deposit.go index fb2c64870..08ebeb55c 100644 --- a/domains/zeto/internal/zeto/handler_deposit.go +++ b/domains/zeto/internal/zeto/handler_deposit.go @@ -22,6 +22,8 @@ import ( "github.com/hyperledger/firefly-common/pkg/i18n" "github.com/hyperledger/firefly-signer/pkg/abi" "github.com/kaleido-io/paladin/domains/zeto/internal/msgs" + "github.com/kaleido-io/paladin/domains/zeto/internal/zeto/common" + "github.com/kaleido-io/paladin/domains/zeto/pkg/constants" corepb "github.com/kaleido-io/paladin/domains/zeto/pkg/proto" "github.com/kaleido-io/paladin/domains/zeto/pkg/types" "github.com/kaleido-io/paladin/domains/zeto/pkg/zetosigner/zetosignerapi" @@ -51,7 +53,7 @@ var depositABI = &abi.Entry{ func (h *depositHandler) ValidateParams(ctx context.Context, config *types.DomainInstanceConfig, params string) (interface{}, error) { var depositParams types.DepositParams if err := json.Unmarshal([]byte(params), &depositParams); err != nil { - return nil, err + return nil, i18n.NewError(ctx, msgs.MsgErrorDecodeDepositCall, err) } if err := validateAmountParam(ctx, depositParams.Amount, 0); err != nil { @@ -82,26 +84,23 @@ func (h *depositHandler) Assemble(ctx context.Context, tx *types.ParsedTransacti return nil, i18n.NewError(ctx, msgs.MsgErrorResolveVerifier, tx.Transaction.From) } - outputCoins, outputStates, err := h.zeto.prepareOutputsForDeposit(ctx, amount, req.ResolvedVerifiers) + useNullifiers := common.IsNullifiersToken(tx.DomainConfig.TokenName) + outputCoins, outputStates, err := h.zeto.prepareOutputsForDeposit(ctx, useNullifiers, amount, resolvedSender) if err != nil { return nil, i18n.NewError(ctx, msgs.MsgErrorPrepTxOutputs, err) } - contractAddress, err := tktypes.ParseEthAddress(req.Transaction.ContractInfo.ContractAddress) - if err != nil { - return nil, i18n.NewError(ctx, msgs.MsgErrorDecodeContractAddress, err) - } - payloadBytes, err := h.formatProvingRequest(ctx, outputCoins, tx.DomainConfig.CircuitId, tx.DomainConfig.TokenName, req.StateQueryContext, contractAddress) + payloadBytes, err := h.formatProvingRequest(ctx, outputCoins) if err != nil { return nil, i18n.NewError(ctx, msgs.MsgErrorFormatProvingReq, err) } - amountStr := amount.String() + amountStr := amount.Int().Text(10) return &pb.AssembleTransactionResponse{ AssemblyResult: pb.AssembleTransactionResponse_OK, AssembledTransaction: &pb.AssembledTransaction{ OutputStates: outputStates, - ExtraData: &amountStr, + DomainData: &amountStr, }, AttestationPlan: []*pb.AttestationRequest{ { @@ -140,7 +139,7 @@ func (h *depositHandler) Prepare(ctx context.Context, tx *types.ParsedTransactio return nil, i18n.NewError(ctx, msgs.MsgErrorUnmarshalProvingRes, err) } - outputSize := getInputSize(len(req.OutputStates)) + outputSize := common.GetInputSize(len(req.OutputStates)) outputs := make([]string, outputSize) for i := 0; i < outputSize; i++ { if i < len(req.OutputStates) { @@ -163,7 +162,7 @@ func (h *depositHandler) Prepare(ctx context.Context, tx *types.ParsedTransactio if err != nil { return nil, i18n.NewError(ctx, msgs.MsgErrorEncodeTxData, err) } - amount := tktypes.MustParseHexUint256(*req.ExtraData) + amount := tktypes.MustParseHexUint256(*req.DomainData) params := map[string]any{ "amount": amount.Int().Text(10), "outputs": outputs, @@ -188,30 +187,26 @@ func (h *depositHandler) Prepare(ctx context.Context, tx *types.ParsedTransactio }, nil } -func (h *depositHandler) formatProvingRequest(ctx context.Context, outputCoins []*types.ZetoCoin, circuitId, tokenName, stateQueryContext string, contractAddress *tktypes.EthAddress) ([]byte, error) { - outputSize := getInputSize(len(outputCoins)) +func (h *depositHandler) formatProvingRequest(ctx context.Context, outputCoins []*types.ZetoCoin) ([]byte, error) { + outputSize := common.GetInputSize(len(outputCoins)) outputCommitments := make([]string, outputSize) outputValueInts := make([]uint64, outputSize) outputSalts := make([]string, outputSize) outputOwners := make([]string, outputSize) for i := 0; i < outputSize; i++ { - if i < len(outputCoins) { - coin := outputCoins[i] - hash, err := coin.Hash(ctx) - if err != nil { - return nil, i18n.NewError(ctx, msgs.MsgErrorHashInputState, err) - } - outputCommitments[i] = hash.Int().Text(16) - outputValueInts[i] = coin.Amount.Int().Uint64() - outputSalts[i] = coin.Salt.Int().Text(16) - outputOwners[i] = coin.Owner.String() - } else { - outputSalts[i] = "0" + coin := outputCoins[i] + hash, err := coin.Hash(ctx) + if err != nil { + return nil, i18n.NewError(ctx, msgs.MsgErrorHashInputState, err) } + outputCommitments[i] = hash.Int().Text(16) + outputValueInts[i] = coin.Amount.Int().Uint64() + outputSalts[i] = coin.Salt.Int().Text(16) + outputOwners[i] = coin.Owner.String() } payload := &corepb.ProvingRequest{ - CircuitId: circuitId, + CircuitId: constants.CIRCUIT_DEPOSIT, Common: &corepb.ProvingRequestCommon{ OutputCommitments: outputCommitments, OutputValues: outputValueInts, diff --git a/domains/zeto/internal/zeto/handler_mint.go b/domains/zeto/internal/zeto/handler_mint.go index 0e6265c09..ea7af068d 100644 --- a/domains/zeto/internal/zeto/handler_mint.go +++ b/domains/zeto/internal/zeto/handler_mint.go @@ -22,6 +22,7 @@ import ( "github.com/hyperledger/firefly-common/pkg/i18n" "github.com/hyperledger/firefly-signer/pkg/abi" "github.com/kaleido-io/paladin/domains/zeto/internal/msgs" + "github.com/kaleido-io/paladin/domains/zeto/internal/zeto/common" "github.com/kaleido-io/paladin/domains/zeto/pkg/types" "github.com/kaleido-io/paladin/domains/zeto/pkg/zetosigner/zetosignerapi" "github.com/kaleido-io/paladin/toolkit/pkg/algorithms" @@ -76,7 +77,7 @@ func (h *mintHandler) Init(ctx context.Context, tx *types.ParsedTransaction, req func (h *mintHandler) Assemble(ctx context.Context, tx *types.ParsedTransaction, req *pb.AssembleTransactionRequest) (*pb.AssembleTransactionResponse, error) { params := tx.Params.([]*types.TransferParamEntry) - useNullifiers := isNullifiersToken(tx.DomainConfig.TokenName) + useNullifiers := common.IsNullifiersToken(tx.DomainConfig.TokenName) _, outputStates, err := h.zeto.prepareOutputsForTransfer(ctx, useNullifiers, params, req.ResolvedVerifiers) if err != nil { return nil, err diff --git a/domains/zeto/internal/zeto/handler_transfer.go b/domains/zeto/internal/zeto/handler_transfer.go index aa36f524e..dd99362c5 100644 --- a/domains/zeto/internal/zeto/handler_transfer.go +++ b/domains/zeto/internal/zeto/handler_transfer.go @@ -18,18 +18,15 @@ package zeto import ( "context" "encoding/json" - "math/big" "strings" - "github.com/hyperledger-labs/zeto/go-sdk/pkg/sparse-merkle-tree/core" - "github.com/hyperledger-labs/zeto/go-sdk/pkg/sparse-merkle-tree/node" "github.com/hyperledger/firefly-common/pkg/i18n" "github.com/hyperledger/firefly-signer/pkg/abi" "github.com/kaleido-io/paladin/domains/zeto/internal/msgs" + "github.com/kaleido-io/paladin/domains/zeto/internal/zeto/common" "github.com/kaleido-io/paladin/domains/zeto/internal/zeto/smt" corepb "github.com/kaleido-io/paladin/domains/zeto/pkg/proto" "github.com/kaleido-io/paladin/domains/zeto/pkg/types" - "github.com/kaleido-io/paladin/domains/zeto/pkg/zetosigner" "github.com/kaleido-io/paladin/domains/zeto/pkg/zetosigner/zetosignerapi" "github.com/kaleido-io/paladin/toolkit/pkg/algorithms" "github.com/kaleido-io/paladin/toolkit/pkg/domain" @@ -145,8 +142,8 @@ func (h *transferHandler) Assemble(ctx context.Context, tx *types.ParsedTransact return nil, i18n.NewError(ctx, msgs.MsgErrorResolveVerifier, tx.Transaction.From) } - useNullifiers := isNullifiersToken(tx.DomainConfig.TokenName) - inputCoins, inputStates, _, remainder, err := h.zeto.prepareInputs(ctx, useNullifiers, req.StateQueryContext, resolvedSender.Verifier, params) + useNullifiers := common.IsNullifiersToken(tx.DomainConfig.TokenName) + inputCoins, inputStates, _, remainder, err := h.zeto.prepareInputsForTransfer(ctx, useNullifiers, req.StateQueryContext, resolvedSender.Verifier, params) if err != nil { return nil, i18n.NewError(ctx, msgs.MsgErrorPrepTxInputs, err) } @@ -223,7 +220,7 @@ func (h *transferHandler) Prepare(ctx context.Context, tx *types.ParsedTransacti return nil, i18n.NewError(ctx, msgs.MsgErrorUnmarshalProvingRes, err) } - inputSize := getInputSize(len(req.InputStates)) + inputSize := common.GetInputSize(len(req.InputStates)) inputs := make([]string, inputSize) for i := 0; i < inputSize; i++ { if i < len(req.InputStates) { @@ -270,12 +267,12 @@ func (h *transferHandler) Prepare(ctx context.Context, tx *types.ParsedTransacti "data": data, } transferFunction := getTransferABI(tx.DomainConfig.TokenName) - if isEncryptionToken(tx.DomainConfig.TokenName) { + if common.IsEncryptionToken(tx.DomainConfig.TokenName) { params["ecdhPublicKey"] = strings.Split(proofRes.PublicInputs["ecdhPublicKey"], ",") params["encryptionNonce"] = proofRes.PublicInputs["encryptionNonce"] params["encryptedValues"] = strings.Split(proofRes.PublicInputs["encryptedValues"], ",") } - if isNullifiersToken(tx.DomainConfig.TokenName) { + if common.IsNullifiersToken(tx.DomainConfig.TokenName) { delete(params, "inputs") params["nullifiers"] = strings.Split(proofRes.PublicInputs["nullifiers"], ",") params["root"] = proofRes.PublicInputs["root"] @@ -298,7 +295,7 @@ func (h *transferHandler) Prepare(ctx context.Context, tx *types.ParsedTransacti } func (h *transferHandler) formatProvingRequest(ctx context.Context, inputCoins, outputCoins []*types.ZetoCoin, circuitId, tokenName, stateQueryContext string, contractAddress *tktypes.EthAddress) ([]byte, error) { - inputSize := getInputSize(len(inputCoins)) + inputSize := common.GetInputSize(len(inputCoins)) inputCommitments := make([]string, inputSize) inputValueInts := make([]uint64, inputSize) inputSalts := make([]string, inputSize) @@ -334,8 +331,8 @@ func (h *transferHandler) formatProvingRequest(ctx context.Context, inputCoins, } var extras []byte - if isNullifiersCircuit(circuitId) { - proofs, extrasObj, err := h.generateMerkleProofs(ctx, tokenName, stateQueryContext, contractAddress, inputCoins) + if common.IsNullifiersCircuit(circuitId) { + proofs, extrasObj, err := generateMerkleProofs(ctx, h.zeto, tokenName, stateQueryContext, contractAddress, inputCoins) if err != nil { return nil, i18n.NewError(ctx, msgs.MsgErrorGenerateMTP, err) } @@ -368,84 +365,14 @@ func (h *transferHandler) formatProvingRequest(ctx context.Context, inputCoins, return proto.Marshal(payload) } -func (h *transferHandler) generateMerkleProofs(ctx context.Context, tokenName string, stateQueryContext string, contractAddress *tktypes.EthAddress, inputCoins []*types.ZetoCoin) ([]core.Proof, *corepb.ProvingRequestExtras_Nullifiers, error) { - smtName := smt.MerkleTreeName(tokenName, contractAddress) - storage := smt.NewStatesStorage(h.zeto.Callbacks, smtName, stateQueryContext, h.zeto.merkleTreeRootSchema.Id, h.zeto.merkleTreeNodeSchema.Id) - mt, err := smt.NewSmt(storage) - if err != nil { - return nil, nil, i18n.NewError(ctx, msgs.MsgErrorNewSmt, smtName, err) - } - // verify that the input UTXOs have been indexed by the Merkle tree DB - // and generate a merkle proof for each - var indexes []*big.Int - for _, coin := range inputCoins { - pubKey, err := zetosigner.DecodeBabyJubJubPublicKey(coin.Owner.String()) - if err != nil { - return nil, nil, i18n.NewError(ctx, msgs.MsgErrorLoadOwnerPubKey, err) - } - idx := node.NewFungible(coin.Amount.Int(), pubKey, coin.Salt.Int()) - leaf, err := node.NewLeafNode(idx) - if err != nil { - return nil, nil, i18n.NewError(ctx, msgs.MsgErrorNewLeafNode, err) - } - n, err := mt.GetNode(leaf.Ref()) - if err != nil { - // TODO: deal with when the node is not found in the DB tables for the tree - // e.g because the transaction event hasn't been processed yet - return nil, nil, i18n.NewError(ctx, msgs.MsgErrorQueryLeafNode, leaf.Ref().Hex(), err) - } - hash, err := coin.Hash(ctx) - if err != nil { - return nil, nil, i18n.NewError(ctx, msgs.MsgErrorHashInputState, err) - } - if n.Index().BigInt().Cmp(hash.Int()) != 0 { - expectedIndex, err := node.NewNodeIndexFromBigInt(hash.Int()) - if err != nil { - return nil, nil, i18n.NewError(ctx, msgs.MsgErrorNewNodeIndex, err) - } - return nil, nil, i18n.NewError(ctx, msgs.MsgErrorHashMismatch, leaf.Ref().Hex(), n.Index().BigInt().Text(16), n.Index().Hex(), hash.HexString0xPrefix(), expectedIndex.Hex()) - } - indexes = append(indexes, n.Index().BigInt()) - } - mtRoot := mt.Root() - proofs, _, err := mt.GenerateProofs(indexes, mtRoot) - if err != nil { - return nil, nil, i18n.NewError(ctx, msgs.MsgErrorGenerateMTP, err) - } - var mps []*corepb.MerkleProof - var enabled []bool - for i, proof := range proofs { - cp, err := proof.ToCircomVerifierProof(indexes[i], indexes[i], mtRoot, smt.SMT_HEIGHT_UTXO) - if err != nil { - return nil, nil, i18n.NewError(ctx, msgs.MsgErrorConvertToCircomProof, err) - } - proofSiblings := make([]string, len(cp.Siblings)-1) - for i, s := range cp.Siblings[0 : len(cp.Siblings)-1] { - proofSiblings[i] = s.BigInt().Text(16) - } - p := corepb.MerkleProof{ - Nodes: proofSiblings, - } - mps = append(mps, &p) - enabled = append(enabled, true) - } - extrasObj := corepb.ProvingRequestExtras_Nullifiers{ - Root: mt.Root().BigInt().Text(16), - MerkleProofs: mps, - Enabled: enabled, - } - - return proofs, &extrasObj, nil -} - func getTransferABI(tokenName string) *abi.Entry { transferFunction := transferABI - if isEncryptionToken(tokenName) { + if common.IsEncryptionToken(tokenName) { transferFunction = transferABI_withEncryption - if isNullifiersToken(tokenName) { + if common.IsNullifiersToken(tokenName) { transferFunction = transferABI_withEncryption_nullifiers } - } else if isNullifiersToken(tokenName) { + } else if common.IsNullifiersToken(tokenName) { transferFunction = transferABI_nullifiers } return transferFunction diff --git a/domains/zeto/internal/zeto/states.go b/domains/zeto/internal/zeto/states.go index cc991b337..b7d49297c 100644 --- a/domains/zeto/internal/zeto/states.go +++ b/domains/zeto/internal/zeto/states.go @@ -95,17 +95,25 @@ func (z *Zeto) makeNewState(ctx context.Context, useNullifiers bool, coin *types return newState, nil } -func (z *Zeto) prepareInputs(ctx context.Context, useNullifiers bool, stateQueryContext, senderKey string, params []*types.TransferParamEntry) ([]*types.ZetoCoin, []*pb.StateRef, *big.Int, *big.Int, error) { - var lastStateTimestamp int64 - total := big.NewInt(0) - stateRefs := []*pb.StateRef{} - coins := []*types.ZetoCoin{} - +func (z *Zeto) prepareInputsForTransfer(ctx context.Context, useNullifiers bool, stateQueryContext, senderKey string, params []*types.TransferParamEntry) ([]*types.ZetoCoin, []*pb.StateRef, *big.Int, *big.Int, error) { expectedTotal := big.NewInt(0) for _, param := range params { expectedTotal = expectedTotal.Add(expectedTotal, param.Amount.Int()) } + return z.buildInputsForExpectedTotal(ctx, useNullifiers, stateQueryContext, senderKey, expectedTotal) +} + +func (z *Zeto) prepareInputsForWithdraw(ctx context.Context, useNullifiers bool, stateQueryContext, senderKey string, amount *tktypes.HexUint256) ([]*types.ZetoCoin, []*pb.StateRef, *big.Int, *big.Int, error) { + expectedTotal := amount.Int() + return z.buildInputsForExpectedTotal(ctx, useNullifiers, stateQueryContext, senderKey, expectedTotal) +} + +func (z *Zeto) buildInputsForExpectedTotal(ctx context.Context, useNullifiers bool, stateQueryContext, senderKey string, expectedTotal *big.Int) ([]*types.ZetoCoin, []*pb.StateRef, *big.Int, *big.Int, error) { + var lastStateTimestamp int64 + total := big.NewInt(0) + stateRefs := []*pb.StateRef{} + coins := []*types.ZetoCoin{} for { queryBuilder := query.NewQueryBuilder(). Limit(10). @@ -176,7 +184,7 @@ func (z *Zeto) prepareOutputsForTransfer(ctx context.Context, useNullifiers bool return coins, newStates, nil } -func (z *Zeto) prepareOutputsForDeposit(ctx context.Context, amount *tktypes.HexUint256, resolvedVerifiers []*pb.ResolvedVerifier) ([]*types.ZetoCoin, []*pb.NewState, error) { +func (z *Zeto) prepareOutputsForDeposit(ctx context.Context, useNullifiers bool, amount *tktypes.HexUint256, resolvedSender *pb.ResolvedVerifier) ([]*types.ZetoCoin, []*pb.NewState, error) { var coins []*types.ZetoCoin // the token implementation allows up to 2 output states, we will use one of them // to bear the deposit amount, and set the other to value of 0. we randomize @@ -188,7 +196,7 @@ func (z *Zeto) prepareOutputsForDeposit(ctx context.Context, amount *tktypes.Hex amounts[randomIdx] = amount amounts[size-randomIdx-1] = tktypes.MustParseHexUint256("0x0") for _, amt := range amounts { - resolvedRecipient := resolvedVerifiers[0] + resolvedRecipient := resolvedSender recipientKey, err := loadBabyJubKey([]byte(resolvedRecipient.Verifier)) if err != nil { return nil, nil, i18n.NewError(ctx, msgs.MsgErrorLoadOwnerPubKey, err) @@ -202,7 +210,7 @@ func (z *Zeto) prepareOutputsForDeposit(ctx context.Context, amount *tktypes.Hex Amount: amt, } - newState, err := z.makeNewState(ctx, false, newCoin, resolvedRecipient.Lookup) + newState, err := z.makeNewState(ctx, useNullifiers, newCoin, resolvedRecipient.Lookup) if err != nil { return nil, nil, i18n.NewError(ctx, msgs.MsgErrorCreateNewState, err) } @@ -212,6 +220,27 @@ func (z *Zeto) prepareOutputsForDeposit(ctx context.Context, amount *tktypes.Hex return coins, newStates, nil } +func (z *Zeto) prepareOutputForWithdraw(ctx context.Context, amount *tktypes.HexUint256, resolvedRecipient *pb.ResolvedVerifier) (*types.ZetoCoin, *pb.NewState, error) { + recipientKey, err := loadBabyJubKey([]byte(resolvedRecipient.Verifier)) + if err != nil { + return nil, nil, i18n.NewError(ctx, msgs.MsgErrorLoadOwnerPubKey, err) + } + + salt := crypto.NewSalt() + compressedKeyStr := zetosigner.EncodeBabyJubJubPublicKey(recipientKey) + newCoin := &types.ZetoCoin{ + Salt: (*tktypes.HexUint256)(salt), + Owner: tktypes.MustParseHexBytes(compressedKeyStr), + Amount: amount, + } + + newState, err := z.makeNewState(ctx, false, newCoin, resolvedRecipient.Lookup) + if err != nil { + return nil, nil, i18n.NewError(ctx, msgs.MsgErrorCreateNewState, err) + } + return newCoin, newState, nil +} + func (z *Zeto) findAvailableStates(ctx context.Context, useNullifiers bool, stateQueryContext, query string) ([]*pb.StoredState, error) { req := &pb.FindAvailableStatesRequest{ StateQueryContext: stateQueryContext, diff --git a/domains/zeto/internal/zeto/utils.go b/domains/zeto/internal/zeto/utils.go index 6995a95ce..b642efe93 100644 --- a/domains/zeto/internal/zeto/utils.go +++ b/domains/zeto/internal/zeto/utils.go @@ -17,47 +17,21 @@ package zeto import ( "context" + "math/big" + "github.com/hyperledger-labs/zeto/go-sdk/pkg/sparse-merkle-tree/core" + "github.com/hyperledger-labs/zeto/go-sdk/pkg/sparse-merkle-tree/node" "github.com/hyperledger/firefly-common/pkg/i18n" "github.com/iden3/go-iden3-crypto/babyjub" "github.com/kaleido-io/paladin/domains/zeto/internal/msgs" - "github.com/kaleido-io/paladin/domains/zeto/pkg/constants" + "github.com/kaleido-io/paladin/domains/zeto/internal/zeto/smt" corepb "github.com/kaleido-io/paladin/domains/zeto/pkg/proto" "github.com/kaleido-io/paladin/domains/zeto/pkg/types" + "github.com/kaleido-io/paladin/domains/zeto/pkg/zetosigner" "github.com/kaleido-io/paladin/toolkit/pkg/prototk" "github.com/kaleido-io/paladin/toolkit/pkg/tktypes" ) -func isNullifiersCircuit(circuitId string) bool { - return circuitId == constants.CIRCUIT_ANON_NULLIFIER || circuitId == constants.CIRCUIT_ANON_NULLIFIER_BATCH -} - -func isNullifiersToken(tokenName string) bool { - return tokenName == constants.TOKEN_ANON_NULLIFIER -} - -func isEncryptionToken(tokenName string) bool { - return tokenName == constants.TOKEN_ANON_ENC -} - -// the Zeto implementations support two input/output sizes for the circuits: 2 and 10, -// if the input or output size is larger than 2, then the batch circuit is used with -// input/output size 10 -func getInputSize(sizeOfEndorsableStates int) int { - if sizeOfEndorsableStates <= 2 { - return 2 - } - return 10 -} - -func loadBabyJubKey(payload []byte) (*babyjub.PublicKey, error) { - var keyCompressed babyjub.PublicKeyComp - if err := keyCompressed.UnmarshalText(payload); err != nil { - return nil, err - } - return keyCompressed.Decompress() -} - func validateTransferParams(ctx context.Context, params []*types.TransferParamEntry) error { if len(params) == 0 { return i18n.NewError(ctx, msgs.MsgNoTransferParams) @@ -116,3 +90,81 @@ func encodeProof(proof *corepb.SnarkProof) map[string]interface{} { "pC": []string{proof.C[0], proof.C[1]}, } } + +func loadBabyJubKey(payload []byte) (*babyjub.PublicKey, error) { + var keyCompressed babyjub.PublicKeyComp + if err := keyCompressed.UnmarshalText(payload); err != nil { + return nil, err + } + return keyCompressed.Decompress() +} + +func generateMerkleProofs(ctx context.Context, zeto *Zeto, tokenName string, stateQueryContext string, contractAddress *tktypes.EthAddress, inputCoins []*types.ZetoCoin) ([]core.Proof, *corepb.ProvingRequestExtras_Nullifiers, error) { + smtName := smt.MerkleTreeName(tokenName, contractAddress) + storage := smt.NewStatesStorage(zeto.Callbacks, smtName, stateQueryContext, zeto.merkleTreeRootSchema.Id, zeto.merkleTreeNodeSchema.Id) + mt, err := smt.NewSmt(storage) + if err != nil { + return nil, nil, i18n.NewError(ctx, msgs.MsgErrorNewSmt, smtName, err) + } + // verify that the input UTXOs have been indexed by the Merkle tree DB + // and generate a merkle proof for each + var indexes []*big.Int + for _, coin := range inputCoins { + pubKey, err := zetosigner.DecodeBabyJubJubPublicKey(coin.Owner.String()) + if err != nil { + return nil, nil, i18n.NewError(ctx, msgs.MsgErrorLoadOwnerPubKey, err) + } + idx := node.NewFungible(coin.Amount.Int(), pubKey, coin.Salt.Int()) + leaf, err := node.NewLeafNode(idx) + if err != nil { + return nil, nil, i18n.NewError(ctx, msgs.MsgErrorNewLeafNode, err) + } + n, err := mt.GetNode(leaf.Ref()) + if err != nil { + // TODO: deal with when the node is not found in the DB tables for the tree + // e.g because the transaction event hasn't been processed yet + return nil, nil, i18n.NewError(ctx, msgs.MsgErrorQueryLeafNode, leaf.Ref().Hex(), err) + } + hash, err := coin.Hash(ctx) + if err != nil { + return nil, nil, i18n.NewError(ctx, msgs.MsgErrorHashInputState, err) + } + if n.Index().BigInt().Cmp(hash.Int()) != 0 { + expectedIndex, err := node.NewNodeIndexFromBigInt(hash.Int()) + if err != nil { + return nil, nil, i18n.NewError(ctx, msgs.MsgErrorNewNodeIndex, err) + } + return nil, nil, i18n.NewError(ctx, msgs.MsgErrorHashMismatch, leaf.Ref().Hex(), n.Index().BigInt().Text(16), n.Index().Hex(), hash.HexString0xPrefix(), expectedIndex.Hex()) + } + indexes = append(indexes, n.Index().BigInt()) + } + mtRoot := mt.Root() + proofs, _, err := mt.GenerateProofs(indexes, mtRoot) + if err != nil { + return nil, nil, i18n.NewError(ctx, msgs.MsgErrorGenerateMTP, err) + } + var mps []*corepb.MerkleProof + var enabled []bool + for i, proof := range proofs { + cp, err := proof.ToCircomVerifierProof(indexes[i], indexes[i], mtRoot, smt.SMT_HEIGHT_UTXO) + if err != nil { + return nil, nil, i18n.NewError(ctx, msgs.MsgErrorConvertToCircomProof, err) + } + proofSiblings := make([]string, len(cp.Siblings)-1) + for i, s := range cp.Siblings[0 : len(cp.Siblings)-1] { + proofSiblings[i] = s.BigInt().Text(16) + } + p := corepb.MerkleProof{ + Nodes: proofSiblings, + } + mps = append(mps, &p) + enabled = append(enabled, true) + } + extrasObj := corepb.ProvingRequestExtras_Nullifiers{ + Root: mt.Root().BigInt().Text(16), + MerkleProofs: mps, + Enabled: enabled, + } + + return proofs, &extrasObj, nil +} diff --git a/domains/zeto/internal/zeto/zeto.go b/domains/zeto/internal/zeto/zeto.go index eb1f8c0c7..a73133abc 100644 --- a/domains/zeto/internal/zeto/zeto.go +++ b/domains/zeto/internal/zeto/zeto.go @@ -27,6 +27,7 @@ import ( "github.com/hyperledger/firefly-signer/pkg/ethtypes" "github.com/iden3/go-iden3-crypto/babyjub" "github.com/kaleido-io/paladin/domains/zeto/internal/msgs" + "github.com/kaleido-io/paladin/domains/zeto/internal/zeto/common" "github.com/kaleido-io/paladin/domains/zeto/internal/zeto/signer" "github.com/kaleido-io/paladin/domains/zeto/internal/zeto/smt" "github.com/kaleido-io/paladin/domains/zeto/pkg/types" @@ -53,6 +54,7 @@ type Zeto struct { mintSignature string transferSignature string transferWithEncSignature string + withdrawSignature string snarkProver signerapi.InMemorySigner } @@ -75,6 +77,13 @@ type TransferWithEncryptedValuesEvent struct { EncryptedValues []tktypes.HexUint256 `json:"encryptedValues"` } +type WithdrawEvent struct { + Amount tktypes.HexUint256 `json:"amount"` + Inputs []tktypes.HexUint256 `json:"inputs"` + Output tktypes.HexUint256 `json:"output"` + Data tktypes.HexBytes `json:"data"` +} + var factoryDeployABI = &abi.Entry{ Type: abi.Function, Name: "deploy", @@ -284,6 +293,8 @@ func (z *Zeto) GetHandler(method string) types.DomainHandler { return &lockHandler{zeto: z} case "deposit": return &depositHandler{zeto: z} + case "withdraw": + return &withdrawHandler{zeto: z} default: return nil } @@ -360,6 +371,8 @@ func (z *Zeto) registerEventSignatures(eventAbis abi.ABI) { z.transferSignature = event.SolString() case "UTXOTransferWithEncryptedValues": z.transferWithEncSignature = event.SolString() + case "UTXOWithdraw": + z.withdrawSignature = event.SolString() } } } @@ -381,7 +394,7 @@ func (z *Zeto) HandleEventBatch(ctx context.Context, req *prototk.HandleEventBat var smtName string var storage smt.StatesStorage var tree core.SparseMerkleTree - if isNullifiersCircuit(domainConfig.CircuitId) { + if common.IsNullifiersToken(domainConfig.TokenName) { smtName = smt.MerkleTreeName(domainConfig.TokenName, contractAddress) storage = smt.NewStatesStorage(z.Callbacks, smtName, req.StateQueryContext, z.merkleTreeRootSchema.Id, z.merkleTreeNodeSchema.Id) tree, err = smt.NewSmt(storage) @@ -398,6 +411,8 @@ func (z *Zeto) HandleEventBatch(ctx context.Context, req *prototk.HandleEventBat err = z.handleTransferEvent(ctx, tree, storage, ev, domainConfig.TokenName, &res) case z.transferWithEncSignature: err = z.handleTransferWithEncryptionEvent(ctx, tree, storage, ev, domainConfig.TokenName, &res) + case z.withdrawSignature: + err = z.handleWithdrawEvent(ctx, tree, storage, ev, domainConfig.TokenName, &res) } if err != nil { errors = append(errors, err.Error()) @@ -406,7 +421,7 @@ func (z *Zeto) HandleEventBatch(ctx context.Context, req *prototk.HandleEventBat if len(errors) > 0 { return &res, i18n.NewError(ctx, msgs.MsgErrorHandleEvents, formatErrors(errors)) } - if isNullifiersCircuit(domainConfig.CircuitId) { + if common.IsNullifiersToken(domainConfig.TokenName) { newStatesForSMT, err := storage.GetNewStates() if err != nil { return nil, i18n.NewError(ctx, msgs.MsgErrorGetNewSmtStates, smtName, err) diff --git a/domains/zeto/internal/zeto/zeto_test.go b/domains/zeto/internal/zeto/zeto_test.go index 208b26798..6b8a7468f 100644 --- a/domains/zeto/internal/zeto/zeto_test.go +++ b/domains/zeto/internal/zeto/zeto_test.go @@ -479,6 +479,11 @@ func TestHandleEventBatch(t *testing.T) { assert.NoError(t, err) assert.Len(t, res4.TransactionsComplete, 1) assert.Len(t, res4.NewStates, 2) + + req.Events[0].SoliditySignature = "event UTXOWithdraw(uint256 amount, uint256[] inputs, uint256 output, address indexed submitter, bytes data)" + req.Events[0].DataJson = "{\"data\":\"0x0001000030e43028afbb41d6887444f4c2b4ed6d00000000000000000000000000000000\",\"output\":\"7980718117603030807695495350922077879582656644717071592146865497574198464253\",\"submitter\":\"0x74e71b05854ee819cb9397be01c82570a178d019\"}" + _, err = z.HandleEventBatch(ctx, req) + assert.NoError(t, err) } func TestGetVerifier(t *testing.T) { @@ -650,5 +655,19 @@ func TestGetHandler(t *testing.T) { assert.NotNil(t, z.GetHandler("mint")) assert.NotNil(t, z.GetHandler("transfer")) assert.NotNil(t, z.GetHandler("lockProof")) + assert.NotNil(t, z.GetHandler("deposit")) + assert.NotNil(t, z.GetHandler("withdraw")) assert.Nil(t, z.GetHandler("bad")) } + +func TestUnimplementedMethods(t *testing.T) { + z := &Zeto{} + _, err := z.InitCall(context.Background(), nil) + assert.ErrorContains(t, err, "PD210085: Not implemented") + + _, err = z.ExecCall(context.Background(), nil) + assert.ErrorContains(t, err, "PD210085: Not implemented") + + _, err = z.BuildReceipt(context.Background(), nil) + assert.ErrorContains(t, err, "PD210102: Not implemented") +} diff --git a/domains/zeto/pkg/types/abi.go b/domains/zeto/pkg/types/abi.go index 10e41eb8f..32aaf279c 100644 --- a/domains/zeto/pkg/types/abi.go +++ b/domains/zeto/pkg/types/abi.go @@ -66,6 +66,13 @@ var ZetoABI = abi.ABI{ {Name: "call", Type: "bytes"}, // assumed to be an encoded "transfer" }, }, + { + Name: "setERC20", + Type: abi.Function, + Inputs: abi.ParameterArray{ + {Name: "_erc20", Type: "address"}, + }, + }, { Name: "deposit", Type: abi.Function, @@ -73,6 +80,13 @@ var ZetoABI = abi.ABI{ {Name: "amount", Type: "uint256"}, }, }, + { + Name: "withdraw", + Type: abi.Function, + Inputs: abi.ParameterArray{ + {Name: "amount", Type: "uint256"}, + }, + }, } type InitializerParams struct { @@ -95,10 +109,6 @@ type TransferParams struct { Transfers []*TransferParamEntry `json:"transfers"` } -type DepositParams struct { - Amount *tktypes.HexUint256 `json:"amount"` -} - type TransferParamEntry struct { To string `json:"to"` Amount *tktypes.HexUint256 `json:"amount"` @@ -108,3 +118,11 @@ type LockParams struct { Delegate *tktypes.EthAddress `json:"delegate"` Call tktypes.HexBytes `json:"call"` } + +type DepositParams struct { + Amount *tktypes.HexUint256 `json:"amount"` +} + +type WithdrawParams struct { + Amount *tktypes.HexUint256 `json:"amount"` +} From 658333d47d22d3b466335d3485a765600cf2f2be Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Fri, 6 Dec 2024 15:24:16 -0500 Subject: [PATCH 16/36] Update Zeto example to incorporate the deposit/withdraw flow Signed-off-by: Jim Zhang --- example/zeto/package-lock.json | 301 +++++------------------------ example/zeto/src/index.ts | 144 +++++++++++--- sdk/typescript/src/domains/zeto.ts | 236 +++++++++++++++++++++- 3 files changed, 400 insertions(+), 281 deletions(-) diff --git a/example/zeto/package-lock.json b/example/zeto/package-lock.json index a610f4618..dcca8146a 100644 --- a/example/zeto/package-lock.json +++ b/example/zeto/package-lock.json @@ -33,253 +33,10 @@ "typescript": "^5.6.3" } }, - "../../sdk/typescript/node_modules/@adraffy/ens-normalize": { - "version": "1.10.1", - "license": "MIT" - }, - "../../sdk/typescript/node_modules/@noble/curves": { - "version": "1.2.0", - "license": "MIT", - "dependencies": { - "@noble/hashes": "1.3.2" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "../../sdk/typescript/node_modules/@noble/hashes": { - "version": "1.3.2", - "license": "MIT", - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "../../sdk/typescript/node_modules/@types/node": { - "version": "22.9.0", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.19.8" - } - }, - "../../sdk/typescript/node_modules/aes-js": { - "version": "4.0.0-beta.5", - "license": "MIT" - }, - "../../sdk/typescript/node_modules/asynckit": { - "version": "0.4.0", - "license": "MIT" - }, - "../../sdk/typescript/node_modules/axios": { - "version": "1.7.7", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, - "../../sdk/typescript/node_modules/combined-stream": { - "version": "1.0.8", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "../../sdk/typescript/node_modules/copy-file": { - "version": "11.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.11", - "p-event": "^6.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "../../sdk/typescript/node_modules/delayed-stream": { - "version": "1.0.0", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "../../sdk/typescript/node_modules/ethers": { - "version": "6.13.4", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/ethers-io/" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@adraffy/ens-normalize": "1.10.1", - "@noble/curves": "1.2.0", - "@noble/hashes": "1.3.2", - "@types/node": "22.7.5", - "aes-js": "4.0.0-beta.5", - "tslib": "2.7.0", - "ws": "8.17.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "../../sdk/typescript/node_modules/ethers/node_modules/@types/node": { - "version": "22.7.5", - "license": "MIT", - "dependencies": { - "undici-types": "~6.19.2" - } - }, - "../../sdk/typescript/node_modules/follow-redirects": { - "version": "1.15.9", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "../../sdk/typescript/node_modules/form-data": { - "version": "4.0.1", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "../../sdk/typescript/node_modules/graceful-fs": { - "version": "4.2.11", - "dev": true, - "license": "ISC" - }, - "../../sdk/typescript/node_modules/mime-db": { - "version": "1.52.0", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "../../sdk/typescript/node_modules/mime-types": { - "version": "2.1.35", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "../../sdk/typescript/node_modules/p-event": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "p-timeout": "^6.1.2" - }, - "engines": { - "node": ">=16.17" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "../../sdk/typescript/node_modules/p-timeout": { - "version": "6.1.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "../../sdk/typescript/node_modules/proxy-from-env": { - "version": "1.1.0", - "license": "MIT" - }, - "../../sdk/typescript/node_modules/tslib": { - "version": "2.7.0", - "license": "0BSD" - }, - "../../sdk/typescript/node_modules/typescript": { - "version": "5.6.3", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "../../sdk/typescript/node_modules/undici-types": { - "version": "6.19.8", - "license": "MIT" - }, - "../../sdk/typescript/node_modules/uuid": { - "version": "11.0.2", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/esm/bin/uuid" - } - }, - "../../sdk/typescript/node_modules/ws": { - "version": "8.17.1", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "dev": true, "license": "MIT", "dependencies": { @@ -291,6 +48,8 @@ }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, "license": "MIT", "engines": { @@ -299,11 +58,15 @@ }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", "dev": true, "license": "MIT", "dependencies": { @@ -313,34 +76,46 @@ }, "node_modules/@tsconfig/node10": { "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", "dev": true, "license": "MIT" }, "node_modules/@tsconfig/node12": { "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", "dev": true, "license": "MIT" }, "node_modules/@tsconfig/node14": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", "dev": true, "license": "MIT" }, "node_modules/@tsconfig/node16": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "dev": true, "license": "MIT" }, "node_modules/@types/node": { - "version": "22.9.0", + "version": "22.10.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.1.tgz", + "integrity": "sha512-qKgsUwfHZV2WCWLAnVP1JqnpE6Im6h3Y0+fYgMTasNQ7V++CBX5OT1as0g0f+OyubbFqhf6XVNIsmN4IIhEgGQ==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~6.19.8" + "undici-types": "~6.20.0" } }, "node_modules/acorn": { "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "dev": true, "license": "MIT", "bin": { @@ -352,6 +127,8 @@ }, "node_modules/acorn-walk": { "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", "dev": true, "license": "MIT", "dependencies": { @@ -363,11 +140,15 @@ }, "node_modules/arg": { "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", "dev": true, "license": "MIT" }, "node_modules/copy-file": { "version": "11.0.0", + "resolved": "https://registry.npmjs.org/copy-file/-/copy-file-11.0.0.tgz", + "integrity": "sha512-mFsNh/DIANLqFt5VHZoGirdg7bK5+oTWlhnGu6tgRhzBlnEKWaPX2xrFaLltii/6rmhqFMJqffUgknuRdpYlHw==", "dev": true, "license": "MIT", "dependencies": { @@ -383,11 +164,15 @@ }, "node_modules/create-require": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true, "license": "MIT" }, "node_modules/diff": { "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -396,16 +181,22 @@ }, "node_modules/graceful-fs": { "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true, "license": "ISC" }, "node_modules/make-error": { "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true, "license": "ISC" }, "node_modules/p-event": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/p-event/-/p-event-6.0.1.tgz", + "integrity": "sha512-Q6Bekk5wpzW5qIyUP4gdMEujObYstZl6DMMOSenwBvV0BlE5LkDwkjs5yHbZmdCEq2o4RJx4tE1vwxFVf2FG1w==", "dev": true, "license": "MIT", "dependencies": { @@ -420,6 +211,8 @@ }, "node_modules/p-timeout": { "version": "6.1.3", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.3.tgz", + "integrity": "sha512-UJUyfKbwvr/uZSV6btANfb+0t/mOhKV/KXcCUTp8FcQI+v/0d+wXqH4htrW0E4rR6WiEO/EPvUFiV9D5OI4vlw==", "dev": true, "license": "MIT", "engines": { @@ -435,6 +228,8 @@ }, "node_modules/ts-node": { "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, "license": "MIT", "dependencies": { @@ -476,7 +271,9 @@ } }, "node_modules/typescript": { - "version": "5.6.3", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", + "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", "dev": true, "license": "Apache-2.0", "bin": { @@ -488,17 +285,23 @@ } }, "node_modules/undici-types": { - "version": "6.19.8", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", "dev": true, "license": "MIT" }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "dev": true, "license": "MIT" }, "node_modules/yn": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "dev": true, "license": "MIT", "engines": { diff --git a/example/zeto/src/index.ts b/example/zeto/src/index.ts index 194f99bc7..b965b7e5e 100644 --- a/example/zeto/src/index.ts +++ b/example/zeto/src/index.ts @@ -25,25 +25,73 @@ async function main() { const bank2 = `bank2@node2`; // Deploy a Zeto token to represent cash (CBDC) - logger.log("Deploying Zeto CBDC token..."); + logger.log("Use case #1: Privacy-preserving CBDC token, using private minting..."); + logger.log("- Deploying Zeto token..."); const zetoFactory = new ZetoFactory(paladin3, "zeto"); - const zetoCBDC = await zetoFactory.newZeto(cbdcIssuer, { + const zetoCBDC1 = await zetoFactory.newZeto(cbdcIssuer, { tokenName: "Zeto_AnonNullifier", }); - if (zetoCBDC === undefined) { + if (zetoCBDC1 === undefined) { logger.error("Failed!"); return; } - logger.log(`Success! Zeto deployed at: ${zetoCBDC.address}`); + logger.log(` Zeto deployed at: ${zetoCBDC1.address}`); - logger.log("Deploying ERC20 token..."); + // Issue some cash + logger.log("- Issuing CBDC to bank1 and bank2 with private minting..."); + let receipt = await zetoCBDC1.mint(cbdcIssuer, { + mints: [ + { + to: bank1, + amount: 100000, + }, + { + to: bank2, + amount: 100000, + }, + ] + }); + if (receipt === undefined) { + logger.error("Failed!"); + return; + } + logger.log(" Success!"); + + // Transfer some cash from bank1 to bank2 + logger.log("- Bank1 transferring CBDC to bank2 to pay for some asset trades ..."); + receipt = await zetoCBDC1.using(paladin1).transfer(bank1, { + transfers: [ + { + to: bank2, + amount: 1000, + }, + ] + }); + if (receipt === undefined) { + logger.error("Failed!"); + return; + } + logger.log(" Success!\n"); + + logger.log("Use case #2: Privacy-preserving CBDC token, using public minting of an ERC20 token..."); + logger.log("- Deploying Zeto token..."); + const zetoCBDC2 = await zetoFactory.newZeto(cbdcIssuer, { + tokenName: "Zeto_AnonNullifier", + }); + if (zetoCBDC2 === undefined) { + logger.error("Failed!"); + return; + } + logger.log(` Zeto deployed at: ${zetoCBDC2.address}`); + + logger.log("- Deploying ERC20 token to manage the CBDC supply publicly..."); const cbdcIssuerAddress = await paladin1.resolveVerifier( cbdcIssuer, Algorithms.ECDSA_SECP256K1, Verifiers.ETH_ADDRESS ); - const txId = await paladin3.sendTransaction({ + const txId1 = await paladin3.sendTransaction({ type: TransactionType.PUBLIC, from: cbdcIssuer, data: { @@ -53,51 +101,69 @@ async function main() { abi: erc20Abi.abi, bytecode: erc20Abi.bytecode, }); - if (txId === undefined) { + if (txId1 === undefined) { logger.error("Failed!"); return; } - const result1 = await paladin3.pollForReceipt(txId, 5000); + const result1 = await paladin3.pollForReceipt(txId1, 5000); if (result1 === undefined) { logger.error("Failed!"); return; } const erc20Address = result1.contractAddress; - logger.log(`Success! ERC20 deployed at: ${erc20Address}`); + logger.log(` ERC20 deployed at: ${erc20Address}`); - logger.log("Setting ERC20 to the Zeto token contract ..."); - const result2 = await zetoCBDC.setERC20(cbdcIssuer, { + logger.log("- Setting ERC20 to the Zeto token contract ..."); + const result2 = await zetoCBDC2.setERC20(cbdcIssuer, { _erc20: erc20Address as string }); - if (result1 === undefined) { + if (result2 === undefined) { logger.error("Failed!"); return; } - logger.log(`Success! ERC20 configured on the Zeto contract`); + logger.log(` ERC20 configured on the Zeto contract`); - // Issue some cash - logger.log("Issuing CBDC to bank1 and bank2 ..."); - let receipt = await zetoCBDC.mint(cbdcIssuer, { - mints: [ - { - to: bank1, - amount: 100000, - }, - { - to: bank2, - amount: 100000, - }, - ] + logger.log("- Issuing CBDC to bank1 with public minting in ERC20..."); + const bank1Address = await paladin1.resolveVerifier( + bank1, + Algorithms.ECDSA_SECP256K1, + Verifiers.ETH_ADDRESS + ); + const txId2 = await paladin3.sendTransaction({ + type: TransactionType.PUBLIC, + from: cbdcIssuer, + to: erc20Address, + data: { + "amount": 100000, + "to": bank1Address, + }, + function: "mint", + abi: erc20Abi.abi, }); - if (receipt === undefined) { + if (txId2 === undefined) { + logger.error("Failed!"); + return; + } + const result3 = await paladin3.pollForReceipt(txId2, 5000); + if (result3 === undefined) { + logger.error("Failed!"); + return; + } + logger.log(" Success!"); + + logger.log("- Bank1 deposit ERC20 balance to Zeto ..."); + const result4 = await zetoCBDC2.using(paladin1).deposit(bank1, { + amount: 10000 + }); + if (result4 === undefined) { logger.error("Failed!"); return; } - logger.log("Success!"); + logger.log(` Bank1 deposit successful`); // Transfer some cash from bank1 to bank2 - logger.log("Bank1 transferring CBDC to bank2 to pay for some asset trades ..."); - receipt = await zetoCBDC.using(paladin1).transfer(bank1, { + logger.log("- Bank1 transferring CBDC to bank2 to pay for some asset trades ..."); + receipt = await zetoCBDC2.using(paladin1).transfer(bank1, { transfers: [ { to: bank2, @@ -109,7 +175,23 @@ async function main() { logger.error("Failed!"); return; } - logger.log("Success!"); + logger.log(" Success!"); + + logger.log("- Bank1 withdraws Zeto back to ERC20 balance ..."); + const result5 = await zetoCBDC2.using(paladin1).withdraw(bank1, { + amount: 1000 + }); + if (result5 === undefined) { + logger.error("Failed!"); + return; + } + logger.log(` Bank1 withdraw successful`); + + logger.log("\nSuccess!"); +} + +function getFunctionAbi(abi: any, functionName: string): any { + return abi.find((element: any) => element.name === functionName); } if (require.main === module) { diff --git a/sdk/typescript/src/domains/zeto.ts b/sdk/typescript/src/domains/zeto.ts index 88d1d40b8..cdb4f9715 100644 --- a/sdk/typescript/src/domains/zeto.ts +++ b/sdk/typescript/src/domains/zeto.ts @@ -51,6 +51,28 @@ const zetoPrivateAbi = [{ } ], "outputs": [] +}, { + "type": "function", + "name": "deposit", + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "outputs": [] +}, { + "type": "function", + "name": "withdraw", + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "outputs": [] }]; const zetoPublicAbi = [{ @@ -65,6 +87,138 @@ const zetoPublicAbi = [{ "outputs": [], "stateMutability": "nonpayable", "type": "function" +}, { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "outputs", + "type": "uint256[]" + }, + { + "components": [ + { + "internalType": "uint256[2]", + "name": "pA", + "type": "uint256[2]" + }, + { + "internalType": "uint256[2][2]", + "name": "pB", + "type": "uint256[2][2]" + }, + { + "internalType": "uint256[2]", + "name": "pC", + "type": "uint256[2]" + } + ], + "internalType": "struct Commonlib.Proof", + "name": "proof", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "deposit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" +}, { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "nullifiers", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "output", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "root", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint256[2]", + "name": "pA", + "type": "uint256[2]" + }, + { + "internalType": "uint256[2][2]", + "name": "pB", + "type": "uint256[2][2]" + }, + { + "internalType": "uint256[2]", + "name": "pC", + "type": "uint256[2]" + } + ], + "internalType": "struct Commonlib.Proof", + "name": "proof", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "withdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" +}]; + +const erc20Abi = [{ + "type": "function", + "name": "mint", + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "outputs": [] +}, { + "type": "function", + "name": "approve", + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "outputs": [] }]; export const zetoConstructorABI = { @@ -93,6 +247,14 @@ export interface ZetoTransfer { amount: string | number; } +export interface ZetoDepositParams { + amount: string | number; +} + +export interface ZetoWithdrawParams { + amount: string | number; +} + export class ZetoFactory { private options: Required; @@ -132,6 +294,7 @@ export class ZetoFactory { export class ZetoInstance { private options: Required; + private erc20?: string; constructor( private paladin: PaladinClient, @@ -145,7 +308,9 @@ export class ZetoInstance { } using(paladin: PaladinClient) { - return new ZetoInstance(paladin, this.address, this.options); + const zeto = new ZetoInstance(paladin, this.address, this.options); + zeto.erc20 = this.erc20; + return zeto; } async mint(from: string, data: ZetoMintParams) { @@ -181,6 +346,75 @@ export class ZetoInstance { from, data, }); + const result = await this.paladin.pollForReceipt(txID, POLL_TIMEOUT_MS); + if (result === undefined) { + throw new Error("Failed to set ERC20"); + } + this.erc20 = data._erc20; + return result; + } + + async deposit(from: string, data: ZetoDepositParams) { + // first approve the Zeto contract to draw the amount from our balance in the ERC20 + const txID1 = await this.paladin.sendTransaction({ + type: TransactionType.PUBLIC, + abi: erc20Abi, + function: "approve", + to: this.erc20, + from, + data: { value: data.amount, spender: this.address }, + }); + const result1 = await this.paladin.pollForReceipt(txID1, POLL_TIMEOUT_MS); + if (result1 === undefined) { + throw new Error("Failed to approve transfer"); + } + + const depositID = await this.paladin.prepareTransaction({ + type: TransactionType.PRIVATE, + abi: zetoPrivateAbi, + function: "deposit", + to: this.address, + from, + data, + }); + const result2 = await this.paladin.pollForPreparedTransaction(depositID, this.options.pollTimeout); + if (result2 === undefined) { + throw new Error("Failed to prepare deposit"); + } + + const txID2 = await this.paladin.sendTransaction({ + type: TransactionType.PUBLIC, + abi: zetoPublicAbi, + function: "deposit", + to: this.address, + from, + data: result2!.transaction.data, + }); + return this.paladin.pollForReceipt(txID2, POLL_TIMEOUT_MS); + } + + async withdraw(from: string, data: ZetoWithdrawParams) { + const withdrawID = await this.paladin.prepareTransaction({ + type: TransactionType.PRIVATE, + abi: zetoPrivateAbi, + function: "withdraw", + to: this.address, + from, + data, + }); + const result1 = await this.paladin.pollForPreparedTransaction(withdrawID, this.options.pollTimeout); + if (result1 === undefined) { + throw new Error("Failed to prepare withdraw"); + } + + const txID = await this.paladin.sendTransaction({ + type: TransactionType.PUBLIC, + abi: zetoPublicAbi, + function: "withdraw", + to: this.address, + from, + data: result1!.transaction.data, + }); return this.paladin.pollForReceipt(txID, POLL_TIMEOUT_MS); } } From 19133ea1bd0d6b5c0cfd8c38c8f47efc1eed9503 Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Fri, 6 Dec 2024 15:52:08 -0500 Subject: [PATCH 17/36] add copyContracts as dependency to build Signed-off-by: Jim Zhang --- example/zeto/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/zeto/build.gradle b/example/zeto/build.gradle index 7314f0f34..5d653280a 100644 --- a/example/zeto/build.gradle +++ b/example/zeto/build.gradle @@ -50,7 +50,7 @@ task install(type: Exec) { outputs.dir("node_modules") } -task build(type: Exec, dependsOn: [install]) { +task build(type: Exec, dependsOn: [install, copyContracts]) { executable 'npm' args 'run' args 'build' From fda5a06aa6b555a054b281c8ba683fea76cff40e Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Fri, 6 Dec 2024 16:27:41 -0500 Subject: [PATCH 18/36] Update domain integration-test for Zeto deployments Signed-off-by: Jim Zhang --- domains/integration-test/zeto/config-for-deploy.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/domains/integration-test/zeto/config-for-deploy.yaml b/domains/integration-test/zeto/config-for-deploy.yaml index 4ef29632b..2ec0d71d1 100644 --- a/domains/integration-test/zeto/config-for-deploy.yaml +++ b/domains/integration-test/zeto/config-for-deploy.yaml @@ -23,6 +23,9 @@ contracts: - name: Zeto_Anon verifier: Groth16Verifier_Anon batchVerifier: Groth16Verifier_AnonBatch + depositVerifier: Groth16Verifier_CheckHashesValue + withdrawVerifier: Groth16Verifier_CheckInputsOutputsValue + batchWithdrawVerifier: Groth16Verifier_CheckInputsOutputsValueBatch circuitId: anon cloneable: true abiAndBytecode: From 29d841dedcf1ac454f3961fd52eda9af1d00a09d Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Mon, 9 Dec 2024 11:09:15 -0500 Subject: [PATCH 19/36] Update zeto example to make erc20 deploy a function Signed-off-by: Jim Zhang --- example/zeto/src/index.ts | 58 +++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/example/zeto/src/index.ts b/example/zeto/src/index.ts index b965b7e5e..224a6ce29 100644 --- a/example/zeto/src/index.ts +++ b/example/zeto/src/index.ts @@ -1,6 +1,5 @@ import PaladinClient, { Algorithms, - newTransactionId, ZetoFactory, TransactionType, Verifiers, @@ -85,32 +84,7 @@ async function main() { logger.log(` Zeto deployed at: ${zetoCBDC2.address}`); logger.log("- Deploying ERC20 token to manage the CBDC supply publicly..."); - const cbdcIssuerAddress = await paladin1.resolveVerifier( - cbdcIssuer, - Algorithms.ECDSA_SECP256K1, - Verifiers.ETH_ADDRESS - ); - - const txId1 = await paladin3.sendTransaction({ - type: TransactionType.PUBLIC, - from: cbdcIssuer, - data: { - "initialOwner": cbdcIssuerAddress, - }, - function: "", - abi: erc20Abi.abi, - bytecode: erc20Abi.bytecode, - }); - if (txId1 === undefined) { - logger.error("Failed!"); - return; - } - const result1 = await paladin3.pollForReceipt(txId1, 5000); - if (result1 === undefined) { - logger.error("Failed!"); - return; - } - const erc20Address = result1.contractAddress; + const erc20Address = await deployERC20(paladin3, cbdcIssuer); logger.log(` ERC20 deployed at: ${erc20Address}`); logger.log("- Setting ERC20 to the Zeto token contract ..."); @@ -190,8 +164,34 @@ async function main() { logger.log("\nSuccess!"); } -function getFunctionAbi(abi: any, functionName: string): any { - return abi.find((element: any) => element.name === functionName); +async function deployERC20(paladin: PaladinClient, cbdcIssuer: string): Promise { + const cbdcIssuerAddress = await paladin.resolveVerifier( + cbdcIssuer, + Algorithms.ECDSA_SECP256K1, + Verifiers.ETH_ADDRESS + ); + + const txId1 = await paladin3.sendTransaction({ + type: TransactionType.PUBLIC, + from: cbdcIssuer, + data: { + "initialOwner": cbdcIssuerAddress, + }, + function: "", + abi: erc20Abi.abi, + bytecode: erc20Abi.bytecode, + }); + if (txId1 === undefined) { + logger.error("Failed!"); + return; + } + const result1 = await paladin.pollForReceipt(txId1, 5000); + if (result1 === undefined) { + logger.error("Failed!"); + return; + } + const erc20Address = result1.contractAddress; + return erc20Address; } if (require.main === module) { From 597f819737e647c30330367c07006f7eb31d9334 Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Mon, 9 Dec 2024 11:42:54 -0500 Subject: [PATCH 20/36] Move erc20 mint to a function Signed-off-by: Jim Zhang --- example/zeto/src/index.ts | 54 +++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/example/zeto/src/index.ts b/example/zeto/src/index.ts index 224a6ce29..56fff88bf 100644 --- a/example/zeto/src/index.ts +++ b/example/zeto/src/index.ts @@ -98,31 +98,7 @@ async function main() { logger.log(` ERC20 configured on the Zeto contract`); logger.log("- Issuing CBDC to bank1 with public minting in ERC20..."); - const bank1Address = await paladin1.resolveVerifier( - bank1, - Algorithms.ECDSA_SECP256K1, - Verifiers.ETH_ADDRESS - ); - const txId2 = await paladin3.sendTransaction({ - type: TransactionType.PUBLIC, - from: cbdcIssuer, - to: erc20Address, - data: { - "amount": 100000, - "to": bank1Address, - }, - function: "mint", - abi: erc20Abi.abi, - }); - if (txId2 === undefined) { - logger.error("Failed!"); - return; - } - const result3 = await paladin3.pollForReceipt(txId2, 5000); - if (result3 === undefined) { - logger.error("Failed!"); - return; - } + await mintERC20(paladin3, cbdcIssuer, bank1, erc20Address!, 100000); logger.log(" Success!"); logger.log("- Bank1 deposit ERC20 balance to Zeto ..."); @@ -194,6 +170,34 @@ async function deployERC20(paladin: PaladinClient, cbdcIssuer: string): Promise< return erc20Address; } +async function mintERC20(paladin: PaladinClient, cbdcIssuer: string, bank1: string, erc20Address: string, amount: number): Promise { + const bank1Address = await paladin.resolveVerifier( + bank1, + Algorithms.ECDSA_SECP256K1, + Verifiers.ETH_ADDRESS + ); + const txId2 = await paladin.sendTransaction({ + type: TransactionType.PUBLIC, + from: cbdcIssuer, + to: erc20Address, + data: { + "amount": amount, + "to": bank1Address, + }, + function: "mint", + abi: erc20Abi.abi, + }); + if (txId2 === undefined) { + logger.error("Failed!"); + return; + } + const result3 = await paladin.pollForReceipt(txId2, 5000); + if (result3 === undefined) { + logger.error("Failed!"); + return; + } +} + if (require.main === module) { main().catch((err) => { console.error("Exiting with uncaught error"); From 0892196ba76161e9ed07815cec7ae81dffb6b826 Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Mon, 9 Dec 2024 11:43:14 -0500 Subject: [PATCH 21/36] Update the zeto cash tutorial Signed-off-by: Jim Zhang --- doc-site/docs/tutorials/zkp-cbdc.md | 91 +++++++++++++++++++++++++++-- 1 file changed, 86 insertions(+), 5 deletions(-) diff --git a/doc-site/docs/tutorials/zkp-cbdc.md b/doc-site/docs/tutorials/zkp-cbdc.md index e20bc3da9..2ed158338 100644 --- a/doc-site/docs/tutorials/zkp-cbdc.md +++ b/doc-site/docs/tutorials/zkp-cbdc.md @@ -1,8 +1,8 @@ -# CBDC Tokens based on Zeto +# Cash Tokens based on Zeto The code for this tutorial can be found in [example/zeto](https://github.com/LF-Decentralized-Trust-labs/paladin/blob/main/example/zeto). -This shows how to leverage the [Zeto](../../architecture/zeto/) in order to build a wholesale CBDC with privacy, illustrating multiple aspects of Paladin's privacy capabilities. +This shows how to leverage the [Zeto](../../architecture/zeto/) in order to build a cash payment solution, for instance wholesale CBDC or a payment rail with commercial bank money, with privacy, illustrating multiple aspects of Paladin's privacy capabilities. ## Running the example @@ -10,7 +10,9 @@ Follow the [Getting Started](../../getting-started/installation/) instructions t then follow the example [README](https://github.com/LF-Decentralized-Trust-labs/paladin/blob/main/example/zeto/README.md) to run the code. -## Explanation +## Scenario #1: cash solution with private minting + +In this scenario, the Zeto tokens are directly minted by the authority in the Zeto contract, making the mint amounts private. This also means the total supply of the Zeto tokens is unknown to the participants. Only the authority performing the minting operations is aware of the total supply. Below is a walkthrough of each step in the example, with an explanation of what it does. @@ -23,8 +25,7 @@ const zetoCBDC = await zetoFactory.newZeto(cbdcIssuer, { }); ``` -This creates a new instance of the Zeto domain, using the [Zeto_AnonNullifier](https://github.com/hyperledger-labs/zeto/tree/main?tab=readme-ov-file#zeto_anonnullifier) contract. -This results in a new cloned contract on the base ledger, with a new unique address. This Zeto token contract will be used to represent +This creates a new instance of the Zeto domain, using the [Zeto_AnonNullifier](https://github.com/hyperledger-labs/zeto/tree/main?tab=readme-ov-file#zeto_anonnullifier) contract. This results in a new cloned contract on the base ledger, with a new unique address. This Zeto token contract will be used to represent tokenized cash/CBDC. The token will be minted by the central bank/CBDC issuer party. Minting is restricted to be requested only by the central bank, the @@ -64,3 +65,83 @@ receipt = await zetoCBDC.using(paladin1).transfer(bank1, { Bank1 can call the `transfer` function to transfer zeto tokens to multiple parties, up to 10. Note that the identity `bank1` exists on the `paladin1` instance, therefore it must use that instance to send the transfer transction (`.using(paladin1)`). + +## Scenario #2: cash solution with public minting + +This scenario supports the requirement to make the total supply of the cash tokens public. This is achieved by making the authority perform the minting operations in an ERC20 contract. The participants can then exchange their ERC20 balances for Zeto tokens, by calling `deposit`, and exchange back to their ERC20 balances by calling `withdraw`. + +Below is a walkthrough of each step in the example, with an explanation of what it does. + +### Create CBDC token + +```typescript +const zetoFactory = new ZetoFactory(paladin3, 'zeto'); +const zetoCBDC = await zetoFactory.newZeto(cbdcIssuer, { + tokenName: 'Zeto_AnonNullifier', +}); +``` + +This creates a new instance of the Zeto domain, using the [Zeto_AnonNullifier](https://github.com/hyperledger-labs/zeto/tree/main?tab=readme-ov-file#zeto_anonnullifier) contract. This results in a new cloned contract on the base ledger, with a new unique address. This Zeto token contract will be used to represent +tokenized cash/CBDC. + +### Create public supply token (ERC20) + +```typescript +const erc20Address = await deployERC20(paladin3, cbdcIssuer); +``` + +This deploys the ERC20 token which will be used by the authority to regulate the CBDC supply, with transparency to the paricipants. + +### Configure the Zeto token contract to accept deposits and withdraws from the ERC20 + +```typescript +const result2 = await zetoCBDC.setERC20(cbdcIssuer, { + _erc20: erc20Address as string, +}); +``` + +When the `deposit` function is called on the Zeto contract, this ERC20 contract will be called to draw the requested funds from the depositor's account. Conversely, when the `withdraw` function is called, this ERC20 contract will be called to transfer back the ERC20 balance to the withdrawer's account. + +### Mint ERC20 tokens to publicly regulate CBDC supplies + +```typescript +await mintERC20(paladin3, cbdcIssuer, bank1, erc20Address!, 100000); +``` + +Because the ERC20 implementation provides full transparency of the token operations, minting in the ERC20 allows all blockchain network participants to be aware of the overall supply of the CBDC tokens. + +### Banks exchange ERC20 balances for Zeto tokens - deposit + +```typescript +const result4 = await zetoCBDC.using(paladin1).deposit(bank1, { + amount: 10000, +}); +``` + +After having been minted ERC20 balances, a partcipant like `bank1` can call `deposit` on the Paladin Zeto domain to exchange for Zeto tokens. Behind the scenes, the ERC20 balance is transferred to the Zeto contract which will hold until `withdraw` is called later. + +### Bank1 transfers tokens to bank2 as payment + +```typescript +receipt = await zetoCBDC.using(paladin1).transfer(bank1, { + transfers: [ + { + to: bank2, + amount: 1000, + }, + ], +}); +``` + +Bank1 can call the `transfer` function to transfer zeto tokens to multiple parties, up to 10. Note that the identity `bank1` exists on the `paladin1` instance, +therefore it must use that instance to send the transfer transction (`.using(paladin1)`). + +### Bank1 exchanges Zeto tokens for ERC20 balances - withdraw + +```typescript +const result5 = await zetoCBDC.using(paladin1).withdraw(bank1, { + amount: 1000, +}); +``` + +A participant like `bank1` who has unspent Zeto tokens can call `withdraw` on the Paladin Zeto domain to exchange them for ERC20 balances. Behind the scenes, the requested amount are "burnt" in the Zeto contract, and the corresponding ERC20 amount are released by the Zeto contract, by transferring to the requesting account. From 7cc1da676cfa6e0b05c7d3e767b0e084a31bf2b8 Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Tue, 17 Dec 2024 09:24:35 -0500 Subject: [PATCH 22/36] use .sol for the zeto private ABI Signed-off-by: Jim Zhang --- domains/zeto/build.gradle | 6 ++--- sdk/typescript/scripts/abi.mjs | 5 +++++ .../domains/interfaces/IZetoPrivate.sol | 22 +++++++++++++++++++ 3 files changed, 30 insertions(+), 3 deletions(-) create mode 100644 solidity/contracts/domains/interfaces/IZetoPrivate.sol diff --git a/domains/zeto/build.gradle b/domains/zeto/build.gradle index 20e490e4f..43bb06e93 100644 --- a/domains/zeto/build.gradle +++ b/domains/zeto/build.gradle @@ -74,7 +74,7 @@ dependencies { coreGo project(path: ":core:go", configuration: "goSource") } -task downloadZetoProver(dependsOn: "protoc") { +task downloadZetoProver() { def outname = "zeto-wasm-${zetoVersion}.tar.gz" def url = "https://github.com/${zetoHost}/zeto/releases/download/${zetoVersion}/${outname}" def f = new File(toolsOut, outname) @@ -85,7 +85,7 @@ task downloadZetoProver(dependsOn: "protoc") { outputs.file(f) } -task downloadZetoTestProvingKeys(dependsOn: "protoc") { +task downloadZetoTestProvingKeys() { def outname = "zeto-test-proving-keys-${zetoVersion}.tar.gz" def url = "https://github.com/${zetoHost}/zeto/releases/download/${zetoVersion}/${outname}" def f = new File(toolsOut, outname) @@ -96,7 +96,7 @@ task downloadZetoTestProvingKeys(dependsOn: "protoc") { outputs.file(f) } -task downloadZetoCompiledContracts(dependsOn: "protoc") { +task downloadZetoCompiledContracts() { def outname = "zeto-contracts-${zetoVersion}.tar.gz" def url = "https://github.com/${zetoHost}/zeto/releases/download/${zetoVersion}/${outname}" def f = new File(toolsOut, outname) diff --git a/sdk/typescript/scripts/abi.mjs b/sdk/typescript/scripts/abi.mjs index a0a481f85..9dc579515 100644 --- a/sdk/typescript/scripts/abi.mjs +++ b/sdk/typescript/scripts/abi.mjs @@ -9,3 +9,8 @@ await copyFile( "../../solidity/artifacts/contracts/domains/interfaces/INotoPrivate.sol/INotoPrivate.json", "src/domains/abis/INotoPrivate.json" ); + +await copyFile( + "../../solidity/artifacts/contracts/domains/interfaces/IZetoPrivate.sol/IZetoPrivate.json", + "src/domains/abis/IZetoPrivate.json" +); diff --git a/solidity/contracts/domains/interfaces/IZetoPrivate.sol b/solidity/contracts/domains/interfaces/IZetoPrivate.sol new file mode 100644 index 000000000..7e0c1f1ba --- /dev/null +++ b/solidity/contracts/domains/interfaces/IZetoPrivate.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.20; + +/** + * @title IZetoPrivate + * @dev This is the ABI of the Zeto private transaction interface, which is implemented in Go. + * This interface is never expected to be implemented in a smart contract. + */ +interface IZetoPrivate { + function mint(TransferParam[] memory mints) external; + + function transfer(TransferParam[] memory transfers) external; + + function deposit(uint256 amount) external; + + function withdraw(uint256 amount) external; + + struct TransferParam { + address to; + uint256 amount; + } +} From b8f9b82bdca0423c4a9a08752b16ac7ea29cccd8 Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Tue, 17 Dec 2024 09:25:08 -0500 Subject: [PATCH 23/36] set RequiredSigner for zeto deposit and withdraw Signed-off-by: Jim Zhang --- domains/zeto/internal/zeto/handler_deposit.go | 1 + 1 file changed, 1 insertion(+) diff --git a/domains/zeto/internal/zeto/handler_deposit.go b/domains/zeto/internal/zeto/handler_deposit.go index 08ebeb55c..fd8215bfb 100644 --- a/domains/zeto/internal/zeto/handler_deposit.go +++ b/domains/zeto/internal/zeto/handler_deposit.go @@ -183,6 +183,7 @@ func (h *depositHandler) Prepare(ctx context.Context, tx *types.ParsedTransactio Transaction: &pb.PreparedTransaction{ FunctionAbiJson: string(functionJSON), ParamsJson: string(paramsJSON), + RequiredSigner: &req.Transaction.From, // must be signed by the original sender }, }, nil } From bbf6cee999645e776b6ec5a8b189b1f865c431f8 Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Tue, 17 Dec 2024 12:00:40 -0500 Subject: [PATCH 24/36] add utility to parse ABIs Signed-off-by: Jim Zhang --- toolkit/go/pkg/solutils/solbuild.go | 13 +++++++++++++ toolkit/go/pkg/solutils/solbuild_test.go | 18 ++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/toolkit/go/pkg/solutils/solbuild.go b/toolkit/go/pkg/solutils/solbuild.go index 4a8ac779e..a20fc7dab 100644 --- a/toolkit/go/pkg/solutils/solbuild.go +++ b/toolkit/go/pkg/solutils/solbuild.go @@ -104,3 +104,16 @@ func (unlinked *SolidityBuildWithLinks) ResolveLinks(ctx context.Context, librar } return hex.DecodeString(strings.TrimPrefix(bytecode, "0x")) } + +func MustParseBuildABI(buildJSON []byte) abi.ABI { + var buildParsed map[string]tktypes.RawJSON + var buildABI abi.ABI + err := json.Unmarshal(buildJSON, &buildParsed) + if err == nil { + err = json.Unmarshal(buildParsed["abi"], &buildABI) + } + if err != nil { + panic(err) + } + return buildABI +} diff --git a/toolkit/go/pkg/solutils/solbuild_test.go b/toolkit/go/pkg/solutils/solbuild_test.go index 7670f4857..4734160b7 100644 --- a/toolkit/go/pkg/solutils/solbuild_test.go +++ b/toolkit/go/pkg/solutils/solbuild_test.go @@ -87,3 +87,21 @@ func TestLibraryLinkingSuccess(t *testing.T) { require.Regexp(t, "PD021000.*0102030405060708aabbccddeeff998877", err) // bad links } + +func TestMustParseBuildABI(t *testing.T) { + abi := MustParseBuildABI([]byte(`{ + "abi": [ + { + "type":"function", + "name":"foo", + "inputs":[ + { + "name":"a", + "type":"uint256" + } + ] + } + ]}`)) + assert.Equal(t, 1, len(abi.Functions())) + assert.Equal(t, "foo", abi.Functions()["foo"].Name) +} From 75a1e3116f3516a9d41202459b590b2f6caabd06 Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Tue, 17 Dec 2024 12:01:30 -0500 Subject: [PATCH 25/36] Use utility for parsing ABIs Signed-off-by: Jim Zhang --- domains/noto/pkg/types/abi.go | 17 +------- domains/zeto/pkg/types/abi.go | 76 ++++------------------------------- 2 files changed, 9 insertions(+), 84 deletions(-) diff --git a/domains/noto/pkg/types/abi.go b/domains/noto/pkg/types/abi.go index fa0672f76..31235d595 100644 --- a/domains/noto/pkg/types/abi.go +++ b/domains/noto/pkg/types/abi.go @@ -17,30 +17,17 @@ package types import ( _ "embed" - "encoding/json" "github.com/hyperledger/firefly-signer/pkg/abi" "github.com/kaleido-io/paladin/toolkit/pkg/pldapi" + "github.com/kaleido-io/paladin/toolkit/pkg/solutils" "github.com/kaleido-io/paladin/toolkit/pkg/tktypes" ) //go:embed abis/INotoPrivate.json var notoPrivateJSON []byte -func mustParseBuildABI(buildJSON []byte) abi.ABI { - var buildParsed map[string]tktypes.RawJSON - var buildABI abi.ABI - err := json.Unmarshal(buildJSON, &buildParsed) - if err == nil { - err = json.Unmarshal(buildParsed["abi"], &buildABI) - } - if err != nil { - panic(err) - } - return buildABI -} - -var NotoABI = mustParseBuildABI(notoPrivateJSON) +var NotoABI = solutils.MustParseBuildABI(notoPrivateJSON) type ConstructorParams struct { Notary string `json:"notary"` // Lookup string for the notary identity diff --git a/domains/zeto/pkg/types/abi.go b/domains/zeto/pkg/types/abi.go index 32aaf279c..8322e8c75 100644 --- a/domains/zeto/pkg/types/abi.go +++ b/domains/zeto/pkg/types/abi.go @@ -16,78 +16,16 @@ package types import ( - "github.com/hyperledger/firefly-signer/pkg/abi" + _ "embed" + + "github.com/kaleido-io/paladin/toolkit/pkg/solutils" "github.com/kaleido-io/paladin/toolkit/pkg/tktypes" ) -var ZetoABI = abi.ABI{ - { - Type: abi.Constructor, - Inputs: abi.ParameterArray{ - { - Name: "tokenName", - Type: "string", - }, - }, - }, - { - Name: "mint", - Type: abi.Function, - Inputs: abi.ParameterArray{ - { - Name: "mints", - Type: "tuple[]", - Components: abi.ParameterArray{ - {Name: "to", Type: "string"}, - {Name: "amount", Type: "uint256"}, - }, - }, - }, - }, - { - Name: "transfer", - Type: abi.Function, - Inputs: abi.ParameterArray{ - { - Name: "transfers", - Type: "tuple[]", - Components: abi.ParameterArray{ - {Name: "to", Type: "string"}, - {Name: "amount", Type: "uint256"}, - }, - }, - }, - }, - { - Name: "lockProof", - Type: abi.Function, - Inputs: abi.ParameterArray{ - {Name: "delegate", Type: "address"}, - {Name: "call", Type: "bytes"}, // assumed to be an encoded "transfer" - }, - }, - { - Name: "setERC20", - Type: abi.Function, - Inputs: abi.ParameterArray{ - {Name: "_erc20", Type: "address"}, - }, - }, - { - Name: "deposit", - Type: abi.Function, - Inputs: abi.ParameterArray{ - {Name: "amount", Type: "uint256"}, - }, - }, - { - Name: "withdraw", - Type: abi.Function, - Inputs: abi.ParameterArray{ - {Name: "amount", Type: "uint256"}, - }, - }, -} +//go:embed abis/IZeto.json +var zetoPrivateJSON []byte + +var ZetoABI = solutils.MustParseBuildABI(zetoPrivateJSON) type InitializerParams struct { TokenName string `json:"tokenName"` From e40282326f6ad81215f4912faf113b9b85c5d1f3 Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Tue, 17 Dec 2024 12:02:19 -0500 Subject: [PATCH 26/36] use RequiredSigner for withdraw as well Signed-off-by: Jim Zhang --- domains/zeto/.gitignore | 1 + domains/zeto/build.gradle | 13 +++++++++++++ domains/zeto/integration-test/e2e_test.go | 2 +- domains/zeto/internal/zeto/handler_withdraw.go | 1 + 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/domains/zeto/.gitignore b/domains/zeto/.gitignore index ac4fb0cfb..f837bf059 100644 --- a/domains/zeto/.gitignore +++ b/domains/zeto/.gitignore @@ -1,4 +1,5 @@ internal/zeto/abis/ +pkg/types/abis/ integration-test/abis/ zkp/ tools/ diff --git a/domains/zeto/build.gradle b/domains/zeto/build.gradle index 43bb06e93..25864eb01 100644 --- a/domains/zeto/build.gradle +++ b/domains/zeto/build.gradle @@ -173,6 +173,19 @@ task copySolidity(type: Copy, dependsOn: [protoc, ":solidity:compile", extractZe includeEmptyDirs = false } +task copyPkgSolidity(type: Copy) { + inputs.files(configurations.contractCompile) + + into 'pkg/types/abis' + from fileTree(configurations.contractCompile.asPath) { + include 'contracts/domains/interfaces/IZeto.sol/IZeto.json' + } + + // Flatten all paths into the destination folder + eachFile { path = name } + includeEmptyDirs = false +} + task copySolidityForTest(type: Copy, dependsOn: [extractZetoArtifacts, generatePoseidonArtifacts, ":solidity:compile"]) { inputs.files(configurations.contractCompile) from fileTree(configurations.contractCompile.asPath) { diff --git a/domains/zeto/integration-test/e2e_test.go b/domains/zeto/integration-test/e2e_test.go index 369d24dad..c511d2a2e 100644 --- a/domains/zeto/integration-test/e2e_test.go +++ b/domains/zeto/integration-test/e2e_test.go @@ -138,7 +138,7 @@ func (s *zetoDomainTestSuite) testZetoFungible(t *testing.T, tokenName string, u require.NoError(t, err) log.L(ctx).Infof("Setting the ERC20 contract (%s) to the Zeto instance", erc20Address) - paramsJson, err := json.Marshal(&map[string]string{"_erc20": erc20Address.String()}) + paramsJson, err := json.Marshal(&map[string]string{"erc20": erc20Address.String()}) require.NoError(t, err) _, err = s.tb.ExecTransactionSync(ctx, &pldapi.TransactionInput{ TransactionBase: pldapi.TransactionBase{ diff --git a/domains/zeto/internal/zeto/handler_withdraw.go b/domains/zeto/internal/zeto/handler_withdraw.go index faa688541..b67fade98 100644 --- a/domains/zeto/internal/zeto/handler_withdraw.go +++ b/domains/zeto/internal/zeto/handler_withdraw.go @@ -225,6 +225,7 @@ func (h *withdrawHandler) Prepare(ctx context.Context, tx *types.ParsedTransacti Transaction: &pb.PreparedTransaction{ FunctionAbiJson: string(functionJSON), ParamsJson: string(paramsJSON), + RequiredSigner: &req.Transaction.From, // must be signed by the original sender }, }, nil } From 0a01a13a8a86115bf96cfe1ee9ff73c76668289e Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Tue, 17 Dec 2024 12:03:24 -0500 Subject: [PATCH 27/36] switch to using Zeto ABI from compiling solidity Signed-off-by: Jim Zhang --- example/zeto/src/index.ts | 77 ++--- sdk/typescript/scripts/abi.mjs | 4 +- sdk/typescript/src/domains/zeto.ts | 302 ++---------------- .../{IZetoPrivate.sol => IZeto.sol} | 20 +- 4 files changed, 76 insertions(+), 327 deletions(-) rename solidity/contracts/domains/interfaces/{IZetoPrivate.sol => IZeto.sol} (69%) diff --git a/example/zeto/src/index.ts b/example/zeto/src/index.ts index 3db30aa92..47d7746de 100644 --- a/example/zeto/src/index.ts +++ b/example/zeto/src/index.ts @@ -1,8 +1,7 @@ import PaladinClient, { - Algorithms, ZetoFactory, TransactionType, - Verifiers, + PaladinVerifier } from "@lfdecentralizedtrust-labs/paladin-sdk"; import erc20Abi from "./abis/SampleERC20.json"; import { checkDeploy, checkReceipt } from "./util"; @@ -51,7 +50,7 @@ async function main(): Promise { // Transfer some cash from bank1 to bank2 logger.log( - "Bank1 transferring CBDC to bank2 to pay for some asset trades ..." + "- Bank1 transferring CBDC to bank2 to pay for some asset trades ..." ); receipt = await zetoCBDC1.using(paladin1).transfer(bank1, { transfers: [ @@ -62,7 +61,7 @@ async function main(): Promise { ], }); if (!checkReceipt(receipt)) return false; - logger.log(" Success!\n"); + logger.log("\nUse case #1 complete!\n"); logger.log("Use case #2: Privacy-preserving CBDC token, using public minting of an ERC20 token..."); logger.log("- Deploying Zeto token..."); @@ -70,29 +69,28 @@ async function main(): Promise { tokenName: "Zeto_AnonNullifier", }); if (!checkDeploy(zetoCBDC2)) return false; - logger.log(` Zeto deployed at: ${zetoCBDC2.address}`); logger.log("- Deploying ERC20 token to manage the CBDC supply publicly..."); - const erc20Address = await deployERC20(paladin3, cbdcIssuer.lookup); + const erc20Address = await deployERC20(paladin3, cbdcIssuer); logger.log(` ERC20 deployed at: ${erc20Address}`); logger.log("- Setting ERC20 to the Zeto token contract ..."); - const result2 = await zetoCBDC2.setERC20(cbdcIssuer.lookup, { - _erc20: erc20Address as string + const result2 = await zetoCBDC2.setERC20(cbdcIssuer, { + erc20: erc20Address as string }); if (!checkReceipt(result2)) return false; - logger.log(` ERC20 configured on the Zeto contract`); logger.log("- Issuing CBDC to bank1 with public minting in ERC20..."); - await mintERC20(paladin3, cbdcIssuer.lookup, bank1.lookup, erc20Address!, 100000); - logger.log(" Success!"); + await mintERC20(paladin3, cbdcIssuer, bank1, erc20Address!, 100000); + + logger.log("- Bank1 approve ERC20 balance for the Zeto token contract as spender, to prepare for deposit..."); + await approveERC20(paladin1, bank1, zetoCBDC2.address, erc20Address!, 10000); logger.log("- Bank1 deposit ERC20 balance to Zeto ..."); - const result4 = await zetoCBDC2.using(paladin1).deposit(bank1.lookup, { + const result4 = await zetoCBDC2.using(paladin1).deposit(bank1, { amount: 10000 }); if (!checkReceipt(result4)) return false; - logger.log(` Bank1 deposit successful`); // Transfer some cash from bank1 to bank2 logger.log("- Bank1 transferring CBDC to bank2 to pay for some asset trades ..."); @@ -105,62 +103,69 @@ async function main(): Promise { ] }); if (!checkReceipt(receipt)) return false; - logger.log(" Success!"); logger.log("- Bank1 withdraws Zeto back to ERC20 balance ..."); - const result5 = await zetoCBDC2.using(paladin1).withdraw(bank1.lookup, { + const result5 = await zetoCBDC2.using(paladin1).withdraw(bank1, { amount: 1000 }); if (!checkReceipt(result5)) return false; - logger.log(` Bank1 withdraw successful`); - logger.log("\nSuccess!"); + logger.log("\nUse case #2 complete!"); return true; } -async function deployERC20(paladin: PaladinClient, cbdcIssuer: string): Promise { - const cbdcIssuerAddress = await paladin.resolveVerifier( - cbdcIssuer, - Algorithms.ECDSA_SECP256K1, - Verifiers.ETH_ADDRESS - ); - +async function deployERC20(paladin: PaladinClient, cbdcIssuer: PaladinVerifier): Promise { const txId1 = await paladin3.sendTransaction({ type: TransactionType.PUBLIC, - from: cbdcIssuer, + from: cbdcIssuer.lookup, data: { - "initialOwner": cbdcIssuerAddress, + "initialOwner": await cbdcIssuer.address(), }, function: "", abi: erc20Abi.abi, bytecode: erc20Abi.bytecode, }); const result1 = await paladin.pollForReceipt(txId1, 5000); - if (!checkReceipt(result1)) return; + if (!checkReceipt(result1)) { + throw new Error("Failed to deploy ERC20 token"); + }; const erc20Address = result1.contractAddress; return erc20Address; } -async function mintERC20(paladin: PaladinClient, cbdcIssuer: string, bank1: string, erc20Address: string, amount: number): Promise { - const bank1Address = await paladin.resolveVerifier( - bank1, - Algorithms.ECDSA_SECP256K1, - Verifiers.ETH_ADDRESS - ); +async function mintERC20(paladin: PaladinClient, cbdcIssuer: PaladinVerifier, bank1: PaladinVerifier, erc20Address: string, amount: number): Promise { const txId2 = await paladin.sendTransaction({ type: TransactionType.PUBLIC, - from: cbdcIssuer, + from: cbdcIssuer.lookup, to: erc20Address, data: { "amount": amount, - "to": bank1Address, + "to": await bank1.address(), }, function: "mint", abi: erc20Abi.abi, }); const result3 = await paladin.pollForReceipt(txId2, 5000); - if (!checkReceipt(result3)) return; + if (!checkReceipt(result3)) { + throw new Error("Failed to mint ERC20 tokens to bank1"); + } +} + +async function approveERC20(paladin: PaladinClient, from: PaladinVerifier, spender: string, erc20Address: string, amount: number): Promise { + // first approve the Zeto contract to draw the amount from our balance in the ERC20 + const txID1 = await paladin.sendTransaction({ + type: TransactionType.PUBLIC, + abi: erc20Abi.abi, + function: "approve", + to: erc20Address, + from: from.lookup, + data: { value: amount, spender }, + }); + const result1 = await paladin.pollForReceipt(txID1, 5000); + if (!checkReceipt(result1)) { + throw new Error("Failed to approve transfer"); + } } if (require.main === module) { diff --git a/sdk/typescript/scripts/abi.mjs b/sdk/typescript/scripts/abi.mjs index 9dc579515..4ae2ed4f4 100644 --- a/sdk/typescript/scripts/abi.mjs +++ b/sdk/typescript/scripts/abi.mjs @@ -11,6 +11,6 @@ await copyFile( ); await copyFile( - "../../solidity/artifacts/contracts/domains/interfaces/IZetoPrivate.sol/IZetoPrivate.json", - "src/domains/abis/IZetoPrivate.json" + "../../solidity/artifacts/contracts/domains/interfaces/IZeto.sol/IZeto.json", + "src/domains/abis/IZeto.json" ); diff --git a/sdk/typescript/src/domains/zeto.ts b/sdk/typescript/src/domains/zeto.ts index da9bb3f2e..f922f125a 100644 --- a/sdk/typescript/src/domains/zeto.ts +++ b/sdk/typescript/src/domains/zeto.ts @@ -1,6 +1,7 @@ import { TransactionType } from "../interfaces"; import PaladinClient from "../paladin"; import { PaladinVerifier } from "../verifier"; +import * as zetoPrivateJSON from "./abis/IZeto.json"; const POLL_TIMEOUT_MS = 10000; @@ -8,219 +9,7 @@ export interface ZetoOptions { pollTimeout?: number; } -const zetoPrivateAbi = [{ - "name": "mint", - "type": "function", - "inputs": [ - { - "name": "mints", - "type": "tuple[]", - "components": [ - { - "name": "to", - "type": "string", - "internalType": "string" - }, - { - "name": "amount", - "type": "uint256", - "internalType": "uint256" - } - ] - } - ], - "outputs": [], -}, { - "type": "function", - "name": "transfer", - "inputs": [ - { - "name": "transfers", - "type": "tuple[]", - "components": [ - { - "name": "to", - "type": "string", - "internalType": "string" - }, - { - "name": "amount", - "type": "uint256", - "internalType": "uint256" - } - ] - } - ], - "outputs": [] -}, { - "type": "function", - "name": "deposit", - "inputs": [ - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "outputs": [] -}, { - "type": "function", - "name": "withdraw", - "inputs": [ - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "outputs": [] -}]; - -const zetoPublicAbi = [{ - "inputs": [ - { - "internalType": "contract IERC20", - "name": "_erc20", - "type": "address" - } - ], - "name": "setERC20", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" -}, { - "inputs": [ - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "internalType": "uint256[]", - "name": "outputs", - "type": "uint256[]" - }, - { - "components": [ - { - "internalType": "uint256[2]", - "name": "pA", - "type": "uint256[2]" - }, - { - "internalType": "uint256[2][2]", - "name": "pB", - "type": "uint256[2][2]" - }, - { - "internalType": "uint256[2]", - "name": "pC", - "type": "uint256[2]" - } - ], - "internalType": "struct Commonlib.Proof", - "name": "proof", - "type": "tuple" - }, - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - } - ], - "name": "deposit", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" -}, { - "inputs": [ - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "internalType": "uint256[]", - "name": "nullifiers", - "type": "uint256[]" - }, - { - "internalType": "uint256", - "name": "output", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "root", - "type": "uint256" - }, - { - "components": [ - { - "internalType": "uint256[2]", - "name": "pA", - "type": "uint256[2]" - }, - { - "internalType": "uint256[2][2]", - "name": "pB", - "type": "uint256[2][2]" - }, - { - "internalType": "uint256[2]", - "name": "pC", - "type": "uint256[2]" - } - ], - "internalType": "struct Commonlib.Proof", - "name": "proof", - "type": "tuple" - }, - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - } - ], - "name": "withdraw", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" -}]; - -const erc20Abi = [{ - "type": "function", - "name": "mint", - "inputs": [ - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "outputs": [] -}, { - "type": "function", - "name": "approve", - "inputs": [ - { - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "outputs": [] -}]; +const zetoAbi = zetoPrivateJSON.abi; export const zetoConstructorABI = { type: "constructor", @@ -240,7 +29,7 @@ export interface ZetoTransferParams { } export interface ZetoSetERC20Params { - _erc20: string; + erc20: string; } export interface ZetoTransfer { @@ -315,15 +104,16 @@ export class ZetoInstance { } async mint(from: PaladinVerifier, data: ZetoMintParams) { + const params = { + mints: data.mints.map((t) => ({ ...t, to: t.to.lookup })), + }; const txID = await this.paladin.sendTransaction({ type: TransactionType.PRIVATE, - abi: zetoPrivateAbi, + abi: zetoAbi, function: "mint", to: this.address, from: from.lookup, - data: { - mints: data.mints.map((t) => ({ ...t, to: t.to.lookup })), - }, + data: params, }); return this.paladin.pollForReceipt(txID, this.options.pollTimeout); } @@ -331,7 +121,7 @@ export class ZetoInstance { async transfer(from: PaladinVerifier, data: ZetoTransferParams) { const txID = await this.paladin.sendTransaction({ type: TransactionType.PRIVATE, - abi: zetoPrivateAbi, + abi: zetoAbi, function: "transfer", to: this.address, from: from.lookup, @@ -342,84 +132,40 @@ export class ZetoInstance { return this.paladin.pollForReceipt(txID, this.options.pollTimeout); } - async setERC20(from: string, data: ZetoSetERC20Params) { + async setERC20(from: PaladinVerifier, data: ZetoSetERC20Params) { const txID = await this.paladin.sendTransaction({ type: TransactionType.PUBLIC, - abi: zetoPublicAbi, + abi: zetoAbi, function: "setERC20", to: this.address, - from, + from: from.lookup, data, }); - const result = await this.paladin.pollForReceipt(txID, POLL_TIMEOUT_MS); - if (result === undefined) { - throw new Error("Failed to set ERC20"); - } - this.erc20 = data._erc20; - return result; + this.erc20 = data.erc20; + return this.paladin.pollForReceipt(txID, POLL_TIMEOUT_MS); } - async deposit(from: string, data: ZetoDepositParams) { - // first approve the Zeto contract to draw the amount from our balance in the ERC20 - const txID1 = await this.paladin.sendTransaction({ - type: TransactionType.PUBLIC, - abi: erc20Abi, - function: "approve", - to: this.erc20, - from, - data: { value: data.amount, spender: this.address }, - }); - const result1 = await this.paladin.pollForReceipt(txID1, POLL_TIMEOUT_MS); - if (result1 === undefined) { - throw new Error("Failed to approve transfer"); - } - - const depositID = await this.paladin.prepareTransaction({ + async deposit(from: PaladinVerifier, data: ZetoDepositParams) { + const receipt = await this.paladin.sendTransaction({ type: TransactionType.PRIVATE, - abi: zetoPrivateAbi, + abi: zetoAbi, function: "deposit", to: this.address, - from, + from: from.lookup, data, }); - const result2 = await this.paladin.pollForPreparedTransaction(depositID, this.options.pollTimeout); - if (result2 === undefined) { - throw new Error("Failed to prepare deposit"); - } - - const txID2 = await this.paladin.sendTransaction({ - type: TransactionType.PUBLIC, - abi: zetoPublicAbi, - function: "deposit", - to: this.address, - from, - data: result2!.transaction.data, - }); - return this.paladin.pollForReceipt(txID2, POLL_TIMEOUT_MS); + return this.paladin.pollForReceipt(receipt, POLL_TIMEOUT_MS); } - async withdraw(from: string, data: ZetoWithdrawParams) { - const withdrawID = await this.paladin.prepareTransaction({ + async withdraw(from: PaladinVerifier, data: ZetoWithdrawParams) { + const receipt = await this.paladin.sendTransaction({ type: TransactionType.PRIVATE, - abi: zetoPrivateAbi, + abi: zetoAbi, function: "withdraw", to: this.address, - from, + from: from.lookup, data, }); - const result1 = await this.paladin.pollForPreparedTransaction(withdrawID, this.options.pollTimeout); - if (result1 === undefined) { - throw new Error("Failed to prepare withdraw"); - } - - const txID = await this.paladin.sendTransaction({ - type: TransactionType.PUBLIC, - abi: zetoPublicAbi, - function: "withdraw", - to: this.address, - from, - data: result1!.transaction.data, - }); - return this.paladin.pollForReceipt(txID, POLL_TIMEOUT_MS); + return this.paladin.pollForReceipt(receipt, POLL_TIMEOUT_MS); } } diff --git a/solidity/contracts/domains/interfaces/IZetoPrivate.sol b/solidity/contracts/domains/interfaces/IZeto.sol similarity index 69% rename from solidity/contracts/domains/interfaces/IZetoPrivate.sol rename to solidity/contracts/domains/interfaces/IZeto.sol index 7e0c1f1ba..f70dbc742 100644 --- a/solidity/contracts/domains/interfaces/IZetoPrivate.sol +++ b/solidity/contracts/domains/interfaces/IZeto.sol @@ -2,21 +2,19 @@ pragma solidity ^0.8.20; /** - * @title IZetoPrivate - * @dev This is the ABI of the Zeto private transaction interface, which is implemented in Go. + * @title IZeto + * @dev This is the ABI of the Zeto domain transaction interface, which is implemented in Go. * This interface is never expected to be implemented in a smart contract. */ -interface IZetoPrivate { - function mint(TransferParam[] memory mints) external; +interface IZeto { + struct TransferParam { + string to; + uint256 amount; + } + function mint(TransferParam[] memory mints) external; function transfer(TransferParam[] memory transfers) external; - function deposit(uint256 amount) external; - function withdraw(uint256 amount) external; - - struct TransferParam { - address to; - uint256 amount; - } + function setERC20(address erc20) external; } From 960358e80e4c67283a7c560718fba788777db667 Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Tue, 17 Dec 2024 12:08:15 -0500 Subject: [PATCH 28/36] misc update in e2e test Signed-off-by: Jim Zhang --- domains/zeto/integration-test/e2e_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/domains/zeto/integration-test/e2e_test.go b/domains/zeto/integration-test/e2e_test.go index c511d2a2e..e750592b4 100644 --- a/domains/zeto/integration-test/e2e_test.go +++ b/domains/zeto/integration-test/e2e_test.go @@ -466,7 +466,7 @@ func getERC20Spec() (*solutils.SolidityBuild, error) { if err != nil { return nil, err } - build := solutils.MustLoadBuildResolveLinks(bytes, map[string]*tktypes.EthAddress{}) + build := solutils.MustLoadBuild(bytes) return build, nil } From 20dde4ed866dcc6d1f9a880898ef87220ff7882f Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Tue, 17 Dec 2024 12:27:37 -0500 Subject: [PATCH 29/36] fix unit tests Signed-off-by: Jim Zhang --- domains/zeto/internal/zeto/zeto_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/domains/zeto/internal/zeto/zeto_test.go b/domains/zeto/internal/zeto/zeto_test.go index 6b8a7468f..6475695fa 100644 --- a/domains/zeto/internal/zeto/zeto_test.go +++ b/domains/zeto/internal/zeto/zeto_test.go @@ -246,9 +246,9 @@ func TestInitTransaction(t *testing.T) { req.Transaction.FunctionParamsJson = "{\"mints\":[{\"to\":\"Alice\",\"amount\":\"10\"}]}" _, err = z.InitTransaction(context.Background(), req) - assert.EqualError(t, err, "PD210008: Failed to validate init transaction spec. PD210016: Unexpected signature for function 'mint': expected='function mint(mints[] memory mints) external { }; struct mints { string to; uint256 amount; }', actual=''") + assert.EqualError(t, err, "PD210008: Failed to validate init transaction spec. PD210016: Unexpected signature for function 'mint': expected='function mint(TransferParam[] memory mints) external { }; struct TransferParam { string to; uint256 amount; }', actual=''") - req.Transaction.FunctionSignature = "function mint(mints[] memory mints) external { }; struct mints { string to; uint256 amount; }" + req.Transaction.FunctionSignature = "function mint(TransferParam[] memory mints) external { }; struct TransferParam { string to; uint256 amount; }" _, err = z.InitTransaction(context.Background(), req) assert.EqualError(t, err, "PD210008: Failed to validate init transaction spec. PD210017: Failed to decode contract address. bad address - must be 20 bytes (len=0)") @@ -290,7 +290,7 @@ func TestAssembleTransaction(t *testing.T) { _, err := z.AssembleTransaction(context.Background(), req) assert.ErrorContains(t, err, "PD210009") - req.Transaction.FunctionSignature = "function mint(mints[] memory mints) external { }; struct mints { string to; uint256 amount; }" + req.Transaction.FunctionSignature = "function mint(TransferParam[] memory mints) external { }; struct TransferParam { string to; uint256 amount; }" conf := types.DomainInstanceConfig{ CircuitId: "circuit1", TokenName: "testToken1", @@ -316,7 +316,7 @@ func TestEndorseTransaction(t *testing.T) { assert.EqualError(t, err, "PD210010: Failed to validate endorse transaction spec. PD210012: Failed to unmarshal function abi json. unexpected end of JSON input") req.Transaction.FunctionAbiJson = "{\"type\":\"function\",\"name\":\"mint\"}" - req.Transaction.FunctionSignature = "function mint(mints[] memory mints) external { }; struct mints { string to; uint256 amount; }" + req.Transaction.FunctionSignature = "function mint(TransferParam[] memory mints) external { }; struct TransferParam { string to; uint256 amount; }" conf := types.DomainInstanceConfig{ CircuitId: "circuit1", TokenName: "testToken1", @@ -357,7 +357,7 @@ func TestPrepareTransaction(t *testing.T) { assert.EqualError(t, err, "PD210011: Failed to validate prepare transaction spec. PD210012: Failed to unmarshal function abi json. unexpected end of JSON input") req.Transaction.FunctionAbiJson = "{\"type\":\"function\",\"name\":\"mint\"}" - req.Transaction.FunctionSignature = "function mint(mints[] memory mints) external { }; struct mints { string to; uint256 amount; }" + req.Transaction.FunctionSignature = "function mint(TransferParam[] memory mints) external { }; struct TransferParam { string to; uint256 amount; }" conf := types.DomainInstanceConfig{ CircuitId: "circuit1", TokenName: "testToken1", From 42fddee1114c49e4ea198096a2285d63b723a356 Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Tue, 17 Dec 2024 12:36:06 -0500 Subject: [PATCH 30/36] revert updates to go.work.sum checked in by accident Signed-off-by: Jim Zhang --- go.work.sum | 3 --- 1 file changed, 3 deletions(-) diff --git a/go.work.sum b/go.work.sum index a11f577b6..d0ff8011e 100644 --- a/go.work.sum +++ b/go.work.sum @@ -503,9 +503,6 @@ github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47 github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20240312041847-bd984b5ce465/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= -github.com/iden3/go-iden3-crypto v0.0.15/go.mod h1:dLpM4vEPJ3nDHzhWFXDjzkn1qHoBeOT/3UEhXsEsP3E= -github.com/iden3/go-rapidsnark/prover v0.0.10/go.mod h1:wgDsmKOGCuWGtgVtuW9ARWNguNr4NJAIyg2G7+uTax0= -github.com/iden3/go-rapidsnark/types v0.0.2/go.mod h1:ApgcaUxKIgSRA6fAeFxK7p+lgXXfG4oA2HN5DhFlfF4= github.com/iden3/go-rapidsnark/witness/wasmer v0.0.0-20230524142950-0986cf057d4e/go.mod h1:WUtPVKXrhfZHJXavwId2+8J/fKMHQ92N0MZDxt8sfEA= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= From 21025f5032f72422afc4648d2de10d6150941699 Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Tue, 17 Dec 2024 12:55:43 -0500 Subject: [PATCH 31/36] add copy dependency Signed-off-by: Jim Zhang --- domains/zeto/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/domains/zeto/build.gradle b/domains/zeto/build.gradle index 25864eb01..3fe16252b 100644 --- a/domains/zeto/build.gradle +++ b/domains/zeto/build.gradle @@ -267,7 +267,7 @@ task test { finalizedBy checkCoverage } -task buildGo(type: GoLib, dependsOn: [":toolkit:go:protoc", copySolidity]) { +task buildGo(type: GoLib, dependsOn: [":toolkit:go:protoc", copySolidity, copyPkgSolidity]) { inputs.files(configurations.coreGo) inputs.files(configurations.toolkitGo) baseName "zeto" From ab60257a35adf99ece3f3feebab4dc21826539fe Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Tue, 17 Dec 2024 13:02:10 -0500 Subject: [PATCH 32/36] add dependency copyPkgSolidity to unitTests Signed-off-by: Jim Zhang --- domains/zeto/build.gradle | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/domains/zeto/build.gradle b/domains/zeto/build.gradle index 3fe16252b..34443b5c1 100644 --- a/domains/zeto/build.gradle +++ b/domains/zeto/build.gradle @@ -230,7 +230,7 @@ task testE2E(type: Exec, dependsOn: [protoc, copySolidity, copySolidityForTest, helpers.dumpLogsOnFailure(it, ':testinfra:startTestInfra') } -task unitTests(type: Exec, dependsOn: [protoc, ":core:go:makeMocks", downloadZetoCompiledContracts]) { +task unitTests(type: Exec, dependsOn: [protoc, ":core:go:makeMocks", downloadZetoCompiledContracts, copyPkgSolidity]) { inputs.files(configurations.toolkitGo) inputs.files(configurations.coreGo) inputs.files(goFiles) @@ -283,6 +283,7 @@ task build { task clean(type: Delete) { delete 'coverage' delete 'internal/zeto/abis' + delete 'pkg/types/abis' delete 'integration-test/abis' delete zkpOut delete toolsOut From c459f488e8879baf54fbae94c2f3f842a4ddec30 Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Tue, 17 Dec 2024 13:55:11 -0500 Subject: [PATCH 33/36] add copyPkgSolidity to dependency of goSource in zeto build Signed-off-by: Jim Zhang --- domains/zeto/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/domains/zeto/build.gradle b/domains/zeto/build.gradle index 34443b5c1..9faceab64 100644 --- a/domains/zeto/build.gradle +++ b/domains/zeto/build.gradle @@ -294,7 +294,7 @@ task assemble { } dependencies { - goSource files(goFiles, copySolidity) + goSource files(goFiles, copySolidity, copyPkgSolidity) zetoArtifacts files(extractZetoArtifacts) poseidonArtifacts files(generatePoseidonArtifacts) } From 1818616b930c4e75e40eb3bd527cf90ac7c055a4 Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Tue, 17 Dec 2024 14:23:06 -0500 Subject: [PATCH 34/36] add lockProof() to the zeto private solidity Signed-off-by: Jim Zhang --- solidity/contracts/domains/interfaces/IZeto.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/solidity/contracts/domains/interfaces/IZeto.sol b/solidity/contracts/domains/interfaces/IZeto.sol index f70dbc742..7104b80a6 100644 --- a/solidity/contracts/domains/interfaces/IZeto.sol +++ b/solidity/contracts/domains/interfaces/IZeto.sol @@ -14,6 +14,7 @@ interface IZeto { function mint(TransferParam[] memory mints) external; function transfer(TransferParam[] memory transfers) external; + function lockProof(address delegate, bytes memory call) external; function deposit(uint256 amount) external; function withdraw(uint256 amount) external; function setERC20(address erc20) external; From cfda5c9f02e7c0600a57e852228162f2a6ac45ad Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Tue, 17 Dec 2024 14:24:13 -0500 Subject: [PATCH 35/36] Rename IZeto.sol to IZetoPrivate.sol to avoid conflict Signed-off-by: Jim Zhang --- domains/zeto/build.gradle | 2 +- domains/zeto/pkg/types/abi.go | 2 +- sdk/typescript/scripts/abi.mjs | 4 ++-- sdk/typescript/src/domains/zeto.ts | 2 +- .../domains/interfaces/{IZeto.sol => IZetoPrivate.sol} | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) rename solidity/contracts/domains/interfaces/{IZeto.sol => IZetoPrivate.sol} (93%) diff --git a/domains/zeto/build.gradle b/domains/zeto/build.gradle index 9faceab64..eb0152192 100644 --- a/domains/zeto/build.gradle +++ b/domains/zeto/build.gradle @@ -178,7 +178,7 @@ task copyPkgSolidity(type: Copy) { into 'pkg/types/abis' from fileTree(configurations.contractCompile.asPath) { - include 'contracts/domains/interfaces/IZeto.sol/IZeto.json' + include 'contracts/domains/interfaces/IZetoPrivate.sol/IZetoPrivate.json' } // Flatten all paths into the destination folder diff --git a/domains/zeto/pkg/types/abi.go b/domains/zeto/pkg/types/abi.go index 8322e8c75..5894c405d 100644 --- a/domains/zeto/pkg/types/abi.go +++ b/domains/zeto/pkg/types/abi.go @@ -22,7 +22,7 @@ import ( "github.com/kaleido-io/paladin/toolkit/pkg/tktypes" ) -//go:embed abis/IZeto.json +//go:embed abis/IZetoPrivate.json var zetoPrivateJSON []byte var ZetoABI = solutils.MustParseBuildABI(zetoPrivateJSON) diff --git a/sdk/typescript/scripts/abi.mjs b/sdk/typescript/scripts/abi.mjs index 4ae2ed4f4..9dc579515 100644 --- a/sdk/typescript/scripts/abi.mjs +++ b/sdk/typescript/scripts/abi.mjs @@ -11,6 +11,6 @@ await copyFile( ); await copyFile( - "../../solidity/artifacts/contracts/domains/interfaces/IZeto.sol/IZeto.json", - "src/domains/abis/IZeto.json" + "../../solidity/artifacts/contracts/domains/interfaces/IZetoPrivate.sol/IZetoPrivate.json", + "src/domains/abis/IZetoPrivate.json" ); diff --git a/sdk/typescript/src/domains/zeto.ts b/sdk/typescript/src/domains/zeto.ts index f922f125a..7b3117534 100644 --- a/sdk/typescript/src/domains/zeto.ts +++ b/sdk/typescript/src/domains/zeto.ts @@ -1,7 +1,7 @@ import { TransactionType } from "../interfaces"; import PaladinClient from "../paladin"; import { PaladinVerifier } from "../verifier"; -import * as zetoPrivateJSON from "./abis/IZeto.json"; +import * as zetoPrivateJSON from "./abis/IZetoPrivate.json"; const POLL_TIMEOUT_MS = 10000; diff --git a/solidity/contracts/domains/interfaces/IZeto.sol b/solidity/contracts/domains/interfaces/IZetoPrivate.sol similarity index 93% rename from solidity/contracts/domains/interfaces/IZeto.sol rename to solidity/contracts/domains/interfaces/IZetoPrivate.sol index 7104b80a6..d4168325e 100644 --- a/solidity/contracts/domains/interfaces/IZeto.sol +++ b/solidity/contracts/domains/interfaces/IZetoPrivate.sol @@ -2,11 +2,11 @@ pragma solidity ^0.8.20; /** - * @title IZeto + * @title IZetoPrivate * @dev This is the ABI of the Zeto domain transaction interface, which is implemented in Go. * This interface is never expected to be implemented in a smart contract. */ -interface IZeto { +interface IZetoPrivate { struct TransferParam { string to; uint256 amount; From b2507b6b3cc67f50ab25bb34dff70a0b53cebc39 Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Tue, 17 Dec 2024 14:47:51 -0500 Subject: [PATCH 36/36] add copyPkgSolidity to dependency of testE2E Signed-off-by: Jim Zhang --- domains/zeto/build.gradle | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/domains/zeto/build.gradle b/domains/zeto/build.gradle index eb0152192..948b06cda 100644 --- a/domains/zeto/build.gradle +++ b/domains/zeto/build.gradle @@ -210,7 +210,7 @@ task copySolidityForTest(type: Copy, dependsOn: [extractZetoArtifacts, generateP includeEmptyDirs = false } -task testE2E(type: Exec, dependsOn: [protoc, copySolidity, copySolidityForTest, ':testinfra:startTestInfra', ":core:go:makeMocks"]) { +task testE2E(type: Exec, dependsOn: [protoc, copySolidity, copyPkgSolidity, copySolidityForTest, ':testinfra:startTestInfra', ":core:go:makeMocks"]) { inputs.files(configurations.toolkitGo) inputs.files(configurations.coreGo) inputs.files(goFiles) @@ -230,7 +230,7 @@ task testE2E(type: Exec, dependsOn: [protoc, copySolidity, copySolidityForTest, helpers.dumpLogsOnFailure(it, ':testinfra:startTestInfra') } -task unitTests(type: Exec, dependsOn: [protoc, ":core:go:makeMocks", downloadZetoCompiledContracts, copyPkgSolidity]) { +task unitTests(type: Exec, dependsOn: [protoc, ":core:go:makeMocks", ":testinfra:startTestInfra", downloadZetoCompiledContracts, copySolidity, copyPkgSolidity]) { inputs.files(configurations.toolkitGo) inputs.files(configurations.coreGo) inputs.files(goFiles) @@ -251,9 +251,6 @@ task unitTests(type: Exec, dependsOn: [protoc, ":core:go:makeMocks", downloadZet if (project.findProperty('verboseTests') == 'true') { args '-v' } - - dependsOn copySolidity - dependsOn ':testinfra:startTestInfra' helpers.dumpLogsOnFailure(it, ':testinfra:startTestInfra') }