|
|
@@ -0,0 +1,267 @@
|
|
|
+# Custom Smart Contracts
|
|
|
+
|
|
|
+Users can deploy their own zero-knowledge contracts, written for the
|
|
|
+DarkFi [zkVM](../zkas/index.md), becoming anonymous engineers
|
|
|
+themselves!
|
|
|
+
|
|
|
+More information about the smart contracts architecture can be found
|
|
|
+[here](../arch/sc/sc.md).
|
|
|
+
|
|
|
+## Hello World
|
|
|
+
|
|
|
+For the porpuses of this guide, an example smart contract is provided,
|
|
|
+which users can deploy to the testnet and interact with. This is a very
|
|
|
+simple smart contract simulating a registration ledger, where users can
|
|
|
+register or remove themselves from it, and each user is represented as
|
|
|
+the [poseidon hash](../spec/crypto-schemes.md#poseidonhash-function) of their
|
|
|
+public key.
|
|
|
+
|
|
|
+First, open another terminal and navigate (from repo root) to the
|
|
|
+example:
|
|
|
+
|
|
|
+```shell
|
|
|
+$ cd example/wasm-hello-world
|
|
|
+```
|
|
|
+
|
|
|
+Here, generate the contract `WASM` bincode by executing:
|
|
|
+
|
|
|
+```shell
|
|
|
+$ make
|
|
|
+
|
|
|
+"../../zkas" proof/secret_commitment.zk -o proof/secret_commitment.zk.bin
|
|
|
+Wrote output to proof/secret_commitment.zk.bin
|
|
|
+RUSTFLAGS="" cargo build --target=wasm32-unknown-unknown \
|
|
|
+ --release --package wasm_hello_world
|
|
|
+...
|
|
|
+ Compiling wasm_hello_world v0.0.1 (/home/anon/darkfi/example/wasm-hello-world)
|
|
|
+ Finished `release` profile [optimized] target(s) in 15.90s
|
|
|
+cp -f target/wasm32-unknown-unknown/release/wasm_hello_world.wasm wasm_hello_world.wasm
|
|
|
+wasm-strip wasm_hello_world.wasm
|
|
|
+```
|
|
|
+
|
|
|
+Apart from the contract, an example client to interact with it is
|
|
|
+provided, which we also need to compile:
|
|
|
+
|
|
|
+```shell
|
|
|
+$ cd client
|
|
|
+$ make
|
|
|
+
|
|
|
+RUSTFLAGS="" cargo build --target=x86_64-unknown-linux-gnu --release --package client
|
|
|
+...
|
|
|
+ Compiling client v0.0.1 (/home/anon/darkfi/example/wasm-hello-world/client)
|
|
|
+ Finished `release` profile [optimized] target(s) in 2m 29s
|
|
|
+cp -f target/x86_64-unknown-linux-gnu/release/client client
|
|
|
+```
|
|
|
+
|
|
|
+Now both the contract and its client are ready to use. Leave this
|
|
|
+terminal open, as we will come back to it later in the guide, and
|
|
|
+return back to your `drk` interactive shell.
|
|
|
+
|
|
|
+## Creating contracts
|
|
|
+
|
|
|
+Each contract is controlled by a secret key, from which its Contract ID
|
|
|
+derives. To deploy a smart contract, we need to generate an authority
|
|
|
+keypair first. The Contract ID shown in the outputs is a placeholder
|
|
|
+for the one that will be generated from you. In rest of the guide, use
|
|
|
+the one you generated by replacing the corresponding placeholder. We
|
|
|
+can create our own contract authority by executing the following
|
|
|
+command:
|
|
|
+
|
|
|
+```shell
|
|
|
+drk> contract generate-deploy
|
|
|
+
|
|
|
+Generating a new keypair
|
|
|
+Created new contract deploy authority
|
|
|
+Contract ID: {CONTRACT_ID}
|
|
|
+```
|
|
|
+
|
|
|
+You can list your mint authorities with:
|
|
|
+
|
|
|
+```shell
|
|
|
+drk> contract list
|
|
|
+
|
|
|
+ Index | Contract ID | Locked | Lock Height
|
|
|
+-------+---------------+--------+-------------
|
|
|
+ 1 | {CONTRACT_ID} | false | -
|
|
|
+```
|
|
|
+
|
|
|
+## Deploy transaction
|
|
|
+
|
|
|
+Now that we have a contract authority, we can deploy the example
|
|
|
+contract we compiled earlier using it:
|
|
|
+
|
|
|
+```shell
|
|
|
+drk> contract deploy {CONTRACT_ID} example/wasm-hello-world/wasm_hello_world.wasm | broadcast
|
|
|
+
|
|
|
+[mark_tx_spend] Processing transaction: d0824bb0ecb9b12af69579c01c570c0275e399b80ef10f0a9c645af65bdd0415
|
|
|
+[mark_tx_spend] Found Money contract in call 1
|
|
|
+Broadcasting transaction...
|
|
|
+Transaction ID: d0824bb0ecb9b12af69579c01c570c0275e399b80ef10f0a9c645af65bdd0415
|
|
|
+```
|
|
|
+
|
|
|
+Now the transaction should be published to the network. When the
|
|
|
+transaction is confirmed, the contract history will show its record:
|
|
|
+
|
|
|
+```shell
|
|
|
+drk> contract list {CONTRACT_ID}
|
|
|
+
|
|
|
+ Transaction Hash | Type | Block Height
|
|
|
+------------------+------------+--------------
|
|
|
+ {TX_HASH} | DEPLOYMENT | 34
|
|
|
+```
|
|
|
+
|
|
|
+We can redeploy the contract as many times as we want, as long as it's
|
|
|
+not locked. Each redeployment will show a new record in the contract
|
|
|
+history. We can also export the deployed data by executing:
|
|
|
+
|
|
|
+```shell
|
|
|
+drk> contract export-data {TX_HASH} > wasm_hello_world.dat
|
|
|
+```
|
|
|
+
|
|
|
+The exported files contains the `WASM` bincode and instruction data
|
|
|
+deployed by that transaction, encoded in `base64` as a tuple.
|
|
|
+
|
|
|
+## Lock transaction
|
|
|
+
|
|
|
+After we finished deploying our contract and don't require further code
|
|
|
+changes, we can lock it. This will not allow further deployment
|
|
|
+transactions, effectively locking the smart contract on-chain code. To
|
|
|
+lock down the contract, execute:
|
|
|
+
|
|
|
+```shell
|
|
|
+drk> contract lock {CONTRACT_ID} | broadcast
|
|
|
+
|
|
|
+[mark_tx_spend] Processing transaction: 9eee9799d77d0ef1dd115738982296c9c481b4412c75a0a0955fd67d87bfe6a0
|
|
|
+[mark_tx_spend] Found Money contract in call 1
|
|
|
+Broadcasting transaction...
|
|
|
+Transaction ID: 9eee9799d77d0ef1dd115738982296c9c481b4412c75a0a0955fd67d87bfe6a0
|
|
|
+```
|
|
|
+
|
|
|
+After the transaction has been confirmed, we will see our contract
|
|
|
+`Locked` status set to `true`, along with the block height it was
|
|
|
+locked on:
|
|
|
+
|
|
|
+```shell
|
|
|
+drk> contract list
|
|
|
+
|
|
|
+ Index | Contract ID | Locked | Lock Height
|
|
|
+-------+---------------+--------+-------------
|
|
|
+ 1 | {CONTRACT_ID} | true | 36
|
|
|
+```
|
|
|
+
|
|
|
+We will also see the lock transaction in its history:
|
|
|
+
|
|
|
+```shell
|
|
|
+drk> contract list {CONTRACT_ID}
|
|
|
+
|
|
|
+ Transaction Hash | Type | Block Height
|
|
|
+------------------+------------+--------------
|
|
|
+ {TX_HASH} | DEPLOYMENT | 34
|
|
|
+ {LOCK_TX_HASH} | LOCK | 36
|
|
|
+```
|
|
|
+
|
|
|
+## Interacting with the smart contract
|
|
|
+
|
|
|
+Now that the contract code is set on-chain and cannot be modified
|
|
|
+further, let's interact with it using its client!
|
|
|
+
|
|
|
+> NOTE: This is a very basic example client so secrets keys are used
|
|
|
+> as plainext for simplicity. Do not run this in a machine with
|
|
|
+> commands history or in a hostile environment where your secret key
|
|
|
+> can be exposed.
|
|
|
+
|
|
|
+First lets generate a new throwaway address to register:
|
|
|
+
|
|
|
+```shell
|
|
|
+drk> wallet keygen
|
|
|
+
|
|
|
+Generating a new keypair
|
|
|
+New address:
|
|
|
+{DUMMY_ADDRESS}
|
|
|
+```
|
|
|
+
|
|
|
+Set it as the default one in the wallet and grab its secret key:
|
|
|
+
|
|
|
+```shell
|
|
|
+drk> wallet addresses
|
|
|
+
|
|
|
+ Key ID | Public Key | Secret Key | Is Default
|
|
|
+-----------------------+------------------+-----------------------------+------------
|
|
|
+ 1 | {NORMAL_ADDRESS} | {NORMAL_ADDRESS_SECRET_KEY} | *
|
|
|
+ ...
|
|
|
+ {DUMMY_ADDRESS_INDEX} | {DUMMY_ADDRESS} | {DUMMY_ADDRESS_SECRET_KEY} |
|
|
|
+
|
|
|
+drk> wallet default-address {DUMMY_ADDRESS_INDEX}
|
|
|
+```
|
|
|
+
|
|
|
+### List members
|
|
|
+
|
|
|
+Let's go to the contract client terminal, and check current members:
|
|
|
+
|
|
|
+```shell
|
|
|
+$ ./client -c {CONTRACT_ID} list
|
|
|
+
|
|
|
+{CONTRACT_ID} members:
|
|
|
+No members found
|
|
|
+```
|
|
|
+
|
|
|
+No one has registered yet, so let's change that!
|
|
|
+
|
|
|
+### Registration
|
|
|
+
|
|
|
+Let's go to the contract client terminal, and register ourselves:
|
|
|
+
|
|
|
+```shell
|
|
|
+$ ./client -c {CONTRACT_ID} register {DUMMY_ADDRESS_SECRET_KEY} > register.tx
|
|
|
+```
|
|
|
+
|
|
|
+The produced transaction doesn't contain a fee, so we need to attach it
|
|
|
+in our `drk` interactive shell:
|
|
|
+
|
|
|
+```shell
|
|
|
+drk> attach-fee < example/wasm-hello-world/client/register.tx | broadcast
|
|
|
+
|
|
|
+[mark_tx_spend] Processing transaction: 23ea7d01ae16389e71d73fa27748ce1633d39c6b55a4aa31d8f5ba1017a4f840
|
|
|
+[mark_tx_spend] Found Money contract in call 1
|
|
|
+Broadcasting transaction...
|
|
|
+Transaction ID: 23ea7d01ae16389e71d73fa27748ce1633d39c6b55a4aa31d8f5ba1017a4f840
|
|
|
+```
|
|
|
+
|
|
|
+After a while, we will see a new member in our contract registry:
|
|
|
+
|
|
|
+```shell
|
|
|
+$ ./client -c {CONTRACT_ID} list
|
|
|
+
|
|
|
+{CONTRACT_ID} members:
|
|
|
+1. 0x242d4a0dc358e0b61053c28352f666bace79889559b65e91efd1c83c7c817fdd
|
|
|
+```
|
|
|
+
|
|
|
+### Deregistration
|
|
|
+
|
|
|
+To remove ourselves from the registry, we create a deregister
|
|
|
+transaction with the contract client:
|
|
|
+
|
|
|
+```shell
|
|
|
+$ ./client -c {CONTRACT_ID} deregister {DUMMY_ADDRESS_SECRET_KEY} > deregister.tx
|
|
|
+```
|
|
|
+
|
|
|
+Then, we attach the fee again and broadcast it to the network:
|
|
|
+
|
|
|
+```shell
|
|
|
+drk> attach-fee < example/wasm-hello-world/client/deregister.tx | broadcast
|
|
|
+
|
|
|
+[mark_tx_spend] Processing transaction: f3304e6f5673d9ece211af6dd85c70ec8c8e85e91439b8cffbcf5387b11de1d0
|
|
|
+[mark_tx_spend] Found Money contract in call 1
|
|
|
+Broadcasting transaction...
|
|
|
+Transaction ID: f3304e6f5673d9ece211af6dd85c70ec8c8e85e91439b8cffbcf5387b11de1d0
|
|
|
+```
|
|
|
+
|
|
|
+When the transaction gets confirmed, our registry will not have members
|
|
|
+again:
|
|
|
+
|
|
|
+```shell
|
|
|
+$ ./client -c {CONTRACT_ID} list
|
|
|
+
|
|
|
+{CONTRACT_ID} members:
|
|
|
+No members found
|
|
|
+```
|