doc/testnet/contract: use the smart contract template repository for examples

This commit is contained in:
skoupidi
2026-01-02 18:15:07 +02:00
parent 375da0047b
commit 28659e261f

View File

@@ -1,55 +1,44 @@
# Custom Smart Contracts # Custom Smart Contracts
Users can deploy their own zero-knowledge contracts, written for the Users can deploy their own zero-knowledge contracts, written for the
DarkFi [zkVM](../zkas/index.md), becoming anonymous engineers DarkFi [zkVM][1], becoming anonymous engineers themselves!
themselves!
More information about the smart contracts architecture can be found More information about the smart contracts architecture can be found
[here](../arch/sc/sc.md). [here][2].
## Hello World ## Hello World
For the porpuses of this guide, an example smart contract is provided, For the porpuses of this guide we are going to use the smart contract
which users can deploy to the testnet and interact with. This is a very template found [here][3]. Its a very simple smart contract simulating a
simple smart contract simulating a registration ledger, where users can registration ledger, where users can create their membership keys and
register or remove themselves from it, and each user is represented as register or remove themselves from it. Each user is represented as the
the [poseidon hash](../spec/crypto-schemes.md#poseidonhash-function) of their [poseidon hash][4] of their membership public key.
public key.
First, open another terminal and navigate (from repo root) to the First, open another terminal, clone the template repository and enter
example: its directory:
```shell ```shell
$ cd example/wasm-hello-world $ git clone https://codeberg.org/darkrenaissance/smart-contract
$ cd smart-contract
``` ```
Here, generate the contract `WASM` bincode by executing: Here, generate the contract `WASM` bincode and its client by executing:
```shell ```shell
$ make $ make
"../../zkas" proof/secret_commitment.zk -o proof/secret_commitment.zk.bin ../darkfi/zkas proof/membership_proof.zk -o proof/membership_proof.zk.bin
Wrote output to proof/secret_commitment.zk.bin Wrote output to proof/membership_proof.zk.bin
RUSTFLAGS="" cargo build --target=wasm32-unknown-unknown \ cargo build --target=wasm32-unknown-unknown --release --lib
--release --package wasm_hello_world
... ...
Compiling wasm_hello_world v0.0.1 (/home/anon/darkfi/example/wasm-hello-world) Compiling membership v0.0.1 (/home/anon/smart-contract)
Finished `release` profile [optimized] target(s) in 15.90s Finished `release` profile [optimized] target(s) in 14.45s
cp -f target/wasm32-unknown-unknown/release/wasm_hello_world.wasm wasm_hello_world.wasm cp -f target/wasm32-unknown-unknown/release/membership.wasm membership.wasm
``` cargo build --release --features=client --bin membership
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) Compiling membership v0.0.1 (/home/anon/smart-contract)
Finished `release` profile [optimized] target(s) in 2m 29s Finished `release` profile [optimized] target(s) in 30.78s
cp -f target/x86_64-unknown-linux-gnu/release/client client cp -f target/release/membership membership
``` ```
Now both the contract and its client are ready to use. Leave this Now both the contract and its client are ready to use. Leave this
@@ -90,7 +79,7 @@ Now that we have a contract authority, we can deploy the example
contract we compiled earlier using it: contract we compiled earlier using it:
```shell ```shell
drk> contract deploy {CONTRACT_ID} example/wasm-hello-world/wasm_hello_world.wasm | broadcast drk> contract deploy {CONTRACT_ID} ../smart-contract/membership.wasm | broadcast
[mark_tx_spend] Processing transaction: d0824bb0ecb9b12af69579c01c570c0275e399b80ef10f0a9c645af65bdd0415 [mark_tx_spend] Processing transaction: d0824bb0ecb9b12af69579c01c570c0275e399b80ef10f0a9c645af65bdd0415
[mark_tx_spend] Found Money contract in call 1 [mark_tx_spend] Found Money contract in call 1
@@ -114,7 +103,7 @@ not locked. Each redeployment will show a new record in the contract
history. We can also export the deployed data by executing: history. We can also export the deployed data by executing:
```shell ```shell
drk> contract export-data {TX_HASH} > wasm_hello_world.dat drk> contract export-data {TX_HASH} > membership.dat
``` ```
The exported files contains the `WASM` bincode and instruction data The exported files contains the `WASM` bincode and instruction data
@@ -164,61 +153,35 @@ drk> contract list {CONTRACT_ID}
Now that the contract code is set on-chain and cannot be modified Now that the contract code is set on-chain and cannot be modified
further, let's interact with it using its client! further, let's interact with it using its client!
### Registration
Let's go to the contract client terminal, and create our membership
keys:
```shell
$ ./membership generate
Secret key: {IDENTITY_SECRET_KEY}
Public key: {IDENTITY_PUBLIC_KEY}
```
> NOTE: This is a very basic example client so secrets keys are used > 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 > as plainext for simplicity. Do not run this in a machine with
> commands history or in a hostile environment where your secret key > commands history or in a hostile environment where your secret key
> can be exposed. > can be exposed.
First lets generate a new throwaway address to register: Now we can can create our `register` call using our membership secret
key:
```shell ```shell
drk> wallet keygen $ ./membership register {CONTRACT_ID} {IDENTITY_SECRET_KEY} > register.call
Generating a new keypair
New address:
{DUMMY_ADDRESS}
``` ```
Set it as the default one in the wallet and grab its secret key: Now we need to go back to our `drk` interactive shell, to generate the
actual registration transaction, attach a fee to it and broadcast it to
the network:
```shell ```shell
drk> wallet addresses drk> tx-from-calls < ../smart-contract/register.call | attach-fee | broadcast
Key ID | Address | Public Key | Secret Key | Is Default
-----------------------+------------------+-----------------------------+-----------------------------+------------
1 | {NORMAL_ADDRESS} | {NORMAL_ADDRESS_PUBLIC_KEY} | {NORMAL_ADDRESS_SECRET_KEY} | *
...
{DUMMY_ADDRESS_INDEX} | {DUMMY_ADDRESS} | {DUMMY_ADDRESS_PUBLIC_KEY} | {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] Processing transaction: 23ea7d01ae16389e71d73fa27748ce1633d39c6b55a4aa31d8f5ba1017a4f840
[mark_tx_spend] Found Money contract in call 1 [mark_tx_spend] Found Money contract in call 1
@@ -226,28 +189,23 @@ Broadcasting transaction...
Transaction ID: 23ea7d01ae16389e71d73fa27748ce1633d39c6b55a4aa31d8f5ba1017a4f840 Transaction ID: 23ea7d01ae16389e71d73fa27748ce1633d39c6b55a4aa31d8f5ba1017a4f840
``` ```
After a while, we will see a new member in our contract registry: After the transaction has been confirmed, our membership commitment
will exist in our contract registry.
```shell
$ ./client -c {CONTRACT_ID} list
{CONTRACT_ID} members:
1. 0x242d4a0dc358e0b61053c28352f666bace79889559b65e91efd1c83c7c817fdd
```
### Deregistration ### Deregistration
To remove ourselves from the registry, we create a deregister To remove ourselves from the registry, we create a `deregister` call
transaction with the contract client: with the contract client:
```shell ```shell
$ ./client -c {CONTRACT_ID} deregister {DUMMY_ADDRESS_SECRET_KEY} > deregister.tx $ ./membership deregister {CONTRACT_ID} {IDENTITY_SECRET_KEY} > deregister.call
``` ```
Then, we attach the fee again and broadcast it to the network: Then, we build the actual deregistration transaction again, attach its
fee and broadcast it to the network:
```shell ```shell
drk> attach-fee < example/wasm-hello-world/client/deregister.tx | broadcast drk> tx-from-calls < ../smart-contract/deregister.call | attach-fee | broadcast
[mark_tx_spend] Processing transaction: f3304e6f5673d9ece211af6dd85c70ec8c8e85e91439b8cffbcf5387b11de1d0 [mark_tx_spend] Processing transaction: f3304e6f5673d9ece211af6dd85c70ec8c8e85e91439b8cffbcf5387b11de1d0
[mark_tx_spend] Found Money contract in call 1 [mark_tx_spend] Found Money contract in call 1
@@ -255,12 +213,64 @@ Broadcasting transaction...
Transaction ID: f3304e6f5673d9ece211af6dd85c70ec8c8e85e91439b8cffbcf5387b11de1d0 Transaction ID: f3304e6f5673d9ece211af6dd85c70ec8c8e85e91439b8cffbcf5387b11de1d0
``` ```
When the transaction gets confirmed, our registry will not have members When the transaction gets confirmed, our membership commitment will
again: will not exist in our contract registry.
```shell ### Extending the smart contract client
$ ./client -c {CONTRACT_ID} list
{CONTRACT_ID} members: The template client is barebones and doesn't provide us with a way to
No members found view the on chain records of our registry. For that porpuse we can
create a new small program, or extend the client to support this
functionality. Following you will find examplatory code for retrieving
a smart contract records from `darkfid` [JSON-RPC][5], which we can use
to list our registry records:
{{#tabs }}
{{#tab name="Rust" }}
```rust
// Parse Contract ID
let contract_id = match ContractId::from_str(&args.contract_id)?;
// Initialize an rpc client
let rpc_client = RpcClient::new(args.endpoint, executor.clone()).await?;
// Create the request params
let params = JsonValue::Array(vec![
JsonValue::String(contract_id.to_string()),
JsonValue::String(String::from("smart-contract-members")),
]);
// Execute the request
let req = JsonRequest::new("blockchain.get_contract_state", params);
let rep = rpc_client.request(req).await?;
// Parse response
let bytes = base64::decode(rep.get::<String>().unwrap()).unwrap();
let members: BTreeMap<Vec<u8>, Vec<u8>> = deserialize(&bytes)?;
// Print records
println!("{contract_id} members:");
if members.is_empty() {
println!("No members found");
} else {
let mut index = 1;
for member in members.keys() {
let member: pallas::Base = deserialize(member)?;
println!("{index}. {member:?}");
index += 1;
}
}
``` ```
{{#endtab }}
{{#tab name="Python" }}
```python
print("TODO")
```
{{#endtab }}
{{#endtabs }}
[1]: ../zkas/index.md
[2]: ../arch/sc/sc.md
[3]: https://codeberg.org/darkrenaissance/smart-contract
[4]: ../spec/crypto-schemes.md#poseidonhash-function
[5]: ../clients/darkfid_jsonrpc.md