Skip to content

Commit

Permalink
sapling package (#1524)
Browse files Browse the repository at this point in the history
* feat(sapling viewing key): inMemoryViewingKey class

A class that holds the viewing key and can be instantiated with a spending key

* 1509 state tree (#1599)

- Added support for SaplingState
- Added unit tests
- Added necessary error classes and util methods

* feat(sapling): saplingTransactionViewer class (#1601)

Allows to retrieve and decrypt sapling transactions and fetch the balance

re #1571

* missing bignumber in utils package

* Implemented sapling forger and added unit tests (#1723)
* Added new `types` file

* sapling spending address viewing key 1604 fix branch (#1734)

Co-authored-by: Zainen Suzuki <zainensuzuki@Zainens-MacBook-Pro.local>

* refactor(taquito and utils): move the format function for taquito package to utils

* refactor(integration-tests): accept the memo size as a param for the sapling contract

Co-authored-by: Daniele Lisi <22307776+danielelisi@users.noreply.github.com>

* feat(taquito): add getSaplingDiffByContract method to the TzReadProvider

* feat(sapling): prepare sapling transactions (#1794)

* refactor(integration-tests): accept the memo size as a param for the sapling contract

* feat(taquito): add getSaplingDiffByContract method to the TzReadProvider

* feat(sapling): prepare sapling transactions

The SaplingToolkit class allows to prepare shielded transactions, unshielded transactions and
sapling transactions based on a spendingkey

re #1572

* Added axios dependency

* Added bip39 dependency to integration tests

* Support instantiation of InMemoryViewingKey with encrypted sk

* docs(sapling): documentation for the sapling package

* Add info on the README

* refactor(read-adapter): remove the context from the constructor

Takes a rpcClient in the constructor instead of a context. Context is an internal class that should
not be available to the users.

BREAKING CHANGE: RpcReadAdapter needs a RpcClient in the constructor

* fix(sapling): fix for using the package on the web

Fetch and bundle the zcash sapling params in the package

* Removed sapling params files as their size are very large

* Upgrade to @airgap/sapling-wasm version having fix for node

* Reduce bundle size

* Ignore lint rule for generated files sapling params

* Fix path

* test(integration tests): sapling multiple states test

An integration test to interact with a contract having 2 sapling pools

re #1825

* Make the sapling examples runnable on the website

* feat(sapling): support proof generation from provingkey (#1837)

* feat(sapling): support proof generation from provingkey

Implemented an InMemorySpendingKey class that allows producing proof independently from the
spending key. Did some refactoring and renaming also.

* Updated README and added load test

* upgrade airgap/sapling-wasm package

* import sapling package in example folder

Co-authored-by: Davis Sawali <davis.sawali@ecadlabs.com>
Co-authored-by: Zainen <72581075+zainen@users.noreply.github.com>
Co-authored-by: Zainen Suzuki <zainensuzuki@Zainens-MacBook-Pro.local>
Co-authored-by: Daniele Lisi <22307776+danielelisi@users.noreply.github.com>
  • Loading branch information
5 people authored Sep 1, 2022
1 parent 043d892 commit bbd425f
Show file tree
Hide file tree
Showing 129 changed files with 207,884 additions and 11,575 deletions.
4 changes: 3 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
},
"ignorePatterns": [
"**/dist/**",
"**/rollup*.ts"
"**/rollup*.ts",
"**/saplingOutputParams.js",
"**/saplingSpendParams.js"
]
}
17 changes: 2 additions & 15 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,5 @@ manifest.json
!.vscode/extensions.json
website/*.bak
integration-tests/jest-stare
packages/taquito/src/version.ts
packages/taquito-beacon-wallet/src/version.ts
packages/taquito-http-utils/src/version.ts
packages/taquito-ledger-signer/src/version.ts
packages/taquito-local-forging/src/version.ts
packages/taquito-michel-codec/src/version.ts
packages/taquito-michelson-encoder/src/version.ts
packages/taquito-remote-signer/src/version.ts
packages/taquito-rpc/src/version.ts
packages/taquito-signer/src/version.ts
packages/taquito-tezbridge-signer/src/version.ts
packages/taquito-tezbridge-wallet/src/version.ts
packages/taquito-tzip12/src/version.ts
packages/taquito-tzip16/src/version.ts
packages/taquito-utils/src/version.ts
**/saplingOutputParams*
**/saplingSpendParams*
360 changes: 360 additions & 0 deletions docs/sapling.md

Large diffs are not rendered by default.

52 changes: 52 additions & 0 deletions docs/sapling_in_memory_spending_key.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
title: InMemorySpendingKey
author: Zainen Suzuki
---

# Sapling Spending Key Instantiation

:::caution Warning
**Storing private keys in memory is suitable for development workflows but risky for
production use-cases! Use the `InMemorySpendingKey` appropriately given your risk profile**
:::

### From Mnemonic

Instantiation of a `InMemorySpendingkey` from Mnemonic does not require a password as it is only used to change an unencrypted `sask...` to an encrypted `MMXj...` spending key or vice versa

Params:
- `mnemonic` list of words
- `derivationPath` tezos current standard 'm/'

Returns:
- InMemorySpendingKey class instantiated



```js
const SaplingKeyProvider = await InMemorySpendingKey.fromMnemonic([
'leopard', 'crouch', 'simple', 'blind', 'castle', 'they',
'elder', 'enact', 'slow', 'rate', 'mad', 'blanket',
'saddle', 'tail', 'silk', 'fury', 'quarter', 'obscure',
'interest', 'exact', 'veteran', 'volcano', 'fabric', 'cherry'
`],
'm/'
) // derivationPath by current standard is 'm/'
```

### From Encrypted Spending Key

If the spending key is encrypted prefixed with `MMXj...` then a password is required to decrypt to a unencrypted Spending Key `sask...`

Params:
- `spendingKey` unencrypted sask... or encrypted MMXj...
- `password` required for MMXj encrypted keys

```js
const SaplingKeyProvider = new InMemorySpendingKey(
'MMXjN99mhomTm1Y5nQt8NfwEKTHWugsLtucX7oWrpsJd99qxGYJWP5aMb3t8zZaoKHQ898bLu9dwpog71bnjiDZfS9J9hWnTLCGm4fAjKKYeRuwTgCRjSdsP9znCPBUpCvyxeEFvUfamA5URrp8c7AaooAkobLW1PjNh2vjHobtiyNVTEtyTUWTLcjdxaiPbQWs3NaWvcb5Qr6z9MHhKrYNBHmsd9HBeRB2rVnvvL7pMc8f8zqyuXtmAuzMhiqPz3B4BRzuc8a2jkkoL14',
'test'
)
```
62 changes: 62 additions & 0 deletions docs/sapling_in_memory_viewing_key.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
---
title: InMemoryViewingKey
author: Roxane Letourneau
---

The `InMemoryViewingKey` class can be instantiated from a viewing or spending key.

### Instantiation from a viewing key:

```js
import { InMemoryViewingKey } from '@taquito/sapling';
const inMemoryViewingKey = new InMemoryViewingKey(
'000000000000000000977d725fc96387e8ec1e603e7ae60c6e63529fb84e36e126770e9db9899d7f2344259fd700dc80120d3c9ca65d698f6064043b048b079caa4f198aed96271740b1d6fd523d71b15cd0b3d75644afbe9abfedb6883e299165665ab692c14ca5c835c61a0e53de553a751c78fbc42d5e7eca807fd441206651c84bf88de803efba837583145a5f338b1a7af8a5f9bec4783054f9d063d365f2352f72cbced95e0a'
);
```

### Instantiation from an unencrypted spending key:

```js live noInline
import { InMemoryViewingKey } from '@taquito/sapling';

InMemoryViewingKey.fromSpendingKey(
'sask27SLmU9herddHz4qFJBLMjWYMbJF8RtS579w9ej9mfCYK7VUdyCJPHK8AzW9zMsopGZEkYeNjAY7Zz1bkM7CGu8eKLzrjBLTMC5wWJDhxiK91ahA29rhDRsHdJDV2u2jFwb2MNUix8JW7sAkAqYVaJpCehTBPgRQ1KqKwqqUaNmuD8kazd4Q8MCWmgbWs21Yuomdqyi9FLigjRp7oY4m5adaVU19Nj1AHvsMY2tePeU2L',
).then((inMemoryViewingKey) => {
const viewingKey = inMemoryViewingKey.getFullViewingKey()
println(`The viewing key is ${viewingKey.toString('hex')}`);
})
.catch((error) => println(`Error: ${JSON.stringify(error, null, 2)}`));

```

### Instantiation from an encrypted spending key:

```js live noInline
import { InMemoryViewingKey } from '@taquito/sapling';

InMemoryViewingKey.fromSpendingKey(
'MMXjN99mhomTm1Y5nQt8NfwEKTHWugsLtucX7oWrpsJd99qxGYJWP5aMb3t8zZaoKHQ898bLu9dwpog71bnjiDZfS9J9hWnTLCGm4fAjKKYeRuwTgCRjSdsP9znCPBUpCvyxeEFvUfamA5URrp8c7AaooAkobLW1PjNh2vjHobtiyNVTEtyTUWTLcjdxaiPbQWs3NaWvcb5Qr6z9MHhKrYNBHmsd9HBeRB2rVnvvL7pMc8f8zqyuXtmAuzMhiqPz3B4BRzuc8a2jkkoL14',
'test' // password
).then((inMemoryViewingKey) => {
const viewingKey = inMemoryViewingKey.getFullViewingKey()
println(`The viewing key is ${viewingKey.toString('hex')}`);
})
.catch((error) => println(`Error: ${JSON.stringify(error, null, 2)}`));

```

## How to retrieve payment addresses from the viewing key

The `InMemoryViewingKey` class has a method named `getAddress`, allowing to derive addresses (zet) from the viewing key. An index can be specified as a parameter, or the default value `0` will be used.

```js live noInline
import { InMemoryViewingKey } from '@taquito/sapling';

const inMemoryViewingKey = new InMemoryViewingKey(
'000000000000000000977d725fc96387e8ec1e603e7ae60c6e63529fb84e36e126770e9db9899d7f2344259fd700dc80120d3c9ca65d698f6064043b048b079caa4f198aed96271740b1d6fd523d71b15cd0b3d75644afbe9abfedb6883e299165665ab692c14ca5c835c61a0e53de553a751c78fbc42d5e7eca807fd441206651c84bf88de803efba837583145a5f338b1a7af8a5f9bec4783054f9d063d365f2352f72cbced95e0a'
);

inMemoryViewingKey.getAddress()
.then((address) => println(`The address is ${JSON.stringify(address, null, 2)}`))
.catch((error) => println(`Error: ${JSON.stringify(error, null, 2)}`));
```
53 changes: 53 additions & 0 deletions docs/version.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,59 @@ The `txRollupSubmitBatch` method also takes optional `storageLimit`, `gasLimit`



## Compatibility with the Jakarta protocol
We addressed the Jakarta protocol's breaking changes, making this version of Taquito compatible with the Jakarta protocol. This early integration has been possible by using the Mondaynet testnet.

The Jakarta protocol addresses the [malleability issue](https://tezos.gitlab.io/alpha/sapling.html#preventing-malleability) discovered in Sapling. It introduces changes around the sapling related types and instructions that are now supported in Taquito:
- The encoding of `sapling_transaction` has changed; we added support for it in the `@taquito/local-forging` package and support for `sapling_transaction_deprecated`.

- The optional type returned by the `SAPLING_VERIFY_UPDATE` instruction contains an additional property named `bound_data`. We added support for it in the `@taquito/michel-codec` package.

This release introduces some breaking changes in the `@taquito/rpc` package:
- The type of the proposal response items returned by the `getProposals` methods has changed from `[string, number]` to `[string, BigNumber]`.
- The type of the properties in the response of the `getBallots` methods have changed from `number` to `BigNumber`.
- In the response of `getVotesListings`, the field `rolls` is now optional as it has been replaced by `voting_power`, which type is a `BigNumber`.
- In the response of `getDelegates`, the type of the `voting_power` property has changed from `number` to `BigNumber`.

Note that support for new features brought by the Jakarta protocol is not part of the current release.

## `@taquito/taquito` - Avoid doing POST call to fetch contract script with the RPC

In the latest versions, the RPC `context/contracts/{contractAddress}/script/normalized` endpoint was used to fetch the script when building the contract abstraction. This endpoint which is a POST call has been replaced with `context/contracts/{contractAddress}`, which is a GET call instead. The reason for changing the endpoints is that it is more convenient to avoid POST calls when reading from the chain, as this prevents caching using standard HTTP caches. Also, both endpoints return expanded global constants for all protocols so far.

## Review and improve Error classes in Taquito

Many error classes in Taquito returned a regular `Error` class. We adjusted them to use custom errors to provide a better error handling experience for our users. The errors are now available on the typedoc documentation in an `Error Classes` section for the different packages.

Note that this improvement results in a potential breaking change for users who were catching the regular Error.

## `@taquito/http-utils` - Make HttpBackend.defaultTimeout configurable

The timeout has been added to the construction of the HttpBackend class with a default value of 30000 milliseconds.

A different timeout value can be configured when creating an instance of RpcClient as follows:

```javascript=
new RpcClient('url', 'chain', new HttpBackend(50000));
```

# Taquito v12.1.0-beta

## Summary
### Jakarta initial support
- Compatibility with the Jakarta protocol

### Improvements
- `@taquito/taquito` - Avoid doing POST call to fetch contract script with the RPC #1532
- Review and improve Error classes in Taquito #1472
- `@taquito/http-utils` - Make HttpBackend.defaultTimeout configurable #751
- `@taquito/local-forging` - Reject Invalid Inputs When Forging #483

### Documentation
- How to capture failwith errors: https://tezostaquito.io/docs/next/failwith_errors



## Compatibility with the Jakarta protocol
We addressed the Jakarta protocol's breaking changes, making this version of Taquito compatible with the Jakarta protocol. This early integration has been possible by using the Mondaynet testnet.

Expand Down
2 changes: 1 addition & 1 deletion example/deploy-integration-tests-contracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ async function example() {

console.log('Deploying the Sapling State Contract...');
const opknownSaplingContract = await tezos.contract.originate({
code: singleSaplingStateContractJProtocol,
code: singleSaplingStateContractJProtocol(),
init: '{}',
});
await opknownSaplingContract.confirmation();
Expand Down
71 changes: 71 additions & 0 deletions example/example-sapling-load-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { TezosToolkit, RpcReadAdapter } from '@taquito/taquito';
import { InMemorySigner } from '@taquito/signer';
import { InMemorySpendingKey, SaplingToolkit } from '@taquito/sapling';
//import { singleSaplingStateContractJProtocol } from '../integration-tests/data/single_sapling_state_contract_jakarta_michelson';

const provider = 'https://kathmandunet.ecadinfra.com/';
const numberOfSaplingTx = 1000;
let totalTime = 0;

async function example() {
try {
const tezos = new TezosToolkit(provider);
tezos.setSignerProvider(new InMemorySigner('spsk1m4s7kPegWrAHVVJQ8H64UvtojtopmmAfkr64UYyAMDHD4pGDA'))
console.log(await tezos.signer.publicKeyHash())

/* const saplingContractOrigination = await tezos.contract.originate({
code: singleSaplingStateContractJProtocol(),
init: '{}'
}); */

//const saplingContract = await saplingContractOrigination.contract();
const saplingContract = await tezos.contract.at('KT1SmRBZvitJKeW6G491FUyGKHvdUrQtZ5CS');
console.log(saplingContract.address);

const aliceInMemorySpendingKey = new InMemorySpendingKey('sask27SLmU9herddHz4qFJBLMjWYMbJF8RtS579w9ej9mfCYK7VUdyCJPHK8AzW9zMsopGZEkYeNjAY7Zz1bkM7CGu8eKLzrjBLTMC5wWJDhxiK91ahA29rhDRsHdJDV2u2jFwb2MNUix8JW7sAkAqYVaJpCehTBPgRQ1KqKwqqUaNmuD8kazd4Q8MCWmgbWs21Yuomdqyi9FLigjRp7oY4m5adaVU19Nj1AHvsMY2tePeU2L');
const aliceSaplingToolkit = new SaplingToolkit({ saplingSigner: aliceInMemorySpendingKey }, { contractAddress: saplingContract.address, memoSize: 8 }, new RpcReadAdapter(tezos.rpc));
const aliceInMemoryViewingKey = await aliceInMemorySpendingKey.getSaplingViewingKeyProvider();
const alicePaymentAddress = (await aliceInMemoryViewingKey.getAddress()).address;

const shieldedTx = await aliceSaplingToolkit.prepareShieldedTransaction([{
to: alicePaymentAddress,
amount: 5,
memo: 'First Tx'
}])

const op = await saplingContract.methods.default([shieldedTx]).send({ amount: 5 });
await op.confirmation();

// loop to create multiple sapling transactions (numberOfSaplingTx) and log the time taken to prepare it
for (let i = 0; i < numberOfSaplingTx; i++) {

const start = Date.now();

const tx = await aliceSaplingToolkit.prepareSaplingTransaction([{
to: 'zet14CMN2T4x1f8sgXeAGWQwczSf6SJ8bm8nyP2Tg7HJn2VmtPtB2nE2q7MMgdmMEwpGQ',
amount: 2,
memo: `tx ${i}`,
mutez: true
}])

const millis = Date.now() - start;
const time = Math.floor(millis / 1000)
console.log(`seconds elapsed for tx ${i} = ${time}`);
totalTime += time

const op = await saplingContract.methods.default([tx]).send();
await op.confirmation();

}

const aliceTxViewer = await aliceSaplingToolkit.getSaplingTransactionViewer()
const balance = await aliceTxViewer.getBalance();
console.log('balance', balance.toString());
console.log('average', totalTime / numberOfSaplingTx)

} catch (ex) {
console.error(ex);
}
}

example();
1 change: 1 addition & 0 deletions example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
"@taquito/taquito": "^13.0.1",
"@taquito/tzip16": "^13.0.1",
"@taquito/utils": "^13.0.1",
"@taquito/sapling": "^13.0.1",
"bignumber.js": "^9.0.2"
},
"devDependencies": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ CONFIGS().forEach(({ lib, rpc, setup}) => {

test('Originates a contract with a single sapling state in its storage', async (done) => {
const op = await Tezos.contract.originate({
code: singleSaplingStateContractJProtocol,
code: singleSaplingStateContractJProtocol(),
init: '{}'
});
await op.confirmation();
Expand Down
3 changes: 1 addition & 2 deletions integration-tests/contract-simple-origination.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@ import { CONFIGS } from "./config";

CONFIGS().forEach(({ lib, rpc, setup }) => {
const Tezos = lib;
const test = require('jest-retries');

describe(`Originating a contract api using: ${rpc}`, () => {

beforeEach(async (done) => {
await setup()
done()
})
test('Simple origination scenario', 2, async (done: () => void) => {
test('Simple origination scenario', async (done) => {
const op = await Tezos.contract.originate({
balance: "1",
code: `parameter string;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
export const singleSaplingStateContractJProtocol = `# This contract manages a shielded pool with a 1 to 1 conversion with respect to
export const singleSaplingStateContractJProtocol = (memoSize = 8) => `
# This contract manages a shielded pool with a 1 to 1 conversion with respect to
# the mutez, updated by a list of Sapling transactions.
# As a convention, all unshield transactions must contain in their bound_data
# field a Micheline encoded public_key_hash which is used as the recipient of
# the unshielded tez.
storage (sapling_state 8);
parameter (list (sapling_transaction 8));
storage (sapling_state ${memoSize});
parameter (list (sapling_transaction ${memoSize}));
code { # Stack manipulation
UNPAIR;
NIL operation;
Expand Down
2 changes: 1 addition & 1 deletion integration-tests/originate-known-contracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ CONFIGS().forEach(({ lib, setup }) => {

// KnownSaplingContract
await originateKnownContract('SaplingContract', tezos, {
code: singleSaplingStateContractJProtocol,
code: singleSaplingStateContractJProtocol(),
init: '{}'
});

Expand Down
Loading

0 comments on commit bbd425f

Please sign in to comment.