mirror of
https://github.com/alexgo-io/stacks-subnets.git
synced 2026-04-29 04:05:24 +08:00
Added steps to build and test on Subnets
This commit is contained in:
committed by
Brice Dobry
parent
256b58a0af
commit
565323acbd
@@ -267,4 +267,4 @@ principal now owns the NFT (`ALT_USER_ADDR`). You can check this by clicking on
|
||||
|
||||
|
||||
That is the conclusion of this demo! If you have any issues with this demo, reach out on the Stacks Discord or leave an issue in the
|
||||
stacks-subnets repository.
|
||||
stacks-subnets repository.
|
||||
|
||||
434
README.md
434
README.md
@@ -41,9 +41,437 @@ In a fully - trusted model:
|
||||
- Trust can be federated with a 2-phase commit and BFT protocol for miner block issuance.
|
||||
- Federation requires a majority of miners to approve withdrawals.
|
||||
|
||||
## Getting started
|
||||
|
||||
You can start with an NFT use case demo [here](https://github.com/hirosystems/stacks-subnets/blob/master/NFT_USE_CASE.md).
|
||||
## Getting Started
|
||||
|
||||
Subnets can be built using any of the following methods:
|
||||
- Build with Clarinet
|
||||
- Build with Testnet
|
||||
- Test Locally
|
||||
|
||||
> **_NOTE:_**
|
||||
>
|
||||
> Subnets was earlier referred as Hyperchains. While we are in the process of updating the content, you might still see subnets being referred as Hyperchains.
|
||||
|
||||
## Build with Clarinet
|
||||
|
||||
Make sure you have `clarinet` installed locally, and that it is at version 0.33.0 or above.
|
||||
If you do not have clarinet, you can find installation instructions [here](https://github.com/hirosystems/clarinet).
|
||||
|
||||
Let's create a new clarinet project. This will create a new directory with a Clarinet project initialized.
|
||||
```
|
||||
clarinet new nft-use-case
|
||||
```
|
||||
|
||||
Let us copy contract files and scripts over from the `stacks-hyperchains` repository into the `nft-use-case` directory.
|
||||
If you don't already have the stacks-subnets repository, you can [clone it](https://github.com/hirosystems/stacks-subnets).
|
||||
Here's the command to clone the stacks-hyperchains repository:
|
||||
```
|
||||
git clone https://github.com/hirosystems/stacks-hyperchains.git
|
||||
```
|
||||
Set the environment variable `HYPERCHAIN_PATH` to the location of the stacks-hyperchains repository on your computer.
|
||||
```
|
||||
export HYPERCHAIN_PATH=<YOUR_PATH_HERE>
|
||||
```
|
||||
|
||||
Now, we can copy files from the stacks-hyperchains repository. These files are contracts that define the layer-1 and layer-2 Clarity traits for NFTs and fungible tokens, implement an NFT in layer-1 and layer-2, and some NodeJS scripts for helping to deploy the contracts.
|
||||
```
|
||||
mkdir nft-use-case/contracts-l2
|
||||
mkdir nft-use-case/scripts
|
||||
cp $HYPERCHAIN_PATH/core-contracts/contracts/helper/simple-nft.clar nft-use-case/contracts/
|
||||
cp $HYPERCHAIN_PATH/core-contracts/contracts/helper/trait-standards.clar nft-use-case/contracts/
|
||||
cp $HYPERCHAIN_PATH/core-contracts/contracts/helper/simple-nft-l2.clar nft-use-case/contracts-l2/
|
||||
cp $HYPERCHAIN_PATH/core-contracts/contracts/helper/trait-standards.clar nft-use-case/contracts-l2/
|
||||
cp $HYPERCHAIN_PATH/contrib/scripts/nft-use-case/* nft-use-case/scripts/
|
||||
cd nft-use-case/scripts
|
||||
```
|
||||
|
||||
To use the scripts in this demo, we need to install some NodeJS libraries.
|
||||
Before running the following instructions, make sure you have [node](https://nodejs.org/en/) installed.
|
||||
```
|
||||
npm install
|
||||
```
|
||||
|
||||
The `Devnet.toml` file in the `nft-use-case` directory is responsible for configuring the `clarinet integrate`
|
||||
local network. Make the following change in `settings/Devnet.toml` to enable the hyperchain:
|
||||
```
|
||||
[devnet]
|
||||
...
|
||||
enable_hyperchain_node = true
|
||||
```
|
||||
|
||||
Let's spin up a hyperchain node. Before you call this, make sure that you have a working installation of Docker running
|
||||
locally.
|
||||
|
||||
```
|
||||
clarinet integrate
|
||||
```
|
||||
|
||||
Before we publish any transactions, you will need to set up some environment variables.
|
||||
These environment variables contain the address and private key of the hyperchain miner, two user addresses
|
||||
and private keys, and the RPC URL which we can query for hyperchain state.
|
||||
Open a separate terminal window, navigate to the directory `nft-use-case/scripts`, and enter the following.
|
||||
```
|
||||
export AUTH_HC_MINER_ADDR=ST3AM1A56AK2C1XAFJ4115ZSV26EB49BVQ10MGCS0
|
||||
export AUTH_HC_MINER_KEY=7036b29cb5e235e5fd9b09ae3e8eec4404e44906814d5d01cbca968a60ed4bfb01
|
||||
|
||||
export USER_ADDR=ST2NEB84ASENDXKYGJPQW86YXQCEFEX2ZQPG87ND
|
||||
export USER_KEY=f9d7206a47f14d2870c163ebab4bf3e70d18f5d14ce1031f3902fbbc894fe4c701
|
||||
|
||||
export ALT_USER_ADDR=ST2REHHS5J3CERCRBEPMGH7921Q6PYKAADT7JP2VB
|
||||
export ALT_USER_KEY=3eccc5dac8056590432db6a35d52b9896876a3d5cbdea53b72400bc9c2099fe801
|
||||
|
||||
export HYPERCHAIN_URL="http://localhost:30443"
|
||||
```
|
||||
|
||||
## Build with Testnet
|
||||
|
||||
### 1. Getting Testnet STX
|
||||
|
||||
```bash
|
||||
./target/debug/blockstack-cli generate-sk --testnet
|
||||
{
|
||||
"publicKey": "02c3c7ab279c5637ea5f024f8036c5218b6d1e71518adba0693c3dcc7bead92305",
|
||||
"stacksAddress": "STFTX3F4XCY7RS5VRHXP2SED0WC0YRKNWTNXD74P"
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
curl -X POST "https://stacks-node-api.testnet.stacks.co/extended/v1/faucets/stx?address=STFTX3F4XCY7RS5VRHXP2SED0WC0YRKNWTNXD74P&stacking=true"
|
||||
```
|
||||
|
||||
### 2. Spin up testnet `stacks-node`
|
||||
|
||||
|
||||
```toml
|
||||
[node]
|
||||
working_dir = "/var/testnet-stacks-node"
|
||||
rpc_bind = "127.0.0.1:20443"
|
||||
p2p_bind = "0.0.0.0:20444"
|
||||
bootstrap_node = "047435c194e9b01b3d7f7a2802d6684a3af68d05bbf4ec8f17021980d777691f1d51651f7f1d566532c804da506c117bbf79ad62eea81213ba58f8808b4d9504ad@testnet.stacks.co:20444"
|
||||
|
||||
[burnchain]
|
||||
chain = "bitcoin"
|
||||
mode = "xenon"
|
||||
peer_host = "bitcoind.testnet.stacks.co"
|
||||
username = "blockstack"
|
||||
password = "blockstacksystem"
|
||||
rpc_port = 18332
|
||||
peer_port = 18333
|
||||
|
||||
# Used for sending events to a local stacks-blockchain-api service
|
||||
# [[events_observer]]
|
||||
# endpoint = "localhost:3700"
|
||||
# retry_count = 255
|
||||
# events_keys = ["*"]
|
||||
|
||||
[[ustx_balance]]
|
||||
address = "ST2QKZ4FKHAH1NQKYKYAYZPY440FEPK7GZ1R5HBP2"
|
||||
amount = 10000000000000000
|
||||
|
||||
[[ustx_balance]]
|
||||
address = "ST319CF5WV77KYR1H3GT0GZ7B8Q4AQPY42ETP1VPF"
|
||||
amount = 10000000000000000
|
||||
|
||||
[[ustx_balance]]
|
||||
address = "ST221Z6TDTC5E0BYR2V624Q2ST6R0Q71T78WTAX6H"
|
||||
amount = 10000000000000000
|
||||
|
||||
[[ustx_balance]]
|
||||
address = "ST2TFVBMRPS5SSNP98DQKQ5JNB2B6NZM91C4K3P7B"
|
||||
amount = 10000000000000000
|
||||
```
|
||||
|
||||
|
||||
```bash
|
||||
./target/release/stacks-node start --config=/var/devel/stacks-hyperchains/contrib/conf/stacks-l1-testnet.toml 2>&1 | tee -i /tmp/stacks-testnet-0426-1055.log
|
||||
```
|
||||
|
||||
Note: You can use an existing testnet chain state if you have one available.
|
||||
I used `cp /root/stacks-node/` on one of the Hiro deployed xenon followers.
|
||||
The first bootup did not work: I `CTRL-C`'d the execution, which triggered a panic,
|
||||
but on the next start, the node booted fine.
|
||||
|
||||
### 3. Launch the contract
|
||||
|
||||
Collect the contracts:
|
||||
|
||||
```bash
|
||||
mkdir my-hyperchain/
|
||||
mkdir my-hyperchain/contracts
|
||||
cp stacks-hyperchains/core-contracts/contracts/hyperchains.clar my-hyperchain/contracts/
|
||||
cp stacks-hyperchains/core-contracts/contracts/helper/trait-standards.clar my-hyperchain/contracts/
|
||||
```
|
||||
|
||||
Set the miners list to contain the address generated in Step 1:
|
||||
|
||||
```bash
|
||||
sed -ie "s#^(define-constant miners.*#(define-constant miners (list \'STFTX3F4XCY7RS5VRHXP2SED0WC0YRKNWTNXD74P))#" my-hyperchain/contracts/hyperchains.clar
|
||||
```
|
||||
|
||||
Make the transactions -- you will need to set the private key of the contract publisher as an env var:
|
||||
|
||||
```bash
|
||||
export CONTRACT_PUBLISH_KEY=<PRIVATEKEY>
|
||||
```
|
||||
|
||||
This is the private key from the first step.
|
||||
|
||||
```bash
|
||||
mkdir my-hyperchain/scripts
|
||||
cp stacks-hyperchains/contrib/scripts/* my-hyperchain/scripts/
|
||||
cd my-hyperchain/scripts/
|
||||
npm i @stacks/network
|
||||
npm i @stacks/transactions
|
||||
mkdir ../transactions/
|
||||
node ./publish_tx.js trait-standards ../contracts/trait-standards.clar 0 > ../transactions/trait-publish.hex
|
||||
node ./publish_tx.js hc-alpha ../contracts/hyperchains.clar 1 > ../transactions/hc-publish.hex
|
||||
```
|
||||
|
||||
Submit the transactions:
|
||||
|
||||
```bash
|
||||
$ node ./broadcast_tx.js ../transactions/trait-publish.hex
|
||||
{
|
||||
txid: '93cae889b9382c512e55715e5357b388734c0448643e2cc35d2a1aab90dcf61a'
|
||||
}
|
||||
|
||||
$ node ./broadcast_tx.js ../transactions/hc-publish.hex
|
||||
{
|
||||
txid: '8c457091916a7f57b487162e0692c2cd28e71dd0b2dc9a9dfad73f93babe1dfd'
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Configure the HC miner
|
||||
|
||||
Create a `toml` configuration for the hyperchains miner. Importantly,
|
||||
you should set the `contract_identifier` to the contract published in
|
||||
Steps 3 (e.g., `STFTX3F4XCY7RS5VRHXP2SED0WC0YRKNWTNXD74P.hc-alpha`).
|
||||
|
||||
```toml
|
||||
[node]
|
||||
working_dir = "/var/my-hyperchain/hc-alpha"
|
||||
rpc_bind = "127.0.0.1:80443"
|
||||
p2p_bind = "127.0.0.1:80444"
|
||||
mining_key = "<FILL HERE>"
|
||||
miner = true
|
||||
wait_time_for_microblocks = 50_000
|
||||
|
||||
[miner]
|
||||
first_attempt_time_ms = 60_000
|
||||
subsequent_attempt_time_ms = 60_000
|
||||
microblock_attempt_time_ms = 30_000
|
||||
|
||||
[burnchain]
|
||||
chain = "stacks_layer_1"
|
||||
mode = "xenon"
|
||||
first_burn_header_height = 46_721
|
||||
first_burn_header_hash = "9ba2f357115308fb1c503715f3a1b0cb3e8fdbe6baea7e7634635affdf675501"
|
||||
contract_identifier = "<CONTRACT_NAME_HERE>"
|
||||
peer_host = "127.0.0.1"
|
||||
rpc_port = 20443
|
||||
peer_port = 20444
|
||||
rpc_ssl = false
|
||||
|
||||
[[ustx_balance]]
|
||||
address = "STFTX3F4XCY7RS5VRHXP2SED0WC0YRKNWTNXD74P"
|
||||
amount = 10000000000000000
|
||||
```
|
||||
|
||||
Add to L1 node config:
|
||||
```
|
||||
[[events_observer]]
|
||||
endpoint = "localhost:50303"
|
||||
retry_count = 255
|
||||
events_keys = ["*"]
|
||||
```
|
||||
|
||||
### 5. Start the nodes
|
||||
|
||||
The `hyperchain-node` must be started before the `stacks-node`:
|
||||
|
||||
```bash
|
||||
./target/release/hyperchain-node start --config=/var/my-hyperchain/configs/hc-miner.toml 2>&1 | tee /var/my-hyperchain/hc-miner.log
|
||||
```
|
||||
|
||||
The `stacks-node` must be started from a state _before_ the
|
||||
`first_burn_header_height` and `first_burn_header_hash` configured
|
||||
in the hyperchain node's TOML.
|
||||
|
||||
```bash
|
||||
./target/release/stacks-node start --config=/var/stacks-hyperchains/contrib/conf/stacks-l1-testnet.toml 2>&1 | tee -i /tmp/stacks-testnet.log
|
||||
```
|
||||
|
||||
## Test Locally
|
||||
|
||||
### 1. Start the hyperchain miner
|
||||
|
||||
```bash
|
||||
hyperchain-node start --config=$STACKS_HYPERCHAINS_PATH/contrib/conf/hyperchain-l2.toml 2>&1 | tee -i /tmp/stacks-hc.log
|
||||
```
|
||||
|
||||
### 2. Start a local Stacks network
|
||||
|
||||
```bash
|
||||
stacks-node start --config=$STACKS_HYPERCHAINS_PATH/contrib/conf/stacks-l1-mocknet-local.toml 2>&1 | tee -i /tmp/stacks-mocknet.log
|
||||
```
|
||||
|
||||
### 3. Launch the contract
|
||||
|
||||
Collect the contracts:
|
||||
|
||||
```bash
|
||||
mkdir my-hyperchain/
|
||||
mkdir my-hyperchain/contracts
|
||||
cp stacks-hyperchains/core-contracts/contracts/hyperchains.clar my-hyperchain/contracts/
|
||||
cp stacks-hyperchains/core-contracts/contracts/helper/ft-trait-standard.clar my-hyperchain/contracts/
|
||||
cp stacks-hyperchains/core-contracts/contracts/helper/nft-trait-standard.clar my-hyperchain/contracts/
|
||||
```
|
||||
|
||||
Set the miners list to contain the address generated in Step 1:
|
||||
|
||||
```bash
|
||||
sed -ie "s#^(define-data-var miner (optional principal) none)#(define-data-var miner (optional principal) (some \'ST2GE6HSXT81X9X3ATQ14WPT49X915R8X7FVERMBP))#" my-hyperchain/contracts/hyperchains.clar
|
||||
```
|
||||
|
||||
Make the transactions -- you will need to set the private key of the contract publisher as an env var:
|
||||
|
||||
```bash
|
||||
export CONTRACT_PUBLISH_KEY=0916e2eb04b5702e0e946081829cee67d3bb76e1792af506646843db9252ff4101
|
||||
```
|
||||
|
||||
This is the private key from the first step.
|
||||
|
||||
```bash
|
||||
mkdir my-hyperchain/scripts
|
||||
cp stacks-hyperchains/contrib/scripts/* my-hyperchain/scripts/
|
||||
cd my-hyperchain/scripts/
|
||||
npm i @stacks/network
|
||||
npm i @stacks/transactions
|
||||
mkdir ../transactions/
|
||||
node ./publish_tx.js ft-trait-standard ../contracts/ft-trait-standard.clar 0 > ../transactions/ft-publish.hex
|
||||
node ./publish_tx.js nft-trait-standard ../contracts/nft-trait-standard.clar 1 > ../transactions/nft-publish.hex
|
||||
node ./publish_tx.js hyperchain ../contracts/hyperchains.clar 2 > ../transactions/hc-publish.hex
|
||||
```
|
||||
|
||||
Submit the transactions:
|
||||
|
||||
```bash
|
||||
for I in `ls ../transactions/`; do node ./broadcast_tx.js "../transactions/$I" http://localhost:20443; done
|
||||
```
|
||||
|
||||
### 4. Deposit some funds to L2
|
||||
|
||||
```js
|
||||
const network = require('@stacks/network');
|
||||
const transactions = require('@stacks/transactions');
|
||||
const senderKey = "aaf57b4730f713cf942bc63f0801c4a62abe5a6ac8e3da10389f9ca3420b0dc701"
|
||||
const layer1 = new network.StacksTestnet();
|
||||
layer1.coreApiUrl = "http://localhost:20443";
|
||||
|
||||
const depositTransaction = await transactions.makeContractCall({
|
||||
senderKey, network: layer1, anchorMode: transactions.AnchorMode.Any,
|
||||
nonce: 0,
|
||||
contractAddress: "ST2GE6HSXT81X9X3ATQ14WPT49X915R8X7FVERMBP",
|
||||
contractName: "hyperchain",
|
||||
functionName: "deposit-stx",
|
||||
functionArgs: [ transactions.uintCV(100000000000),
|
||||
transactions.standardPrincipalCV("ST18F1AHKW194BWQ3CEFDPWVRARA79RBGFEWSDQR8")],
|
||||
fee: 10000,
|
||||
postConditionMode: transactions.PostConditionMode.Allow,
|
||||
});
|
||||
|
||||
const depositTxid = await transactions.broadcastTransaction(depositTransaction, layer1);
|
||||
```
|
||||
|
||||
Check that you received the funds in L2:
|
||||
|
||||
```js
|
||||
const layer2 = new network.StacksTestnet();
|
||||
layer2.coreApiUrl = "http://localhost:19443";
|
||||
await fetch(layer2.getAccountApiUrl("ST18F1AHKW194BWQ3CEFDPWVRARA79RBGFEWSDQR8")).then(x => x.json()).then(x => parseInt(x.balance));
|
||||
```
|
||||
|
||||
### 5. Submit an L2 transaction
|
||||
|
||||
|
||||
```js
|
||||
const codeBody = "(define-public (stx-withdraw (amount uint)) (stx-withdraw? amount tx-sender))";
|
||||
const contractName = "withdraw-helper";
|
||||
const deployWithdrawal = await transactions.makeContractDeploy({
|
||||
codeBody, contractName, senderKey, network: layer2,
|
||||
anchorMode: transactions.AnchorMode.Any, nonce: 0,
|
||||
fee: 10000,
|
||||
});
|
||||
|
||||
await transactions.broadcastTransaction(deployWithdrawal, layer2);
|
||||
```
|
||||
|
||||
|
||||
### 6. Withdraw
|
||||
|
||||
Perform the withdrawal on layer-2
|
||||
|
||||
```js
|
||||
const withdrawTransaction = await transactions.makeContractCall({
|
||||
senderKey, network: layer2, anchorMode: transactions.AnchorMode.Any,
|
||||
nonce: 1,
|
||||
contractAddress: "ST18F1AHKW194BWQ3CEFDPWVRARA79RBGFEWSDQR8",
|
||||
contractName: "withdraw-helper",
|
||||
functionName: "stx-withdraw",
|
||||
functionArgs: [ transactions.uintCV(50000) ],
|
||||
fee: 10000,
|
||||
postConditionMode: transactions.PostConditionMode.Allow,
|
||||
});
|
||||
|
||||
await transactions.broadcastTransaction(withdrawTransaction, layer2);
|
||||
```
|
||||
|
||||
Find the withdrawal event in our log:
|
||||
|
||||
```bash
|
||||
cat /tmp/stacks-hc.log | grep "Parsed L2"
|
||||
```
|
||||
|
||||
Perform the withdrawal on layer-1
|
||||
|
||||
```js
|
||||
let withdrawUrl = "http://localhost:19443/v2/withdrawal/stx/14/ST18F1AHKW194BWQ3CEFDPWVRARA79RBGFEWSDQR8/0/50000";
|
||||
let json_merkle_entry = await fetch(withdrawUrl).then(x => x.json())
|
||||
let cv_merkle_entry = {
|
||||
withdrawal_leaf_hash: transactions.deserializeCV(json_merkle_entry.withdrawal_leaf_hash),
|
||||
withdrawal_root: transactions.deserializeCV(json_merkle_entry.withdrawal_root),
|
||||
sibling_hashes: transactions.deserializeCV(json_merkle_entry.sibling_hashes),
|
||||
};
|
||||
|
||||
const layer1WithdrawTransaction = await transactions.makeContractCall({
|
||||
senderKey, network: layer1, anchorMode: transactions.AnchorMode.Any,
|
||||
nonce: 1,
|
||||
contractAddress: "ST2GE6HSXT81X9X3ATQ14WPT49X915R8X7FVERMBP",
|
||||
contractName: "hyperchain",
|
||||
functionName: "withdraw-stx",
|
||||
functionArgs: [ transactions.uintCV(50000),
|
||||
transactions.standardPrincipalCV("ST18F1AHKW194BWQ3CEFDPWVRARA79RBGFEWSDQR8"),
|
||||
cv_merkle_entry.withdrawal_root,
|
||||
cv_merkle_entry.withdrawal_leaf_hash,
|
||||
cv_merkle_entry.sibling_hashes ],
|
||||
fee: 5000,
|
||||
postConditionMode: transactions.PostConditionMode.Allow,
|
||||
});
|
||||
|
||||
await transactions.broadcastTransaction(layer1WithdrawTransaction, layer1)
|
||||
;
|
||||
```
|
||||
|
||||
## Run Tests
|
||||
|
||||
In your terminal, you can run tests by navigating to the `testnet/stacks-node/` directory and run the following command:
|
||||
|
||||
`testnet/stacks-node$ cargo test`
|
||||
|
||||
If you want to ignore some tests, you can use the following command:
|
||||
|
||||
`testnet/stacks-node$ cargo test -- --ignored --num-threads=1`
|
||||
|
||||
## Resources
|
||||
|
||||
@@ -52,3 +480,5 @@ You can start with an NFT use case demo [here](https://github.com/hirosystems/st
|
||||
- [Update on subnets, a scaling solution for Stacks.](https://www.hiro.so/blog/an-update-on-hyperchains-a-scaling-solution-for-stacks)
|
||||
|
||||
|
||||
## License information
|
||||
To be added
|
||||
|
||||
Reference in New Issue
Block a user