diff --git a/.gitbook/assets/._lw3 faucet.png b/.gitbook/assets/._lw3 faucet.png new file mode 100644 index 0000000000..24ebd31ae5 Binary files /dev/null and b/.gitbook/assets/._lw3 faucet.png differ diff --git a/.gitbook/assets/Diagram Feb 2 2024 from Mermaid Chart (1).png b/.gitbook/assets/Diagram Feb 2 2024 from Mermaid Chart (1).png new file mode 100644 index 0000000000..ade88fb32f Binary files /dev/null and b/.gitbook/assets/Diagram Feb 2 2024 from Mermaid Chart (1).png differ diff --git a/.gitbook/assets/Diagram Feb 2 2024 from Mermaid Chart.png b/.gitbook/assets/Diagram Feb 2 2024 from Mermaid Chart.png new file mode 100644 index 0000000000..5115528c3e Binary files /dev/null and b/.gitbook/assets/Diagram Feb 2 2024 from Mermaid Chart.png differ diff --git a/.gitbook/assets/Diagram Feb 2 2024.png b/.gitbook/assets/Diagram Feb 2 2024.png new file mode 100644 index 0000000000..66f0f55535 Binary files /dev/null and b/.gitbook/assets/Diagram Feb 2 2024.png differ diff --git a/.gitbook/assets/_category_ (1).json b/.gitbook/assets/_category_ (1).json new file mode 100644 index 0000000000..0bb2c6de80 --- /dev/null +++ b/.gitbook/assets/_category_ (1).json @@ -0,0 +1,9 @@ +{ + "label": "References", + "position": 9, + "link": { + "type": "generated-index", + "description": "Stacks references." + } + } + \ No newline at end of file diff --git a/.gitbook/assets/_category_ (2).json b/.gitbook/assets/_category_ (2).json new file mode 100644 index 0000000000..eb32d48e32 --- /dev/null +++ b/.gitbook/assets/_category_ (2).json @@ -0,0 +1,8 @@ +{ + "label": "Noteworthy Contracts", + "position": 5, + "link": { + "type": "generated-index", + "description": "Specific deployed Clarity Contracts worth mentioning." + } +} diff --git a/.gitbook/assets/_category_.json b/.gitbook/assets/_category_.json new file mode 100644 index 0000000000..72c0987c1f --- /dev/null +++ b/.gitbook/assets/_category_.json @@ -0,0 +1,9 @@ +{ + "label": "Services using Stacks", + "position": 8, + "link": { + "type": "generated-index", + "description": "An 'always incomplete' list of services that use Stacks." + } +} + \ No newline at end of file diff --git a/.gitbook/assets/clarinet structure.png b/.gitbook/assets/clarinet structure.png new file mode 100644 index 0000000000..8d16dc55cf Binary files /dev/null and b/.gitbook/assets/clarinet structure.png differ diff --git a/.gitbook/assets/event log.png b/.gitbook/assets/event log.png new file mode 100644 index 0000000000..51f04851f8 Binary files /dev/null and b/.gitbook/assets/event log.png differ diff --git a/.gitbook/assets/explorer view.png b/.gitbook/assets/explorer view.png new file mode 100644 index 0000000000..74a2042496 Binary files /dev/null and b/.gitbook/assets/explorer view.png differ diff --git a/.gitbook/assets/function call.png b/.gitbook/assets/function call.png new file mode 100644 index 0000000000..32a28b347a Binary files /dev/null and b/.gitbook/assets/function call.png differ diff --git a/.gitbook/assets/image (1).png b/.gitbook/assets/image (1).png new file mode 100644 index 0000000000..7c20603126 Binary files /dev/null and b/.gitbook/assets/image (1).png differ diff --git a/.gitbook/assets/image (10).png b/.gitbook/assets/image (10).png new file mode 100644 index 0000000000..32a8b88537 Binary files /dev/null and b/.gitbook/assets/image (10).png differ diff --git a/.gitbook/assets/image (11).png b/.gitbook/assets/image (11).png new file mode 100644 index 0000000000..22cea089f0 Binary files /dev/null and b/.gitbook/assets/image (11).png differ diff --git a/.gitbook/assets/image (12).png b/.gitbook/assets/image (12).png new file mode 100644 index 0000000000..6801c67710 Binary files /dev/null and b/.gitbook/assets/image (12).png differ diff --git a/.gitbook/assets/image (13).png b/.gitbook/assets/image (13).png new file mode 100644 index 0000000000..6b97f69623 Binary files /dev/null and b/.gitbook/assets/image (13).png differ diff --git a/.gitbook/assets/image (14).png b/.gitbook/assets/image (14).png new file mode 100644 index 0000000000..74747c4066 Binary files /dev/null and b/.gitbook/assets/image (14).png differ diff --git a/.gitbook/assets/image (15).png b/.gitbook/assets/image (15).png new file mode 100644 index 0000000000..a07a970d39 Binary files /dev/null and b/.gitbook/assets/image (15).png differ diff --git a/.gitbook/assets/image (16).png b/.gitbook/assets/image (16).png new file mode 100644 index 0000000000..3f64fe503c Binary files /dev/null and b/.gitbook/assets/image (16).png differ diff --git a/.gitbook/assets/image (2).png b/.gitbook/assets/image (2).png new file mode 100644 index 0000000000..c9bb5f0c0a Binary files /dev/null and b/.gitbook/assets/image (2).png differ diff --git a/.gitbook/assets/image (3).png b/.gitbook/assets/image (3).png new file mode 100644 index 0000000000..4d5d838943 Binary files /dev/null and b/.gitbook/assets/image (3).png differ diff --git a/.gitbook/assets/image (4).png b/.gitbook/assets/image (4).png new file mode 100644 index 0000000000..ff4958f8a3 Binary files /dev/null and b/.gitbook/assets/image (4).png differ diff --git a/.gitbook/assets/image (5).png b/.gitbook/assets/image (5).png new file mode 100644 index 0000000000..94f40f5e5c Binary files /dev/null and b/.gitbook/assets/image (5).png differ diff --git a/.gitbook/assets/image (6).png b/.gitbook/assets/image (6).png new file mode 100644 index 0000000000..cc5939c28f Binary files /dev/null and b/.gitbook/assets/image (6).png differ diff --git a/.gitbook/assets/image (7).png b/.gitbook/assets/image (7).png new file mode 100644 index 0000000000..5d5fc5edad Binary files /dev/null and b/.gitbook/assets/image (7).png differ diff --git a/.gitbook/assets/image (8).png b/.gitbook/assets/image (8).png new file mode 100644 index 0000000000..2f2497d30b Binary files /dev/null and b/.gitbook/assets/image (8).png differ diff --git a/.gitbook/assets/image (9).png b/.gitbook/assets/image (9).png new file mode 100644 index 0000000000..ebb4cff489 Binary files /dev/null and b/.gitbook/assets/image (9).png differ diff --git a/.gitbook/assets/image.png b/.gitbook/assets/image.png new file mode 100644 index 0000000000..e003e7d3ff Binary files /dev/null and b/.gitbook/assets/image.png differ diff --git a/.gitbook/assets/initial screen.png b/.gitbook/assets/initial screen.png new file mode 100644 index 0000000000..222c5c517d Binary files /dev/null and b/.gitbook/assets/initial screen.png differ diff --git a/.gitbook/assets/language functions.mdx b/.gitbook/assets/language functions.mdx new file mode 100644 index 0000000000..a2aef51295 --- /dev/null +++ b/.gitbook/assets/language functions.mdx @@ -0,0 +1,15 @@ +--- +title: Functions +description: See a detailed list of all functions for the Clarity language. +sidebar_position: 3 +tags: + - clarity +--- + +import { FunctionsReferences } from "@site/src/components/ApiReferences/FunctionsReferences.jsx"; + +## Functions + +Detailed list of all functions for the Clarity language. + + diff --git a/.gitbook/assets/language keywords.mdx b/.gitbook/assets/language keywords.mdx new file mode 100644 index 0000000000..63658fab60 --- /dev/null +++ b/.gitbook/assets/language keywords.mdx @@ -0,0 +1,17 @@ +--- +title: Keywords +description: See a detailed list of all keywords for the Clarity language. +sidebar_position: 4 +tags: + - clarity +--- + +import { KeywordsReferences } from "@site/src/components/ApiReferences/KeywordsReferences.jsx"; + +![Abstract image illustrate Clarity native keywords](/img/keywords.jpg) + +## Keyword reference + +Detailed list of all keywords for the Clarity language. + + diff --git a/.gitbook/assets/loaded contract.png b/.gitbook/assets/loaded contract.png new file mode 100644 index 0000000000..95a182977d Binary files /dev/null and b/.gitbook/assets/loaded contract.png differ diff --git a/.gitbook/assets/lw3 faucet.png b/.gitbook/assets/lw3 faucet.png new file mode 100644 index 0000000000..cea83a0086 Binary files /dev/null and b/.gitbook/assets/lw3 faucet.png differ diff --git a/.gitbook/assets/new function.png b/.gitbook/assets/new function.png new file mode 100644 index 0000000000..a956d776ba Binary files /dev/null and b/.gitbook/assets/new function.png differ diff --git a/.gitbook/assets/post condition.jpeg b/.gitbook/assets/post condition.jpeg new file mode 100644 index 0000000000..465695e0bd Binary files /dev/null and b/.gitbook/assets/post condition.jpeg differ diff --git a/.gitbook/assets/quicknode endpoint.png b/.gitbook/assets/quicknode endpoint.png new file mode 100644 index 0000000000..0bde1b9353 Binary files /dev/null and b/.gitbook/assets/quicknode endpoint.png differ diff --git a/.gitbook/assets/rollup comparison.png b/.gitbook/assets/rollup comparison.png new file mode 100644 index 0000000000..e444a6e3ec Binary files /dev/null and b/.gitbook/assets/rollup comparison.png differ diff --git a/.gitbook/assets/tools new.png b/.gitbook/assets/tools new.png new file mode 100644 index 0000000000..094e36cc4f Binary files /dev/null and b/.gitbook/assets/tools new.png differ diff --git a/.gitbook/assets/tx call.png b/.gitbook/assets/tx call.png new file mode 100644 index 0000000000..e376f24ef1 Binary files /dev/null and b/.gitbook/assets/tx call.png differ diff --git a/.gitbook/assets/tx proof.png b/.gitbook/assets/tx proof.png new file mode 100644 index 0000000000..0bd0a0525f Binary files /dev/null and b/.gitbook/assets/tx proof.png differ diff --git a/README.md b/README.md index 978c9e5b07..65b420ab5a 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,45 @@ -[![Pull Requests Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat)](http://makeapullrequest.com) -[![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/stacks-network/docs) -[![Crowdin](https://badges.crowdin.net/docsstacksco/localized.svg)](https://crowdin.com/project/docsstacksco) +# Start Here -# Stacks documentation +{% hint style="info" %} +Note that these docs describe how Stacks _currently_ works pre-Nakamoto. There is a significant undertaking to improve the functionality of the chain that will change some of this called the Nakamoto Release. To learn more about how Stacks will work after Nakamoto, check out the Nakamoto Upgrade section. +{% endhint %} -This repository stores the files for the Stacks documentation website on [docs.stacks.co](https://docs.stacks.co) +## Stacks Docs -![A screenshot of docs.stacks.co](/static/img/docs-homepage.png) +**Stacks is a Bitcoin L2 that brings smart contract functionality to Bitcoin, without modifying Bitcoin itself.** -## Contributing +These docs serve as: -If you are interested in contributing to the site and making changes, please refer to our [contributing guide here](https://docs.stacks.co/contribute). +1. An introduction to Stacks (what it is, why you should care, how to build Bitcoin apps with it), +2. A reference guide to various aspects of the Stacks ecosystem +3. A collection of tutorials and code samples to help you learn and build -If you are interested in contributing to the translations of this site to multiple languages, please refer to the [translations guide](https://docs.stacks.co/docs/contribute/translations) +They are divided up into several sections: -This website uses [Docusaurus 2](https://docusaurus.io/). A local version can be run by cloning this repo and typing the following commands. +### Nakamoto Upgrade -``` -npm install -npx docusaurus start -``` +There is currently a major upgrade to Stacks underway called the Nakamoto Release. This will bring significant improvements to both security and speed. Since this work is still in-progress, there is a dedicated section describing how it works. Once the release is live on mainnet, these docs will be adjusted to reflect the way the chain works. +### Stacks 101 + +If you are interested in the basics of how Stacks works, the Stacks 101 section contains the essential information to understand how everything works. Note that this section explains how Stacks operates pre-Nakamoto, and will be updated in conjunction with the release. + +### Tutorials + +For all you developers, we have a collection of tutorials here for you to explore, both hosted here on the docs site and some others from the community. + +### Clarity Reference + +Clarity is the smart contract programming language used on the Stacks chain. This section is a reference for Clarity keywords and functions, as well as a quick tutorial for wrapping your head around the language. + +### sBTC + +sBTC is the upcoming trust-minimized Bitcoin peg system build on top of Stacks. This section will give you all the information you need to understand how it works. For tutorials on building with it, be sure to check out the tutorials section. + +### In-Depth Technical Explanations + +Finally, for those of you interested in diving deep into how Stacks works, the in-depth technical explanations are drawn from the SIPs, the documents formally outlining the technical functionality of Stacks. This will get you as much information on how things work outside of reading the source code yourself. + +### AI-Powered Semantic Search + +Looking for something specific? These docs are integrated with AI-powered semantic search, hit `Cmd/Ctrl + K` to open up the search box and ask the docs whatever you like. diff --git a/SUMMARY.md b/SUMMARY.md new file mode 100644 index 0000000000..79c080e8d2 --- /dev/null +++ b/SUMMARY.md @@ -0,0 +1,111 @@ +# Table of contents + +* [Start Here](README.md) + +## ⬆ Nakamoto Upgrade + +* [What is the Nakamoto Release?](nakamoto-upgrade/what-is-the-nakamoto-release.md) +* [Nakamoto in 10 Minutes](nakamoto-upgrade/nakamoto-in-10-minutes.md) +* [Nakamoto In-Depth](nakamoto-upgrade/nakamoto-in-depth/README.md) + * [Stackers and Signing](nakamoto-upgrade/nakamoto-in-depth/stackers-and-signing.md) + * [Chain Structure and Synchronization](nakamoto-upgrade/nakamoto-in-depth/chain-structure-and-synchronization.md) + * [Block Structure and Validation](nakamoto-upgrade/nakamoto-in-depth/block-structure-and-validation.md) + * [Transactions](nakamoto-upgrade/nakamoto-in-depth/transactions.md) + * [Changes to PoX and Clarity](nakamoto-upgrade/nakamoto-in-depth/changes-to-pox-and-clarity.md) + * [Financial Incentives and Security Budget](nakamoto-upgrade/nakamoto-in-depth/financial-incentives-and-security-budget.md) +* [Testnets](nakamoto-upgrade/testnets/README.md) + * [Neon](nakamoto-upgrade/testnets/neon.md) + * [Argon](nakamoto-upgrade/testnets/argon.md) +* [Running a Signer](nakamoto-upgrade/running-a-signer.md) +* [Stacking Flow](nakamoto-upgrade/stacking-flow.md) + +## 🎓 Stacks 101 (Pre-Nakamoto) + +* [What Is Stacks?](stacks-101/what-is-stacks.md) +* [SIPs](stacks-101/sips.md) +* [Whitepapers](stacks-101/whitepapers.md) +* [Bitcoin Connection](stacks-101/bitcoin-connection.md) +* [Proof of Transfer](stacks-101/proof-of-transfer.md) +* [Mining](stacks-101/mining.md) +* [Network](stacks-101/network.md) +* [API](stacks-101/api.md) +* [Accounts](stacks-101/accounts.md) +* [Authentication](stacks-101/authentication.md) +* [Bitcoin Name System](stacks-101/bitcoin-name-system.md) +* [Transactions](stacks-101/transactions.md) +* [Post Conditions](stacks-101/post-conditions.md) +* [Stacking](stacks-101/stacking.md) +* [Testnet](stacks-101/testnet.md) +* [Technical Specifications](stacks-101/technical-specifications.md) + +## 🛠 Tutorials + +* [Hello Stacks (Quickstart Tutorial)](tutorials/hello-stacks-quickstart-tutorial.md) +* [Bitcoin Integration](tutorials/bitcoin-integration/README.md) + * [Sending Bitcoin with Leather Wallet](tutorials/bitcoin-integration/sending-bitcoin-with-leather-wallet.md) + * [Verifying a Bitcoin Transaction](tutorials/bitcoin-integration/verifying-a-bitcoin-transaction.md) + * [Parsing a Bitcoin Transaction](tutorials/bitcoin-integration/parsing-a-bitcoin-transaction.md) +* [Tokens](tutorials/tokens/README.md) + * [Creating a NFT](tutorials/tokens/creating-a-nft.md) + * [Creating a Fungible Token](tutorials/tokens/creating-a-fungible-token.md) +* [Frontend](tutorials/frontend/README.md) + * [Post Conditions with Stacks.js](tutorials/frontend/post-conditions-with-stacks.js.md) + * [Authentication with Stacks.js](tutorials/frontend/authentication-with-stacks.js.md) + * [Sending Transactions with Stacks.js](tutorials/frontend/sending-transactions-with-stacks.js.md) +* [Community Tutorials](tutorials/community-tutorials.md) + +## 👓 Clarity + +* [Overview](clarity/overview.md) +* [Clarity Crash Course](clarity/clarity-crash-course.md) +* [Types](clarity/types.md) +* [Functions](clarity/functions.md) +* [Keywords](clarity/keywords.md) +* [Decidability](clarity/decidability.md) +* [Example Contracts](clarity/example-contracts/README.md) + * [Audited Starter Contracts](clarity/example-contracts/audited-starter-contracts.md) + * [Stacking](clarity/example-contracts/stacking.md) + * [BNS](clarity/example-contracts/bns.md) + +## ⛓ sBTC + +* [Introduction](sbtc/introduction.md) +* [sBTC Design](sbtc/sbtc-design/README.md) + * [sBTC Requests and Responses](sbtc/sbtc-design/sbtc-requests-and-responses/README.md) + * [Commit-Reveal System](sbtc/sbtc-design/sbtc-requests-and-responses/commit-reveal-system.md) + * [Clarity Contracts](sbtc/sbtc-design/sbtc-requests-and-responses/clarity-contracts.md) + * [Stacker Responsibilities](sbtc/sbtc-design/stacker-responsibilities.md) + * [Security Model](sbtc/sbtc-design/security-model.md) +* [User Guides](sbtc/user-guides/README.md) + * [How to Deposit and Withdraw](sbtc/user-guides/how-to-deposit-and-withdraw.md) + * [How to Participate as a Stacker](sbtc/user-guides/how-to-participate-as-a-stacker.md) +* [Developer Guides](sbtc/developer-guides/README.md) + * [Quickstart](sbtc/developer-guides/quickstart.md) + * [Local Environment Setup](sbtc/developer-guides/local-environment-setup.md) + * [End to End Tutorial](sbtc/developer-guides/end-to-end-tutorial.md) + * [Initiating a Deposit](sbtc/developer-guides/initiating-a-deposit.md) + * [Initiating a Withdrawal](sbtc/developer-guides/initiating-a-withdrawal.md) +* [sBTC Releases](sbtc/sbtc-releases/README.md) + * [sBTC 0.1](sbtc/sbtc-releases/sbtc-0.1.md) + * [sBTC 1.0](sbtc/sbtc-releases/sbtc-1.0.md) + * [sBTC 1.1](sbtc/sbtc-releases/sbtc-1.1.md) + +## 💻 Stacks In-Depth + +* [Nodes and Miners](stacks-in-depth/nodes-and-miners/README.md) + * [Run a Node with Docker](stacks-in-depth/nodes-and-miners/run-a-node-with-docker.md) + * [Run a Node with Digital Ocean](stacks-in-depth/nodes-and-miners/run-a-node-with-digital-ocean.md) + * [Run a Node with a Hosted Provider](stacks-in-depth/nodes-and-miners/run-a-node-with-a-hosted-provider.md) + * [Run a Node with Quicknode](stacks-in-depth/nodes-and-miners/run-a-node-with-quicknode.md) + * [Stacks Node Configuration](stacks-in-depth/nodes-and-miners/stacks-node-configuration.md) + * [Mine Testnet Stacks Tokens](stacks-in-depth/nodes-and-miners/mine-testnet-stacks-tokens.md) + * [Mine Mainnet Stacks Tokens](stacks-in-depth/nodes-and-miners/mine-mainnet-stacks-tokens.md) + * [Verify Miner](stacks-in-depth/nodes-and-miners/verify-miner.md) + * [Miner Costs and Fees](stacks-in-depth/nodes-and-miners/miner-costs-and-fees.md) + * [Miner Prerequisites](stacks-in-depth/nodes-and-miners/miner-prerequisites.md) +* [Gaia](stacks-in-depth/gaia/README.md) + * [Configuration](stacks-in-depth/gaia/configuration.md) + * [Deploy Gaia Hub](stacks-in-depth/gaia/deploy-gaia-hub.md) + * [Amazon EC2](stacks-in-depth/gaia/amazon-ec2.md) + * [Linux](stacks-in-depth/gaia/linux.md) + * [Mac OS](stacks-in-depth/gaia/mac-os.md) diff --git a/clarity/clarity-crash-course.md b/clarity/clarity-crash-course.md new file mode 100644 index 0000000000..9bb6f5b4bb --- /dev/null +++ b/clarity/clarity-crash-course.md @@ -0,0 +1,108 @@ +# Clarity Crash Course + +You now understand why Clarity exists and what problems it was designed to solve. :::note If you didn't read the previous section, go ahead and do that first. ::: + +Now let's do a really quick introduction to Clarity so you can familiarize yourself with the language and syntax. + +This crash course is designed for people who have some experience with programming but are new to Clarity. You don't need smart contract development experience, but if you do, with something like Solidity, you'll pick this up really quick. + +Once you've familiarized yourself with the language, if you'd like to continue your journey and master Clarity to become a smart contract developer, we recommend either the book, [Clarity of Mind](https://book.clarity-lang.org/title-page.html), or the course, [Clarity Universe](https://clarity-lang.org/universe), which has both a self-paced and guided cohort-based version. + +### Your First Clarity Smart Contract + +We're going to build a basic Clarity smart contract using [Clarity Tools](https://clarity.tools/code/new), so you won't have to install anything for this introduction. + +Visit that link, and it will open up a new contract for you. + +Already we can take a look at a few basic features of both Clarity and Clarity Tools. First, on the left you'll see that our Clarity code is being evaluated in real-time for us. This is really nice for experimenting and demonstrating basic Clarity code. + +Next up we can see our first bit of Clarity code. + +Those two semicolons are how we denote comments in Clarity. + +Then the next line down we have a public function declaration. + +Here is out first glimpse of Clarity's syntax, which may be new to you depending on your development background. + +For those new to Clarity, it's a little weird and uncomfortable at first, but one of the core design tenets of Clarity is simplicity and readability, and the more you work with it the more you come to appreciate the succinctness and _clarity_ (sorry) of the code you are writing. + +Clarity takes inspiration from LISP, and you can think of everything in Clarity as a list inside of a list, or an expression inside of an expression. Everything in Clarity is wrapped in parentheses, function definitions, variable declarations, function parameters, etc. + +So here we are saying that we want to: + +1. Call a function called `define-read-only`. This is a built-in function, one of many that you can refer to in the docs. +2. Pass it a parameter of hello, which corresponds to the method signature type. +3. Pass it a parameter of "Hello", which corresponds to the function body. + +You can refer to the [`define-read-only`](https://docs.stacks.co/docs/write-smart-contracts/clarity-language/language-functions#define-read-only) documentation to see these parameters. + +Why am I describing this as if we are calling a function? Because we are, and it's an important concept in Clarity that everything is an expression inside of an expression. + +Let's expand on this concept a bit by deleting this and writing a new function. + +```clarity +(define-data-var count int 0) +(define-public (add-number (number int)) + (let + ( + (current-count count) + ) + + (var-set count (+ 1 number)) + (ok (var-get count)) + ) +) + + +(add-number 5) +``` + +If you type that into Clarity Tools, you'll see the result that gets printed is 6. + +Okay there are actually a lot of Clarity concepts packed into this simple code. + +Let's go over what this code is doing, and you'll pick up some Clarity concepts along the way. + +The first thing we are doing here is defining a new variable called `count` and setting its initial value to 0. + +This will be a persistent state variable, so this is actually getting written to the blockchain. If you are new to smart contract development, the fact that data is persisted within the file like this might take some getting used to. + +So if we were to write and deploy this contract (which you can do in the Stacks Explorer if you like), as soon as it gets deployed, it will run through the contract line by line executing anything at the root level. + +Remember that Clarity is interpreted, not compiled, so there's not compile step when developing Clarity contracts. + +So this `define-data-var` will be evaluated and the `count` variable will be initialized with a value of 0. + +Next we are defining a public function called `add-number`, which will be created (but not called) on deploy as well. + +:::note In Clarity, we have public, private, and read only functions. Public allow you to modify chain state and can be called from anywhere, private do the same except they can only be called from within the contract, and read only will fail if they attempt to modify state. ::: + +This function is taking a single parameter, called `number` that is a type of `int`. + +Now, what is this `let` business all about? Remember that we said that everything in Clarity is an expression and declaring new functions is just a matter of calling the `define-public` function? + +Well the second argument to this is the function body, but it can only evaluate a single expression. + +So this `let` function is a way of wrapping a multi-step function into a single argument. + +But it does one other crucial thing at the beginning. This line: + +```clarity +(current-count count) +``` + +Sets a variable that only exists in the context of this particular function. So here we are saying, create a new variable called `current-count` and set it equal to the value of `count`. + +Then we use that in our actual function body down below. + +In the next step we are setting the value of our `count` variable to 1 plus whatever number we passed in. The `+` is just another call to a function where the parameters are the numbers we want to add. + +Then, finally, we are returning the new value of `count` with our `ok` response, indicating that the function completed successfully. + +Then in the very last line we are actually calling this function, passing in 5. + +This was a very brief overview of Clarity just to get your feet wet and give you a taste of how it works. + +If you are interested in diving into the details, we highly recommend going through either the [Clarity Book](https://book.clarity-lang.org/title-page.html) or [Clarity Universe](https://clarity-lang.org/universe), depending on your learning style. + +If you prefer to dive directly into the docs, you can continue on ahead and check out the types, keywords, and functions available in Clarity, as well as a few sample contracts. diff --git a/clarity/decidability.md b/clarity/decidability.md new file mode 100644 index 0000000000..52db783d2c --- /dev/null +++ b/clarity/decidability.md @@ -0,0 +1,167 @@ +# Decidability + +### What does it mean for a language to be Non-Turing Complete or Decidable? + +Non-Turing complete and decidable are two terms you will often hear about the security advantages of Clarity, but what do they mean? + +While related, they are not quite interchangeable, since there are a few differences. + +#### Non-Turing Complete + +A system or language is non-Turing complete if it cannot simulate a Turing machine, which is an abstract model of computation. Non-Turing complete systems have limited computational power compared to Turing complete systems. A Turing-complete system or language can simulate any Turing machine. Examples of non-Turing complete systems include finite state machines and some domain-specific languages (like Clarity). + +Non-Turing complete languages typically cannot express all possible algorithms. Specifically, some problems whose solutions require unbounded loops or recursion cannot be expressed using non-Turing complete languages. This last property is especially important in the context of Clarity, as it makes it so that features like unbounded loops and reentrancy are disallowed at a language level. + +#### Decidable + +A problem is decidable if there exists an algorithm that can always determine whether a given input has a particular property or not in a finite amount of time. In other words, a decidable problem can be solved by a Turing machine that is guaranteed to halt for all input instances. Decidability is a property of problems, whereas Turing completeness is a property of languages or computational systems. + +The fact that Clarity is decidable means that developers (and tooling) can more easily reason about and predict with certainty the behavior of Clarity contracts, regardless of the input. + +### Mindset of a Smart Contract Developer + +Before we dive into specifics, let's first set the context and viewpoint we should hold as smart contract developers who want to write secure code. + +As you explore further into the security properties of Solidity and Clarity, you'll see that there are always mitigation steps that _can_ be taken by developers to help address some of these security issues. + +The main issue, with this line of thinking, is it increases the odds of human error in smart contract security. If we can preserve functionality while mitigating the chance of human error as much as possible, we should do so. + +### Should smart contracts be Turing complete? + +We will discover new applications for smart contracts. These applications will go beyond current smart contracts, traditional contracts, and may even open new economic opportunities. Given these possibilities, how should we build our smart contracts? What characteristics should our smart contract languages have? + +It is good practice to separate data from programs. Should smart contracts be data, or programs, or something in between? If smart contracts are data, then should the programs that execute-them be Turing complete or perhaps less powerful? If smart contracts are programs, then what language should smart contracts be written in? What characteristics should this programming language have? + +The Church-Turing thesis is the hypothesis that all formal notions of computation are captured by Turing machines or modern computers. A programming language is Turing complete if it captures all formal notions of computation. Many programming languages are Turing complete. For example, Python, C++, Rust, Java, Lisp, and Solidity are all Turing complete. + +Consider a program and its input. In the worst case, determining this program’s output is impossible. Validating a program, on a particular input, is done by generating a proof-of-correctness. + +Proofs-of-correctness are logical proofs that can be mechanically validated. Finding proofs-of-correctness for programs and their input is undecidable. Kurt G\”odel showed there are undecidability logical statements. + +This indicates all programs in Turing complete languages cannot be validated in the worst case. Thus, Turing complete smart contract languages must allow contracts that cannot be validated. + +Alonzo Church and Alan Turing showed there are problems that are uncomputable. Uncomputable problems cannot be solved by any Turing machine. Hence, assuming the Church-Turing thesis, these uncomputable problems cannot be solved by any computer. + +We'll explore this idea further later in this section. + +Turing complete languages are very expressive. In fact, assuming the Church-Turing thesis, Turing complete languages are as expressive as possible in some sense. + +Is there a trade-off? What types of problems can occur with uncomputable problems and programs whose validity may be undecidable? + +As smart contracts subsume parts of contract law, consider the large body of laws and regulations for tax law. + +For instance, US tax law and regulations take up several million words. International tax law and regulations pushes these numbers much higher. + +Are these laws and regulations programs or are they data? If tax law were to be written in a Turing complete language, then the law may codify uncomputable problems. It is an accountant’s nightmare for their advice to be undecidable. + +Clarity is non-Turing complete, yet very expressive. This makes it so that Clarity is decidable and cannot encode uncomputable problems. There are discussions and papers on smart contract languages such as Solidity that propose subsets of Solidity that are non-Turing complete. These subsets are decidable and cannot encode uncomputable problems. However, there is no consensus on which subsets to work with and they are not widely used. + +### Advantages of Decidability in Smart Contracts + +Why is decidability important in the context of smart contracts? + +First, it is not possible for a Clarity call to run out of gas in the middle of a call. Because of its decidability, it is possible to get a complete static analysis of the call graph to get an accurate picture of the cost before execution. + +Solidity allows for unbounded loops, recursion, and dynamic function calls, which makes it difficult to accurately predict the execution cost or gas usage beforehand. As a result, Solidity contracts may run out of gas during execution if the gas limit is not set appropriately or if the contract encounters a scenario with unexpectedly high computational requirements. + +One practical example is the issue of a specific kind of DoS attack in Solidity, where the contract is rendered inoperable because of unbounded execution constraints. An example of this is the GovernMental attack, where a mapping that needed to be deleted for a payout became so large that working with it exceeded the block gas limit. + +There are a few different properties of Clarity's language design that prevents such DoS attacks. + +The reason that the analysis system can accurately estimate the execution cost is because certain functionality is intentionally limited in Clarity. + +For example, there is no recursion in Clarity, so we can't infinitely call into a function over and over. + +Data types in Clarity are also restricted. Any data types that don't require a hard length limit are not iterable. + +Maps and tuples, for example, do not require you to enter a maximum length when defining them, but you also can't iterate over them. + +Lists, on the other hand, which are iterable, do require the developer to define an upper limit when defining them. This is a large part of what allows an accurate static analysis of Clarity contracts. + +So how would we implement a mapping of an undefined size in Clarity? We wouldn't, because it's an anti-pattern in smart contract design. + +Instead, Clarity forces us to think of a better solution to our problem. For example, implementing a way for users to handle mapping/list element operations themselves, instead of mass operations handled at the contract level. + +If you [analyze the GovernMental attack](https://hackernoon.com/smart-contract-attacks-part-2-ponzi-games-gone-wrong-d5a8b1a98dd8#h-attack-2-call-stack-attack), you'll see that it took advantage of multiple security issues, all of which are mitigated in Clarity. You'll also see that a fix was added to make it economically infeasible to carry out this type of attack again. + +This brings up another crucial point when setting appropriate mental models for smart contracts and blockchain systems: complexity means more potential bugs, which means adding more complexity to address those bugs. + +When this happens over and over again, we are trapping ourselves into creating an evermore complex system. Addressing these issues at the language level prevents this ever-growing complexity. + +For a deep dive into how Clarity was designed, check out [SIP-002](https://github.com/stacksgov/sips/blob/main/sips/sip-002/sip-002-smart-contract-language.md). + +:::note You can view some more common smart contract vulnerabilities and how they are mitigated in [this article](https://stacks.org/bringing-clarity-to-8-dangerous-smart-contract-vulnerabilities/). ::: + +This has second-order effects as well when we look at security testing and auditing. One of the common tools for testing smart contracts is formal verification, where we mathematically prove that certain properties of smart contracts will or will not remain true in all cases. + +This can lead to the path explosion problem, where there are so many paths available that formal verification becomes incredibly difficult. This problem is mitigated in Clarity, since there is not chance of a program encountering an unbounded loop. + +This leads us to a more general mental model for thinking about decidability as smart contracts continue to become a larger part of our economy. Remember that the goal with blockchain systems is to create an open, transparent, fair financial system. + +This means that smart contracts will be responsible for managing large amounts of wealth for ever-growing amounts of people. As smart contracts encompass more financial structures, their complexity and usage will grow. + +Complexity is the enemy of security. The more complex a system is, the more danger there is in creating uncomputable problems when there are no hard restrictions on the execution steps that can be taken. + +This is deadly in financial infrastructure that is not only open and transparent, but immutable. Let's explore this idea of uncomputability a bit more. + +### Intuition on Uncomputability + +Intuitively, uncomputability is an algorithmic view of undecidability. Uncomputability has the same foundations as undecidability. Undecidable questions are framed as logic statements or statements about integers. Of course, programs are logic statements and may even be viewed as integers, though we view programs differently. We often view programs with additional details of memory models, implementation details, and execution semantics. + +The [Halting problem](https://en.wikipedia.org/wiki/Halting\_problem): As an example, given any program `P` and any finite input `I` for `P`, then the Halting Problem is the challenge of determining if `P` halts on input `I`. + +Alonzo Church and Alan Turing showed the Halting Problem is unsolvable. + +Christopher Strachey gave an intuitive proof-by-contradiction showing the Halting problem is uncomputable. This is set up by supposing there is a program `H` that can solve the Halting problem for any program `P`. `H(P)` returns true if `P` halts and false otherwise. Then build a program `P` that does not halt when `H(P)` is true, giving a contradiction. Similarly, this program `P` halts when `H(P)` is false, also a contradiction. + +Uncomputable problems are problems that cannot be solved by an algorithm or a computer, no matter how much time or resources are provided. These problems exist in various forms, and one such example is the Post correspondence problem, which was proposed by Emil Post. + +The Post correspondence problem can be described using pairs of strings and an integer. Imagine you have n pairs of strings, called P. These strings are made up of characters from a character set, such as UTF-8 or any other alphabet with at least two symbols. The pairs of strings look like this: + +`P = { (x1, y1), (x2, y2), … , (xn, yn) }` + +Now, you also have an integer `m` that is greater than 0. The Post correspondence problem asks whether there is a way to create a list of indices `(i1, i2, …, im)` using the given pairs of strings. You can repeat these indices if needed, with one condition: when you combine the `x` strings from the pairs using the indices, the resulting string must be equal to the combined `y` strings from the same pairs using the same indices. In other words: + +`x(i1) x(i2) … x(im) = y(i1) y(i2) … y(im)` + +When developers try to solve the Post correspondence problem, they often attempt to use indeterminate loops (loops without a fixed number of iterations) rather than recursion. This is because the problem seems to require searching through different combinations of indices until a solution is found or it's proven that no solution exists. + +In simple terms, the Post correspondence problem involves trying to find a sequence of indices that, when applied to the given pairs of strings, produces equal concatenated strings from both the `x` and `y` components. This problem is considered uncomputable because there is no general algorithm that can solve it for all possible input pairs of strings and integers. + +It turns out, many questions about how programs behave are uncomputable. This has a number of consequences for smart contracts that are built in Turing complete languages, many of which we are not aware of yet but will surely become aware of as we encounter them in the future. + +### Raymond Smullyan’s Intuition on Undecidability + +This is a part of Raymond Smullyan’s approach to understanding undecidability in propositional logic. It uses meta-information to show something must be true, though it cannot be proved in propositional logic. This is based on a paradox. + +In propositional logic, a logical statement is undecidable if we cannot prove it true or false. Given a propositional logic statement S, a proof is a sequence of formal logical deductions, starting from basic facts and ending by indicating if S is true or false. + +Smullyan’s starts with an island of Knights and Knaves. Knights always tell the truth. Knaves always lie. We cannot distinguish islanders otherwise. + +There is a great logician named Ray. Whatever Ray proves is true. This is just like a good theorem prover. + +An islander Jack proclaims: “You cannot prove I am a Knight” to the logician Ray. + +The next reasoning is based on meta-knowledge of this situation. This meta-knowledge shows that some problems are undecidable in propositional logic. + +If Ray can prove Jack is a Knight, then Jack must be a Knave, since Jack must have lied. That is because Ray proved Jack is a Knight. Since Jack is a Knave, Ray’s proof contradicts the assumption that Ray only proves true things. So, this case cannot hold. + +If Ray cannot prove Jack is a Knight, then Jack must be a Knight, since Jack stated the truth. But Ray cannot prove the fact that Jack is a Knight. + +In the context of smart contracts and programming languages, Turing complete languages like Solidity come with the possibility of undecidable problems. + +These undecidable problems are similar to the paradox presented in the Knights and Knaves story, where it's impossible to determine whether Jack is a Knight or a Knave based on the given information. + +In the Knights and Knaves story, Ray is analogous to a theorem prover or a smart contract in a Turing complete language. Ray is faced with a statement that is undecidable within the constraints of the system (Knights and Knaves), which leads to a paradox. + +Similarly, a Turing complete smart contract language might face undecidable problems that can't be resolved, leading to unexpected behavior, vulnerabilities, or resource consumption issues (like running out of gas in Ethereum). + +On the other hand, non-Turing complete languages like Clarity are designed to avoid undecidable problems by limiting their expressiveness. + +In the context of the Knights and Knaves story, a non-Turing complete language would simply not allow Jack to make a statement that could lead to a paradox. By disallowing certain features like unbounded loops and recursion, non-Turing complete languages can provide stronger guarantees about the behavior and resource usage of smart contracts. + +This predictability is desirable in many cases, especially when dealing with high-value transactions or critical systems. + +### Reference + +The Mathematics of Various Entertaining Subjects: Research in Recreational Math Illustrated Edition, Jennifer Beineke (Editor), Jason Rosenhouse (Editor), Raymond M. Smullyan (Foreword), Princeton University Press, 2016. diff --git a/clarity/example-contracts/README.md b/clarity/example-contracts/README.md new file mode 100644 index 0000000000..8bfe0241ca --- /dev/null +++ b/clarity/example-contracts/README.md @@ -0,0 +1,2 @@ +# Example Contracts + diff --git a/clarity/example-contracts/audited-starter-contracts.md b/clarity/example-contracts/audited-starter-contracts.md new file mode 100644 index 0000000000..30ae4487bf --- /dev/null +++ b/clarity/example-contracts/audited-starter-contracts.md @@ -0,0 +1,10 @@ +# Audited Starter Contracts + +Here's a list of sample contracts to learn Clarity or to serve as a starting point for your next project. All contracts come from the [Clarity book](https://book.clarity-lang.org/) and have been audited by [Coinfabrik](https://www.coinfabrik.com/). + +* [Counter](https://github.com/clarity-lang/book/tree/main/projects/counter) +* [Multisig Vault](https://github.com/clarity-lang/book/tree/main/projects/multisig-vault) +* [Sip-009 NFT](https://github.com/clarity-lang/book/tree/main/projects/sip009-nft) +* [SIP-010 FT](https://github.com/clarity-lang/book/tree/main/projects/sip010-ft) +* [Timelocked Wallet](https://github.com/clarity-lang/book/tree/main/projects/timelocked-wallet) +* [Tiny Market (NFT marketplace)](https://github.com/clarity-lang/book/tree/main/projects/tiny-market) diff --git a/clarity/example-contracts/bns.md b/clarity/example-contracts/bns.md new file mode 100644 index 0000000000..ffb4a68c85 --- /dev/null +++ b/clarity/example-contracts/bns.md @@ -0,0 +1,510 @@ +# BNS + +The Bitcoin Name System (BNS) is implemented as a smart contract using Clarity. + +Below is a list of public and read-only functions as well as error codes that can be returned by those methods: + +* Public functions +* Read-only functions +* Error codes + +### Public functions + +#### name-import + +**Input: `(buff 20), (buff 48), principal, (buff 20)`** + +**Output: `(response bool int)`** + +**Signature: `(name-import namespace name beneficiary zonefile-hash)`** + +**Description:** + +Imports name to a revealed namespace. Each imported name is given both an owner and some off-chain state. + +#### name-preorder + +**Input: `(buff 20), uint`** + +**Output: `(response uint int)`** + +**Signature: `(name-preorder hashed-salted-fqn stx-to-burn)`** + +**Description:** + +Preorders a name by telling all BNS nodes the salted hash of the BNS name. It pays the registration fee to the namespace owner's designated address. + +#### name-register + +**Input: `(buff 20), (buff 48), (buff 20), (buff 20)`** + +**Output: `(response bool int)`** + +**Signature: `(name-register namespace name salt zonefile-hash)`** + +**Description:** + +Reveals the salt and the name to all BNS nodes, and assigns the name an initial public key hash and zone file hash. + +#### name-renewal + +**Input: `(buff 20), (buff 48), uint, (optional principal), (optional (buff 20))`** + +**Output: `(response bool int)`** + +**Signature: `(name-renewal namespace name stx-to-burn new-owner zonefile-hash)`** + +**Description:** + +Depending in the namespace rules, a name can expire. For example, names in the .id namespace expire after 2 years. You need to send a name renewal every so often to keep your name. + +You will pay the registration cost of your name to the namespace's designated burn address when you renew it. When a name expires, it enters a "grace period". The period is set to 5000 blocks (a month) but can be configured for each namespace. + +It will stop resolving in the grace period, and all of the above operations will cease to be honored by the BNS consensus rules. You may, however, send a NAME\_RENEWAL during this grace period to preserve your name. After the grace period, everybody can register that name again. If your name is in a namespace where names do not expire, then you never need to use this transaction. + +#### name-revoke + +**Input: `(buff 20), (buff 48)`** + +**Output: `(response bool int)`** + +**Signature: `(name-revoke namespace name)`** + +**Description:** + +Makes a name unresolvable. The BNS consensus rules stipulate that once a name is revoked, no one can change its public key hash or its zone file hash. The name's zone file hash is set to null to prevent it from resolving. You should only do this if your private key is compromised, or if you want to render your name unusable for whatever reason. + +#### name-transfer + +**Input: `(buff 20), (buff 48), principal, (optional (buff 20))`** + +**Output: `(response bool int)`** + +**Signature: `(name-transfer namespace name new-owner zonefile-hash)`** + +**Description:** + +Changes the name's public key hash. You would send a name transfer transaction if you wanted to: + +* Change your private key +* Send the name to someone else or +* Update your zone file + +When transferring a name, you have the option to also clear the name's zone file hash (i.e. set it to null). This is useful for when you send the name to someone else, so the recipient's name does not resolve to your zone file. + +#### name-update + +**Input: `(buff 20), (buff 48), (buff 20)`** + +**Output: `(response bool int)`** + +**Signature: `(name-update namespace name zonefile-hash)`** + +**Description:** + +Changes the name's zone file hash. You would send a name update transaction if you wanted to change the name's zone file contents. For example, you would do this if you want to deploy your own Gaia hub and want other people to read from it. + +#### namespace-preorder + +**Input: `(buff 20), uint`** + +**Output: `(response uint int)`** + +**Signature: `(namespace-preorder hashed-salted-namespace stx-to-burn)`** + +**Description:** + +Registers the salted hash of the namespace with BNS nodes, and burns the requisite amount of cryptocurrency. Additionally, this step proves to the BNS nodes that user has honored the BNS consensus rules by including a recent consensus hash in the transaction. Returns pre-order's expiration date (in blocks). + +#### namespace-ready + +**Input: `(buff 20)`** + +**Output: `(response bool int)`** + +**Signature: `(namespace-ready namespace)`** + +**Description:** + +Launches the namespace and makes it available to the public. Once a namespace is launched, anyone can register a name in it if they pay the appropriate amount of cryptocurrency. + +#### namespace-reveal + +**Input: `(buff 20), (buff 20), uint, uint, uint, uint, uint, uint, uint, uint, uint, uint, uint, uint, uint, uint, uint, uint, uint, uint, uint, uint, uint, principal`** + +**Output: `(response bool int)`** + +**Signature: `(namespace-reveal namespace namespace-salt p-func-base p-func-coeff p-func-b1 p-func-b2 p-func-b3 p-func-b4 p-func-b5 p-func-b6 p-func-b7 p-func-b8 p-func-b9 p-func-b10 p-func-b11 p-func-b12 p-func-b13 p-func-b14 p-func-b15 p-func-b16 p-func-non-alpha-discount p-func-no-vowel-discount lifetime namespace-import)`** + +**Description:** + +Reveals the salt and the namespace ID (after a namespace preorder). It reveals how long names last in this namespace before they expire or must be renewed, and it sets a price function for the namespace that determines how cheap or expensive names its will be.All of the parameters prefixed by `p` make up the `price function`. These parameters govern the pricing and lifetime of names in the namespace. + +The rules for a namespace are as follows: + +* a name can fall into one of 16 buckets, measured by length. Bucket 16 incorporates all names at least 16 characters long. +* the pricing structure applies a multiplicative penalty for having numeric characters, or punctuation characters. +* the price of a name in a bucket is `((coeff) * (base) ^ (bucket exponent)) / ((numeric discount multiplier) * (punctuation discount multiplier))` + +Example: + +* base = 10 +* coeff = 2 +* nonalpha discount: 10 +* no-vowel discount: 10 +* buckets 1, 2: 9 +* buckets 3, 4, 5, 6: 8 +* buckets 7, 8, 9, 10, 11, 12, 13, 14: 7 +* buckets 15, 16+: + +### Read-only functions + +#### can-name-be-registered + +**Input: `(buff 20), (buff 48)`** + +**Output: `(response bool int)`** + +**Signature: `(can-name-be-registered namespace name)`** + +**Description:** + +Returns true if the provided name can be registered. + +#### can-namespace-be-registered + +**Input: `(buff 20)`** + +**Output: `(response bool UnknownType)`** + +**Signature: `(can-namespace-be-registered namespace)`** + +**Description:** + +Returns true if the provided namespace is available. + +#### can-receive-name + +**Input: `principal`** + +**Output: `(response bool int)`** + +**Signature: `(can-receive-name owner)`** + +**Description:** + +Returns true if the provided name can be received. That is, if it is not currently owned, a previous lease is expired, and the name wasn't revoked. + +#### get-name-price + +**Input: `(buff 20), (buff 48)`** + +**Output: `(response uint int)`** + +**Signature: `(get-name-price namespace name)`** + +**Description:** + +Gets the price for a name. + +#### get-namespace-price + +**Input: `(buff 20)`** + +**Output: `(response uint int)`** + +**Signature: `(get-namespace-price namespace)`** + +**Description:** + +Gets the price for a namespace. + +#### get-namespace-properties + +**Input: `(buff 20)`** + +**Output: `(response (tuple (namespace (buff 20)) (properties (tuple (can-update-price-function bool) (launched-at (optional uint)) (lifetime uint) (namespace-import principal) (price-function (tuple (base uint) (buckets (list 16 uint)) (coeff uint) (no-vowel-discount uint) (nonalpha-discount uint))) (revealed-at uint)))) int)`** + +**Signature: `(get-namespace-properties namespace)`** + +**Description:** + +Get namespace properties. + +#### is-name-lease-expired + +**Input: `(buff 20), (buff 48)`** + +**Output: `(response bool int)`** + +**Signature: `(is-name-lease-expired namespace name)`** + +**Description:** + +Return true if the provided name lease is expired. + +#### name-resolve + +**Input: `(buff 20), (buff 48)`** + +**Output: `(response (tuple (lease-ending-at (optional uint)) (lease-started-at uint) (owner principal) (zonefile-hash (buff 20))) int)`** + +**Signature: `(name-resolve namespace name)`** + +**Description:** + +Get name registration details. + +#### resolve-principal + +**Input: `principal`** + +**Output: `(response (tuple (name (buff 48)) (namespace (buff 20))) (tuple (code int) (name (optional (tuple (name (buff 48)) (namespace (buff 20)))))))`** + +**Signature: `(resolve-principal owner)`** + +**Description:** + +Returns the registered name that a principal owns if there is one. A principal can only own one name at a time. + +### Error codes + +#### ERR\_INSUFFICIENT\_FUNDS + +**type: `int`** + +**value: `4001`** + +#### ERR\_NAMESPACE\_ALREADY\_EXISTS + +**type: `int`** + +**value: `1006`** + +#### ERR\_NAMESPACE\_ALREADY\_LAUNCHED + +**type: `int`** + +**value: `1014`** + +#### ERR\_NAMESPACE\_BLANK + +**type: `int`** + +**value: `1013`** + +#### ERR\_NAMESPACE\_CHARSET\_INVALID + +**type: `int`** + +**value: `1016`** + +#### ERR\_NAMESPACE\_HASH\_MALFORMED + +**type: `int`** + +**value: `1015`** + +#### ERR\_NAMESPACE\_NOT\_FOUND + +**type: `int`** + +**value: `1005`** + +#### ERR\_NAMESPACE\_NOT\_LAUNCHED + +**type: `int`** + +**value: `1007`** + +#### ERR\_NAMESPACE\_OPERATION\_UNAUTHORIZED + +**type: `int`** + +**value: `1011`** + +#### ERR\_NAMESPACE\_PREORDER\_ALREADY\_EXISTS + +**type: `int`** + +**value: `1003`** + +#### ERR\_NAMESPACE\_PREORDER\_CLAIMABILITY\_EXPIRED + +**type: `int`** + +**value: `1009`** + +#### ERR\_NAMESPACE\_PREORDER\_EXPIRED + +**type: `int`** + +**value: `1002`** + +#### ERR\_NAMESPACE\_PREORDER\_LAUNCHABILITY\_EXPIRED + +**type: `int`** + +**value: `1010`** + +#### ERR\_NAMESPACE\_PREORDER\_NOT\_FOUND + +**type: `int`** + +**value: `1001`** + +#### ERR\_NAMESPACE\_PRICE\_FUNCTION\_INVALID + +**type: `int`** + +**value: `1008`** + +#### ERR\_NAMESPACE\_STX\_BURNT\_INSUFFICIENT + +**type: `int`** + +**value: `1012`** + +#### ERR\_NAMESPACE\_UNAVAILABLE + +**type: `int`** + +**value: `1004`** + +#### ERR\_NAME\_ALREADY\_CLAIMED + +**type: `int`** + +**value: `2011`** + +#### ERR\_NAME\_BLANK + +**type: `int`** + +**value: `2010`** + +#### ERR\_NAME\_CHARSET\_INVALID + +**type: `int`** + +**value: `2022`** + +#### ERR\_NAME\_CLAIMABILITY\_EXPIRED + +**type: `int`** + +**value: `2012`** + +#### ERR\_NAME\_COULD\_NOT\_BE\_MINTED + +**type: `int`** + +**value: `2020`** + +#### ERR\_NAME\_COULD\_NOT\_BE\_TRANSFERRED + +**type: `int`** + +**value: `2021`** + +#### ERR\_NAME\_EXPIRED + +**type: `int`** + +**value: `2008`** + +#### ERR\_NAME\_GRACE\_PERIOD + +**type: `int`** + +**value: `2009`** + +#### ERR\_NAME\_HASH\_MALFORMED + +**type: `int`** + +**value: `2017`** + +#### ERR\_NAME\_NOT\_FOUND + +**type: `int`** + +**value: `2013`** + +#### ERR\_NAME\_NOT\_RESOLVABLE + +**type: `int`** + +**value: `2019`** + +#### ERR\_NAME\_OPERATION\_UNAUTHORIZED + +**type: `int`** + +**value: `2006`** + +#### ERR\_NAME\_PREORDERED\_BEFORE\_NAMESPACE\_LAUNCH + +**type: `int`** + +**value: `2018`** + +#### ERR\_NAME\_PREORDER\_ALREADY\_EXISTS + +**type: `int`** + +**value: `2016`** + +#### ERR\_NAME\_PREORDER\_EXPIRED + +**type: `int`** + +**value: `2002`** + +#### ERR\_NAME\_PREORDER\_FUNDS\_INSUFFICIENT + +**type: `int`** + +**value: `2003`** + +#### ERR\_NAME\_PREORDER\_NOT\_FOUND + +**type: `int`** + +**value: `2001`** + +#### ERR\_NAME\_REVOKED + +**type: `int`** + +**value: `2014`** + +#### ERR\_NAME\_STX\_BURNT\_INSUFFICIENT + +**type: `int`** + +**value: `2007`** + +#### ERR\_NAME\_TRANSFER\_FAILED + +**type: `int`** + +**value: `2015`** + +#### ERR\_NAME\_UNAVAILABLE + +**type: `int`** + +**value: `2004`** + +#### ERR\_PANIC + +**type: `int`** + +**value: `0`** + +#### ERR\_PRINCIPAL\_ALREADY\_ASSOCIATED + +**type: `int`** + +**value: `3001`** diff --git a/clarity/example-contracts/stacking.md b/clarity/example-contracts/stacking.md new file mode 100644 index 0000000000..0825409783 --- /dev/null +++ b/clarity/example-contracts/stacking.md @@ -0,0 +1,170 @@ +# Stacking + +### Introduction + +Stacking rewards Stacks (STX) token holders with bitcoin for providing a valuable service to the network by locking up their tokens for a certain time. + +### Stacking vs Staking + +It is crucial to note that this has no impact on block production or validation. **Stacks is not a Proof-of-Stake network and **_**stacking**_** is different than **_**staking**_. + +There are two primary differences between stacking in Stacks and staking in other PoS networks. + +#### Yield generated in burnchain token + +In staking, users lock one token and earn their yield in the same token. In stacking, users lock one token (STX) and earn a yield in the "burnchain" token (BTC), rather than the same token that was locked. In PoX, the yield comes from a finite, external source (Bitcoin deposits from Stacks miners). In PoS, the yield comes from the currency's issuance schedule itself, which means it is programmatically unlimited (but theoretically limited, we'll get into this a bit more below). + +That's the first main difference. Staking involves a yield of the same token being generated by the issuance mechanism set by the core protocol, where stacking yield requires an input of an external, separate token. + +How are these issuance rates set? In Ethereum, issuance rates are determined by network usage. Ethereum's goal is to create a deflationary money supply, so the issuance rate is determined depending on the usage of the network. In order for an Ethereum transaction to be considered valid, it must include a base fee that is burned during transaction execution. The [issuance rate is algorithmically determined](https://ethereum.org/en/roadmap/merge/issuance/#post-merge) block-by-block depending on how much ETH is being burned by these base fees plus normal gas fees. + +Stacking doesn't have any of this complex functionality, since it does not generate a yield of the same token (and therefore doesn't need to issue new tokens, but rather transfer existing tokens from the base network) and it does not need to maintain an issuance rate. We are speaking here of the yield specifically, Stacks does have an issuance rate and does generate new STX tokens, but this process is completely separate from stacking and the yield generated from it. + +The Bitcoin yield that stackers earn is determined by a combination of the Bitcoin being committed by miners and the number of STX tokens that are locked up in the network. + +#### No effect on transaction validation or consensus mechanism + +The other main difference is that staking involves validators, and is tightly coupled with the operations of the network. + +In both protocols stackers/stakers are earning a yield for providing a service. In PoX, that service is providing economic security by signaling what the canonical Stacks chain is (the chain with the most money locked in it is the real chain) and soon will provide the service of serving as signers for [sBTC](https://stacks.co/sbtc). + +In PoS, the service stakers provide is running a validator node, which is in charge of determining what transactions are or are not valid. So in a PoX system with stacking, the yield generation process is not attached to the validity and operations of the network, whereas in PoS it is. + +In PoS, the entities that are locking up their tokens and earning a yield are the same entities that are in charge of determining what does and does not count as a valid transaction. This and the fact that the yield is in the same token that is locked, have large implications on a PoS chain functioning much like equities in that the entities with the largest stake have the most controlling power over the transactional operations of the chain. + +Here's an [article](https://krgrs.dev/why-bitcoin-is-the-biggest-opportunity-for-developers-in-2023#heading-technology-and-economic-incentives) by Stacks Foundation Developer Advocate Kenny Rogers that goes more in-depth into these economic incentives, with additional resources. + +It is important to note that this control does not extend to being able to change the rules of the blockchain itself, but only to vote transactions as valid or invalid. + +In order to incentivize honest behavior on the part of validators, stake can be slashed as a punishment. Since PoX stackers do not have any say in transaction validity, this punishment mechanism does not exist and is not necessary in PoX. + +Stacking is a built-in action, required by the "proof-of-transfer" (PoX) mechanism. The PoX mechanism is executed by every miner on the Stacks network. + +:::info Stacking functionality is implemented as a smart contract, using Clarity. Read more about the contract. ::: + +### Stacking flow + +The Stacking mechanism can be presented as a flow of actions: + +1. Make API calls to get details about the upcoming reward cycle +2. For a specific Stacks account, confirm eligibility +3. Confirm the BTC reward address and the lockup duration +4. The transaction is broadcasted and the STX tokens are locked. This needs to happen before the prepare phase of the next reward cycle, the last 100 Bitcoin blocks of the ongoing reward phase +5. The Stacking mechanism executes reward cycles and sends out rewards to the set BTC reward address +6. During the lockup period, details about unlocking timing, rewards and more can be obtained +7. Once the lockup period is passed, the tokens are released and accessible again +8. Display reward history, including details like earnings for previous reward cycles + +:::info Keep in mind that the target duration for a reward cycles is \~2 weeks. This duration is based on the target block time of the network (10 minutes) and can be higher at times due to [confirmation time variances](https://www.blockchain.com/charts/median-confirmation-time) of the bitcoin network. ::: + +### Stacking delegation flow + +The Stacking flow is different for delegation use cases: + +* Before Stacking can be initiated for a token holder, the delegator needs to be granted permission to Stack on behalf of the account owner. The permission is restricted to the maximum amount the delegator is allowed to Stack. The maximum amount is not limited by the available funds and can be set much higher. An account can only be associated with one single delegator +* The account has to define the delegation relationship. They can optionally restrict the Bitcoin reward address that must be used for payouts, and the expiration burn block height for the permission, thus limiting the time a delegator has permission to Stack +* Delegators have to lock Stacks from different accounts ("pooling phase") until they reach the minimum amount of Stacks required to participate in Stacking +* Once a delegator locks enough STX tokens, they can finalize and commit their participation in the next reward cycle +* Certain delegation relationships may allow the STX holder to receive the payout directly from the miner (step 5/6) +* The termination of the delegation relationship can either happen automatically based on set expiration rules or by actively revoking delegation rights + +### Token holder eligibility + +Stacks (STX) token holders don't automatically receive stacking rewards. Instead, they must: + +* Commit to participation before a reward cycle begins +* Commit the minimum amount of STX tokens to secure a reward slot, or pool with others to reach the minimum +* Lock up STX tokens for a specified period +* Provide a supported Bitcoin address to receive rewards (native segwit is not supported) + +The following diagram describes how the minimum STX tokens per slot is determined. More information on [dynamic minimums for stacking](https://stacking.club) is available at stacking.club. + +Token holders have a variety of providers and tools to support their participation in Stacking. The Stacks website contains a [list of stacking providers and pools](https://stacks.org/stacking#earn). + +### Stacking in the PoX consensus algorithm + +Stacking is a built-in capability of PoX and occurs through a set of actions on the Stacks blockchain. The [full proof-of-transfer implementation details](https://github.com/stacks-network/stacks-blockchain/blob/develop/sip/sip-007-stacking-consensus.md) are in SIP-007. Below is a summary of the most relevant actions of the algorithm. + +* Stacking happens over reward cycles with a fixed length. In each reward cycle, a set of Bitcoin addresses associated with stacking participants receive BTC rewards +* A reward cycle consists of two phases: prepare and reward +* During the prepare phase, miners decide on an anchor block and a reward set. Mining any descendant forks of the anchor block requires transferring mining funds to the appropriate reward addresses. The reward set is the set of Bitcoin addresses which are eligible to receive funds in the reward cycle +* Miners register as leader candidates for a future election by sending a key transaction that burns cryptocurrency. The transaction also registers the leader's preferred chain tip (must be a descendant of the anchor block) and commitment of funds to 2 addresses from the reward set +* Token holders register for the next rewards cycle by broadcasting a signed message that locks up associated STX tokens for a protocol-specified lockup period, specifies a Bitcoin address to receive the funds, and votes on a Stacks chain tip +* Multiple leaders can commit to the same chain tip. The leader that wins the election and the peers who also burn for that leader collectively share the reward, proportional to how much each one burned +* Token holders' locked up tokens automatically unlock as soon as the lockup period finishes + +### Bitcoin address + +:::danger You must provide a BTC address in one of two formats: + +* [Legacy (P2PKH)](https://en.bitcoin.it/wiki/Transaction#Pay-to-PubkeyHash), which starts with `1`. +* [Segregated Witness / Segwit (P2SH)](https://en.bitcoin.it/wiki/Pay\_to\_script\_hash), which starts with `3`. The "Native Segwit" format (which starts with `bc1`), for example, is not supported. ::: + +The Stacking contract needs a special format for the Bitcoin address (the reward address). This is required to ensure that miners are able to correctly construct the Bitcoin transaction containing the reward address. + +The address must be specified in the following format using the Clarity language: + +```clar +;; a tuple of a version and hashbytes buffer +(pox-addr (tuple (version (buff 1)) (hashbytes (buff 20)))) +``` + +The `version` buffer must represent what kind of bitcoin address is being submitted. It can be one of the following: + +```js +SerializeP2PKH = 0x00, // hash160(public-key), same as bitcoin's p2pkh +SerializeP2SH = 0x01, // hash160(multisig-redeem-script), same as bitcoin's multisig p2sh +SerializeP2WPKH = 0x02, // hash160(segwit-program-00(p2pkh)), same as bitcoin's p2sh-p2wpkh +SerializeP2WSH = 0x03, // hash160(segwit-program-00(public-keys)), same as bitcoin's p2sh-p2wsh +``` + +The `hashbytes` are the 20 hash bytes of the bitcoin address. You can obtain that from a bitcoin library, for instance using [`bitcoinjs-lib`](https://github.com/bitcoinjs/bitcoinjs-lib): + +```js +const btc = require("bitcoinjs-lib"); +console.log( + "0x" + + btc.address + .fromBase58Check("1C56LYirKa3PFXFsvhSESgDy2acEHVAEt6") + .hash.toString("hex") +); +``` + +### Choosing the right Stacking strategy + +[Here](https://blog.stacks.co/stacking-strategy) is an interesting article that may help you choose the right Stacking strategy. + +### Where to Stack? + +You can Stack on your own, on a pool or on an exchange: + +#### Stacking on your own + +Stacking on your own is non-custodial. + +Stacking on your own requires a protocol minimum (amount changes but about 100,000 STX). + +[Hiro Wallet](https://www.hiro.so/wallet) allows stacking on your own. + +#### Stacking on a pool + +Stacking on a pool allows Stacking without the requirement of the protocol minimum. + +Some available pools are: + +| Pool | Type | Pays rewards in | Fee | Minimum amount | +| --------------------------------------------------- | ------------- | :-------------: | --- | :------------: | +| [Friedger's Pool](https://pool.friedger.de/) | Non custodial | STX or xBTC | No | 40 STX | +| [Planbetter](https://planbetter.org/) | Non custodial | BTC | 5% | 200 STX | +| [Stacked](https://staking.staked.us/stacks-staking) | Non custodial | BTC | | 100,000 STX | +| [Xverse](https://www.xverse.app/) | Non custodial | BTC | No | 100 STX | + +#### Stacking on an exchange + +Stacking on an exchange is custodial, meaning you are trusting the exchange with your Stacks. + +Several exchanges allow Stacking directly on their sites. Examples are [Okcoin](https://www.okcoin.com) and [Binance](https://www.binance.com/en/staking) + +### Stacking statistics + +You can view all sorts of Stacking data and statistics on [Stacking Club](https://stacking.club) diff --git a/clarity/functions.md b/clarity/functions.md new file mode 100644 index 0000000000..577a3e7e41 --- /dev/null +++ b/clarity/functions.md @@ -0,0 +1,2591 @@ +# Functions + +#### \* (multiply)​ + +Introduced in: **Clarity 1** + +**input:** `int, ... | uint, ...` + +**output:** `int | uint` + +**signature:** `(* i1 i2...)` + +**description:** + +Multiplies a variable number of integer inputs and returns the result. In the event of an _overflow_, throws a runtime error. + +**example:** + +``` +(* 2 3) ;; Returns 6(* 5 2) ;; Returns 10(* 2 2 2) ;; Returns 8 +``` + +#### + (add)​ + +Introduced in: **Clarity 1** + +**input:** `int, ... | uint, ...` + +**output:** `int | uint` + +**signature:** `(+ i1 i2...)` + +**description:** + +Adds a variable number of integer inputs and returns the result. In the event of an _overflow_, throws a runtime error. + +**example:** + +``` +(+ 1 2 3) ;; Returns 6 +``` + +#### - (subtract)​ + +Introduced in: **Clarity 1** + +**input:** `int, ... | uint, ...` + +**output:** `int | uint` + +**signature:** `(- i1 i2...)` + +**description:** + +Subtracts a variable number of integer inputs and returns the result. In the event of an _underflow_, throws a runtime error. + +**example:** + +``` +(- 2 1 1) ;; Returns 0(- 0 3) ;; Returns -3 +``` + +#### / (divide)​ + +Introduced in: **Clarity 1** + +**input:** `int, ... | uint, ...` + +**output:** `int | uint` + +**signature:** `(/ i1 i2...)` + +**description:** + +Integer divides a variable number of integer inputs and returns the result. In the event of division by zero, throws a runtime error. + +**example:** + +``` +(/ 2 3) ;; Returns 0(/ 5 2) ;; Returns 2(/ 4 2 2) ;; Returns 1 +``` + +#### < (less than)​ + +Introduced in: **Clarity 1** + +**input:** `int, int | uint, uint | string-ascii, string-ascii | string-utf8, string-utf8 | buff, buff` + +**output:** `bool` + +**signature:** `(< i1 i2)` + +**description:** + +Compares two integers, returning `true` if `i1` is less than `i2` and `false` otherwise. i1 and i2 must be of the same type. Starting with Stacks 1.0, the `<`-comparable types are `int` and `uint`. Starting with Stacks 2.1, the `<`-comparable types are expanded to include `string-ascii`, `string-utf8` and `buff`. + +**example:** + +``` +(< 1 2) ;; Returns true(< 5 2) ;; Returns false(< "aaa" "baa") ;; Returns true(< "aa" "aaa") ;; Returns true(< 0x01 0x02) ;; Returns true(< 5 u2) ;; Throws type error +``` + +#### <= (less than or equal)​ + +Introduced in: **Clarity 1** + +**input:** `int, int | uint, uint | string-ascii, string-ascii | string-utf8, string-utf8 | buff, buff` + +**output:** `bool` + +**signature:** `(<= i1 i2)` + +**description:** + +Compares two integers, returning true if `i1` is less than or equal to `i2` and `false` otherwise. i1 and i2 must be of the same type. Starting with Stacks 1.0, the `<=`-comparable types are `int` and `uint`. Starting with Stacks 2.1, the `<=`-comparable types are expanded to include `string-ascii`, `string-utf8` and `buff`. + +**example:** + +``` +(<= 1 1) ;; Returns true(<= 5 2) ;; Returns false(<= "aaa" "baa") ;; Returns true(<= "aa" "aaa") ;; Returns true(<= 0x01 0x02) ;; Returns true(<= 5 u2) ;; Throws type error +``` + +#### > (greater than)​ + +Introduced in: **Clarity 1** + +**input:** `int, int | uint, uint | string-ascii, string-ascii | string-utf8, string-utf8 | buff, buff` + +**output:** `bool` + +**signature:** `(> i1 i2)` + +**description:** + +Compares two integers, returning `true` if `i1` is greater than `i2` and false otherwise. i1 and i2 must be of the same type. Starting with Stacks 1.0, the `>`-comparable types are `int` and `uint`. Starting with Stacks 2.1, the `>`-comparable types are expanded to include `string-ascii`, `string-utf8` and `buff`. + +**example:** + +``` +(> 1 2) ;; Returns false(> 5 2) ;; Returns true(> "baa" "aaa") ;; Returns true(> "aaa" "aa") ;; Returns true(> 0x02 0x01) ;; Returns true(> 5 u2) ;; Throws type error +``` + +#### >= (greater than or equal)​ + +Introduced in: **Clarity 1** + +**input:** `int, int | uint, uint | string-ascii, string-ascii | string-utf8, string-utf8 | buff, buff` + +**output:** `bool` + +**signature:** `(>= i1 i2)` + +**description:** + +Compares two integers, returning `true` if `i1` is greater than or equal to `i2` and `false` otherwise. i1 and i2 must be of the same type. Starting with Stacks 1.0, the `>=`-comparable types are `int` and `uint`. Starting with Stacks 2.1, the `>=`-comparable types are expanded to include `string-ascii`, `string-utf8` and `buff`. + +**example:** + +``` +(>= 1 1) ;; Returns true(>= 5 2) ;; Returns true(>= "baa" "aaa") ;; Returns true(>= "aaa" "aa") ;; Returns true(>= 0x02 0x01) ;; Returns true(>= 5 u2) ;; Throws type error +``` + +#### and​ + +Introduced in: **Clarity 1** + +**input:** `bool, ...` + +**output:** `bool` + +**signature:** `(and b1 b2 ...)` + +**description:** + +Returns `true` if all boolean inputs are `true`. Importantly, the supplied arguments are evaluated in-order and lazily. Lazy evaluation means that if one of the arguments returns `false`, the function short-circuits, and no subsequent arguments are evaluated. + +**example:** + +``` +(and true false) ;; Returns false(and (is-eq (+ 1 2) 1) (is-eq 4 4)) ;; Returns false(and (is-eq (+ 1 2) 3) (is-eq 4 4)) ;; Returns true +``` + +#### append​ + +Introduced in: **Clarity 1** + +**input:** `list A, A` + +**output:** `list` + +**signature:** `(append (list 1 2 3 4) 5)` + +**description:** + +The `append` function takes a list and another value with the same entry type, and outputs a list of the same type with max\_len += 1. + +**example:** + +``` +(append (list 1 2 3 4) 5) ;; Returns (1 2 3 4 5) +``` + +#### as-contract​ + +Introduced in: **Clarity 1** + +**input:** `A` + +**output:** `A` + +**signature:** `(as-contract expr)` + +**description:** + +The `as-contract` function switches the current context's `tx-sender` value to the _contract's_ principal and executes `expr` with that context. It returns the resulting value of `expr`. + +**example:** + +``` +(as-contract tx-sender) ;; Returns S1G2081040G2081040G2081040G208105NK8PE5.docs-test +``` + +#### as-max-len?​ + +Introduced in: **Clarity 1** + +**input:** `sequence_A, uint` + +**output:** `(optional sequence_A)` + +**signature:** `(as-max-len? sequence max_length)` + +**description:** + +The `as-max-len?` function takes a sequence argument and a uint-valued, literal length argument. The function returns an optional type. If the input sequence length is less than or equal to the supplied max\_length, this returns `(some sequence)`, otherwise it returns `none`. Applicable sequence types are `(list A)`, `buff`, `string-ascii` and `string-utf8`. + +**example:** + +``` +(as-max-len? (list 2 2 2) u3) ;; Returns (some (2 2 2))(as-max-len? (list 1 2 3) u2) ;; Returns none(as-max-len? "hello" u10) ;; Returns (some "hello")(as-max-len? 0x010203 u10) ;; Returns (some 0x010203) +``` + +#### asserts + +Introduced in: **Clarity 1** + +**input:** `bool, C` + +**output:** `bool` + +**signature:** `(asserts! bool-expr thrown-value)` + +**description:** + +The `asserts!` function admits a boolean argument and asserts its evaluation: if bool-expr is `true`, `asserts!` returns `true` and proceeds in the program execution. If the supplied argument is returning a false value, `asserts!` _returns_ `thrown-value` and exits the current control-flow. + +**example:** + +``` +(asserts! (is-eq 1 1) (err 1)) ;; Returns true +``` + +#### at-block​ + +Introduced in: **Clarity 1** + +**input:** `(buff 32), A` + +**output:** `A` + +**signature:** `(at-block id-block-hash expr)` + +**description:** + +The `at-block` function evaluates the expression `expr` _as if_ it were evaluated at the end of the block indicated by the _block-hash_ argument. The `expr` closure must be read-only. + +Note: The block identifying hash must be a hash returned by the `id-header-hash` block information property. This hash uniquely identifies Stacks blocks and is unique across Stacks forks. While the hash returned by `header-hash` is unique within the context of a single fork, it is not unique across Stacks forks. + +The function returns the result of evaluating `expr`. + +**example:** + +``` +(define-data-var data int 1)(at-block 0x0000000000000000000000000000000000000000000000000000000000000000 block-height) ;; Returns u0(at-block (get-block-info? id-header-hash 0) (var-get data)) ;; Throws NoSuchDataVariable because `data` wasn't initialized at block height 0 +``` + +#### begin​ + +Introduced in: **Clarity 1** + +**input:** `AnyType, ... A` + +**output:** `A` + +**signature:** `(begin expr1 expr2 expr3 ... expr-last)` + +**description:** + +The `begin` function evaluates each of its input expressions, returning the return value of the last such expression. Note: intermediary statements returning a response type must be checked. + +**example:** + +``` +(begin (+ 1 2) 4 5) ;; Returns 5 +``` + +#### bit-and​ + +Introduced in: **Clarity 2** + +**input:** `int, ... | uint, ...` + +**output:** `int | uint` + +**signature:** `(bit-and i1 i2...)` + +**description:** + +Returns the result of bitwise and'ing a variable number of integer inputs. + +**example:** + +``` +(bit-and 24 16) ;; Returns 16(bit-and 28 24 -1) ;; Returns 24(bit-and u24 u16) ;; Returns u16(bit-and -128 -64) ;; Returns -128(bit-and 28 24 -1) ;; Returns 24 +``` + +#### bit-not​ + +Introduced in: **Clarity 2** + +**input:** `int | uint` + +**output:** `int | uint` + +**signature:** `(bit-not i1)` + +**description:** + +Returns the one's compliement (sometimes also called the bitwise compliment or not operator) of `i1`, effectively reversing the bits in `i1`. In other words, every bit that is `1` in ì1`will be`0`in the result. Conversely, every bit that is`0`in`i1`will be`1\` in the result. + +**example:** + +``` +(bit-not 3) ;; Returns -4(bit-not u128) ;; Returns u340282366920938463463374607431768211327(bit-not 128) ;; Returns -129(bit-not -128) ;; Returns 127 +``` + +#### bit-or​ + +Introduced in: **Clarity 2** + +**input:** `int, ... | uint, ...` + +**output:** `int | uint` + +**signature:** `(bit-or i1 i2...)` + +**description:** + +Returns the result of bitwise inclusive or'ing a variable number of integer inputs. + +**example:** + +``` +(bit-or 4 8) ;; Returns 12(bit-or 1 2 4) ;; Returns 7(bit-or 64 -32 -16) ;; Returns -16(bit-or u2 u4 u32) ;; Returns u38 +``` + +#### bit-shift-left​ + +Introduced in: **Clarity 2** + +**input:** `int, uint | uint, uint` + +**output:** `int | uint` + +**signature:** `(bit-shift-left i1 shamt)` + +**description:** + +Shifts all the bits in `i1` to the left by the number of places specified in `shamt` modulo 128 (the bit width of Clarity integers). + +Note that there is a deliberate choice made to ignore arithmetic overflow for this operation. In use cases where overflow should be detected, developers should use `*`, `/`, and `pow` instead of the shift operators. + +**example:** + +``` +(bit-shift-left 2 u1) ;; Returns 4(bit-shift-left 16 u2) ;; Returns 64(bit-shift-left -64 u1) ;; Returns -128(bit-shift-left u4 u2) ;; Returns u16(bit-shift-left 123 u9999999999) ;; Returns -170141183460469231731687303715884105728(bit-shift-left u123 u9999999999) ;; Returns u170141183460469231731687303715884105728(bit-shift-left -1 u7) ;; Returns -128(bit-shift-left -1 u128) ;; Returns -1 +``` + +#### bit-shift-right​ + +Introduced in: **Clarity 2** + +**input:** `int, uint | uint, uint` + +**output:** `int | uint` + +**signature:** `(bit-shift-right i1 shamt)` + +**description:** + +Shifts all the bits in `i1` to the right by the number of places specified in `shamt` modulo 128 (the bit width of Clarity integers). When `i1` is a `uint` (unsigned), new bits are filled with zeros. When `i1` is an `int` (signed), the sign is preserved, meaning that new bits are filled with the value of the previous sign-bit. + +Note that there is a deliberate choice made to ignore arithmetic overflow for this operation. In use cases where overflow should be detected, developers should use `*`, `/`, and `pow` instead of the shift operators. + +**example:** + +``` +(bit-shift-right 2 u1) ;; Returns 1(bit-shift-right 128 u2) ;; Returns 32(bit-shift-right -64 u1) ;; Returns -32(bit-shift-right u128 u2) ;; Returns u32(bit-shift-right 123 u9999999999) ;; Returns 0(bit-shift-right u123 u9999999999) ;; Returns u0(bit-shift-right -128 u7) ;; Returns -1(bit-shift-right -256 u1) ;; Returns -128(bit-shift-right 5 u2) ;; Returns 1(bit-shift-right -5 u2) ;; Returns -2 +``` + +#### bit-xor​ + +Introduced in: **Clarity 2** + +**input:** `int, ... | uint, ...` + +**output:** `int | uint` + +**signature:** `(bit-xor i1 i2...)` + +**description:** + +Returns the result of bitwise exclusive or'ing a variable number of integer inputs. + +**example:** + +``` +(bit-xor 1 2) ;; Returns 3(bit-xor 120 280) ;; Returns 352(bit-xor -128 64) ;; Returns -64(bit-xor u24 u4) ;; Returns u28(bit-xor 1 2 4 -1) ;; Returns -8 +``` + +#### buff-to-int-be​ + +Introduced in: **Clarity 2** + +**input:** `(buff 16)` + +**output:** `int` + +**signature:** `(buff-to-int-be (buff 16))` + +**description:** + +Converts a byte buffer to a signed integer use a big-endian encoding. The byte buffer can be up to 16 bytes in length. If there are fewer than 16 bytes, as this function uses a big-endian encoding, the input behaves as if it is zero-padded on the _left_. + +Note: This function is only available starting with Stacks 2.1. + +**example:** + +``` +(buff-to-int-be 0x01) ;; Returns 1(buff-to-int-be 0x00000000000000000000000000000001) ;; Returns 1(buff-to-int-be 0xffffffffffffffffffffffffffffffff) ;; Returns -1(buff-to-int-be 0x) ;; Returns 0 +``` + +#### buff-to-int-le​ + +Introduced in: **Clarity 2** + +**input:** `(buff 16)` + +**output:** `int` + +**signature:** `(buff-to-int-le (buff 16))` + +**description:** + +Converts a byte buffer to a signed integer use a little-endian encoding. The byte buffer can be up to 16 bytes in length. If there are fewer than 16 bytes, as this function uses a little-endian encoding, the input behaves as if it is zero-padded on the _right_. + +Note: This function is only available starting with Stacks 2.1. + +**example:** + +``` +(buff-to-int-le 0x01) ;; Returns 1(buff-to-int-le 0x01000000000000000000000000000000) ;; Returns 1(buff-to-int-le 0xffffffffffffffffffffffffffffffff) ;; Returns -1(buff-to-int-le 0x) ;; Returns 0 +``` + +#### buff-to-uint-be​ + +Introduced in: **Clarity 2** + +**input:** `(buff 16)` + +**output:** `uint` + +**signature:** `(buff-to-uint-be (buff 16))` + +**description:** + +Converts a byte buffer to an unsigned integer use a big-endian encoding. The byte buffer can be up to 16 bytes in length. If there are fewer than 16 bytes, as this function uses a big-endian encoding, the input behaves as if it is zero-padded on the _left_. + +Note: This function is only available starting with Stacks 2.1. + +**example:** + +``` +(buff-to-uint-be 0x01) ;; Returns u1(buff-to-uint-be 0x00000000000000000000000000000001) ;; Returns u1(buff-to-uint-be 0xffffffffffffffffffffffffffffffff) ;; Returns u340282366920938463463374607431768211455(buff-to-uint-be 0x) ;; Returns u0 +``` + +#### buff-to-uint-le​ + +Introduced in: **Clarity 2** + +**input:** `(buff 16)` + +**output:** `uint` + +**signature:** `(buff-to-uint-le (buff 16))` + +**description:** + +Converts a byte buffer to an unsigned integer use a little-endian encoding.. The byte buffer can be up to 16 bytes in length. If there are fewer than 16 bytes, as this function uses a little-endian encoding, the input behaves as if it is zero-padded on the _right_. + +Note: This function is only available starting with Stacks 2.1. + +**example:** + +``` +(buff-to-uint-le 0x01) ;; Returns u1(buff-to-uint-le 0x01000000000000000000000000000000) ;; Returns u1(buff-to-uint-le 0xffffffffffffffffffffffffffffffff) ;; Returns u340282366920938463463374607431768211455(buff-to-uint-le 0x) ;; Returns u0 +``` + +#### concat​ + +Introduced in: **Clarity 1** + +**input:** `sequence_A, sequence_A` + +**output:** `sequence_A` + +**signature:** `(concat sequence1 sequence2)` + +**description:** + +The `concat` function takes two sequences of the same type, and returns a concatenated sequence of the same type, with the resulting sequence\_len = sequence1\_len + sequence2\_len. Applicable sequence types are `(list A)`, `buff`, `string-ascii` and `string-utf8`. + +**example:** + +``` +(concat (list 1 2) (list 3 4)) ;; Returns (1 2 3 4)(concat "hello " "world") ;; Returns "hello world"(concat 0x0102 0x0304) ;; Returns 0x01020304 +``` + +#### contract-call?​ + +Introduced in: **Clarity 1** + +**input:** `ContractName, PublicFunctionName, Arg0, ...` + +**output:** `(response A B)` + +**signature:** `(contract-call? .contract-name function-name arg0 arg1 ...)` + +**description:** + +The `contract-call?` function executes the given public function of the given contract. You _may not_ use this function to call a public function defined in the current contract. If the public function returns _err_, any database changes resulting from calling `contract-call?` are aborted. If the function returns _ok_, database changes occurred. + +**example:** + +``` +;; instantiate the sample-contracts/tokens.clar contract first!(as-contract (contract-call? .tokens mint! u19)) ;; Returns (ok u19) +``` + +#### contract-of​ + +Introduced in: **Clarity 1** + +**input:** `Trait` + +**output:** `principal` + +**signature:** `(contract-of .contract-name)` + +**description:** + +The `contract-of` function returns the principal of the contract implementing the trait. + +**example:** + +``` +(use-trait token-a-trait 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF.token-a.token-trait)(define-public (forward-get-balance (user principal) (contract )) (begin (ok (contract-of contract)))) ;; returns the principal of the contract implementing +``` + +#### default-to​ + +Introduced in: **Clarity 1** + +**input:** `A, (optional A)` + +**output:** `A` + +**signature:** `(default-to default-value option-value)` + +**description:** + +The `default-to` function attempts to 'unpack' the second argument: if the argument is a `(some ...)` option, it returns the inner value of the option. If the second argument is a `(none)` value, `default-to` it returns the value of `default-value`. + +**example:** + +``` +(define-map names-map { name: (string-ascii 12) } { id: int })(map-set names-map { name: "blockstack" } { id: 1337 })(default-to 0 (get id (map-get? names-map (tuple (name "blockstack"))))) ;; Returns 1337(default-to 0 (get id (map-get? names-map (tuple (name "non-existant"))))) ;; Returns 0 +``` + +#### define-constant​ + +Introduced in: **Clarity 1** + +**input:** `MethodSignature, MethodBody` + +**output:** `Not Applicable` + +**signature:** `(define-constant name expression)` + +**description:** + +`define-constant` is used to define a private constant value in a smart contract. The expression passed into the definition is evaluated at contract launch, in the order that it is supplied in the contract. This can lead to undefined function or undefined variable errors in the event that a function or variable used in the expression has not been defined before the constant. + +Like other kinds of definition statements, `define-constant` may only be used at the top level of a smart contract definition (i.e., you cannot put a define statement in the middle of a function body). + +**example:** + +``` +(define-constant four (+ 2 2))(+ 4 four) ;; Returns 8 +``` + +#### define-data-var​ + +Introduced in: **Clarity 1** + +**input:** `VarName, TypeDefinition, Value` + +**output:** `Not Applicable` + +**signature:** `(define-data-var var-name type value)` + +**description:** + +`define-data-var` is used to define a new persisted variable for use in a smart contract. Such variable are only modifiable by the current smart contract. + +Persisted variable are defined with a type and a value. + +Like other kinds of definition statements, `define-data-var` may only be used at the top level of a smart contract definition (i.e., you cannot put a define statement in the middle of a function body). + +**example:** + +``` +(define-data-var size int 0)(define-private (set-size (value int)) (var-set size value))(set-size 1)(set-size 2) +``` + +#### define-fungible-token​ + +Introduced in: **Clarity 1** + +**input:** `TokenName, ` + +**output:** `Not Applicable` + +**signature:** `(define-fungible-token token-name )` + +**description:** + +`define-fungible-token` is used to define a new fungible token class for use in the current contract. + +The second argument, if supplied, defines the total supply of the fungible token. This ensures that all calls to the `ft-mint?` function will never be able to create more than `total-supply` tokens. If any such call were to increase the total supply of tokens passed that amount, that invocation of `ft-mint?` will result in a runtime error and abort. + +Like other kinds of definition statements, `define-fungible-token` may only be used at the top level of a smart contract definition (i.e., you cannot put a define statement in the middle of a function body). + +Tokens defined using `define-fungible-token` may be used in `ft-transfer?`, `ft-mint?`, and `ft-get-balance` functions + +**example:** + +``` +(define-fungible-token stacks)(define-fungible-token limited-supply-stacks u100) +``` + +#### define-map​ + +Introduced in: **Clarity 1** + +**input:** `MapName, TypeDefinition, TypeDefinition` + +**output:** `Not Applicable` + +**signature:** `(define-map map-name key-type value-type)` + +**description:** + +`define-map` is used to define a new datamap for use in a smart contract. Such maps are only modifiable by the current smart contract. + +Maps are defined with a key type and value type, often these types are tuple types. + +Like other kinds of definition statements, `define-map` may only be used at the top level of a smart contract definition (i.e., you cannot put a define statement in the middle of a function body). + +**example:** + +``` +(define-map squares { x: int } { square: int })(define-private (add-entry (x int)) (map-insert squares { x: 2 } { square: (* x x) }))(add-entry 1)(add-entry 2)(add-entry 3)(add-entry 4)(add-entry 5) +``` + +#### define-non-fungible-token​ + +Introduced in: **Clarity 1** + +**input:** `AssetName, TypeSignature` + +**output:** `Not Applicable` + +**signature:** `(define-non-fungible-token asset-name asset-identifier-type)` + +**description:** + +`define-non-fungible-token` is used to define a new non-fungible token class for use in the current contract. Individual assets are identified by their asset identifier, which must be of the type `asset-identifier-type`. Asset identifiers are _unique_ identifiers. + +Like other kinds of definition statements, `define-non-fungible-token` may only be used at the top level of a smart contract definition (i.e., you cannot put a define statement in the middle of a function body). + +Assets defined using `define-non-fungible-token` may be used in `nft-transfer?`, `nft-mint?`, and `nft-get-owner?` functions + +**example:** + +``` +(define-non-fungible-token names (buff 50)) +``` + +#### define-private​ + +Introduced in: **Clarity 1** + +**input:** `MethodSignature, MethodBody` + +**output:** `Not Applicable` + +**signature:** `(define-private (function-name (arg-name-0 arg-type-0) (arg-name-1 arg-type-1) ...) function-body)` + +**description:** + +`define-private` is used to define _private_ functions for a smart contract. Private functions may not be called from other smart contracts, nor may they be invoked directly by users. Instead, these functions may only be invoked by other functions defined in the same smart contract. + +Like other kinds of definition statements, `define-private` may only be used at the top level of a smart contract definition (i.e., you cannot put a define statement in the middle of a function body). + +Private functions may return any type. + +**example:** + +``` +(define-private (max-of (i1 int) (i2 int)) (if (> i1 i2) i1 i2))(max-of 4 6) ;; Returns 6 +``` + +#### define-public​ + +Introduced in: **Clarity 1** + +**input:** `MethodSignature, MethodBody` + +**output:** `Not Applicable` + +**signature:** `(define-public (function-name (arg-name-0 arg-type-0) (arg-name-1 arg-type-1) ...) function-body)` + +**description:** + +`define-public` is used to define a _public_ function and transaction for a smart contract. Public functions are callable from other smart contracts and may be invoked directly by users by submitting a transaction to the Stacks blockchain. + +Like other kinds of definition statements, `define-public` may only be used at the top level of a smart contract definition (i.e., you cannot put a define statement in the middle of a function body). + +Public functions _must_ return a ResponseType (using either `ok` or `err`). Any datamap modifications performed by a public function is aborted if the function returns an `err` type. Public functions may be invoked by other contracts via `contract-call?`. + +**example:** + +``` +(define-public (hello-world (input int)) (begin (print (+ 2 input)) (ok input))) +``` + +#### define-read-only​ + +Introduced in: **Clarity 1** + +**input:** `MethodSignature, MethodBody` + +**output:** `Not Applicable` + +**signature:** `(define-read-only (function-name (arg-name-0 arg-type-0) (arg-name-1 arg-type-1) ...) function-body)` + +**description:** + +`define-read-only` is used to define a _public read-only_ function for a smart contract. Such functions are callable from other smart contracts. + +Like other kinds of definition statements, `define-read-only` may only be used at the top level of a smart contract definition (i.e., you cannot put a define statement in the middle of a function body). + +Read-only functions may return any type. However, read-only functions may not perform any datamap modifications, or call any functions which perform such modifications. This is enforced both during type checks and during the execution of the function. Public read-only functions may be invoked by other contracts via `contract-call?`. + +**example:** + +``` +(define-read-only (just-return-one-hundred) (* 10 10)) +``` + +#### define-trait​ + +Introduced in: **Clarity 1** + +**input:** `VarName, [MethodSignature]` + +**output:** `Not Applicable` + +**signature:** `(define-trait trait-name ((func1-name (arg1-type arg2-type ...) (return-type))))` + +**description:** + +`define-trait` is used to define a new trait definition for use in a smart contract. Other contracts can implement a given trait and then have their contract identifier being passed as a function argument in order to be called dynamically with `contract-call?`. + +Traits are defined with a name, and a list functions, defined with a name, a list of argument types, and return type. + +In Clarity 1, a trait type can be used to specify the type of a function parameter. A parameter with a trait type can be used as the target of a dynamic `contract-call?`. A principal literal (e.g. `ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.foo`) may be passed as a trait parameter if the specified contract implements all of the functions specified by the trait. A trait value (originating from a parameter with trait type) may also be passed as a trait parameter if the types are the same. + +Beginning in Clarity 2, a trait can be used in all of the same ways that a built-in type can be used, except that it cannot be stored in a data var or map, since this would inhibit static analysis. This means that a trait type can be embedded in a compound type (e.g. `(optional )` or `(list 4 )`) and a trait value can be bound to a variable in a `let` or `match` expression. In addition to the principal literal and trait value with matching type allowed in Clarity 1, Clarity 2 also supports implicit casting from a compatible trait, meaning that a value of type `trait-a` may be passed to a parameter with type `trait-b` if `trait-a` includes all of the requirements of `trait-b` (and optionally additional functions). + +Like other kinds of definition statements, `define-trait` may only be used at the top level of a smart contract definition (i.e., you cannot put a define statement in the middle of a function body). + +**example:** + +``` +(define-trait token-trait ((transfer? (principal principal uint) (response uint uint)) (get-balance (principal) (response uint uint)))) +``` + +#### element-at​ + +Introduced in: **Clarity 1** + +**input:** `sequence_A, uint` + +**output:** `(optional A)` + +**signature:** `(element-at? sequence index)` + +**description:** + +The `element-at?` function returns the element at `index` in the provided sequence. Applicable sequence types are `(list A)`, `buff`, `string-ascii` and `string-utf8`, for which the corresponding element types are, respectively, `A`, `(buff 1)`, `(string-ascii 1)` and `(string-utf8 1)`. In Clarity1, `element-at` must be used (without the `?`). The `?` is added in Clarity2 for consistency -- built-ins that return responses or optionals end in `?`. The Clarity1 spelling is left as an alias in Clarity2 for backwards compatibility. + +**example:** + +``` +(element-at? "blockstack" u5) ;; Returns (some "s")(element-at? (list 1 2 3 4 5) u5) ;; Returns none(element-at? (list 1 2 3 4 5) (+ u1 u2)) ;; Returns (some 4)(element-at? "abcd" u1) ;; Returns (some "b")(element-at? 0xfb01 u1) ;; Returns (some 0x01) +``` + +#### element-at?​ + +Introduced in: **Clarity 2** + +**input:** `sequence_A, uint` + +**output:** `(optional A)` + +**signature:** `(element-at? sequence index)` + +**description:** + +The `element-at?` function returns the element at `index` in the provided sequence. Applicable sequence types are `(list A)`, `buff`, `string-ascii` and `string-utf8`, for which the corresponding element types are, respectively, `A`, `(buff 1)`, `(string-ascii 1)` and `(string-utf8 1)`. In Clarity1, `element-at` must be used (without the `?`). The `?` is added in Clarity2 for consistency -- built-ins that return responses or optionals end in `?`. The Clarity1 spelling is left as an alias in Clarity2 for backwards compatibility. + +**example:** + +``` +(element-at? "blockstack" u5) ;; Returns (some "s")(element-at? (list 1 2 3 4 5) u5) ;; Returns none(element-at? (list 1 2 3 4 5) (+ u1 u2)) ;; Returns (some 4)(element-at? "abcd" u1) ;; Returns (some "b")(element-at? 0xfb01 u1) ;; Returns (some 0x01) +``` + +#### err​ + +Introduced in: **Clarity 1** + +**input:** `A` + +**output:** `(response A B)` + +**signature:** `(err value)` + +**description:** + +The `err` function constructs a response type from the input value. Use `err` for creating return values in public functions. An _err_ value indicates that any database changes during the processing of the function should be rolled back. + +**example:** + +``` +(err true) ;; Returns (err true) +``` + +#### filter​ + +Introduced in: **Clarity 1** + +**input:** `Function(A) -> bool, sequence_A` + +**output:** `sequence_A` + +**signature:** `(filter func sequence)` + +**description:** + +The `filter` function applies the input function `func` to each element of the input sequence, and returns the same sequence with any elements removed for which `func` returned `false`. Applicable sequence types are `(list A)`, `buff`, `string-ascii` and `string-utf8`, for which the corresponding element types are, respectively, `A`, `(buff 1)`, `(string-ascii 1)` and `(string-utf8 1)`. The `func` argument must be a literal function name. + +**example:** + +``` +(filter not (list true false true false)) ;; Returns (false false)(define-private (is-a (char (string-utf8 1))) (is-eq char u"a"))(filter is-a u"acabd") ;; Returns u"aa"(define-private (is-zero (char (buff 1))) (is-eq char 0x00))(filter is-zero 0x00010002) ;; Returns 0x0000 +``` + +#### fold​ + +Introduced in: **Clarity 1** + +**input:** `Function(A, B) -> B, sequence_A, B` + +**output:** `B` + +**signature:** `(fold func sequence_A initial_B)` + +**description:** + +The `fold` function condenses `sequence_A` into a value of type `B` by recursively applies the function `func` to each element of the input sequence _and_ the output of a previous application of `func`. + +`fold` uses `initial_B` in the initial application of `func`, along with the first element of `sequence_A`. The resulting value of type `B` is used for the next application of `func`, along with the next element of `sequence_A` and so on. `fold` returns the last value of type `B` returned by these successive applications `func`. + +Applicable sequence types are `(list A)`, `buff`, `string-ascii` and `string-utf8`, for which the corresponding element types are, respectively, `A`, `(buff 1)`, `(string-ascii 1)` and `(string-utf8 1)`. The `func` argument must be a literal function name. + +**example:** + +``` +(fold * (list 2 2 2) 1) ;; Returns 8(fold * (list 2 2 2) 0) ;; Returns 0;; calculates (- 11 (- 7 (- 3 2)))(fold - (list 3 7 11) 2) ;; Returns 5 (define-private (concat-string (a (string-ascii 20)) (b (string-ascii 20))) (unwrap-panic (as-max-len? (concat a b) u20)))(fold concat-string "cdef" "ab") ;; Returns "fedcab"(fold concat-string (list "cd" "ef") "ab") ;; Returns "efcdab"(define-private (concat-buff (a (buff 20)) (b (buff 20))) (unwrap-panic (as-max-len? (concat a b) u20)))(fold concat-buff 0x03040506 0x0102) ;; Returns 0x060504030102 +``` + +#### from-consensus-buff?​ + +Introduced in: **Clarity 2** + +**input:** `type-signature(t), buff` + +**output:** `(optional t)` + +**signature:** `(from-consensus-buff? type-signature buffer)` + +**description:** + +`from-consensus-buff?` is a special function that will deserialize a buffer into a Clarity value, using the SIP-005 serialization of the Clarity value. The type that `from-consensus-buff?` tries to deserialize into is provided by the first parameter to the function. If it fails to deserialize the type, the method returns `none`. + +**example:** + +``` +(from-consensus-buff? int 0x0000000000000000000000000000000001) ;; Returns (some 1)(from-consensus-buff? uint 0x0000000000000000000000000000000001) ;; Returns none(from-consensus-buff? uint 0x0100000000000000000000000000000001) ;; Returns (some u1)(from-consensus-buff? bool 0x0000000000000000000000000000000001) ;; Returns none(from-consensus-buff? bool 0x03) ;; Returns (some true)(from-consensus-buff? bool 0x04) ;; Returns (some false)(from-consensus-buff? principal 0x051fa46ff88886c2ef9762d970b4d2c63678835bd39d) ;; Returns (some SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR)(from-consensus-buff? { abc: int, def: int } 0x0c00000002036162630000000000000000000000000000000003036465660000000000000000000000000000000004) ;; Returns (some (tuple (abc 3) (def 4))) +``` + +#### ft-burn?​ + +Introduced in: **Clarity 1** + +**input:** `TokenName, uint, principal` + +**output:** `(response bool uint)` + +**signature:** `(ft-burn? token-name amount sender)` + +**description:** + +`ft-burn?` is used to decrease the token balance for the `sender` principal for a token type defined using `define-fungible-token`. The decreased token balance is _not_ transfered to another principal, but rather destroyed, reducing the circulating supply. + +On a successful burn, it returns `(ok true)`. In the event of an unsuccessful burn it returns one of the following error codes: + +`(err u1)` -- `sender` does not have enough balance to burn this amount or the amount specified is not positive + +**example:** + +``` +(define-fungible-token stackaroo)(ft-mint? stackaroo u100 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF) ;; Returns (ok true)(ft-burn? stackaroo u50 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF) ;; Returns (ok true) +``` + +#### ft-get-balance​ + +Introduced in: **Clarity 1** + +**input:** `TokenName, principal` + +**output:** `uint` + +**signature:** `(ft-get-balance token-name principal)` + +**description:** + +`ft-get-balance` returns `token-name` balance of the principal `principal`. The token type must have been defined using `define-fungible-token`. + +**example:** + +``` +(define-fungible-token stackaroo)(ft-mint? stackaroo u100 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR)(ft-get-balance stackaroo 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR) ;; Returns u100 +``` + +#### ft-get-supply​ + +Introduced in: **Clarity 1** + +**input:** `TokenName` + +**output:** `uint` + +**signature:** `(ft-get-supply token-name)` + +**description:** + +`ft-get-balance` returns `token-name` circulating supply. The token type must have been defined using `define-fungible-token`. + +**example:** + +``` +(define-fungible-token stackaroo)(ft-mint? stackaroo u100 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR)(ft-get-supply stackaroo) ;; Returns u100 +``` + +#### ft-mint?​ + +Introduced in: **Clarity 1** + +**input:** `TokenName, uint, principal` + +**output:** `(response bool uint)` + +**signature:** `(ft-mint? token-name amount recipient)` + +**description:** + +`ft-mint?` is used to increase the token balance for the `recipient` principal for a token type defined using `define-fungible-token`. The increased token balance is _not_ transfered from another principal, but rather minted. + +If a non-positive amount is provided to mint, this function returns `(err 1)`. Otherwise, on successfuly mint, it returns `(ok true)`. + +**example:** + +``` +(define-fungible-token stackaroo)(ft-mint? stackaroo u100 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF) ;; Returns (ok true) +``` + +#### ft-transfer?​ + +Introduced in: **Clarity 1** + +**input:** `TokenName, uint, principal, principal` + +**output:** `(response bool uint)` + +**signature:** `(ft-transfer? token-name amount sender recipient)` + +**description:** + +`ft-transfer?` is used to increase the token balance for the `recipient` principal for a token type defined using `define-fungible-token` by debiting the `sender` principal. In contrast to `stx-transfer?`, any user can transfer the assets. When used, relevant guards need to be added. + +This function returns (ok true) if the transfer is successful. In the event of an unsuccessful transfer it returns one of the following error codes: + +`(err u1)` -- `sender` does not have enough balance to transfer `(err u2)` -- `sender` and `recipient` are the same principal `(err u3)` -- amount to send is non-positive + +**example:** + +``` +(define-fungible-token stackaroo)(ft-mint? stackaroo u100 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR)(ft-transfer? stackaroo u50 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF) ;; Returns (ok true)(ft-transfer? stackaroo u60 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF) ;; Returns (err u1) +``` + +#### get​ + +Introduced in: **Clarity 1** + +**input:** `KeyName, (tuple) | (optional (tuple))` + +**output:** `A` + +**signature:** `(get key-name tuple)` + +**description:** + +The `get` function fetches the value associated with a given key from the supplied typed tuple. If an `Optional` value is supplied as the inputted tuple, `get` returns an `Optional` type of the specified key in the tuple. If the supplied option is a `(none)` option, get returns `(none)`. + +**example:** + +``` +(define-map names-map { name: (string-ascii 12) } { id: int })(map-insert names-map { name: "blockstack" } { id: 1337 }) ;; Returns true(get id (tuple (name "blockstack") (id 1337))) ;; Returns 1337(get id (map-get? names-map (tuple (name "blockstack")))) ;; Returns (some 1337)(get id (map-get? names-map (tuple (name "non-existent")))) ;; Returns none +``` + +#### get-block-info?​ + +Introduced in: **Clarity 1** + +**input:** `BlockInfoPropertyName, uint` + +**output:** `(optional buff) | (optional uint)` + +**signature:** `(get-block-info? prop-name block-height)` + +**description:** + +The `get-block-info?` function fetches data for a block of the given _Stacks_ block height. The value and type returned are determined by the specified `BlockInfoPropertyName`. If the provided `block-height` does not correspond to an existing block prior to the current block, the function returns `none`. The currently available property names are as follows: + +`burnchain-header-hash`: This property returns a `(buff 32)` value containing the header hash of the burnchain (Bitcoin) block that selected the Stacks block at the given Stacks chain height. + +`id-header-hash`: This property returns a `(buff 32)` value containing the _index block hash_ of a Stacks block. This hash is globally unique, and is derived from the block hash and the history of accepted PoX operations. This is also the block hash value you would pass into `(at-block)`. + +`header-hash`: This property returns a `(buff 32)` value containing the header hash of a Stacks block, given a Stacks chain height. \*_WARNING_ this hash is not guaranteed to be globally unique, since the same Stacks block can be mined in different PoX forks. If you need global uniqueness, you should use `id-header-hash`. + +`miner-address`: This property returns a `principal` value corresponding to the miner of the given block. **WARNING** In Stacks 2.1, this is not guaranteed to be the same `principal` that received the block reward, since Stacks 2.1 supports coinbase transactions that pay the reward to a contract address. This is merely the address of the `principal` that produced the block. + +`time`: This property returns a `uint` value of the block header time field. This is a Unix epoch timestamp in seconds which roughly corresponds to when the block was mined. **Note**: this does not increase monotonically with each block and block times are accurate only to within two hours. See [BIP113](https://github.com/bitcoin/bips/blob/master/bip-0113.mediawiki) for more information. + +New in Stacks 2.1: + +`block-reward`: This property returns a `uint` value for the total block reward of the indicated Stacks block. This value is only available once the reward for the block matures. That is, the latest `block-reward` value available is at least 101 Stacks blocks in the past (on mainnet). The reward includes the coinbase, the anchored block's transaction fees, and the shares of the confirmed and produced microblock transaction fees earned by this block's miner. Note that this value may be smaller than the Stacks coinbase at this height, because the miner may have been punished with a valid `PoisonMicroblock` transaction in the event that the miner published two or more microblock stream forks. + +`miner-spend-total`: This property returns a `uint` value for the total number of burnchain tokens (i.e. satoshis) spent by all miners trying to win this block. + +`miner-spend-winner`: This property returns a `uint` value for the number of burnchain tokens (i.e. satoshis) spent by the winning miner for this Stacks block. Note that this value is less than or equal to the value for `miner-spend-total` at the same block height. + +**example:** + +``` +(get-block-info? time u0) ;; Returns (some u1557860301)(get-block-info? header-hash u0) ;; Returns (some 0x374708fff7719dd5979ec875d56cd2286f6d3cf7ec317a3b25632aab28ec37bb)(get-block-info? vrf-seed u0) ;; Returns (some 0xf490de2920c8a35fabeb13208852aa28c76f9be9b03a4dd2b3c075f7a26923b4) +``` + +#### get-burn-block-info?​ + +Introduced in: **Clarity 2** + +**input:** `BurnBlockInfoPropertyName, uint` + +**output:** `(optional buff) | (optional (tuple (addrs (list 2 (tuple (hashbytes (buff 32)) (version (buff 1))))) (payout uint)))` + +**signature:** `(get-burn-block-info? prop-name block-height)` + +**description:** + +The `get-burn-block-info?` function fetches data for a block of the given _burnchain_ block height. The value and type returned are determined by the specified `BlockInfoPropertyName`. Valid values for `block-height` only include heights between the burnchain height at the time the Stacks chain was launched, and the last-processed burnchain block. If the `block-height` argument falls outside of this range, then `none` shall be returned. + +The following `BlockInfoPropertyName` values are defined: + +* The `header-hash` property returns a 32-byte buffer representing the header hash of the burnchain block at burnchain height `block-height`. +* The `pox-addrs` property returns a tuple with two items: a list of up to two PoX addresses that received a PoX payout at that block height, and the amount of burnchain tokens paid to each address (note that per the blockchain consensus rules, each PoX payout will be the same for each address in the block-commit transaction). The list will include burn addresses -- that is, the unspendable addresses that miners pay to when there are no PoX addresses left to be paid. During the prepare phase, there will be exactly one burn address reported. During the reward phase, up to two burn addresses may be reported in the event that some PoX reward slots are not claimed. + +The `addrs` list contains the same PoX address values passed into the PoX smart contract: + +* They each have type signature `(tuple (hashbytes (buff 32)) (version (buff 1)))` +* The `version` field can be any of the following: + * `0x00` means this is a p2pkh address, and `hashbytes` is the 20-byte hash160 of a single public key + * `0x01` means this is a p2sh address, and `hashbytes` is the 20-byte hash160 of a redeemScript script + * `0x02` means this is a p2wpkh-p2sh address, and `hashbytes` is the 20-byte hash160 of a p2wpkh witness script + * `0x03` means this is a p2wsh-p2sh address, and `hashbytes` is the 20-byte hash160 of a p2wsh witness script + * `0x04` means this is a p2wpkh address, and `hashbytes` is the 20-byte hash160 of the witness script + * `0x05` means this is a p2wsh address, and `hashbytes` is the 32-byte sha256 of the witness script + * `0x06` means this is a p2tr address, and `hashbytes` is the 32-byte sha256 of the witness script + +**example:** + +``` +(get-burn-block-info? header-hash u677050) ;; Returns (some 0xe67141016c88a7f1203eca0b4312f2ed141531f59303a1c267d7d83ab6b977d8)(get-burn-block-info? pox-addrs u677050) ;; Returns (some (tuple (addrs ((tuple (hashbytes 0x395f3643cea07ec4eec73b4d9a973dcce56b9bf1) (version 0x00)) (tuple (hashbytes 0x7c6775e20e3e938d2d7e9d79ac310108ba501ddb) (version 0x01)))) (payout u123))) +``` + +#### hash160​ + +Introduced in: **Clarity 1** + +**input:** `buff|uint|int` + +**output:** `(buff 20)` + +**signature:** `(hash160 value)` + +**description:** + +The `hash160` function computes `RIPEMD160(SHA256(x))` of the inputted value. If an integer (128 bit) is supplied the hash is computed over the little-endian representation of the integer. + +**example:** + +``` +(hash160 0) ;; Returns 0xe4352f72356db555721651aa612e00379167b30f +``` + +#### if​ + +Introduced in: **Clarity 1** + +**input:** `bool, A, A` + +**output:** `A` + +**signature:** `(if bool1 expr1 expr2)` + +**description:** + +The `if` function admits a boolean argument and two expressions which must return the same type. In the case that the boolean input is `true`, the `if` function evaluates and returns `expr1`. If the boolean input is `false`, the `if` function evaluates and returns `expr2`. + +**example:** + +``` +(if true 1 2) ;; Returns 1(if (> 1 2) 1 2) ;; Returns 2 +``` + +#### impl-trait​ + +Introduced in: **Clarity 1** + +**input:** `TraitIdentifier` + +**output:** `Not Applicable` + +**signature:** `(impl-trait trait-identifier)` + +**description:** + +`impl-trait` can be use for asserting that a contract is fully implementing a given trait. Additional checks are being performed when the contract is being published, rejecting the deployment if the contract is violating the trait specification. + +Trait identifiers can either be using the sugared syntax (.token-a.token-trait), or be fully qualified ('SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF.token-a.token-trait). + +Like other kinds of definition statements, `impl-trait` may only be used at the top level of a smart contract definition (i.e., you cannot put such a statement in the middle of a function body). + +**example:** + +``` +(impl-trait 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF.token-a.token-trait)(define-public (get-balance (account principal)) (ok u0))(define-public (transfer? (from principal) (to principal) (amount uint)) (ok u0)) +``` + +#### index-of​ + +Introduced in: **Clarity 1** + +**input:** `sequence_A, A` + +**output:** `(optional uint)` + +**signature:** `(index-of? sequence item)` + +**description:** + +The `index-of?` function returns the first index at which `item` can be found, using `is-eq` checks, in the provided sequence. Applicable sequence types are `(list A)`, `buff`, `string-ascii` and `string-utf8`, for which the corresponding element types are, respectively, `A`, `(buff 1)`, `(string-ascii 1)` and `(string-utf8 1)`. If the target item is not found in the sequence (or if an empty string or buffer is supplied), this function returns `none`. In Clarity1, `index-of` must be used (without the `?`). The `?` is added in Clarity2 for consistency -- built-ins that return responses or optionals end in `?`. The Clarity1 spelling is left as an alias in Clarity2 for backwards compatibility. + +**example:** + +``` +(index-of? "blockstack" "b") ;; Returns (some u0)(index-of? "blockstack" "k") ;; Returns (some u4)(index-of? "blockstack" "") ;; Returns none(index-of? (list 1 2 3 4 5) 6) ;; Returns none(index-of? 0xfb01 0x01) ;; Returns (some u1) +``` + +#### index-of?​ + +Introduced in: **Clarity 2** + +**input:** `sequence_A, A` + +**output:** `(optional uint)` + +**signature:** `(index-of? sequence item)` + +**description:** + +The `index-of?` function returns the first index at which `item` can be found, using `is-eq` checks, in the provided sequence. Applicable sequence types are `(list A)`, `buff`, `string-ascii` and `string-utf8`, for which the corresponding element types are, respectively, `A`, `(buff 1)`, `(string-ascii 1)` and `(string-utf8 1)`. If the target item is not found in the sequence (or if an empty string or buffer is supplied), this function returns `none`. In Clarity1, `index-of` must be used (without the `?`). The `?` is added in Clarity2 for consistency -- built-ins that return responses or optionals end in `?`. The Clarity1 spelling is left as an alias in Clarity2 for backwards compatibility. + +**example:** + +``` +(index-of? "blockstack" "b") ;; Returns (some u0)(index-of? "blockstack" "k") ;; Returns (some u4)(index-of? "blockstack" "") ;; Returns none(index-of? (list 1 2 3 4 5) 6) ;; Returns none(index-of? 0xfb01 0x01) ;; Returns (some u1) +``` + +#### int-to-ascii​ + +Introduced in: **Clarity 2** + +**input:** `int | uint` + +**output:** `(string-ascii 40)` + +**signature:** `(int-to-ascii (int|uint))` + +**description:** + +Converts an integer, either `int` or `uint`, to a `string-ascii` string-value representation. + +Note: This function is only available starting with Stacks 2.1. + +**example:** + +``` +(int-to-ascii 1) ;; Returns "1"(int-to-ascii u1) ;; Returns "1"(int-to-ascii -1) ;; Returns "-1" +``` + +#### int-to-utf8​ + +Introduced in: **Clarity 2** + +**input:** `int | uint` + +**output:** `(string-utf8 40)` + +**signature:** `(int-to-utf8 (int|uint))` + +**description:** + +Converts an integer, either `int` or `uint`, to a `string-utf8` string-value representation. + +Note: This function is only available starting with Stacks 2.1. + +**example:** + +``` +(int-to-utf8 1) ;; Returns u"1"(int-to-utf8 u1) ;; Returns u"1"(int-to-utf8 -1) ;; Returns u"-1" +``` + +#### is-eq​ + +Introduced in: **Clarity 1** + +**input:** `A, A, ...` + +**output:** `bool` + +**signature:** `(is-eq v1 v2...)` + +**description:** + +Compares the inputted values, returning `true` if they are all equal. Note that _unlike_ the `(and ...)` function, `(is-eq ...)` will _not_ short-circuit. All values supplied to is-eq _must_ be the same type. + +**example:** + +``` +(is-eq 1 1) ;; Returns true(is-eq true false) ;; Returns false(is-eq "abc" 234 234) ;; Throws type error(is-eq "abc" "abc") ;; Returns true(is-eq 0x0102 0x0102) ;; Returns true +``` + +#### is-err​ + +Introduced in: **Clarity 1** + +**input:** `(response A B)` + +**output:** `bool` + +**signature:** `(is-err value)` + +**description:** + +`is-err` tests a supplied response value, returning `true` if the response was an `err`, and `false` if it was an `ok`. + +**example:** + +``` +(is-err (ok 1)) ;; Returns false(is-err (err 1)) ;; Returns true +``` + +#### is-none​ + +Introduced in: **Clarity 1** + +**input:** `(optional A)` + +**output:** `bool` + +**signature:** `(is-none value)` + +**description:** + +`is-none` tests a supplied option value, returning `true` if the option value is `(none)`, and `false` if it is a `(some ...)`. + +**example:** + +``` +(define-map names-map { name: (string-ascii 12) } { id: int })(map-set names-map { name: "blockstack" } { id: 1337 })(is-none (get id (map-get? names-map { name: "blockstack" }))) ;; Returns false(is-none (get id (map-get? names-map { name: "non-existant" }))) ;; Returns true +``` + +#### is-ok​ + +Introduced in: **Clarity 1** + +**input:** `(response A B)` + +**output:** `bool` + +**signature:** `(is-ok value)` + +**description:** + +`is-ok` tests a supplied response value, returning `true` if the response was `ok`, and `false` if it was an `err`. + +**example:** + +``` +(is-ok (ok 1)) ;; Returns true(is-ok (err 1)) ;; Returns false +``` + +#### is-some​ + +Introduced in: **Clarity 1** + +**input:** `(optional A)` + +**output:** `bool` + +**signature:** `(is-some value)` + +**description:** + +`is-some` tests a supplied option value, returning `true` if the option value is `(some ...)`, and `false` if it is a `none`. + +**example:** + +``` +(define-map names-map { name: (string-ascii 12) } { id: int })(map-set names-map { name: "blockstack" } { id: 1337 })(is-some (get id (map-get? names-map { name: "blockstack" }))) ;; Returns true(is-some (get id (map-get? names-map { name: "non-existant" }))) ;; Returns false +``` + +#### is-standard​ + +Introduced in: **Clarity 2** + +**input:** `principal` + +**output:** `bool` + +**signature:** `(is-standard standard-or-contract-principal)` + +**description:** + +Tests whether `standard-or-contract-principal` _matches_ the current network type, and therefore represents a principal that can spend tokens on the current network type. That is, the network is either of type `mainnet`, or `testnet`. Only `SPxxxx` and `SMxxxx` _c32check form_ addresses can spend tokens on a mainnet, whereas only `STxxxx` and `SNxxxx` _c32check forms_ addresses can spend tokens on a testnet. All addresses can _receive_ tokens, but only principal _c32check form_ addresses that match the network type can _spend_ tokens on the network. This method will return `true` if and only if the principal matches the network type, and false otherwise. + +Note: This function is only available starting with Stacks 2.1. + +**example:** + +``` +(is-standard 'STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6) ;; returns true on testnet and false on mainnet(is-standard 'STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6.foo) ;; returns true on testnet and false on mainnet(is-standard 'SP3X6QWWETNBZWGBK6DRGTR1KX50S74D3433WDGJY) ;; returns true on mainnet and false on testnet(is-standard 'SP3X6QWWETNBZWGBK6DRGTR1KX50S74D3433WDGJY.foo) ;; returns true on mainnet and false on testnet(is-standard 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR) ;; returns false on both mainnet and testnet +``` + +#### keccak256​ + +Introduced in: **Clarity 1** + +**input:** `buff|uint|int` + +**output:** `(buff 32)` + +**signature:** `(keccak256 value)` + +**description:** + +The `keccak256` function computes `KECCAK256(value)` of the inputted value. Note that this differs from the `NIST SHA-3` (that is, FIPS 202) standard. If an integer (128 bit) is supplied the hash is computed over the little-endian representation of the integer. + +**example:** + +``` +(keccak256 0) ;; Returns 0xf490de2920c8a35fabeb13208852aa28c76f9be9b03a4dd2b3c075f7a26923b4 +``` + +#### len​ + +Introduced in: **Clarity 1** + +**input:** `sequence_A` + +**output:** `uint` + +**signature:** `(len sequence)` + +**description:** + +The `len` function returns the length of a given sequence. Applicable sequence types are `(list A)`, `buff`, `string-ascii` and `string-utf8`. + +**example:** + +``` +(len "blockstack") ;; Returns u10(len (list 1 2 3 4 5)) ;; Returns u5(len 0x010203) ;; Returns u3 +``` + +#### let​ + +Introduced in: **Clarity 1** + +**input:** `((name1 AnyType) (name2 AnyType) ...), AnyType, ... A` + +**output:** `A` + +**signature:** `(let ((name1 expr1) (name2 expr2) ...) expr-body1 expr-body2 ... expr-body-last)` + +**description:** + +The `let` function accepts a list of `variable name` and `expression` pairs, evaluating each expression and _binding_ it to the corresponding variable name. `let` bindings are sequential: when a `let` binding is evaluated, it may refer to prior binding. The _context_ created by this set of bindings is used for evaluating its body expressions. The let expression returns the value of the last such body expression. Note: intermediary statements returning a response type must be checked + +**example:** + +``` +(let ((a 2) (b (+ 5 6 7))) (print a) (print b) (+ a b)) ;; Returns 20(let ((a 5) (c (+ a 1)) (d (+ c 1)) (b (+ a c d))) (print a) (print b) (+ a b)) ;; Returns 23 +``` + +#### list​ + +Introduced in: **Clarity 1** + +**input:** `A, ...` + +**output:** `(list A)` + +**signature:** `(list expr1 expr2 expr3 ...)` + +**description:** + +The `list` function constructs a list composed of the inputted values. Each supplied value must be of the same type. + +**example:** + +``` +(list (+ 1 2) 4 5) ;; Returns (3 4 5) +``` + +#### log2​ + +Introduced in: **Clarity 1** + +**input:** `int | uint` + +**output:** `int | uint` + +**signature:** `(log2 n)` + +**description:** + +Returns the power to which the number 2 must be raised to to obtain the value `n`, rounded down to the nearest integer. Fails on a negative numbers. + +**example:** + +``` +(log2 u8) ;; Returns u3(log2 8) ;; Returns 3(log2 u1) ;; Returns u0(log2 1000) ;; Returns 9 +``` + +#### map​ + +Introduced in: **Clarity 1** + +**input:** `Function(A, B, ..., N) -> X, sequence_A, sequence_B, ..., sequence_N` + +**output:** `(list X)` + +**signature:** `(map func sequence_A sequence_B ... sequence_N)` + +**description:** + +The `map` function applies the function `func` to each corresponding element of the input sequences, and outputs a _list_ of the same type containing the outputs from those function applications. Applicable sequence types are `(list A)`, `buff`, `string-ascii` and `string-utf8`, for which the corresponding element types are, respectively, `A`, `(buff 1)`, `(string-ascii 1)` and `(string-utf8 1)`. The `func` argument must be a literal function name. Also, note that, no matter what kind of sequences the inputs are, the output is always a list. + +**example:** + +``` +(map not (list true false true false)) ;; Returns (false true false true)(map + (list 1 2 3) (list 1 2 3) (list 1 2 3)) ;; Returns (3 6 9)(define-private (a-or-b (char (string-utf8 1))) (if (is-eq char u"a") u"a" u"b"))(map a-or-b u"aca") ;; Returns (u"a" u"b" u"a")(define-private (zero-or-one (char (buff 1))) (if (is-eq char 0x00) 0x00 0x01))(map zero-or-one 0x000102) ;; Returns (0x00 0x01 0x01) +``` + +#### map-delete​ + +Introduced in: **Clarity 1** + +**input:** `MapName, tuple` + +**output:** `bool` + +**signature:** `(map-delete map-name key-tuple)` + +**description:** + +The `map-delete` function removes the value associated with the input key for the given map. If an item exists and is removed, the function returns `true`. If a value did not exist for this key in the data map, the function returns `false`. + +**example:** + +``` +(define-map names-map { name: (string-ascii 10) } { id: int })(map-insert names-map { name: "blockstack" } { id: 1337 }) ;; Returns true(map-delete names-map { name: "blockstack" }) ;; Returns true(map-delete names-map { name: "blockstack" }) ;; Returns false(map-delete names-map (tuple (name "blockstack"))) ;; Same command, using a shorthand for constructing the tuple +``` + +#### map-get?​ + +Introduced in: **Clarity 1** + +**input:** `MapName, tuple` + +**output:** `(optional (tuple))` + +**signature:** `(map-get? map-name key-tuple)` + +**description:** + +The `map-get?` function looks up and returns an entry from a contract's data map. The value is looked up using `key-tuple`. If there is no value associated with that key in the data map, the function returns a `none` option. Otherwise, it returns `(some value)`. + +**example:** + +``` +(define-map names-map { name: (string-ascii 10) } { id: int })(map-set names-map { name: "blockstack" } { id: 1337 })(map-get? names-map (tuple (name "blockstack"))) ;; Returns (some (tuple (id 1337)))(map-get? names-map { name: "blockstack" }) ;; Same command, using a shorthand for constructing the tuple +``` + +#### map-insert​ + +Introduced in: **Clarity 1** + +**input:** `MapName, tuple_A, tuple_B` + +**output:** `bool` + +**signature:** `(map-insert map-name key-tuple value-tuple)` + +**description:** + +The `map-insert` function sets the value associated with the input key to the inputted value if and only if there is not already a value associated with the key in the map. If an insert occurs, the function returns `true`. If a value already existed for this key in the data map, the function returns `false`. + +Note: the `value-tuple` requires 1 additional byte for storage in the materialized blockchain state, and therefore the maximum size of a value that may be inserted into a map is MAX\_CLARITY\_VALUE - 1. + +**example:** + +``` +(define-map names-map { name: (string-ascii 10) } { id: int })(map-insert names-map { name: "blockstack" } { id: 1337 }) ;; Returns true(map-insert names-map { name: "blockstack" } { id: 1337 }) ;; Returns false(map-insert names-map (tuple (name "blockstack")) (tuple (id 1337))) ;; Same command, using a shorthand for constructing the tuple +``` + +#### map-set​ + +Introduced in: **Clarity 1** + +**input:** `MapName, tuple_A, tuple_B` + +**output:** `bool` + +**signature:** `(map-set map-name key-tuple value-tuple)` + +**description:** + +The `map-set` function sets the value associated with the input key to the inputted value. This function performs a _blind_ update; whether or not a value is already associated with the key, the function overwrites that existing association. + +Note: the `value-tuple` requires 1 additional byte for storage in the materialized blockchain state, and therefore the maximum size of a value that may be inserted into a map is MAX\_CLARITY\_VALUE - 1. + +**example:** + +``` +(define-map names-map { name: (string-ascii 10) } { id: int })(map-set names-map { name: "blockstack" } { id: 1337 }) ;; Returns true(map-set names-map (tuple (name "blockstack")) (tuple (id 1337))) ;; Same command, using a shorthand for constructing the tuple +``` + +#### match​ + +Introduced in: **Clarity 1** + +**input:** `(optional A) name expression expression | (response A B) name expression name expression` + +**output:** `C` + +**signature:** `(match opt-input some-binding-name some-branch none-branch) | (match-resp input ok-binding-name ok-branch err-binding-name err-branch)` + +**description:** + +The `match` function is used to test and destructure optional and response types. + +If the `input` is an optional, it tests whether the provided `input` is a `some` or `none` option, and evaluates `some-branch` or `none-branch` in each respective case. + +Within the `some-branch`, the _contained value_ of the `input` argument is bound to the provided `some-binding-name` name. + +Only _one_ of the branches will be evaluated (similar to `if` statements). + +If the `input` is a response, it tests whether the provided `input` is an `ok` or `err` response type, and evaluates `ok-branch` or `err-branch` in each respective case. + +Within the `ok-branch`, the _contained ok value_ of the `input` argument is bound to the provided `ok-binding-name` name. + +Within the `err-branch`, the _contained err value_ of the `input` argument is bound to the provided `err-binding-name` name. + +Only _one_ of the branches will be evaluated (similar to `if` statements). + +Note: Type checking requires that the type of both the ok and err parts of the response object be determinable. For situations in which one of the parts of a response is untyped, you should use `unwrap-panic` or `unwrap-err-panic` instead of `match`. + +**example:** + +``` +(define-private (add-10 (x (optional int))) (match x value (+ 10 value) 10))(add-10 (some 5)) ;; Returns 15(add-10 none) ;; Returns 10(define-private (add-or-pass-err (x (response int (string-ascii 10))) (to-add int)) (match x value (ok (+ to-add value)) err-value (err err-value)))(add-or-pass-err (ok 5) 20) ;; Returns (ok 25)(add-or-pass-err (err "ERROR") 20) ;; Returns (err "ERROR") +``` + +#### merge​ + +Introduced in: **Clarity 1** + +**input:** `tuple, tuple` + +**output:** `tuple` + +**signature:** `(merge tuple { key1: val1 })` + +**description:** + +The `merge` function returns a new tuple with the combined fields, without mutating the supplied tuples. + +**example:** + +``` +(define-map users { id: int } { name: (string-ascii 12), address: (optional principal) })(map-insert users { id: 1337 } { name: "john", address: none }) ;; Returns true(let ((user (unwrap-panic (map-get? users { id: 1337 })))) (merge user { address: (some 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF) })) ;; Returns (tuple (address (some SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF)) (name "john")) +``` + +#### mod​ + +Introduced in: **Clarity 1** + +**input:** `int, int | uint, uint | string-ascii, string-ascii | string-utf8, string-utf8 | buff, buff` + +**output:** `int | uint` + +**signature:** `(mod i1 i2)` + +**description:** + +Returns the integer remainder from integer dividing `i1` by `i2`. In the event of a division by zero, throws a runtime error. + +**example:** + +``` +(mod 2 3) ;; Returns 2(mod 5 2) ;; Returns 1(mod 7 1) ;; Returns 0 +``` + +#### nft-burn?​ + +Introduced in: **Clarity 1** + +**input:** `AssetName, A, principal` + +**output:** `(response bool uint)` + +**signature:** `(nft-burn? asset-class asset-identifier sender)` + +**description:** + +`nft-burn?` is used to burn an asset that the `sender` principal owns. The asset must have been defined using `define-non-fungible-token`, and the supplied `asset-identifier` must be of the same type specified in that definition. + +On a successful burn, it returns `(ok true)`. In the event of an unsuccessful burn it returns one of the following error codes: + +`(err u1)` -- `sender` does not own the specified asset `(err u3)` -- the asset specified by `asset-identifier` does not exist + +**example:** + +``` +(define-non-fungible-token stackaroo (string-ascii 40))(nft-mint? stackaroo "Roo" 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF) ;; Returns (ok true)(nft-burn? stackaroo "Roo" 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF) ;; Returns (ok true) +``` + +#### nft-get-owner?​ + +Introduced in: **Clarity 1** + +**input:** `AssetName, A` + +**output:** `(optional principal)` + +**signature:** `(nft-get-owner? asset-class asset-identifier)` + +**description:** + +`nft-get-owner?` returns the owner of an asset, identified by `asset-identifier`, or `none` if the asset does not exist. The asset type must have been defined using `define-non-fungible-token`, and the supplied `asset-identifier` must be of the same type specified in that definition. + +**example:** + +``` +(define-non-fungible-token stackaroo (string-ascii 40))(nft-mint? stackaroo "Roo" 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF)(nft-get-owner? stackaroo "Roo") ;; Returns (some SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF)(nft-get-owner? stackaroo "Too") ;; Returns none +``` + +#### nft-mint?​ + +Introduced in: **Clarity 1** + +**input:** `AssetName, A, principal` + +**output:** `(response bool uint)` + +**signature:** `(nft-mint? asset-class asset-identifier recipient)` + +**description:** + +`nft-mint?` is used to instantiate an asset and set that asset's owner to the `recipient` principal. The asset must have been defined using `define-non-fungible-token`, and the supplied `asset-identifier` must be of the same type specified in that definition. + +If an asset identified by `asset-identifier` _already exists_, this function will return an error with the following error code: + +`(err u1)` + +Otherwise, on successfuly mint, it returns `(ok true)`. + +**example:** + +``` +(define-non-fungible-token stackaroo (string-ascii 40))(nft-mint? stackaroo "Roo" 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF) ;; Returns (ok true) +``` + +#### nft-transfer?​ + +Introduced in: **Clarity 1** + +**input:** `AssetName, A, principal, principal` + +**output:** `(response bool uint)` + +**signature:** `(nft-transfer? asset-class asset-identifier sender recipient)` + +**description:** + +`nft-transfer?` is used to change the owner of an asset identified by `asset-identifier` from `sender` to `recipient`. The `asset-class` must have been defined by `define-non-fungible-token` and `asset-identifier` must be of the type specified in that definition. In contrast to `stx-transfer?`, any user can transfer the asset. When used, relevant guards need to be added. + +This function returns (ok true) if the transfer is successful. In the event of an unsuccessful transfer it returns one of the following error codes: + +`(err u1)` -- `sender` does not own the asset `(err u2)` -- `sender` and `recipient` are the same principal `(err u3)` -- asset identified by asset-identifier does not exist + +**example:** + +``` +(define-non-fungible-token stackaroo (string-ascii 40))(nft-mint? stackaroo "Roo" 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR)(nft-transfer? stackaroo "Roo" 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF) ;; Returns (ok true)(nft-transfer? stackaroo "Roo" 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF) ;; Returns (err u1)(nft-transfer? stackaroo "Stacka" 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF) ;; Returns (err u3) +``` + +#### not​ + +Introduced in: **Clarity 1** + +**input:** `bool` + +**output:** `bool` + +**signature:** `(not b1)` + +**description:** + +Returns the inverse of the boolean input. + +**example:** + +``` +(not true) ;; Returns false(not (is-eq 1 2)) ;; Returns true +``` + +#### ok​ + +Introduced in: **Clarity 1** + +**input:** `A` + +**output:** `(response A B)` + +**signature:** `(ok value)` + +**description:** + +The `ok` function constructs a response type from the input value. Use `ok` for creating return values in public functions. An _ok_ value indicates that any database changes during the processing of the function should materialize. + +**example:** + +``` +(ok 1) ;; Returns (ok 1) +``` + +#### or​ + +Introduced in: **Clarity 1** + +**input:** `bool, ...` + +**output:** `bool` + +**signature:** `(or b1 b2 ...)` + +**description:** + +Returns `true` if any boolean inputs are `true`. Importantly, the supplied arguments are evaluated in-order and lazily. Lazy evaluation means that if one of the arguments returns `true`, the function short-circuits, and no subsequent arguments are evaluated. + +**example:** + +``` +(or true false) ;; Returns true(or (is-eq (+ 1 2) 1) (is-eq 4 4)) ;; Returns true(or (is-eq (+ 1 2) 1) (is-eq 3 4)) ;; Returns false(or (is-eq (+ 1 2) 3) (is-eq 4 4)) ;; Returns true +``` + +#### pow​ + +Introduced in: **Clarity 1** + +**input:** `int, int | uint, uint | string-ascii, string-ascii | string-utf8, string-utf8 | buff, buff` + +**output:** `int | uint` + +**signature:** `(pow i1 i2)` + +**description:** + +Returns the result of raising `i1` to the power of `i2`. In the event of an _overflow_, throws a runtime error. Note: Corner cases are handled with the following rules: + +* if both `i1` and `i2` are `0`, return `1` +* if `i1` is `1`, return `1` +* if `i1` is `0`, return `0` +* if `i2` is `1`, return `i1` +* if `i2` is negative or greater than `u32::MAX`, throw a runtime error + +**example:** + +``` +(pow 2 3) ;; Returns 8(pow 2 2) ;; Returns 4(pow 7 1) ;; Returns 7 +``` + +#### principal-construct?​ + +Introduced in: **Clarity 2** + +**input:** `(buff 1), (buff 20), [(string-ascii 40)]` + +**output:** `(response principal { error_code: uint, principal: (option principal) })` + +**signature:** `(principal-construct? (buff 1) (buff 20) [(string-ascii 40)])` + +**description:** + +A principal value represents either a set of keys, or a smart contract. The former, called a _standard principal_, is encoded as a `(buff 1)` _version byte_, indicating the type of account and the type of network that this principal can spend tokens on, and a `(buff 20)` _public key hash_, characterizing the principal's unique identity. The latter, a _contract principal_, is encoded as a standard principal concatenated with a `(string-ascii 40)` _contract name_ that identifies the code body. + +The `principal-construct?` function allows users to create either standard or contract principals, depending on which form is used. To create a standard principal, `principal-construct?` would be called with two arguments: it takes as input a `(buff 1)` which encodes the principal address's `version-byte`, a `(buff 20)` which encodes the principal address's `hash-bytes`. To create a contract principal, `principal-construct?` would be called with three arguments: the `(buff 1)` and `(buff 20)` to represent the standard principal that created the contract, and a `(string-ascii 40)` which encodes the contract's name. On success, this function returns either a standard principal or contract principal, depending on whether or not the third `(string-ascii 40)` argument is given. + +This function returns a `Response`. On success, the `ok` value is a `Principal`. The `err` value is a value tuple with the form `{ error_code: uint, value: (optional principal) }`. + +If the single-byte `version-byte` is in the valid range `0x00` to `0x1f`, but is not an appropriate version byte for the current network, then the error will be `u0`, and `value` will contain `(some principal)`, where the wrapped value is the principal. If the `version-byte` is not in this range, however, then the `value` will be `none`. + +If the `version-byte` is a `buff` of length 0, if the single-byte `version-byte` is a value greater than `0x1f`, or the `hash-bytes` is a `buff` of length not equal to 20, then `error_code` will be `u1` and `value` will be `None`. + +If a name is given, and the name is either an empty string or contains ASCII characters that are not allowed in contract names, then `error_code` will be `u2`. + +Note: This function is only available starting with Stacks 2.1. + +**example:** + +``` +(principal-construct? 0x1a 0xfa6bf38ed557fe417333710d6033e9419391a320) ;; Returns (ok ST3X6QWWETNBZWGBK6DRGTR1KX50S74D3425Q1TPK)(principal-construct? 0x1a 0xfa6bf38ed557fe417333710d6033e9419391a320 "foo") ;; Returns (ok ST3X6QWWETNBZWGBK6DRGTR1KX50S74D3425Q1TPK.foo)(principal-construct? 0x16 0xfa6bf38ed557fe417333710d6033e9419391a320) ;; Returns (err (tuple (error_code u0) (value (some SP3X6QWWETNBZWGBK6DRGTR1KX50S74D3433WDGJY))))(principal-construct? 0x16 0xfa6bf38ed557fe417333710d6033e9419391a320 "foo") ;; Returns (err (tuple (error_code u0) (value (some SP3X6QWWETNBZWGBK6DRGTR1KX50S74D3433WDGJY.foo))))(principal-construct? 0x 0xfa6bf38ed557fe417333710d6033e9419391a320) ;; Returns (err (tuple (error_code u1) (value none)))(principal-construct? 0x16 0xfa6bf38ed557fe417333710d6033e9419391a3) ;; Returns (err (tuple (error_code u1) (value none)))(principal-construct? 0x20 0xfa6bf38ed557fe417333710d6033e9419391a320) ;; Returns (err (tuple (error_code u1) (value none)))(principal-construct? 0x1a 0xfa6bf38ed557fe417333710d6033e9419391a320 "") ;; Returns (err (tuple (error_code u2) (value none)))(principal-construct? 0x1a 0xfa6bf38ed557fe417333710d6033e9419391a320 "foo[") ;; Returns (err (tuple (error_code u2) (value none))) +``` + +#### principal-destruct?​ + +Introduced in: **Clarity 2** + +**input:** `principal` + +**output:** `(response (tuple (hash-bytes (buff 20)) (name (optional (string-ascii 40))) (version (buff 1))) (tuple (hash-bytes (buff 20)) (name (optional (string-ascii 40))) (version (buff 1))))` + +**signature:** `(principal-destruct? principal-address)` + +**description:** + +A principal value represents either a set of keys, or a smart contract. The former, called a _standard principal_, is encoded as a `(buff 1)` _version byte_, indicating the type of account and the type of network that this principal can spend tokens on, and a `(buff 20)` _public key hash_, characterizing the principal's unique identity. The latter, a _contract principal_, is encoded as a standard principal concatenated with a `(string-ascii 40)` _contract name_ that identifies the code body. + +`principal-destruct?` will decompose a principal into its component parts: either`{version-byte, hash-bytes}` for standard principals, or `{version-byte, hash-bytes, name}` for contract principals. + +This method returns a `Response` that wraps this data as a tuple. + +If the version byte of `principal-address` matches the network (see `is-standard`), then this method returns the pair as its `ok` value. + +If the version byte of `principal-address` does not match the network, then this method returns the pair as its `err` value. + +In both cases, the value itself is a tuple containing three fields: a `version` value as a `(buff 1)`, a `hash-bytes` value as a `(buff 20)`, and a `name` value as an `(optional (string-ascii 40))`. The `name` field will only be `(some ..)` if the principal is a contract principal. + +Note: This function is only available starting with Stacks 2.1. + +**example:** + +``` +(principal-destruct? 'STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6) ;; Returns (ok (tuple (hash-bytes 0x164247d6f2b425ac5771423ae6c80c754f7172b0) (name none) (version 0x1a)))(principal-destruct? 'STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6.foo) ;; Returns (ok (tuple (hash-bytes 0x164247d6f2b425ac5771423ae6c80c754f7172b0) (name (some "foo")) (version 0x1a)))(principal-destruct? 'SP3X6QWWETNBZWGBK6DRGTR1KX50S74D3433WDGJY) ;; Returns (err (tuple (hash-bytes 0xfa6bf38ed557fe417333710d6033e9419391a320) (name none) (version 0x16)))(principal-destruct? 'SP3X6QWWETNBZWGBK6DRGTR1KX50S74D3433WDGJY.foo) ;; Returns (err (tuple (hash-bytes 0xfa6bf38ed557fe417333710d6033e9419391a320) (name (some "foo")) (version 0x16))) +``` + +#### principal-of?​ + +Introduced in: **Clarity 1** + +**input:** `(buff 33)` + +**output:** `(response principal uint)` + +**signature:** `(principal-of? public-key)` + +**description:** + +The `principal-of?` function returns the principal derived from the provided public key. If the `public-key` is invalid, it will return the error code `(err u1).`. + +Note: Before Stacks 2.1, this function has a bug, in that the principal returned would always be a testnet single-signature principal, even if the function were run on the mainnet. Starting with Stacks 2.1, this bug is fixed, so that this function will return a principal suited to the network it is called on. In particular, if this is called on the mainnet, it will return a single-signature mainnet principal. + +**example:** + +``` +(principal-of? 0x03adb8de4bfb65db2cfd6120d55c6526ae9c52e675db7e47308636534ba7786110) ;; Returns (ok ST1AW6EKPGT61SQ9FNVDS17RKNWT8ZP582VF9HSCP) +``` + +#### print​ + +Introduced in: **Clarity 1** + +**input:** `A` + +**output:** `A` + +**signature:** `(print expr)` + +**description:** + +The `print` function evaluates and returns its input expression. On Stacks Core nodes configured for development (as opposed to production mining nodes), this function prints the resulting value to `STDOUT` (standard output). + +**example:** + +``` +(print (+ 1 2 3)) ;; Returns 6 +``` + +#### replace-at?​ + +Introduced in: **Clarity 2** + +**input:** `sequence_A, uint, A` + +**output:** `(optional sequence_A)` + +**signature:** `(replace-at? sequence index element)` + +**description:** + +The `replace-at?` function takes in a sequence, an index, and an element, and returns a new sequence with the data at the index position replaced with the given element. The given element's type must match the type of the sequence, and must correspond to a single index of the input sequence. The return type on success is the same type as the input sequence. + +If the provided index is out of bounds, this functions returns `none`. + +**example:** + +``` +(replace-at? u"ab" u1 u"c") ;; Returns (some u"ac")(replace-at? 0x00112233 u2 0x44) ;; Returns (some 0x00114433)(replace-at? "abcd" u3 "e") ;; Returns (some "abce")(replace-at? (list 1) u0 10) ;; Returns (some (10))(replace-at? (list (list 1) (list 2)) u0 (list 33)) ;; Returns (some ((33) (2)))(replace-at? (list 1 2) u3 4) ;; Returns none +``` + +#### secp256k1-recover?​ + +Introduced in: **Clarity 1** + +**input:** `(buff 32), (buff 65)` + +**output:** `(response (buff 33) uint)` + +**signature:** `(secp256k1-recover? message-hash signature)` + +**description:** + +The `secp256k1-recover?` function recovers the public key used to sign the message which sha256 is `message-hash` with the provided `signature`. If the signature does not match, it will return the error code `(err u1).`. If the signature is invalid, it will return the error code `(err u2).`. The signature includes 64 bytes plus an additional recovery id (00..03) for a total of 65 bytes. + +**example:** + +``` +(secp256k1-recover? 0xde5b9eb9e7c5592930eb2e30a01369c36586d872082ed8181ee83d2a0ec20f04 0x8738487ebe69b93d8e51583be8eee50bb4213fc49c767d329632730cc193b873554428fc936ca3569afc15f1c9365f6591d6251a89fee9c9ac661116824d3a1301) ;; Returns (ok 0x03adb8de4bfb65db2cfd6120d55c6526ae9c52e675db7e47308636534ba7786110) +``` + +#### secp256k1-verify​ + +Introduced in: **Clarity 1** + +**input:** `(buff 32), (buff 64) | (buff 65), (buff 33)` + +**output:** `bool` + +**signature:** `(secp256k1-verify message-hash signature public-key)` + +**description:** + +The `secp256k1-verify` function verifies that the provided signature of the message-hash was signed with the private key that generated the public key. The `message-hash` is the `sha256` of the message. The signature includes 64 bytes plus an optional additional recovery id (00..03) for a total of 64 or 65 bytes. + +**example:** + +``` +(secp256k1-verify 0xde5b9eb9e7c5592930eb2e30a01369c36586d872082ed8181ee83d2a0ec20f04 0x8738487ebe69b93d8e51583be8eee50bb4213fc49c767d329632730cc193b873554428fc936ca3569afc15f1c9365f6591d6251a89fee9c9ac661116824d3a1301 0x03adb8de4bfb65db2cfd6120d55c6526ae9c52e675db7e47308636534ba7786110) ;; Returns true(secp256k1-verify 0xde5b9eb9e7c5592930eb2e30a01369c36586d872082ed8181ee83d2a0ec20f04 0x8738487ebe69b93d8e51583be8eee50bb4213fc49c767d329632730cc193b873554428fc936ca3569afc15f1c9365f6591d6251a89fee9c9ac661116824d3a13 0x03adb8de4bfb65db2cfd6120d55c6526ae9c52e675db7e47308636534ba7786110) ;; Returns true(secp256k1-verify 0x0000000000000000000000000000000000000000000000000000000000000000 0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0x03adb8de4bfb65db2cfd6120d55c6526ae9c52e675db7e47308636534ba7786110) ;; Returns false +``` + +#### sha256​ + +Introduced in: **Clarity 1** + +**input:** `buff|uint|int` + +**output:** `(buff 32)` + +**signature:** `(sha256 value)` + +**description:** + +The `sha256` function computes `SHA256(x)` of the inputted value. If an integer (128 bit) is supplied the hash is computed over the little-endian representation of the integer. + +**example:** + +``` +(sha256 0) ;; Returns 0x374708fff7719dd5979ec875d56cd2286f6d3cf7ec317a3b25632aab28ec37bb +``` + +#### sha512​ + +Introduced in: **Clarity 1** + +**input:** `buff|uint|int` + +**output:** `(buff 64)` + +**signature:** `(sha512 value)` + +**description:** + +The `sha512` function computes `SHA512(x)` of the inputted value. If an integer (128 bit) is supplied the hash is computed over the little-endian representation of the integer. + +**example:** + +``` +(sha512 1) ;; Returns 0x6fcee9a7b7a7b821d241c03c82377928bc6882e7a08c78a4221199bfa220cdc55212273018ee613317c8293bb8d1ce08d1e017508e94e06ab85a734c99c7cc34 +``` + +#### sha512/256​ + +Introduced in: **Clarity 1** + +**input:** `buff|uint|int` + +**output:** `(buff 32)` + +**signature:** `(sha512/256 value)` + +**description:** + +The `sha512/256` function computes `SHA512/256(x)` (the SHA512 algorithm with the 512/256 initialization vector, truncated to 256 bits) of the inputted value. If an integer (128 bit) is supplied the hash is computed over the little-endian representation of the integer. + +**example:** + +``` +(sha512/256 1) ;; Returns 0x515a7e92e7c60522db968d81ff70b80818fc17aeabbec36baf0dda2812e94a86 +``` + +#### slice?​ + +Introduced in: **Clarity 2** + +**input:** `sequence_A, uint, uint` + +**output:** `(optional sequence_A)` + +**signature:** `(slice? sequence left-position right-position)` + +**description:** + +The `slice?` function attempts to return a sub-sequence of that starts at `left-position` (inclusive), and ends at `right-position` (non-inclusive). If `left_position`==`right_position`, the function returns an empty sequence. If either `left_position` or `right_position` are out of bounds OR if `right_position` is less than `left_position`, the function returns `none`. + +**example:** + +``` +(slice? "blockstack" u5 u10) ;; Returns (some "stack")(slice? (list 1 2 3 4 5) u5 u9) ;; Returns none(slice? (list 1 2 3 4 5) u3 u4) ;; Returns (some (4))(slice? "abcd" u1 u3) ;; Returns (some "bc")(slice? "abcd" u2 u2) ;; Returns (some "")(slice? "abcd" u3 u1) ;; Returns none +``` + +#### some​ + +Introduced in: **Clarity 1** + +**input:** `A` + +**output:** `(optional A)` + +**signature:** `(some value)` + +**description:** + +The `some` function constructs a `optional` type from the input value. + +**example:** + +``` +(some 1) ;; Returns (some 1)(is-none (some 2)) ;; Returns false +``` + +#### sqrti​ + +Introduced in: **Clarity 1** + +**input:** `int | uint` + +**output:** `int | uint` + +**signature:** `(sqrti n)` + +**description:** + +Returns the largest integer that is less than or equal to the square root of `n`.\ +Fails on a negative numbers. + +**example:** + +``` +(sqrti u11) ;; Returns u3(sqrti 1000000) ;; Returns 1000(sqrti u1) ;; Returns u1(sqrti 0) ;; Returns 0 +``` + +#### string-to-int?​ + +Introduced in: **Clarity 2** + +**input:** `(string-ascii 1048576) | (string-utf8 262144)` + +**output:** `(optional int)` + +**signature:** `(string-to-int? (string-ascii|string-utf8))` + +**description:** + +Converts a string, either `string-ascii` or `string-utf8`, to an optional-wrapped signed integer. If the input string does not represent a valid integer, then the function returns `none`. Otherwise it returns an integer wrapped in `some`. + +Note: This function is only available starting with Stacks 2.1. + +**example:** + +``` +(string-to-int? "1") ;; Returns (some 1)(string-to-int? u"-1") ;; Returns (some -1)(string-to-int? "a") ;; Returns none +``` + +#### string-to-uint?​ + +Introduced in: **Clarity 2** + +**input:** `(string-ascii 1048576) | (string-utf8 262144)` + +**output:** `(optional uint)` + +**signature:** `(string-to-uint? (string-ascii|string-utf8))` + +**description:** + +Converts a string, either `string-ascii` or `string-utf8`, to an optional-wrapped unsigned integer. If the input string does not represent a valid integer, then the function returns `none`. Otherwise it returns an unsigned integer wrapped in `some`. + +Note: This function is only available starting with Stacks 2.1. + +**example:** + +``` +(string-to-uint? "1") ;; Returns (some u1)(string-to-uint? u"1") ;; Returns (some u1)(string-to-uint? "a") ;; Returns none +``` + +#### stx-account​ + +Introduced in: **Clarity 2** + +**input:** `principal` + +**output:** `(tuple (locked uint) (unlock-height uint) (unlocked uint))` + +**signature:** `(stx-account owner)` + +**description:** + +`stx-account` is used to query the STX account of the `owner` principal. + +This function returns a tuple with the canonical account representation for an STX account. This includes the current amount of unlocked STX, the current amount of locked STX, and the unlock height for any locked STX, all denominated in microstacks. + +**example:** + +``` +(stx-account 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR) ;; Returns (tuple (locked u0) (unlock-height u0) (unlocked u0))(stx-account (as-contract tx-sender)) ;; Returns (tuple (locked u0) (unlock-height u0) (unlocked u1000)) +``` + +#### stx-burn?​ + +Introduced in: **Clarity 1** + +**input:** `uint, principal` + +**output:** `(response bool uint)` + +**signature:** `(stx-burn? amount sender)` + +**description:** + +`stx-burn?` decreases the `sender` principal's STX holdings by `amount`, specified in microstacks, by destroying the STX. The `sender` principal _must_ be equal to the current context's `tx-sender`. + +This function returns (ok true) if the transfer is successful. In the event of an unsuccessful transfer it returns one of the following error codes: + +`(err u1)` -- `sender` does not have enough balance to transfer `(err u3)` -- amount to send is non-positive `(err u4)` -- the `sender` principal is not the current `tx-sender` + +**example:** + +``` +(as-contract (stx-burn? u60 tx-sender)) ;; Returns (ok true)(as-contract (stx-burn? u50 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR)) ;; Returns (err u4) +``` + +#### stx-get-balance​ + +Introduced in: **Clarity 1** + +**input:** `principal` + +**output:** `uint` + +**signature:** `(stx-get-balance owner)` + +**description:** + +`stx-get-balance` is used to query the STX balance of the `owner` principal. + +This function returns the STX balance, in microstacks (1 STX = 1,000,000 microstacks), of the `owner` principal. In the event that the `owner` principal isn't materialized, it returns 0. + +**example:** + +``` +(stx-get-balance 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR) ;; Returns u0(stx-get-balance (as-contract tx-sender)) ;; Returns u1000 +``` + +#### stx-transfer-memo?​ + +Introduced in: **Clarity 2** + +**input:** `uint, principal, principal, buff` + +**output:** `(response bool uint)` + +**signature:** `(stx-transfer-memo? amount sender recipient memo)` + +**description:** + +`stx-transfer-memo?` is similar to `stx-transfer?`, except that it adds a `memo` field. + +This function returns (ok true) if the transfer is successful, or, on an error, returns the same codes as `stx-transfer?`. + +**example:** + +``` +(as-contract (stx-transfer-memo? u60 tx-sender 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 0x010203)) ;; Returns (ok true) +``` + +#### stx-transfer?​ + +Introduced in: **Clarity 1** + +**input:** `uint, principal, principal, buff` + +**output:** `(response bool uint)` + +**signature:** `(stx-transfer? amount sender recipient)` + +**description:** + +`stx-transfer?` is used to increase the STX balance for the `recipient` principal by debiting the `sender` principal by `amount`, specified in microstacks. The `sender` principal _must_ be equal to the current context's `tx-sender`. + +This function returns (ok true) if the transfer is successful. In the event of an unsuccessful transfer it returns one of the following error codes: + +`(err u1)` -- `sender` does not have enough balance to transfer `(err u2)` -- `sender` and `recipient` are the same principal `(err u3)` -- amount to send is non-positive `(err u4)` -- the `sender` principal is not the current `tx-sender` + +**example:** + +``` +(as-contract (stx-transfer? u60 tx-sender 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR)) ;; Returns (ok true)(as-contract (stx-transfer? u60 tx-sender 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR)) ;; Returns (ok true)(as-contract (stx-transfer? u50 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR tx-sender)) ;; Returns (err u4) +``` + +#### to-consensus-buff?​ + +Introduced in: **Clarity 2** + +**input:** `any` + +**output:** `(optional buff)` + +**signature:** `(to-consensus-buff? value)` + +**description:** + +`to-consensus-buff?` is a special function that will serialize any Clarity value into a buffer, using the SIP-005 serialization of the Clarity value. Not all values can be serialized: some value's consensus serialization is too large to fit in a Clarity buffer (this is because of the type prefix in the consensus serialization). + +If the value cannot fit as serialized into the maximum buffer size, this returns `none`, otherwise, it will be `(some consensus-serialized-buffer)`. During type checking, the analyzed type of the result of this method will be the maximum possible consensus buffer length based on the inferred type of the supplied value. + +**example:** + +``` +(to-consensus-buff? 1) ;; Returns (some 0x0000000000000000000000000000000001)(to-consensus-buff? u1) ;; Returns (some 0x0100000000000000000000000000000001)(to-consensus-buff? true) ;; Returns (some 0x03)(to-consensus-buff? false) ;; Returns (some 0x04)(to-consensus-buff? none) ;; Returns (some 0x09)(to-consensus-buff? 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR) ;; Returns (some 0x051fa46ff88886c2ef9762d970b4d2c63678835bd39d)(to-consensus-buff? { abc: 3, def: 4 }) ;; Returns (some 0x0c00000002036162630000000000000000000000000000000003036465660000000000000000000000000000000004) +``` + +#### to-int​ + +Introduced in: **Clarity 1** + +**input:** `uint` + +**output:** `int` + +**signature:** `(to-int u)` + +**description:** + +Tries to convert the `uint` argument to an `int`. Will cause a runtime error and abort if the supplied argument is >= `pow(2, 127)` + +**example:** + +``` +(to-int u238) ;; Returns 238 +``` + +#### to-uint​ + +Introduced in: **Clarity 1** + +**input:** `int` + +**output:** `uint` + +**signature:** `(to-uint i)` + +**description:** + +Tries to convert the `int` argument to a `uint`. Will cause a runtime error and abort if the supplied argument is negative. + +**example:** + +``` +(to-uint 238) ;; Returns u238 +``` + +#### try + +Introduced in: **Clarity 1** + +**input:** `(optional A) | (response A B)` + +**output:** `A` + +**signature:** `(try! option-input)` + +**description:** + +The `try!` function attempts to 'unpack' the first argument: if the argument is an option type, and the argument is a `(some ...)` option, `try!` returns the inner value of the option. If the argument is a response type, and the argument is an `(ok ...)` response, `try!` returns the inner value of the `ok`. If the supplied argument is either an `(err ...)` or a `none` value, `try!` _returns_ either `none` or the `(err ...)` value from the current function and exits the current control-flow. + +**example:** + +``` +(define-map names-map { name: (string-ascii 12) } { id: int })(map-set names-map { name: "blockstack" } { id: 1337 })(try! (map-get? names-map { name: "blockstack" })) ;; Returns (tuple (id 1337))(define-private (checked-even (x int)) (if (is-eq (mod x 2) 0) (ok x) (err false)))(define-private (double-if-even (x int)) (ok (* 2 (try! (checked-even x)))))(double-if-even 10) ;; Returns (ok 20)(double-if-even 3) ;; Returns (err false) +``` + +#### tuple​ + +Introduced in: **Clarity 1** + +**input:** `(key-name A), (key-name-2 B), ...` + +**output:** `(tuple (key-name A) (key-name-2 B) ...)` + +**signature:** `(tuple (key0 expr0) (key1 expr1) ...)` + +**description:** + +The `tuple` special form constructs a typed tuple from the supplied key and expression pairs. A `get` function can use typed tuples as input to select specific values from a given tuple. Key names may not appear multiple times in the same tuple definition. Supplied expressions are evaluated and associated with the expressions' paired key name. + +There is a shorthand using curly brackets of the form {key0: expr0, key1: expr, ...} + +**example:** + +``` +(tuple (name "blockstack") (id 1337)) ;; using tuple {name: "blockstack", id: 1337} ;; using curly brackets +``` + +#### unwrap + +Introduced in: **Clarity 1** + +**input:** `(optional A) | (response A B), C` + +**output:** `A` + +**signature:** `(unwrap! option-input thrown-value)` + +**description:** + +The `unwrap!` function attempts to 'unpack' the first argument: if the argument is an option type, and the argument is a `(some ...)` option, `unwrap!` returns the inner value of the option. If the argument is a response type, and the argument is an `(ok ...)` response, `unwrap!` returns the inner value of the `ok`. If the supplied argument is either an `(err ...)` or a `(none)` value, `unwrap!` _returns_ `thrown-value` from the current function and exits the current control-flow. + +**example:** + +``` +(define-map names-map { name: (string-ascii 12) } { id: int })(map-set names-map { name: "blockstack" } { id: 1337 })(define-private (get-name-or-err (name (string-ascii 12))) (let ((raw-name (unwrap! (map-get? names-map { name: name }) (err 1)))) (ok raw-name)))(get-name-or-err "blockstack") ;; Returns (ok (tuple (id 1337)))(get-name-or-err "non-existant") ;; Returns (err 1) +``` + +#### unwrap-err + +Introduced in: **Clarity 1** + +**input:** `(response A B), C` + +**output:** `B` + +**signature:** `(unwrap-err! response-input thrown-value)` + +**description:** + +The `unwrap-err!` function attempts to 'unpack' the first argument: if the argument is an `(err ...)` response, `unwrap-err!` returns the inner value of the `err`. If the supplied argument is an `(ok ...)` value, `unwrap-err!` _returns_ `thrown-value` from the current function and exits the current control-flow. + +**example:** + +``` +(unwrap-err! (err 1) false) ;; Returns 1 +``` + +#### unwrap-err-panic​ + +Introduced in: **Clarity 1** + +**input:** `(response A B)` + +**output:** `B` + +**signature:** `(unwrap-err-panic response-input)` + +**description:** + +The `unwrap-err` function attempts to 'unpack' the first argument: if the argument is an `(err ...)` response, `unwrap` returns the inner value of the `err`. If the supplied argument is an `(ok ...)` value, `unwrap-err` throws a runtime error, aborting any further processing of the current transaction. + +**example:** + +``` +(unwrap-err-panic (err 1)) ;; Returns 1(unwrap-err-panic (ok 1)) ;; Throws a runtime exception +``` + +#### unwrap-panic​ + +Introduced in: **Clarity 1** + +**input:** `(optional A) | (response A B)` + +**output:** `A` + +**signature:** `(unwrap-panic option-input)` + +**description:** + +The `unwrap` function attempts to 'unpack' its argument: if the argument is an option type, and the argument is a `(some ...)` option, this function returns the inner value of the option. If the argument is a response type, and the argument is an `(ok ...)` response, it returns the inner value of the `ok`. If the supplied argument is either an `(err ...)` or a `(none)` value, `unwrap` throws a runtime error, aborting any further processing of the current transaction. + +**example:** + +``` +(define-map names-map { name: (string-ascii 12) } { id: int })(map-set names-map { name: "blockstack" } { id: 1337 })(unwrap-panic (map-get? names-map { name: "blockstack" })) ;; Returns (tuple (id 1337))(unwrap-panic (map-get? names-map { name: "non-existant" })) ;; Throws a runtime exception +``` + +#### use-trait​ + +Introduced in: **Clarity 1** + +**input:** `VarName, TraitIdentifier` + +**output:** `Not Applicable` + +**signature:** `(use-trait trait-alias trait-identifier)` + +**description:** + +`use-trait` is used to bring a trait, defined in another contract, to the current contract. Subsequent references to an imported trait are signaled with the syntax ``. + +Traits import are defined with a name, used as an alias, and a trait identifier. Trait identifiers can either be using the sugared syntax (.token-a.token-trait), or be fully qualified ('SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF.token-a.token-trait). + +Like other kinds of definition statements, `use-trait` may only be used at the top level of a smart contract definition (i.e., you cannot put such a statement in the middle of a function body). + +**example:** + +``` +(use-trait token-a-trait 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF.token-a.token-trait)(define-public (forward-get-balance (user principal) (contract )) (begin (ok 1))) +``` + +#### var-get​ + +Introduced in: **Clarity 1** + +**input:** `VarName` + +**output:** `A` + +**signature:** `(var-get var-name)` + +**description:** + +The `var-get` function looks up and returns an entry from a contract's data map. The value is looked up using `var-name`. + +**example:** + +``` +(define-data-var cursor int 6)(var-get cursor) ;; Returns 6 +``` + +#### var-set​ + +Introduced in: **Clarity 1** + +**input:** `VarName, AnyType` + +**output:** `bool` + +**signature:** `(var-set var-name expr1)` + +**description:** + +The `var-set` function sets the value associated with the input variable to the inputted value. The function always returns `true`. + +**example:** + +``` +(define-data-var cursor int 6)(var-get cursor) ;; Returns 6(var-set cursor (+ (var-get cursor) 1)) ;; Returns true(var-get cursor) ;; Returns 7 +``` + +#### xor​ + +Introduced in: **Clarity 1** + +**input:** `int, int | uint, uint | string-ascii, string-ascii | string-utf8, string-utf8 | buff, buff` + +**output:** `int | uint` + +**signature:** `(xor i1 i2)` + +**description:** + +Returns the result of bitwise exclusive or'ing `i1` with `i2`. + +**example:** + +``` +(xor 1 2) ;; Returns 3(xor 120 280) ;; Returns 352 +``` diff --git a/clarity/keywords.md b/clarity/keywords.md new file mode 100644 index 0000000000..f96568274d --- /dev/null +++ b/clarity/keywords.md @@ -0,0 +1,200 @@ +# Keywords + +#### block-height​ + +Introduced in: Clarity 1 + +**output: `uint`** + +**description:** + +Returns the current block height of the Stacks blockchain as an uint + +**example:** + +``` +(> block-height 1000) ;; returns true if the current block-height has passed 1000 blocks. +``` + +#### burn-block-height​ + +Introduced in: Clarity 1 + +**output: `uint`** + +**description:** + +Returns the current block height of the underlying burn blockchain as a uint + +**example:** + +``` +(> burn-block-height 1000) ;; returns true if the current height of the underlying burn blockchain has passed 1000 blocks. +``` + +#### chain-id​ + +Introduced in: Clarity 2 + +**output: `uint`** + +**description:** + +Returns the 32-bit chain ID of the blockchain running this transaction + +**example:** + +``` +(print chain-id) ;; Will print 'u1' if the code is running on mainnet, and 'u2147483648' on testnet, and other values on different chains. +``` + +#### contract-caller​ + +Introduced in: Clarity 1 + +**output: `principal`** + +**description:** + +Returns the caller of the current contract context. If this contract is the first one called by a signed transaction, the caller will be equal to the signing principal. If `contract-call?` was used to invoke a function from a new contract, `contract-caller` changes to the _calling_ contract's principal. If `as-contract` is used to change the `tx-sender` context, `contract-caller` _also_ changes to the same contract principal. + +**example:** + +``` +(print contract-caller) ;; Will print out a Stacks address of the transaction sender +``` + +#### false​ + +Introduced in: Clarity 1 + +**output: `bool`** + +**description:** + +Boolean false constant. + +**example:** + +``` +(and true false) ;; Evaluates to false +(or false true) ;; Evaluates to true +``` + +#### is-in-mainnet​ + +Introduced in: Clarity 2 + +**output: `bool`** + +**description:** + +Returns a boolean indicating whether or not the code is running on the mainnet + +**example:** + +``` +(print is-in-mainnet) ;; Will print 'true' if the code is running on the mainnet +``` + +#### is-in-regtest​ + +Introduced in: Clarity 1 + +**output: `bool`** + +**description:** + +Returns whether or not the code is running in a regression test + +**example:** + +``` +(print is-in-regtest) ;; Will print 'true' if the code is running in a regression test +``` + +#### none​ + +Introduced in: Clarity 1 + +**output: `(optional ?)`** + +**description:** + +Represents the _none_ option indicating no value for a given optional (analogous to a null value). + +**example:** + +``` +(define-public (only-if-positive (a int)) + (if (> a 0) + (some a) + none)) +(only-if-positive 4) ;; Returns (some 4) +(only-if-positive (- 3)) ;; Returns none +``` + +#### stx-liquid-supply​ + +Introduced in: Clarity 1 + +**output: `uint`** + +**description:** + +Returns the total number of micro-STX (uSTX) that are liquid in the system as of this block. + +**example:** + +``` +(print stx-liquid-supply) ;; Will print out the total number of liquid uSTX +``` + +#### true​ + +Introduced in: Clarity 1 + +**output: `bool`** + +**description:** + +Boolean true constant. + +**example:** + +``` +(and true false) ;; Evaluates to false +(or false true) ;; Evaluates to true +``` + +#### tx-sender​ + +Introduced in: Clarity 1 + +**output: `principal`** + +**description:** + +Returns the original sender of the current transaction, or if `as-contract` was called to modify the sending context, it returns that contract principal. + +**example:** + +``` +(print tx-sender) ;; Will print out a Stacks address of the transaction sender +``` + +#### tx-sponsor?​ + +Introduced in: Clarity 2 + +**output: `optional principal`** + +**description:** + +Returns the sponsor of the current transaction if there is a sponsor, otherwise returns None. + +**example:** + +``` +(print tx-sponsor?) ;; Will print out an optional value containing the Stacks address of the transaction sponsor +``` diff --git a/clarity/overview.md b/clarity/overview.md new file mode 100644 index 0000000000..35fc7ed507 --- /dev/null +++ b/clarity/overview.md @@ -0,0 +1,53 @@ +# Overview + +Clarity is a **decidable** smart contract language that optimizes for predictability and security, designed for the Stacks blockchain. Smart contracts allow developers to encode essential business logic on a blockchain. + +The design decisions behind Clarity were based heavily on taking lessons learned in common Solidity exploits and creating a language that has been purpose-built for safety and security in mind. + +These docs serve primarily as a reference for the functions and keywords that you can use in Clarity. + +In order to learn Clarity, we recommend diving into the [Clarity of Mind](https://book.clarity-lang.org/), an online book to teach you everything you need to know to build robust smart contracts, or joining a [Clarity Camp](https://clarity-lang.org/universe#camp), the cohort-based immersive Clarity experience. + +### What makes Clarity different + +The following section is an excerpt from the excellent book, [Clarity of Mind](https://book.clarity-lang.org/ch00-00-introduction.html): + +The number of smart contract languages grows by the year. Choosing a first language can be challenging, especially for a beginner. The choice is largely dictated by the ecosystem you are interested in, although some languages are applicable to more than just one platform. Each language has its own upsides and downsides and it is out of the scope of this book to look at all of them. Instead, we will focus on what sets Clarity apart and why it is a prime choice if you require the utmost security and transparency. + +One of the core precepts of Clarity is that it is secure by design. The design process was guided by examining common pitfalls, mistakes, and vulnerabilities in the field of smart contract engineering as a whole. There are countless real world examples of where developer failure led to the loss or theft of vast amounts of tokens. To name two big ones: an issue that has become known as the Parity bug led to the irreparable loss of millions of dollars worth of Ethereum. Second, the hacking of The DAO (a "Decentralized Autonomous Organization") caused financial damage so great that the Ethereum Foundation decided to issue a contentious hard fork that undid the theft. These and many other mistakes could have been prevented in the design of the language itself. + +#### Clarity is interpreted, not compiled + +Clarity code is interpreted and committed to the chain exactly as written. Solidity and other languages are compiled to byte-code before it is submitted to the chain. The danger of compiled smart contract languages is two-fold: first, a compiler adds a layer of complexity. A bug in the compiler may lead to different byte-code than was intended and thus carries the risk of introducing a vulnerability. Second, byte-code is not human-readable, which makes it very hard to verify what the smart contract is actually doing. Ask yourself, would you sign a contract you cannot read? If your answer is no, then why should it be any different for smart contracts? With Clarity, what you see is what you get. + +#### Clarity is decidable + +A decidable language has the property that from the code itself, you can know with certainty what the program will do. This avoids issues like the halting problem. With Clarity you know for sure that given any input, the program will halt in a finite number of steps. In simple terms: it is guaranteed that program execution will end. Decidability also allows for complete static analysis of the call graph so you get an accurate picture of the exact cost before execution. There is no way for a Clarity call to "run out of gas" in the middle of the call. We explore this idea more, along with a discussion on Turing completeness, in the security deep dive on decidability. + +#### Clarity does not permit reentrancy + +Reentrancy is a situation where one smart contract calls into another, which then calls back into the first contract—the call "re-enters" the same logic. It may allow an attacker to trigger multiple token withdrawals before the contract has had a chance to update its internal balance sheet. Clarity's design considers reentrancy an anti-feature and disallows it on the language level. + +#### Clarity guards against overflow and underflows + +Overflows and underflows happen when a calculation results in a number that is either too large or too small to be stored, respectively. These events throw smart contracts into disarray and may intentionally be triggered in poorly written contracts by attackers. Usually this leads to a situation where the contract is either frozen or drained of tokens. Overflows and underflows of any kind automatically cause a transaction to be aborted in Clarity. + +#### Support for custom tokens is built-in + +Issuance of custom fungible and non-fungible tokens is a popular use-case for smart contracts. Custom token features are built into the Clarity language. Developers do not need to worry about creating an internal balance sheet, managing supply, and emitting token events. Creating custom tokens is covered in depth in later chapters. + +#### On Stacks, transactions are secured by post conditions + +In order to further safeguard user tokens, post conditions can be attached to transactions to assert the chain state has changed in a certain way once the transaction has completed. For example, a user calling into a smart contract may attach a post condition that states that after the call completes, exactly 500 STX should have been transferred from one address to another. If the post condition check fails, then the entire transaction is reverted. Since custom token support is built right into Clarity, post conditions can also be used to guard any other token in the same way. + +#### Returned responses cannot be left unchecked + +Public contract calls must return a so-called response that indicates success or failure. Any contract that calls another contract is required to properly handle the response. Clarity contracts that fail to do so are invalid and cannot be deployed on the network. Other languages like Solidity permit the use of low level calls without requiring the return value to be checked. For example, a token transfer can fail silently if the developer forgets to check the result. In Clarity it is not possible to ignore errors, although that obviously does prevent buggy error handling on behalf of the developer. Responses and error handling are covered extensively in the chapters on functions and control flow. + +#### Composition over inheritance + +Clarity adopts a composition over inheritance. It means that Clarity smart contracts do not inherit from one another like you see in languages like Solidity. Developers instead define traits which are then implemented by different smart contracts. It allows contracts to conform to different interfaces with greater flexibility. There is no need to worry about complex class trees and contracts with implicit inherited behavior. + +#### Access to the base chain: Bitcoin + +Clarity smart contracts can read the state of the Bitcoin base chain. It means you can use Bitcoin transactions as a trigger in your smart contracts! Clarity also features a number of built-in functions to verify secp256k1 signatures and recover keys. diff --git a/clarity/types.md b/clarity/types.md new file mode 100644 index 0000000000..e45502243e --- /dev/null +++ b/clarity/types.md @@ -0,0 +1,19 @@ +# Types + +### Clarity Type System + +The type system contains the following types: + +| Types | Notes | +| ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `int` | signed 128-bit integer | +| `uint` | unsigned 128-bit integer | +| `bool` | boolean value (`true` or `false`) | +| `principal` | object representing a principal (whether a contract principal or standard principal) | +| `(buff max-len)` | byte buffer of maximum length `max-len`. | +| `(string-ascii max-len)` | ASCII string of maximum length `max-len` | +| `(string-utf8 max-len)` | UTF-8 string of maximum length `max-len` (u"A smiley face emoji \u{1F600} as a utf8 string") | +| `(list max-len entry-type)` | list of maximum length `max-len`, with entries of type `entry-type` | +| `{label-0: value-type-0, label-1: value-type-1, ...}` | tuple, group of data values with named fields | +| `(optional some-type)` | an option type for objects that can either be `(some value)` or `none` | +| `(response ok-type err-type)` | object used by public functions to commit their changes or abort. May be returned or used by other functions as well, however, only public functions have the commit/abort behavior. | diff --git a/nakamoto-upgrade/nakamoto-in-10-minutes.md b/nakamoto-upgrade/nakamoto-in-10-minutes.md new file mode 100644 index 0000000000..8002027846 --- /dev/null +++ b/nakamoto-upgrade/nakamoto-in-10-minutes.md @@ -0,0 +1,113 @@ +# Nakamoto in 10 Minutes + +On the previous page, we outlined three primary changes to the way Stacks works that Nakamoto introduces: + +* **Fast blocks:** The time taken for a user-submitted transaction to be mined within a block (and thus confirmed) will now take on the order of seconds, instead of tens of minutes. This is achieved by separating block production from cryptographic sortitions -- a winning miner may produce many blocks between two subsequent sortitions. +* **Bitcoin finality:** Once a transaction is confirmed, reversing it is at least as hard as reversing a Bitcoin transaction. The Stacks blockchain no longer forks on its own. +* **Bitcoin Miner MEV Resistance:** This proposal alters the sortition algorithm to ensure that Bitcoin miners do not have an advantage as Stacks miners. They must spend competitive amounts of Bitcoin currency to have a chance of earning STX. + +Let's briefly go over how Nakamoto accomplishes each of these. + +### Fast Blocks + +One of the most significant changes coming in Nakamoto is how new blocks are produced. Historically, because Stacks blocks have been anchored 1:1 to Bitcoin blocks, slow block times and transaction times have been one of the biggest pain points for Stacks users and developers. + +Nakamoto brings significantly faster block times by decoupling Stacks block production from Bitcoin block production. In Nakamoto, new Stacks blocks are produced roughly every 5 seconds. + +#### Tenure-Based Block Production + +This is achieved via the use of tenure-based block production. Each Bitcoin block introduces a new tenure, in which a single miner cryptographically selected for that tenure is responsible for producing all Stacks blocks. + +Rather than single Stacks blocks being tied to a single Bitcoin block, Bitcoin blocks are now tied to a miner tenure, during which they mine several Stacks blocks which settle in around 5 seconds. + +But if a single miner is only cryptographically selected for their tenure, and not their produced blocks, what mechanisms exist to ensure the validity of their block production? + +#### Stackers + +This is where Stackers come in. In pre-Nakamoto Stacks, Stackers were responsible only for locking their STX tokens to contribute to the economic security of the network. + +In Nakamoto, Stackers are responsible for validating and approving each block produced during a miner's tenure. + +To ensure network consistency, the Stacks protocol commits to the state of the Stacks blockchain with each new Bitcoin block by referencing the first Stacks block produced in the previous tenure. Such a design reinforces the fidelity of transaction data and the synchronization between the two chains. It also links the Stacker’s actions with the actions of miners producing a partnership between the two to create both fast and secure blocks. + +As part of this tenure change, Stackers also agree on a last signed block and require the next miner to build off of this, which prevents new Stacks forks. Stacks does not fork on its own and automatically forks with Bitcoin. + +This symbiotic relationship between Stackers and miners is what creates the capability for both fast blocks and 100% Bitcoin finality. + +This elegant design creates a cooperative relationship between miners and stackers while achieving the best of both worlds with block production and transaction speed and security. + +### Bitcoin Finality + +Speaking of security, the concept of 100% Bitcoin finality is crucial to the design of Stacks. This is what turns Stacks into a true Bitcoin L2 and allows it to leverage all of the security inherent in Bitcoin. + +Let's first define what we mean by 100% Bitcoin finality, then we'll dig into how Nakamoto accomplishes this. + +Finality refers to the point at which transactions are irreversible. Once a blockchain reaches finality, it is nearly impossible to change the ledger's history without undertaking extraordinary measures that are often computationally and economically prohibitive. + +When we talk about Stacks blocks having 100% Bitcoin finality, we mean that they are as hard to reverse as Bitcoin transactions themselves. + +That's a bold claim, so how does Stacks accomplish that? + +As discussed above, miners are responsible for producing Stacks blocks in their tenure, which corresponds to a single Bitcoin block. As part of their block commit transaction, which is the transaction that commits the hash of the Stacks blocks to the Bitcoin chain, miners will also be required to add an indexed block hash. + +Stacks miners are required to commit the indexed block hash of the first block produced by the last Stacks miner in their block-commit transactions on Bitcoin. This is the SHA512/256 hash of both the consensus hash of all previously-accepted Bitcoin transactions that Stacks recognizes, as well as the hash of the block itself. + +This will anchor the Stacks chain history to Bitcoin up to the start of the previous miner's tenure, as well as all causally-dependent Bitcoin state that Stacks has processed. This ensures Bitcoin finality, resolves miner connectivity issues by putting fork prevention on stackers, and allows nodes with up-to-date copies of the Stacks chain state to identify which Stacks blocks are affected by a Bitcoin reorg and recover the affected Stacks transactions. + +### Bitcoin MEV Mitigation + +Miner Extractable Value (MEV) has been a longstanding issue across many blockchains, including Stacks pre-Nakamoto. + +MEV refers to the potential profit miners can extract from the manipulation of transaction inclusion and ordering within the blocks they produce, which can lead to unfair practices and diminished trust in the network. + +Specifically in pre-Nakamoto releases of Stacks, Bitcoin miners with a significant percentage of Bitcoin’s hashrate had the ability to censor commitment transactions of other Stacks miners ensuring they were able to win the block rewards and fees of Stacks blocks where they were also the winner of the Bitcoin block as a Bitcoin miner. + +This allowed them to sometimes win these rewards for minimal bitcoin cost in PoX. This has led to reduction in Stacker BTC rewards and other bad incentives for Stacks mining. As such Nakamoto has changed the approach to sortitions to promote better fairness in the mining process. + +#### Sortition Probability Factors + +With the Nakamoto release, Stacks introduces a series of countermeasures to mitigate the influence of MEV and promote a fairer mining landscape. + +* Miner Participation in Recent Blocks: The update emphasizes a miner's consistent participation within the network, requiring a history of attempts in recent blocks (the last 10) to qualify for sortition. This persistent involvement in at least the last 10 blocks aims to foster a dedicated and stable miner community. +* Median of Past Bids Method: By calculating the winning probability based on the median total BTC bids of the last ten blocks, the system discourages aberrant bidding behavior. This reduces the likelihood of a miner skewing sortition chances through atypical or extreme bids. +* Absolute Bid Total: Inclusion of an absolute measure of bids further strengthens the system's robustness. Rather than having a variable determined by just the immediate mining environment, the absolute bid total keeps the process anchored to a broader and more stable economic baseline. + +These changes ensure that miners are rewarded for their genuine contributions to the network's security and continuity, safeguarding the blockchain from manipulation and encouraging a more equitable distribution of rewards. + +#### MEV Mitigation Details + +The Nakamoto system uses a variation of the Assumed Total Commitment with Carryforward (ATC-C) MEV mitigation strategy described in [this document](https://forum.stacks.org/uploads/short-url/bqIX4EQ5Wgf2PH4UtiZHZBqJvYE.pdf) to allocate block rewards to miners. Unlike pre-Nakamoto Stacks, there is no 40/60 fee split between two consecutive miners. + +Each miner nominally receives the entire coinbase and transaction fees before the MEV mitigation is applied. + +In the ATC-C algorithm, Nakamoto uses the document's recommended assumed total commitment function: the median total PoX spend across all miners for the past ten Bitcoin blocks. + +It also uses the document's recommended carryforward function for missed sortitions' coinbases: the coinbase for a Bitcoin block without a sortition would be available to winning miners across the next ten tenures. That is, if a miner whose tenure begins during the next ten tenure-changes manages to produce a Stacks block with a Coinbase, then they receive a 10% of the coinbase that was lost. + +The reason ATC (and ATC-C) were not considered as viable anti-MEV strategies before is because a decrease in the PoX total spend can lead to a Bitcoin block with no sortition. This is a deliberate design choice in ATC-C, because it has the effect of lowering the expected return of MEV mining. + +In ATC-C, the probability of a miner winning a sortition is equal to (i.e. no longer proportional to) the miner's BTC spend, divided by the maximum of either the assumed total commit (median total BTC spend in the last 10 blocks) or the total BTC spend in this Bitcoin block. This means that the sum of each miners' winning probabilities is not guaranteed to be 1. The system deals with this by creating a virtual "null" miner that participates in each sortition, such that its probability of the null miner winning is 1 - sum(probabilities-of-all-other-miners). If the null miner wins, then the sortition is treated as empty. + +While the existence of a null miner was a liveness concern in pre-Nakamoto Stacks, it is not a concern in Nakamoto. If the null miner wins tenure N, then the last non-null miner continues to produce blocks in tenure N. They receive transaction fees, but no coinbase for tenure N. + +Nakamoto includes one additional change to ATC-C as described in the above report: if a miner does not mine in at least five of the ten prior Bitcoin blocks, it has zero chance of winning. This requires a Bitcoin MEV miner to participate as an honest miner for the majority of blocks it produces, such that even if they pay the bare minimum PoX payout each time, they are still paying Bitcoin transaction fees to other miners. + +#### Example + +The need for this additional tweak becomes apparent when considering the consequences for a real Bitcoin MEV miner who was active in pre-Nakamoto Stacks: F2Pool. + +Consider what happens to F2Pool, who spends 200 sats on PoX and zero sats on transaction fees for their block-commit. Suppose the median total BTC spend over the last ten Bitcoin blocks was 500,000 sats (about what it is at the time of this writing). + +With ATC-C alone, their probability of winning the sortition would be 200 / max(500,000, 200), or about 0.04% chance. The miner would need to send 2,500 such block-commits before winning a Stacks coinbase (worth about 500 USD). + +F2Pool had 13.89% of Bitcoin's mining power over the past three months, so it would take them about 4 months to win a single STX coinbase (which is a very long time horizon). Right now, it costs 22 sats/vbyte to get a Bitcoin transaction mined in the next Bitcoin block; this is what Stacks miners pay. + +A block-commit tx is about 250 vbytes, so that's 5500 sats, or about 1.41 USD with today's BTC price. So, F2Pool would lose money by MEV mining at their current rate if prices stay the same over those 4 months -- they'd forfeit about 3,525 USD in Bitcoin transaction fees (lost by excluding other Bitcoin transactions in order to include their block-commit) for a Stacks coinbase worth 500 USD. They'd have to pay about 1410 sats per block-commit just to break even, and they'd only recoup their investment on average once every 4 months. + +This by itself is not a significant improvement -- F2Pool would be required to go from paying 200 sats to 1410 sats. However, with this proposal's tweek, F2Pool would be required to additionally win five Bitcoin blocks in a row in order to mine this cheaply. + +Given that they have 13.89% of the mining power today, the odds of this happening by chance are only 0.005%. Since this is unlikely -- about once every 20,000 Bitcoin blocks (once every 138.9 days) -- F2Pool would instead be required to send legitimate block-commit transactions in at least 50% of the Bitcoin blocks. + +In 87.11% of those, they would be paying the same transaction fees as every other Stacks miner. This alone would cost them $106.13 USD/day. With the additional de minimis PoX payout, this rises to $212.25 USD/day. In other words, they would expect to pay $29,481.51 USD just to be able to mine one Stacks block for a de minimis PoX payout. This is more expensive than mining honestly! + +If the largest Bitcoin mining pool -- Foundry USA, at 30% of the Bitcoin mining power -- wanted to become a Bitcoin MEV miner on Stacks, then the given parameter choice still renders this unprofitable. There is a 0.243% chance that they win five blocks in a row, and can thus mine a de-minimis block-commit and be guaranteed to win. This happens about once every 2.85 days (every 411.5 Bitcoin blocks), so they'd be spending about $604.91 USD just to mine one Stacks block for free (which is not profitable either). diff --git a/nakamoto-upgrade/nakamoto-in-depth/README.md b/nakamoto-upgrade/nakamoto-in-depth/README.md new file mode 100644 index 0000000000..179dad99af --- /dev/null +++ b/nakamoto-upgrade/nakamoto-in-depth/README.md @@ -0,0 +1,12 @@ +# Nakamoto In-Depth + +The pages within this section will describe how Nakamoto works in-depth. Much of this content has been pulled and adapted from the SIP, which lays out the entire technical specification of Nakamoto. + +This section is broken up into the following sub-pages. + +* Stackers and Signing +* Chain Structure and Synchronization +* Block Structure and Validation +* Transactions +* Changes to PoX and Clarity +* Financial Incentives and Security Budget diff --git a/nakamoto-upgrade/nakamoto-in-depth/block-structure-and-validation.md b/nakamoto-upgrade/nakamoto-in-depth/block-structure-and-validation.md new file mode 100644 index 0000000000..cbcbc6b235 --- /dev/null +++ b/nakamoto-upgrade/nakamoto-in-depth/block-structure-and-validation.md @@ -0,0 +1,59 @@ +# Block Structure and Validation + +#### Block Header + +Nakamoto utilizes tenure changes as a consensus critical artifact that links sortitions to the chosen miner as well as removes the support for microblocks. As such, the following is the updated block header to support the features in this proposal and remove support for those being deprecated: + +The Nakamoto block header wire format is as follows. + +| Name | Description | Representation | +| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------- | +| version | Version number to describe how to validate the block. | 1 byte | +| chain length | The total number of StacksBlock and NakamotoBlocks preceding this block in this block's history. | 8 bytes, big-endian | +| burn spent | Total amount of BTC spent producing the sortition that selected the miner whose tenure produced this block. | 8 bytes, big-endian | +| consensus hash | The consensus hash of the burnchain block that selected this tenure. The consensus hash uniquely identifies this tenure, including across all Bitcoin forks. | 20 bytes | +| parent block ID | The index block hash of the immediate parent of this block. This is the hash of the parent block's hash and consensus hash. | 32 bytes | +| transaction Merkle root | The SHA512/256 root hash of the binary Merkle tree calculated over the sequence of transactions in this block. | 32 bytes | +| state index Merkle root | The SHA512/256 root hash of the MARF once all of the contained transactions are processed. | 32 bytes | +| miner signature | Recoverable ECDSA signature from the tenure's miner. | 65 bytes | +| stacker signature | A Schnorr signature collectively generated by the set of Stackers over this block | 65 bytes | + +**Total bytes:** 275 + +Absent from this header is the VRF proof, because it only needs to be included once per tenure. Instead, this information will be put into the updated Nakamoto Coinbase transaction, which has a different wire format than the current Coinbase transaction. + +#### Maximum Block Size + +The limit on Stacks block size is 16 MB. + +The 16 MB limit is the largest-allowed network message size (in bytes, as it is encoded on the wire). If you mine a block that is 16.001 MB and try to broadcast it or serve it, no one will be able to receive or download it. + +### New Block Validation Rules + +In Nakamoto, a block is valid if and only if the following are true: + +* The block is well-formed + * It has the correct version and mainnet/testnet flag + * **(NEW)** Its header contains the right number of Stacks blocks preceding this one. + * **(NEW)** Its header contains the correct total Bitcoin spent in the sortition that elected the current tenure. + * **(NEW)** Its header contains the same Bitcoin block hash as the Bitcoin block that contains its tenure's block-commit transaction + * Its header contains the correct parent block ID of the immediate parent of this block. + * The transaction Merkle tree root is consistent with the transactions + * The state root hash matches the MARF tip root hash once all transactions are applied + * **(NEW)** the block header has a valid ECDSA signature from the miner + * **(NEW)** the block header has a valid WSTS Schnorr signature from the set of Stackers +* **(NEW)** All Bitcoin transactions since the last valid sortition up to (but not including) this tenure's block-commit’s Bitcoin block have been applied to the Stacks chain state +* In the case of a tenure start block: + * **(NEW)** The first transaction is the `TenureChange` transaction. + * **(NEW)** The first transaction after the `TenureChange` transaction is a `Coinbase`. +* All transactions either run to completion, or fail due to runtime errors. That is: + * The transaction is well-formed + * All transactions' senders and sponsors are able to pay the transaction fee + * The runtime budget for the tenure is not exceeded + * **(NEW)** The total runtime budget is equal to the runtime budget for one tenure, multiplied by the number of valid `TenureExtension` transactions mined in this tenure. + * No expression exceeds the maximum nesting depth + * No supertype is too large + * **(NEW)** The `PoisonMicroblock` transaction variant is no longer supported + * **(NEW)** The current `Coinbase` transaction variant is no longer supported + +In addition to the new `TenureChange` transaction, Nakamoto changes coinbase transactions to include VRF proof for the current tenure. As stated above, the existing Coinbase transaction variant is no longer supported. diff --git a/nakamoto-upgrade/nakamoto-in-depth/chain-structure-and-synchronization.md b/nakamoto-upgrade/nakamoto-in-depth/chain-structure-and-synchronization.md new file mode 100644 index 0000000000..21fcda8a3d --- /dev/null +++ b/nakamoto-upgrade/nakamoto-in-depth/chain-structure-and-synchronization.md @@ -0,0 +1,28 @@ +# Chain Structure and Synchronization + +### Chain Structure + +The Nakamoto Stacks chain is a linearized history of blocks without forks. Miners create blocks at a fast cadence (on the order of seconds), they send them to signers for validation, and if signers reach at least 70% quorum on the block, then the block is replicated to the rest of the peer network. The process repeats until the next cryptographic sortition chooses a different miner to produce blocks. + +Miners submit their candidacy to produce blocks by sending a block-commit transaction on the Bitcoin chain. Nakamoto alters the semantics of the block-commit in one key way: the block\_header\_hash field is no longer the hash of a proposed Stacks block (i.e. its BlockHeaderHash), but instead is the index block hash (i.e. StacksBlockId) of the previous miner's first-ever produced block. + +By altering the block-commit to expect the first block from the previous tenure, we make the system resilient to high network latency. Miner candidates will have approximately the time between Bitcoin blocks to obtain and process the previous miner’s blocks and submit a valid block-commit for the current Stacks tip. + +
+ +_Figure 1: Overview of the relationship between Bitcoin blocks (and sortitions), Stacks blocks, and the inventory bitmaps exchanged by Stacks nodes. Each winning block-commit's BlockHeaderHash field no longer refers to a new Stacks block to be appended, but instead contains the index block hash of the very first Stacks block in the previous tenure. Signers force the miner to build upon the last signed Stacks block in the previous tenure by refusing to sign blocks that don’t build upon the most recently signed block. These tenure start blocks each contain a TenureChange transaction (not shown), which, among other things, identifies the number of Stacks blocks produced since the last valid start block (numbers in dark orange circles)._ + +This is to both preserve Bitcoin finality and to facilitate initial block downloads without significantly altering the inventory state synchronization and block downloader state machines. Bitcoin finality is preserved because at every Bitcoin block N+1, the state of the Stacks chain as of the start of tenure N is written to Bitcoin. Even if at a future date all of the former Stackers' signing keys were compromised, they would be unable to rewrite Stacks history for tenure N without rewriting Bitcoin history back to sortition N+1. + +### Chain Synchronization + +Nodes do the following when they have all of the Stacks chain state up to reward cycle R: + +1. **Download and process all sortitions in reward cycle R+1.** The node downloads the Bitcoin blocks, identifies the valid block-commits within them, and runs sortition on each Bitcoin block's block-commits to choose a winner. It does this on a reward-cycle by reward-cycle basis, since it must first process the PoX anchor block for the next reward cycle before it can validate the next reward cycle's block-commits. +2. **For each sortition N+1, go and fetch the start block of tenure N if sortition N+1 has a valid block-commit and the inventory bit for tenure N is 1.** Each neighbor node serves the node an inventory bitmap of all tenure start blocks they have available, which enables the node to identify neighbors that have the blocks they need. Only the inventory bitmap of tenure start blocks is needed; there is no longer a need for a PoX anchor block bitmap nor a microblock bitmap. +3. **For each start block of tenure N, identify the number of blocks** between this start block and the last prior tenure committed to by a winning block-commit (note that this may not always be tenure N-1). This information is identified by a special TenureChange transaction that must be included in each tenure start block (see next section). So, the act of fetching the tenure-start blocks in step 2 is the act of obtaining these TenureChange transactions. +4. **Download and validate the continuity of each block sequence between consecutive block commits.** Now that the node knows the number of blocks between two consecutive winning block-commits, as well as the hashes of the first and last block in this sequence, the node can do this in a bounded amount of space and time. There is no risk of a malicious node serving an endless stream of well-formed but invalid blocks to a booting-up node, because the booting-up node knows exactly how many blocks to expect and knows what hashes they must have. +5. **Concurrently processes newly-downloaded blocks in reward cycle R+1** to build up its tenure of the blockchain. +6. **Repeat once the PoX anchor block for R+2 has been downloaded and processed** + +When the node has synchronized to the latest reward cycle, it would run this algorithm to discover new tenures within the current reward cycle until it reaches the chain tip. Once it has processed all blocks as of the last sortition, it continues to keep pace with the current miner by receiving new blocks broadcasted through the peer network by Stackers once they accept the next Stacks block. diff --git a/nakamoto-upgrade/nakamoto-in-depth/changes-to-pox-and-clarity.md b/nakamoto-upgrade/nakamoto-in-depth/changes-to-pox-and-clarity.md new file mode 100644 index 0000000000..4046ae049a --- /dev/null +++ b/nakamoto-upgrade/nakamoto-in-depth/changes-to-pox-and-clarity.md @@ -0,0 +1,68 @@ +# Changes to PoX and Clarity + +### **PoX Contract Changes** + +**PoX Failure** + +In the event that PoX does not activate, the chain halts. If there are no Stackers, then block production cannot happen. + +**Changes to PoX** + +To support tenure changes, Nakamoto uses a new PoX contract, `.pox-4`. The `.pox-4` contract would be altered over the current PoX contract (`.pox-3`) to meet the following requirements: + +* Stackers register a WSTS signing key when they call `stack-stx` or a delegate provides the signing key with `delegate-stack-stx`. + +In addition, a `.stackers` boot contracts will be created which carries out the following tasks: + +* The `.stackers` contract will expose each reward cycle's full reward set, including the signing keys for each stacker, via a public function. Internally, the Stacks node will call a private function to load the next reward set into the `.stackers` data space after it identifies the PoX anchor block. This is required for some future Stacks features that have been discussed. +* The `.stackers` contract will expose two private functions to update the signing set for the following reward cycle. + * `update-signer-set`: will update the reward set for the upcoming reward cycle. It will take in a list of at maximum size 4096 of signer pubkeys. + * `update-current-signer`: will update the the specified signer with their corresponding key-ids. +* The `.stackers` contract will expose a function to vote on the aggregate public key used by the Stackers to sign new blocks in the current tenure. + * `vote-for-aggregated-public-key` takes the key of the signer calling the contract, the reward cycle number, the round number, and the list of all tapleaves. + +### New Stacks-on-Chain Rules + +The `stack-stx` Stacks-on-Chain transaction will be extended to include the Stacker’s signing key, as follows: + +``` + 0 2 3 19 20 53 + |------|--|---------------|-----------|------------| + magic op uSTX to lock num-cycles signing key +``` + +The new field, `signing key`, will contain the compressed secp256k1 ECDSA public key for the stacker. + +In addition, the following two new Stacks-on-Chain transactions are added: + +#### Delegate-Stack-STX + +In Nakamoto, it will now be possible for a stacking delegate to lock up their delegated STX via a Bitcoin transaction. It shall contain an OP\_RETURN with the following payload: + +``` + 0 2 3 19 20 21 25 + |------|--|---------------|-----------|--------------|----------| + magic op uSTX to lock num-cycles has-pox-addr? index +``` + +Where `op` = `+`, `uSTX to lock` is the number of microSTX to lock up, `num-cycles` is the number of reward cycles to lock for. The field `has-pox-addr?` can be `0x00` or `0x01`. If it is `0x01`, then `index` is treated as a 4-byte big-endian integer, and is used as an index into the transaction outputs to identify the PoX address the delegate must use. The value `0` refers to the first output after the OP\_RETURN. The output’s `scriptPubKey` is then decoded and converted to the PoX address. If `index` points to a nonexistent transaction output or if `scriptPubKey` cannot be decoded into a PoX address, then this transaction is treated as invalid and has no effect. If `has-pox-addr?` is `0x00` instead, then index is not decoded and the delegate may choose the PoX address. + +**Stack-Aggregation-Commit** + +In Nakamoto, it will now be possible for a stacking delegate to commit delegated STX behind a PoX address. It shall contain an OP\_RETURN with the following payload: + +``` + 0 2 3 7 11 + |------|--|-------|-----------| + magic op index reward cycle +``` + +Where `op` = `*`, and as with `delegate-stack-stx` above, `index` is a big-endian 4-byte integer which points to a transaction output whose `scriptPubKey` must decode to a supported PoX address. The `reward cycle` field is a 4 byte big-endian integer which identifies the `reward cycle` for which to aggregate the stacked STX. + +### Changes to Clarity + +Nakamoto produces Stacks blocks occur at a much greater frequency than in the past. Many contracts rely on the `block-height` primitive to approximate a time assuming that a block takes, on average, 10 minutes. To release faster blocks while preserving the functionality of existing contracts that make this block frequency assumption, Nakamoto will use a new version of Clarity, version 3, which includes the following changes. + +1. A new primitive `stacks-block-height` that indicates the block height under Nakamoto +2. A new Clarity global variable `tenure-height` will be introduced, which evaluates to the number of tenures that have passed. When the Nakamoto block-processing starts, this will be equal to the chain length. +3. The clarity global variable block-height will continue to be supported in the existing Clarity 2 contracts by returning the same value as `tenure-height`, but usage of block-height in a Clarity 3 contract will trigger an analysis error. diff --git a/nakamoto-upgrade/nakamoto-in-depth/financial-incentives-and-security-budget.md b/nakamoto-upgrade/nakamoto-in-depth/financial-incentives-and-security-budget.md new file mode 100644 index 0000000000..e5c1ad2300 --- /dev/null +++ b/nakamoto-upgrade/nakamoto-in-depth/financial-incentives-and-security-budget.md @@ -0,0 +1,11 @@ +# Financial Incentives and Security Budget + +Miners remain incentivized to mine blocks because they earn STX by spending BTC. This dynamic is not affected by Nakamoto changes. + +Stackers have the new-found power to sign blocks in order to append them to the Stacks chain. However, some of them could refuse to sign, and ensure that no block ever reaches the 70% signature threshold. While this can happen by accident, this is not economically rational behavior -- if they stall the chain for too long, their STX loses their value, and furthermore, they cannot re-stack or liquidate their STX or activate PoX to earn BTC. Also, miners will stop mining if no blocks are getting confirmed, which eliminates their ongoing PoX payouts. + +Stackers may refuse to sign blocks that contain transactions they do not like, for various reasons. In the case of `stack-stx`, `delegate-stx`, and `transfer-stx`, users have the option to _force_ Stackers to accept the block by sending these transactions as Bitcoin transactions. Then, all subsequently-mined blocks must include these transactions in order to be valid. This forces Stackers to choose between signing the block and stalling the network forever. + +Stackers who do not wish to be in this position should evaluate whether or not to continue Stacking. Furthermore, Stackers may delegate their signing authority to a third party if they feel that they cannot participate directly in block signing. + +That all said, the security budget of the chain is considerably larger in Nakamoto than before. In order to reorg the Stacks chain, someone must take control of at least 70% of the STX that are currently Stacked. If acquired at market prices, then at the time of this writing, that amounts to spending about $191 million USD. By contrast, Stacks miners today spend a few hundred USD per Bitcoin block to mine a Stacks block. Reaching the same economic resistance to reorgs provided by a signature from 70% of all stacked STX would take considerably longer. diff --git a/nakamoto-upgrade/nakamoto-in-depth/stackers-and-signing.md b/nakamoto-upgrade/nakamoto-in-depth/stackers-and-signing.md new file mode 100644 index 0000000000..06bda08252 --- /dev/null +++ b/nakamoto-upgrade/nakamoto-in-depth/stackers-and-signing.md @@ -0,0 +1,22 @@ +# Stackers and Signing + +Stackers play an essential role in the Nakamoto system that had previously been the responsibility of miners. Before, miners both decided the contents of blocks, and decided whether or not to include them in the chain (i.e. by deciding whether or not to confirm them). In this system each actor has the following responsibilities necessary to make the system function reliably without forks: + +* **Miners** decide the contents of blocks. +* **Stackers** decide whether or not the block is included in the chain. + +The bulk of the complexity of the Nakamoto changes is in separating these two concerns while ensuring that both mining and Stacking remain open-membership processes. **Crucially, anyone can become a miner and anyone can become a Stacker, just as before.** The most substantial changes are in getting miners and Stackers to work together in their new roles to achieve this proposal's goals. + +The key idea is that Stackers are required to acknowledge and validate a miner's block before it can be appended to the chain. To do so, Stackers must first agree on the canonical chain tip, and then apply (and roll back) the block on this chain tip to determine its validity. Once Stackers agree that the block is both canonical and valid, they collectively sign it and replicate it to the rest of the Stacks peer network. Only at this point do nodes append the block to their chain histories. + +This new behavior prevents forks from arising. If a miner builds a block atop a stale tip, Stackers will refuse to sign the block. If Stackers cannot agree on the canonical Stacks tip, then no block will be appended in the first place. While this behavior creates a new failure mode for Stacks -- namely, the chain can halt indefinitely if Stackers cannot agree on the chain tip -- this is mitigated by having a large and diverse body of Stackers such that enough of them are online at all times to meet quorum and incentivizing them via PoX rewards to act as such. + +### Stacker Signing + +The means by which Stackers agree on the canonical chain tip and agree to append blocks is tied to PoX. In each reward cycle, a Stacker clinches one or more reward slots; there are at most 4,000 reward slots per reward cycle. Stackers vote to accept blocks by producing a weighted threshold signature over the block. The signature must represent a substantial fraction of the total STX locked in PoX (the threshold), and each Stacker's share of the signature (its weight) is proportional to the fraction of locked STX it owns. + +The weighted threshold signature is a Schnorr signature generated through a variation of the [FROST protocol](https://eprint.iacr.org/2020/852.pdf). Each Stacker generates a signing key pair, and they collectively generate an aggregate public key for nodes to use to verify signatures computed through a distributed signing protocol. This signing protocol allocates shares of the associated aggregate private key to Stackers proportional to the number of reward slots they clinch. No Stacker learns the aggregate private key; Stackers instead compute shares of the private key and use them to compute shares of a signature, which can be combined into a single Schnorr signature. + +When a miner produces a block, Stackers execute a distributed signing protocol to collectively generate a single Schnorr signature for the block. Crucially, the signing protocol will succeed only if at least X% of the reward slots are accounted for in the aggregate signature. Nakamoto is currently set to use a 70% signing threshold -- at least 70% of the reward slots (by proxy, 70% of the stacked STX) must sign a block in order to append it to the Stacks blockchain. + +Nakamoto uses the [WSTS protocol with the FIRE extension](https://trust-machines.github.io/wsts/wsts.pdf), which admits a distributed key generation and signature generation algorithm pair whose CPU and network bandwidth complexity grows with the number of distinct Stackers. The FIRE extension enables WSTS to tolerate byzantine Stackers. diff --git a/nakamoto-upgrade/nakamoto-in-depth/transactions.md b/nakamoto-upgrade/nakamoto-in-depth/transactions.md new file mode 100644 index 0000000000..9093e65700 --- /dev/null +++ b/nakamoto-upgrade/nakamoto-in-depth/transactions.md @@ -0,0 +1,63 @@ +# Transactions + +### New Transactions + +**Tenure Change** + +A tenure change is an event in the existing Stacks blockchain when one miner assumes responsibility for creating new stacks blocks from another miner. Currently, the miner's tenure lasts until the next cryptographic sortition, over which time it has the option to create a single Stacks block and stream microblocks until the next sortition ends its tenure. A change in tenure occurs when a Stacks block is discovered from a cryptographic sortition. + +In Nakamoto, the Stackers themselves carry out a tenure change by creating a specially-crafted `TenureChange` transaction to serve as a consensus critical artifact stored on the Stacks blockchain which ties the selection of a new miner to the sortition that selected it. Miners must include this artifact as the first transaction in the first block it produces in its new tenure. The sortition prompts Stackers to begin creating the `TenureChange` for the next miner, and the next miner's tenure begins only once the newly selected miner produces a block with the `TenureChange` transaction. + +In the act of producing a `TenureChange` transaction, the Stackers also internally agree to no longer sign the current miner's blocks. Thus, the act of producing a `TenureChange` atomically transfers block-production responsibilities from one miner to another. The new miner cannot orphan recently-confirmed blocks from the old miner because the `TenureChange` transaction contains within it the most recently confirmed blockId as identified by the same Stackers that produced the `TenureChange`. As a result, any new blocks that fail to build upon the latest Stacks block identified by the Signers will be verifiably invalid. + +
+ +_Figure 3: Tenure change overview. When a new Bitcoin block arrives, Stackers begin the process of deciding the last block they will sign from miner N. When they reach quorum, they make this data available for download by miners, and wrap it in a WSTS-signed specially-crafted data payload. This information serves as a record of the tenure change, and must be incorporated in miner N+1's tenure-start block. In other words, miner N+1 cannot begin producing Stacks blocks until Stackers informs it of block X -- the block from miner N that it must build atop. Stacks-on-Bitcoin transactions are applied by miner N+1 for all Bitcoin blocks in sortitions N and earlier when its tenure begins._ + +The `TenureChange` transaction encodes the following data: + +| Name | Description | Representation | +| ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- | +| tenure consensus hash | Consensus hash of this tenure. Corresponds to the sortition in which the miner of this block was chosen. It may be the case that this miner's tenure gets extended across subsequent sortitions; if this happens, then this `consensus hash` value remains the same as the sortition in which the winning block-commit was mined. | 20 bytes | +| previous tenure consensus hash | Consensus hash of the previous tenure. Corresponds to the sortition of the previous winning block-commit. | 20 bytes | +| burn view consensus hash | Current consensus hash on the underlying burnchain. Corresponds to the last-seen sortition. | 20 bytes | +| previous tenure end | The index block hash of the last Stacks block from the previous tenure. | 32 bytes | +| previous tenure blocks | The number of blocks produced since the last sortition-linked tenure. | 4 bytes, big-endian | +| cause |

A flag to indicate the cause of this tenure change
- 0x00 indicates that a sortition occurred, and a new miner should begin producing blocks.
- 0x01 indicates that the current miner should continue producing blocks. The current miner’s tenure execution budget is reset upon processing this transaction.

| 1 byte | +| pubkey hash | The ECDSA public key hash of the current tenure. | 20 bytes | +| signature | The Stacker signature. | 65 bytes | +| signers | A bitmap of which Stackers signed. The ith bit refers to the ith Stacker in the order in which the principals are stacked. | 4 bytes big-endian length + ceil(num\_stackers /8) | + +**TenureChange-BlockFound** + +_**A**** ****`TenureChange-BlockFound`**** ****transaction is induced by a winning sortition. This causes the new miner to start producing blocks, and stops the current miner from producing more blocks.**_ + +When produced, the `TenureChange-BlockFound` transaction will be made available to miners for download, so that miners can include it in their first block. Miners N and N+1 will both monitor the availability of this data in order to determine when the former must stop producing blocks and the latter may begin producing blocks. Once miner N+1 receives this data, it begins its tenure by doing the following: + +1. It processes any currently-unprocessed Stacks-on-Bitcoin transactions up to (but excluding) the Bitcoin block which contains its sortition (so, up to sortition N). +2. It produces its tenure-start block, which contains the `TenureChange-BlockFound` transaction as its first transaction. +3. It begins mining transactions out of the mempool to produce Stacks blocks. + +If miner N cannot obtain or observe the `TenureChange-BlockFound` transaction, then it will keep producing blocks. However, Stackers will not sign them, so as far as the rest of the network is concerned, these blocks never materialized. If miner N+1 does not see the `TenureChange-BlockFound` transaction, it does not start mining; a delay in obtaining the `TenureChange-BlockFound` transaction can lead to a period of chain inactivity. This can be mitigated by the fact that the miner can learn the set of Stackers' IP addresses in advance, and can directly query them for the data. + +**TenureChange-Extend** + +_**A**** ****`TenureChange-Extend`****, which is induced by Stackers, resets the current tenure's ongoing execution budget, thereby allowing the miner to continue producing blocks.**_ + +The time between cryptographic sortitions (and thus tenure changes) depends on the time between two consecutive Bitcoin blocks. This can be highly variable, which complicates the task of sustaining a predictable transaction confirmation latency while also preventing a malicious miner from spamming the network with too many high-resource transactions. + +Today, each miner receives a tenure block budget, which places hard limits on how much CPU, RAM, and I/O their block can consume when evaluated. In this proposal, each miner begins its tenure with a fixed budget, but Stackers may opt to increase that budget through a vote. This is done to enable the miner to continue to produce blocks if the next Bitcoin block is late. + +Ultimately the cadence and decision making around when to initiate, approve, and execute a TenureChange-Extend is at the discretion of the Stackers. To achieve a regular cadence, Stackers are recommended to keep track of the elapsed wall-clock time since the start of the tenure. Once the expected tenure time has passed (e.g. 10 minutes), they can vote to grant the miner an additional tenure execution budget. + +Stackers can produce as many TenureChange-Extend transactions as they like to extend a miner’s tenure. This offers a forward-compatibility path for increasing the blockchain’s throughput to take advantage of future optimizations to the chain’s performance. Also, it allows Stackers to keep the last-winning miner online in order to tolerate empty sortitions, which may arise from misconfigured miners as well as the Bitcoin MEV miner solution (described below). + +### Stacker Turnover + +A miner tenure change happens every time the Signers collectively issue a `TenureChange-BlockFound` transaction in response to a sortition on the burn chain selecting a valid miner. + +Additionally, there are Stacker cycles, which happen once every 2100 Bitcoin blocks (one reward cycle) in which a new set of Stackers are selected by the PoX anchor block (see [SIP-007](https://github.com/stacksgov/sips/blob/main/sips/sip-007/sip-007-stacking-consensus.md)). + +Because Stacks will no longer fork, the PoX anchor block is always known 100 Bitcoin blocks before the start of the next reward cycle. It is the last tenure-start block that precedes prepare-phase. + +The PoX anchor block identifies the next Stackers. They have 100 Bitcoin blocks to prepare for signing Stacks blocks. Within this amount of time, the new Stackers would complete a WSTS DKG for signing blocks. The PoX contract will require Stackers to register their block-signing keys when they stack or delegate-stack STX, so the entire network knows enough information to validate their WSTS Schnorr signatures on blocks. diff --git a/nakamoto-upgrade/running-a-signer.md b/nakamoto-upgrade/running-a-signer.md new file mode 100644 index 0000000000..cfb869570a --- /dev/null +++ b/nakamoto-upgrade/running-a-signer.md @@ -0,0 +1,142 @@ +# Running a Signer + +{% hint style="danger" %} +This document intends to lay out all the steps required to run a signer on testnet after the “Argon” milestone is reached and deployed to testnet. Much of this will not work today, but is being included so that interested parties can familiarize themselves with the process as it will work after the Argon release. Argon is still in development and these instructions will change. +{% endhint %} + +### Step by Step Instructions + +#### 1. Generate TOML file + +Create a TOML file (`signers.toml`) with the following content: + +```toml +node_host = "127.0.0.1:20443" +endpoint = "127.0.0.1:30000" +network = "testnet" +``` + +#### 2. Generate Keys + +You’ll need two private keys: + +`stacks_private_key`: the private key that corresponds to the public key that will be registered in the StackerDB `message_private_key`: the key used when signing messages for block proposals and other messages. + +One way to generate a private key is using the `@stacks/cli` package. With node.js installed, run in the command line: + +`npx –yes @stacks/cli@latest make_keychain` + +You’ll see output like this: + +```json +{ + "mnemonic": "tilt invite caught shoe shed gravity guitar bench spot dial garlic cushion gate garlic need often boss spoon under fence used across tip use", + "keyInfo": { + "privateKey": "7567052905c21867fff273f5c018fb254a282499008d7e8181935a1a1855cb0601", + "publicKey": "0396152c9c1ba3edce6d708d9b652916a4a8320f3a4a5f44c7b1142002cf87882f", + "address": "SP2R765DGGBJRAEJD368QDWS471NRY4D566XKTSY8", + "btcAddress": "1H5ynzN1D9FPV7wknsAHR7RhoX1YBEoSy9", + "wif": "L19veovqpwzNgVgzY45ETu8nSMc8vp21vVjbHNdDN85KnUAEEKQX", + "index": 0 + } +} +``` + +Save the output somewhere. + +Take the `privateKey` property from that and add it to your `signer.toml` with the property \`stacks\_private\_key. + +e.g. `stacks_private_key = "7567052905c21867fff273f5c018fb254a282499008d7e8181935a1a1855cb0601"` + +Next, we need to add the `message_private_key`. You can choose to reuse your Stacks private key or generate a new one. + +The `message_private_key` needs to be base58 encoded. You can use [this tool](https://appdevtools.com/base58-encoder-decoder) to encode the private key as base58. + +In the base58 encoding tool, paste in your `stacks_private_key` and set "Treat input as Hex". Make sure you delete the 01 at the end or the signer will throw an error. + +Take the output and add it to your signer.toml file: + +`message_private_key = "8uHp7CVqu8JWiPzW5g7FX2ZthwCKzyxVK9DfjiYjUkiy"` + +The config file should now look like this, but with different private keys: + +```toml +node_host = "127.0.0.1:20443" +endpoint = "127.0.0.1:30000" +network = "testnet" +stacks_private_key = "7567052905c21867fff273f5c018fb254a282499008d7e8181935a1a1855cb0601" +message_private_key = "8uHp7CVqu8JWiPzW5g7FX2ZthwCKzyxVK9DfjiYjUkiy" +``` + +{% hint style="info" %} +At the moment, there are 3 other fields that need to be included, but those will be removed by the Argon release. Prior to argon, add this to the end of the config file if you want to run the signer: + +```toml +stackerdb_contract_id = "ST11Z60137Y96MF89K1KKRTA3CR6B25WY1Y931668.signers-stackerdb" +signer_id = 0 +signers = [ + {public_key = "swBaKxfzs4pQne7spxhrkF6AtB34WEcreAkJ8mPcqx3t", key_ids = [1, 2, 3, 4]}, + {public_key = "yDJhntuJczbss1XGDmyWtG9Wpw5NDqoBBnedxmyhKiFN", key_ids = [5, 6, 7, 8]}, + {public_key = "xNVCph6zd7HLLJcuwrWz1gNbFoPHjXxn7cyRvvTYhP3U", key_ids = [9, 10, 11, 12]}, + {public_key = "p2wFfLEbwGCmxCR5eGa46Ct6i3BVjFrvBixRn7FnCQjA", key_ids = [13, 14, 15, 16]}, + {public_key = "26jpUNnJPvzDJRJg3hfBn5s5MR4eQ4LLTokjrSDzByh4i", key_ids = [17, 18, 19, 20]} +] +``` + +#### +{% endhint %} + +3\. Run the signer + +We don't yet have Docker images that include the stacks-signer binary, so in the meantime you'll build from source. [This pull request](https://github.com/stacks-network/stacks-core/pull/4268) changes our CI to include the stacks-signer binary in Docker builds. + +The signer will need to store state via disk, but that code is not yet built. Documentation will be created when it is more defined. + +Follow [these instructions](https://github.com/stacks-network/stacks-core?tab=readme-ov-file#building) for building the stacks-core repository from source. + +Once you've run `cargo build`, go back to the folder with your `signer.toml` file and run: + +```bash +# replace with the location of your `stacks-core` folder: +_STACKS_CORE_FOLDER_/target/debug/stacks-signer start --config signer.toml +``` + +You should see output that looks like this: + +```bash +Signer spawned successfully. Waiting for messages to process... +INFO [1705699009.844671] [stacks-signer/src/runloop.rs:438] [signer_runloop] Running one pass for signer ID# 0. Current state: Uninitialized +``` + +{% hint style="info" %} +[This PR](https://github.com/stacks-network/stacks-core/pull/4280) adds a /status endpoint to the signer. Once the signer is running, the client should check this endpoint to ensure it is running and listening correctly. +{% endhint %} + +### Production environment for Signer + +Running the signer in production requires two things: + +* A machine for running a Docker image +* A volume for storing state + +The Docker image needs to use a user-provided config file, which is used when running the signer. Depending on the production environment, there are typically two ways to do this: + +Use the default published image, which expects you to include the config file somewhere on disk Create your own Dockerfile, which is a simple wrapper around the official image + +An example wrapper Dockerfile (this doesn’t work today!): + +```DOCKERFILE +FROM blockstack/stacks-core + +RUN mkdir -p /config + +COPY signer.toml /config/signer.toml + +CMD ["stacks-signer", "run", "--config", "/config/signer.toml"] +``` + +### System Requirements to Run a Signer + +[https://github.com/stacksfoundation/miner-docs/blob/main/stacks-blockchain.md ](https://github.com/stacksfoundation/miner-docs/blob/main/stacks-blockchain.md)[https://github.com/stacksfoundation/miner-docs/blob/main/scripts/install\_stacks.sh](https://github.com/stacksfoundation/miner-docs/blob/main/scripts/install\_stacks.sh) + +Minimum Requirements: run a full node, run the signer binary. 1 cpu, 4gb of ram, 150GB of storage as a minimum. diff --git a/nakamoto-upgrade/stacking-flow.md b/nakamoto-upgrade/stacking-flow.md new file mode 100644 index 0000000000..49f93e79b9 --- /dev/null +++ b/nakamoto-upgrade/stacking-flow.md @@ -0,0 +1,169 @@ +# Stacking Flow + +In Nakamoto, stacking flows have significant changes in comparison to previous versions of Stacks. Because Nakamoto requires stackers to run a signer, which validates blocks produced by Stacks miners, stackers need to provide new information when making Stacking transactions. + +These changes affect both solo Stacking and delegated Stacking. This document intends to outline the new flows for both cases, so that we can better understand what Stackers need to do to Stack in Nakamoto. + +### Definitions + +* Stacker: an entity locking their STX to receive PoX rewards. This is a broad term including solo Stackers and Stackers who use pools. +* Solo stacker: an entity that locks their own STX and runs a signer. They don’t receive delegation. +* Delegator: a stacker who locks their STX and delegates to a signer. They don’t run the signer. +* Delegatee: an entity that runs a Signer and allows others to delegate their STX to them. A delegatee doesn’t need to Stack their own STX, but they can. +* Signer: an entity that runs the stacks-signer software and participates in block validation. This can be either a solo Stacker an entity receiving delegated STX. In many cases we also refer to “signer” as the software that validates blocks. We may want to try and improve this language to be more clear. + +### First, run a signer + +This is a necessary prerequisite to stacking. + +Running a signer involves setting up a hosted production environment that includes both a Stacks Node and the Stacks Signer. For more information, refer to how to run a signer. + +Once the signer software is running, the signer needs to keep track of the `stacks_private_key` that they used when configuring their signer. This will be used in subsequent Stacking transactions. + +### Signer keys and signer key signatures + +The main difference with Stacking in Nakamoto is that the Signer (either solo Stacker or signer running a pool) needs to include two new parameters in their Stacking transactions: + +1. `signer-key`: the public key that corresponds to the `stacks_private_key` their signer is using +2. `signer-signature`: a signature that demonstrates that this Stacker actually controls their `signer-key`. Because signer keys need to be unique, this is also a safety check to ensure that other Stackers can’t use someone else’s public key. + +### Generating a signer key signature + +The `signer-signature` uses the signer’s Stacks private key and creates a signature over the message (`pox-address`, `reward-cycle`), `where pox-address` is the BTC address used for rewards, and `reward-cycle` is the current reward cycle. + +The exact user experiences for generating the `signer-signature` is to be determined, but there are two high-level processes that can be expected: + +1. Using the stacks-signer or other CLI, via a command like `stacks-signer generate-stacking-signature {pox-address} {reward-cycle}`. +2. Using an application like a wallet or a web app. The signature is designed such that Stackers can use pre-existing standards for signing data with a wallet like [Leather](https://leather.io/) and [XVerse](https://www.xverse.app/). Stackers can also use hardware wallets to create this signature. + +Solo Stackers will likely generate their signature as a first step while making their Stacking transaction. + +#### Using a hardware or software wallet to generate signatures + +When the signer is configured with a `stacks_private_key`, the signer may want to be able to use that key in a wallet to make stacking signatures. + +If the signer uses a tool like [@stacks/cli](https://docs.hiro.so/get-started/command-line-interface) to generate the key, the CLI also outputs a mnemonic (aka “seed phrase”) that can be imported into a wallet. Because the Stacks CLI uses the standard derivation path for generating Stacks keys, any Stacks wallet will default to having that same private key when the wallet is imported from a derivation path. Similarly, if a hardware wallet is setup with that mnemonic, then the Signer can use a wallet like Leather to make stacking signatures. + +The workflow for using a setting up a wallet to generate signatures would be: + +1. Use @stacks/cli to generate the keychain and private key. + 1. Typically, when using a hardware wallet, it’s better to generate the mnemonic on the hardware wallet. For this use case, however, the signer software needs the private key, and hardware wallets (by design) don’t allow exporting private keys. +2. Take the `privateKey` from the CLI output and add it to your signer’s configuration. +3. Take the mnemonic (24 words) and either: + 1. Setup a new hardware wallet with this mnemonic + 2. Store it somewhere securely, like a password manager. When the signer needs to generate signatures for Stacking transactions, they can import it into either Leather or XVerse. + +When the user needs to generate signatures: + +1. Setup your wallet with your signer key’s private key. Either: + 1. Setup your Leather wallet with a Ledger hardware wallet + 2. Import your mnemonic into Leather, XVerse, or another Stacks wallet +2. Open an app that has stacking signature functionality built-in +3. Connect your wallet to the app (aka sign in) +4. In the app, enter your PoX address and “submit” +5. The app will popup a window in your wallet that prompts you to sign the information + 1. The app will show clear information about what you’re signing +6. Create the signature + 1. If using a Ledger, confirm on your device +7. The app will display two results: + 1. Your signer key, which is the public key associated with your signer’s key + 2. Your signer signature +8. Finally, make a Stacking transaction using the signer key and signer signature. + +### Solo Stacking + +#### Call the function `stack-stx` + +Just like in previous versions of PoX, Stackers call `stack-stx`, but with the new arguments added in Nakamoto. The arguments are: + +* Amount +* PoX Address: the BTC wallet to receive Stacking rewards +* Start burn height: the current BTC block height +* Lock period: the number of cycles to lock for (1 minimum, 12 max) +* Signer key: the public key that their signer is using +* Signer signature: the signature that proves control of this signer key + +#### Act as a signer + +In the “prepare phase” before the next stacking cycle (100 blocks), the exact set of Stackers will be selected based on the amount of STX stacked. Just like in previous versions of PoX, each Stacker will have some number of reward slots based on the amount of STX locked. + +It is critical that Stackers have their signer running during this period. The prepare phase is when distributed key generation (DKG) occurs, which is used when validating blocks by signers. + +In general, Stackers don’t need to do anything actively during this period, other than monitoring their Signer to ensure it’s running properly. + +#### Extending their stacking period + +Just like in the current version of PoX, Stackers can extend their lock period while still Stacking. The function called is `stack-extend`. + +Stackers can “rotate” their signing key when extending their lock period. + +The arguments are: + +* Extend count: the number of cycles to add to their lock period. The resulting lock period cannot be larger than 12. For example, if currently locked with 6 cycles remaining, the maximum number they can extend is 6. +* Pox Address: the BTC address to receive rewards +* Signer public key: the public key used for signing. This can stay the same, or the Stacker can use a new key. +* Signer signature: a signature proving control of their signing key + +### Delegated stacking + +Similar to the changes to solo Stacking, the big difference for delegation flows is the inclusion of signer keys and signatures. Because signers need to make transactions to “finalize” a delegation, these new arguments add new complexities to the signer. + +#### Delegator initiates delegation + +The first step, where the delegator sets up their delegation to a signer, is to call `delegate-stx`. The arguments here are unchanged: + +* Amount +* Delegate to: the STX address of the signer they’re delegating to. Note that this is different from the “signer key” used. Instead, this is the STX address that is used to make PoX transactions. +* Until burn height: an optional argument where the delegation expires +* Pox Address: an option BTC address that, if specified, the signer must use to accept this delegation + +#### Signer “activates” the delegation + +Just as in the current PoX contract, the signer calls `delegate-stack-stx` to commit the delegator’s STX. The arguments are: + +* Stacker: the STX address of the delegator +* Amount +* Pox Address +* Start burn height +* Lock period: number of cycles to lock for. If the delegator provided the until burn height argument, then the end of these cycles cannot be past the expiration provided. + +This step also allows the Signer to proactively choose which Stackers they’ll accept delegation from. For “closed” pools, the signer will only call this function for approved Stackers. It is up to each signer who runs a closed pool to implement this process, but tooling will likely be created to make this easier. + +This step can happen for many Stackers before going to the next step. + +#### Signer “commits” delegated STX + +At this point, the STX are committed to the signer, and the signer has some “aggregate balance” of committed STX. The signer is not actually eligible for rewards and signer slots until this step is called. + +The signer cannot call this until the total number of STX committed is larger than the minimum threshold required to Stack. This threshold is a function of the total number of liquid STX. At the moment, the threshold is 90,000 STX. + +Once the threshold is reached, the signer calls `stack-aggregation-commit`. This is the point where the signer must provide their signer key and signer key signature. The arguments are: + +* Pox Address +* Reward-cycle: the current reward cycle +* Signer key: the public key of their signer +* Signer signature + +Once this succeeds, the signer is eligible for reward slots. The number of reward slots depends on the amount of STX committed to this signer. Even if the signer commits more than the “global minimum”, the minimum amount to receive a slot depends on the amount of STX locked for each cycle. For example, in the current PoX cycle, that amount is 110,000 STX. + +To act as a signer, each step up to this point must be taken before the prepare phase of the next cycle begins. It is crucial that the signer software is running. + +#### Signer increases amount committed + +Even after the signer commits to a certain amount of STX in the previous step, the signer can increase this amount once more delegations are received. The initial steps must be taken for each Stacker (`delegate-stx` and then `delegate-stack-stx`), and then `stack-aggregation-increase` can be called. + +#### The need for a separate “Delegate Private Key” + +The concept of the “signer key” or “signer private key” was introduced earlier in this document. The signer key is used during consensus mechanisms to sign blocks in Nakamoto. + +Signers using delegation also need a separate keychain to make Stacking transactions such as `delegate-stack-stx` listed earlier. They need a separate key because they need to have the ability to rotate their signer key when necessary. The PoX contract is designed to support rotating the signer key without needing their Stackers to un-stack and re-stack later. You cannot rotate a delegate key without needing to wait for all delegated Stackers to un-stack and finally re-stack to the new delegate address. + +Because of this limitation of being unable to rotate delegate addresses, it’s highly recommended to have a separate delegate key. The benefit of a separate delegate key is that it can easily be used in existing wallets, including hardware wallets. + +### Tooling for Stackers + +The exact tooling that will be available for both solo and delegated stacking is still in the works, but several options are being explored including: + +* CLI tooling +* Web apps +* BTC native multi-sigs diff --git a/nakamoto-upgrade/testnets/README.md b/nakamoto-upgrade/testnets/README.md new file mode 100644 index 0000000000..23c162187f --- /dev/null +++ b/nakamoto-upgrade/testnets/README.md @@ -0,0 +1,7 @@ +# Testnets + +There are two testnet releases of Nakamoto for developers to experiment with: Neon and Argon. + +Neon is a controlled testnet (only a single block producer) designed to get the minimum functionality of Nakamoto running for people to experiment with. + +Argon is a much more fully-featured testnet release that will allow developers and users to test out Nakamoto functionality much more like what the final release will be. diff --git a/nakamoto-upgrade/testnets/argon.md b/nakamoto-upgrade/testnets/argon.md new file mode 100644 index 0000000000..6b359a33ef --- /dev/null +++ b/nakamoto-upgrade/testnets/argon.md @@ -0,0 +1,2 @@ +# Argon + diff --git a/nakamoto-upgrade/testnets/neon.md b/nakamoto-upgrade/testnets/neon.md new file mode 100644 index 0000000000..dfb3f5669c --- /dev/null +++ b/nakamoto-upgrade/testnets/neon.md @@ -0,0 +1,27 @@ +# Neon + +Neon is the first controlled testnet release for Nakamoto and ships with two separate testnets, one with the previous Clarity VM and one with the upcoming Clarity WASM. + +The primary purpose of these testnets is is performance benchmarking with the new consensus rules. + +Both testnets allow users and developers to view and interact with Nakamoto block production, meaning each testnet has Nakamoto coinbase transactions, testable VRF, tenure change transactions, and multiple Stacks blocks per Bitcoin block. + +Both testnets utilize Bitcoin Regtest and utilize a single miner, this is what is meant by "controlled" testnet. These testnets are producing blocks roughly every 5 seconds, which you can see reflected in the explorer. + +If non of that makes any sense, be sure to check out the previous pages of this Nakamoto section. + +Both testnets ship with functioning APIs and explorers to experiment with + +### Controlled Testnet 1 (Clarity VM) + +* [API](https://api.nakamoto-1.hiro.so/extended/v1/status) +* [Explorer](https://explorer.hiro.so/blocks?chain=testnet\&api=https://api.nakamoto-1.hiro.so) + +### Controlled Testnet 2 (Clarity WASM) + +* [API](https://api.nakamoto-2.hiro.so/extended/v1/status) +* [Explorer](https://explorer.hiro.so/blocks?chain=testnet\&api=https://api.nakamoto-2.hiro.so) + +You can also use this [preview version of Clarinet](https://github.com/hirosystems/clarinet/releases/tag/nakamoto-preview-1) in order to test Clarity WASM in your local Devnet. + +Further documentation on API endpoints and Stacks.js functions is forthcoming. diff --git a/nakamoto-upgrade/what-is-the-nakamoto-release.md b/nakamoto-upgrade/what-is-the-nakamoto-release.md new file mode 100644 index 0000000000..325916ad2e --- /dev/null +++ b/nakamoto-upgrade/what-is-the-nakamoto-release.md @@ -0,0 +1,79 @@ +# What is the Nakamoto Release? + +The Nakamoto Release is an upcoming hard fork on the Stacks network designed to bring several benefits, chief among them are increased transaction throughput and 100% Bitcoin finality. + +With Nakamoto, Stacks block production would no longer be tied to miner elections. Instead, miners produce blocks at a fixed cadence, and the set of PoX Stackers rely on the miner elections to determine when the current miner should stop producing blocks and a new miner should start. This blockchain will only fork if 70% of Stackers approve the fork, and chain reorganization will be as difficult as reorganizing Bitcoin. + +The Nakamoto release brings many new capabilities and improvements to the Stacks blockchain by focusing on a set of core advancements: improving transaction speed, enhancing finality guarantees for transactions, mitigating Bitcoin miner MEV (miner extractable value) opportunities that affect PoX, and boosting robustness against chain reorganizations. + +### Current Design + +The Stacks blockchain today produces blocks in accordance with the algorithms described in [SIP-001](https://github.com/stacksgov/sips/blob/main/sips/sip-001/sip-001-burn-election.md) and [SIP-007](https://github.com/stacksgov/sips/blob/main/sips/sip-007/sip-007-stacking-consensus.md), and [SIP-015](https://github.com/stacksgov/sips/blob/main/sips/sip-015/sip-015-network-upgrade.md). Miners compete to append a block to the blockchain through the miner selection process facilitated by a VRF backed sortition process. Miners submit a block-commit transaction to Bitcoin, which commits to the hash of the block the miner intends to append. The sortition process selects at most one block-commit in the subsequent Bitcoin block, which entitles the submitter to propagate their block and earn a block reward. + +{% hint style="info" %} +Throughout this documentation and the SIPs, you'll frequently see the term "cryptographic sortition" or some variation thereof (miner sortition, the sortition, etc.). A Cryptographic sortition is a process of randomly selecting one or more entities from a set using cryptography. This is a decentralized and verifiable way to select participants for a variety of tasks, such as consensus protocols, lotteries, and auctions. + +More specifically, miner sortition in the context of Stacks is the weighted cryptographic sortition process by which a miner candidate is selected as the next miner (leader). Details of this process are in [SIP-001](https://github.com/stacksgov/sips/blob/main/sips/sip-001/sip-001-burn-election.md) with mechanism alterations in [SIP-007](https://github.com/stacksgov/sips/blob/main/sips/sip-007/sip-007-stacking-consensus.md). + +Nakamoto will introduce further mechanism alterations to this process. +{% endhint %} + +### The Problems + +Over the last three years the Stacks community has identified several issues with the current system design: + +1. **Slow Bitcoin blocks, Stacks forks, and missed sortitions are disruptive to on-chain applications.** The act of waiting to produce a new block until after a sortition elects a valid miner ties best-case Stacks block production rate to the block production rate of Bitcoin, leading to very high transaction confirmation latency. +2. **Microblocks are not effective in speeding up transaction confirmation time.** While microblocks have the potential to mitigate missed sortitions and improve transaction inclusion time, they do not work in practice because the protocol cannot ensure that microblocks will be confirmed until the next sortition happens. Additionally, new miners will often orphan recently-confirmed transactions from the old miner that were included in microblocks because there is no consensus-critical procedure that forces the next miner to build upon the latest microblock. +3. **Stacks forks are not tied to Bitcoin forks, allowing cheap reorgs** The cost to reorg the last N blocks in the Stacks blockchain is the cost to produce the next N + 1 Stacks blocks (i.e. by spending BTC), which is cheap compared to the cost of reorging the Bitcoin. This SIP describes an opportunity to tie the canonical Stacks fork to the Bitcoin blockchain such that the act of reorging Stacks chain history requires the Stacks miner to produce the fork with 70% of stacker sign-off. +4. **Stacks forks arise due to poorly-connected miners.** If a set of miners has a hard time learning the canonical Stacks chain tip when they submit block-commits, then they will collectively orphan other miners who are better-connected. This has happened in practice. +5. **Some Bitcoin miners run their own Stacks miners and deliberately exclude other Stacks miners' `block-commits` from their Bitcoin blocks.** Once the STX block reward became sufficiently large this allowed them to pay a trivial PoX payout while guaranteeing that they would win the cryptographic sortition in their Bitcoin block. This was anticipated in the original design but the regularity with which it happens today is greater than the original protocol accounted for, and thus must be addressed now. + +### The Solutions + +To address these shortcomings, Nakamoto applies three fundamental changes to the way Stacks works. + +* **Fast blocks:** The time taken for a user-submitted transaction to be mined within a block (and thus confirmed) will now take on the order of seconds, instead of tens of minutes. This is achieved by separating block production from cryptographic sortitions -- a winning miner may produce many blocks between two subsequent sortitions. +* **Bitcoin finality:** Once a transaction is confirmed, reversing it is at least as hard as reversing a Bitcoin transaction. The Stacks blockchain no longer forks on its own. +* **Bitcoin Miner MEV Resistance:** This proposal alters the sortition algorithm to ensure that Bitcoin miners do not have an advantage as Stacks miners. They must spend competitive amounts of Bitcoin currency to have a chance of earning STX. + +### Nakamoto Design + +To achieve these goals Nakamoto will introduce the following changes to the Stacks protocol: + +1. **Decouple Stacks tenure changes from Bitcoin block arrivals.** In both today's system and Nakamoto, miners take turns appending blocks to the Stacks blockchain -- the next miner is selected by cryptographic sortition, and the miner has the duration of the Bitcoin block (its tenure) to announce a new block state. Nakamoto allows a miner to produce many Stacks blocks per Bitcoin block instead of one, and requiring the next miner to confirm all of them. There are no more microblocks or Bitcoin-anchored blocks; instead, there are only Nakamoto Stacks blocks. This will achieve fast block times. +2. **Require stackers to collaborate before the next block can be produced.** Stackers will need to collectively validate, store, sign, and propagate each Nakamoto Stacks block the miner produces before the next block can be produced. Stackers must do this in order to earn their PoX payouts and unlock their STX (i.e. PoX is now treated as compensation from the miner for playing this essential role). In Nakamoto, a sortition only selects a new miner; it does not give the miner the power to unilaterally orphan confirmed transactions as it does today. This will ensure that miners do not produce forks and are able to confirm all prior Stacks blocks prior to selection. +3. **Use stackers to police miner behavior.** A sortition causes the Stackers to carry out a tenure change by (a) agreeing on a "last-signed" block from the current miner, and (b) agreeing to only sign blocks from the new miner which descend from this last-signed block. Thus, Stackers police miner behavior -- Stackers prevent miners from mining forks during their tenure, and ensure that they begin their tenures by building atop the canonical chain tip. The new miner cannot orphan recently-confirmed transactions from the old miner because the signers who approved the tenure change are necessarily aware of all Stacks blocks that came before it. This **further prevents miners from forking the Stacks blockchain.** +4. **Require Stacks miners to commit the indexed block hash of the first block produced by the last Stacks miner in their block-commit transactions on the Bitcoin blockchain.** This is the SHA512/256 hash of both the consensus hash of all previously-accepted Bitcoin transactions that Stacks recognizes, as well as the hash of the block itself (a block-commit today only contains the hash of the Stacks block). This will anchor the Stacks chain history to the Bitcoin up to the start of the previous miner's tenure, as well as all causally-dependent Bitcoin state that Stacks has processed. This **ensures Bitcoin finality and resolves miner connectivity issues** by putting fork prevention on Stackers. +5. **Adopt a Bitcoin MEV solution which punishes block-commit censorship.** The probability a stacks miner wins a sortition should be altered such that omitting block commits of honest Stacks miners is not profitable to Bitcoin miners. The mechanics of this are outlined below. + +All together these changes will achieve the goals outlined, resolving key areas of improvement for the Stacks protocol. + +Although Nakamoto is a breaking change, all smart contracts published prior to this its activation will be usable after it activates. + +Let's dive into how each of these pieces work so we can get an in-depth understanding of exactly how Nakamoto works. + +This portion of the documentation is broken up into several sections. + +### Nakamoto in 10 Minutes + +Interested in learning how Nakamoto works at a high level? This section will provide a brief overview of how each component works and how they contribute to the goals above without getting bogged down in the technical details + +### Nakamoto In-Depth + +This section will describe exactly how each component of Nakamoto works in detail. If you are interested in diving into all of the technical details of exactly how Nakamoto works, this section will get you there. + +### Testnets + +Neon is the currently released controlled testnet that is available for us. It actually comes with two testnets, one which uses the new ClarityWASM VM (one of the components of Nakamoto) and one that uses the current Clarity VM. + +Argon is the next testnet to be released, slated for late February. + +Neon is available to be used now, get more details on the Neon docs page. + +Expect frequent changes to the testnets as it will be upgraded quite often. + +You can track the work being done on Nakamoto [on GitHub](https://github.com/stacks-network/stacks-core/milestones?direction=asc\&sort=title\&state=open). + +### Running a Signer + +Signers play a critical role in Stacks and in this section we'll go over how you can run your own signer. diff --git a/sbtc/developer-guides/README.md b/sbtc/developer-guides/README.md new file mode 100644 index 0000000000..a8309ce984 --- /dev/null +++ b/sbtc/developer-guides/README.md @@ -0,0 +1,7 @@ +# Developer Guides + +The real power of sBTC will come from developers building applications that utilize it. These guides will show you how to utilize sBTC in your applications. + +{% hint style="warning" %} +Big caveat for the developer tutorials: These tutorials utilized libraries that used the previous OP\_RETURN system. sBTC is being redesigned to utilize only OP\_DROP, so these libraries will change. You can view the tutorials as a way to familiarize yourself with how you might build on sBTC, but know that this will change. +{% endhint %} diff --git a/sbtc/developer-guides/end-to-end-tutorial.md b/sbtc/developer-guides/end-to-end-tutorial.md new file mode 100644 index 0000000000..4b09c7a6b7 --- /dev/null +++ b/sbtc/developer-guides/end-to-end-tutorial.md @@ -0,0 +1,1067 @@ +# End to End Tutorial + +### Build a Basic DeFi Application using Next, Stacks.js, Clarity, and the sBTC Developer Release + +If you are looking to start building full-stack applications with the sBTC Developer Release (sBTC DR), this is the place to start. We'll walk through the entire process of building a full-stack application utilizing sBTC from start to finish. + +If you prefer a quicker introduction, check out the Quickstart, which will get you up to speed on the essentials of building with sBTC. + +First, some housekeeping. + +### Prerequisites + +In this tutorial, we'll be building a basic Bitcoin lending app called Lagoon. We'll be using Next for our frontend and the Developer Release of sBTC on Stacks for the smart contract functionality. + +We'll be building a full-stack app in this tutorial, primarily working in JavaScript and Clarity. + +You should have at least some familiarity with the following before starting: + +* React +* Next +* Stacks +* sBTC +* Clarity + +If you aren't familiar with React or Next for frontend development, I recommend Vercel's [Next Foundations](https://nextjs.org/learn/foundations/about-nextjs) to get up to speed. + +If you aren't familiar with Stacks, check out [Stacks Academy](https://docs.stacks.co/docs/stacks-academy) and if you need an intro to building on Stacks, take a look at the [Stacks Developer Quickstart](https://docs.stacks.co/docs/tutorials/hello-stacks). + +To get you up to speed on sBTC, you can start by familiarizing yourself with sBTC from a high level. + +Specifically, we're working with the Developer Release now, which is an early version of sBTC for developers to begin experimenting and building before the full version is released. + +Finally, if you aren't familiar with Clarity, you can get the basics, which is enough for this tutorial, by going through this [Clarity Crash Course](https://docs.stacks.co/docs/clarity/crash-course). + +You'll also want to make sure you have the [Leather web wallet](https://leather.io/) installed, as that is what we'll be using to interact with our application. + +Now let's get started. + +### What We're Building + +We're going to be building an app called Lagoon. Lagoon is a very basic Bitcoin lending application that allows you to borrow and lend BTC using Clarity smart contracts. We'll take full advantage of sBTC here and use it in the process. + +One of the primary solutions sBTC brings to the world is to create a robust decentralized financial system on top of Bitcoin, rather than needing to go through centralized custodians or pay high fees on the L1. + +Lagoon allows users to connect their wallet, convert their BTC to sBTC, and then take that sBTC and deposit it into a lending pool in order to earn interest on it. + +This is by no means a production level borrowing and lending app, and is only meant to be used to illustrate how to get started with sBTC. Feel free to use it as a starting point in your projects, but just know that the code is only for demo purposes and should not be used in production. + +### Getting Set Up + +Note: There are still some bugs being worked out with the testnet sBTC system, so we're going to use a local developer environment for this tutorial. + +For this tutorial, we're going to get you set up with a local version of the sBTC DR. Although this does require a bit more setup time, it will pay off by making your development experience significantly faster. + +So, before going any further, make sure you have sBTC set up locally by following the Local environment setup guide. + +Once you're all set up, it's time to start building! + +If at any time you get stuck, you can refer to the final code at the [Lagoon repo](https://github.com/kenrogers/lagoon). + +### Creating Our Front End + +The first thing we need to do is create a new folder for our project, called `lagoon`. + +```bash +mkdir lagoon && cd lagoon +``` + +Inside that we'll initiate our Next project for our frontend. + +```bash +npx create-next-app@latest +``` + +Use the following values when answering the setup questions: + +* Name: frontend +* TypeScript: no +* ESLint: up to you +* Tailwind: yes +* src directory: yes +* App Router: yes +* Customize import alias: no + +Now let's get our frontend created. Since this isn't a React/Next tutorial, I'll gloss over the boilerplate code. + +First, we need to install the `@stacks/connect` package as this is what we'll use to connect our wallet. + +```bash +npm install @stacks/connect +``` + +Now, let's update our `layout.js` file in `frontend/src/app/layout.js` to get a Navbar and Connect Wallet component created. + +```jsx +"use client"; + +import "./globals.css"; +import { Inter } from "next/font/google"; +import React, { useState, useEffect } from "react"; +import { AppConfig, UserSession } from "@stacks/connect"; + +import Navbar from "./components/Navbar"; + +const inter = Inter({ subsets: ["latin"] }); + +export default function RootLayout({ children }) { + const [userData, setUserData] = useState({}); + + const appConfig = new AppConfig(); + const userSession = new UserSession({ appConfig }); + + useEffect(() => { + if (userSession.isSignInPending()) { + userSession.handlePendingSignIn().then((userData) => { + setUserData(userData); + }); + } else if (userSession.isUserSignedIn()) { + setUserData(userSession.loadUserData()); + } + }, []); + return ( + + +
+ {userData !== undefined ? ( + <> + + {children} + + ) : ( + "" + )} +
+ + + ); +} +``` + +Next up let's add our Navbar by creating a `Navbar.js` file inside the `src/app/components` directory. + +```jsx +"use client"; + +import Link from "next/link"; +import ConnectWallet from "./ConnectWallet"; + +export default function Navbar({ userSession, userData, setUserData }) { + return ( + + ); +} +``` + +Next we need to create the `ConnectWallet.js` component. You can do that inside the `src/app/components` directory. + +Inside that file, we'll add the following. + +```jsx +import { showConnect } from "@stacks/connect"; +import { StacksMocknet, StacksTestnet } from "@stacks/network"; + +export default function ConnectWallet({ userSession, userData, setUserData }) { + const connectWallet = () => { + showConnect({ + userSession, + network: StacksTestnet, + appDetails: { + name: "BitLoan", + icon: "https://freesvg.org/img/bitcoin.png", + }, + onFinish: () => { + window.location.reload(); + }, + onCancel: () => { + // handle if user closed connection prompt + }, + }); + }; + + const disconnectWallet = () => { + userSession.signUserOut(window.location.origin); + setUserData({}); + }; + return ( + + ); +} +``` + +All we are doing here is providing a mechanism for the user to connect with a web wallet. Walking through how each piece of the authentication works is outside the scope of this sBTC tutorial. Refer to the Stacks Quickstart linked above if you are unsure of what is happening here. + +Then, update your `src/app/page.js` file to look like this. + +```jsx +export const metadata = { + title: "Lagoon", + description: "A decentralized Bitcoin lending application", +}; + +export default function Home() { + return ( + <> +

Lagoon

+

+ Decentralized lending and borrowing with sBTC. +

+ + ); +} +``` + +Now we're going to add each page and component to create a basic UI. + +`src/app/borrow/page.js` + +```jsx +import BorrowForm from "../components/BorrowForm"; + +export const metadata = { + title: "Borrow", + description: "A decentralized Bitcoin lending application", +}; + +export default function Borrow() { + return ( +
+

Borrow sBTC

+ +
+ ); +} +``` + +`deposit/page.js` + +```jsx +import DepositForm from "../components/DepositForm"; + +export const metadata = { + title: "Deposit", + description: "A decentralized Bitcoin lending application", +}; + +export default function Deposit() { + return ( +
+

Deposit BTC to Mint sBTC

+ +
+ ); +} +``` + +`lend/page.js` + +```jsx +import LendForm from "../components/LendForm"; + +export const metadata = { + title: "Lend", + description: "A decentralized Bitcoin lending application", +}; + +export default function Lend() { + return ( +
+

Lend your sBTC

+ +
+ ); +} +``` + +`withdraw/page.js` + +```jsx +import WithdrawForm from "../components/WithdrawForm"; + +export const metadata = { + title: "Withdraw", + description: "A decentralized Bitcoin lending application", +}; + +export default function Withdraw() { + return ( +
+

Withdraw sBTC to BTC

+ +
+ ); +} +``` + +`components/BorrowForm.js` + +```jsx +export default function BorrowForm() { + return ( +
+ + + +
+ ); +} +``` + +`components/DepositForm.js` + +```jsx +export default function DepositForm() { + return ( +
+ + +
+ ); +} +``` + +`components/LendForm.js` + +```jsx +export default function LendForm() { + return ( +
+ + +
+ ); +} +``` + +`components/WithdrawForm.js` + +```jsx +export default function WithdrawForm() { + return ( +
+ + +
+ ); +} +``` + +If you run `npm run dev` from the `frontend` folder you should see the UI display. + +What we have here is a basic UI for converting BTC and sBTC, and borrowing and lending sBTC. + +Now that we have our basic UI in place, let's add functionality one piece at a time. + +### Initiating a sBTC Deposit + +The first thing we are going to do is create a component to initiate a sBTC deposit. + +You should already be familiar with how sBTC works at a high level, but what we are going to be doing is constructing a custom Bitcoin transaction that will have all the data we need in order to successfully deposit it into the sBTC wallet and then mint our sBTC. + +Remember that for the Developer Release, the system that actually does the minting is not the fully decentralized version, it is a centralized single binary, but for the purposes of interacting with it as an application developer, the interface will be very similar to the final version. + +Recall that in order to mint sBTC to our Stacks address we need to deposit the amount of BTC we want to convert into the threshold signature wallet, and pass in what Stacks address we want the sBTC minted to in via the OP\_RETURN data. + +The protocol and sBTC Clarity contracts will handle the actual minting of the sBTC. + +We can use the sBTC package to make constructing that transaction much easier, and then we can use Leather's API to broadcast it. + +We'll start by installing the sBTC package. + +```bash +npm install sbtc +``` + +Next we need to set up the context that will allow us to have our `UserData` available everywhere. + +Create the `UserContext.js` file within the `src` directory and put this content in it: + +```jsx +import React from "react"; + +export const UserContext = React.createContext(); +``` + +This will allow us to read from this file and pull in our authenticated user data in any part of the app. + +Let's now update the `DepositForm.js` component. + +```jsx +"use client"; + +import { useState, useContext } from "react"; +import { + DevEnvHelper, + sbtcDepositHelper, + TESTNET, + TestnetHelper, + WALLET_00, + WALLET_01, +} from "sbtc"; +import { bytesToHex, hexToBytes } from "@noble/hashes/utils"; +import * as btc from "@scure/btc-signer"; + +import { UserContext } from "../UserContext"; + +export default function DepositForm() { + const { userData } = useContext(UserContext); + const [satoshis, setSatoshis] = useState(""); + + const handleInputChange = (event) => { + setSatoshis(event.target.value); + }; + + const buildTransaction = async (e) => { + e.preventDefault(); + const testnet = new TestnetHelper(); + // const testnet = new DevEnvHelper(); + + // setting BTC address for devnet + // const bitcoinAccountA = await testnet.getBitcoinAccount(WALLET_00); + // const btcAddress = bitcoinAccountA.wpkh.address; + // const btcPublicKey = bitcoinAccountA.publicKey.buffer.toString(); + + // setting BTC address for testnet + const btcAddress = userData.profile.btcAddress.p2wpkh.testnet; + const btcPublicKey = userData.profile.btcPublicKey.p2wpkh; + + let utxos = await testnet.fetchUtxos(btcAddress); + + // If we are working via testnet + // get sBTC deposit address from bridge API + const response = await fetch( + "https://bridge.sbtc.tech/bridge-api/testnet/v1/sbtc/init-ui" + ); + const data = await response.json(); + const sbtcWalletAddress = data.sbtcContractData.sbtcWalletAddress; + + // if we are working via devnet + // const sbtcWalletAccount = await testnet.getBitcoinAccount(WALLET_00); + // const sbtcWalletAddress = sbtcWalletAccount.tr.address; + const tx = await sbtcDepositHelper({ + // comment this line out if working via devnet + network: TESTNET, + sbtcWalletAddress, + stacksAddress: userData.profile.stxAddress.testnet, + amountSats: satoshis, + feeRate: await testnet.estimateFeeRate("low"), + utxos, + bitcoinChangeAddress: btcAddress, + }); + + const psbt = tx.toPSBT(); + const requestParams = { + publicKey: btcPublicKey, + hex: bytesToHex(psbt), + }; + const txResponse = await window.btc.request("signPsbt", requestParams); + const formattedTx = btc.Transaction.fromPSBT( + hexToBytes(txResponse.result.hex) + ); + formattedTx.finalize(); + const finalTx = await testnet.broadcastTx(formattedTx); + console.log(finalTx); + }; + + return ( +
+ + +
+ ); +} +``` + +Before we go through the code, a reminder that in order to mint sBTC, you initiate a deposit transaction via Bitcoin. So to mint sBTC from a UI like this, we need to construct and broadcast that transaction on the Bitcoin network. Then the sBTC binary will detect that transaction and mint the sBTC. + +We need a way to tell the Bitcoin network how much sBTC we want to mint. That's easy, we just send that amount to the threshold wallet. And we need to tell it where to send the sBTC. To do that we need to pass our Stacks address to the OP\_RETURN of the Bitcoin transaction. + +It can get pretty complicated to construct this manually. Luckily, the sBTC library will do most of the heavy lifting. + +Okay let's go through this step by step. First we are importing some packages we need in order to construct our Bitcoin transaction. + +We're also using React's state and context APIs to set how many sats we are depositing and to get our `userData`. + +At the bottom you can see in our UI that we have a basic form where users enter how many sats they want to convert and call the `buildTransaction` function to initiate that. + +Let's dig into that function. + +The very first thing we are doing is setting what network we are on with either the `DevenvHelper` or the `TestnetHelper` from the `sbtc` package. + +Here we have it configured to use either testnet or devnet, depending on which line we have commented out. If you have your local `devenv` up and running, great, use that. Otherwise, you can use testnet as well, you'll just need to wait for transactions to confirm. + +The next thing we need to do is get all of the UTXOs that belong to our currently logged in address. The `sbtc` package helps us with that as well and we can pass in our authenticated address. + +Again, this will be a slightly different process depending on if you are on testnet or devnet. + +Bitcoin, unlike the accounts model of something like Ethereum, works off of a UTXO model. + +UTXO stands for unspent transaction output, and the collection of UTXOs that are spendable by a specific Bitcoin address determines how much bitcoin an address has. + +When we create new transactions, we need to construct the inputs (what is being sent) based on those UTXOs. This helper function gets all of our UTXOs and formats them the right way. + +Next we are using the [sBTC Bridge](https://bridge.sbtc.tech) API in order to get the current threshold wallet we should be sending the BTC to. + +Next it's time to actually build the transaction. To do that we need to pass i: + +* What network we are using, imported from the `sbtc` package. This defaults to `devnet` so you only need to pass this parameter if you are using something else. +* The threshold wallet address we got above +* Our stacks address. This is where the sBTC will be minted to +* How many sats we want to deposit and convert to sBTC +* The fee to use, we can get this by using another helper from the `sbtc` package +* Our utxos that we fetched +* And where to send the remaining Bitcoin change after the transaction (UTXOs can only be spent as a whole, so we need to create another transaction to send any remainder) + +Then we need to covnert that transaction to a PSBT. In order to use the Leather wallet, our transaction needs to be a PSBT, which we then pass to the wallet, use the wallet to sign, and then use the `sbtc` helper to broadcast it. + +The next few lines are converting the transaction to the right format, calling the wallet API to sign it, and broadcasting it. + +Then we simply log the transaction. After you confirm the transaction in the Leather wallet, you can view this transaction in a Bitcoin explorer. Wait a few minutes (or a few seconds on devnet) and you should see your sBTC minted in your wallet. + +:::note If you run into an error about the input script not having a pubKey, make sure you are authenticated with the same account you are initiating the transaction from. ::: + +Alright now that we are successfully minting sBTC, we're going to switch gears a bit and build out a super simple Clarity contract to handle our borrowing and lending functionality. + +### Creating Our Lending Smart Contract + +We're going to be creating our new smart contract inside the sBTC repo we have running. + +Switch into `sbtc/romeo/asset-contract` and run `clarinet contract new lagoon`. + +If you are working from testnet the easiest option will probably be to write and deploy your contract from the [Hiro explorer sandbox](https://explorer.hiro.so/sandbox/deploy?chain=testnet). + +We want to developer our smart contract in the same folder and environment as sBTC. + +Please keep in mind that this is for demo purposes only. Real DeFi lending systems are much more complex and robust than this. Don't use this as a model for building a DeFi protocol, it's only meant to be a starting point for how to work with sBTC. + +Now let's write it. + +First we're going to set up the data variables we need. + +```clojure +;; Define the contract's data variables +(define-map deposits { owner: principal } { amount: uint }) +(define-map loans principal { amount: uint, last-interaction-block: uint }) + +(define-data-var total-deposits uint u0) +(define-data-var total-loans uint u0) +(define-data-var pool-reserve uint u0) +(define-data-var loan-interest-rate uint u10) ;; Representing 10% interest rate +``` + +We're creating a few different things here. First, we're setting up some maps that define all of the deposits and loans currently in the protocol, assigning an owner and an amount. We also have the `last-interaction-block` field that will help us calculate how much interest is owed to a lender. + +Next up we have a few basic variables defining how many deposits and loans we have in total, the pool reserve, which is the total interest paid by borrowers, and the interest rate, which we are hardcoding here for simplicity. + +Now let's write our deposit function. This function will allow users to deposit sBTC into the protocol in order to generate interest. + +```clojure +(define-public (deposit (amount uint)) + (let ( + (current-balance (default-to u0 (get amount (map-get? deposits { owner: tx-sender })))) + ) + (try! (contract-call? .asset transfer amount tx-sender (as-contract tx-sender) none)) + (map-set deposits { owner: tx-sender } { amount: (+ current-balance amount) }) + (var-set total-deposits (+ (var-get total-deposits) amount)) + (ok true) + ) +) +``` + +Let's walk through this. We are declaring a public function that takes in a `uint` called `amount` that will represent how many sats the user wants to deposit into the protocol. + +Next we are using `let` to get the current balance of the depositor. If they have never made a deposit and don't have an entry in the map, we default to 0. + +Next up we are initiating a `try!` call and calling the `transfer` function from the sBTC contract, `.asset` in this case. We are transferring the corresponding amount of sBTC from ourselves into the Lagoon contract. + +If that is successful, we then update our `deposits` map to add the amount we just deposited. + +Finally, we update the `total-deposits` as well and return `true`. + +Next up, let's write the `borrow` function. + +```clojure +(define-public (borrow (amount uint)) + (let ( + (user-deposit (default-to u0 (get amount (map-get? deposits { owner: tx-sender })))) + (allowed-borrow (/ user-deposit u2)) + (current-loan-details (default-to { amount: u0, last-interaction-block: u0 } (map-get? loans tx-sender ))) + (accrued-interest (calculate-accrued-interest (get amount current-loan-details) (get last-interaction-block current-loan-details))) + (total-due (+ (get amount current-loan-details) (unwrap! accrued-interest (err u8)))) + (new-loan (+ total-due amount)) + ) + (asserts! (<= amount allowed-borrow) (err u7)) + (try! (contract-call? .asset transfer amount (as-contract tx-sender) tx-sender none)) + (map-set loans tx-sender { amount: new-loan, last-interaction-block: block-height }) + (ok true) + ) +) +``` + +Again here we are using `let` to retrieve and set the amount this user currently has in the pool, defaulting to 0. + +Next we are getting the number that this user is allowed to borrow. Borrowers can only borrow up to 50% of their collateral. + +Next we are checking to see how many blocks it has been since they last interacted with the protocol. We set this to keep track of interest accrual. We use the `calculate-accrued-interest` function (which we'll implement in a moment) to calculate how much they owe and set that as well. + +Next we are checking to make sure that the depositor is allowed to borrow and that the pool has enough liquidity to cover the new loan. + +Then we transfer the sBTC from the contract to the borrower. + +Finally we update all of our variables to include the new amount. Note that when we update these variables, we are taking interest into account. In this case, when this borrower repays this loan, they will repay the original amount plus the 10% interest. + +Next up we have the repay function. + +```clojure +;; Users can repay their sBTC loans +(define-public (repay (amount uint)) + (let ( + (current-loan-details (default-to { amount: u0, last-interaction-block: u0 } (map-get? loans tx-sender))) + (accrued-interest (calculate-accrued-interest (get amount current-loan-details) (get last-interaction-block current-loan-details))) + (total-due (+ (get amount current-loan-details) (unwrap! accrued-interest (err u8)))) + ) + (asserts! (>= total-due amount) (err u4)) + (try! (contract-call? .asset transfer amount tx-sender (as-contract tx-sender) none)) + (map-set loans tx-sender { amount: (- total-due amount), last-interaction-block: block-height }) + (var-set total-loans (- (var-get total-loans) amount)) + (ok true) + ) +) +``` + +This is very similar to the other functions. In this case the borrower is simply paying back their loan with the included interest. Remember that the interest was calculated when they initially borrowed, so we don't need to include it here. + +Now let's write the function that allows lenders to claim their yield. + +```clojure +(define-public (claim-yield) + (let ( + (user-deposit (default-to u0 (get amount (map-get? deposits { owner: tx-sender })))) + (yield-amount (/ (* (var-get pool-reserve) user-deposit) (var-get total-deposits))) + ) + (try! (as-contract (contract-call? .asset transfer yield-amount (as-contract tx-sender) tx-sender none))) + (var-set pool-reserve (- (var-get pool-reserve) yield-amount)) + (ok true) + ) +) +``` + +First we get the user's deposits and calculate how much interest they are owed based on their proportion of the pool reserve. + +Then we simply transfer that amount to them and update the pool reserve with the new amount. + +And finally let's implement the function to calculate interest. + +```clojure +(define-private (calculate-accrued-interest (principal uint) (start-block uint)) + (let ( + (elapsed-blocks (- block-height start-block)) + (interest (/ (* principal (var-get loan-interest-rate) elapsed-blocks) u10000)) + ) + (asserts! (not (is-eq start-block u0)) (ok u0)) + (ok interest) + ) +) +``` + +Here we are using the amount of elapsed blocks to calculate interest that accrues linearly across blocks. If the user has never taken out a loan, the interest is set to 0. + +There is a lot of functionality missing here that we would need in a real DeFi protocol including liquidation, compound interest, withdrawal functionality for depositors, and a lot of controls to ensure the system can't be gamed. The purpose of this tutorial is to show you how to use sBTC, not build a full-fledged DeFi product. As such, we'll keep the functionality basic, as that's enough to understand how to utilize sBTC and the role it would play in a DeFi application. + +Before we can interact with our contract, we need to deploy it. To do that, we can create a new deployment plan with Clarinet. + +To do this, simply modify the `default.devnet.yaml` file in deployments and add your new contract to the list of contracts to publish. + +```yaml +- contract-publish: + contract-name: lagoon + expected-sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + cost: 38610 + path: contracts/lagoon.clar + anchor-block-only: true + clarity-version: 2 +``` + +Now you'll need to restart your environment and call the `deploy_contracts.sh` helper again. Refer back to the devnet setup guide if this is confusing. + +If you deployed to testnet, you don't need to worry about this part. + +Now that we have a basic smart contract in place, let's build the UI to actually interact with it. + +### Creating the Lend UI + +We're going to be build out the Lend form first. Go ahead and replace the contents of the `LendForm.js` file with the following, then we'll go through it line by line. + +```jsx +"use client"; + +import React, { useState } from "react"; +import { uintCV, PostConditionMode } from "@stacks/transactions"; +import { openContractCall } from "@stacks/connect"; +import { StacksMocknet, StacksTestnet } from "@stacks/network"; + +export default function LendForm() { + const [amount, setAmount] = useState(0); + + const handleDeposit = async () => { + const functionArgs = [ + uintCV(amount), // Convert the amount to uintCV + ]; + + const contractAddress = "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM"; // Replace with your contract address + const contractName = "lagoon"; // Replace with your contract name + const functionName = "deposit"; // Function for deposit + + const options = { + contractAddress, + contractName, + functionName, + functionArgs, + network: new StacksTestnet(), + // network: new StacksMocknet(), + postConditionMode: PostConditionMode.Allow, + appDetails: { + name: "Lagoon", + icon: "https://freesvg.org/img/bitcoin.png", // You can provide an icon URL for your application + }, + onFinish: (data) => { + console.log(data); + }, + }; + + await openContractCall(options); + }; + + return ( +
{ + e.preventDefault(); + handleDeposit(); + }} + > + setAmount(e.target.value)} + className="w-1/3 px-4 py-2 text-gray-300 bg-gray-700 rounded focus:outline-none focus:border-orange-500" + /> + +
+ ); +} +``` + +### Creating the Borrow UI + +The borrow UI will be very similar. We're doing almost the exact same thing except renaming a couple things and calling the `borrow` function instead of `lend`. + +```jsx +"use client"; + +import React, { useState } from "react"; +import { uintCV, PostConditionMode } from "@stacks/transactions"; +import { openContractCall } from "@stacks/connect"; +import { StacksMocknet, StacksTestnet } from "@stacks/network"; + +export default function BorrowForm() { + const [amount, setAmount] = useState(0); + + const handleDeposit = async () => { + const functionArgs = [ + uintCV(amount), // Convert the amount to uintCV + ]; + + const contractAddress = "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM"; // Replace with your contract address + const contractName = "lagoon"; // Replace with your contract name + const functionName = "borrow"; // Function for deposit + + const options = { + contractAddress, + contractName, + functionName, + functionArgs, + network: new StacksTestnet(), + //network: new StacksMocknet(), + postConditionMode: PostConditionMode.Allow, + appDetails: { + name: "Lagoon", + icon: "https://freesvg.org/img/bitcoin.png", // You can provide an icon URL for your application + }, + onFinish: (data) => { + console.log(data); + }, + }; + + await openContractCall(options); + }; + + return ( +
{ + e.preventDefault(); + handleDeposit(); + }} + > + setAmount(e.target.value)} + className="w-1/3 px-4 py-2 text-gray-300 bg-gray-700 rounded focus:outline-none focus:border-orange-500" + /> + +
+ ); +} +``` + +### Initiating a sBTC Withdrawal + +Now we can mint sBTC, borrow sBTC, and lend out sBTC. Now if we want to convert our sBTC back to BTC, we need to withdraw it. We can do that with a very similar process as the deposit. + +Add the following to the `WithdrawForm.js` file. + +```jsx +"use client"; + +import { useState, useContext } from "react"; +import { + DevEnvHelper, + sbtcWithdrawHelper, + sbtcWithdrawMessage, + TESTNET, + TestnetHelper, +} from "sbtc"; +import { bytesToHex, hexToBytes } from "@noble/hashes/utils"; +import * as btc from "@scure/btc-signer"; +import { openSignatureRequestPopup } from "@stacks/connect"; + +import { UserContext } from "../UserContext"; +import { StacksTestnet } from "@stacks/network"; + +export default function WithdrawForm() { + const { userData, userSession } = useContext(UserContext); + const [satoshis, setSatoshis] = useState(""); + const [signature, setSignature] = useState(""); + + const handleInputChange = (event) => { + setSatoshis(event.target.value); + }; + + const signMessage = async (e) => { + e.preventDefault(); + const message = + sbtcWithdrawMessage({ + network: TESTNET, + amountSats: satoshis, + bitcoinAddress: userData.profile.btcAddress.p2wpkh.testnet, + }); + + openSignatureRequestPopup({ + message, + userSession, + network: new StacksTestnet(), + onFinish: (data) => { + setSignature(data.signature); + }, + }); + }; + + const buildTransaction = async (e) => { + e.preventDefault(); + const testnet = new TestnetHelper(); + // const testnet = new DevEnvHelper(); + + let utxos = await testnet.fetchUtxos( + userData.profile.btcAddress.p2wpkh.testnet + ); + + // get sBTC deposit address from bridge API + const response = await fetch( + "https://bridge.sbtc.tech/bridge-api/testnet/v1/sbtc/init-ui" + ); + const data = await response.json(); + + const tx = await sbtcWithdrawHelper({ + network: TESTNET, + sbtcWalletAddress: data.sbtcContractData.sbtcWalletAddress, + bitcoinAddress: userData.profile.btcAddress.p2wpkh.testnet, + amountSats: satoshis, + signature, + feeRate: await testnet.estimateFeeRate("low"), + fulfillmentFeeSats: 2000, + utxos, + bitcoinChangeAddress: userData.profile.btcAddress.p2wpkh.testnet, + }); + const psbt = tx.toPSBT(); + const requestParams = { + publicKey: userData.profile.btcPublicKey.p2wpkh.testnet, + hex: bytesToHex(psbt), + }; + const txResponse = await window.btc.request("signPsbt", requestParams); + const formattedTx = btc.Transaction.fromPSBT( + hexToBytes(txResponse.result.hex) + ); + formattedTx.finalize(); + const finalTx = await testnet.broadcastTx(formattedTx); + console.log(finalTx); + }; + + return ( +
+ + +
+ ); +} +``` + +There's a lot going on here, so let's break it down. + +First we need to import quite a few different things from `sbtc` and some stacks.js packages. We'll go over what these do as we use them. + +Next we are pulling in our user data and the network we will be working with, testnet in this case. + +Next up we have some similar state variables as before, and an addition one, signature. + +When we initiate withdrawals, we first need to sign a Stacks message proving we are the owner of the account with the sBTC, then we can take that signature and broadcast it with our withdrawal request on the Bitcoin side. + +That's what this `signMessage` function is doing. We are generating our message in a specific format that the sBTC binary expects it, and then signing it, and saving that signature. + +Once that signature is set, we can then broadcast our transaction. + +We do that in a similar way to the deposit function, where we use a helper to build the withdrawal transaction, passing it all the data we need (including the signature we just generated) and using Leather to sign and broadcast it. + +Once that is broadcasted successfully, you'll see the transaction ID logged and you can view it in a block explorer. + +### Wrapping Up + +Congratulations! You just built a basic DeFi app powered by Bitcoin. sBTC is just a baby right now, and many more improvements will be made over the coming months as the system is fully fleshed out. + +In the meantime, continue to explore what it's capable of and keep up with development by checking out [sBTC.tech](https://sbtc.tech) and [Bitcoin Writes](https://bitcoinwrites.com). diff --git a/sbtc/developer-guides/initiating-a-deposit.md b/sbtc/developer-guides/initiating-a-deposit.md new file mode 100644 index 0000000000..3d142cf581 --- /dev/null +++ b/sbtc/developer-guides/initiating-a-deposit.md @@ -0,0 +1,121 @@ +# Initiating a Deposit + +In order to create a deposit and convert our BTC to sBTC, we need to create and broadcast a Bitcoin transaction with the necessary data. + +Below is an example code snippet for doing that using the Leather wallet in a Next.js app. + +If you want to see how to do this in the context of a complete full-stack app, check out the tutorial, which this example was pulled from. We have it here for easy reference. + +This example heavily relies on the `sbtc` package. Documentation is in progress, but you can see the [repo here](https://github.com/hirosystems/stacks.js/tree/feat/add-sbtc-contracts/packages/sbtc) and take a look at the test files to see examples of how to use it to construct transactions. + +```jsx +"use client"; + +import { useState, useContext } from "react"; +import { + DevEnvHelper, + sbtcDepositHelper, + TESTNET, + TestnetHelper, + WALLET_00, + WALLET_01, +} from "sbtc"; +import { bytesToHex, hexToBytes } from "@noble/hashes/utils"; +import * as btc from "@scure/btc-signer"; + +import { UserContext } from "../UserContext"; + +export default function DepositForm() { + const { userData } = useContext(UserContext); + const [satoshis, setSatoshis] = useState(""); + + const handleInputChange = (event) => { + setSatoshis(event.target.value); + }; + + const buildTransaction = async (e) => { + e.preventDefault(); + // Helper for working with various API and RPC endpoints and getting and processing data + // Change this depending on what network you are working with + const testnet = new TestnetHelper(); + // const testnet = new DevEnvHelper(); + + // setting BTC address for devnet + // Because of some quirks with Leather, we need to pull our BTC wallet using the helper if we are on devnet + // const bitcoinAccountA = await testnet.getBitcoinAccount(WALLET_00); + // const btcAddress = bitcoinAccountA.wpkh.address; + // const btcPublicKey = bitcoinAccountA.publicKey.buffer.toString(); + + // setting BTC address for testnet + // here we are pulling directly from our authenticated wallet + const btcAddress = userData.profile.btcAddress.p2wpkh.testnet; + const btcPublicKey = userData.profile.btcPublicKey.p2wpkh; + + let utxos = await testnet.fetchUtxos(btcAddress); + + // If we are working via testnet + // get sBTC deposit address from bridge API + const response = await fetch( + "https://bridge.sbtc.tech/bridge-api/testnet/v1/sbtc/init-ui" + ); + const data = await response.json(); + const sbtcWalletAddress = data.sbtcContractData.sbtcWalletAddress; + + // if we are working via devnet we can use the helper to get the sbtc wallet address, which is associated with the first wallet + // const sbtcWalletAccount = await testnet.getBitcoinAccount(WALLET_00); + // const sbtcWalletAddress = sbtcWalletAccount.tr.address; + + const tx = await sbtcDepositHelper({ + // comment this line out if working via devnet + network: TESTNET, + sbtcWalletAddress, + stacksAddress: userData.profile.stxAddress.testnet, + amountSats: satoshis, + // we can use the helper to get an estimated fee for our transaction + feeRate: await testnet.estimateFeeRate("low"), + // the helper will automatically parse through these and use one or some as inputs + utxos, + // where we want our remainder to be sent. UTXOs can only be spent as is, not divided, so we need a new input with the difference between our UTXO and how much we want to send + bitcoinChangeAddress: btcAddress, + }); + + // convert the returned transaction object into a PSBT for Leather to use + const psbt = tx.toPSBT(); + const requestParams = { + publicKey: btcPublicKey, + hex: bytesToHex(psbt), + }; + // Call Leather API to sign the PSBT and finalize it + const txResponse = await window.btc.request("signPsbt", requestParams); + const formattedTx = btc.Transaction.fromPSBT( + hexToBytes(txResponse.result.hex) + ); + formattedTx.finalize(); + + // Broadcast it using the helper + const finalTx = await testnet.broadcastTx(formattedTx); + + // Get the transaction ID + console.log(finalTx); + }; + + return ( +
+ + +
+ ); +} +``` diff --git a/sbtc/developer-guides/initiating-a-withdrawal.md b/sbtc/developer-guides/initiating-a-withdrawal.md new file mode 100644 index 0000000000..11d942e818 --- /dev/null +++ b/sbtc/developer-guides/initiating-a-withdrawal.md @@ -0,0 +1,141 @@ +# Initiating a Withdrawal + +In order to create a withdrawal and convert our sBTC back to BTC, we need to create and broadcast a Bitcoin transaction with the necessary data in addition to signing a Stacks message to prove we own the sBTC we are trying to withdraw. + +Below is an example code snippet for doing that using the Leather wallet in a Next.js app. + +If you want to see how to do this in the context of a complete full-stack app, check out the tutorial, which this example was pulled from. We have it here for easy reference. + +This example heavily relies on the `sbtc` package. Documentation is in progress, but you can see the [repo here](https://github.com/hirosystems/stacks.js/tree/feat/add-sbtc-contracts/packages/sbtc) and take a look at the test files to see examples of how to use it to construct transactions. + +```jsx +"use client"; + +import { useState, useContext } from "react"; +import { + DevEnvHelper, + sbtcWithdrawHelper, + sbtcWithdrawMessage, + TESTNET, + TestnetHelper, +} from "sbtc"; +import { bytesToHex, hexToBytes } from "@noble/hashes/utils"; +import * as btc from "@scure/btc-signer"; +import { openSignatureRequestPopup } from "@stacks/connect"; + +import { UserContext } from "../UserContext"; +import { StacksTestnet } from "@stacks/network"; + +export default function WithdrawForm() { + const { userData, userSession } = useContext(UserContext); + const [satoshis, setSatoshis] = useState(""); + const [signature, setSignature] = useState(""); + + const handleInputChange = (event) => { + setSatoshis(event.target.value); + }; + + const signMessage = async (e) => { + e.preventDefault(); + + // First we need to sign a Stacks message to prove we own the sBTC + // The sbtc paclage can help us format this + const message = + sbtcWithdrawMessage({ + network: TESTNET, + amountSats: satoshis, + bitcoinAddress: userData.profile.btcAddress.p2wpkh.testnet, + }); + + // Now we can use Leather to sign that message + openSignatureRequestPopup({ + message, + userSession, + network: new StacksTestnet(), + onFinish: (data) => { + // Here we set the signature + setSignature(data.signature); + }, + }); + }; + + const buildTransaction = async (e) => { + // Once the signature has been set, we can build and broadcast the transaction + e.preventDefault(); + // Helper for working with various API and RPC endpoints and getting and processing data + // Change this depending on what network you are working with + const testnet = new TestnetHelper(); + // const testnet = new DevEnvHelper(); + + // setting BTC address for devnet + // Because of some quirks with Leather, we need to pull our BTC wallet using the helper if we are on devnet + // const bitcoinAccountA = await testnet.getBitcoinAccount(WALLET_00); + // const btcAddress = bitcoinAccountA.wpkh.address; + // const btcPublicKey = bitcoinAccountA.publicKey.buffer.toString(); + + // setting BTC address for testnet + // here we are pulling directly from our authenticated wallet + const btcAddress = userData.profile.btcAddress.p2wpkh.testnet; + const btcPublicKey = userData.profile.btcPublicKey.p2wpkh; + + let utxos = await testnet.fetchUtxos(btcAddress); + + // If we are working via testnet + // get sBTC deposit address from bridge API + const response = await fetch( + "https://bridge.sbtc.tech/bridge-api/testnet/v1/sbtc/init-ui" + ); + const data = await response.json(); + const sbtcWalletAddress = data.sbtcContractData.sbtcWalletAddress; + + // if we are working via devnet we can use the helper to get the sbtc wallet address, which is associated with the first wallet + // const sbtcWalletAccount = await testnet.getBitcoinAccount(WALLET_00); + // const sbtcWalletAddress = sbtcWalletAccount.tr.address; + + const tx = await sbtcWithdrawHelper({ + // comment this line out if working via devnet + network: TESTNET, + sbtcWalletAddress, + bitcoinAddress: btcAddress, + amountSats: satoshis, + signature, + feeRate: await testnet.estimateFeeRate("low"), + fulfillmentFeeSats: 2000, + utxos, + bitcoinChangeAddress: btcAddress, + }); + const psbt = tx.toPSBT(); + const requestParams = { + publicKey: btcPublicKey, + hex: bytesToHex(psbt), + }; + const txResponse = await window.btc.request("signPsbt", requestParams); + const formattedTx = btc.Transaction.fromPSBT( + hexToBytes(txResponse.result.hex) + ); + formattedTx.finalize(); + const finalTx = await testnet.broadcastTx(formattedTx); + console.log(finalTx); + }; + + return ( +
+ + +
+ ); +} +``` diff --git a/sbtc/developer-guides/local-environment-setup.md b/sbtc/developer-guides/local-environment-setup.md new file mode 100644 index 0000000000..035b2535b7 --- /dev/null +++ b/sbtc/developer-guides/local-environment-setup.md @@ -0,0 +1,98 @@ +# Local Environment Setup + +Devnet is a setup with a local bitcoin node running regtest, a local stacks node running testnet and connecting to the local bitcoin node. In addition, explorers and api servers are setup to provide easy access to information. + +Developers should use docker to setup all required services. + +Here are the basic steps we'll need to follow to get everything running. + +1. Launch devnet. +2. Deploy sbtc contract (happens automatically). +3. Launch sBTC binary (Alpha romeo engine). +4. Use sBTC web app (sBTC Bridge) or sbtc cli or your own app. + +#### Requirements + +* Install [Docker](https://docs.docker.com/engine/install/). +* Install [Clarinet](https://github.com/hirosystems/clarinet) (version 1.7.0 or higher). +* _Mac OS X_: Install [jq](https://formulae.brew.sh/formula/jq) + +### Launch Devnet + +We'll get sBTC up and running locally using the [sBTC devenv](https://github.com/stacks-network/sbtc/blob/main/devenv/README.md). You'll need to make sure you have Docker and Docker Compose installed on your system. + +First, make sure you have the [`sbtc`](https://github.com/stacks-network/sbtc) repo cloned locally. + +Now let's get everything running. Make sure you are in the `devenv` folder and run: + +```bash +./build.sh +``` + +This first build will take a while to complete, but once that's done we just need to run everything with: + +```bash +./up.sh +``` + +Let's check to make sure things are running by visiting the Stacks explorer and the Bridge app. The Stacks explorer will take a bit get up and running. + +* [http://127.0.0.1:3020/?chain=testnet\&api=http://127.0.0.1:3999](http://127.0.0.1:3020/?chain=testnet\&api=http://127.0.0.1:3999) (Stacks explorer) +* [http://127.0.0.1:8083](http://127.0.0.1:8083) (Bitcoin explorer) +* [http://127.0.0.1:8080](http://127.0.0.1:8080) (sBTC Bridge App) + +### Deploy Contracts + +The deployment of the Clarity contracts `asset.clar` and `clarinet-bitcoin-mini.clar` happens during the launch of devenv.sbtcWalletAddress + +Under the hood, there is a script `utils/deploy_contracts.sh` that is used to deploy the contracts The local environment defines wallets that are already funded. The deployer wallet is used to deploy the contract using clarinet deployments. + +### Prepare Wallet + +You can take a look at the config file at `sbtc/devenv/sbtc/docker/config.json` to see the mnemonic that defines the taproot address that will be the contract owner for sBTC. This is the mnemonic that generates the address that will actually call the `mint` function in the sBTC contract. + +If you take a look at the `sbtc/romeo/asset-contract/settings/Devnet.toml` you can see this mnemonic associated with a specific Stacks address. This will also be the address that will be used to deploy the actual contracts. + +Download the [Leather wallet](https://leather.io) (version 6.11.+) and import the deployer address mnemonic into it to load that account locally. + +``` +twice kind fence tip hidden tilt action fragile skin nothing glory cousin green tomorrow spring wrist shed math olympic multiply hip blue scout claw +``` + +You can see more default generated mnemonics for the devnet in the [`devenv` repo](https://github.com/stacks-network/sbtc/devenv). + +In Leather wallet, you see that Account 1 is the deployer. Nevertheless, you can use this account or Account 2 as a normal user. + +### Mint some BTC and sBTC + +There are helper scripts to create and broadcast deposit and withdrawal requests. They are contained in the `utils` folder of devenv: + +1. mint\_btc.sh: funds the btc addresses with BTC. Add the number of blocks to mint for the two addresses as parameter. +2. deposit.sh: deposits a random amount of less than 10,000 sats. sBTC will be received by the deployer stacks address. +3. withdrawal.sh: withdraws a random amount of less than 2,000 sats. + +Before running these, make sure the Stacks devnet is running by visiting the explorer at [http://127.0.0.1:3020/transactions?chain=testnet\&api=http://127.0.0.1:3999](http://127.0.0.1:3020/transactions?chain=testnet\&api=http://127.0.0.1:3999). + +If you get an error, wait a few minutes and refresh. Once that page loads correctly you should be good to go. + +If you are using the Leather wallet, make sure to use the same secret key as used in devnet (deployer wallet). If you are using a different secret key you'll want to run this again and make sure that this is mining to the same wallet you are going to use in your sBTC app. To do that, view the Bitcoin address displayed in Leather (make sure you are on Devnet) and add it to the `mine_btc.sh` script at the end like this: + +```bash +btc_address='bcrt1q3tj2fr9scwmcw3rq5m6jslva65f2rqjxfrjz47' +``` + +First, let's mine some BTC with `./utils/mine_btc.sh 200`. This will mine 200 BTC blocks and ensure our address (Account 1 and Account 2) is funded. + +Next we can initiate a deposit, which will deposit a random amount of satoshis from our Bitcoin wallet (Account 2) into the sBTC threshold wallet, minting sBTC in the process. + +We can do that with `./utils/deposit.sh`. + +And finally, we can do the reverse, convert our sBTC back to BTC, with `./utils/withdraw.sh`, which will print the txid of the withdrawal transaction on completion. + +Check the results on Stacks at our address: [http://localhost:3020/address/ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM?chain=testnet\&api=http://localhost:3999](http://localhost:3020/address/ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM?chain=testnet\&api=http://localhost:3999) + +Check the results on Bitcoin at the txs printed by the utility functions, eg. [http://127.0.0.1:8083/tx/38089273be6ed7521261c3a3a7d1bd36bc67d97123c27263e880350fc519899d](http://127.0.0.1:8083/tx/38089273be6ed7521261c3a3a7d1bd36bc67d97123c27263e880350fc519899d), replacing the txid parameter with the given tx id. + +### Next Steps + +Congratulations! You are among the first people in the world to run sBTC. Now you're ready to actually build something. For that, head over to the End to End Tutorial and we'll build a simple full-stack Bitcoin lending application powered by sBTC. diff --git a/sbtc/developer-guides/quickstart.md b/sbtc/developer-guides/quickstart.md new file mode 100644 index 0000000000..f611773081 --- /dev/null +++ b/sbtc/developer-guides/quickstart.md @@ -0,0 +1,23 @@ +# Quickstart + +If you are a developer looking to start building with sBTC, this is the place to start. + +### 1. Familiarize yourself with sBTC + +First, if you aren't familiar with what sBTC is or how it works, be sure to check out the [sBTC Design documentation](../sbtc-design/). + +### 2. Learn about the Developer Release + +The [Developer Release](../sbtc-releases/sbtc-0.1.md) is the very first version of sBTC, and is designed to provide a preview version of sBTC for developers to learn and experiment with. + +### 3. Get setup with a local dev environment + +Once you understand how it works and what the Developer Release is, it's time to get the sBTC development environment set up locally with the [Local Setup Guide](local-environment-setup.md). + +### 4. Learn sBTC development + +Now it's time to get up and running with a complete full-stack sBTC-powered application. The [End to End Tutorial](end-to-end-tutorial.md) will teach you how to build an app using Next, Clarity, Stacks.js, and sBTC from start to finish. + +### 5. Keep Going + +Now that you've got the basics down, you can refer to the [deposit](../user-guides/how-to-deposit-and-withdraw.md) and [withdrawal](initiating-a-withdrawal.md) code examples for a quick reference on how to initiate deposits and withdrawals with Stacks.js and the Leather wallet. diff --git a/sbtc/introduction.md b/sbtc/introduction.md new file mode 100644 index 0000000000..d4d505d097 --- /dev/null +++ b/sbtc/introduction.md @@ -0,0 +1,51 @@ +# Introduction + +{% hint style="warning" %} +sBTC is under heavy development, and much of this information will change as design and architecture decisions are fleshed out. This documentation represents the most accurate understanding of how sBTC will function as of January 22, 2024. +{% endhint %} + +Welcome to the sBTC developer documentation. + +Here you will find the most up-to-date description of what sBTC is and how it is implemented. + +## Introduction to sBTC + +To understand sBTC, we first need to understand the current limitations of Bitcoin (BTC). + +Bitcoin is to date the most secure and decentralized blockchain. While Bitcoin is the largest cryptocurrency by market cap, comparatively few applications exist within the Bitcoin ecosystem. Developers interested in building applications for the Bitcoin community often find it difficult or impossible to implement their logic directly on the Bitcoin chain. Although Bitcoin has a simple scripting system built in, it lacks the expressiveness of many other smart contract languages. + +sBTC is for: + +* Bitcoin holders who want to participate in smart contracts. +* Developers who want to build applications on Bitcoin. + +sBTC empowers developers to build applications on Bitcoin by bridging Bitcoin and [Stacks](https://www.stacks.co/). We achieve this by introducing a fungible token (sBTC) on the Stacks blockchain. The token has the following properties: + +* **1:1 redeemability**. sBTC can always be exchanged 1:1 for BTC on the Bitcoin chain, as long as the Stacks blockchain is operational. +* **Open membership**. Anyone can participate in the sBTC protocol. No centralized entity maintains custody over any BTC in the protocol. + +Other tokens which try to achieve the same end as sBTC are + +* [xBTC](https://www.stacks.co/blog/tokensoft-wrapped-fundamental-bitcoin-defi-building-blocks-xbtc) + +While these tokens all achieve the same value as BTC, they maintain BTC reserves through trusted entities. sBTC is the only truly decentralized Bitcoin backed asset on Stacks. + +## How does sBTC work? + +Bitcoin holders can do two things to interact with sBTC, deposit and withdraw. Both of these operations are controlled through special Bitcoin transactions. + +To deposit BTC into sBTC, a Bitcoin holder would create a deposit transaction on the Bitcoin chain. This deposit transaction informs the protocol how much BTC the holder has deposited, and to which Stacks address the holder wishes to receive the sBTC. The sBTC system responds to the deposit transaction by minting sBTC to the given Stacks address. + +To withdraw BTC, a Bitcoin holder creates a withdrawal transaction on the Bitcoin chain. This withdrawal transaction informs the protocol how much sBTC the holder wishes to withdraw, from which stacks address the sBTC should be withdrawn and which Bitcoin address should receive the withdrawn BTC. In response to this transaction, the sBTC system burns the requested amount of sBTC from the given Stacks address and fulfills the withdrawal by issuing a BTC payment to the given BTC address with the same amount. + +The following diagram illustrates the deposit and withdrawal flows. + +
+ +## Where to go next? + +If you want to use sBTC as user, check out the [User Guides](user-guides/). + +If you're a developer looking to build applications with sBTC, check out the [Quickstart](developer-guides/quickstart.md). + +If you want to dive into the details and understand how sBTC achieves a secure open-membership wrapping of Bitcoin, look into the design documentation. A good start is the [sBTC Design Overview](sbtc-design/). diff --git a/sbtc/sbtc-design/README.md b/sbtc/sbtc-design/README.md new file mode 100644 index 0000000000..b296183435 --- /dev/null +++ b/sbtc/sbtc-design/README.md @@ -0,0 +1,18 @@ +# sBTC Design + +In Introduction to sBTC we established that sBTC is a fungible token on the Stacks blockchain, and explained how users interact with the protocol. This chapter takes a closer look at the major entities in the sBTC protocol and briefly explain how they interact. The following chapters goes into more details on each component. + +sBTC builds on the Proof-of-Transfer (PoX) consensus mechanism defined in [SIP-007](https://github.com/stacksgov/sips/blob/main/sips/sip-007/sip-007-stacking-consensus.md). This SIP introduces the concept of stacking, which is to lock STX for a period of time to earn Bitcoin rewards. Stacking is performed through a special smart contract, called the PoX contract. People who stack are called stackers. + +In sBTC we introduce the following changes to Stacks consensus: + +* A new Clarity contract is created to include sBTC as a [SIP-010 fungible token](https://github.com/stacksgov/sips/blob/main/sips/sip-010/sip-010-fungible-token-standard.md). +* Stacks miners must include sBTC mint and burn transactions in their blocks in response to valid sBTC requests on the Bitcoin chain. +* Stackers must collectively generate a Bitcoin address every reward cycle and publish it in the PoX contract as the sBTC wallet address. +* Stackers are required to respond to sBTC withdrawal requests. + +The following chart illustrates the main components of sBTC. + +
+ +Now that we have established the main components of sBTC, we're ready to dig deeper in the actual workings of it. The following three chapters explains different aspects of the sBTC design and can be read in any order. diff --git a/sbtc/sbtc-design/sbtc-requests-and-responses/README.md b/sbtc/sbtc-design/sbtc-requests-and-responses/README.md new file mode 100644 index 0000000000..0e407b7913 --- /dev/null +++ b/sbtc/sbtc-design/sbtc-requests-and-responses/README.md @@ -0,0 +1,40 @@ +# sBTC Requests and Responses + +Requests to the sBTC system happen on the Bitcoin blockchain. In this chapter, we explain the two requests that users can create. We go over all information they must contain, and how the sBTC protocol responds to the requests. For a more in-depth reference on how these requests are represented on the Bitcoin blockchain, see Bitcoin Transactions. + +### Deposit Request + +When a user wishes to deposit BTC in favor of receiving sBTC, they create a deposit request transaction. This is a bitcoin transaction sending the requested deposit amount of BTC to the address provided by the Stackers. In addition, the transaction must also specify to which Stacks address the sBTC should be minted. + +The sBTC deposit request transaction will therefore contain the following data: + +* Recipient address: The Stacks address which should receive the sBTC. +* sBTC wallet address: The Bitcoin address maintaining custody of the deposited BTC. +* Amount: The amount to deposit. + +#### How the protocol responds to a deposit request + +When a deposit request is mined on the Bitcoin blockchain, the next Stacks block must contain an sBTC mint transaction to the recipient address with the designated amount. + +This is enforced on a consensus level in Stacks, so that Stacks blocks which do not respond to deposit requests are considered invalid by Miners. + +If for some reason the sBTC mint does not materialize, there is a timeout function that will return the user's BTC to them once the subsequent reward cycle finishes. + +### Withdrawal Request + +An sBTC withdraw request is a bitcoin transaction containing data and two outputs. The first output of this transaction marks the recipient address of the BTC to be withdrawn. The second output of this transaction is a small fee subsidy to the stackers intended to cover the transaction cost of fulfilling the withdrawal. Finally, the transaction specifies the amount to be withdrawn and signs the amount and recipient address with the Stacks address from which the sBTC should be burned. + +To summarize, the sBTC withdrawal transaction will contain the following data: + +* Recipient address: The Bitcoin address which should receive the BTC. +* sBTC wallet address: The Bitcoin address maintaining custody of the deposited BTC. +* Amount: The amount to withdraw. +* Sender address: The Stacks address holding the sBTC to be burned. + +#### How the protocol responds to a withdrawal request + +When a withdrawal request is mined on the Bitcoin blockchain, the next Stacks block must contain a sBTC burn transaction burning the requested amount from the sender address. Once the withdrawal request is final[^1] on the Stacks blockchain, Stackers must fulfill the withdrawal request on the Bitcoin chain by creating a fulfillment transaction. + +The fulfillment transaction is a bitcoin transaction sending the requested withdrawal amount to the designated recipient address specified in the Withdrawal request. + +[^1]: Block finality is a property introduced in the [Nakamoto release](https://stx.is/nakamoto) of Stacks, and a requirement for sBTC. diff --git a/sbtc/sbtc-design/sbtc-requests-and-responses/clarity-contracts.md b/sbtc/sbtc-design/sbtc-requests-and-responses/clarity-contracts.md new file mode 100644 index 0000000000..fbbf1e0092 --- /dev/null +++ b/sbtc/sbtc-design/sbtc-requests-and-responses/clarity-contracts.md @@ -0,0 +1,263 @@ +# Clarity Contracts + +One of the key pieces of the sBTC system is the set of Clarity contracts that facilitate the operations of the sBTC token. + +Recall that sBTC is a [SIP-010 fungible token](https://github.com/stacksgov/sips/blob/main/sips/sip-010/sip-010-fungible-token-standard.md) on the Stacks chain. This provides an easy-to-use interface for interacting with sBTC. + +Upon successful deposit and withdrawal transactions the signer system will call functions in this contract. Here we'll walk through the contracts and explain what each piece is doing so you have a thorough understanding of the Clarity code running sBTC. + +:::note It's important to note that sBTC is currently in Developer Release. This is a developer preview so developers can begin learning and experimenting with sBTC before moving to the fully decentralized version. As such, these contracts are subject to change. ::: + +The Clarity contracts live in the [`romeo/asset-contract/contracts`](https://github.com/stacks-network/sbtc/tree/main/romeo/asset-contract/contracts) folder of the [`sbtc` repo](https://github.com/stacks-network/sbtc). + +In that folder you'll see three files: + +* `asset.clar` +* `clarity-bitcoin-mini-deploy.clar` +* `clarity-bitcoin-mini.clar` + +The `asset` contract is what does the heavy lifting for sBTC operations. The `clarity-bitcoin-mini` library is a stateless utility library to make it easier to interact with Bitcoin. This is a key feature of sBTC and this library provides several helper functions to handle that. + +The `clarity-bitcoin-mini-deploy` contract is exactly the same, except debug mode is set to false for production. + +Now, let's go through the `asset` contract. + +```clojure +;; title: wrapped BTC on Stacks +;; version: 0.1.0 +;; summary: sBTC dev release asset contract +;; description: sBTC is a wrapped BTC asset on Stacks. +;; It is a fungible token (SIP-10) that is backed 1:1 by BTC +;; For this version the wallet is controlled by a centralized entity. +;; sBTC is minted when BTC is deposited into the wallet and +;; burned when BTC is withdrawn from the wallet. +;; Requests for minting and burning are made by the contract owner. + +;; token definitions +;; 100 M sats = 1 sBTC +;; 21 M sBTC supply = 2.1 Q sats total +(define-fungible-token sbtc u2100000000000000) + +;; constants +;; +(define-constant err-invalid-caller (err u4)) +(define-constant err-forbidden (err u403)) +(define-constant err-btc-tx-already-used (err u500)) + +;; data vars +;; +(define-data-var contract-owner principal tx-sender) +(define-data-var bitcoin-wallet-public-key (optional (buff 33)) none) + +;; stores all btc txids that have been used to mint or burn sBTC +(define-map amounts-by-btc-tx (buff 32) int) + +;; public functions +;; + +;; #[allow(unchecked_data)] +(define-public (set-contract-owner (new-owner principal)) + (begin + (try! (is-contract-owner)) + (ok (var-set contract-owner new-owner)) + ) +) + +;; #[allow(unchecked_data)] +(define-public (set-bitcoin-wallet-public-key (public-key (buff 33))) + (begin + (try! (is-contract-owner)) + (ok (var-set bitcoin-wallet-public-key (some public-key))) + ) +) + +;; #[allow(unchecked_data)] +(define-public (mint (amount uint) + (destination principal) + (deposit-txid (buff 32)) + (burn-chain-height uint) + (merkle-proof (list 14 (buff 32))) + (tx-index uint) + (block-header (buff 80))) + (begin + (try! (is-contract-owner)) + (try! (verify-txid-exists-on-burn-chain deposit-txid burn-chain-height merkle-proof tx-index block-header)) + (asserts! (map-insert amounts-by-btc-tx deposit-txid (to-int amount)) err-btc-tx-already-used) + (try! (ft-mint? sbtc amount destination)) + (print {notification: "mint", payload: deposit-txid}) + (ok true) + ) +) + +;; #[allow(unchecked_data)] +(define-public (burn (amount uint) + (owner principal) + (withdraw-txid (buff 32)) + (burn-chain-height uint) + (merkle-proof (list 14 (buff 32))) + (tx-index uint) + (block-header (buff 80))) + (begin + (try! (is-contract-owner)) + (try! (verify-txid-exists-on-burn-chain withdraw-txid burn-chain-height merkle-proof tx-index block-header)) + (asserts! (map-insert amounts-by-btc-tx withdraw-txid (* -1 (to-int amount))) err-btc-tx-already-used) + (try! (ft-burn? sbtc amount owner)) + (print {notification: "burn", payload: withdraw-txid}) + (ok true) + ) +) + +;; #[allow(unchecked_data)] +(define-public (transfer (amount uint) (sender principal) (recipient principal) (memo (optional (buff 34)))) + (begin + (asserts! (or (is-eq tx-sender sender) (is-eq contract-caller sender)) err-invalid-caller) + (try! (ft-transfer? sbtc amount sender recipient)) + (match memo to-print (print to-print) 0x) + (ok true) + ) +) + +;; read only functions +;; +(define-read-only (get-bitcoin-wallet-public-key) + (var-get bitcoin-wallet-public-key) +) + +(define-read-only (get-contract-owner) + (var-get contract-owner) +) + +(define-read-only (get-name) + (ok "sBTC") +) + +(define-read-only (get-symbol) + (ok "sBTC") +) + +(define-read-only (get-decimals) + (ok u8) +) + +(define-read-only (get-balance (who principal)) + (ok (ft-get-balance sbtc who)) +) + +(define-read-only (get-total-supply) + (ok (ft-get-supply sbtc)) +) + +(define-read-only (get-token-uri) + (ok (some u"https://gateway.pinata.cloud/ipfs/Qma5P7LFGQAXt7gzkNZGxet5qJcVxgeXsenDXwu9y45hpr?_gl=1*1mxodt*_ga*OTU1OTQzMjE2LjE2OTQwMzk2MjM.*_ga_5RMPXG14TE*MTY5NDA4MzA3OC40LjEuMTY5NDA4MzQzOC42MC4wLjA")) +) + +(define-read-only (get-amount-by-btc-txid (btc-txid (buff 32))) + (map-get? amounts-by-btc-tx btc-txid) +) + +;; private functions +;; +(define-private (is-contract-owner) + (ok (asserts! (is-eq (var-get contract-owner) contract-caller) err-forbidden)) +) + +(define-read-only (verify-txid-exists-on-burn-chain (txid (buff 32)) (burn-chain-height uint) (merkle-proof (list 14 (buff 32))) (tx-index uint) (block-header (buff 80))) + (contract-call? .clarity-bitcoin-mini was-txid-mined burn-chain-height txid block-header { tx-index: tx-index, hashes: merkle-proof}) +) +``` + +Although powerful, the sBTC contract is actually relatively simple. The `mint` and `burn` functions of the contract will not be interacted with directly by a developer or a user, but instead will be called by the sBTC signer system upon successful deposit and withdrawal requests. The `transfer` function, however, will often be called by developers for users looking to transfer their sBTC. + +Up at the top we are setting some variables, most notably the sBTC supply, which is set to match 1:1 with BTC supply. + +We also have the contract owner and Bitcoin wallet public key which will be set by the sBTC protocol. As per the design documentation, the Bitcoin public key will change every stacking reward cycle to the new threshold wallet. + +Next we have two basic public functions to set those two variables. + +Finally we are defining a map to define all deposit and withdrawal transaction IDs to ensure that once a particular Bitcoin transaction has been used in a sBTC operation, it can not be used again. + +The next three functions comprise the bulk of the sBTC functionality. + +Before we get to those, let's take a look at the `verify-txid-exists-on-burn-chain` towards the bottom of the contract. This utilizes the helper contract to ensure that a transaction actually happened on the Bitcoin chain. + +This native integration with the Bitcoin L1 is one of the great parts of Clarity, as we can verify directly from our smart contracts whether or not a Bitcoin transaction was actually mined, all on chain. + +```clojure +(define-read-only (verify-txid-exists-on-burn-chain (txid (buff 32)) (burn-chain-height uint) (merkle-proof (list 14 (buff 32))) (tx-index uint) (block-header (buff 80))) + (contract-call? .clarity-bitcoin-mini was-txid-mined burn-chain-height txid block-header { tx-index: tx-index, hashes: merkle-proof}) +) +``` + +This takes in a transaction ID, the Bitcoin block height the transaction was in, and a merkle proof. All of this information is passed to the library to verify that the transaction actually occurred. + +For some more context on how this process works and to see how to use it your own projects, be sure to check out the [Bitcoin Primer](https://bitcoinprimer.dev). + +### Mint + +```clojure +;; #[allow(unchecked_data)] +(define-public (mint (amount uint) + (destination principal) + (deposit-txid (buff 32)) + (burn-chain-height uint) + (merkle-proof (list 14 (buff 32))) + (tx-index uint) + (block-header (buff 80))) + (begin + (try! (is-contract-owner)) + (try! (verify-txid-exists-on-burn-chain deposit-txid burn-chain-height merkle-proof tx-index block-header)) + (asserts! (map-insert amounts-by-btc-tx deposit-txid (to-int amount)) err-btc-tx-already-used) + (try! (ft-mint? sbtc amount destination)) + (print {notification: "mint", payload: deposit-txid}) + (ok true) + ) +) +``` + +Now we get to the `mint` function. This is the function that is called when a deposit transaction is initiated and processed on the Bitcoin side. The signer will detect that and call the `mint` function. + +This function takes in some information that is all read directly from the provided Bitcoin transaction. It includes the Stacks principal to mint the sBTC to, and all of the required Bitcoin transaction information required to verify it. + +It then checks to make sure the contract owner (the signer system) is calling it, makes sure the Bitcoin transaction actually happened, updates the map of Bitcoin transactions that have been used in sBTC operations, and mints the token to the specified Stacks principal. + +### Burn + +```clojure +;; #[allow(unchecked_data)] +(define-public (burn (amount uint) + (owner principal) + (withdraw-txid (buff 32)) + (burn-chain-height uint) + (merkle-proof (list 14 (buff 32))) + (tx-index uint) + (block-header (buff 80))) + (begin + (try! (is-contract-owner)) + (try! (verify-txid-exists-on-burn-chain withdraw-txid burn-chain-height merkle-proof tx-index block-header)) + (asserts! (map-insert amounts-by-btc-tx withdraw-txid (* -1 (to-int amount))) err-btc-tx-already-used) + (try! (ft-burn? sbtc amount owner)) + (print {notification: "burn", payload: withdraw-txid}) + (ok true) + ) +) +``` + +The `burn` function works much the same except it is called upon a successful withdrawal request, when a user wants to convert their sBTC back to BTC. + +### Transfer + +```clojure +;; #[allow(unchecked_data)] +(define-public (transfer (amount uint) (sender principal) (recipient principal) (memo (optional (buff 34)))) + (begin + (asserts! (or (is-eq tx-sender sender) (is-eq contract-caller sender)) err-invalid-caller) + (try! (ft-transfer? sbtc amount sender recipient)) + (match memo to-print (print to-print) 0x) + (ok true) + ) +) +``` + +Finally we have a basic transfer function that allows users to transfer sBTC between each other. + +The rest of the functions are basic `read-only` functions that are used to retrieve information about the asset. diff --git a/sbtc/sbtc-design/sbtc-requests-and-responses/commit-reveal-system.md b/sbtc/sbtc-design/sbtc-requests-and-responses/commit-reveal-system.md new file mode 100644 index 0000000000..553440e100 --- /dev/null +++ b/sbtc/sbtc-design/sbtc-requests-and-responses/commit-reveal-system.md @@ -0,0 +1,94 @@ +# Commit-Reveal System + +Thus far, we have have been introduced to some of the Bitcoin transactions that make it possible to interact with sBTC. However, one big drawback of the transaction formats we have been talking about is that they require the creation of custom Bitcoin transactions, which is a moderately sophisticated use case of Bitcoin and thus remains unsupported by some wallets as well as a lot of custodian solutions that are widely used in the current landscape. Cutting out this portion of the Bitcoin ecosystem from being able to use sBTC is not ideal. As a protocol, it is super important that sBTC is accessible to anyone who wants to use it, and this translates to allowing sBTC compatible transactions to be sent from any viable Bitcoin wallet. + +To accommodate for this use case, sBTC also supports an alternate transaction wire-format that has universal compatibility with any Bitcoin wallet. Since this scheme uses two transactions to fulfill a sBTC operation, we call it the commit-reveal scheme. As the name suggests, one of the transactions _commits_ the intent of the user and the second transaction _reveals_ it to the protocol. + +Note that commit-reveal does not introduce new sBTC operations, but rather an alternate way to _express_ the same operations . From the perspective of the sBTC protocol, these two schemes are completely compatible and interchangeable with each other, in terms of how they are interpreted by the protocol and the effects they produce. + +Let's dig a little deeper into the details. + +### Operation format + +Fundamentally, all sBTC transactions on Bitcoin have to embed some data into the blockchain itself that highlights the intent of the user to interact with the sBTC protocol. The protocol then identifies these intents from the chain and verifies and executes them, thus executing the intent of the user that was previously embedded in the chain. + +As long as we have a reliable way to achieve this cycle of embedding an intent into Bitcoin, reading it and processing it, we can fulfill any sBTC operation. Both the direct scheme (SIP-021 style transactions) and transactions that fulfill commit-reveal scheme achieve this, only differing slightly in how the data is embedded into the chain itself. + +In the direct scheme, we embed the data directly in the transaction itself, by using an `OP_RETURN` output. + +In the commit reveal scheme, this embedding is done in two stages: the commit transaction and the reveal transaction. + +#### Commit transaction + +The commit transaction is a very simple transaction with only one requirement: it must contain an output to either a `p2tr`, `p2wsh`, `p2sh-p2wsh` or `p2sh` address. We need to use these types of addresses because all of them require a user to reveal a script to be spent. We require the revealed script to have the following format: + +``` + OP_DROP +``` + +where the `DATA` part of the script is similar to the corresponding data format of the direct scheme using `OP_RETURN`, minus the first two magic bytes (that part will be dealt with in the reveal transaction that follows). The data is at most 86 bytes long (the opcode + payload is at most 78 bytes) and also contains an 8 byte chunk that specifies a fee subsidy, i.e. the amount of funds that the reveal transaction is allowed to spend as transaction fees. + +The `DATA` section of the script thus looks like this: + +``` +0 1 n n + 8 +|--|----------------------------|--------------| + op sBTC payload fee subsidy +``` + +where the first byte is the opcode of the sBTC transaction, the payload is essential data required for the specific sBTC operation and the fee subsidy limits the maximum amount of money the reveal transaction is allowed to use as fees. + +#### Reveal transaction + +The reveal transaction is also fairly simple in construction and MUST satisfy the following: + +1. It MUST consume an UTXO from a commit transaction as its first input. +2. The first output MUST be an `OP_RETURN` output with a three byte payload where the first two bytes are the magic bytes (the same ones we promised to add back) that specify the network they are running on - `T2` for mainnet and `X2` for testnet, and the last two bytes is an opcode and a script version byte. + + ``` + 0 2 3 4 + |------|--|---------| + magic op version + ``` + +The opcode identifies which type of script is revealed. It is `w` if the script is embedded in segwit witness data, and `r` if the script is in a p2sh redeem script. + +The version identifies the SegWit witness version. It is `0` for `p2wsh` scripts and `1` for `p2tr` scripts. If the opcode is `r`, this version byte may be omitted. + +Because the reveal transaction consumes the UTXO from the commit transaction, the data that was embedded in the script of the commit transaction is _revealed_. Thus, when the sBTC protocol observes a bitcoin operation with the opcode `w` or `r`, it indicates a reveal transaction and the data for the intended operation by the initiator of the commit transaction can be found in either the witness or redeem script of the first input. + +Any remaining outputs of the reveal transaction must be of the same form as in the direct scheme. For instance, the reveal transaction representing an sBTC withdrawal request must contain two additional outputs (just like its direct scheme counterpart) in order: + +1. the BTC recipient address +2. the funding of the fulfillment transaction. + +### Processing the commit-reveal scheme at the protocol level + +Now that we understand how the low level representations of commit-reveal transactions and what they represent, we need to talk about how the sBTC protocol itself interacts with the scheme to ensure fulfillment of such transactions. + +On a high level, this diagram summarizes the interactions between the various parties involved in the fulfillment of the commit-reveal scheme: + +
+ +More importantly there are three parts of the process that need to interact: + +1. The _Committer_ (the person the submitting the _commit_ transaction, initiating the scheme) +2. The _Revealer_ (the person that consumes _commit_ transactions and initiates reveal transactions) +3. The Bitcoin blockchain itself, which provides the underlying data layer which is used to express the scheme + +The _Committer_ can be thought of as a system that interacts with a user wallet and constructs _commit_ transactions (like the sBTC bridge). + +The _Revealer_ can be thought of a specific system that participates in the sBTC protocol, maintaining a wallet and can consume _commit_ transactions and broadcast _reveal_ transactions. They probably have convenient API endpoints for the _Committer_ to use to construct Witness data. + +Here is an example flow of data through the protocol (for illustration purposes only): + +1. User interacts with a web UI of a _Committer_, providing operation data to construct the commit transaction +2. The _Committer_ constructs a witness script that can be spent by the _Revealer_ +3. The _Committer_ then sends this witness script and any other associated data that might be required to construct a reveal transaction to the _Revealer_ +4. The _Committer_ returns the address to send the _commit_ operation to the user +5. User broadcasts the _commit_ transaction by making a payment to the given commit address +6. The _Revealer_ reads the _commit_ transaction, having already processed the witness script +7. The _Revealer_ broadcasts the _reveal_ transaction (there can be economic incentives here that makes the _Revealer_ not reveal a _commit_ transaction. For example, if the cost of revealing the transaction is too high, the _Revealer_ might choose to ignore it altogether) +8. The sBTC protocol picks up the _reveal_ transaction and fulfills it (by interpreting the operation data from the witness script) + +NOTE: _It is entirely possible for the Revealer to steal the witness data and use it for its own benefit, although this will be entire visible on the Bitcoin chain data itself and can be completely traced. Thus, there needs to be some degree of cryptoeconomic incentives present that discourage the Revealer from doing this._ diff --git a/sbtc/sbtc-design/security-model.md b/sbtc/sbtc-design/security-model.md new file mode 100644 index 0000000000..c5470b26a3 --- /dev/null +++ b/sbtc/sbtc-design/security-model.md @@ -0,0 +1,69 @@ +# Security Model + +Unlike other BTC bridges, sBTC is not supported by a central custodian or a federation: instead, sBTC is secured by an open network of signers, making sBTC the most decentralized, Bitcoin-backed asset. + +In this article, we’ll cover the fundamentals of the sBTC security model, the role of sBTC Signers, and how the sBTC working group is enabling a more scalable sBTC protocol design. + +### A Bitcoin-Backed Asset Worthy of Bitcoin + +sBTC embraces the core principles of Bitcoin: decentralization, permissionless, and trust-minimization. The system is: + +* Open Membership: sBTC operates as an open network, where anyone can become a validator, increasing the decentralization and inclusivity within the sBTC ecosystem. In contrast, other alternatives have closed groups of signers. +* Decentralized: The software libraries for sBTC can support over 150 validators, surpassing the limitations of current multi-sig implementations. This allows for a broader and more diverse set of participants, further strengthening decentralization. +* Trust-Minimized: sBTC provides users and developers with a trust-minimized option—you interact with a decentralized network instead of a central custodian or small group of signers, aligning with the core principles of Bitcoin. +* Collateral-Backed: Unlike alternative multi-sig approaches, which have zero collateral backing their respective Bitcoin pegs, sBTC has $250M+ of collateral in STX tokens deposited in the protocol, providing a substantial incentive to maintain the peg. + +### Who Are the Signers? + +The sBTC asset is supported by this network of “validators.” There can be up to 150 of them, and anyone can join/leave the signer network as they desire. We expect these validators will be a combination of: + +* Trusted institutions (including exchanges and custodians, such as [Figment](https://flight.beehiiv.net/v2/clicks/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1cmwiOiJodHRwczovL2ZpZ21lbnQuaW8vaW5zaWdodHMvZmlnbWVudC10by1lbmFibGUtYml0Y29pbi1sMi1yZXdhcmRzLXdpdGgtdXBjb21pbmctc3VwcG9ydC1vZi10aGUtc3RhY2tzLWxheWVyLz91dG1fc291cmNlPXd3dy5iaXRjb2lud3JpdGVzLmNvbSZ1dG1fbWVkaXVtPXJlZmVycmFsJnV0bV9jYW1wYWlnbj1hLW1vcmUtc2VjdXJlLWJ0Yy1icmlkZ2UtdW5kZXJzdGFuZGluZy1zYnRjLXMtc2VjdXJpdHktbW9kZWwiLCJwb3N0X2lkIjoiY2M3ZDYyNGItZWNiYS00ZjA2LWFiZjYtNDFjZDdkODVhNTc4IiwicHVibGljYXRpb25faWQiOiIwNGYyNjRlOS02MjZiLTQ4OGYtYmMzYS0wOTQzNDVmOWZjZDIiLCJ2aXNpdF90b2tlbiI6ImNlZTQ1ZjUxLWU1ZjYtNDAwOC1iZTE4LWYzZjdiZTU1MGRkYyIsImlhdCI6MTcwNTk1OTYwNywiaXNzIjoib3JjaGlkIn0.r6zVSbBoqzl8oPj\_KBelC8PLxkq\_pzEI6O2BlRM3JVY) and [Copper](https://flight.beehiiv.net/v2/clicks/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1cmwiOiJodHRwczovL2NvcHBlci5jby9pbnNpZ2h0cy9jb21wYW55LW5ld3Mvc3RhY2tzLWxheWVyLWJpdGNvaW4tc3VwcG9ydD91dG1fc291cmNlPXd3dy5iaXRjb2lud3JpdGVzLmNvbSZ1dG1fbWVkaXVtPXJlZmVycmFsJnV0bV9jYW1wYWlnbj1hLW1vcmUtc2VjdXJlLWJ0Yy1icmlkZ2UtdW5kZXJzdGFuZGluZy1zYnRjLXMtc2VjdXJpdHktbW9kZWwiLCJwb3N0X2lkIjoiY2M3ZDYyNGItZWNiYS00ZjA2LWFiZjYtNDFjZDdkODVhNTc4IiwicHVibGljYXRpb25faWQiOiIwNGYyNjRlOS02MjZiLTQ4OGYtYmMzYS0wOTQzNDVmOWZjZDIiLCJ2aXNpdF90b2tlbiI6ImNlZTQ1ZjUxLWU1ZjYtNDAwOC1iZTE4LWYzZjdiZTU1MGRkYyIsImlhdCI6MTcwNTk1OTYwNywiaXNzIjoib3JjaGlkIn0.5ZwOvwnfmqtHg3QhJz9Qq6-dzgr\_3R4uOaQgMI0GbSQ)) +* Stacking pools (such as [Xverse](https://flight.beehiiv.net/v2/clicks/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1cmwiOiJodHRwczovL3Bvb2wueHZlcnNlLmFwcC8\_dXRtX3NvdXJjZT13d3cuYml0Y29pbndyaXRlcy5jb20mdXRtX21lZGl1bT1yZWZlcnJhbCZ1dG1fY2FtcGFpZ249YS1tb3JlLXNlY3VyZS1idGMtYnJpZGdlLXVuZGVyc3RhbmRpbmctc2J0Yy1zLXNlY3VyaXR5LW1vZGVsIiwicG9zdF9pZCI6ImNjN2Q2MjRiLWVjYmEtNGYwNi1hYmY2LTQxY2Q3ZDg1YTU3OCIsInB1YmxpY2F0aW9uX2lkIjoiMDRmMjY0ZTktNjI2Yi00ODhmLWJjM2EtMDk0MzQ1ZjlmY2QyIiwidmlzaXRfdG9rZW4iOiJjZWU0NWY1MS1lNWY2LTQwMDgtYmUxOC1mM2Y3YmU1NTBkZGMiLCJpYXQiOjE3MDU5NTk2MDcsImlzcyI6Im9yY2hpZCJ9.TPIBy5jzj4l7ytNVqUeIckhqdzRW2dWA-g6JdgrUxrk)) +* Individual node operators + +These signers lock up Stacks tokens (STX) in order to be a signer on the network, and for their work to maintain the sBTC peg, earn BTC rewards, making honest behavior the most profitable course of action. + +Greater than 70% of these signers are required to approve deposit and withdrawals from the system. This means that as long as at least 30% of validators are honest, the BTC deposited in sBTC is secure. To steal the BTC deposits, malicious actors would need to convince a number of validators that represent > 70% of the stacked STX capital to collude in order to steal the BTC funds. This is highly unlikely in practice given Stacks validators are geographically dispersed entities with significant business risk for behaving maliciously. + +For more technical details on the implementation and design of sBTC, please refer to the [SIP025](https://flight.beehiiv.net/v2/clicks/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1cmwiOiJodHRwczovL2dpdGh1Yi5jb20vc3RhY2tzZ292L3NpcHMvYmxvYi9lNmIzMjMzZTc2YzIyY2ZkNmVmMDJmMjFhZGQ2NjY5NmI5ZTRjMzE0L3NpcHMvc2lwLTAyNS9zaXAtMDI1LXNidGMubWQ\_dXRtX3NvdXJjZT13d3cuYml0Y29pbndyaXRlcy5jb20mdXRtX21lZGl1bT1yZWZlcnJhbCZ1dG1fY2FtcGFpZ249YS1tb3JlLXNlY3VyZS1idGMtYnJpZGdlLXVuZGVyc3RhbmRpbmctc2J0Yy1zLXNlY3VyaXR5LW1vZGVsIiwicG9zdF9pZCI6ImNjN2Q2MjRiLWVjYmEtNGYwNi1hYmY2LTQxY2Q3ZDg1YTU3OCIsInB1YmxpY2F0aW9uX2lkIjoiMDRmMjY0ZTktNjI2Yi00ODhmLWJjM2EtMDk0MzQ1ZjlmY2QyIiwidmlzaXRfdG9rZW4iOiJjZWU0NWY1MS1lNWY2LTQwMDgtYmUxOC1mM2Y3YmU1NTBkZGMiLCJpYXQiOjE3MDU5NTk2MDcsImlzcyI6Im9yY2hpZCJ9.MpRVoPOtDQKxXZt-vIS2dwloDaYT4aEUVcpR4oyGUho). The SIP is currently under community review, pending approval on a future date to be announced. + +### Inheriting Bitcoin’s Security + +sBTC has a number of traits that make it more secure than other Bitcoin-backed assets. + +#### sBTC has read access to Bitcoin. + +Because Stacks smart contracts have read access to Bitcoin, users can send simple Bitcoin L1 transactions to interact with Stacks and deposit BTC for sBTC. In contrast, moving BTC to a chain that is not designed to work with BTC, like Solana or Ethereum, do not have this read access, which brings different security assumptions. + +#### Stacks forks with Bitcoin. + +A lot of complexity is introduced to Bitcoin-backed projects on other chains, given that Bitcoin and the other L1 are independent of each other. When Bitcoin forks, the other chain won’t care, and each fork can destabilize the peg as large numbers of transactions can be forced to roll back after the fact. + +Unlike those projects, Stacks auto-follows all Bitcoin forks and derives its security from Bitcoin. There is no risk of the sBTC peg being deprecated by a Bitcoin fork because the entire Stacks blockchain will reorg alongside Bitcoin, bringing the peg back into alignment with Bitcoin. + +#### sBTC is reorg-resistant. + +Taking this one step further, the security properties of a Stacks transaction are the same as doing a Bitcoin transaction. As soon as a Bitcoin block arrives, Stacks transactions start getting Bitcoin finality. This is not possible with other systems. In other words, Stacks does not have a separate security budget. The security level of a Bitcoin L1 transaction in a Bitcoin block is the same as a Stacks transaction that gets packaged into that Bitcoin block. + +#### Does sBTC have a hard cap/liveness ratio? + +The original white paper proposed a liveness ratio, essentially a cap on the amount of sBTC that can be minted based on the value of STX locked into the protocol. This concept came from a security model of economic incentives, in which stackers have full signing control over a Bitcoin wallet containing the locked BTC. In the original white paper this was set to 60% of locked STX (i.e. if there is $200M of STX locked, the system capacity is $120M of BTC). + +However, after further research and consideration, the sBTC working group concluded that such a cap would significantly limit potential DeFi usage and discourage large players from entering the system. Given these findings and market realities, the sBTC design no longer has this liveness ratio, and instead relies on the trust-assumption of honest institutional validators as the final security backstop. + +The research concluded that a hybrid system that has both (a) anonymous signers with locked STX _and_ (b) known, institutional signers that collectively hold > 30% locked STX, is arguably more secure than a system with just anonymous signers with locked capital. This is because institutional participants have significant incentives to behave honestly and thus bolster the organic economic incentives of the system. + +For more information on this topic, see these resources: + +* [Making sBTC ready for DeFi prime time](https://flight.beehiiv.net/v2/clicks/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1cmwiOiJodHRwczovL2ZvcnVtLnN0YWNrcy5vcmcvdC9tYWtpbmctc2J0Yy1yZWFkeS1mb3ItZGVmaS1wcmltZS10aW1lLzE0NDIxP3V0bV9zb3VyY2U9d3d3LmJpdGNvaW53cml0ZXMuY29tJnV0bV9tZWRpdW09cmVmZXJyYWwmdXRtX2NhbXBhaWduPWEtbW9yZS1zZWN1cmUtYnRjLWJyaWRnZS11bmRlcnN0YW5kaW5nLXNidGMtcy1zZWN1cml0eS1tb2RlbCIsInBvc3RfaWQiOiJjYzdkNjI0Yi1lY2JhLTRmMDYtYWJmNi00MWNkN2Q4NWE1NzgiLCJwdWJsaWNhdGlvbl9pZCI6IjA0ZjI2NGU5LTYyNmItNDg4Zi1iYzNhLTA5NDM0NWY5ZmNkMiIsInZpc2l0X3Rva2VuIjoiY2VlNDVmNTEtZTVmNi00MDA4LWJlMTgtZjNmN2JlNTUwZGRjIiwiaWF0IjoxNzA1OTU5NjA3LCJpc3MiOiJvcmNoaWQifQ.3Q8GK4GhEb8EZou9r7y1JZ\_lcRLFjpFTysv1wyZon8g) +* [sBTC Research - Cap on BTC in Peg Wallet](https://flight.beehiiv.net/v2/clicks/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1cmwiOiJodHRwczovL2dpdGh1Yi5jb20vc3RhY2tzLW5ldHdvcmsvc2J0Yy9kaXNjdXNzaW9ucy8zMTY\_dXRtX3NvdXJjZT13d3cuYml0Y29pbndyaXRlcy5jb20mdXRtX21lZGl1bT1yZWZlcnJhbCZ1dG1fY2FtcGFpZ249YS1tb3JlLXNlY3VyZS1idGMtYnJpZGdlLXVuZGVyc3RhbmRpbmctc2J0Yy1zLXNlY3VyaXR5LW1vZGVsIiwicG9zdF9pZCI6ImNjN2Q2MjRiLWVjYmEtNGYwNi1hYmY2LTQxY2Q3ZDg1YTU3OCIsInB1YmxpY2F0aW9uX2lkIjoiMDRmMjY0ZTktNjI2Yi00ODhmLWJjM2EtMDk0MzQ1ZjlmY2QyIiwidmlzaXRfdG9rZW4iOiJjZWU0NWY1MS1lNWY2LTQwMDgtYmUxOC1mM2Y3YmU1NTBkZGMiLCJpYXQiOjE3MDU5NTk2MDcsImlzcyI6Im9yY2hpZCJ9.YYbye-GFds4tBWFq6PL-J0-f1otxKQdGVjLVVKoo9cs)[ ](https://flight.beehiiv.net/v2/clicks/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1cmwiOiJodHRwczovL2dpdGh1Yi5jb20vc3RhY2tzLW5ldHdvcmsvc2J0Yy9kaXNjdXNzaW9ucy8zMTY\_dXRtX3NvdXJjZT13d3cuYml0Y29pbndyaXRlcy5jb20mdXRtX21lZGl1bT1yZWZlcnJhbCZ1dG1fY2FtcGFpZ249YS1tb3JlLXNlY3VyZS1idGMtYnJpZGdlLXVuZGVyc3RhbmRpbmctc2J0Yy1zLXNlY3VyaXR5LW1vZGVsIiwicG9zdF9pZCI6ImNjN2Q2MjRiLWVjYmEtNGYwNi1hYmY2LTQxY2Q3ZDg1YTU3OCIsInB1YmxpY2F0aW9uX2lkIjoiMDRmMjY0ZTktNjI2Yi00ODhmLWJjM2EtMDk0MzQ1ZjlmY2QyIiwidmlzaXRfdG9rZW4iOiJjZWU0NWY1MS1lNWY2LTQwMDgtYmUxOC1mM2Y3YmU1NTBkZGMiLCJpYXQiOjE3MDU5NTk2MDcsImlzcyI6Im9yY2hpZCJ9.YYbye-GFds4tBWFq6PL-J0-f1otxKQdGVjLVVKoo9cs) + +#### What happens if the value of STX drops in relation to Bitcoin’s price? + +Even if the price drops, validators will still have STX locked in the protocol, offering economic incentive to behave rationally (and remember, validators are also incentivized to behave honestly through the BTC rewards earned by their work to process deposits and withdrawals.) + +With a combination of institutions, pools, and individuals, the design offers enough flexibility that we believe that at least 30% of signers will remain honest regardless of the price. The decision to utilize a combination of both economic and reputational incentives supports this assumption because institutions are likely to continue normal Signing operations, even if the collateral backing the system declines relative to BTC. + +#### What is the purpose of the STX token? + +The STX asset has two main uses: (a) mining incentives i.e., to keep the mining system open and decentralized; and (b) STX is the capital locked up in consensus that provides economic security to users who mint sBTC. STX will collateralize the sBTC system, offering greater levels security than systems that don’t post any collateral. diff --git a/sbtc/sbtc-design/stacker-responsibilities.md b/sbtc/sbtc-design/stacker-responsibilities.md new file mode 100644 index 0000000000..7115adc52f --- /dev/null +++ b/sbtc/sbtc-design/stacker-responsibilities.md @@ -0,0 +1,21 @@ +# Stacker Responsibilities + +One of the most significant changes to accommodate the sBTC design is that Stackers must now perform active work to continue receiving PoX rewards. Stackers provide partial signatures for BTC withdrawal fulfillment transactions\[the BTC wallet] for the duration of each reward cycle in which their STX are locked. This chapter outlines the new role of the stackers, and how they interact with each other to fulfill their duties. + +### sBTC wallet address generation + +Stackers in sBTC operate in Reward cycles similar to previous versions of [PoX](https://github.com/stacksgov/sips/blob/main/sips/sip-007/sip-007-stacking-consensus.md). However, the reward cycle has been modified to consist of three phases: prepare (80 blocks), handoff (20 blocks), and reward (2000 blocks). + +During the prepare phase, miners decide on an anchor block and the next reward cycle's Stackers as before. During the handoff phase, this new set of Stackers MUST collectively determine and provide a single Bitcoin address in the PoX contract to operate as the sBTC wallet address for the next reward cycle. If they fail to do so within the first 10 blocks of the handoff phase, the prepare phase is reinitiated and a new set of stackers will be selected. + +If no valid sBTC address is provided, the current set of Stackers' continue operating as before. Their STX will remain frozen, and they will continue to receive PoX rewards until a successful prepare phase and handoff has occurred. However, once the new set of stackers has provided an sBTC wallet address, the current set of Stackers MUST execute a wallet handoff to this newly generated sBTC wallet address. + +### sBTC Wallet handoff + +An sBTC Wallet handoff is used by the current reward cycle's Stackers to send all deposited BTC to the next reward cycle's Stackers' sBTC wallet address within 10 blocks of the reward cycle starting. Additionally, Stackers MUST transfer an _equal or greater_ number of BTC than the amount of sBTC that existed at the end of their own wallet's lifetime. This implies that Stackers MUST cover any fee costs associated with fulfilling sBTC withdrawal requests. + +### sBTC Withdrawal fulfillment + +To fulfill an sBTC withdrawal request, Stackers send one or more Bitcoin transactions that pay the requested amount of BTC to the withdrawal address stipulated by the withdrawal request. If Stackers have received their sBTC wallet handoff and they fail to fulfill a request within 50 Bitcoin blocks of the request being finalized (i.e. at most 150 Bitcoin blocks after the request is submitted), then the system transitions to Recovery mode and PoX payouts are repurposed for fulfilling pending withdrawal requests. + +If Stackers do not fulfill the pending sBTC withdrawal requests, their STX will be frozen by .pox and any earned BTC used to fulfill these pending requests. Stackers may only receive back their STX and resume earning BTC once all the withdrawal requests for which they were responsible are fulfilled. diff --git a/sbtc/sbtc-releases/README.md b/sbtc/sbtc-releases/README.md new file mode 100644 index 0000000000..b3a61c61cc --- /dev/null +++ b/sbtc/sbtc-releases/README.md @@ -0,0 +1,2 @@ +# sBTC Releases + diff --git a/sbtc/sbtc-releases/sbtc-0.1.md b/sbtc/sbtc-releases/sbtc-0.1.md new file mode 100644 index 0000000000..71c8c76efe --- /dev/null +++ b/sbtc/sbtc-releases/sbtc-0.1.md @@ -0,0 +1,2 @@ +# sBTC 0.1 + diff --git a/sbtc/sbtc-releases/sbtc-1.0.md b/sbtc/sbtc-releases/sbtc-1.0.md new file mode 100644 index 0000000000..c166977baa --- /dev/null +++ b/sbtc/sbtc-releases/sbtc-1.0.md @@ -0,0 +1,2 @@ +# sBTC 1.0 + diff --git a/sbtc/sbtc-releases/sbtc-1.1.md b/sbtc/sbtc-releases/sbtc-1.1.md new file mode 100644 index 0000000000..0b5a80749a --- /dev/null +++ b/sbtc/sbtc-releases/sbtc-1.1.md @@ -0,0 +1,2 @@ +# sBTC 1.1 + diff --git a/sbtc/user-guides/README.md b/sbtc/user-guides/README.md new file mode 100644 index 0000000000..0fcf8398d9 --- /dev/null +++ b/sbtc/user-guides/README.md @@ -0,0 +1,3 @@ +# User Guides + +Users will interact with sBTC in a variety of ways, but the two primary ways in the beginning will be by depositing and withdrawing their BTC in and out of sBTC and acting as a signer. diff --git a/sbtc/user-guides/how-to-deposit-and-withdraw.md b/sbtc/user-guides/how-to-deposit-and-withdraw.md new file mode 100644 index 0000000000..6177e68271 --- /dev/null +++ b/sbtc/user-guides/how-to-deposit-and-withdraw.md @@ -0,0 +1,42 @@ +# How to Deposit and Withdraw + +Depositing and withdrawing sBTC can be done in three ways: + +1. Using the [sBTC Bridge](https://bridge.stx.eco/) application. +2. Using an sBTC enabled wallet. +3. Using an app which integrates with sBTC natively. + +This guide will walk you through how to deposit and withdraw sBTC using the sBTC Brdige application. + +### Preparation + +First, make sure you have the [Hiro Wallet](https://wallet.hiro.so/) browser extension installed. Then, to begin your deposit or withdrawal, navigate to [https://bridge.stx.eco/](https://bridge.stx.eco/). Once there, click the `Settings` dropdown and make sure you're on the right network. Thereafter, you should select the appropriate transaction mode: + +* If you want to deposit BTC from your Hiro Wallet, select `OP_RETURN`. +* If you want to deposit BTC from a custodial wallet, select `OP_DROP`. + +With these settings in place, you may proceed to do your deposit or withdrawal. + +### How to Deposit + +To deposit you will be prompted to enter + +1. Your bitcoin address to deposit from. +2. A stacks address to receive the sBTC. +3. The amount to deposit. + +Once you have entered this information and continue, you will either be prompted to sign a transaction with the Hiro Wallet or receive a QR code to scan depending on your transaction mode. + +When you have signed or paid to the QR code you'll get a link to follow your request on the Bitcoin chain. The sBTC should be minted shortly after your request is mined. + +### How to Withdraw + +To withdraw you will be prompted to enter + +1. Your bitcoin address to receive the BTC which is also used to request the withdrawal. +2. Your stacks address from which the sBTC should be withdrawn. +3. The amount to withdraw. + +You will then be prompted to sign a message payload with the Hiro Wallet to authenticate your request. Once the request is authenticated you will be prompted to either sign a bitcoin transaction or receive a QR code to scan depending on your transaction mode. + +When you have signed or paid to the QR code you'll get a link to follow your request on the Bitcoin chain. The sBTC should be burned shortly after your request is mined. Thereafter, the system will wait until the sBTC burn is final before fulfilling your withdrawal on the Bitcoin chain. This may take up to 150 bitcoin blocks. diff --git a/sbtc/user-guides/how-to-participate-as-a-stacker.md b/sbtc/user-guides/how-to-participate-as-a-stacker.md new file mode 100644 index 0000000000..221a4087a3 --- /dev/null +++ b/sbtc/user-guides/how-to-participate-as-a-stacker.md @@ -0,0 +1,38 @@ +# How to Participate as a Stacker + +Participating as a Stacker in the Stacks Blockchain is an essential role to ensure the liveliness of sBTC. To become a Stacker, you must hold and temporarily lock STX, Stacks’ native currency, and support the network’s security and consensus. As a reward, you earn BTC. + +There are multiple ways for you to stack and earn Bitcoin - either through an exchange, a non-custodial stacking service, or independently. The most appropriate choice depends on your crypto experience and the amount of STX you have at your disposal. For direct participation, Stacks holders need a dynamic minimum amount of STX (approximately 100,000k STX during the mainnet, but this amount fluctuates based on overall participation and supply. See the [`pox` endpoint](https://docs.hiro.so/api#tag/Info/operation/get\_pox\_info) of the Hiro API to get the minimum of the next cycle). If you don't meet this minimum, you can still participate by leveraging third-party Stacking delegation services. These services combine your holdings with others, allowing joint participation. A complete breakdown of the stacking mechanism can be found in [SIP-007](https://github.com/stacksgov/sips/blob/main/sips/sip-007/sip-007-stacking-consensus.md). + +Below is a documentation guide that outlines the steps to participate as a Stacker in the Stacks blockchain network, along with the responsibilities and actions involved in the role. Options for stacking through an exchange a stacking service can be found on the [stacks.co stacking page](https://www.stacks.co/learn/stacking#startstacking). + +### Stacking Through an Exchange + +1. Choose an Exchange Select a reputable cryptocurrency exchange that offers Stacking services for STX. +2. Create an Account If you don't have an account on the chosen exchange, sign up for one. Complete the necessary verification procedures. +3. Deposit STX Deposit the desired amount of STX into your exchange account. +4. Navigate to Stacking Section Go to the Stacking section or menu within the exchange platform. +5. Select Stacking Options Choose the stacking options that suit your preferences, such as the duration and amount to be stacked. +6. Start Stacking Confirm the stacking process on the exchange platform. Your STX will be locked for the chosen stacking period, and you'll start earning rewards. The exchange will fulfill your signing obligations on your behalf according to their own configurations. +7. Monitor Stacking Rewards Keep track of your stacking rewards on the exchange platform. The rewards will typically be automatically credited to your account. + +### Stacking Through a Non-Custodial Stacking Service + +1. Choose a Non-Custodial Stacking Service Research and select a reputable non-custodial Stacking stacking service that aligns with your preferences and goals. +2. Set Up a Stacks Wallet Ensure you have a compatible Stacks wallet that supports Stacking. Examples include the Stacks Wallet or other wallets that are Stacking-enabled. +3. Acquire STX Acquire the desired amount of STX to participate in the Stacking stacking service. +4. Register with the Stacking Service Follow the registration process for the chosen stacking service. Provide the necessary details, including your wallet address. +5. Delegate Your STX Delegate your STX holdings to the stacking service. This process allows the stacking service to participate in Stacking on your behalf while your STX remains under your control. The stacking service will fulfill your signing obligations on your behalf according to their own configurations. +6. Receive Stacking Rewards As part of the stacking service, you'll receive Stacking rewards proportionate to your contribution. The rewards will typically be automatically distributed to your wallet by the stacking service operator. + +### Independent Stacking + +1. Set Up a Stacks Wallet Choose a compatible Stacks wallet that supports Stacking. Download and install the wallet on your device. +2. Acquire STX Acquire the dynamic minimum amount of STX required for independent Stacking. As of now, this amount is approximately 100,000k STX, but verify for any updates on this requirement. +3. Setup or Select a Signer To fulfill your obligation to validate and sign sBTC transactions, you must first select or setup a Signer +4. Register as a Stacker After setting up your wallet and acquiring the required STX, register as a stacker on the Stacks Blockchain. The registration process may vary based on wallet providers. +5. Start Stacking Once registered, your wallet will facilitate the process of participating in the Stacking consensus and validating transactions. You will earn STX rewards for your contributions to securing the network. + +### Conclusion + +Stacking in the Stacks Blockchain is a rewarding way to participate in the network's consensus and earn BTC rewards. Choose the method that aligns with your preferences, and always prioritize security when participating in Stacking. Remember to research the chosen exchange or stacking service and stay informed about any updates or changes to the Stacking process. diff --git a/stacks-101/accounts.md b/stacks-101/accounts.md new file mode 100644 index 0000000000..734a75e489 --- /dev/null +++ b/stacks-101/accounts.md @@ -0,0 +1,221 @@ +# Accounts + +### Introduction + +Stacks 2.0 accounts are entities that own assets, like Stacks (STX) tokens. An account has an address, private key, nonce, and one or more asset balances. + +{% hint style="info" %} +The encryption algorithm used in Stacks 2.0 is [**secp256k1**](https://en.bitcoinwiki.org/wiki/Secp256k1). + +Additionally, [Ed25519](https://ed25519.cr.yp.to/) is also used just for the VRF (Verifiable Random Function). +{% endhint %} + +Assets cannot leave an account without an action from the account owner. All changes to assets (and the balances of the account) require a corresponding transaction. + +{% hint style="info" %} +The transaction type doesn't need to be a token transfer - contract deploy and contract call transactions can change the balances of an account +{% endhint %} + +### Creation + +An account is generated from a 24-word mnemonic phrase. This is often referred to as the **seed phrase**. The seed phrase provides access to Stacks 2.0 accounts. + +{% hint style="danger" %} +If the seed phrase is lost, access to the associated account cannot be restored. No person or organization can recover a lost seed phrase. +{% endhint %} + +The easiest way to generate a new Stacks 2.0 account is to use the [Stacks CLI](https://github.com/hirosystems/stacks.js/tree/master/packages/cli): + +```bash +# install CLI globally +npm install --global @stacks/cli + +# generate a new account and store details in a new file +# '-t' option makes this a testnet account +stx make_keychain -t > cli_keychain.json +``` + +`make_keychain` creates the following file: + +```js +{ + "mnemonic": "aaa bbb ccc ddd ...", + "keyInfo": { + "privateKey": "5a3f1f15245bb3fb...", + "address": "STJRM2AMVF90ER6G3RW1QTF85E3HZH37006D5ER1", + "btcAddress": "biwSd6KTEvJcyX2R8oyfgj5REuLzczMYC1", + "wif": "L4HXn7PLmzoNW...", + "index": 0 + } +} +``` + +{% hint style="info" %} +Check out the [Stacks CLI reference](https://docs.hiro.so/references/stacks-cli) for more details +{% endhint %} + +| Field | Description | +| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `mnemonic` | A 24-word seed phrase used to access the account, generated using [BIP39](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki) with 256 bits of entropy | +| `keyInfo.privateKey` | Private key for the account. Required for token transfers and often referred to as `senderKey` | +| `keyInfo.address` | Stacks address for the account | +| `keyInfo.btcAddress` | Corresponding BTC address for the account. | +| `keyInfo.wif` | Private key of the btcAddress in compressed format. | +| `keyInfo.index` | Nonce for the account, starting at 0 | + +Note that a new account automatically exists for each new private key. There is no need to manually instantiate an account on the Stacks 2.0 blockchain. + +{% hint style="info" %} +Addresses are created by generating the [RIPEMD-160 hash](https://en.wikipedia.org/wiki/RIPEMD#RIPEMD-160\_hashes) of the [SHA256](https://en.bitcoinwiki.org/wiki/SHA-256) of the public key. BTC addresses are encoded with [Base58Check](https://en.bitcoin.it/wiki/Base58Check\_encoding). For Stacks addresses, [c32check](https://github.com/stacks-network/c32check) is used. Deriving an address from a public key can be done without internet access, for instance using the c32check `c32addressDecode` method. +{% endhint %} + +Alternatively to the CLI creation, the [Stacks Transactions JS](https://github.com/hirosystems/stacks.js/tree/master/packages/transactions) library can be used: + +```js +import { + makeRandomPrivKey, + privateKeyToString, + getAddressFromPrivateKey, + TransactionVersion, + getPublicKey, +} from "@stacks/transactions"; + +const privateKey = makeRandomPrivKey(); + +// Get public key from private +const publicKey = getPublicKey(privateKey); + +const stacksAddress = getAddressFromPrivateKey( + privateKeyToString(privateKey), + TransactionVersion.Testnet // remove for Mainnet addresses +); +``` + +A second alternative would be to use [stacks-gen](https://github.com/psq/stacks-gen). This tool will generate all the keys you need in one place, including the values needed for calling the stacking contract, and also a WIF key for use with `bitcoind`. + +**stacks-gen prerequisite** + +Install [npx](https://github.com/npm/npx) if not already installed. (npx will check whether `` exists in $PATH, or in the local project binaries, and execute that. If `` is not found, it will be installed prior to execution). + +``` +npm install -g npx +``` + +**stacks-gen usage** + +``` +npx -q stacks-gen sk --testnet + +{ + "phrase": "guide air pet hat friend anchor harvest dog depart matter deny awkward sign almost speak short dragon rare private fame depart elevator snake chef", + "private": "0351764dc07ee1ad038ff49c0e020799f0a350dd0769017ea09460e150a6401901", + "public": "022d82baea2d041ac281bebafab11571f45db4f163a9e3f8640b1c804a4ac6f662", + "stacks": "ST16JQQNQXVNGR8RZ1D52TMH5MFHTXVPHRV6YE19C", + "stacking": "{ hashbytes: 0x4d2bdeb7eeeb0c231f0b4a2d5225a3e3aeeed1c6, version: 0x00 }", + "btc": "mnYzsxxW271GkmyMnRfiopEkaEpeqLtDy8", + "wif": "cMh9kwaCEttgTQYkyMUYQVbdm5ZarZdBHErcq7mXUChXXCo7CFEh" +} +``` + +{% hint style="info" %} +The stacking object with hashbytes and a version represents the bitcoin address derived from the Stacks address. Read more about the bitcoin address format. +{% endhint %} + +Full documentation available at [stacks-gen](https://github.com/psq/stacks-gen). + +### Querying + +#### Get Stacks (STX) balance and nonce + +STX balance and nonce can be obtained through the [`GET /v2/accounts/`](https://docs.hiro.so/api#operation/get\_account\_info) endpoint: + +```bash +# for mainnet, replace `testnet` with `mainnet` +curl 'https://stacks-node-api.testnet.stacks.co/v2/accounts/' +``` + +Sample response: + +```js +{ + "balance": "0x0000000000000000002386f26f3f40ec", + "nonce": 17 +} +``` + +{% hint style="info" %} +The balance string represents an unsigned 128-bit integer (big-endian) in hex encoding +{% endhint %} + +#### Get all token balances + +All token balances can be obtained through the [`GET /extended/v1/address//balances`](https://docs.hiro.so/api#operation/get\_account\_balance) endpoint: + +```bash +# for mainnet, replace `testnet` with `mainnet` +curl 'https://stacks-node-api.testnet.stacks.co/extended/v1/address//balances' +``` + +Sample response: + +```js +{ + "stx": { + "balance": "0", + "total_sent": "0", + "total_received": "0" + }, + "fungible_tokens": {}, + "non_fungible_tokens": {} +} +``` + +{% hint style="info" %} +Stacks accounts cannot hold bitcoins. The best way to obtain corresponding BTC balances is to derive the BTC address from the Stacks address (using [`c32check`](https://github.com/stacks-network/c32check#c32tob58-b58toc32)) and query the Bitcoin network. +{% endhint %} + +#### Get all asset events + +All asset events associated with the account can be obtained through the [`GET /extended/v1/address//assets`](https://docs.hiro.so/api#operation/get\_account\_balance) endpoint: + +```bash +# for mainnet, replace `testnet` with `mainnet` +curl 'https://stacks-node-api.testnet.stacks.co/extended/v1/address//assets' +``` + +Sample response: + +```js +{ + "limit": 20, + "offset": 0, + "total": 0, + "results": [ + { + "event_index": 5, + "event_type": "non_fungible_token_asset", + "asset": { + "asset_event_type": "transfer", + "asset_id": "ST2W14YX9SFVDB1ZGHSH40CX1YQAP9XKRAYSSVYAG.hello_world::hello-nft", + "sender": "SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR", + "recipient": "SM2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQVX8X0G", + "value": { + "hex": "0x0100000000000000000000000000000001", + "repr": "u1" + } + } + }, + { + "event_index": 3, + "event_type": "fungible_token_asset", + "asset": { + "asset_event_type": "mint", + "asset_id": "ST2W14YX9SFVDB1ZGHSH40CX1YQAP9XKRAYSSVYAG.hello_world::novel-token-19", + "sender": "", + "recipient": "SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR", + "amount": "12" + } + } + ] +} +``` diff --git a/stacks-101/api.md b/stacks-101/api.md new file mode 100644 index 0000000000..d9ba2fd18f --- /dev/null +++ b/stacks-101/api.md @@ -0,0 +1,45 @@ +# API + +### Introduction + +The Stacks 2.0 Blockchain API allows you to query the Stacks 2.0 blockchain and interact with smart contracts. It was built to maintain pageable materialized views of the Stacks 2.0 Blockchain. + +{% hint style="info" %} +API Documentation Official API Documentation is available [here](https://stacks-network.github.io/stacks-blockchain/). +{% endhint %} + +Note that the [Stacks Node RPC API](https://github.com/stacks-network/stacks-blockchain/) and the [Hiro Stacks API](https://www.hiro.so/stacks-api) are two different things. The Hiro API is a centralized service run by Hiro, a developer tooling company, that makes it easy to get onboarded and begin interacting with the Stacks blockchain in a RESTful way. You can also [run your own API server](https://docs.hiro.so/get-started/running-api-node) + +The Hiro Stacks API is a proxy for the Stacks Node API that makes it a bit easier to work with by providing additional functionality. + +The RPC API is generated by every Stacks node and allows developers to self-host their own node and API for a more decentralized architecture. + +{% hint style="info" %} +This documentation only covers endpoints that are exposed on a Stacks node, referred to as the RPC API. For full documentation on the RESTful API, check out the [Hiro's API reference](https://docs.hiro.so/api). +{% endhint %} + +The RPC API can be used without any authorization. The basepath for the API is: + +```bash +# for mainnet, replace `testnet` with `mainnet` +https://stacks-node-api.testnet.stacks.co/ +``` + +### Proxied Stacks Node RPC API endpoints + +The Stacks 2.0 Blockchain API (Hiro's API) is centrally hosted. However, every running Stacks node exposes an RPC API, which allows you to interact with the underlying blockchain. Instead of using a centrally hosted API, you can directly access the RPC API of a locally hosted Node. + +While the Node RPC API doesn't give the same functionality as the hosted Stacks 2.0 Blockchain API, you get similar functionality in a way that is scoped to that specific node. The RPC API includes the following endpoints: + +* [POST /v2/transactions](https://docs.hiro.so/api#operation/post\_core\_node\_transactions) +* [GET /v2/contracts/interface/{contract\_address}/{contract\_name}](https://docs.hiro.so/api#operation/get\_contract\_interface) +* [POST /v2/map\_entry/{contract\_address}/{contract\_name}/{map\_name}](https://docs.hiro.so/api#operation/get\_contract\_data\_map\_entry) +* [GET /v2/contracts/source/{contract\_address}/{contract\_name}](https://docs.hiro.so/api#operation/get\_contract\_source) +* [GET /v2/accounts/{principal}](https://docs.hiro.so/api#operation/get\_account\_info) +* [POST /v2/contracts/call-read/{contract\_address}/{contract\_name}/{function\_name}](https://docs.hiro.so/api#operation/call\_read\_only\_function) +* [GET /v2/fees/transfer](https://docs.hiro.so/api#operation/get\_fee\_transfer) +* [GET /v2/info](https://docs.hiro.so/api#operation/get\_core\_api\_info) + +{% hint style="warning" %} +If you run a local node, it exposes an HTTP server on port `20443`. The info endpoint would be `localhost:20443/v2/info`. +{% endhint %} diff --git a/stacks-101/authentication.md b/stacks-101/authentication.md new file mode 100644 index 0000000000..e6b7e91974 --- /dev/null +++ b/stacks-101/authentication.md @@ -0,0 +1,59 @@ +# Authentication + +### Introduction + +This guide explains how authentication is performed on the Stacks blockchain. + +Authentication provides a way for users to identify themselves to an app while retaining complete control over their credentials and personal details. It can be integrated alone or used in conjunction with [transaction signing](https://docs.hiro.so/get-started/transactions#signature-and-verification) and [data storage](https://docs.stacks.co/gaia/overview), for which it is a prerequisite. + +Users who register for your app can subsequently authenticate to any other app with support for the Blockchain Naming System and vice versa. + +### How it works + +The authentication flow with Stacks is similar to the typical client-server flow used by centralized sign in services (for example, OAuth). However, with Stacks the authentication flow happens entirely client-side. + +An app and authenticator, such as [the Stacks Wallet](https://www.hiro.so/wallet/install-web), communicate during the authentication flow by passing back and forth two tokens. The requesting app sends the authenticator an `authRequest` token. Once a user approves authentication, the authenticator responds to the app with an `authResponse` token. + +These tokens are based on [a JSON Web Token (JWT) standard](https://tools.ietf.org/html/rfc7519) with additional support for the `secp256k1` curve used by Bitcoin and many other cryptocurrencies. They are passed via URL query strings. + +When a user chooses to authenticate an app, it sends the `authRequest` token to the authenticator via a URL query string with an equally named parameter: + +`https://wallet.hiro.so/...?authRequest=j902120cn829n1jnvoa...` + +When the authenticator receives the request, it generates an `authResponse` token for the app using an _ephemeral transit key_ . The ephemeral transit key is just used for the particular instance of the app, in this case, to sign the `authRequest`. + +The app stores the ephemeral transit key during request generation. The public portion of the transit key is passed in the `authRequest` token. The authenticator uses the public portion of the key to encrypt an _app private key_ which is returned via the `authResponse`. + +The authenticator generates the app private key from the user's _identity address private key_ and the app's domain. The app private key serves three functions: + +1. It is used to create credentials that give the app access to a storage bucket in the user's Gaia hub +2. It is used in the end-to-end encryption of files stored for the app in the user's Gaia storage. +3. It serves as a cryptographic secret that apps can use to perform other cryptographic functions. + +Finally, the app private key is deterministic, meaning that the same private key will always be generated for a given Stacks address and domain. + +The first two of these functions are particularly relevant to [data storage with Stacks.js](https://docs.stacks.co/docs/gaia). + +### Key pairs + +Authentication with Stacks makes extensive use of public key cryptography generally and ECDSA with the `secp256k1` curve in particular. + +The following sections describe the three public-private key pairs used, including how they're generated, where they're used and to whom private keys are disclosed. + +#### Transit private key + +The transit private is an ephemeral key that is used to encrypt secrets that need to be passed from the authenticator to the app during the authentication process. It is randomly generated by the app at the beginning of the authentication response. + +The public key that corresponds to the transit private key is stored in a single element array in the `public_keys` key of the authentication request token. The authenticator encrypts secret data such as the app private key using this public key and sends it back to the app when the user signs in to the app. The transit private key signs the app authentication request. + +#### Identity address private key + +The identity address private key is derived from the user's keychain phrase and is the private key of the Stacks username that the user chooses to use to sign in to the app. It is a secret owned by the user and never leaves the user's instance of the authenticator. + +This private key signs the authentication response token for an app to indicate that the user approves sign in to that app. + +#### App private key + +The app private key is an app-specific private key that is generated from the user's identity address private key using the `domain_name` as input. + +The app private key is securely shared with the app on each authentication, encrypted by the authenticator with the transit public key. Because the transit key is only stored on the client side, this prevents a man-in-the-middle attack where a server or internet provider could potentially snoop on the app private key. diff --git a/stacks-101/bitcoin-connection.md b/stacks-101/bitcoin-connection.md new file mode 100644 index 0000000000..88c36de3bb --- /dev/null +++ b/stacks-101/bitcoin-connection.md @@ -0,0 +1,221 @@ +# Bitcoin Connection + +At the very beginning of these docs, we described Stacks as bringing smart contract functionality to Bitcoin, without modifying Bitcoin itself. + +That's a big promise, but how does Stacks actually deliver on it? And what makes Stacks unique among other Bitcoin layers and other blockchains like Ethereum? + +Before we get into the technical details of how Stacks works, it's important to get a high-level overview of the problem its solving and how it actually does that. We'll dive deeper into some of these topics as we go through Stacks Academy, but it's good to get a high-level picture to bring everything together. + +This topic is a bit of a rabbit hole, and this section of Stacks Academy is pretty long, but it will give you an in-depth understanding of exactly the problem Stacks is looking to solve, and how it solves it. + +Let's get into it. + +### Is Stacks a Bitcoin L2? + +Stacks is a Bitcoin layer for smart contracts. The classification as a layer-1 (L1) or layer-2 (L2) or sidechain really depends on the definition used. With that said, generally speaking L1 chains are sovereign meaning that (a) they have their own security budget, and (b) they can survive without the need for any other L1 chain. L2 chains typically do not have their own security budget and share the security of the underlying L1 chain, and they cannot live without the underlying L1 chain. + +The initial release of Stacks in early 2021 had a separate security budget from Bitcoin L1. Even though the Stacks layer could not function without Bitcoin L1, the developers working on the project described it as a different system that does not fit neatly into existing classifications, sometimes using the term layer 1.5 (see [this Decrypt article](https://decrypt.co/82019/bitcoin-defi-thing-says-stacks-founder-muneeb-ali) for example). + +The upcoming planned release of Stacks, called the Nakamoto release (convention to use names of famous Computer Scientists for major releases), will no longer have a separate security budget from Bitcoin. Instead, a 100% of Bitcoin hashpower will determine finality on Stacks layer. After the next upgrade, to reorg Stacks blocks/transactions the attacker will need to reorg Bitcoin L1 itself (which is very hard to do and therefore a great security property for a Bitcoin layer to have). More details in the [Stacks paper](https://stacks.co/stacks.pdf). + +The definition of [L2 used in Ethereum](https://ethereum.org/en/layer-2/) and other newer ecosystems is different and focuses on the ability to withdraw assets using only L1 security and L1 miners. According to that definition Stacks layer is not a clear L2, given the set of peg-out signers determine if users can withdraw sBTC. Bitcoin cannot support such verification without changes to Bitcoin L1 (which may happen in the future). The Ethereum L2 definition also does not apply that cleanly to Bitcoin L2s, given new assets are issued on L2s when it comes to Bitcoin and not issued on L1 (only BTC is the L1 asset). Therefore, using the definition of security of withdrawing assets is not directly applicable given assets are defined and used on L2s and not withdrawn out to Bitcoin L1 anyway (with the exception of BTC itself). Rather, what becomes more important is "settlement on Bitcoin" i.e., is contract data and state secured by 100% of Bitcoin's hashpower or not. + +Users and developers organically call Stacks a Bitcoin L2, since it is a simpler concept to understand. There are certain properties of Stacks layer that also help the concept of Stacks as a Bitcoin L2: + +1. Bitcoin finality, as discussed above, where 100% of the Bitcoin hashpower decides block ordering and transaction finality. +2. Stacks consensus runs on Bitcoin L1, and Stacks L2 cannot operate or survive without Bitcoin L1. +3. With the upcoming decentralized Bitcoin peg, called sBTC (see [sBTC paper](https://stacks.co/sbtc.pdf)), most of economy on Stacks layer will likely use BTC as the unit of economy. It is expected that most users will simply use Bitcoin in wallets and apps and then peg out their BTC to Bitcoin L1. +4. All data and transactions on Stacks are automatically hashed and permanently stored on Bitcoin L1 on every Bitcoin block. Anyone can verify that some data on Stacks is valid by checking the corresponding hash on Bitcoin L1. This compact storage of hashes on L1 is somewhat similar to rollups (although there are other differences). +5. Contracts on Stacks layer can read Bitcoin L1 transactions and respond to them. Assets on Stacks layer can be moved simply through Bitcoin L1 transactions. + +Given all the details above, why would some people think that Stacks is not a Bitcoin L2? There are a couple of reasons why this question comes up often: + +1. The initial version of Stacks (released early 2021) had a separate security budget which changed to following 100% Bitcoin hashpower with the Nakamoto release. There is old material and blog posts floating around that still talk about the initial Stacks version. The old materials will likely get updated with time. +2. According to the Ethereum definition of L2s a user should be able to withdraw their base-layer assets purely by doing a L1 transaction and relying only on L1 security (this is true for Lightning for example). This definition does not apply cleanly to Bitcoin L2s because assets are not defined at Bitcoin L1 but are defined in L2s instead. The only asset where this matters is the pegged BTC asset from Bitcoin L1, given all other assets are native to L2s anyway. In the upcoming Stacks release, users can withdraw their BTC by sending just a Bitcoin L1 transaction but Bitcoin L1 cannot validate that complex transaction and a majority of peg-out signers will need to sign on the peg-out request. In an ideal world Bitcoin miners can validate such transactions but that would require a change to Bitcoin L1. Therefore, Stacks design optimizes for a method that is decentralized and can be deployed without any changes to Bitcoin L1. If in the future it is possible to make changes to Bitcoin L1 then Stacks layer security can benefit from that as well. +3. Bitcoin community members are generally skeptical of claims and on a look out for people making any false marketing claims. This is generally a healthy thing for the Bitcoin ecosystem and builds up the immune system. Some such community members might be skeptical about Stacks as a Bitcoin layer or L2 until they fully read the technical details and reasoning. There is a good [Twitter thread](https://twitter.com/lopp/status/1623756872976158722?s=20) about his topic as well. + +Why don't we use the term 'sidechain' for Stacks then? Sidechains in Bitcoin typically have a different security budget from Bitcoin L1, typically as a subset of Bitcoin miners who participate in the sidechain (they don't follow 100% Bitcoin finality), their consensus runs on the sidechain (vs running on Bitcoin L1), and they don't publish their data/hashes on Bitcoin L1. The Stacks layer does not fit that definition cleanly given the consensus runs on Bitcoin L1, it follows Bitcoin finality, and publishes data/hashes on L1. + +The TLDR is that it is better to refer to Stacks as a Bitcoin layer (the more generic term). However, the Bitcoin L2 term is more organic and simpler for users and we'll likely keep seeing the use of it. If the Bitcoin L2 term comes up then it's important to understand the technical differences and that unlike Ethereum L2s withdrawing BTC from Stacks requires signatures from peg-out signers. + +### Stacks Among Other Bitcoin Layers + +Let's take a look at how Stacks compares to some of the other Bitcoin layering solutions. This is a fresh and rapidly evolving ecosystem, so there are quite a few interesting approaches in the works. + +#### Lightning + +Lightning is probably the most well-known Bitcoin layer, and is primarily designed to address scalability issues. Lightning functions as a separate P2P network from Bitcoin, allowing participants to conduct move their BTC from the main chain to Lightning, conduct multiple transactions on Lightning, and then send the final result to the BTC chain where it is finalized. + +This is actually a completely separate problem from what Stacks is trying to address. Where Lightning takes the existing functionality of Bitcoin and makes it much more scalable, Stacks is seeking to expand Bitcoin's functionality to do things you can't do now. + +Crucially, Lightning is ephemeral, meaning it has no state management. There is no continuous record of what has happened on the Lightning network, only current channels. Once users close their channel and their transactions are written back to the Bitcoin chain, they are gone. + +A key component of full-expressive smart contracts is that they maintain a permanent historical record of all transactions that have happened on the chain. + +Bitcoin does this now, but its scripting language is very limited. So where Lightning seeks to make existing Bitcoin functionality happen faster, Stacks seeks to add new functionality. + +#### RSK + +Like Stacks, [RSK](https://www.rsk.co/) seeks to add additional functionality to Bitcoin, but it goes about that process differently than Stacks. + +RSK is a merge-mined chain, meaning that it is mined concurrently with Bitcoin. Stacks has its own miners and mining process, and its own economic value and security that is a function of that token value, more on this below. + +There are multiple perspectives to look at this from. Because RSK is merge-mined, Bitcoin miners are also the ones mining RSK blocks, and RSK does not have its own token. + +RSK can only exist with opt-in from Bitcoin miners and mining rewards are highly dependent on transaction volume. + +This also opens up a wider discussion on the costs and benefits of having a separate token, which we'll get into below. + +RSK is also EVM-compatible, where Stacks uses Clarity and the Clarity VM. + +#### Liquid + +[Liquid](https://liquid.net/) is a federated network focused on unlocking more advanced financial capabilities with Bitcoin. Being federated, Liquid is not an open network, and thus not decentralized. + +The Liquid consensus mechanism is managed by 15 functionaries, who handle the transaction processing and validating. Liquid also does not support general-purpose applications, but is solely focused on financial applications. + +This was a brief overview of these other Bitcoin projects, for another perspective, Hiro wrote an [excellent post](https://www.hiro.so/blog/building-on-bitcoin-project-comparison) comparing Stacks with other Bitcoin projects. + +#### Bitcoin Rollups + +Rollups are an exciting development for scaling decentralized applications. There are many different types of rollups; they're broadly divided into ZK rollups and Optimistic rollups, although other classifications are also there (see [this overview](https://era.zksync.io/docs/dev/fundamentals/rollups.html#what-are-rollups)). + +Rollups are generally considered layer-2 (L2) technology that runs on top of a layer-1 blockchain like Bitcoin or Ethereum. A critical aspect of rollups is the trustless nature where logic running on the L1 chain can determine whether something that happened on the rollup was valid. This is not true for all types of rollups, and there is some fuzziness around exact definitions. [Sovereign rollups](https://blog.celestia.org/sovereign-rollup-chains/), for example, only use the underlying L1 for data availability (DA) and not for consensus. + +Most of the rollups work on Ethereum uses Ethereum L1 both as a data availability layer, and for consensus, i.e., the validity of rollup transactions is determined by logic running on Ethereum L1. Newer systems, [like Celestia](https://celestia.org/), are taking a more modular approach and are separating DA from consensus. One interesting aspect of separating DA is that more established and durable chains like Bitcoin can be used for DA as well. Below is an interesting comparison of sidechains and two types of rollups possible on Bitcoin (John Light posted this [on Twitter](https://twitter.com/lightcoin/status/1630301411962388481?s=20)): + +
+ +This image broadly means developers can build sovereign rollups on Bitcoin today, but you'll need a "trusted" setup for moving BTC in and out of the rollup. In fact, people are already doing this -- see the recent [Rollkit announcement](https://rollkit.dev/blog/sovereign-rollups-on-bitcoin/). To build validity rollups, meaning Bitcoin L1 enforces BTC withdrawals from the rollup, you'll need modifications to Bitcoin L1. See [this overview](https://bitcoinrollups.org/) for more details. + +**How does the Stacks layer compare?** + +Stacks is not really a sidechain, given the Nakamoto release (see [latest Stacks paper](https://stacks.co/stacks.pdf)), Stacks layer will follow Bitcoin finality with 100% Bitcoin hashpower. Also, the Stacks layer has various other direct connections to Bitcoin L1 that sidechains typically do not have -- see the above section for details if Stacks is a Bitcoin L2 or not; the short answer is it depends on the definition you use. + +Stacks with the Nakamoto release will have Bitcoin-grade reorg resistance. The designers of the Nakamoto release have decided to wait for 150 blocks before Bitcoin finality kicks in; this is mostly done to allow short-term forks to be resolved at the Stacks level. This design also means that most Maximal Extractable Value (MEV) action happens on the Stacks layer side and not on the Bitcoin side. There is always a fear of MEV incentives messing with Bitcoin mining, and the Stacks layer explicitly attracts most MEV activity to happen on the Stacks layer vs. Bitcoin L1 (the assumption here is that most MEV activity will occur within 150 blocks). Changing the variable from 150 to 6 blocks is trivial technically and can be configured as needed. Sovereign rollups effectively use a variable of 0 blocks and work similarly to the Stacks layer for reorg resistance. + +For data availability, Stacks publishes only hashes of data to Bitcoin every Bitcoin block instead of posting all the data to Bitcoin. The designers separate data validation from data availability. Bitcoin is used for data validation, which is important. Bitcoin L1 and only Bitcoin L1 can confirm whether a presented Stacks layer history is valid. The block data itself is kept outside of Bitcoin L1 for scalability. As long as STX has any market cap, there is an incentive for Stacks miners to keep copies of the Stacks layer ledger around. Even if a single copy of the Stacks ledger exists, it can be independently verified against Bitcoin L1. Sovereign rollups publish all data to Bitcoin L1, giving both Bitcoin-grade data validity and data availability. The potential downside is scalability at Bitcoin L1, but the hope is that rollup data will not become very large. + +**Can Stacks layer work with rollups?** + +Yes! There is already an active R\&D effort to integrate rollups with the Stacks layer. Both with the Stacks layer and sovereign rollups the technically challenging part is how to get BTC in and out of the Stacks layer or the sovereign rollup. The decentralized BTC peg, see [the sBTC paper](https://stacks.co/sbtc.pdf), applies to both the Stacks layer and sovereign rollups. Without modifying Bitcoin L1, an sBTC-like design with a decentralized open-membership group of signers is the most trust-minimized way to move BTC in and out of Bitcoin layers. Once the necessary upgrades to Bitcoin L1 can be made to enable validity rollups i.e., Bitcoin L1 can enforce BTC withdrawal from a layer, then the Stacks layer can also upgrade to benefit from it. + +Given a trust-minimized asset like sBTC is needed for sovereign rollups, with the launch of sBTC such sovereign rollups become even more interesting to deploy. The Stacks layer can potentially provide the decentralized group of signers for a trust-minimized BTC asset that can be used in a sovereign rollup, and DA comes directly from Bitcoin L1 e.g., with Ordinals. If you want to learn more, please join the [sBTC working group](https://github.com/stacks-network/stacks/discussions/469). There might be a dedicated rollups working group in the Stacks project soon as well. + +### Why Does Stacks Need a Token? + +This brings us to a central philosophical conversation in the world of crypto and Bitcoin, whether or not blockchains need tokens. + +Let's start by looking at the fundamental reason why tokens exist: to fund the maintenance and forward progress of a blockchain. + +Bitcoin is a token. It is a cryptocurrency that is used to incentivize miners to add new blocks to the chain. In Bitcoin's case, mining rewards are set on a predefined schedule, and once those mining rewards run out, the chain will need to survive on transaction fees alone. + +The purpose of a blockchain is to have a permanent historical record of every transaction that has ever occurred on the chain. Blockchains are basically ledgers. The token aspect is used as an incentive mechanism to secure the chain. + +This is why networks like Lightning and other P2P networks don't need tokens, they don't need to maintain a historical record. When we are talking about a system that is supposed to maintain a global financial system, it is important for the maintenance of that system to be incentivized correctly. + +Let's look at this concept in the context of Stacks and its goals. Stacks seeks to provide smart contract functionality to Bitcoin, to serve as the programming rails for building a decentralized economy on top of Bitcoin. + +Many Bitcoin community members are skeptical of new tokens and rightly so. There are countless projects out there that force the use of a token on their project and in many cases a token is actually not needed. Stacks project was started by Bitcoin builders who have a long history of building apps & protocols on Bitcoin L1 without any token (e.g., BNS launched in 2015 on Bitcoin L1 which was one of the largest protocols using OP\_RETURN on Bitcoin L1). So why did a bunch of Bitcoin builders decided to have a separate token for Stacks L2? Great question! Let's dig into the details. + +The Stacks token (STX) is primarily meant to be used for two things (details in Stacks paper): + +1. Incentives for Stacks L2 miners +2. Incentives for peg-out signers + +The only way to remove the token is to build Stacks as a federated network like Liquid. In a federation the pre-selected group of companies control the mining and block production and a pre-selected group of companies need to be trusted for peg-out transactions. Stacks developers wanted to design an open and permissionsless system. The only way to have a decentralized mining process is through incentives. As mentioned above, his is how Bitcoin works as well, where newly minted BTC are used as incentives to mine new blocks and anyone in the world can decide to become a miner. Anyone with BTC can mine the Stacks L2 chain, it is open and permissionless. + +Similarly, the way the decentralized BTC peg, called sBTC (see the sBTC paper), is designed is that the group of signer is open and permissionless (unlike a federation). These signers have economic incentives to correctly follow the protocol for peg-out requests. In a federation, users need to blindly trust the pre-set federation members to get their BTC out of the federation and back on Bitcoin L1. Stacks developers wanted to have an open, permissionless, and decentralized way to move BTC from Bitcoin L1 to Stacks L2 and back. This is made possible through economic incentives i.e., need for a token. + +Other than these two reasons, STX is also used to pay gas fees for transactions. However, once the upcoming sBTC peg is live most of the economy of Stacks L2 is expected to follow a Bitcoin standard and work using BTC as the economic unit. It is expected that users will mostly interact just with Bitcoin and use BTC in wallets and apps (gas fees can be paid with BTC using atomic swaps in the background). It is important to note that BTC cannot be used for mining incentives on Stacks L2 because the only way to incentivize decentralized block production is through newly minted assets by the protocol (similar to how Bitcoin works itself) i.e., need for a token. + +### The Symbiotic Relationship Between Stacks and Bitcoin + +Stacks and Bitcoin complement each other. Stacks leverages the extreme decentralization of Bitcoin, its PoW consensus mechanism, and its value as a cryptocurrency. + +But Stacks also complements Bitcoin by unlocking additional use cases, thereby increasing its value over time. This also helps to solve the additional problem of the future maintainability of Bitcoin after the coinbase rewards are gone and Bitcoin has to function on transaction fees alone. + +If Bitcoin is seen as only a store of value, the economic density, meaning how much value is being exchanged, of each transaction will be minimal. But if Bitcoin is the underlying foundation for an entire decentralized economy, those [transactions become much more valuable](https://twitter.com/muneeb/status/1506976317618728963), increasing transaction fees. This is a crucial incentive for miners to continue securing the network as coinbase rewards drop. + +### Reading from and Writing to Bitcoin + +One of the things that gives the Stacks chain its superpowers in connecting with Bitcoin is not only how it connects to Bitcoin at a protocol level, discussed above, but also how we can utilize that Bitcoin at a programmatic level. + +That's where Clarity comes in. Clarity is the smart contract language for Stacks, and is how we actually build out a lot of the functionality we are talking about here. + +#### How Does Clarity Read BTC State? + +One of the often-touted features of Clarity is that it has access to the state of the Bitcoin chain built in, but how does it actually do that? Because of Stacks' PoX mechanism, every Stacks block is connected to a Bitcoin block, and can query Bitcoin block header hashes with the [`get-burn-block-info?` function](https://github.com/stacksgov/sips/blob/feat/sip-015/sips/sip-015/sip-015-network-upgrade.md#new-method-get-burn-block-info). + +This function allows us to pass in a Bitcoin block height and get back the header hash. The [`burn-block-height` Clarity keyword](https://docs.stacks.co/docs/write-smart-contracts/clarity-language/language-keywords#burn-block-height) will give us the current block height of the Bitcoin chain. + +However, `get-burn-block-info?` only returns data of the Bitcoin block at that height if it has already been processed and was created after the launch of the Stacks chain. So if we want to evaluate whether or not something happened on Bitcoin, we have to wait at least one block later to do so. + +This is step 1 of Clarity contracts being able to serve as the programming layer for Bitcoin, when a BTC transaction is initiated, the first thing that needs to happen is that a Clarity contract needs to become aware of it. This can happen manually by utilizing Clarity functions discussed above with the [BTC library](https://explorer.stacks.co/txid/0x8b112f2b50c1fa864997b7496aaad1e3940700309a3fdcc6c07f1c6f8b9cfb7b?chain=mainnet), as [Catamaran Swaps](https://docs.catamaranswaps.org/en/latest/catamaran.html) do. + +{% hint style="info" %} +Note that this process is made easier by the additional Clarity functions added in 2.1, like the `get-burn-block-info?` function we looked at above. +{% endhint %} + +Or we can automate (albeit at a cost of some centralization in our dapp) using an event-based architecture using something like Hiro's [chainhooks](https://www.hiro.so/blog/meet-4-new-features-in-clarinet#setting-up-trigger-actions-with-chainhooks), which will allow us to automatically trigger a Clarity contract call when a certain BTC transaction is initiated. + +This is the first component of using Stacks to build Bitcoin dapps, the read access to Bitcoin chain. + +#### Can Clarity Write to BTC? + +But how can Stacks actually write to the Bitcoin chain? This has been somewhat of a holy grail problem in the Bitcoin and crypto world for years now, as it solves a difficult dilemma. + +On one hand, Bitcoin write functionality from more expressive smart contract chains allows for us to take the latent capital of Bitcoin and utilize it to build a Bitcoin-based economy. + +Bitcoin has a market cap of more than double that of Ethereum's, but has an almost non-existent app ecosystem because of this write problem. There is a massive market opportunity here for both developers and entrepreneurs if we are able to build decentralized applications that have a rival programming capacity to other chains like Ethereum. + +But the tradeoff here is that allowing this on the Bitcoin chain opens up a lot of new attack vectors and undermines a core tenet of Bitcoin: the fact that it is very simple and very good at doing what it does. + +Vitalik originally wanted to add functionality to Bitcoin, and created Ethereum when that didn't work. There's good reason for this. Allowing additional functionality like this exposes Bitcoin to more risk and attack vectors, this conundrum is what Stacks seeks to solve with sBTC. How can we achieve the same functionality as Ethereum but keep the security of Bitcoin? + +One possible route is by separating the programming and money layers, as Stacks and a few other chains do. While Stacks is a good chunk of the way towards this goal right now, due to the PoX mechanism, there is still some work to be done before it can be considered a Bitcoin programming layer. + +Specifically, it needs a tighter connection to the Bitcoin chain than PoX currently provides, and needs to share complete finality with Bitcoin. + +Second, it needs a completely trustless, decentralized two-way peg between the Stacks chain and Bitcoin, so that end users only have to send Bitcoin to an address, have things happen on the Stacks side, and have Bitcoin returned to them. + +These issues are currently being addressed with sBTC and the Stacks Nakamoto Release, both of which you can [read about on Stacks' website](https://www.stacks.co/learn/sbtc). + +### Bitcoin-First Applications in the Wild + +Putting all of this information together we can begin to see how we might build applications that are Bitcoin-first. The ultimate goal of Stacks is to serve as an invisible programming layer for Bitcoin, where STX operates primarily as a gas token to operate the Stacks network, but users only ever have to interact with it using their Bitcoin wallet. + +One of the most interesting projects currently taking advantage of this functionality is Zest, which uses a protocol called Magic under the hood to trustlessly swap BTC on the Bitcoin chain for xBTC on the Stacks chain. Zest is a protocol for decentralized borrowing and lending with Bitcoin. + +Here are the basics of how it works: + +1. First, Zest generates a unique [HTLC](https://en.bitcoin.it/wiki/Hash\_Time\_Locked\_Contracts) Bitcoin address for liquidity providers to deposit their BTC to, this uses [Magic Protocol](https://www.magic.fun/) under the hood, which allows users to trustlessly swap BTC for xBTC. +2. After LP (liquidity provider) sends funds to the HTLC address and the block is confirmed, the payment is considered in escrow. +3. Now Stacks comes in by reading the Bitcoin state and verifying that deposit actually occurred on the BTC chain, and escrows the corresponding amount of xBTC if it did. If for some reason that were to fail, the LP is still in control of their BTC funds. +4. Once that escrow is confirmed, LP signs a Stacks transaction that transfers control of the BTC over to Magic, at which point Magic controls the BTC and xBTC is transferred to the Zest liquidity pool. Zest tokens are then minted on the Stacks chain, representing a claim token for the LP's BTC funds + +The cool thing is that this all happens in a single Stacks block, because Stacks and Bitcoin blocks are completed in lockstep. For more details on how Magic handles this process we highly recommend [checking out their docs](https://magicstx.gitbook.io/magic-protocol/guides/technical-overview). + +To dive deeper into the details of how Zest is building a Bitcoin-first application, [check out their docs](https://zestprotocol.gitbook.io/zest/what-is-zest-protocol/the-technology-under-the-hood), and if you are interested in building something like this out for yourself, check out their smart contracts. + +### How to Build Bitcoin-First Dapps with Stacks + +We are currently working on a Stacks Cookbook, which will provide numerous examples for how to build Bitcoin-first apps using both Clarity and frontend Stacks and Bitcoin libraries. + +Until then, you can take a look at Magic and Zest, the protocols mentioned above, to see how they are going about facilitating this process using xBTC and model your applications after them. + +We are in the very early stages of being able to unlock and use BTC in decentralized applications, and much more content and tools will be created in the coming year to make this process easier for developers. + +### What's Next? + +As mentioned earlier on this page, xBTC is not an ideal solution. xBTC is a custodial solution run by [Wrapped](https://wrapped.com/). It does however, give us the building blocks we need to begin building Bitcoin-first applications with Stacks. And updates from Stacks 2.1 make it easier to perform these actions. + +With sBTC on its way, we'll begin to see a new batch of innovative projects that are able to build truly trustless, decentralized dapps with Bitcoin. + +Here are some resources to dive into to learn more about improvements coming to Stacks via the 2.1 upgrade, Nakamoto release, and sBTC. + +* [List of New Clarity Functions for 2.1](https://github.com/stacksgov/sips/blob/feat/sip-015/sips/sip-015/sip-015-network-upgrade.md#clarity) +* [How the Stacks 2.1 Transition Impacts Stacking](https://www.hiro.so/blog/how-the-stacks-2-1-transition-impacts-stacking) +* [Developer's Guide to Stacks 2.1](https://www.hiro.so/blog/a-developers-guide-to-stacks-2-1) +* [Learn About sBTC](https://www.stacks.co/learn/sbtc) +* [Stacks Nakamoto Release](https://stx.is/nakamoto) diff --git a/stacks-101/bitcoin-name-system.md b/stacks-101/bitcoin-name-system.md new file mode 100644 index 0000000000..66fe22e8eb --- /dev/null +++ b/stacks-101/bitcoin-name-system.md @@ -0,0 +1,231 @@ +# Bitcoin Name System + +Bitcoin Name System (BNS) is a network system that binds Stacks usernames to off-chain state without relying on any central points of control. + +The Stacks V1 blockchain implemented BNS through first-order name operations. In Stacks V2, BNS is instead implemented through a smart-contract loaded during the genesis block. + +Names in BNS have three properties: + +* **Names are globally unique.** The protocol does not allow name collisions, and all well-behaved nodes resolve a given name to the same state. +* **Names are human-meaningful.** Each name is chosen by its creator. +* **Names are strongly owned.** Only the name's owner can change the state it resolves to. Specifically, a name is owned by one or more ECDSA private keys. + +The Stacks blockchain insures that each node's BNS view is synchronized to all of the other nodes in the world, so queries on one node will be the same on other nodes. Stacks blockchain nodes allow a name's owner to bind up to 40Kb of off-chain state to their name, which will be replicated to all other Stacks blockchain nodes via a P2P network. + +The biggest consequence for developers is that in BNS, reading name state is fast and cheap but writing name state is slow and expensive. This is because registering and modifying names requires one or more transactions to be sent to the underlying blockchain, and BNS nodes will not process them until they are sufficiently confirmed. Users and developers need to acquire and spend the requisite cryptocurrency (STX) to send BNS transactions. + +### Motivation behind name systems + +We rely on name systems in everyday life, and they play a critical role in many different applications. For example, when you look up a friend on social media, you are using the platform's name system to resolve their name to their profile. When you look up a website, you are using the Domain Name Service to resolve the hostname to its host's IP address. When you check out a Git branch, you are using your Git client to resolve the branch name to a commit hash. When you look up someone's PGP key on a keyserver, you are resolving their key ID to their public key. + +What kinds of things do we want to be true about names? In BNS, names are globally unique, names are human-meaningful, and names are strongly owned. However, if you look at these examples, you'll see that each of them only guarantees _two_ of these properties. This limits how useful they can be. + +* In DNS and social media, names are globally unique and human-readable, but not strongly owned. The system operator has the final say as to what each names resolves to. + * **Problem**: Clients must trust the system to make the right choice in what a given name resolves to. This includes trusting that no one but the system administrators can make these changes. +* In Git, branch names are human-meaningful and strongly owned, but not globally unique. Two different Git nodes may resolve the same branch name to different unrelated repository states. + * **Problem**: Since names can refer to conflicting state, developers have to figure out some other mechanism to resolve ambiguities. In Git's case, the user has to manually intervene. +* In PGP, names are key IDs. They are globally unique and cryptographically owned, but not human-readable. PGP key IDs are derived from the keys they reference. + * **Problem**: These names are difficult for most users to remember since they do not carry semantic information relating to their use in the system. + +BNS names have all three properties, and none of these problems. This makes it a powerful tool for building all kinds of network applications. With BNS, we can do the following and more: + +* Build domain name services where hostnames can't be hijacked. +* Build social media platforms where user names can't be stolen by phishers. +* Build version control systems where repository branches do not conflict. +* Build public-key infrastructure where it's easy for users to discover and remember each other's keys. + +### Organization of BNS + +BNS names are organized into a global name hierarchy. There are three different layers in this hierarchy related to naming: + +* **Namespaces**. These are the top-level names in the hierarchy. An analogy to BNS namespaces are DNS top-level domains. Existing BNS namespaces include `.id`, `.podcast`, and `.helloworld`. All other names belong to exactly one namespace. Anyone can create a namespace, but in order for the namespace to be persisted, it must be _launched_ so that anyone can register names in it. Namespaces are not owned by their creators. +* **BNS names**. These are names whose records are stored directly on the blockchain. The ownership and state of these names are controlled by sending blockchain transactions. Example names include `verified.podcast` and `muneeb.id`. Anyone can create a BNS name, as long as the namespace that contains it exists already. +* **BNS subdomains**. These are names whose records are stored off-chain, but are collectively anchored to the blockchain. The ownership and state for these names lives within the P2P network data. While BNS subdomains are owned by separate private keys, a BNS name owner must broadcast their subdomain state. Example subdomains include `jude.personal.id` and `podsaveamerica.verified.podcast`. Unlike BNS namespaces and names, the state of BNS subdomains is _not_ part of the blockchain consensus rules. + +A feature comparison matrix summarizing the similarities and differences between these name objects is presented below: + +| Feature | **Namespaces** | **BNS names** | **BNS Subdomains** | +| -------------------------------------- | -------------- | ------------- | ------------------ | +| Globally unique | X | X | X | +| Human-meaningful | X | X | X | +| Owned by a private key | | X | X | +| Anyone can create | X | X | \[1] | +| Owner can update | | X | \[1] | +| State hosted on-chain | X | X | | +| State hosted off-chain | | X | X | +| Behavior controlled by consensus rules | X | X | | +| May have an expiration date | | X | | + +\[1] Requires the cooperation of a BNS name owner to broadcast its transactions + +### Namespaces + +Namespaces are the top-level name objects in BNS. + +They control a few properties about the names within them: + +* How expensive they are to register +* How long they last before they have to be renewed +* Who (if anyone) receives the name registration fees +* Who is allowed to seed the namespace with its initial names. + +At the time of this writing, by far the largest BNS namespace is the `.id` namespace. Names in the `.id` namespace are meant for resolving user identities. Short names in `.id` are more expensive than long names, and have to be renewed by their owners every two years. Name registration fees are not paid to anyone in particular---they are instead sent to a "black hole" where they are rendered non-spendable (the intention is to discourage ID squatters). + +Unlike DNS, _anyone_ can create a namespace and set its properties. Namespaces are created on a first-come first-serve basis, and once created, they last forever. + +However, creating a namespace is not free. The namespace creator must _burn_ cryptocurrency to do so. The shorter the namespace, the more cryptocurrency must be burned (that is, short namespaces are more valuable than long namespaces). For example, it cost Blockstack PBC 40 BTC to create the `.id` namespace in 2015 (in transaction `5f00b8e609821edd6f3369ee4ee86e03ea34b890e242236cdb66ef6c9c6a1b281`). + +Namespaces can be between 1 and 19 characters long, and are composed of the characters `a-z`, `0-9`, `-`, and `_`. + +### Subdomains + +BNS names are strongly owned because the owner of its private key can generate valid transactions that update its zone file hash and owner. However, this comes at the cost of requiring a name owner to pay for the underlying transaction in the blockchain. Moreover, this approach limits the rate of BNS name registrations and operations to the underlying blockchain's transaction bandwidth. + +BNS overcomes this with subdomains. A **BNS subdomain** is a type of BNS name whose state and owner are stored outside of the blockchain, but whose existence and operation history are anchored to the blockchain. Like their on-chain counterparts, subdomains are globally unique, strongly owned, and human-readable. BNS gives them their own name state and public keys. Unlike on-chain names, subdomains can be created and managed cheaply, because they are broadcast to the BNS network in batches. A single blockchain transaction can send up to 120 subdomain operations. + +This is achieved by storing subdomain records in the BNS name zone files. An on-chain name owner broadcasts subdomain operations by encoding them as `TXT` records within a DNS zone file. To broadcast the zone file, the name owner sets the new zone file hash with a `NAME_UPDATE` transaction and replicates the zone file. This, in turn, replicates all subdomain operations it contains, and anchors the set of subdomain operations to an on-chain transaction. The BNS node's consensus rules ensure that only valid subdomain operations from _valid_ `NAME_UPDATE` transactions will ever be stored. + +For example, the name `verified.podcast` once wrote the zone file hash `247121450ca0e9af45e85a82e61cd525cd7ba023`, which is the hash of the following zone file: + +```bash +$TTL 3600 +1yeardaily TXT "owner=1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH" "seqn=0" "parts=1" "zf0=JE9SSUdJTiAxeWVhcmRhaWx5CiRUVEwgMzYwMApfaHR0cC5fdGNwIFVSSSAxMCAxICJodHRwczovL3BoLmRvdHBvZGNhc3QuY28vMXllYXJkYWlseS9oZWFkLmpzb24iCg==" +2dopequeens TXT "owner=1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH" "seqn=0" "parts=1" "zf0=JE9SSUdJTiAyZG9wZXF1ZWVucwokVFRMIDM2MDAKX2h0dHAuX3RjcCBVUkkgMTAgMSAiaHR0cHM6Ly9waC5kb3Rwb2RjYXN0LmNvLzJkb3BlcXVlZW5zL2hlYWQuanNvbiIK" +10happier TXT "owner=1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH" "seqn=0" "parts=1" "zf0=JE9SSUdJTiAxMGhhcHBpZXIKJFRUTCAzNjAwCl9odHRwLl90Y3AgVVJJIDEwIDEgImh0dHBzOi8vcGguZG90cG9kY2FzdC5jby8xMGhhcHBpZXIvaGVhZC5qc29uIgo=" +31thoughts TXT "owner=1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH" "seqn=0" "parts=1" "zf0=JE9SSUdJTiAzMXRob3VnaHRzCiRUVEwgMzYwMApfaHR0cC5fdGNwIFVSSSAxMCAxICJodHRwczovL3BoLmRvdHBvZGNhc3QuY28vMzF0aG91Z2h0cy9oZWFkLmpzb24iCg==" +359 TXT "owner=1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH" "seqn=0" "parts=1" "zf0=JE9SSUdJTiAzNTkKJFRUTCAzNjAwCl9odHRwLl90Y3AgVVJJIDEwIDEgImh0dHBzOi8vcGguZG90cG9kY2FzdC5jby8zNTkvaGVhZC5qc29uIgo=" +30for30 TXT "owner=1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH" "seqn=0" "parts=1" "zf0=JE9SSUdJTiAzMGZvcjMwCiRUVEwgMzYwMApfaHR0cC5fdGNwIFVSSSAxMCAxICJodHRwczovL3BoLmRvdHBvZGNhc3QuY28vMzBmb3IzMC9oZWFkLmpzb24iCg==" +onea TXT "owner=1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH" "seqn=0" "parts=1" "zf0=JE9SSUdJTiBvbmVhCiRUVEwgMzYwMApfaHR0cC5fdGNwIFVSSSAxMCAxICJodHRwczovL3BoLmRvdHBvZGNhc3QuY28vb25lYS9oZWFkLmpzb24iCg==" +10minuteteacher TXT "owner=1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH" "seqn=0" "parts=1" "zf0=JE9SSUdJTiAxMG1pbnV0ZXRlYWNoZXIKJFRUTCAzNjAwCl9odHRwLl90Y3AgVVJJIDEwIDEgImh0dHBzOi8vcGguZG90cG9kY2FzdC5jby8xMG1pbnV0ZXRlYWNoZXIvaGVhZC5qc29uIgo=" +36questionsthepodcastmusical TXT "owner=1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH" "seqn=0" "parts=1" "zf0=JE9SSUdJTiAzNnF1ZXN0aW9uc3RoZXBvZGNhc3RtdXNpY2FsCiRUVEwgMzYwMApfaHR0cC5fdGNwIFVSSSAxMCAxICJodHRwczovL3BoLmRvdHBvZGNhc3QuY28vMzZxdWVzdGlvbnN0aGVwb2RjYXN0bXVzaWNhbC9oZWFkLmpzb24iCg==" +_http._tcp URI 10 1 "https://dotpodcast.co/" +``` + +Each `TXT` record in this zone file encodes a subdomain-creation. For example, `1yeardaily.verified.podcast` resolves to: + +```json +{ + "address": "1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH", + "blockchain": "bitcoin", + "last_txid": "d87a22ebab3455b7399bfef8a41791935f94bc97aee55967edd5a87f22cce339", + "status": "registered_subdomain", + "zonefile_hash": "e7acc97fd42c48ed94fd4d41f674eddbee5557e3", + "zonefile_txt": "$ORIGIN 1yeardaily\n$TTL 3600\n_http._tcp URI 10 1 \"https://ph.dotpodcast.co/1yeardaily/head.json\"\n" +} +``` + +This information was extracted from the `1yeardaily` `TXT` resource record in the zone file for `verified.podcast`. + +#### Subdomain Lifecycle + +Note that `1yeardaily.verified.podcast` has a different public key hash (address) than `verified.podcast`. A BNS node will only process a subsequent subdomain operation on `1yeardaily.verified.podcast` if it includes a signature from this address's private key. `verified.podcast` cannot generate updates; only the owner of `1yeardaily.verified.podcast can do so`. + +The lifecycle of a subdomain and its operations is shown in Figure 2. + +``` + subdomain subdomain subdomain + creation update transfer ++----------------+ +----------------+ +----------------+ +| cicero | | cicero | | cicero | +| owner="1Et..." | signed | owner="1Et..." | signed | owner="1cJ..." | +| zf0="7e4..." |<--------| zf0="111..." |<--------| zf0="111..." |<---- ... +| seqn=0 | | seqn=1 | | seqn=2 | +| | | sig="xxxx" | | sig="xxxx" | ++----------------+ +----------------+ +----------------+ + | | | + | off-chain | | +~ ~ ~ ~ | ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~|~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ | ~ ~ ~ ~ ~ ~ ~ ... + | on-chain | | + V V (zone file hash ) V ++----------------+ +----------------+ +----------------+ +| res_publica.id | | jude.id | | res_publica.id | +| NAME_UPDATE |<--------| NAME_UPDATE |<--------| NAME_UPDATE |<---- ... ++----------------+ +----------------+ +----------------+ + blockchain blockchain blockchain + block block block + + +Figure 2: Subdomain lifetime with respect to on-chain name operations .A new +subdomain operation will only be accepted if it has a later "sequence=" number, +and a valid signature in "sig=" over the transaction body .The "sig=" field +includes both the public key and signature, and the public key must hash to +the previous subdomain operation's "addr=" field. + +The subdomain-creation and subdomain-transfer transactions for +"cicero.res_publica.id" are broadcast by the owner of "res_publica.id." +However, any on-chain name ("jude.id" in this case) can broadcast a subdomain +update for "cicero.res_publica.id." +``` + +Subdomain operations are ordered by sequence number, starting at 0. Each new subdomain operation must include: + +* The next sequence number +* The public key that hashes to the previous subdomain transaction's address +* A signature from the corresponding private key over the entire subdomain operation. + +If two correctly signed but conflicting subdomain operations are discovered (that is, they have the same sequence number), the one that occurs earlier in the blockchain's history is accepted. Invalid subdomain operations are ignored. + +Combined, this ensures that a BNS node with all of the zone files with a given subdomain's operations will be able to determine the valid sequence of state-transitions it has undergone, and determine the current zone file and public key hash for the subdomain. + +#### Subdomain Creation and Management + +Unlike an on-chain name, a subdomain owner needs an on-chain name owner's help to broadcast their subdomain operations. In particular: + +* A subdomain-creation transaction can only be processed by the owner of the on-chain name that shares its suffix. For example, only the owner of `res_publica.id` can broadcast subdomain-creation transactions for subdomain names ending in `.res_publica.id`. +* A subdomain-transfer transaction can only be broadcast by the owner of the on-chain name that created it. For example, the owner of `cicero.res_publica.id` needs the owner of `res_publica.id` to broadcast a subdomain-transfer transaction to change `cicero.res_publica.id`'s public key. +* In order to send a subdomain-creation or subdomain-transfer, all of an on-chain name owner's zone files must be present in the Atlas network. This lets the BNS node prove the _absence_ of any conflicting subdomain-creation and subdomain-transfer operations when processing new zone files. +* A subdomain update transaction can be broadcast by _any_ on-chain name owner, but the subdomain owner needs to find one who will cooperate. For example, the owner of `verified.podcast` can broadcast a subdomain-update transaction created by the owner of `cicero.res_publica.id`. + +That said, to create a subdomain, the subdomain owner generates a subdomain-creation operation for their desired name and gives it to the on-chain name owner. + +Once created, a subdomain owner can use any on-chain name owner to broadcast a subdomain-update operation. To do so, they generate and sign the requisite subdomain operation and give it to an on-chain name owner, who then packages it with other subdomain operations into a DNS zone file and broadcasts it to the network. + +If the subdomain owner wants to change the address of their subdomain, they need to sign a subdomain-transfer operation and give it to the on-chain name owner who created the subdomain. They then package it into a zone file and broadcast it. + +#### Subdomain Registrars + +Because subdomain names are cheap, developers may be inclined to run subdomain registrars on behalf of their applications. For example, the name `personal.id` is used to register usernames without requiring them to spend any Bitcoin. + +We supply a reference implementation of a [BNS Subdomain Registrar](https://github.com/stacks-network/subdomain-registrar) to help developers broadcast subdomain operations. Users would still own their subdomain names; the registrar simply gives developers a convenient way for them to register and manage them in the context of a particular application. + +### BNS and DID Standards + +BNS names are compliant with the emerging [Decentralized Identity Foundation](http://identity.foundation) protocol specification for decentralized identifiers (DIDs). + +Each name in BNS has an associated DID. The DID format for BNS is: + +```bash + did:stack:v0:{address}-{index} +``` + +Where: + +* `{address}` is an on-chain public key hash (for example a Bitcoin address). +* `{index}` refers to the `nth` name this address created. + +For example, the DID for `personal.id` is `did:stack:v0:1dARRtzHPAFRNE7Yup2Md9w18XEQAtLiV-0`, because the name `personal.id` was the first-ever name created by `1dARRtzHPAFRNE7Yup2Md9w18XEQAtLiV`. + +As another example, the DID for `jude.id` is `did:stack:v0:16EMaNw3pkn3v6f2BgnSSs53zAKH4Q8YJg-1`. Here, the address `16EMaNw3pkn3v6f2BgnSSs53zAKH4Q8YJg` had created one earlier name in history prior to this one (which happens to be `abcdefgh123456.id`). + +The purpose of a DID is to provide an eternal identifier for a public key. The public key may change, but the DID will not. + +Stacks Blockchain implements a DID method of its own in order to be compatible with other systems that use DIDs for public key resolution. In order for a DID to be resolvable, all of the following must be true for a name: + +* The name must exist +* The name's zone file hash must be the hash of a well-formed DNS zone file +* The DNS zone file must be present in the Stacks node's data. +* The DNS zone file must contain a `URI` resource record that points to a signed JSON Web Token +* The public key that signed the JSON Web Token (and is included with it) must hash to the address that owns the name + +Not all names will have DIDs that resolve to public keys. However, names created by standard tooling will have DIDs that do. + +A RESTful API is under development. + +### DID Encoding for Subdomains + +Every name and subdomain in BNS has a DID. The encoding is slightly different for subdomains, so the software can determine which code-path to take. + +* For on-chain BNS names, the `{address}` is the same as the Bitcoin address that owns the name. Currently, both version byte 0 and version byte 5 addresses are supported (that is, addresses starting with `1` or `3`, meaning `p2pkh` and `p2sh` addresses). +* For off-chain BNS subdomains, the `{address}` has version byte 63 for subdomains owned by a single private key, and version byte 50 for subdomains owned by a m-of-n set of private keys. That is, subdomain DID addresses start with `S` or `M`, respectively. + +The `{index}` field for a subdomain's DID is distinct from the `{index}` field for a BNS name's DID, even if the same created both names and subdomains. For example, the name `abcdefgh123456.id` has the DID `did:stack:v0:16EMaNw3pkn3v6f2BgnSSs53zAKH4Q8YJg-0`, because it was the first name created by `16EMaNw3pkn3v6f2BgnSSs53zAKH4Q8YJg`. However, `16EMaNw3pkn3v6f2BgnSSs53zAKH4Q8YJg` _also_ created `jude.statism.id` as its first subdomain name. The DID for `jude.statism.id` is `did:stack:v0:SSXMcDiCZ7yFSQSUj7mWzmDcdwYhq97p2i-0`. Note that the address `SSXMcDiCZ7yFSQSUj7mWzmDcdwYhq97p2i` encodes the same public key hash as the address `16EMaNw3pkn3v6f2BgnSSs53zAKH4Q8YJg` (the only difference between these two strings is that the first is base58check-encoded with version byte 0, and the second is encoded with version byte 63). diff --git a/stacks-101/mining.md b/stacks-101/mining.md new file mode 100644 index 0000000000..c82088f8be --- /dev/null +++ b/stacks-101/mining.md @@ -0,0 +1,78 @@ +# Mining + +### Introduction + +This guide highlights some technical details related to mining on the Stacks 2.0 network. + +For steps on how to setup your own miner please refer to miner on testnet and miner on mainnet. + +### Mining frequency + +A new Stacks block may be mined once per Bitcoin block. To be considered for mining a block, a miner must have a block commit included in a Bitcoin block. If a miner wishes to update their commitment after submission, they may use Bitcoin Replace-By-Fee. + +### Coinbase rewards + +Miners receive coinbase rewards for blocks they win. + +The reward amounts are: + +* 1000 STX per block are released in the first 4 years of mining +* 500 STX per block are released during the following 4 years +* 250 STX per block are released during the following 4 years +* 125 STX per block are released from then on indefinitely. + +These "halvings" are synchronized with Bitcoin halvings. + +
+ +### Transaction fees + +Miners receive Stacks fees for transactions mined in any block they produce. + +For transactions mined in microblocks, the miner that produces the microblock receives 40% of the fees, while the miner that confirms the microblock receives 60% of the fees. + +### Reward maturity + +Block rewards and transaction fees take 100 blocks on the Bitcoin blockchain to mature. After successfully mining a block your rewards appear in your Stacks account after \~24 hours. + +### Mining with proof-of-transfer + +Miners commit Bitcoin to **two** addresses in every leader block commit. The amount committed to each address must be the same. The addresses are chosen from the current reward set of stacking participants. Addresses are chosen using a verifiable-random-function, and determining the correct two addresses for a given block requires monitoring the Stacks chain. + +
+ +PoX mining is a modification of Proof-of-Burn (PoB) mining, where instead of sending the committed Bitcoin to a burn address, it's transferred to eligible STX holders that participate in the stacking protocol. + +{% hint style="info" %} +A PoX miner can only receive newly minted STX tokens when they transfer Bitcoin to eligible owners of STX tokens +{% endhint %} + +
+ +Miners run Stacks nodes with mining enabled to participate in the PoX mechanism. The node implements the PoX mechanism, which ensures proper handling and incentives through four key phases: + +* Registration: miners register for a future election by sending consensus data to the network +* Commitment: registered miners transfer Bitcoin to participate in the election. Committed BTC are sent to a set participating STX token holders +* Election: a verifiable random function chooses one miner to write a new block on the Stacks blockchain +* Assembly: the elected miner writes the new block and collects rewards in form of new STX tokens + +### Probability to mine next block + +The miner who is selected to mine the next block is chosen depending on the amount of BTC the miners sent, that is, transferred or burnt. + +The probability for a miner to mine the next block equals the BTC the miner sent divided by the total BTC all miners sent. + +While there is no minimum BTC commitment enforced by the protocol, in practice, there's a floor constrained by [dust](https://unchained-capital.com/blog/dust-thermodynamics/)": basically, if the fees for a transaction exceed the value of the spent output, it's considered dust. How dust is [calculated](https://github.com/bitcoin/bitcoin/blob/master/src/policy/policy.cpp#L14) depends on a number of factors, we've found 5,500 satoshis to be good lower bound per [output](https://learnmeabitcoin.com/technical/output). Bitcoin transactions from Stacks miners contain two outputs (for Proof-of-Transfer), so a commitment of at least 11,000 satoshis / block is recommended. + +To calculate the amount of BTC to send miners should: + +* Guess the price BTC/STX for the next day (100 blocks later) +* Guess the total amount of BTCs committed by all miners + +### Microblocks + +The Stacks blockchain produces blocks at the same rate as the Bitcoin blockchain. In order to provide lower latency transactions, miners can opt to enable microblocks. Microblocks allow the current block leader to stream transactions and include their state transitions in the current epoch. + +If a block leader opts to produce microblocks, the next leader builds the chain tip off the last microblock that the current leader produces. + +The block streaming model is described in [SIP-001](https://github.com/stacksgov/sips/blob/main/sips/sip-001/sip-001-burn-election.md#operation-as-a-leader). diff --git a/stacks-101/network.md b/stacks-101/network.md new file mode 100644 index 0000000000..8069bd58c0 --- /dev/null +++ b/stacks-101/network.md @@ -0,0 +1,148 @@ +# Network + +### Tokens + +Stacks (STX) tokens are the native tokens on the Stacks 2.0 blockchain. The smallest fraction is one micro-STX. 1,000,000 micro-STX make one Stacks (STX). + +STX amounts should be stored as integers (8 bytes long), and represent the amount of micro-STX. For display purposes, micro-STX are divided by 1,000,000 (decimal precision of 6). + +### Fees + +Fees are used to incentivize miners to confirm transactions on the Stacks 2.0 blockchain. The fee is calculated based on the estimate fee rate and the size of the raw transaction in bytes. The fee rate is a market determined variable. For the testnet, it is set to 1 micro-STX. + +Fee estimates can obtained through the [`GET /v2/fees/transfer`](https://docs.hiro.so/api#operation/get\_fee\_transfer) endpoint: + +```bash +# for mainnet, replace `testnet` with `mainnet` +curl 'https://stacks-node-api.testnet.stacks.co/v2/fees/transfer' +``` + +The API will respond with the fee rate (as integer): + +```json +1 +``` + +[The Stacks Transactions JS library](https://github.com/hirosystems/stacks.js/tree/master/packages/transactions) supports fee estimation for: + +* token transfers (`estimateTransfer`) +* contract deploys (`estimateContractDeploy`) +* non read-only contract calls (`estimateContractFunctionCall`) + +{% hint style="info" %} +For an implementation using a different language than JavaScript, please review [this reference implementation](https://github.com/hirosystems/stacks.js/blob/master/packages/transactions/src/builders.ts#L97). +{% endhint %} + +### Nonces + +Every account carries a [nonce property](https://en.wikipedia.org/wiki/Cryptographic\_nonce) that indicates the number of transactions processed for the given account. Nonces are one-time codes, starting at `0` for new accounts, and incremented by 1 on every transaction. + +Nonces are added to all transactions and help identify them in order to ensure transactions are processed in order and to avoid duplicated processing. + +{% hint style="info" %} +The consensus mechanism also ensures that transactions aren't "replayed" in two ways. First, nodes query its unspent transaction outputs (UTXOs) in order to satisfy their spending conditions in a new transaction. Second, messages sent between nodes review sequence numbers. +{% endhint %} + +When a new token transfer transaction is constructed, the most recent nonce of the account needs to fetched and set. + +{% hint style="info" %} +The API provides an endpoint to [simplify nonce handling](https://docs.hiro.so/get-started/stacks-blockchain-api#nonce-handling). +{% endhint %} + +### Confirmations + +The Stacks 2.0 network is anchored onto the bitcoin network. This allows transactions on Stacks to inherit the same finality and security of the Bitcoin blockchain. + +The time to mine a block, to confirm transactions, will eventually match the expected "block time" of the bitcoin network: 10 minutes. + +The block time is hardcoded and will change throughout the implementation phases of the testnet. The current block time can be obtained through the [`GET /extended/v1/info/network_block_times`](https://docs.hiro.so/api#operation/get\_network\_block\_times) endpoint: + +```bash +# for mainnet, replace `testnet` with `mainnet` +curl 'https://stacks-node-api.testnet.stacks.co/extended/v1/info/network_block_times' +``` + +The API will respond with the block time (in seconds): + +```js +{ + "testnet": { + "target_block_time": 120 + }, + "mainnet": { + "target_block_time": 600 + } +} +``` + +### Read-only function calls + +Smart contracts can expose public function calls. For functions that make state modifications to the blockchain, transactions need to be generated and broadcasted. + +However, for read-only function calls, transactions are **not** required. Instead, these calls can be done using the [Stacks Blockchain API](https://docs.hiro.so/get-started/stacks-blockchain-api). + +{% hint style="info" %} +Read-only function calls do not require transaction fees +{% endhint %} + +A read-only contract call can be done using the [`POST /v2/contracts/call-read///`](https://docs.hiro.so/api#operation/call\_read\_only\_function) endpoint: + +```bash +# for mainnet, replace `testnet` with `mainnet` +curl --location --request POST 'https://stacks-node-api.testnet.stacks.co/v2/contracts/call-read///' \ +--header 'Content-Type: application/json' \ +--data-raw '{ + "sender": ".", + "arguments": [, ...] +}' +``` + +Sample response for a successful call: + +```js +{ + "okay": true, + "result": "" +} +``` + +{% hint style="info" %} +To set the function call arguments and read the result, Clarity values need to be serialized into a hexadecimal string. The [Stacks Transactions JS](https://github.com/hirosystems/stacks.js/tree/master/packages/transactions) library supports these operations +{% endhint %} + +### Querying + +Stacks 2.0 network details can be queried using the [Stacks Blockchain API](https://docs.hiro.so/get-started/stacks-blockchain-api). + +#### Health check + +The [status checker](https://stacks-status.com/) is a service that provides a user interface to quickly review the health of the Stacks 2.0 blockchain. + +#### Network info + +The network information can be obtained using the [`GET /v2/info`](https://docs.hiro.so/api#operation/get\_core\_api\_info) endpoint: + +```bash +# for mainnet, replace `testnet` with `mainnet` +curl 'https://stacks-node-api.testnet.stacks.co/v2/info' +``` + +Sample response: + +```js +{ + "peer_version": 385875968, + "burn_consensus": "826401d65cf3671210a3fb135d827d549c0b4d37", + "burn_block_height": 1972, + "stable_burn_consensus": "e27ea23f199076bc41a729d76a813e125b725f64", + "stable_burn_block_height": 1971, + "server_version": "blockstack-core 0.0.1 => 23.0.0.0 (master:bdd042242+, release build, linux [x86_64]", + "network_id": 2147483648, + "parent_network_id": 3669344250, + "stacks_tip_height": 933, + "stacks_tip": "1f601823fbcc5b6b2215b2ff59d2818fba61ee4a3cea426d8bc3dbb268005d8f", + "stacks_tip_burn_block": "54c56a9685545c45accf42b5dcb2787c97eda8185a1c794daf9b5a59d4807abc", + "unanchored_tip": "71948ee211dac3b241eb65d881637f649d0d49ac08ee4a41c29217d3026d7aae", + "exit_at_block_height": 28160 +} +``` diff --git a/stacks-101/post-conditions.md b/stacks-101/post-conditions.md new file mode 100644 index 0000000000..33517e4b64 --- /dev/null +++ b/stacks-101/post-conditions.md @@ -0,0 +1,35 @@ +# Post Conditions + +Post conditions are one of the most interesting and unique aspects of Stacks. + +From the beginning, safety and security has been at the heart of the Stacks ethos and formed the foundation of architecture decisions when building it. + +Like Clarity, Stacks' smart contract programming language, post conditions were specifically built and design to solve the problem of user safety when interacting with blockchain applications. + +So what are they and how do they work? + +### How Post Conditions Work + +Post conditions are conditions that are set on the client side to ensure that a smart contract does not perform any unexpected behavior. + +Let's look at an example to make this more concrete. + +Let's say a user is on an NFT marketplace and is expecting to purchase an NFT for 100 STX. Using post conditions, the developer who is building the frontend of the application can add in post conditions to ensure that this is in fact what happens when the user initiates the transaction. + +If it does not, the transaction will abort and the user won't be out anything except the transaction fee. + +It's important to note that post conditions do not live in smart contracts. They are designed to be an extra layer of security on top of smart contracts. + +The problem they help address is a user interacting with a malicious smart contract that attempts to do something the user does not expect. + +But rather than simply being a UI feature of a wallet, these post conditions are built into the Stacks blockchain itself and are enforced at the protocol level. + +When you use a Stacks wallet like the Hiro web wallet and initiate a transaction, the wallet will display the post conditions set by the developer and tell the user exactly what is going to happen. If the action taken by the smart contract matches, the transaction goes through fine, otherwise it aborts. + +Here's what that looks like: + +
+ +In this example, if the smart contract does not transfer one fabulous-frog NFT and and take 50 STX from the user, the transaction will abort. + +You can learn more about how post conditions work in [SIP-005](https://github.com/stacksgov/sips/blob/main/sips/sip-005/sip-005-blocks-and-transactions.md#transaction-post-conditions) and how to utilize them in your applications in the tutorial, [Understanding Stacks Post Conditions](https://dev.to/stacks/understanding-stacks-post-conditions-e65). diff --git a/stacks-101/proof-of-transfer.md b/stacks-101/proof-of-transfer.md new file mode 100644 index 0000000000..85c2fc81ed --- /dev/null +++ b/stacks-101/proof-of-transfer.md @@ -0,0 +1,77 @@ +# Proof of Transfer + +In the previous section, we took a look at the vision and ethos of Stacks. We talked a lot about it being connected to Bitcoin and how it enables expanding functionality without modifying Bitcoin itself. + +In this section, we'll run through the consensus mechanism that makes that happen, Proof of Transfer. + +Consensus algorithms for blockchains require compute or financial resources to secure the blockchain. The general practice of decentralized consensus is to make it practically infeasible for any single malicious actor to have enough computing power or ownership stake to attack the network. + +Popular consensus mechanisms in modern blockchains include proof of work, in which nodes dedicate computing resources, and proof of stake, in which nodes dedicate financial resources to secure the network. + +Proof of burn is another, less-frequently used consensus mechanism where miners compete by ‘burning’ (destroying) a proof of work cryptocurrency as a proxy for computing resources. + +Proof of transfer (PoX) is an extension of the proof of burn mechanism. PoX uses the proof of work cryptocurrency of an established blockchain to secure a new blockchain. However, unlike proof of burn, rather than burning the cryptocurrency, miners transfer the committed cryptocurrency to some other participants in the network. + +
+ +This allows network participants to secure the PoX cryptocurrency network and earn a reward in the base cryptocurrency. Thus, PoX blockchains are anchored on their chosen PoW chain. Stacks uses Bitcoin as its anchor chain. + +
+ +### Why Bitcoin? + +There are a number of reasons that Stacks chose Bitcoin as the blockchain to power consensus. It's the oldest blockchain protocol, having launched in 2009, and has become a recognized asset outside of the cryptocurrency community. BTC has held the highest market capitalization of any cryptocurrency for the past decade. + +Bitcoin champions simplicity and stability, and has stood the test of time. Influencing or attacking the network is infeasible or impractical for any potential hackers. It's one of the only cryptocurrencies to capture public attention. Bitcoin is a household name, and is recognized as an asset by governments, large corporations, and legacy banking institutions. Lastly, Bitcoin is largely considered a reliable store of value, and provides extensive infrastructure to support the PoX consensus mechanism. + +SIP-001 provides a full [list of reasons why Bitcoin was chosen to secure Stacks](https://github.com/stacksgov/sips/blob/main/sips/sip-001/sip-001-burn-election.md). + +{% hint style="info" %} +By the way, SIP stands for Stacks Improvement Proposal, and it's the process by which community members agree on making changes to the network, we'll look at these in a future lesson. +{% endhint %} + +### Blocks and microblocks + +The Stacks blockchain allows for increased transaction throughput using a mechanism called microblocks. Bitcoin and Stacks progress in lockstep, and their blocks are confirmed simultaneously. On Stacks, this is referred to as an ‘anchor block’. An entire block of Stacks transactions corresponds to a single Bitcoin transaction. This significantly improves cost/byte ratio for processing Stacks transactions. Because of simultaneous block production, Bitcoin acts as a rate-limiter for creating Stacks blocks, thereby preventing denial-of-service attacks on its peer network. + +However, in between Stacks anchor blocks settling on the Bitcoin blockchain, there are also a varying number of microblocks that allow rapid settlement of Stacks transactions with a high degree of confidence. This allows Stacks transaction throughput to scale independently of Bitcoin, while still periodically establishing finality with the Bitcoin chain. The Stacks blockchain adopts a block streaming model whereby each leader can adaptively select and package transactions into their block as they arrive in the mempool. Therefore when an anchor block is confirmed, all of the transactions in the parent microblock stream are packaged and processed. This is an unprecedented method for achieving scalability without creating a totally separate protocol from Bitcoin. + +### Unlocking Bitcoin capital + +In the previous section we talked about Stacks being able to allow us to build a decentralized economy on top of Bitcoin and that PoX was a key piece of being able to do that. + +The reason is two-fold. First, as a part of this PoX mining process we have covered here, a hash of each Stacks block is recorded to the OP\_RETURN opcode of a Bitcoin transaction. If you aren't familiar, the OP\_RETURN opcode allows us to store up to 40 bytes of arbitrary data in a Bitcoin transaction. + +{% hint style="info" %} +This [Stack Exchange answer](https://bitcoin.stackexchange.com/questions/29554/explanation-of-what-an-op-return-transaction-looks-like) gives a good overview of the reasoning and history of this opcode. +{% endhint %} + +This is how Stacks records its history to the Bitcoin chain and why it inherits some security as a result of this process. If you wanted to try and create a false Stacks fork, you would have to broadcast the entire process to the Bitcoin chain. + +Similarly, if you wanted to try and change the history of the Stacks chain, you would have to somehow modify these OP\_RETURN values in each corresponding Bitcoin block, meaning you would have to compromise Bitcoin in order to compromise the history of Stacks. + +{% hint style="warning" %} +Note that this is not the same thing as saying that you need to compromise Bitcoin in order compromise Stacks at all, but simply that in order to falsify the history of the Stacks chain you would have to also falsify the history of the Bitcoin chain. +{% endhint %} + +Additionally, part of this PoX process involves each Stacks block also knowing which Bitcoin block it is anchored to. Clarity, Stacks' smart contract language, has built-in functions for reading this data, such as [`get-block-info`](https://docs.stacks.co/docs/write-smart-contracts/clarity-language/language-functions#get-block-info), which returns, among other things, a field called `burnchain-header-hash`, which gives us the hash of the Bitcoin header corresponding to this Stacks block. + +This allows us to do really interesting things like trigger certain things to happen in a Clarity contract by watching the chain and verifying whether or not certain transactions occurred. You can see this in action in [Catamaran Swaps](https://docs.catamaranswaps.org/en/latest/catamaran.html), with other interesting projects like [Zest](https://www.zestprotocol.com/) seeking to expand on this functionality. + +The ultimate goal of all this is to enable the vision of web3, building a decentralized economy and enabling true user ownership of assets and data, on top of Bitcoin as a settlement layer, and using Bitcoin as a base decentralized money. + +
+ +We also recommend [reading the full PoX whitepaper](https://community.stacks.org/pox), as it breaks down the reasoning behind creating a PoX chain and the unique benefits we get from doing so. + +### Proof of Transfer Contracts and Technical Details + +The Proof of Transfer functionality is implemented on the Stacks chain via a [Clarity smart contract](https://explorer.stacks.co/txid/0xfc878ab9c29f3d822a96ee73898000579bdf69619a174e748672eabfc7cfc589). An overview of this contract is available in the docs. + +You can see the original design for stacking and proof of transfer by reading the relevant SIP, [SIP-007](https://github.com/stacksgov/sips/blob/main/sips/sip-007/sip-007-stacking-consensus.md). You can also utilize [Hiro's API](https://docs.hiro.so/api#tag/Info/operation/get\_pox\_info) to get proof of transfer details including the relevant contract address. + +However, since Stacks mainnet launched in January 2021, several shortcomings have been recognized in the stacking process, which are being corrected in the next major network epoch, Stacks 2.1 You can read more about these changes in [SIP-015](https://github.com/stacksgov/sips/blob/feat/sip-015/sips/sip-015/sip-015-network-upgrade.md), the SIP responsible for managing the upgrade to 2.1. + +#### Got another question + +Have another question not answered here? Post in on Stack Overflow under the appropriate tag(s) and post the link to the Stack Overflow question in the Stacks Discord in the appropriate channel. diff --git a/stacks-101/sips.md b/stacks-101/sips.md new file mode 100644 index 0000000000..4ab6b46c66 --- /dev/null +++ b/stacks-101/sips.md @@ -0,0 +1,48 @@ +# SIPs + +### Stacks Improvement Proposals (SIPs) + +Stacks improvement proposals (SIPs) are aimed at describing the implementation of the Stacks blockchain, as well as proposing improvements. + +The SIP process [(SIP-000)](https://github.com/stacksgov/sips/blob/main/sips/sip-000/sip-000-stacks-improvement-proposal-process.md) describes how to make a SIP and get it ratified. + +They should contain concise technical specifications of features or standards and the rationale behind it. SIPs are intended to be the primary medium for proposing new features, for collecting community input on a system-wide issue, and for documenting design decisions. + +The SIPs are located in the [stacksgov/sips](https://github.com/stacksgov/sips) repository as part of the [Stacks Community Governance organization](https://github.com/stacksgov). + +Anyone in the Stacks community can submit a SIP. + +{% hint style="info" %} +Stacks Improvement Proposals Community Calls Add the [weekly community SIP call](https://www.addevent.com/event/wS15955379) to your calendar. + +SIP Meeting calls are recorded and available [here](https://www.youtube.com/playlist?list=PLg717Ri\_rTnx5kuaWqp3cUAtwQk\_yzslT) + +More details of the meetings are available [here](https://github.com/stacksgov/sips/issues/79) +{% endhint %} + +### Ratified SIPSs + +* [x] [SIP 001: Burn Election](https://github.com/stacksgov/sips/blob/main/sips/sip-001/sip-001-burn-election.md) +* [x] [SIP 002: Clarity, a language for predictable smart contracts](https://github.com/stacksgov/sips/blob/main/sips/sip-002/sip-002-smart-contract-language.md) +* [x] [SIP 003: Peer Network](https://github.com/stacksgov/sips/blob/main/sips/sip-003/sip-003-peer-network.md) +* [x] [SIP 004: Cryptographic Commitment to Materialized Views](https://github.com/stacksgov/sips/blob/main/sips/sip-004/sip-004-materialized-view.md) +* [x] [SIP 005: Blocks, Transactions, and Accounts](https://github.com/stacksgov/sips/blob/main/sips/sip-005/sip-005-blocks-and-transactions.md) +* [x] [SIP 006: Clarity Execution Cost Assessment](https://github.com/stacksgov/sips/blob/main/sips/sip-006/sip-006-runtime-cost-assessment.md) +* [x] [SIP 007: Stacking Consensus](https://github.com/stacksgov/sips/blob/main/sips/sip-007/sip-007-stacking-consensus.md) +* [x] [SIP 008: Clarity Parsing and Analysis Cost Assessment](https://github.com/stacksgov/sips/blob/main/sips/sip-008/sip-008-analysis-cost-assessment.md) +* [x] [SIP 009: Standard Trait Definition for Non-Fungible Tokens](https://github.com/stacksgov/sips/blob/main/sips/sip-009/sip-009-nft-standard.md) +* [x] [SIP 010: Standard Trait Definition for Fungible Tokens](https://github.com/stacksgov/sips/blob/main/sips/sip-010/sip-010-fungible-token-standard.md) +* [x] [SIP 012: Burn Height Selection for a Network Upgrade to Introduce New Cost-Limits](https://github.com/stacksgov/sips/blob/main/sips/sip-012/sip-012-cost-limits-network-upgrade.md) +* [x] [SIP-015: Stacks Upgrade of Proof-of-Transfer and Clarity](https://github.com/stacksgov/sips/blob/main/sips/sip-015/sip-015-network-upgrade.md) +* [x] [SIP-016: Metadata for Tokens](https://github.com/stacksgov/sips/blob/main/sips/sip-016/sip-016-token-metadata.md) +* [x] [SIP-018: Signed Structured Data](https://github.com/stacksgov/sips/blob/main/sips/sip-018/sip-018-signed-structured-data.md) +* [x] [SIP-020: Bitwise Operations in Clarity](https://github.com/stacksgov/sips/blob/main/sips/sip-020/sip-020-bitwise-ops.md) + +### How to Get Involved + +There are several ways you can get involved with the SIP process: + +* **Join the weekly SIP Meeting call** listed [here](https://community.stacks.org/events). +* **SIP Editor**. SIP editors help SIP authors make sure their SIPs are well-formed and follow the right process. They help get SIPs ready for deep review by advancing it them from Draft to Accepted status. If you want to become a SIP editor, open an issue with your name and email to ask to be added to the list of SIP editors. +* **Join a CAB** (Consideration Advisory Board). SIPs fall under the purview of one or more considerations. A full list is in [this github](https://github.com/stacksgov/sips/tree/main/considerations) directory. Currently they are: Diversity, Economics, Ethics, Governance and Technical. Members of SIP consideration advisory boards use their domain expertise to give Accepted SIPs a deep read, and give the authors any/all feedback to help make the SIP workable. If you want to join a board, reach out to the board's chairperson via the listed contact information. +* **Steering Committee**. The Steering Committee organizes the consideration advisory boards and votes to advance Recommended SIPs to Activation-in-Progress status, and then to either Ratified or Rejected status. Once they are in the process of being activated, they use a SIP's Activation section to determine whether or not the Stacks ecosystem has ratified or rejected the SIP. Joining this committee requires the consent of the Stacks Foundation board. diff --git a/stacks-101/stacking.md b/stacks-101/stacking.md new file mode 100644 index 0000000000..3d7ba77a78 --- /dev/null +++ b/stacks-101/stacking.md @@ -0,0 +1,184 @@ +# Stacking + +### Introduction + +Stacking rewards Stacks (STX) token holders with bitcoin for providing a valuable service to the network by locking up their tokens for a certain time. + +### Stacking vs Staking + +It is crucial to note that this has no impact on block production or validation. **Stacks is not a Proof-of-Stake network and **_**stacking**_** is different than **_**staking**_. + +There are two primary differences between stacking in Stacks and staking in other PoS networks. + +#### Yield generated in burnchain token + +In staking, users lock one token and earn their yield in the same token. In stacking, users lock one token (STX) and earn a yield in the "burnchain" token (BTC), rather than the same token that was locked. In PoX, the yield comes from a finite, external source (Bitcoin deposits from Stacks miners). In PoS, the yield comes from the currency's issuance schedule itself, which means it is programmatically unlimited (but theoretically limited, we'll get into this a bit more below). + +That's the first main difference. Staking involves a yield of the same token being generated by the issuance mechanism set by the core protocol, where stacking yield requires an input of an external, separate token. + +How are these issuance rates set? In Ethereum, issuance rates are determined by network usage. Ethereum's goal is to create a deflationary money supply, so the issuance rate is determined depending on the usage of the network. In order for an Ethereum transaction to be considered valid, it must include a base fee that is burned during transaction execution. The [issuance rate is algorithmically determined](https://ethereum.org/en/roadmap/merge/issuance/#post-merge) block-by-block depending on how much ETH is being burned by these base fees plus normal gas fees. + +Stacking doesn't have any of this complex functionality, since it does not generate a yield of the same token (and therefore doesn't need to issue new tokens, but rather transfer existing tokens from the base network) and it does not need to maintain an issuance rate. We are speaking here of the yield specifically, Stacks does have an issuance rate and does generate new STX tokens, but this process is completely separate from stacking and the yield generated from it. + +The Bitcoin yield that stackers earn is determined by a combination of the Bitcoin being committed by miners and the number of STX tokens that are locked up in the network. + +#### No effect on transaction validation or consensus mechanism + +The other main difference is that staking involves validators, and is tightly coupled with the operations of the network. + +In both protocols stackers/stakers are earning a yield for providing a service. In PoX, that service is providing economic security by signaling what the canonical Stacks chain is (the chain with the most money locked in it is the real chain) and soon will provide the service of serving as signers for [sBTC](https://stacks.co/sbtc). + +In PoS, the service stakers provide is running a validator node, which is in charge of determining what transactions are or are not valid. So in a PoX system with stacking, the yield generation process is not attached to the validity and operations of the network, whereas in PoS it is. + +In PoS, the entities that are locking up their tokens and earning a yield are the same entities that are in charge of determining what does and does not count as a valid transaction. This and the fact that the yield is in the same token that is locked, have large implications on a PoS chain functioning much like equities in that the entities with the largest stake have the most controlling power over the transactional operations of the chain. + +Here's an [article](https://krgrs.dev/why-bitcoin-is-the-biggest-opportunity-for-developers-in-2023#heading-technology-and-economic-incentives) by Stacks Foundation Developer Advocate Kenny Rogers that goes more in-depth into these economic incentives, with additional resources. + +It is important to note that this control does not extend to being able to change the rules of the blockchain itself, but only to vote transactions as valid or invalid. + +In order to incentivize honest behavior on the part of validators, stake can be slashed as a punishment. Since PoX stackers do not have any say in transaction validity, this punishment mechanism does not exist and is not necessary in PoX. + +
+ +Stacking is a built-in action, required by the "proof-of-transfer" (PoX) mechanism. The PoX mechanism is executed by every miner on the Stacks network. + +{% hint style="info" %} +Stacking functionality is implemented as a smart contract, using Clarity. Read more about [the contract](../clarity/example-contracts/stacking.md). +{% endhint %} + +### Stacking flow + +The Stacking mechanism can be presented as a flow of actions: + +
+ +1. Make API calls to get details about the upcoming reward cycle +2. For a specific Stacks account, confirm eligibility +3. Confirm the BTC reward address and the lockup duration +4. The transaction is broadcasted and the STX tokens are locked. This needs to happen before the prepare phase of the next reward cycle, the last 100 Bitcoin blocks of the ongoing reward phase +5. The Stacking mechanism executes reward cycles and sends out rewards to the set BTC reward address +6. During the lockup period, details about unlocking timing, rewards and more can be obtained +7. Once the lockup period is passed, the tokens are released and accessible again +8. Display reward history, including details like earnings for previous reward cycles + +{% hint style="info" %} +Keep in mind that the target duration for a reward cycles is \~2 weeks. This duration is based on the target block time of the network (10 minutes) and can be higher at times due to [confirmation time variances](https://www.blockchain.com/charts/median-confirmation-time) of the bitcoin network. +{% endhint %} + +### Stacking delegation flow + +
+ +The Stacking flow is different for delegation use cases: + +* Before Stacking can be initiated for a token holder, the delegator needs to be granted permission to Stack on behalf of the account owner. The permission is restricted to the maximum amount the delegator is allowed to Stack. The maximum amount is not limited by the available funds and can be set much higher. An account can only be associated with one single delegator +* The account has to define the delegation relationship. They can optionally restrict the Bitcoin reward address that must be used for payouts, and the expiration burn block height for the permission, thus limiting the time a delegator has permission to Stack +* Delegators have to lock Stacks from different accounts ("pooling phase") until they reach the minimum amount of Stacks required to participate in Stacking +* Once a delegator locks enough STX tokens, they can finalize and commit their participation in the next reward cycle +* Certain delegation relationships may allow the STX holder to receive the payout directly from the miner (step 5/6) +* The termination of the delegation relationship can either happen automatically based on set expiration rules or by actively revoking delegation rights + +### Token holder eligibility + +Stacks (STX) token holders don't automatically receive stacking rewards. Instead, they must: + +* Commit to participation before a reward cycle begins +* Commit the minimum amount of STX tokens to secure a reward slot, or pool with others to reach the minimum +* Lock up STX tokens for a specified period +* Provide a supported Bitcoin address to receive rewards (native segwit is not supported) + +The following diagram describes how the minimum STX tokens per slot is determined. More information on [dynamic minimums for stacking](https://stacking.club) is available at stacking.club. + +
+ +Token holders have a variety of providers and tools to support their participation in Stacking. The Stacks website contains a [list of stacking providers and pools](https://stacks.org/stacking#earn). + +### Stacking in the PoX consensus algorithm + +Stacking is a built-in capability of PoX and occurs through a set of actions on the Stacks blockchain. The [full proof-of-transfer implementation details](https://github.com/stacks-network/stacks-blockchain/blob/develop/sip/sip-007-stacking-consensus.md) are in SIP-007. Below is a summary of the most relevant actions of the algorithm. + +
+ +* Stacking happens over reward cycles with a fixed length. In each reward cycle, a set of Bitcoin addresses associated with stacking participants receive BTC rewards +* A reward cycle consists of two phases: prepare and reward +* During the prepare phase, miners decide on an anchor block and a reward set. Mining any descendant forks of the anchor block requires transferring mining funds to the appropriate reward addresses. The reward set is the set of Bitcoin addresses which are eligible to receive funds in the reward cycle +* Miners register as leader candidates for a future election by sending a key transaction that burns cryptocurrency. The transaction also registers the leader's preferred chain tip (must be a descendant of the anchor block) and commitment of funds to 2 addresses from the reward set +* Token holders register for the next rewards cycle by broadcasting a signed message that locks up associated STX tokens for a protocol-specified lockup period, specifies a Bitcoin address to receive the funds, and votes on a Stacks chain tip +* Multiple leaders can commit to the same chain tip. The leader that wins the election and the peers who also burn for that leader collectively share the reward, proportional to how much each one burned +* Token holders' locked up tokens automatically unlock as soon as the lockup period finishes + +### Bitcoin address + +You must provide a BTC address in one of two formats: + +* [Legacy (P2PKH)](https://en.bitcoin.it/wiki/Transaction#Pay-to-PubkeyHash), which starts with `1`. +* [Segregated Witness / Segwit (P2SH)](https://en.bitcoin.it/wiki/Pay\_to\_script\_hash), which starts with `3`. The "Native Segwit" format (which starts with `bc1`), for example, is not supported. + +The Stacking contract needs a special format for the Bitcoin address (the reward address). This is required to ensure that miners are able to correctly construct the Bitcoin transaction containing the reward address. + +The address must be specified in the following format using the Clarity language: + +```clar +;; a tuple of a version and hashbytes buffer +(pox-addr (tuple (version (buff 1)) (hashbytes (buff 20)))) +``` + +The `version` buffer must represent what kind of bitcoin address is being submitted. It can be one of the following: + +```js +SerializeP2PKH = 0x00, // hash160(public-key), same as bitcoin's p2pkh +SerializeP2SH = 0x01, // hash160(multisig-redeem-script), same as bitcoin's multisig p2sh +SerializeP2WPKH = 0x02, // hash160(segwit-program-00(p2pkh)), same as bitcoin's p2sh-p2wpkh +SerializeP2WSH = 0x03, // hash160(segwit-program-00(public-keys)), same as bitcoin's p2sh-p2wsh +``` + +The `hashbytes` are the 20 hash bytes of the bitcoin address. You can obtain that from a bitcoin library, for instance using [`bitcoinjs-lib`](https://github.com/bitcoinjs/bitcoinjs-lib): + +```js +const btc = require("bitcoinjs-lib"); +console.log( + "0x" + + btc.address + .fromBase58Check("1C56LYirKa3PFXFsvhSESgDy2acEHVAEt6") + .hash.toString("hex") +); +``` + +### Choosing the right Stacking strategy + +[Here](https://blog.stacks.co/stacking-strategy) is an interesting article that may help you choose the right Stacking strategy. + +### Where to Stack? + +You can Stack on your own, on a pool or on an exchange: + +#### Stacking on your own + +Stacking on your own is non-custodial. + +Stacking on your own requires a protocol minimum (amount changes but about 100,000 STX). + +[Hiro Wallet](https://www.hiro.so/wallet) allows stacking on your own. + +#### Stacking on a pool + +Stacking on a pool allows Stacking without the requirement of the protocol minimum. + +Some available pools are: + +| Pool | Type | Pays rewards in | Fee | Minimum amount | +| --------------------------------------------------- | ------------- | :-------------: | --- | :------------: | +| [Friedger's Pool](https://pool.friedger.de/) | Non custodial | STX or xBTC | No | 40 STX | +| [Planbetter](https://planbetter.org/) | Non custodial | BTC | 5% | 200 STX | +| [Stacked](https://staking.staked.us/stacks-staking) | Non custodial | BTC | | 100,000 STX | +| [Xverse](https://www.xverse.app/) | Non custodial | BTC | No | 100 STX | + +#### Stacking on an exchange + +Stacking on an exchange is custodial, meaning you are trusting the exchange with your Stacks. + +Several exchanges allow Stacking directly on their sites. Examples are [Okcoin](https://www.okcoin.com) and [Binance](https://www.binance.com/en/staking) + +### Stacking statistics + +You can view all sorts of Stacking data and statistics on [Stacking Club](https://stacking.club) diff --git a/stacks-101/technical-specifications.md b/stacks-101/technical-specifications.md new file mode 100644 index 0000000000..c6d7c22c29 --- /dev/null +++ b/stacks-101/technical-specifications.md @@ -0,0 +1,74 @@ +# Technical Specifications + +### Consensus + +* Proof of Transfer (PoX) as described in [SIP-007](https://github.com/stacksgov/sips/blob/main/sips/sip-007/sip-007-stacking-consensus.md) +* Network will transition to Proof of Burn (PoB) as described in [SIP-001](https://github.com/stacksgov/sips/blob/main/sips/sip-001/sip-001-burn-election.md) after 10 years. [Learn more about Proof-of-Burn in SIP-001](https://github.com/stacksgov/sips/blob/main/sips/sip-001/sip-001-burn-election.md). +* Threat model + * 51% of malicious Stacks mining power can perform a double-spend attack + * 51% of malicious Bitcoin mining power can reorg the Stacks chain +* Different actors and their roles + * Stacks Miners package transactions into blocks, mine them through a Bitcoin transaction, propagate them, and if they win the block race, append microblocks to their winning block until the next block is mined. The next block confirms the microblock stream. + * Stacks Holders may alter the calculation of block limits (subject to a miner veto) and may vote to disable Proof-of-Transfer rewards for a reward cycle. +* Transactions are considered final when the corresponding "block commit" transaction on Bitcoin is finalized. Typically this can by 3-6 confirmations. +* For more details, see Proof of Transfer. + +### Proof of Transfer Mining + +* Coinbase reward schedule: + * 1000 STX/block for first 4 years + * 500 STX/block for following 4 years + * 250 STX/block for subsequent 4 years + * 125 STX/block in perpetuity after that +* Coinbase rewards accumulate for "missed sortitions": If a Bitcoin block has no sortition (at height N), then any Stacks block mined in a subsequent sortition that builds off of any Stacks chain tip that existed at the penultimate sortition (at height N-1) may claim its coinbase. This encourages miners to keep mining even if Bitcoin fees are high. +* Initial mining bonus: This is a special case of the above to incentivize early miners. Coinbase for all burnchain blocks between the first burn block height (to be chosen by independent miners as part of the Stacks 2.0 launch) and the first sortition winner accumulate and are distributed to miners over a fixed window (to be determined). For instance, say burn block height is 10,000 and first sortition is at block 10500 and distribution window is 100 blocks, then coinbase for the first 500 blocks (10,500 - 10,000) will be distributed evenly to miners who win sortition over the subsequent 100 blocks. +* Reward maturity window: 100 blocks, meaning leaders will earn the coinbase reward 100 blocks after the block they successfully mine. +* Block interval: Stacks blockchain produces blocks at the same rate as the underlying burnchain. For Bitcoin, this is approximately every 10 minutes. +* BTC commitment: Miners must commit at least 11,000 satoshis (5,500 sats / [UTXO output](https://learnmeabitcoin.com/technical/utxo)); 2 outputs / block) to avoid "dust." +* For more details, see Mining. + +### Stacking + +* Stacking works in 2 phases + 1. Prepare phase: In this phase an "anchor block" is chosen. The qualifying set of addresses ("reward set") is determined based on the snapshot of the chain at the anchor block. Length of prepare phase is 100 blocks. Stacking commitments need to be confirmed before this phase starts + 2. Reward phase: In this phase miner BTC commitments are distributed amongst the reward set. Reward cycle length is 2000 BTC blocks (\~2 weeks). +* Two reward addresses / block, for a total of 4000 addresses every reward cycle. The addresses are chosen using a VRF (verifiable random function), so each node can deterministically arrive at the same reward addresses for a given block. +* Stacking threshold: 0.025% of the participating amount of STX when participation is between 25% and 100% and when participation is below 25%, the threshold level is always 0.00625 of the liquid supply of STX. +* Delegation: An STX address can designate another address to participate in Stacking on its behalf. [Relevant section in SIP-007](https://github.com/stacksgov/sips/blob/main/sips/sip-007/sip-007-stacking-consensus.md#stacker-delegation). +* Pooling: STX holders that individually do not meet the Stacking threshold can pool together their holdings to participate in Stacking. To do this, STX holders must set the (optional) reward address to the "delegate address." For more details, see [this reference](https://docs.stacks.co/references/stacking-contract#delegate-stx). +* Only two types of BTC reward addresses are supported: [Legacy (P2PKH)](https://en.bitcoin.it/wiki/Transaction#Pay-to-PubkeyHash) or [Segregated Witness / Segwit (P2SH)](https://en.bitcoin.it/wiki/Pay\_to\_script\_hash). Native Segwit is not supported. +* Further reading: Stacking + +### Accounts and Addresses + +* Transactions in the Stacks blockchain originate from, are paid for by, and execute under the authority of accounts +* An account is fully specified by its address + nonce + assets +* Address contains 2 or 3 fields: 1 byte version, 20 byte public key hash (RIPEMD160(SHA256(input))), optional name (variable length, max 128 bytes) +* Two types of accounts: standard accounts are owned by one or more private keys; contract accounts are materialized when a smart-contract is instantiated (specified by the optional name field above) +* Nonce counts number of times an account has authorized a transaction. Starts at 0, valid authorization must include the _next_ nonce value. +* Assets are a map of all asset types -- STX, any on-chain assets specified by a Clarity contract (for example NFTs) -- to quantities owned by that account. +* Accounts need not be explicit "created" or registered; all accounts implicitly exist and are instantiated on first-use. +* Further reading: Accounts + +### Transactions + +* Transaction types: coinbase, token-transfer, contract-deploy, contract-call, poison-microblock. +* Only standard accounts (not contracts) can pay transaction fees. +* Transaction execution is governed by 3 accounts (may or may not be distinct) + 1. _originating account_ is the account that creates, _authorizes_ and sends the transaction + 2. _paying account_ is the account that is billed by the leader for the cost of validating and executing the transaction + 3. _sending account_ is the account that identifies who is currently executing the transaction: this can change as a transaction executes via the `as-contract` Clarity function +* Transactions can be batched or streamed into blocks. The behavior can be controlled by the anchor mode of a transaction. With streaming (microblocks), a faster confirmation time is possible. +* Two types of authorizations: standard authorization is where originating account is the same as paying account. _Sponsored_ authorization is where originating account and paying account are distinct. For instance, developers or service providers could pay for users to call their smart-contracts. +* For sponsored authorization, first a user signs with the originating account and then a sponsor signs with the paying account. +* Mempool limit for concurrent pending transactions is 25 per account +* Pending mempool transactions will be garbage-collected [256 blocks after receipt](https://github.com/stacks-network/stacks-blockchain/blob/master/src/core/mempool.rs#L62). With 10 minutes target block time, this would equal \~42 hours +* [Learn more about transaction encoding in SIP-005](https://github.com/stacksgov/sips/blob/main/sips/sip-005/sip-005-blocks-and-transactions.md#transaction-encoding) +* [Transaction signing and verification are described in SIP-005](https://github.com/stacksgov/sips/blob/main/sips/sip-005/sip-005-blocks-and-transactions.md#transaction-signing-and-verifying) +* All transactions impacting account balance are atomic, a transfer operation can not increment one account’s balance without decrementing another’s. However, transactions that perform multiple account actions (for example, transferring from multiple accounts) may partially complete. +* Transactions can include a memo string (max 34 bytes) + +### See also + +* [Read the full Stacks 2.0: Apps and Smart Contracts for Bitcoin whitepaper](https://cloudflare-ipfs.com/ipfs/QmaGgiVHymeDjAc3aF1AwyhiFFwN97pme5m536cHT4FsAW). +* [Watch Aaron Blankstein video on Stacks whitepaper v2](https://www.youtube.com/watch?v=Wd-Bfe8Sn-Y). diff --git a/stacks-101/testnet.md b/stacks-101/testnet.md new file mode 100644 index 0000000000..01fbc479f4 --- /dev/null +++ b/stacks-101/testnet.md @@ -0,0 +1,37 @@ +# Testnet + +### About testnet + +The testnet is a separate blockchain from the Stacks mainnet analogous to a staging environnement. It's a network used by developers to test their apps, smart contracts, or changes to the protocol in a production-like environment. + +It produces blocks at roughly the same rate as mainnet; about 1 block every 10 minutes on average. The Stacks testnet is rarely reset. + +#### Faucets + +Testnet faucets provide you with free Stacks Token (STX) to test with. These are not the same as STX on mainnet and have no value. There are a couple of different options for getting testnet STX. + +**Hiro** + +You can get STX from the Hiro faucet on the [Hiro Explorer Sandbox](https://explorer.hiro.so/sandbox/faucet?chain=testnet), or using the [API](https://docs.hiro.so/api#tag/Faucets). + +To get STX tokens from within the Explorer Sandbox, navigate to the "Faucet" tab on the left and click "Request STX" button. + +You can also try out Stacking by clicking on `I want to stack`. + +{% hint style="info" %} +The Explorer Sandbox requires you to login with a Stacks wallet +{% endhint %} + +**LearnWeb3** + +Alternatively, you can use the [LearnWeb3 faucet](https://learnweb3.io/faucets). + +
+ +#### Testnet API + +The hosted Stacks Blockchain API for the testnet is available at this base URL: + +```shell +https://stacks-node-api.testnet.stacks.co/ +``` diff --git a/stacks-101/transactions.md b/stacks-101/transactions.md new file mode 100644 index 0000000000..e539ff2f62 --- /dev/null +++ b/stacks-101/transactions.md @@ -0,0 +1,40 @@ +# Transactions + +### Introduction + +Transactions are the fundamental unit of execution in the Stacks blockchain. Each transaction is originated from a Stacks 2.0 account, and is retained in the Stacks blockchain history for eternity. This guide helps you understand Stacks 2.0 transactions. + +### Lifecycle + +Transactions go through phases before being finally confirmed, and available for all, on the Stacks 2.0 network. + +
+ +* **Generate**: Transactions are assembled according to the encoding specification. +* **Validate and sign**: Transactions are validated to confirm they are well-formed. Required signatures are filled in. +* **Broadcast**: Transactions are sent to a node. +* **Register**: A miner receives transactions, verifies, and adds them to the ["mempool,"](https://academy.binance.com/en/glossary/mempool) a holding area for all the pending transactions. +* **Process**: Miners review the mempool and select transactions for the next block to be mined. Depending on the transaction type, different actions can happen during this step. For example, post-conditions could be verified for a token transfer, smart-contract defined tokens could be minted, or an attempt to call an existing smart contract method could be made. +* **Confirm**: Miners successfully mine blocks with a set of transactions. The transactions inside are successfully propagated to the network. + +{% hint style="info" %} +A transaction can have one of three states once it is registered: `pending`, `success`, or `failed`. +{% endhint %} + +### Types + +The Stacks 2.0 supports a set of different transaction types: + +| **Type** | **Value** | **Description** | +| ----------------- | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Coinbase | `coinbase` | The first transaction in a new block (an entity holding several transactions). Used to register for block rewards. These are not manually generated and broadcasted like other types of transactions. | +| Token transfer | `token_transfer` | Asset transfer from a sender to a recipient | +| Contract deploy | `smart_contract` | Contract instantiation | +| Contract call | `contract_call` | Contract call for a public, non read-only function | +| Poison Microblock | `poison_microblock` | Punish leaders who intentionally equivocate about the microblocks they package | + +A sample of each transaction type can be found in the [Stacks Blockchain API response definition for transactions](https://docs.hiro.so/api#operation/get\_transaction\_by\_id). + +{% hint style="info" %} +Read-only contract call calls do **not** require transactions. Read more about it in the network guide. +{% endhint %} diff --git a/stacks-101/what-is-stacks.md b/stacks-101/what-is-stacks.md new file mode 100644 index 0000000000..7e23134015 --- /dev/null +++ b/stacks-101/what-is-stacks.md @@ -0,0 +1,85 @@ +# What Is Stacks? + +## What is Stacks? + +We can get an idea of the goal and ethos behind Stacks by looking at [how Satoshi envisioned generalizing Bitcoin](https://satoshi.nakamotoinstitute.org/posts/bitcointalk/threads/244/#222) back in 2010: + +> "...to be a completely separate network and separate block chain, yet share CPU power with Bitcoin...all networks in the world would share combined CPU power, increasing the total strength." + +This is major theme in the design decisions for Stacks. A bit of a contradiction in the Bitcoin world, the Stacks network is a Bitcoin L2, but it does have its own token. + +This is an intentional and critical design decision primarily for the purpose of maintaining decentralization, rather than needing to rely on a federation. + +If that's confusing or you are skeptical, that's understandable, we'll be diving deeper into these ideas as we go through Stacks Academy. + +### Stacks and the Purpose of Blockchain Technology + +When evaluating new blockchain technologies, it's important to keep the original intent and purpose of them intact. If we go back to Bitcoin, it was originally designed to be: + +* Decentralized +* Immutable +* Secure + +You've likely heard of the blockchain trilemma, the problem of trying to balance the decentralization, scalability, and security of a blockchain network. + +Stacks takes the approach of solving this trilemma by separating out chains into layers. + +So at the bottom, you have the foundational layer: Bitcoin. + +Bitcoin is the most decentralized, most secure, and most immutable blockchain network. However, that comes with a few tradeoffs. + +Bitcoin is very slow compared to other networks. Bitcoin only has a new block written once every 10 minutes or so, making its throughput negligible compared to networks designed for speed like Solana. + +Bitcoin is also "boring". Ethereum came along after Bitcoin and sought to do the same thing for software that Bitcoin did for money. Ethereum's goal is to be a decentralized supercomputer of sorts, serving as a global compute environment for smart contracts (code that is written to a blockchain). + +Bitcoin is also not scalable. Because every new block must propagate to every node on the network, Bitcoin can only run as fast as the slowest node in the network. Now we are seeing the rise of modular blockchain networks like Cosmos that are designed to make it easy for people to spin up their own blockchain networks. + +While most new blockchain protocols popping up these days see these properties as negatives and seek to eliminate them, the Stacks community sees things differently. + +### The Stacks Way + +Stacks takes a pyramid approach, where you have the foundational settlement layer at the bottom (Bitcoin), and then a layer on top of that to add smart contracts and programmability (Stacks), and then layers on top of that for scalability and speed (Hiro's Subnets). + +Additionally, there are other layers on top of Bitcoin that serve different purpose and have their own mechanisms and philosophies like Lightning, RSK, and Liquid. + +By taking this layered approach, we are able to have all of the same functionality as chains like Ethereum without all of the drawbacks of having a massively complex chain. + +So Stacks is a Bitcoin layer 2 with some unique properties, like having its own token, that acts as an incentive mechanism to maintain a historical ledger of all of its transactions and operate with its own security budget. + +The only alternative to this model is a federated model like what Liquid uses. Stacks' design decisions operate under the idea that decentralization is more important than rejecting altcoins. + +This is one of the things that separates Stacks from other Bitcoin layers like Lightning. + +Lightning doesn't add any additional functionality to Bitcoin, it simply helps to scale functionality Bitcoin already has and help it operate faster. Lightning is also ephemeral, it has no permanent state that it keeps track of, and so is unsuitable for things like smart contracts that need to keep track of data and maintain state. + +Contrast this to Stacks, which adds additional functionality to Bitcoin but still ultimately settles down to Bitcoin (we'll cover this next in the section on Proof of Transfer). + +This is also what separates Stacks from L2 scaling solutions on Ethereum like Polygon or Arbitrum. The benefit we get here is that we can maintain a separation of concerns and keep Bitcoin simple and sturdy, chugging along producing blocks, and add additional layers for functionality and speed. But if those other layers were to be compromised, that would not affect the foundational layer at all. + +This is an important property when we are talking about building blockchains that are designed to be a global decentralized money (Bitcoin) and a decentralized economy built on top of that money (Stacks). + +_Sounds like a sidechain_. + +Not quite. We'll get into the technical details of how this works in the next section, but because Stacks records its entire history to Bitcoin, it is at least as hard to re-org the Stacks chain as it is Bitcoin. This is a key differentiator from side chains, which do not have their history recorded to their primary chain. + +Second, Stacks has its own token, it does not represent pegged Bitcoin. While this may ruffle some feathers among some people in the Bitcoin community, it has several advantages. + +First, rather than the security of the Stacks chain relying exclusively on system-specific measures the sidechain has implemented to incentivize validators to produce blocks honestly. + +What does that actually mean? By implementing a token into the Stacks chain, we provide additional economic incentive for miners to produce Stacks blocks honestly. + +This token provides additional incentive in the form of serving as a way to grow the chain. Rather than relying on altruism in order to produce blocks and grow the chain, we can incentivize builders, token-holders, and investors all at the same time by having a token. + +The ICO scams of 2017 put a bad taste in a lot of peoples' mouths, which has justifiably made a lot of people skeptical of every new blockchain project that pops up with a new token. + +But the problem with all of these projects is that they had no actual value, they weren't anchored to anything else of value and provided no real utility. + +With a project like Stacks, we have real utility in the sense of serving as a way to utilize Bitcoin and make it a productive asset **in a decentralized way.** This is a key point, Currently the only way to make Bitcoin productive (meaning use it to make more money) is by giving it to a custodial service or transferring it off the Bitcoin chain by way of something like wBTC on Ethereum. + +Stacks allows us to do this while ultimately still settling to the Bitcoin chain. + +In addition, Stacks allows us to build decentralized and censorship-resistant software utilizing Bitcoin as the foundational settlement layer. Eventually, the goal is to build a network of financial systems and decentralized software products that all utilize Bitcoin as their money. + +A caveat here, a lot of the functionality we've talked about here is still in the early stages, and the highest priority for the ecosystem at the moment is more tightly integrating Stacks with Bitcoin in order to easily use it with Stacks dapps. + +With that context, let's dive into some of the technical details of how Stacks operates, starting with Proof of Transfer. diff --git a/stacks-101/whitepapers.md b/stacks-101/whitepapers.md new file mode 100644 index 0000000000..6d6e693393 --- /dev/null +++ b/stacks-101/whitepapers.md @@ -0,0 +1,8 @@ +# Whitepapers + +In addition to the documentation here, there are numerous whitepapers detailing how the different pieces of the Stacks ecosystem work. + +* [Stacks 2.0](https://gaia.blockstack.org/hub/1AxyPunHHAHiEffXWESKfbvmBpGQv138Fp/stacks.pdf) +* [Stacks Nakamoto Release](https://assets.stacks.co/stacks.pdf) +* [Proof of Transfer](https://assets.website-files.com/5fcf9ac604d37418aa70a5ab/60072dbb32d416d6b3806935\_5f1596b12bcc0800f3dcadcd\_pox.pdf) +* [sBTC](https://assets.stacks.co/sbtc.pdf) diff --git a/stacks-in-depth/gaia/README.md b/stacks-in-depth/gaia/README.md new file mode 100644 index 0000000000..a16c75d107 --- /dev/null +++ b/stacks-in-depth/gaia/README.md @@ -0,0 +1,2 @@ +# Gaia + diff --git a/stacks-in-depth/gaia/amazon-ec2.md b/stacks-in-depth/gaia/amazon-ec2.md new file mode 100644 index 0000000000..a11f0b0620 --- /dev/null +++ b/stacks-in-depth/gaia/amazon-ec2.md @@ -0,0 +1,88 @@ +# Amazon EC2 + +### Introduction + +The template provided on this page provides an easy way to deploy a Gaia hub directly to Amazon EC2. You can use this template to deploy your own Gaia hub to your Amazon Web Services (AWS) account. Amazon EC2 is an affordable and convenient cloud computing provider. The template provides a one-click deploy for Amazon EC2 with either S3 or EBS as a storage provider. + +### Prerequisites + +This procedure uses Amazon CloudFormation to configure an EC2 cloud compute provider to run the Gaia hub service with an S3 or EBS provider for file storage. You should have access to an AWS account either through your personal account or through a corporate account. This account should have permissions to create resources. + +Additionally, you must also own a domain name and be able to update the DNS records associated with that domain name. + +### Setup steps + +#### Step 1 - Create Stack + +Use a link in the table to launch the [CloudFormation](https://console.aws.amazon.com/cloudformation/) template in the AWS region that you wish to deploy a Gaia hub. + +#### Step 2 - Setup stack using template + +You need to configure the template with the appropriate values for your hub and domain it runs on. + +Select `Template is ready` and `Amazon S3 URL` and enter the following Amazon S3 URL: + +``` +https://s3-external-1.amazonaws.com/cf-templates-vzldibfi2mw8-us-east-1/2022160J6G-cloudformation.yaml +``` + +If you prefer you can instead select `Template is ready` and `Upload a template file` to upload the template file `cloudformation.yaml`. + +The latest `cloudformation.yaml` file can be downloaded [here](https://raw.githubusercontent.com/stacks-network/gaia/master/deploy/cloudformation.yaml). On most browsers you can right-click on that page and click on `Save page as` to download the file. Alternatively, you can copy/paste the text into a text file called `cloudformation.yaml`. + +Then click `Next`. + +#### Step 3 - Specify stack details + +Specify the stack details and then click `Next`: + +| Field | Value | Notes | +| --------------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Stack name | _a unique name in your AWS account_ | e.g.: my\_gaia\_hub | +| DomainName | _your-domain_ | | +| EmailAddress | _your email address_ | | +| GaiaBucketName | _S3 bucket name_ | The template combines this name with the stack name to create a unique S3 bucket. The template ignores this field if GaiaStorageType is set to `disk`. | +| GaiaStorageType | `s3` or `disk` | Select the GaiaStorageType of which to use as a backend for the Gaia Hub. Selecting `s3` causes the template to create an S3 bucket based on the name given in the previous field. Selecting `disk` causes the template to attach a separate EBS volume to the EC2 instance for Hub storage. | +| InstanceType | t2.micro | Select the instance type you want. Default value is `t2.micro`. | +| KeyName | | In the KeyName drop-down, select an [EC2 KeyPair](https://console.aws.amazon.com/ec2/v2/home?region=us-east-1#KeyPairs:) to enable SSH access to the EC2 instance. You should download the `.pem` keyfile for this pair from the EC2 console. For more information see the [EC2 key pair documentation](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-key-pairs.html#prepare-key-pair) | +| SSHLocation | 0.0.0.0/0 | Leave the SSHLocation field with the default value of `0.0.0.0/0` to enable SSH access from any IP address. If you wish to restrict SSH access to the EC2 instance to a certain IP, you can update this field. | +| SubnetId | _subnetid_ | Select a public subnet | +| VpcId | _vpcid_ | | + +#### Step 4 - Configure stack options + +Configure any stack options that fit your desired setup and click `Next`. All these fields are optional. + +#### Step 5 - Review + +Review the configuration of your Gaia hub, select the checkbox to acknowledge that AWS may create IAM resources with custom names and click on `Create stack`. + +#### Step 6 - Retrieve the public IP of your Gaia hub + +Your stack can take several minutes to launch. You can monitor the Events tab of your hub to review the current progress of the launch. When the launch is complete, the Outputs tab displays information about the hub. Select the PublicIP and copy it to configure your domain name. + +Create an `A` DNS record pointing to the given IP in your domain. + +Wait a few minutes for the DNS record to propagate, and your should be able to access your Gaia hub with SSH. + +### Accessing your Gaia hub with SSH + +To SSH into your Gaia hub EC2 host directly, you must have the keyfile used in container creation. Access the host with the following command in your terminal: + +```bash +ssh -i admin@ +``` + +:::tip If you can only access SSH with the IP but not with the DNS name and you wish to do so, you can optionally activate it by following these steps: + +``` +Open your [AWS Console](https://console.aws.amazon.com) +Click on `Service` -> VPC +Open Your VPCs +Select your VPC connected to your Gaia Hub +Click `Actions` -> `Edit DNS Hostnames` -> Change `DNS hostnames` to `Enable` +``` + +::: + +### Graphical representation of the cloudformation template diff --git a/stacks-in-depth/gaia/configuration.md b/stacks-in-depth/gaia/configuration.md new file mode 100644 index 0000000000..8d45d6939d --- /dev/null +++ b/stacks-in-depth/gaia/configuration.md @@ -0,0 +1,69 @@ +# Configuration + +### Configuration files + +The following configuration files exist in the folder `deploy/configs/gaia/`: + +``` +admin-config.json +hub-config.json +reader-config.json +``` + +### GAIA Admin Service + +You can also use the GAIA Admin Service to remotely administer it with an API Key. It will require you to install `npm` with a `apt install npm`. Once `npm` is installed you can continue with the steps in the [GAIA Admin Service](https://github.com/stacks-network/gaia/blob/master/admin/README.md). + +### Driver Selection + +The Gaia hub currently supports the following drivers: + +``` +'aws' == Amazon S3 +'azure' == Azure Blob Storage +'disk' == Local disk +'google-cloud' === Google Cloud Storage +``` + +Set the driver you wish to use in your [config.json](https://github.com/stacks-network/gaia/blob/master/hub/config.sample.json) file with the `driver` parameter. Many drivers additionally accept the `bucket` parameter, which controls the bucket name that files should be written to. + +These driver may require you to provide additional credentials for performing writes to the backends. See `config.sample.json` for fields for those credentials. In some cases, the driver can use a system configured credential for the backend (e.g., if you are logged into your AWS CLI account, and run the hub from that environment, it won't need to read credentials from your `config.json`). + +:::caution The disk driver requires a \*nix like filesystem interface, and will not work correctly when trying to run in a Windows environment. ::: + +### Note on SSL + +We _strongly_ recommend that you deploy your Gaia hub with SSL enabled. Otherwise, the tokens used to authenticate with the Gaia hub may be stolen by attackers, which could allow them to execute writes on your behalf.\ +Configuration options are available to run the hub with an `https` Node.js server.\ +Otherwise, a reverse proxy web server such as nginx or Apache can be used. + +### Require Correct Hub URL + +If you turn on the `requireCorrectHubUrl` option in your `config.json` file, your Gaia hub will require that authentication requests correctly include the `hubURL` they are trying to connect with. This is used to prevent a malicious gaia hub from using an authentication token for itself on other Gaia hubs. + +By default, the Gaia hub will validate that the supplied URL matches `https://${config.serverName}`, but if there are multiple valid URLs for clients to reach the hub at, you can include a list in your `config.json`: + +```javascript +{ + .... + serverName: "normalserver.com" + validHubUrls: [ "https://specialserver.com/", + "https://legacyurl.info" ] + .... +} +``` + +### The readURL parameter + +By default, the gaia hub drivers will return read URLs which point directly at the written content. For example, an S3 driver would return the URL directly to the S3 file. However, if you configure a CDN or domain to point at that same bucket, you can use the `readURL` parameter to tell the gaia hub that files can be read from the given URL. For example, the `hub.blockstack.org` Gaia Hub is configured to return a read URL that looks like `https://gaia.blockstack.org/hub/`. + +Unset this configuration parameter if you do not intend to deploy any caching. + +### Minimum Proofs Requirement + +The gaia hub can also be configured to require a minimum number of social proofs in a user's profile to accept writes from that user. This can be used as a kind of spam-control mechanism. However, we recommend for the smoothest operation of your gaia hub, to set the `proofsConfig.proofsRequired` configuration key to `0`. + +### CDN & Replicated Hubs + +* https://docs.microsoft.com/en-us/azure/storage/blobs/storage-https-custom-domain-cdn +* The hub implementation is designed to be ran from a single Node.js instance. If the hub instance is sharded (e.g. replicated hubs via load balancing), then any given `bucket` (identified by URI segment) must be served by the same instance, At least a couple elements of the Gaia Hub depend on this: token invalidation in-memory caching, and resource endpoint 409 contention behavior. diff --git a/stacks-in-depth/gaia/deploy-gaia-hub.md b/stacks-in-depth/gaia/deploy-gaia-hub.md new file mode 100644 index 0000000000..ac62b48ccd --- /dev/null +++ b/stacks-in-depth/gaia/deploy-gaia-hub.md @@ -0,0 +1,14 @@ +# Deploy Gaia Hub + +### Deploy your GAIA hub + +Tutorials to deploy your own GAIA hub are currently available for Linux, MacOS and Amazon EC2 / CloudFormation. + +Regardless of what server the Gaia Hub runs on, the storage it uses can currently be deployed on multiple platforms: + +* Local disk (the server itself) +* Amazon S3 +* Azure Blob Storage +* Google Cloud + +:::tip Support for IPFS is under development. ::: diff --git a/stacks-in-depth/gaia/linux.md b/stacks-in-depth/gaia/linux.md new file mode 100644 index 0000000000..ebfe40df8f --- /dev/null +++ b/stacks-in-depth/gaia/linux.md @@ -0,0 +1,121 @@ +# Linux + +This configuration will setup the following 4 docker containers: + +* Nginx with certbot on TCP ports 80 and 443. +* Gaia hub on TCP port 3000. +* Gaia admin on TCP port 8009. +* Gaia reader on TCP port 8008 + +**1. Update the system and install the dependencies and software we will use to test:** + +```bash +apt update && apt upgrade -y && apt install -y git vim gnupg jq +``` + +**2. Install** [**docker**](https://docs.docker.com/engine/install/debian/) **and** [**docker-compose**](https://docs.docker.com/compose/cli-command/#install-on-linux) in your OS. For our example we install _docker_ with: + +```bash +curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg + +echo \ + "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian \ + $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null + +apt update && apt install -y docker-ce docker-ce-cli containerd.io +``` + +Install _docker-compose_ by downloading the [latest-release](https://github.com/docker/compose/releases): + +```bash +VERSION_DC=$(curl --silent https://api.github.com/repos/docker/compose/releases/latest | jq .name -r) +DESTINATION_DC=~/.docker/cli-plugins +mkdir -p ${DESTINATION_DC} +curl -SL https://github.com/docker/compose/releases/download/${VERSION_DC}/docker-compose-linux-x86_64 -o ${DESTINATION_DC}/docker-compose +chmod +x ${DESTINATION_DC}/docker-compose +``` + +**3. Clone the GAIA repository** and enter it's docker directory. + +```bash +git clone https://github.com/stacks-network/gaia.git && cd gaia/deploy/docker +``` + +**4. Copy and edit appropriate .env file**. In the folder `./deploy/docker/` they are different sample files for different configurations like using aws, azure or disk among others. In this example we will store the data locally so we will copy the _disk_ file and update the domain and email fields. Please change `gaia.site.com` and `gaiarocks@mydomain.com` accordingly. Note you need both for the SSL certificate to be created correctly. + +```bash +export MYGAIADOMAIN=gaia.site.com +export MYGAIAEMAIL=gaiarocks@mydomain.com +cp sample-disk.env disk.env +sed -i 's/my-domain.com/'"$MYGAIADOMAIN"'/g' disk.env +sed -i 's/my-email@example.com/'"$MYGAIAEMAIL"'/g' disk.env + +``` + +**5. Start GAIA HUB service** + +To start GAIA HUB + +```bash +./gaiahub.sh start +``` + +To stop GAIA HUB + +```bash +./gaiahub.sh stop +``` + +To view GAIA HUB status + +```bash +./gaiahub.sh status +``` + +**6. Verify server works locally** with the following command: + +```bash +curl -sk http://localhost/hub_info | jq +``` + +A correct result should look similar to this: + +```bash + { + "challenge_text": "[\"gaiahub\",\"0\",\"gaia-0\",\"blockstack_storage_please_sign\"]", + "latest_auth_version": "v1", + "max_file_upload_size_megabytes": 20, + "read_url_prefix": "https://gaia.site.com/reader/" + } +``` + +**7. Test your GAIA HUB** Running `gaia_test.js` will test your GAIA Hub, by trying to connect to it, uploading a file and downloading it again. + +First install all required dependencies with: + +```bash +npm install +``` + +Then, from the root folder of the project type: + +```bash +node ./deploy/gaia_test.js https://yourgaiaurl +``` + +A correct result will be something like this: + +``` +Will run a test for the GAIA HUB: https://gaia.mydomain.com +Generating some test keys... +Private key: 5aacc60fc2a429e1f02be139f3cac82061c6a980******************** +Public key: 025691f17f2ab80dc4af363bb9c7aac59e9e1db6ae8ff668202582a3f4ec9678ff +Address: 15n8Xo8acRvSZghJG2dxJ8dCdzDMYicUuS +[DEBUG] connectToGaiaHub: https://gaia.mydomain.com/hub_info +[DEBUG] uploadToGaiaHub: uploading testing.txt to https://gaia.mydomain.com +File uploaded successfully. +Upload to gaia hub thinks it can read it from: https://gaia.mydomain.com/reader/15n8Xo8acRvSZghJG2dxJ8dCdzDMYicUuS/testing.txt +Hub info thinks it can read it from : https://gaia.mydomain.com/reader/15n8Xo8acRvSZghJG2dxJ8dCdzDMYicUuS/testing.txt +Let's now try to fetch the uploaded file... +File fetched successfully. Contents of file: GAIA ROCKS! +``` diff --git a/stacks-in-depth/gaia/mac-os.md b/stacks-in-depth/gaia/mac-os.md new file mode 100644 index 0000000000..00be60dccc --- /dev/null +++ b/stacks-in-depth/gaia/mac-os.md @@ -0,0 +1,76 @@ +# Mac OS + +The following assumes you have [Docker Installed](https://docs.docker.com/docker-for-mac/install/) + +* Recommended to also have [MacOS Homebrew](https://docs.brew.sh/Installation) installed +* Use Homebrew to install jq with `brew install jq` + +In your working directory: + +**1. Clone a copy of the** [**gaia repo**](https://github.com/stacks-network/gaia)**:** + +```bash +$ git clone -b master --single-branch https://github.com/stacks-network/gaia +``` + +**2. Change your cwd:** + +```bash +$ cd gaia/deploy/docker +``` + +**3. Create a copy of sample-disk.env and fill out the values:** + +``` +$ cp sample-disk.env disk.env +# Update the DOMAIN_NAME variable to `localhost` +``` + +**4. Start the server:** + +``` +$ docker-compose -f docker-compose-base.yaml -f docker-compose-disk.yaml --env-file disk.env up +``` + +**5. Verify the server is responding locally:** + +``` +$ curl -sk https://localhost/hub_info | jq + { + "challenge_text": "[\"gaiahub\",\"0\",\"gaia-0\",\"blockstack_storage_please_sign\"]", + "latest_auth_version": "v1", + "max_file_upload_size_megabytes": 20, + "read_url_prefix": "https://localhost/reader/" + } +``` + +#### Modifying the configuration for your gaia-hub + +Two methods exist: + +1. Edit the `gaia/deploy/configs/gaia/hub-config.json` using `vim` or other + +* requires a restart of the containers: `docker-compose -f docker-compose-base.yaml -f docker-compose-disk.yaml --env-file disk.env restart` + +2. Use the running `admin` container to modify any config values, and also reload the hub when complete: + +* [GitHub - Gaia Admin README.md](https://github.com/stacks-network/gaia/blob/master/admin/README.md) + +``` +$ export API_KEY="hello" +$ curl -s \ + -H "Authorization: bearer $API_KEY" \ + -H 'Content-Type: application/json' \ + -X POST \ + --data-raw '{"serverName": "myserver"}' \ + http://localhost:8009/v1/admin/config + +$ curl -s \ + -H "Authorization: bearer $API_KEY" \ + -X POST \ + http://localhost:8009/v1/admin/reload + +$ curl -s \ + -H "Authorization: bearer $API_KEY" \ + http://localhost:8009/v1/admin/config | jq +``` diff --git a/stacks-in-depth/nodes-and-miners/README.md b/stacks-in-depth/nodes-and-miners/README.md new file mode 100644 index 0000000000..0d2ffe3cb6 --- /dev/null +++ b/stacks-in-depth/nodes-and-miners/README.md @@ -0,0 +1,2 @@ +# Nodes and Miners + diff --git a/stacks-in-depth/nodes-and-miners/mine-mainnet-stacks-tokens.md b/stacks-in-depth/nodes-and-miners/mine-mainnet-stacks-tokens.md new file mode 100644 index 0000000000..882c225734 --- /dev/null +++ b/stacks-in-depth/nodes-and-miners/mine-mainnet-stacks-tokens.md @@ -0,0 +1,250 @@ +# Mine Mainnet Stacks Tokens + +### Introduction + +For more on the technical details of mining, please review the mining guide + +The following is an abridged version of the [walkthrough here](https://github.com/stacksfoundation/miner-docs), written for a Linux system. If you're on Windows or MacOS, there will be some slight modifications needed (PR's welcome!). + +If you're interested in mining on the Stacks testnet, you can find instructions on how to do that here: + +### Running a Bitcoin Mainnet Full Node + +To participate as a miner on mainnet, you must have access to a mainnet bitcoin node with a wallet (and the wallet's private key). One way to accomplish this is to run bitcoin locally. + +* [Ensure your computer meets the minimum hardware requirements before continuing.](https://bitcoin.org/en/bitcoin-core/features/requirements#system-requirements) + +First, download a [bitcoin binary](https://bitcoin.org/en/download), or [build from source](https://github.com/stacksfoundation/miner-docs/blob/main/bitcoin.md#source-install) If you want to learn more about the technical details of mining, please review the mining guide: + +:::tip It is recommened to use a persistent location for the chainstate, in the steps below we're using `/bitcoin` ::: + +#### Update the Bitcoin Configuration File + +Next, update the bitcoin configuration: + +* **optional but recommended:** Use a persistent directory to store the Bitcoin chainstate, i.e. `datadir=/bitcoin` +* **optional but recommended:** Update the `rpcallowip` value to only allow `127.0.0.1`, or the stacks miner IPv4 +* Modify the `rpcuser` and `rpcpassword` values from the defaults below +* Store the following configuration somewhere on your filesystem (ex: `$HOME/bitcoin.conf`) + +```toml +server=1 +disablewallet=0 +datadir=/bitcoin +rpcuser=btcuser +rpcpassword=btcpassword +rpcallowip=0.0.0.0/0 +bind=0.0.0.0:8333 +rpcbind=0.0.0.0:8332 +dbcache=512 +banscore=1 +rpcthreads=256 +rpcworkqueue=256 +rpctimeout=100 +txindex=1 +``` + +#### Start Bitcoin + +Finally, start bitcoin as follows (adjust the `conf` path to where it was created in the previous step, i.e. `$HOME/bitcoin.conf`): + +```bash +bitcoind -conf=$HOME/bitcoin.conf +``` + +:::note It will take a few days for the node to synchronize with Bitcoin mainnet. ::: + +While it's syncing, you can track the progress with `bitcoin-cli` or the logfile (will be located where the chainstate is stored, i.e. `/bitcoin/debug.log`): + +```bash +$ bitcoin-cli \ + -rpcconnect=localhost \ + -rpcport=8332 \ + -rpcuser=btcuser \ + -rpcpassword=btcpassword \ +getblockchaininfo | jq .blocks +773281 +``` + +*** + +### Running a Stacks Blockchain miner + +First, download a [stacks blockchain binary](https://github.com/stacks-network/stacks-blockchain/releases/latest), or [build from source](https://github.com/stacksfoundation/miner-docs/blob/main/stacks-blockchain.md#build-and-install-stacks-blockchain-from-source) + +_There may be some extra requirements to building,_ [_defined here_](https://github.com/stacksfoundation/miner-docs/blob/main/prerequisites.md#install-required-packages) + +:::tip It is recommened to use a persistent location for the chainstate, in the steps below we're using `/stacks-blockchain` ::: + +#### Generate a keychain + +First, a keychain needs to be generated. With this keychain, we'll purchase some BTC from a cryptocurrency exchange, and then use that BTC to start mining. + +To create a keychain, the simplest way is to use the [stacks-cli](https://docs.hiro.so/references/stacks-cli) with the `make_keychain` command. + +```bash +npx @stacks/cli make_keychain 2>/dev/null | jq -r +``` + +After this runs, you should see some JSON printed to the screen that looks like this: + +```json +{ + "mnemonic": "exhaust spin topic distance hole december impulse gate century absent breeze ostrich armed clerk oak peace want scrap auction sniff cradle siren blur blur", + "keyInfo": { + "privateKey": "2033269b55026ff2eddaf06d2e56938f7fd8e9d697af8fe0f857bb5962894d5801", + "address": "STTX57EGWW058FZ6WG3WS2YRBQ8HDFGBKEFBNXTF", + "btcAddress": "mkRYR7KkPB1wjxNjVz3HByqAvVz8c4B6ND", + "index": 0 + } +} +``` + +:::warning **Do not lose this information** - we'll need to use the `privateKey` and `btcAddress` fields in later steps. ::: + +The above `btcAddress` (mkRYR7KkPB1wjxNjVz3HByqAvVz8c4B6ND) will then need to be imported into the bitcoin mainnet network. :::note Be sure to replace `` with the bitcoin address in the "Generate a keychain" step ::: + +```bash +bitcoin-cli \ + -rpcport=8332 \ + -rpcuser=btcuser \ + -rpcpassword=btcpassword \ +importaddress +``` + +Once imported, we need to get some BTC to that address. You should be able to transfer BTC to this address using a crytpocurrency exchange such as [Coinbase](https://www.coinbase.com), [Binance](https://www.binance.com), or [Kraken](https://www.kraken.com). + +#### Update the Stacks Blockchain Configuration File + +Now, we need to configure our node to use this Bitcoin keychain. Copy the [sample mainnet miner config](https://raw.githubusercontent.com/stacks-network/stacks-blockchain/master/testnet/stacks-node/conf/mainnet-miner-conf.toml) to your local machine in a _memorable_ location like `$HOME/mainnet-miner-conf.toml`. + +Now, grab your `privateKey` from earlier when you ran the `make_keychain` command. Replace the `seed` and `local_peer_seed` field with your private key. Save and close this configuration file. + +Next, update the bitcoin configuration: + +* **optional but recommended:** Use a persistent directory to store the Stacks chainstate, i.e. `working_dir = "/stacks-blockchain"` +* From the `make_keychain` step, modify the `seed` and `local_peer_seed` values with `privatekey` +* Store the following configuration somewhere on your filesystem (ex: `$HOME/mainnet-miner-conf.toml`) + +```toml +[node] +working_dir = "/stacks-blockchain" +rpc_bind = "0.0.0.0:20443" +p2p_bind = "0.0.0.0:20444" +seed = "" +local_peer_seed = "" +miner = true +bootstrap_node = "02da7a464ac770ae8337a343670778b93410f2f3fef6bea98dd1c3e9224459d36b@seed-0.mainnet.stacks.co:20444,02afeae522aab5f8c99a00ddf75fbcb4a641e052dd48836408d9cf437344b63516@seed-1.mainnet.stacks.co:20444,03652212ea76be0ed4cd83a25c06e57819993029a7b9999f7d63c36340b34a4e62@seed-2.mainnet.stacks.co:20444" + +[burnchain] +chain = "bitcoin" +mode = "mainnet" +peer_host = "127.0.0.1" +username = "" +password = "" +rpc_port = 8332 +peer_port = 8333 +satoshis_per_byte = 100 +burn_fee_cap = 20000 +``` + +#### Start the Stacks Blockchain + +To run your miner, run this in the command line: + +```bash +stacks-node start --config=$HOME/mainnet-miner-conf.toml +``` + +Your node should start. It will take some time to sync, and then your miner will be running. + +#### Enable Debug Logging + +In case you are running into issues or would like to see verbose logging, you can run your node with debug logging enabled. In the command line, run: + +```bash +STACKS_LOG_DEBUG=1 stacks-node start --config=$HOME/mainnet-miner-conf.toml +``` + +*** + +### Optional: Running a Stacks Blockchain miner with Docker + +Alternatively, you can run a Stacks mainnet miner with Docker. + +:::caution Ensure you have [Docker](https://docs.docker.com/get-docker/) installed. ::: + +#### Generate a Keychain and Get Some Tokens + +Generate a keychain: + +```bash +docker run -i node:14-alpine npx @stacks/cli make_keychain 2>/dev/null | jq -r +``` + +We need to get some BTC to that address. You should be able to transfer BTC to this address using a cryptocurrency exchange such as [Coinbase](https://www.coinbase.com), [Binance](https://www.binance.com), or [Kraken](https://www.kraken.com). + +#### Update Stacks Blockchain Docker Configuration File + +Use the steps oulined above to create the configuration file + +#### Start the Stacks Blockchain miner with Docker + +:::info The ENV VARS `RUST_BACKTRACE` and `STACKS_LOG_DEBUG` are optional. If removed, debug logs will be disabled ::: + +```bash +docker run -d \ + --name stacks_miner \ + --rm \ + --network host \ + -e RUST_BACKTRACE="full" \ + -e STACKS_LOG_DEBUG="1" \ + -v "$HOME/mainnet-miner-conf.toml:/src/stacks-node/mainnet-miner-conf.toml" \ + -v "/stacks-blockchain:/stacks-blockchain" \ + -p 20443:20443 \ + -p 20444:20444 \ + blockstack/stacks-blockchain:latest \ +/bin/stacks-node start --config /src/stacks-node/mainnet-miner-conf.toml +``` + +You can review the node logs with this command: + +```bash +docker logs -f stacks_miner +``` + +### Optional: Running in Kubernetes with Helm + +In addition, you're also able to run a Stacks miner in a Kubernetes cluster using the [stacks-blockchain Helm chart](https://github.com/stacks-network/stacks-blockchain/tree/master/deployment/helm/stacks-blockchain). + +Ensure you have the following prerequisites installed: + +* [Docker](https://docs.docker.com/get-docker/) +* [minikube](https://minikube.sigs.k8s.io/docs/start/) (Only needed if standing up a local Kubernetes cluster) +* [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) +* [helm](https://helm.sh/docs/intro/install/) + +#### Generate keychain and get some tokens + +Use the steps outlined above + +#### Install the chart and run the miner + +To install the chart with the release name `my-release` and run the node as a miner: + +```bash +minikube start # Only run this if standing up a local Kubernetes cluster +helm repo add blockstack https://charts.blockstack.xyz +helm install my-release blockstack/stacks-blockchain \ + --set config.node.miner=true \ + --set config.node.seed="replace-with-your-privateKey-from-generate-keychain-step" \ + --set config.burnchain.mode="mainnet" +``` + +You can review the node logs with this command: + +```bash +kubectl logs -l app.kubernetes.io/name=stacks-blockchain +``` + +For more information on the Helm chart and configuration options, please refer to the [chart's homepage](https://github.com/stacks-network/stacks-blockchain/tree/master/deployment/helm/stacks-blockchain). diff --git a/stacks-in-depth/nodes-and-miners/mine-testnet-stacks-tokens.md b/stacks-in-depth/nodes-and-miners/mine-testnet-stacks-tokens.md new file mode 100644 index 0000000000..b2e52a5961 --- /dev/null +++ b/stacks-in-depth/nodes-and-miners/mine-testnet-stacks-tokens.md @@ -0,0 +1,270 @@ +# Mine Testnet Stacks Tokens + +### Introduction + +For more on the technical details of mining, please review the mining guide + +The following is an abridged version of the [walkthrough here](https://github.com/stacksfoundation/miner-docs/tree/testnet), written for a Linux system. If you're on Windows or MacOS, there will be some slight modifications needed (PR's welcome!). + +If you're interested in mining on the Stacks mainnet, you can find instructions on how to do that here: + +### Running a Bitcoin Testnet Full Node + +To participate as a miner on testnet, you must have access to a testnet bitcoin node with a wallet (and the wallet's private key). One way to accomplish this is to run bitcoin locally. + +* [Ensure your computer meets the minimum hardware requirements before continuing.](https://bitcoin.org/en/bitcoin-core/features/requirements#system-requirements) + +First, download a [bitcoin binary](https://bitcoin.org/en/download), or [build from source](https://github.com/stacksfoundation/miner-docs/blob/main/bitcoin.md#source-install) + +:::tip It is recommened to use a persistent location for the chainstate, in the steps below we're using `/bitcoin` ::: + +#### Update the Bitcoin Configuration File + +Next, update the bitcoin configuration: + +* **optional but recommended:** Use a persistent directory to store the Bitcoin chainstate, i.e. `datadir=/bitcoin` +* **optional but recommended:** Update the `rpcallowip` value to only allow `127.0.0.1`, or the stacks miner IPv4 +* Modify the `rpcuser` and `rpcpassword` values from the defaults below +* Store the following configuration somewhere on your filesystem (ex: `$HOME/bitcoin.conf`) + +```toml +server=1 +testnet=1 +disablewallet=0 +datadir=/bitcoin +rpcuser=btcuser +rpcpassword=btcpass +rpcallowip=0.0.0.0/0 +dbcache=512 +banscore=1 +rpcthreads=256 +rpcworkqueue=256 +rpctimeout=100 +txindex=1 + +[test] +bind=0.0.0.0:18333 +rpcbind=0.0.0.0:18332 +rpcport=18332 +``` + +#### Start Bitcoin + +Finally, start bitcoind as follows: + +```bash +bitcoind -conf=$HOME/bitcoin.conf +``` + +:::note It will take a few hours for the node to synchronize with Bitcoin testnet. ::: + +While it's syncing, you can track the progress with `bitcoin-cli` or the logfile (will be located where the chainstate is stored, i.e. `/bitcoin/testnet3/debug.log`): + +```bash +$ bitcoin-cli \ + -rpcconnect=localhost \ + -rpcport=18332 \ + -rpcuser=btcuser \ + -rpcpassword=btcpass \ +getblockchaininfo | jq .blocks +2417570 +``` + +*** + +### Running a Stacks Blockchain miner + +First, download a [stacks blockchain binary](https://github.com/stacks-network/stacks-blockchain/releases/latest), or [build from source](https://github.com/stacksfoundation/miner-docs/blob/main/stacks-blockchain.md#build-and-install-stacks-blockchain-from-source) + +_There may be some extra requirements to building,_ [_defined here_](https://github.com/stacksfoundation/miner-docs/blob/main/prerequisites.md#install-required-packages) + +:::tip It is recommened to use a persistent location for the chainstate, in the steps below we're using `/stacks-blockchain` ::: + +#### Generate a keychain + +First, a keychain needs to be generated. With this keychain, we'll get some testnet BTC from a faucet, and then use that BTC to start mining. + +To create a keychain, the simplest way is to use the [stacks-cli](https://docs.hiro.so/references/stacks-cli) with the `make_keychain` command. + +```bash +npx @stacks/cli make_keychain -t 2>/dev/null | jq -r +``` + +After this runs, you should see some JSON printed to the screen that looks like this: + +```json +{ + "mnemonic": "exhaust spin topic distance hole december impulse gate century absent breeze ostrich armed clerk oak peace want scrap auction sniff cradle siren blur blur", + "keyInfo": { + "privateKey": "2033269b55026ff2eddaf06d2e56938f7fd8e9d697af8fe0f857bb5962894d5801", + "address": "STTX57EGWW058FZ6WG3WS2YRBQ8HDFGBKEFBNXTF", + "btcAddress": "mkRYR7KkPB1wjxNjVz3HByqAvVz8c4B6ND", + "index": 0 + } +} +``` + +:::warning **Do not lose this information** - we'll need to use the `privateKey` and `btcAddress` fields in later steps. ::: + +The above `btcAddress` (mkRYR7KkPB1wjxNjVz3HByqAvVz8c4B6ND) will then need to be imported into the bitcoin testnet network. :::note Be sure to replace `` with the bitcoin address in the "Generate a keychain" step ::: + +```bash +bitcoin-cli \ + -rpcport=18332 \ + -rpcuser=btcuser \ + -rpcpassword=btcpassword \ +importaddress +``` + +Once imported, we need to get some testnet BTC to that address. Grab the `btcAddress` field, and paste it into [this Bitcoin testnet faucet](https://tbtc.bitaps.com/). You'll be sent `0.01` testnet BTC to that address. + +#### Update the Stacks Blockchain Configuration File + +Now, we need to configure our node to use this Bitcoin keychain. Copy the [sample testnet miner config](https://raw.githubusercontent.com/stacks-network/stacks-blockchain/master/testnet/stacks-node/conf/testnet-miner-conf.toml) to your local machine in a _memorable_ location like `$HOME/testnet-miner-conf.toml`. + +Now, grab your `privateKey` from earlier when you ran the `make_keychain` command. Replace the `seed` and `local_peer_seed` field with your private key. Save and close this configuration file. + +Next, update the bitcoin configuration: + +* **optional but recommended:** Use a persistent directory to store the Stacks chainstate, i.e. `working_dir = "/stacks-blockchain"` +* From the `make_keychain` step, modify the `seed` and `local_peer_seed` values with `privatekey` +* Store the following configuration somewhere on your filesystem (ex: `$HOME/testnet-miner-conf.toml`) + +```toml +[node] +working_dir = "/stacks-blockchain" +rpc_bind = "0.0.0.0:20443" +p2p_bind = "0.0.0.0:20444" +seed = "" +local_peer_seed = "" +miner = true +bootstrap_node = "047435c194e9b01b3d7f7a2802d6684a3af68d05bbf4ec8f17021980d777691f1d51651f7f1d566532c804da506c117bbf79ad62eea81213ba58f8808b4d9504ad@testnet.stacks.co:20444" +wait_time_for_microblocks = 10000 + +[burnchain] +chain = "bitcoin" +mode = "xenon" +peer_host = "127.0.0.1" +username = "" +password = "" +rpc_port = 18332 +peer_port = 18333 + +[[ustx_balance]] +address = "ST2QKZ4FKHAH1NQKYKYAYZPY440FEPK7GZ1R5HBP2" +amount = 10000000000000000 + +[[ustx_balance]] +address = "ST319CF5WV77KYR1H3GT0GZ7B8Q4AQPY42ETP1VPF" +amount = 10000000000000000 + +[[ustx_balance]] +address = "ST221Z6TDTC5E0BYR2V624Q2ST6R0Q71T78WTAX6H" +amount = 10000000000000000 + +[[ustx_balance]] +address = "ST2TFVBMRPS5SSNP98DQKQ5JNB2B6NZM91C4K3P7B" +amount = 10000000000000000 +``` + +#### Start the Stacks Blockchain + +To run your miner, run this in the command line: + +```bash +stacks-node start --config=$HOME/testnet-miner-conf.toml +``` + +Your node should start. It will take some time to sync, and then your miner will be running. + +#### Enable Debug Logging + +In case you are running into issues or would like to see verbose logging, you can run your node with debug logging enabled. In the command line, run: + +```bash +STACKS_LOG_DEBUG=1 stacks-node start --config=$HOME/testnet-miner-conf.toml +``` + +*** + +### Optional: Running a Stacks Blockchain miner with Docker + +Alternatively, you can run a Stacks testnet miner with Docker. + +:::caution Ensure you have [Docker](https://docs.docker.com/get-docker/) installed ::: + +#### Generate a Keychain and Get Some Tokens + +Generate a keychain: + +```bash +docker run -i node:14-alpine npx @stacks/cli make_keychain 2>/dev/null | jq -r +``` + +Now, we need to get some tBTC. Grab the `btcAddress` field, and paste it into this Bitcoin testnet faucet. You'll be sent `0.01` tBTC to that address. + +#### Update Stacks Blockchain Docker Configuration File + +Use the steps oulined above to create the configuration file + +#### Start the Stacks Blockchain miner with Docker + +:::info The ENV VARS `RUST_BACKTRACE` and `STACKS_LOG_DEBUG` are optional. If removed, debug logs will be disabled ::: + +```bash +docker run -d \ + --name stacks_miner \ + --rm \ + --network host \ + -e RUST_BACKTRACE="full" \ + -e STACKS_LOG_DEBUG="1" \ + -v "$HOME/testnet-miner-conf.toml:/src/stacks-node/testnet-miner-conf.toml" \ + -v "/stacks-blockchain:/stacks-blockchain" \ + -p 20443:20443 \ + -p 20444:20444 \ + blockstack/stacks-blockchain:latest \ +/bin/stacks-node start --config /src/stacks-node/testnet-miner-conf.toml +``` + +You can review the node logs with this command: + +```bash +docker logs -f stacks_miner +``` + +*** + +### Optional: Running in Kubernetes with Helm + +In addition, you're also able to run a Stacks miner in a Kubernetes cluster using the [stacks-blockchain Helm chart](https://github.com/stacks-network/stacks-blockchain/tree/master/deployment/helm/stacks-blockchain). + +Ensure you have the following prerequisites installed: + +* [Docker](https://docs.docker.com/get-docker/) +* [minikube](https://minikube.sigs.k8s.io/docs/start/) (Only needed if standing up a local Kubernetes cluster) +* [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) +* [helm](https://helm.sh/docs/intro/install/) + +#### Generate keychain and get some tokens + +Use the steps outlined above + +#### Install the chart and run the miner + +To install the chart with the release name `my-release` and run the node as a miner: + +```bash +minikube start # Only run this if standing up a local Kubernetes cluster +helm repo add blockstack https://charts.blockstack.xyz +helm install my-release blockstack/stacks-blockchain \ + --set config.node.miner=true \ + --set config.node.seed="replace-with-your-privateKey-from-generate-keychain-step" \ +``` + +You can review the node logs with this command: + +```bash +kubectl logs -l app.kubernetes.io/name=stacks-blockchain +``` + +For more information on the Helm chart and configuration options, please refer to the [chart's homepage](https://github.com/stacks-network/stacks-blockchain/tree/master/deployment/helm/stacks-blockchain). diff --git a/stacks-in-depth/nodes-and-miners/miner-costs-and-fees.md b/stacks-in-depth/nodes-and-miners/miner-costs-and-fees.md new file mode 100644 index 0000000000..7bc819017e --- /dev/null +++ b/stacks-in-depth/nodes-and-miners/miner-costs-and-fees.md @@ -0,0 +1,22 @@ +# Miner Costs and Fees + +### Configuring Cost and Fee Estimation + +Fee and cost estimators can be configured via the config section `[fee_estimation]`: + +```toml +[fee_estimation] +cost_estimator = naive_pessimistic +fee_estimator = fuzzed_weighted_median_fee_rate +fee_rate_fuzzer_fraction = 0.1 +fee_rate_window_size = 5 +cost_metric = proportion_dot_product +log_error = true +enabled = true +``` + +Fee and cost estimators observe transactions on the network and use the observed costs of those transactions to build estimates for viable fee rates and expected execution costs for transactions. Estimators and metrics can be selected using the configuration fields above, though the default values are the only options currently. `log_error` controls whether or not the INFO logger will display information about the cost estimator accuracy as new costs are observed. Setting `enabled = false` turns off the cost estimators. Cost estimators are **not** consensus-critical components, but rather can be used by miners to rank transactions in the mempool or client to determine appropriate fee rates for transactions before broadcasting them. + +The `fuzzed_weighted_median_fee_rate` uses a median estimate from a window of the fees paid in the last `fee_rate_window_size` blocks. Estimates are then randomly "fuzzed" using uniform random fuzz of size up to `fee_rate_fuzzer_fraction` of the base estimate. + +There is also a [mining calculator](https://friedger.id/mining-calculator/) that can help you with this process, with the [source code available here](https://github.com/friedger/mining-calculator). diff --git a/stacks-in-depth/nodes-and-miners/miner-prerequisites.md b/stacks-in-depth/nodes-and-miners/miner-prerequisites.md new file mode 100644 index 0000000000..7058e44964 --- /dev/null +++ b/stacks-in-depth/nodes-and-miners/miner-prerequisites.md @@ -0,0 +1,92 @@ +# Miner Prerequisites + +## Prerequisites + +### VM setup + +The VM will not need a lot of resources to run a miner - the most resources will be consumed during the blockchain syncs (for both Bitcoin and Stacks). For this example, we'll be assuming a [**Debian**](https://www.debian.org/) host with `x86_64` architecture (_commands may also work on any Debian-derived distribution_) + +A single CPU system with at least 4GB of memory and 1TB of disk space should be considered the minimum required specs to run the miner. + +#### VM Specs + +* Minimum CPU of: `1 vCPU` +* Minimum Memory of: `4GB Memory` +* Minimum Storage of: `1TB Disk` to allow for chainstate growth + * as of **July 2022**: + * Bitcoin chainstate is roughly `420GB` + * Stacks chainstate is roughly `45GB` + +**Disk Configuration** + +Two options here - either are fine but it's _recommended_ to mount the chainstate from a separate disk that only contains the chainstate (option 1) + +1. Separate disks for chainstate(s) and OS: + * mount a dedicated disk for bitcoin at `/bitcoin` of 1TB + * mount a dedicated disk for stacks-blockchain at `/stacks-blockchain` of at least 100GB + * root volume `/` of at least 25GB +2. Combined Disk for all data: + * root volume `/` of at least 1TB + +Create the required directories: + +```bash +$ sudo mkdir -p /bitcoin +$ sudo mkdir -p /stacks-blockchain +$ sudo mkdir -p /etc/bitcoin +$ sudo mkdir -p /etc/stacks-blockchain +``` + +**If using mounted disks**: mount the disks to each filesystem created above - edit `/etc/fstab` to automount these disks at boot. + +Example: + +``` +/dev/xvdb1 /bitcoin xfs rw,relatime,attr2,inode64,noquota +/dev/xvdc1 /stacks-blockchain xfs rw,relatime,attr2,inode64,noquota +``` + +Mount the disks `sudo mount -a` + +### Scripted install + +You can use the scripts/prerequisites.sh to install everything: + +```bash +curl --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/stacksfoundation/miner-docs/main/scripts/prerequisites.sh | bash +``` + +### Install required packages + +The following packages are required, and used by the rest of these docs + +```bash +$ curl -sL https://deb.nodesource.com/setup_16.x | sudo -E bash - +$ sudo apt-get update -y && sudo apt-get install -y \ + build-essential \ + jq \ + netcat \ + nodejs \ + git \ + autoconf \ + libboost-system-dev \ + libboost-filesystem-dev \ + libboost-thread-dev \ + libboost-chrono-dev \ + libevent-dev \ + libzmq5 \ + libtool \ + m4 \ + automake \ + pkg-config \ + libtool \ + libboost-system-dev \ + libboost-filesystem-dev \ + libboost-chrono-dev \ + libboost-program-options-dev \ + libboost-test-dev \ + libboost-thread-dev \ + libboost-iostreams-dev +$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh && source $HOME/.cargo/env +$ sudo npm install -g @stacks/cli rimraf shx +``` diff --git a/stacks-in-depth/nodes-and-miners/run-a-node-with-a-hosted-provider.md b/stacks-in-depth/nodes-and-miners/run-a-node-with-a-hosted-provider.md new file mode 100644 index 0000000000..edc1cd5a00 --- /dev/null +++ b/stacks-in-depth/nodes-and-miners/run-a-node-with-a-hosted-provider.md @@ -0,0 +1,17 @@ +# Run a Node with a Hosted Provider + +We're always looking for new hosting providers that enable anyone to run the Stacks Blockchain. Below, you'll find some examples on the current providers that are know to support running a node. + +### Quicknode + +Please refer to the Quicknode Section for instructions on launching an instance with Quicknode. + +### Stacks on Render + +The [render-stacks](https://github.com/stacksfoundation/render-stacks) GitHub repo has instructions so anyone can deploy a Stacks node to the hosted [Render](https://render.com) service in one click. + +:::note While it is _possible_ to use the free plan with some modifications, \_ _**it is recommended to run this on paid plan**_ \_ ::: + +### Stacks on Fly + +The [fly-stacks](https://github.com/stacksfoundation/fly-stacks) GitHub repo has instructions so anyone can deploy a Stacks node to the hosted [Fly](https://fly.io) service. diff --git a/stacks-in-depth/nodes-and-miners/run-a-node-with-digital-ocean.md b/stacks-in-depth/nodes-and-miners/run-a-node-with-digital-ocean.md new file mode 100644 index 0000000000..3e29ae9c6c --- /dev/null +++ b/stacks-in-depth/nodes-and-miners/run-a-node-with-digital-ocean.md @@ -0,0 +1,81 @@ +# Run a Node with Digital Ocean + +### Introduction + +This is a step by step guide to deploy the [Stacks Blockchain](https://github.com/stacks-network/stacks-blockchain) on [DigitalOcean](https://digitalocean.com). + +Build code is hosted on this [Github repository](https://github.com/stacksfoundation/stacks-machine-images) using the [methods from here](https://github.com/stacks-network/stacks-blockchain-docker) + +### Steps + +**Step 1** + +Go to the [Stacks Blockchain page](https://marketplace.digitalocean.com/apps/stacks-blockchain) in DigitalOcean's marketplace. Click on `Create Stacks Blockchain Droplet`. + +**Step 2** + +Choose a plan (it will only allow you to select a droplet that meets the minimum requirements) and your preferred datacenter region. + +**Step 3** + +Enter a root password or [enable SSH keys](https://docs.digitalocean.com/products/droplets/how-to/add-ssh-keys/) if your prefer. + +**Step 4** + +You can leave the rest of the options as they are and click on `Create Droplet` + +**Step 5** + +You will need to wait a few seconds for the droplet to get created. Once created click on it to see more information. + +#### Step 6 + +Congratulations! You are now running the Stacks Blockchain. You can click on `Console` for a terminal window to open or login using SSH to the public IP you've been assigned to with user `root`. + +### Getting started after deploying Stacks Blockchain + +Once the droplet is launched, the initial startup can take several minutes while BNS data is imported (this is a one time operation) and the Bitcoin headers are synced. + +To keep track of the progress, you can `ssh root@your_droplet_public_ipv4` to the host and run: `/opt/stacks-blockchain-docker/manage.sh -n mainnet -a logs`. + +After the stacks blockchain finishes the initial header sync and starts to sync with its peers, the application ports will open (`20443` and `3999`) and HTTP port `80` will now start proxying requests. + +Use `http://your_droplet_public_ipv4` to access the data directly, with output being similar to: + +```json +{ + "server_version": "stacks-blockchain-api v6.2.3 (master:77ab3ae2)", + "status": "ready", + "chain_tip": { + "block_height": 91820, + "block_hash": "0x06b276e85f238151414616618ae0adaf5eeda4eac6cad5bbefceeb37948ab275", + "index_block_hash": "0x4d7c075d7ab0f90b1dbc175f5c42b7344265d00cfef202dd9681d95388eeed8c", + "microblock_hash": "0xcf4f9037cc10696b2812b617ca105885be625c6acf8ad67e71bb4c09fa6ebb21", + "microblock_sequence": 4 + } +} +``` + +:::tip For the full list of API endpoints for the Stacks Blockchain, consult the [Hiro API Docs](https://docs.hiro.so/api) ::: + +All services are managed by a [systemd unit file](https://github.com/stacksfoundation/stacks-machine-images/blob/master/files/etc/systemd/system/stacks.service) that is set to start on boot. + +Manual control is also possible via the [manage.sh script](https://github.com/stacks-network/stacks-blockchain-docker/blob/master/manage.sh) at `/opt/stacks-blockchain-docker/manage.sh` on the host. + +Full details on how to use the manage.sh script is [available here](https://github.com/stacks-network/stacks-blockchain-docker/blob/master/docs/usage.md). + +### Launching a Droplet using the DigitalOcean API + +In addition to creating a Droplet from the Stacks Blockchain 1-Click App via the control panel, you can also use the [DigitalOcean API](https://digitalocean.com/docs/api). + +As an example, to create a 4GB Stacks Blockchain Droplet in the SFO2 region, you can use the following curl command. You’ll need to either save your [API access token](https://docs.digitalocean.com/reference/api/create-personal-access-token/) to an environment variable or substitute it into the command below. + +:::note _The `name`, `region` and `size` values below are hardcoded, so adjust as desired._ ::: + +```bash +$ export TOKEN= +$ curl -X POST -H 'Content-Type: application/json' \ + -H 'Authorization: Bearer '$TOKEN'' -d \ + '{"name":"stacks-blockchain","region":"sfo2","size":"s-2vcpu-4gb","image":"stacksfoundation-stacksblockchain"}' \ + "https://api.digitalocean.com/v2/droplets" +``` diff --git a/stacks-in-depth/nodes-and-miners/run-a-node-with-docker.md b/stacks-in-depth/nodes-and-miners/run-a-node-with-docker.md new file mode 100644 index 0000000000..50162dc1da --- /dev/null +++ b/stacks-in-depth/nodes-and-miners/run-a-node-with-docker.md @@ -0,0 +1,102 @@ +# Run a Node with Docker + + + +### Stacks Blockchain with Docker + +Run your own Stacks Blockchain node using [docker-compose](https://docs.docker.com/compose/) with just few commands using [stacks-blockchain-docker](https://github.com/stacks-network/stacks-blockchain-docker) + +* Quickstart +* [Requirements](https://github.com/stacks-network/stacks-blockchain-docker/blob/master/docs/requirements.md) +* [Docker Setup](https://github.com/stacks-network/stacks-blockchain-docker/blob/master/docs/docker.md) +* [Configuration](https://github.com/stacks-network/stacks-blockchain-docker/blob/master/docs/config.md) +* [Upgrading](https://github.com/stacks-network/stacks-blockchain-docker/blob/master/docs/upgrade.md) +* [Common Issues](https://github.com/stacks-network/stacks-blockchain-docker/blob/master/docs/issues.md) + +### **Requirements:** + +The **minimum viable requirements** are listed below. + +While you _can_ run a node using these specs, it's _recommended_ to assign more than the minimum for better performance. + +* ⚠️ [docker-compose](https://docs.docker.com/compose/install/) version `2.2.2` or greater is **required** +* **4GB memory** +* **1 Vcpu** ( _minimum of 2 Vcpu is recommended_ ) +* **100GB disk** ( _minimum of 150GB SSD is recommended_ ) + +:::caution MacOS with an ARM processor is NOT recommended + +The way Docker for Mac on an Arm CPU is designed makes the I/O incredibly slow, and blockchains are _**very**_ heavy on I/O. This only seems to affect MacOS, other Arm based systems like Raspberry Pi work as expected. ::: + +### **Quickstart** + +The `` placeholder used below can be replaced with one of: + +* mainnet +* testnet +* mocknet + +1. **Clone the stacks-blockchain-docker repository locally** + +```bash +git clone https://github.com/stacks-network/stacks-blockchain-docker && cd stacks-blockchain-docker +``` + +2. **Start the Services** + +```bash +./manage.sh -n -a start +``` + +:::note With an optional HTTP proxy on port 80: + +```bash +./manage.sh -n -a start -f proxy +``` + +::: + +### **Accessing the services** + +:::tip For networks other than `mocknet`, downloading the initial headers can take several minutes. Until the headers are downloaded, the `/v2/info` endpoints won't return any data. + +Follow the logs to track the sync progress: + +```bash +./manage.sh -n -a logs +``` + +::: + +**stacks-blockchain**: + +* Ports `20443-20444` are exposed on `localhost` + +```bash +curl -sL localhost:20443/v2/info | jq -r +``` + +**stacks-blockchain-api**: + +* Port `3999` is exposed on `localhost` + +```bash +curl -sL localhost:3999 | jq -r +``` + +**proxy**: + +* Port `80` is exposed on `localhost` + +```bash +curl -sL localhost/v2/info | jq -r +curl -sL localhost | jq -r +``` + +*** + +### Upgrades + +:::caution For schema-breaking upgrades to running instances of this repo, you'll need to [run an event-replay](https://github.com/stacks-network/stacks-blockchain-docker/blob/master/docs/upgrade.md): ::: + +*** diff --git a/stacks-in-depth/nodes-and-miners/run-a-node-with-quicknode.md b/stacks-in-depth/nodes-and-miners/run-a-node-with-quicknode.md new file mode 100644 index 0000000000..1879809c7d --- /dev/null +++ b/stacks-in-depth/nodes-and-miners/run-a-node-with-quicknode.md @@ -0,0 +1,37 @@ +# Run a Node with Quicknode + +[QuickNode](https://www.quicknode.com/) is a service for rapidly getting set up with a Stacks node. + +As an easy and fast alternative to running your own node, you can utilize QuickNode to serve as an API. + +To get started, [set up an account](https://www.quicknode.com/signup) on QuickNode. + +Once you are signed in, getting set up with your own Stacks node is a matter of a few clicks, starting with 'Create an endpoint'. + +From there, you just need to select Stacks, your desired network, and your desired QuickNode plan level. + +_That's it._ + +You now have an API endpoint URL you can use to connect to Stacks. + +How do you do that? + +It depends on the frontend framework you are using, but let's see how to do it with Stacks.js. + +First, you'll need to install the `@stacks/network` package. + +Next we'll import it: + +```javascript +import { StacksTestnet } from "@stacks/network"; +``` + +Then in your frontend, set up the network with the following line: + +```javascript +const network = new StacksTestnet({ url: "" }); +``` + +Then you can call transactions like you normally would using the `@stacks/transactions` library. + +For an example of how to do this, please refer to the Hello Stacks tutorial. diff --git a/stacks-in-depth/nodes-and-miners/stacks-node-configuration.md b/stacks-in-depth/nodes-and-miners/stacks-node-configuration.md new file mode 100644 index 0000000000..cd53d8850b --- /dev/null +++ b/stacks-in-depth/nodes-and-miners/stacks-node-configuration.md @@ -0,0 +1,136 @@ +# Stacks Node Configuration + +### Usage + +```bash +stacks-node sub-command [--subcommand-option ] +``` + +#### Subcommands + +* `mocknet`: start a mocknet instance using defaults +* `testnet`: start a testnet instance using defaults (chainstate is not persistent) +* `mainnet`: start a mainnet instance using defaults (chainstate is not persistent) +* `start`: combined with `--config`, starts an instance with a specified configuration file +* `version`: displays binary version +* `help`: displays the help message + +### Configuration File Options + +The Stacks Blockchain configuration file has multiple sections under which an option may be placed. + +* node +* events\_observer +* connection\_options +* burnchain +* ustx\_balance + +For reference, several configuration file examples are [available here](https://github.com/stacks-network/stacks-blockchain/tree/master/testnet/stacks-node/conf) + +* Example mainnet follower configuration + +#### node + +Contains various configuration options for the stacks-node binary. + +| Name | Required | Description | +| ---------------------------- | -------- | ---------------------------------------------------------------------------------------------------------- | +| rpc\_bind | ✓ | IPv4 address and port to open for RPC connections | +| p2p\_bind | ✓ | IPv4 address and port to open for P2P connections | +| working\_dir | | Absolute path to the directory where chainstate data will be stored | +| data\_url | | IPv4 address and port for incoming RPC connections | +| p2p\_address | | IPv4 address and port for incoming P2P connections | +| bootstrap\_node | | Public key, IPv4 address, and port to bootstrap the chainstate | +| wait\_time\_for\_microblocks | | The amount of time in ms to wait before trying to mine a block after catching up to the anchored chain tip | +| seed | | The private key to use for mining. Only needed if `miner` is set to `true` | +| local\_peer\_seed | | The private key to use for signing P2P messages in the networking stack | +| miner | | Determines whether the node is running a follower (`false`) or a miner (`true`). Defaults to `false` | +| mock\_miner | | Simulates running a miner (typically used for debugging) | +| mine\_microblocks | | Determines whether the node will mine microblocks. Will only take effect if `miner` is set to `true` | +| prometheus\_bind | | Address and port for Prometheus metrics collection. | + +#### events\_observer + +:::info This section is _optional_ and not required + +However, if this section is added, **all** fields are required ::: Contains options for sending events emitted to the [stacks-blockchain-api](https://github.com/hirosystems/stacks-blockchain-api) service. + +\| Name | Required | Description | | ----------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | --- | | endpoint | ✓ | Address and port to a [stacks-blockchain-api](https://github.com/hirosystems/stacks-blockchain-api) service | | retry\_count | ✓ | Number of times to retry sending events to the endpoint before failing | | events\_keys | ✓ | Event keys for which to watch. The emitted node events can be restricted by account, function name and event type. Asterix ("\*") can be used to emit all events. | | + +#### connection\_options + +:::info This section is _optional_ and not required. + +However, if this section is added, **all** fields are required ::: + +Specifies configuration options for others connecting to the stacks node. + +| Name | Required | Description | +| ------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | +| public\_ip\_address | ✓ | Public IPv4 to advertise to other nodes | +| download\_interval | ✓ | Time (in seconds) between attempts to download blocks | +| walk\_interval | ✓ | Time (in seconds) between attempts to walk the list of neighbors | +| read\_only\_call\_limit\_read\_length | ✓ | Total number of bytes allowed to be read by an individual read-only function call | +| read\_only\_call\_limit\_read\_count | ✓ | Total number of independent read operations permitted for an individual read-only function call | +| read\_only\_call\_limit\_runtime | ✓ | [Runtime cost](https://github.com/stacksgov/sips/blob/main/sips/sip-006/sip-006-runtime-cost-assessment.md) limit for an individual read-only function call | + +#### burnchain + +This section contains configuration options pertaining to the blockchain the stacks-node binds to on the backend for proof-of-transfer (BTC). + +| Name | Required | Description | +| ---------- | -------- | --------------------------------------------------------------------------------------------------------------------- | +| chain | ✓ | The blockchain stacks-node binds to on the backend for proof-of-transfer. Only value supported: `bitcoin` | +| mode | ✓ | The profile or test phase of which to run stacks-node. Valid values are \[ `mocknet`, `testnet`, `xenon`, `mainnet` ] | +| peer\_host | | FQDN of the host running the backend Bitcoin blockchain | +| rpc\_port | | RPC port of `peer_host` | +| peer\_port | | P2P port of `peer_host` | + +**Mining** + +| Name | Required | Description | +| ----------------------------- | -------- | -------------------------------------------------------------------------------------------------- | +| burn\_fee\_cap | ✓ | Maximum amount (in sats) of "burn commitment" to broadcast for the next block's leader election | +| satoshis\_per\_byte | ✓ | [Amount (in sats) per byte](https://bitcoinfees.net/) - Used to calculate the transaction fees | +| commit\_anchor\_block\_within | | Sets the time period (in milliseconds) for commitments. Only used when `mode` is set to `mocknet`. | + +#### ustx\_balance + +* `mocknet`/`testnet` only + +This section contains configuration options allocating microSTX per address in the genesis block + +This section can repeat multiple times, but each section can only define a single address. + +:::info This section is only required for the `testnet` and `mocknet` networks. + +However, if this section is added, **all** fields are required ::: + +| Name | Required | Description | +| ------- | -------- | --------------------------------------------------------------------- | +| address | ✓ | Address which maintains a microSTX balance | +| amount | ✓ | The balance of microSTX given to the address at the start of the node | + +### Example Mainnet Follower Configuration + +```toml +[node] +working_dir = "/stacks-blockchain" +rpc_bind = "0.0.0.0:20443" +p2p_bind = "0.0.0.0:20444" +bootstrap_node = "02da7a464ac770ae8337a343670778b93410f2f3fef6bea98dd1c3e9224459d36b@seed-0.mainnet.stacks.co:20444,02afeae522aab5f8c99a00ddf75fbcb4a641e052dd48836408d9cf437344b63516@seed-1.mainnet.stacks.co:20444,03652212ea76be0ed4cd83a25c06e57819993029a7b9999f7d63c36340b34a4e62@seed-2.mainnet.stacks.co:20444" + +[burnchain] +chain = "bitcoin" +mode = "mainnet" +peer_host = "localhost" +username = "user" +password = "pass" +rpc_port = 8332 +peer_port = 8333 + +[[events_observer]] +endpoint = "localhost:3700" +retry_count = 255 +events_keys = ["*"] +``` diff --git a/stacks-in-depth/nodes-and-miners/verify-miner.md b/stacks-in-depth/nodes-and-miners/verify-miner.md new file mode 100644 index 0000000000..f1286690b5 --- /dev/null +++ b/stacks-in-depth/nodes-and-miners/verify-miner.md @@ -0,0 +1,9 @@ +# Verify Miner + +You can verify that your node is operating as a miner by checking its log output to verify that it was able to find its Bitcoin UTXOs: + +```bash +$ head -n 1000 /path/to/your/node/logs | grep -i utxo +INFO [1630127492.031042] [testnet/stacks-node/src/run_loop/neon.rs:146] [main] Miner node: checking UTXOs at address: +INFO [1630127492.062652] [testnet/stacks-node/src/run_loop/neon.rs:164] [main] UTXOs found - will run as a Miner node +``` diff --git a/tutorials/bitcoin-integration/README.md b/tutorials/bitcoin-integration/README.md new file mode 100644 index 0000000000..17ee26dcbf --- /dev/null +++ b/tutorials/bitcoin-integration/README.md @@ -0,0 +1,3 @@ +# Bitcoin Integration + +One of the unique features of the Stack chain and the Clarity language is that it allows for directly reading from the Bitcoin chain itself. These tutorials walk you through some different ways you can accomplish that. diff --git a/tutorials/bitcoin-integration/parsing-a-bitcoin-transaction.md b/tutorials/bitcoin-integration/parsing-a-bitcoin-transaction.md new file mode 100644 index 0000000000..77c012d957 --- /dev/null +++ b/tutorials/bitcoin-integration/parsing-a-bitcoin-transaction.md @@ -0,0 +1,48 @@ +# Parsing a Bitcoin Transaction + +While we can verify that a transaction was mined using a library and Clarity's built-in functions, as outlined in the Verifying a transaction on the BTC chain docs, we can parse a Bitcoin transaction using Clarity as well. + +This doesn't actually require having access to the chain, all we need is the raw transaction hex. + +If you aren't familiar with how Bitcoin transactions are encoded in raw form, take a quick look at that. + +The short version is that all of the data from a Bitcoin transaction is encoded in hex form in a string of numbers, we can slice out pieces of that hex value to pull out all of our transaction data. + +The process to do this is relatively complex, but the Clarity-Bitcoin library comes with a function called `parse-tx` that makes this simple. + +All we need to do is pass it a raw transaction hex and we can get back the data of the transaction, including inputs and outputs. + +:::caution Note that currently the library only supports legacy transactions, although work to support segwit and taproot transactions is currently underway. ::: + +You can view a [deployed version of the library](https://explorer.hiro.so/txid/0xd493b9ada8899be8773d3f55b21d300ef83ac5c0dd38c8a4dd52a295bd71d539?chain=mainnet) on the explorer and the [GitHub repo here](https://github.com/friedger/clarity-bitcoin). + +The `parse-tx` function looks like this. + +```clarity +(define-read-only (parse-tx (tx (buff 1024))) + (let ((ctx { txbuff: tx, index: u0}) + (parsed-version (try! (read-uint32 ctx))) + (parsed-txins (try! (read-txins (get ctx parsed-version)))) + (parsed-txouts (try! (read-txouts (get ctx parsed-txins)))) + (parsed-locktime (try! (read-uint32 (get ctx parsed-txouts))))) + (ok {version: (get uint32 parsed-version), + ins: (get txins parsed-txins), + outs: (get txouts parsed-txouts), + locktime: (get uint32 parsed-locktime)}))) +``` + +We can get the raw transaction hex from a Bitcoin block explorer API like mempool, and it will be returned back to use looking something like this: + +`0200000000010196277c04c986c1ad78c909287fd12dba2924324699a0232e0533f46a6a3916bb0100000000ffffffff026400000000000000160014274ae586ad2035efb4c25049c155f98310d7e106ca16440000000000160014599bcef6387256c6b019030c421b4a4d382fe2600247304402204d94a1e4047ca38a450177ccb6f88585ca147f1939df343d8ac5d962c5f35bb302206f7fa42c21c47ebccdc460393d35c5dfd3b6f0a26cf10fac23d3e6fab71835c20121020cb972a66e3fb1cdcc9efcad060b4457ebec534942700d4af1c0d82a33aa13f100000000`. + +You can paste this into a raw transaction decoder like [this one](https://live.blockcypher.com/btc/decodetx/) and see the data. + +If we were using stacks.js, we would just need to pass this to our function as a buffer like this: + +```javascript +bufferCV(Buffer.from(txRaw, "hex")); +``` + +Where `txRaw` is just a string containing our raw transaction. When we do that, we are presented with the data of the transaction. + +We can then take that data and pull out whatever we need from it. diff --git a/tutorials/bitcoin-integration/sending-bitcoin-with-leather-wallet.md b/tutorials/bitcoin-integration/sending-bitcoin-with-leather-wallet.md new file mode 100644 index 0000000000..83b98187a4 --- /dev/null +++ b/tutorials/bitcoin-integration/sending-bitcoin-with-leather-wallet.md @@ -0,0 +1,35 @@ +# Sending Bitcoin with Leather Wallet + +Using Leather's web wallet, we can easily initiate a simple Bitcoin transaction using only a few lines of code. + +You'll need to be authenticated with the Leather wallet for this to work, with you can see how to do in the Authentication with Stacks.js tutorial. + +Once you have the wallet hooked up, you can use the Leather wallet API to initiate a simple Bitcoin transaction in your JS app like this. + +```javascript +const sendBitcoin = async () => { + const resp = await window.btc?.request("sendTransfer", { + address: "tb1qya9wtp4dyq67ldxz2pyuz40esvgd0cgx9s3pjl", //replace this with whatever address you want to send to + amount: "10000", // the amount you want to send denoted in satoshis + }); + + // Storing txid in local storage + // We'll get back the transaction IF, which we can then use to do whatever we want + if (typeof window !== "undefined") { + localStorage.setItem("txid", JSON.stringify(resp.result.txid)); + } + + // We may want to do something once this transaction has confirmed, so we can set it to pending here and then use an API like mempool.space to query the Bitcoin chain for information about this transaction + localStorage.setItem("txStatus", "pending"); +}; +``` + +Then all we would do is hook up our button to call this `sendBitcoin` function. + +```javascript + +``` + +You can take a look at the Verifying a transaction on the BTC chain recipe to see a more complex user flow of verifying a transaction was mined using this returned ID as a starting point. + +You can take a look at a bit more info about this simple API on the[ wallet developer docs](https://hirowallet.gitbook.io/developers/bitcoin/sign-transactions/sending-bitcoin). diff --git a/tutorials/bitcoin-integration/verifying-a-bitcoin-transaction.md b/tutorials/bitcoin-integration/verifying-a-bitcoin-transaction.md new file mode 100644 index 0000000000..a3c24ac31a --- /dev/null +++ b/tutorials/bitcoin-integration/verifying-a-bitcoin-transaction.md @@ -0,0 +1,190 @@ +# Verifying a Bitcoin Transaction + +One of the coolest things about Clarity is that it allows us to have visibility into the state of the Bitcoin blockchain. Since Stacks blocks are mined in lockstep with Bitcoin blocks, we can directly read the burnchain header info of each Bitcoin block using Clarity's built-in [`get-burn-block-info?`](https://docs.stacks.co/docs/clarity/language-functions#get-burn-block-info-clarity2) function. + +There are quite a few relatively complex things that need to happen to do this successfully, but a [clarity-bitcoin library](https://github.com/friedger/clarity-bitcoin/) exists to make the process a lot easier and handle some of the heavy lifting for us. + +Let's take a look at how to verify a Bitcoin transaction was mined using Clarity using the library. If you take a look at the `clarity-bitcoin.clar` file in the linked repo, you'll find a function called `was-tx-mined-compact`. That's what we'll be working with, and it looks like this: + +```clarity +(define-read-only (was-tx-mined-compact (height uint) (tx (buff 1024)) (header (buff 80)) (proof { tx-index: uint, hashes: (list 14 (buff 32)), tree-depth: uint})) + (let ((block (unwrap! (parse-block-header header) (err ERR-BAD-HEADER)))) + (was-tx-mined-internal height tx header (get merkle-root block) proof))) +``` + +The transaction itself is relatively simple, but there's a lot happening within other private function calls. I encourage you to read the contract for yourself and trace what is happening, step-by-step, when we call this function. + +For now, we'll just go over how to actually call this function successfully. + +You can see that it takes a few pieces of information: + +* `(height uint)` the block height you are looking to verify the transaction within +* `(tx (buff 1024))` the raw transaction hex of the transaction you are looking to verify +* `(header (buff 80))` the block header of the block +* `(proof { tx-index: uint, hashes: (list 14 (buff 32)), tree-depth: uint})` a merkle proof formatted as a tuple + +:::info + +A Merkle proof is a compact way to prove that a transaction is included in a block in the Bitcoin blockchain. Here's how it works: + +1. Transactions in a block are hashed and paired, then the hashes of the pairs are hashed and paired, and so on until a single hash remains - this is called the Merkle root. +2. The Merkle root is included in the block header. By providing the hashes that lead from a transaction's hash up to the Merkle root, along with the block header, one can prove that the transaction is included in that block. +3. These hashes that connect a transaction to the Merkle root are called the Merkle proof or Merkle path. By providing the Merkle proof along with the transaction hash and block header, anyone can verify that the transaction is part of that block. +4. This allows for efficient decentralized verification of transactions without having to download the entire blockchain. One only needs the transaction hash, Merkle proof, and block header to verify. + +::: + +Once we have this information, we can call into the `clarity-bitcoin.clar` contract and pass in this data. A common practice would be to get this data from a Bitcoin block explorer API like Mempool.space or Blockstream's esplora, parse it into the correct format for this helper, and then pass it to this function. + +We could do that directly via this contract if we just need a direct response on if the transaction was included or not, but more likely we would want to integrate this functionality into a Clarity contract of our own where we can `asserts!` that a transaction was mined before taking another action. + +Here's a basic example where we are calling [Blockstream's API](https://github.com/Blockstream/esplora/blob/master/API.md) using JavaScript, parsing the data into the right format, and then calling into our own `mint` function to only mint an NFT if the selected transaction was mined. + +We can get all the information we need with nothing but the transaction ID, which will usually be passed to us when we use a wallet like [Hiro's web wallet](https://hirowallet.gitbook.io/developers/bitcoin/sign-transactions/sending-bitcoin) to initiate the Bitcoin transaction. + +Let's go through the code we can use to implement this. For full context, this code is taken from the example [bitbadge](https://github.com/kenrogers/bitbadge) repo, which you can take a look at. For a complete ste-by-step walkthrough of how to implement this, check out the [Bitcoin Primer](https://bitcoinprimer.dev). + +Here's the mint function: + +```clarity +(define-public (mint (recipient principal) (height uint) (tx (buff 1024)) (header (buff 80)) (proof { tx-index: uint, hashes: (list 14 (buff 32)), tree-depth: uint})) + (let + ( + (token-id (+ (var-get last-token-id) u1)) + (tx-was-mined (try! (contract-call? .clarity-bitcoin was-tx-mined-compact height tx header proof))) + ) + (asserts! (is-eq tx-sender contract-owner) err-owner-only) + (asserts! (is-eq tx-was-mined true) err-tx-not-mined) + (try! (nft-mint? bitbadge token-id recipient)) + (var-set last-token-id token-id) + (ok token-id) + ) +) +``` + +Note the `(asserts! (is-eq tx-was-mined true) err-tx-not-mined)` line. This is what is doing the heavy lifting. + +:::caution Right now the clarity-bitcoin library only supports legacy transactions. Work is in-progress to add support for segwit, but until then we have to do a bit of work on the front end to strip the witness data from the raw transaction hex. ::: + +Here's the JavaScript code we can use to get the data we need. + +First we get the raw transaction and the merkle proof (we do this when we first get the transaction ID back). + +The `useEffect` here is so that we can check to see if the transaction was confirmed every 10 seconds before we get the rest of the information. + +```javascript +// Effect hook to check and see if the tx has been confirmed using blockstream API +useEffect(() => { + const intervalId = setInterval(() => { + const txid = JSON.parse(localStorage.getItem("txid")); + if (txid) { + fetch(`https://blockstream.info/testnet/api/tx/${txid}/status`) + .then((response) => response.json()) + .then(async (status) => { + // set txStatus in localStorage if it is confirmed, otherwise we want to leave it pending + if (status.confirmed) { + localStorage.setItem("txStatus", "confirmed"); + // set the block details + const blockDetails = { + block_height: status.block_height, + block_hash: status.block_hash, + }; + setBlockDetails(blockDetails); + localStorage.setItem("blockDetails", JSON.stringify(blockDetails)); + // fetch and set the tx raw + const rawResponse = await fetch( + `https://blockstream.info/testnet/api/tx/${txid}/hex` + ); + const txRaw = await rawResponse.text(); + localStorage.setItem("txRaw", txRaw); + // fetch and set the merkle proof + const proofResponse = await fetch( + `https://blockstream.info/testnet/api/tx/${txid}/merkle-proof` + ); + const txMerkleProof = await proofResponse.json(); + localStorage.setItem( + "txMerkleProof", + JSON.stringify(txMerkleProof) + ); + clearInterval(intervalId); + } + }) + .catch((err) => console.error(err)); + } + }, 10000); + return () => clearInterval(intervalId); // Clean up on component unmount +}, []); +``` + +Then we get and parse the rest of the data when we call the actual mint function. + +```javascript +// This function retrieves raw transaction and merkle proof from localStorage and calls the mint Clarity function +const mintBitbadge = async () => { + // Retrieving rawTx and merkleProof from local storage + let txRaw = ""; + let txMerkleProof = ""; + + if (typeof window !== "undefined") { + txRaw = removeWitnessData(localStorage.getItem("txRaw")); + txMerkleProof = JSON.parse(localStorage.getItem("txMerkleProof")); + } + + // First we need to verify that the sender of this transaction is the same as the user that is signed in + if (!verifyCorrectSender()) { + console.log("wrong sender"); + return false; + } + + const blockHeight = blockDetails.block_height; + + // Fetch the block hash + const blockHashResponse = await fetch( + `https://blockstream.info/testnet/api/block-height/${blockHeight}` + ); + const blockHash = await blockHashResponse.text(); + + // Fetch the block header + const blockHeaderResponse = await fetch( + `https://blockstream.info/testnet/api/block/${blockHash}/header` + ); + const blockHeaderHex = await blockHeaderResponse.text(); + + const txIndex = txMerkleProof.pos; + const hashes = txMerkleProof.merkle.map( + (hash) => bufferCV(hexToBytes(hash).reverse()) // lib needs reversed hashes + ); // Convert each hash to BufferCV and reverse it + + const functionArgs = [ + principalCV(userData.profile.stxAddress.testnet), + uintCV(blockHeight), + bufferCV(Buffer.from(txRaw, "hex")), + bufferCV(Buffer.from(blockHeaderHex, "hex")), + tupleCV({ + "tx-index": uintCV(txIndex), + hashes: listCV(hashes), + "tree-depth": uintCV(txMerkleProof.merkle.length), + }), + ]; + + const contractAddress = "ST3QFME3CANQFQNR86TYVKQYCFT7QX4PRXM1V9W6H"; // Replace with your contract address + const contractName = "bitbadge-v3"; // Replace with your contract name + const functionName = "mint"; // Replace with your function name + + const options = { + contractAddress, + contractName, + functionName, + functionArgs, + appDetails: { + name: "BitBadge", + icon: "https://freesvg.org/img/bitcoin.png", + }, + onFinish: (data) => { + console.log(data); + }, + }; + + await openContractCall(options); +}; +``` diff --git a/tutorials/community-tutorials.md b/tutorials/community-tutorials.md new file mode 100644 index 0000000000..639add400b --- /dev/null +++ b/tutorials/community-tutorials.md @@ -0,0 +1,25 @@ +# Community Tutorials + +These tutorials have been created by various members of the Stacks community. Have a tutorial to suggest? View the [contribution guide](http://localhost:3000/docs/contribute/docs) to submit a PR with a new tutorial added. + +### Written Tutorials + +* [Create a server-side rendered Stacks app with Remix](https://micro-stacks.dev/guides/with-remix) +* [Build a Stacks app with Next.js](https://micro-stacks.dev/guides/with-nextjs) +* [Creating a Voting Contract](https://www.clearness.dev/01-voting-clarity-smart-contract/01-getting-started) +* [Building an NFT with Stacks and Clarity](https://blog.developerdao.com/building-an-nft-with-stacks-and-clarity) +* [Minting NFTs with QuickNode](https://www.quicknode.com/guides/web3-sdks/how-to-mint-nfts-on-the-stacks-blockchain) +* [Order Book Contract Walkthrough](https://byzantion.hiro.so/) +* [Build a DEX on Stacks](https://www.pointer.gg/tutorials/build-a-dex-with-stacks/56abb3a4-05c1-4608-b096-f82189e9f759) +* [NFT Tutorial](https://docs.hiro.so/tutorials/clarity-nft) +* [Billboard Tutorial](https://docs.hiro.so/tutorials/clarity-billboard) +* [Integrating NFTs Into a Game](https://gamefi-stacks.gitbook.io/gamefistacks/tutorials/integrate-nfts-into-game) +* [Building on Stacks](https://github.com/amoweolubusayo/stacks-clarinet-tutorial) + +### Video Tutorials + +* [Web3 for Bitcoin](https://www.crowdcast.io/e/web3-for-bitcoin/) + +### Other Resources + +There are also a great amount of both tutorials and developer tools in the [Awesome Stacks repo](https://github.com/friedger/awesome-stacks-chain#clarity-resources). diff --git a/tutorials/frontend/README.md b/tutorials/frontend/README.md new file mode 100644 index 0000000000..a6f012ce2f --- /dev/null +++ b/tutorials/frontend/README.md @@ -0,0 +1,3 @@ +# Frontend + +A major part of building full-stack Stacks applications will involve building out a UI with a solid user experience. One of your primary tools in accomplishing this will be Stacks.js, a JavaScript library built by Hiro to make working with contracts and the chain itself easier. diff --git a/tutorials/frontend/authentication-with-stacks.js.md b/tutorials/frontend/authentication-with-stacks.js.md new file mode 100644 index 0000000000..926ed55ced --- /dev/null +++ b/tutorials/frontend/authentication-with-stacks.js.md @@ -0,0 +1,95 @@ +# Authentication with Stacks.js + +Authenticating with a Stacks wallet is a very common task when building Stacks dapps. + +Let's see how to set this up on the front end. + +Code here is pulled from the Hello Stacks tutorial. + +### Stacks.js Code + +We're using React here, but you can modify this for whatever frontend code you are using. + +Before you add the frontend code, you need to install the correct NPM package. + +```bash +yarn add @stacks/connect +``` + +And here's the JS code we'll need to implement. + +```js +import { + AppConfig, + UserSession, + AuthDetails, + showConnect, +} from "@stacks/connect"; +import { useState, useEffect } from "react"; + +function App() { + const [message, setMessage] = useState(""); + const [transactionId, setTransactionId] = useState(""); + const [currentMessage, setCurrentMessage] = useState(""); + const [userData, setUserData] = useState(undefined); + + const appConfig = new AppConfig(["store_write"]); + const userSession = new UserSession({ appConfig }); + + const appDetails = { + name: "Hello Stacks", + icon: "https://freesvg.org/img/1541103084.png", + }; + + const connectWallet = () => { + showConnect({ + appDetails, + onFinish: () => window.location.reload(), + userSession, + }); + }; + + useEffect(() => { + if (userSession.isSignInPending()) { + userSession.handlePendingSignIn().then((userData) => { + setUserData(userData); + }); + } else if (userSession.isUserSignedIn()) { + setUserData(userSession.loadUserData()); + } + }, []); + + return ( +
+ +

Hello Stacks

+
+ ); +} + +export default App; +``` + +This is all the code we need to authenticate and access the user data in the UI. + +But how do we actually use it? + +Let's implement a `connectWallet` button to see how we can utilize the data we're pulling here. + +```js +{ + !userData && ( + + ); +} +``` diff --git a/tutorials/frontend/post-conditions-with-stacks.js.md b/tutorials/frontend/post-conditions-with-stacks.js.md new file mode 100644 index 0000000000..a0706f56cd --- /dev/null +++ b/tutorials/frontend/post-conditions-with-stacks.js.md @@ -0,0 +1,133 @@ +# Post Conditions with Stacks.js + +The content in this recipe has ben pulled from the [tutorial on post conditions](https://dev.to/stacks/understanding-stacks-post-conditions-e65). Post conditions are an additional safety feature built into the Stacks chain itself that help to protect end users. + +Rather than being a function of Clarity smart contracts, they are implemented on the client side and meant to be an additional failsafe against malicious contracts. + +Put simply, post conditions are a set of conditions that must be met before a user's transaction will execute. The primary goal behind post conditions is to limit the amount of damage that can be done to a user's assets due to a bug, intentional or otherwise. + +So they are sent as part of the transaction when the user initiates it, meaning we need a frontend library to utilize them. + +Whenever you are transferring an asset (fungible or non-fungible) from one address to another, you should taker advantage of post conditions. + +We're going to use [Stacks.js](https://github.com/hirosystems/stacks.js/tree/master/packages/transactions#post-conditions) to familiarize ourselves with them. + +### STX Post Condition + +```js +import { + FungibleConditionCode, + makeStandardSTXPostCondition, + makeContractSTXPostCondition, +} from "@stacks/transactions"; + +// With a standard principal +const postConditionAddress = "SP2ZD731ANQZT6J4K3F5N8A40ZXWXC1XFXHVVQFKE"; +const postConditionCode = FungibleConditionCode.GreaterEqual; +const postConditionAmount = 12345n; + +const standardSTXPostCondition = makeStandardSTXPostCondition( + postConditionAddress, + postConditionCode, + postConditionAmount +); + +// With a contract principal +const contractAddress = "SPBMRFRPPGCDE3F384WCJPK8PQJGZ8K9QKK7F59X"; +const contractName = "test-contract"; + +const contractSTXPostCondition = makeContractSTXPostCondition( + contractAddress, + contractName, + postConditionCode, + postConditionAmount +); +``` + +### Fungible Token Post Condition + +```js +import { + FungibleConditionCode, + createAssetInfo, + makeStandardFungiblePostCondition, +} from "@stacks/transactions"; + +// With a standard principal +const postConditionAddress = "SP2ZD731ANQZT6J4K3F5N8A40ZXWXC1XFXHVVQFKE"; +const postConditionCode = FungibleConditionCode.GreaterEqual; +const postConditionAmount = 12345n; +const assetAddress = "SP62M8MEFH32WGSB7XSF9WJZD7TQB48VQB5ANWSJ"; +const assetContractName = "test-asset-contract"; +const fungibleAssetInfo = createAssetInfo(assetAddress, assetContractName); + +const standardFungiblePostCondition = makeStandardFungiblePostCondition( + postConditionAddress, + postConditionCode, + postConditionAmount, + fungibleAssetInfo +); + +// With a contract principal +const contractAddress = "SPBMRFRPPGCDE3F384WCJPK8PQJGZ8K9QKK7F59X"; +const contractName = "test-contract"; +const assetAddress = "SP62M8MEFH32WGSB7XSF9WJZD7TQB48VQB5ANWSJ"; +const assetContractName = "test-asset-contract"; +const fungibleAssetInfo = createAssetInfo(assetAddress, assetContractName); + +const contractFungiblePostCondition = makeContractFungiblePostCondition( + contractAddress, + contractName, + postConditionCode, + postConditionAmount, + fungibleAssetInfo +); +``` + +### NFT Post Condition + +```js +import { + NonFungibleConditionCode, + createAssetInfo, + makeStandardNonFungiblePostCondition, + makeContractNonFungiblePostCondition, + bufferCVFromString, +} from "@stacks/transactions"; + +// With a standard principal +const postConditionAddress = "SP2ZD731ANQZT6J4K3F5N8A40ZXWXC1XFXHVVQFKE"; +const postConditionCode = NonFungibleConditionCode.Owns; +const assetAddress = "SP62M8MEFH32WGSB7XSF9WJZD7TQB48VQB5ANWSJ"; +const assetContractName = "test-asset-contract"; +const assetName = "test-asset"; +const tokenAssetName = bufferCVFromString("test-token-asset"); +const nonFungibleAssetInfo = createAssetInfo( + assetAddress, + assetContractName, + assetName +); + +const standardNonFungiblePostCondition = makeStandardNonFungiblePostCondition( + postConditionAddress, + postConditionCode, + nonFungibleAssetInfo, + tokenAssetName +); + +// With a contract principal +const contractAddress = "SPBMRFRPPGCDE3F384WCJPK8PQJGZ8K9QKK7F59X"; +const contractName = "test-contract"; + +const contractNonFungiblePostCondition = makeContractNonFungiblePostCondition( + contractAddress, + contractName, + postConditionCode, + nonFungibleAssetInfo, + tokenAssetName +); +``` + +### Sample App + +Here's a [sample application](https://github.com/kenrogers/fabulous-frogs) that utilizes post conditions on the front end to secure user assets. diff --git a/tutorials/frontend/sending-transactions-with-stacks.js.md b/tutorials/frontend/sending-transactions-with-stacks.js.md new file mode 100644 index 0000000000..b4bfb0f9a2 --- /dev/null +++ b/tutorials/frontend/sending-transactions-with-stacks.js.md @@ -0,0 +1,52 @@ +# Sending Transactions with Stacks.js + +Any Stacks dapp is going to require sending transaction at some point, so how do we do that? We use the `@stacks/transactions` package. + +Again, this particular snippet is pulled from our Hello Stacks tutorial. + +When you send Stacks transactions, don't forget to utilize post conditions. + +But first, you'll need to install the right NPM package. + +```bash +yarn add @stacks/transactions +``` + +### Stacks.js Frontend Code + +Assuming you are authenticated, you'll need to import from the `network` package to connect and import the right Clarity values to convert. + +You need to convert values from JS into the right format for Clarity values to ingest. You can view the complete list of types on the [Stacks.js docs](https://stacks.js.org/modules/\_stacks\_transactions#constructing-clarity-values). + +```js +import { StacksMocknet } from "@stacks/network"; +import { stringUtf8CV } from "@stacks/transactions"; +``` + +Let's assume we have a message that we need to send. + +``` +Hello this is something I need to say. +``` + +```js +const submitMessage = async (e) => { + e.preventDefault(); + + const network = new StacksMocknet(); + + const options = { + contractAddress: "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM", + contractName: "hello-stacks", + functionName: "write-message", + functionArgs: [stringUtf8CV(message)], + network, + appDetails, + onFinish: ({ txId }) => console.log(txId), + }; + + await openContractCall(options); +}; +``` + +For more details on the different types of transactions you can send, the [Stacks.js docs](https://stacks.js.org/modules/\_stacks\_transactions) have multiple examples. diff --git a/tutorials/hello-stacks-quickstart-tutorial.md b/tutorials/hello-stacks-quickstart-tutorial.md new file mode 100644 index 0000000000..7f31d8d33b --- /dev/null +++ b/tutorials/hello-stacks-quickstart-tutorial.md @@ -0,0 +1,723 @@ +# Hello Stacks (Quickstart Tutorial) + +## Hello Stacks (Quickstart Tutorial) + +Looking to see what building on Stacks is all about? You're in the right place. + +This tutorial is designed to take you from never having worked with Stacks before to a complete app in about an hour. + +You'll be introduced to all the main concepts and tools utilized to build Stacks dapps, with further resources to continue to learn. + +It does assume a basic familiarity with blockchain and smart contract technology. If you need an intro to those things, we recommend Codecademy's [Intro to Blockchain and Crypto](https://www.codecademy.com/learn/introduction-to-blockchain-and-crypto) course to get up to speed. + +Let's get started. + +### What We're Building + +We're going to be building a very simple practice application called Hello Stacks. Hello Stacks allows users to post a message to the Stacks blockchain and automatically generates a link for others to view that message. + +Here's a brief overview video of what it looks like. + +We'll be utilizing various tools throughout this tutorial, most of which you don't need to have prior experience with. + +It will be helpful, however, for you to be familiar with JavaScript and React to be able to most effectively follow along, as that's what we're going to be using to build the frontend, along with [Vite](https://vitejs.dev/). + +You can either follow along from scratch starting in the next section, or you can download the [starter repo](https://github.com/kenrogers/hello-stacks/tree/starter-branch) with all of the dependencies already set up on GitHub. + +You can also view the finished code in the `master` branch of that same repo. + +Let's get started by getting everything set up. + +### Wallet Setup + +The very first thing you'll need to do is set up the Hiro wallet extension. This is how you can get STX tokens and interact with Stacks dapps. All you need to do right now is visit Hiro's [wallet page](https://wallet.hiro.so/) and install it. We'll set it up later with our dev environment. + +### Code Setup + +If you want to use the starter repo, you can clone that, run `yarn` and move on to the next section, titled 'Writing Our Smart Contract'. + +Otherwise, follow along if you want to start from scratch. If you are brand new to Stacks and are not familiar with the tooling ecosystem like Clarinet, Stacks.js, etc. I highly recommend following along to get everything set up on your own. + +:::tip Hiro also has a set of [Stacks.js starters](https://docs.hiro.so/stacksjs-starters) that you can use to quickly get up and running with a new Stacks project. We won't be using them here, but they are an excellent resource. ::: + +The first thing we need to do is scaffold a new project with Vite, the frontend build tool we'll be using. + +We're going to be using [Yarn](https://yarnpkg.com/), but you can also use NPM if you prefer. I'm using Yarn version `1.22.19` for this project and running Node version 18.7.0. You'll need to have both of those installed to follow along. I'm also working on a Mac, but will provide alternative installation instructions for tools like Clarinet as we go. + +:::tip We recommend [installing and using NVM](https://github.com/nvm-sh/nvm) in order to install Node and easily switch versions. ::: + +We'll have two separate directories for our project, one to handle the smart contracts and one to handle the frontend. Let's start by creating that and changing into it. + +```bash +mkdir hello-stacks && cd hello-stacks +``` + +Then get our frontend scaffolded. + +```bash +yarn create vite frontend --template react +``` + +Next we are going to install Clarinet. Clarinet is a development environment for Stacks created by Hiro. It allows us to set up a local Stacks chain, easily work with Clarity smart contracts (covered next), deploy our contracts, and test them. You can think of it as similar to Hardhat in the Ethereum world. + +Installation instructions vary depending on your system, so I'll refer you to the [Clarinet documentation](https://github.com/hirosystems/clarinet) to get that installed, then come back here to set up our project. + +Still inside the `hello-stacks` directory, let's get our Stacks folder created with the following command. You can name this whatever you want, but I like the frontend/contracts convention. + +```bash +clarinet new contracts +``` + +After this, you should have a `contracts` directory in your project that looks like this. + +There's not much here yet, we'll be creating our contracts in the next section. You'll also see a folder for tests, which are not covered in this tutorial. + +:::note Some people don't like the `contracts/contracts` structure that results from this naming convention. It's a matter of personal preference what you want to name this top-level folder when you create the Clarinet project. ::: + +Finally, there is a directory called `settings` which contains a few configuration files for how we can interact with each chain. We are primarily concerned with `Devnet.toml` here, as this is how we can set up our local Stacks node for testing purposes. The others mainly come into play when we deploy. + +The `Clarinet.toml` file also provides some configuration options for our smart contracts and Clarinet will add our contracts here when we create them. + +:::tip Hiro has created an excellent [Clarinet 101](https://www.youtube.com/playlist?list=PL5Ujm489LoJaAz9kUJm8lYUWdGJ2AnQTb) series on YouTube for getting up to speed on everything Clarinet has to offer. ::: + +Now we need to install some dependencies for working with Stacks and for styling so our app looks halfway decent. I'm a big fan of Tailwind, so we'll be utilizing that in this tutorial. + +Let's get Tailwind installed. We'll need some additional packages from Stacks.js as well, but we'll install those as we go. First make sure you are in the `frontend` directory and install the necessary dependencies for Tailwind. + +```bash +yarn add -D tailwindcss postcss autoprefixer +``` + +and initialize it with + +```bash +npx tailwindcss init -p +``` + +Now we need to tell Tailwind where our content is coming from and change out processing mode to "Just-In-Time" by modifying the `tailwind.config.js` file to look like this. + +```js +/** @type {import('tailwindcss').Config} */ +module.exports = { + mode: "jit", + content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], + theme: { + extend: {}, + }, + plugins: [], +}; +``` + +Add the Tailwind directives to the `index.css` file. + +```css +@tailwind base; +@tailwind components; +@tailwind utilities; +``` + +Now we need to modify our `App.jsx` file to make sure Tailwind is working. + +```js +function App() { + return ( +
+

Hello Stacks

+
+ ); +} + +export default App; +``` + +You can also delete the `App.css` file. + +Now run `yarn dev` and make sure everything is working. Your screen should look like this. + +Awesome! We have our set up out of the way, now we can start actually coding. We're going to start with our smart contract and learn a bit about Clarity in the process. + +### Writing Our Smart Contract + +Clarity is a smart contract language that has been purpose-built to help developers write safe, secure smart contracts. + +It took a lot of the lessons learned from the Solidity hacks over the years and used them to create something much more secure and safe. + +Stacks smart contracts are written using Clarity and live on the Stacks chain. For a comprehensive introduction to writing Clarity contracts, I highly recommend the book, [Clarity of Mind](https://book.clarity-lang.org/). We'll go over the basics here as we write a simple smart contract, but we are only scratching the surface. + +Before we do that, take a minute to familiarize yourself with what makes Clarity unique and why you might want to build on it by reading just the [Introduction chapter](https://book.clarity-lang.org/ch00-00-introduction.html) of Clarity of Mind. + +Now that you understand why Clarity is useful, let's get started writing our first contract and go over some Clarity concepts along the way. + +The first thing we need to do is generate our new smart contract with Clarinet. Make sure you are in the top level `contracts` folder and generate that. + +```bash +clarinet contract new hello-stacks +``` + +That created a few things for us. First, it created a `hello-stacks_test.ts` file in the `tests` directory. This is how we can use Clarinet to test our smart contracts. We won't cover that here, but you can check out the [TDD with Clarinet](https://dev.to/stacks/test-driven-stacks-development-with-clarinet-2e4i) tutorial for more information on that. + +It also created our actual Clarity file, `hello-stacks.clar` inside the inner `contracts` directory. This is where we'll actually write our smart contract. + +:::tip If you use VS Code, be sure to install the [Clarity Extension](https://marketplace.visualstudio.com/items?itemName=HiroSystems.clarity-lsp) for a more enjoyable Clarity developer experience. ::: + +If you open up the `Clarinet.toml` file, you'll also see that Clarinet added our new contract there. + +Now let's open up that `hello-stacks.clar` file and get writing our contract. + +You'll see that it set up a bit of a template we can use, with each section commented out. This is a good introduction to some of the common data structures and functions we'll be using in a Clarity contract. + +But we can delete it for now as we'll be writing our own and covering what each does along the way. + +Let's look at the very basic completed Clarity smart contract, then we'll go over what each line does. + +``` +(define-public (write-message (message (string-utf8 500))) + (begin + (print message) + (ok "Message printed") + ) +) +``` + +All we are doing here is defining a new public function called `write-message`. Public means that we can call this function from anywhere, as opposed to private, which would mean only this contract could call it. + +Whenever we call this function, we need to pass it a `message` which is a UTF8 string with a maximum of 500 characters. + +Next we see a block called `begin`. In Clarity, everything is a list inside of a list, so in this function definition, what we are really doing is calling the `define-public` function and passing it to arguments. + +The first is the function signature, which contains the name and the arguments. + +The second argument is the body of the function, but this can only contain a single expression. By wrapping our function body in a `begin` block, we can pass a multi-step function as a single expression. + +Again, there is much more information on how Clarity works in the [Clarity Book](https://book.clarity-lang.org/), this is just a high-level introduction to get you familiar. + +Within this `begin` block we are doing two things. First we are bringing the contents of our message to the blockchain, this will actually post the contents of this message to the chain when we send this transaction, something we'll look at soon. + +Then we are returning a response of `ok`, indicating success, with the message "Message printed". + +Clarity requires return values in all functions. In more complex functions, we would return either a success or error message depending on what action was taken and other logic. + +We can test this right now using the Clarinet console. In the `contracts` folder, run `clarinet console` to open it up and write the following: + +```bash +(contract-call? .hello-stacks write-message u"Hello there") +``` + +Here we are calling the function we just created and passing a message of "Hello there". We prefix that with a `u` to indicate this is a UTF8 string. + +You should get an `ok` message returned here, which indicates that our function was called successfully. + +### Running a Local Stacks Devnet + +One of the coolest features of Clarinet is that it allows us to set up an entire mock Stacks network on our local machine for testing. + +This comes complete with a local block explorer and sandbox environment so we can further test our contracts. + +Make sure you have Docker installed and running, exit out of console with `CMD/CTRL + C` and run `clarinet integrate`. + +It will take a few minutes to get up and running, but then we can open up our local Stacks block explorer. + +:::note You can [view the live Stacks block explorer](https://explorer.stacks.co/?chain=mainnet) and switch between mainnet and testnet to get familiar with it. ::: + +Once it finishes, visit the local block explorer at `localhost:8000`. You'll be able to see blocks as they are created and also see your `hello-stacks` contract deployment. + +Click on that and copy the contract name, we'll need it for the next step. + +Now go ahead and visit the sandbox by clicking on the 'Sandbox' link up at the top. You'll need to connect with your wallet to use it, but we can also interact with our contract here. + +Be sure to change your network to `Devnet` in your Hiro wallet, then connect. + +:::caution If you run into funky rendering issues, be sure to change the network URL parameter in the address bar to `testnet`. ::: + +Click on the little function symbol on the left and take the contract name you copied and paste it in the top field there, the name should be automatically populated. + +Hit 'Get Contract' and you should see the function we created there. + +Now we can click on that and type in a message to call the function. But if you try to do that now, you might get an error saying you don't have enough STX. + +How do we get STX tokens in our Devnet account? Remember that `Devnet.toml` file Clarinet generates? That determines what accounts have what tokens. + +So now we need to set up our Hiro wallet extension that we set up at the beginning of this tutorial. If you already have an existing Hiro wallet, you'll need to sign out in order to use it with the local Clarinet Devnet. + +:::warning Make sure you copy your secret key before you sign out. You'll need it to restore your Hiro wallet when you are done developing. ::: + +Go ahead and copy the mnemonic listed in the `Devnet.toml` file and use that to import your local wallet into the Hiro wallet. Then we can use that for interacting with our local Devnet chain. + +Once you do that, go ahead and restart `clarinet integrate`. + +Now we can go back to the sandbox and call that function. You might need to sign out of the sandbox first and reauthenticate. + +When we do, you'll see the Hiro wallet pop up with a transaction confirmation. You'll also see a little notice at the top that no transfers besides fees will occur. + +This is a cool feature of Stacks called Post Conditions. They are outside the scope of this tutorial, but you can learn more about them in the [Understanding Stacks Post Conditions](https://dev.to/stacks/understanding-stacks-post-conditions-e65) tutorial. They are another safety feature to help protect users. + +Now we can go back to the 'Transactions' page, click on this transaction, and see the data that was written to the chain. + +Here you can see a lot of information about the transaction, including the fact that a print event was detected, and what content was printed. + +Let's get our frontend set up so we can see how to do this with a UI and also read this information from the chain. + +### Adding a UI + +For this tutorial, we'll be using [Stacks.js](https://www.hiro.so/stacks-js), a JS library from Hiro that helps us interact with the Stacks chain, our Hiro wallet, and our contracts. Another option is [Micro-Stacks](https://micro-stacks.dev/), a community-created resource. + +Since this is not a React tutorial, I'm going to give you all the boilerplate at once so you can just copy and paste this into `App.jsx` and we'll add the Stacks-specific stuff together. + +```jsx +import { useState } from "react"; + +function App() { + const [message, setMessage] = useState(""); + const [transactionId, setTransactionId] = useState(""); + const [currentMessage, setCurrentMessage] = useState(""); + + const connectWallet = () => { + // implement code + }; + + const handleMessageChange = (e) => { + setMessage(e.target.value); + }; + + const submitMessage = () => { + // submit transaction + }; + + const handleTransactionChange = (e) => { + setTransactionId(e.target.value); + }; + + const retrieveMessage = () => { + // submit transaction + }; + + return ( +
+ +

Hello Stacks

+
+ + +
+
+ + +
+ {currentMessage.length > 0 ? ( +

{currentMessage}

+ ) : ( + "" + )} +
+ ); +} + +export default App; +``` + +We've got a very basic React application set up here that will allow us to add a new message to the Stacks chain and also find the content of a message by entering the transaction ID where the message was posted. + +The first thing we need to do is implement the 'Connect Wallet' functionality. + +In order to do that, we need to add the [`@stacks/connect`](https://github.com/hirosystems/connect/tree/main/packages/connect) package with + +```bash +yarn add @stacks/connect +``` + +:::note Some users get an error about the `regenerator-runtime` dependency being missing. If that's the case, running `yarn add regenerator-runtime` should fix the issue. ::: + +And import a couple things from it at the top of our `App.jsx` file. + +```jsx +import { + AppConfig, + UserSession, + AuthDetails, + showConnect, +} from "@stacks/connect"; +``` + +Now let's use `AppConfig` which is charge of setting some config options for the wallet to read and `UserSession`, which will actually handle the wallet authentication. + +Below the state declarations, add the following. + +```jsx +const appConfig = new AppConfig(["store_write"]); +const userSession = new UserSession({ appConfig }); +``` + +Here we are setting up an app that needs permission to store and write data to the Stacks chain, and we are instantiating a new user session with that config option passed in. + +We also need to add a few details for the Hiro wallet to display to people interacting with our app. We can do that with the following line: + +```jsx +const appDetails = { + name: "Hello Stacks", + icon: "https://freesvg.org/img/1541103084.png", +}; +``` + +We'll use this when we set up the connect function, which we can do right now in the `connectWallet` function. + +```jsx +const connectWallet = () => { + showConnect({ + appDetails, + onFinish: () => window.location.reload(), + userSession, + }); +}; +``` + +Here we are using the `showConnect` function to actually trigger the Hiro wallet to show up, allowing the user to authenticate. From there we are triggering a page refresh when the authentication finishes and setting the `userSession` variable, which handles the data for our logged in user. + +At this point you should be able to authenticate with the wallet and have the page refresh, although we can't really do anything with that yet. + +First we need to get our app to be able to read that data. + +We'll add another state variable for our `userData`. + +```jsx +const [userData, setUserData] = useState(undefined); +``` + +And we will also add a `useEffect` call to set this data on page load. + +```jsx +useEffect(() => { + if (userSession.isSignInPending()) { + userSession.handlePendingSignIn().then((userData) => { + setUserData(userData); + }); + } else if (userSession.isUserSignedIn()) { + setUserData(userSession.loadUserData()); + } +}, []); + +console.log(userData); +``` + +Now we have access to our authenticated user data, but we need to actually utilize it in our UI. + +The first thing we'll do is hide the 'Connect Wallet' button if there is currently an authenticated user. Change the code that renders our button to the following. + +```jsx +{ + !userData && ( + + ); +} +``` + +We also want to hide the form to submit a message if we are not authenticated. + +```jsx +{ + userData && ( +
+ + +
+ ); +} +``` + +Alright, now we need to actually implement the functionality that will call our `hello-stacks` contract. To do that, we need to use Stacks.js to send a transaction, we'll do that in the `submitMessage` function. + +First we need to install a couple more packages. + +```bash +yarn add @stacks/transactions @stacks/network +``` + +And import a couple things from those at the top. + +```jsx +import { + AppConfig, + UserSession, + showConnect, + openContractCall, +} from "@stacks/connect"; +import { StacksMocknet } from "@stacks/network"; +import { stringUtf8CV } from "@stacks/transactions"; +``` + +We're importing the network we'll be calling the transaction on and a utility helper from the `transactions` package that will help to encode our data in a format that the Clarity contract can understand. + +Now we need to add a new constant to hold the network that we are using. In our case we want to be using `Mocknet`, which we can add using the following right under our current state declarations. + +```jsx +const network = new StacksMocknet(); +``` + +And finally we can add the code to initiate the transaction. + +```jsx +const submitMessage = async (e) => { + e.preventDefault(); + + const network = new StacksMocknet(); + + const options = { + contractAddress: "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM", + contractName: "hello-stacks", + functionName: "write-message", + functionArgs: [stringUtf8CV(message)], + network, + appDetails, + onFinish: ({ txId }) => console.log(txId), + }; + + await openContractCall(options); +}; +``` + +What we are doing here is calling our contract (I got the address from the local block explorer) using the `openContractCall` function and passing in some options. + +The contract information, the function we want to call, our function arguments, the network, the app details from earlier, and an action we want to take when we finish calling. + +Note that all values passed to Clarity contracts need to be converted like this. You can see what all the various options are for doing so on the [GitHub page](https://github.com/hirosystems/stacks.js/tree/master/packages/transactions#constructing-clarity-values). + +And if you type a message and hit the submit button, you should see the transaction initiation window pop up. + +Here's our current `App.jsx` file in its entirety. + +```jsx +import { useEffect, useState } from "react"; +import { + AppConfig, + UserSession, + showConnect, + openContractCall, +} from "@stacks/connect"; +import { StacksMocknet } from "@stacks/network"; +import { stringUtf8CV } from "@stacks/transactions"; + +function App() { + const [message, setMessage] = useState(""); + const [transactionId, setTransactionId] = useState(""); + const [currentMessage, setCurrentMessage] = useState(""); + const [userData, setUserData] = useState(undefined); + + const appConfig = new AppConfig(["store_write"]); + const userSession = new UserSession({ appConfig }); + const appDetails = { + name: "Hello Stacks", + icon: "https://freesvg.org/img/1541103084.png", + }; + + useEffect(() => { + if (userSession.isSignInPending()) { + userSession.handlePendingSignIn().then((userData) => { + setUserData(userData); + }); + } else if (userSession.isUserSignedIn()) { + setUserData(userSession.loadUserData()); + } + }, []); + + console.log(userData); + + const connectWallet = () => { + showConnect({ + appDetails, + onFinish: () => window.location.reload(), + userSession, + }); + }; + + const handleMessageChange = (e) => { + setMessage(e.target.value); + }; + + const submitMessage = async (e) => { + e.preventDefault(); + + const network = new StacksMocknet(); + + const options = { + contractAddress: "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM", + contractName: "hello-stacks", + functionName: "write-message", + functionArgs: [stringUtf8CV(message)], + network, + appDetails, + onFinish: ({ txId }) => console.log(txId), + }; + + await openContractCall(options); + }; + + const handleTransactionChange = (e) => { + setTransactionId(e.target.value); + }; + + const retrieveMessage = () => { + // submit transaction + }; + + return ( +
+ {!userData && ( + + )} +

Hello Stacks

+ {userData && ( +
+ + +
+ )} +
+ + +
+ {currentMessage.length > 0 ? ( +

{currentMessage}

+ ) : ( + "" + )} +
+ ); +} + +export default App; +``` + +If you click 'Confirm' you should see the transaction id logged in the console and you can see the transaction in the block explorer. + +### Retrieving Messages + +Now that we can write a message to the chain, we also want to add functionality so that we can retrieve a message from the chain. + +We'll do that by having users enter in a transaction ID and we'll query the chain for that transaction and the event data associated with it. + +The Stacks API is perfect for this, and we can query our local devnet chain directly. + +Let's set that up in our `retrieveMessage` function. + +```jsx +const retrieveMessage = async () => { + const retrievedMessage = await fetch( + "http://localhost:3999/extended/v1/tx/events?" + + new URLSearchParams({ + tx_id: transactionId, + }) + ); + const responseJson = await retrievedMessage.json(); + setCurrentMessage(responseJson.events[0].contract_log.value.repr); +}; +``` + +Here we are using `fetch` to call the Stacks API and passing our transaction as a query parameter to get the events and then digging down through that returned data to get the printed message. + +You can view the documentation for this specific API call on [Hiro's website](https://docs.hiro.so/api#tag/Transactions/operation/get\_filtered\_events). + +If you run this you should see the message printed at the bottom of the screen. + +### Wrapping Up and Next Steps + +Here we've just gone over a very simple and very brief example of a Stacks application. This was meant to give you a high level introduction to the Stacks ecosystem and how you might begin building Stacks dapps. + +There is obviously a lot more to learn, so here are a few good places to continue your learning. + +#### Stacks Academy + +Stacks Academy is your guide to all things Stacks. This comprehensive walkthrough will cover key concepts about Stacks so you can learn everything you need to know about how it works. + +View Stacks Academy + +#### Clarity Book + +The Clarity Book, Clarity of Mind, is the go-to resource for mastering Clarity. It will teach you Clarity development from start to finish so you can begin writing high-quality Clarity smart contracts. + +[Read the Clarity Book](https://book.clarity-lang.org/) + +#### Clarity Universe + +Prefer a more immersive experience? Clarity Universe is a start-to-finish guide to learning Clarity, with self-paced option and a guided cohort-based option. + +[Enroll in Clarity Universe](https://clarity-lang.org/universe) + +#### Community Tutorials + +There is an ever-growing list of tutorials created by members of the Stacks community so you can learn how to accomplish different tasks and build useful things with Stacks. + +View Community Tutorials + +#### Next Steps + +Looking to get paid to build something awesome with Stacks? Be sure to see all the different opportunities available to you like full-time jobs, grants, the startup accelerator, and more. + +Next Steps + +#### Get Involved + +Finally, be sure to get involved in the Stacks community by [joining Discord](https://discord.gg/5DJaBrf) and [checking out the website](https://stacks.co) to learn more about Stacks. diff --git a/tutorials/tokens/README.md b/tutorials/tokens/README.md new file mode 100644 index 0000000000..1a6bc52a92 --- /dev/null +++ b/tutorials/tokens/README.md @@ -0,0 +1,3 @@ +# Tokens + +Rather than needing to work with external libraries, Clarity has built-in functions that make working with fungible and non-fungible tokens a breeze. diff --git a/tutorials/tokens/creating-a-fungible-token.md b/tutorials/tokens/creating-a-fungible-token.md new file mode 100644 index 0000000000..1761d3e21c --- /dev/null +++ b/tutorials/tokens/creating-a-fungible-token.md @@ -0,0 +1,96 @@ +# Creating a Fungible Token + +Much the same as creating an NFT, Clarity also makes creating a fungible token (FT) trivial as well. + +The process and code are much the same. + +### Trait + +Just like with the NFT, it's a good idea to create a trait when you create a fungible token so that different tools and protocols and be confident in building interfaces for those tokens. + +Since FTs may be divisible, the [FT official mainnet deployment trait address](https://explorer.stacks.co/txid/0x99e01721e57adc2c24f7d371b9d302d581dba1d27250c7e25ea5f241af14c387?chain=mainnet) has additional functions. + +```clarity +(define-trait sip-010-trait + ( + ;; Transfer from the caller to a new principal + (transfer (uint principal principal (optional (buff 34))) (response bool uint)) + + ;; the human readable name of the token + (get-name () (response (string-ascii 32) uint)) + + ;; the ticker symbol, or empty if none + (get-symbol () (response (string-ascii 32) uint)) + + ;; the number of decimals used, e.g. 6 would mean 1_000_000 represents 1 token + (get-decimals () (response uint uint)) + + ;; the balance of the passed principal + (get-balance (principal) (response uint uint)) + + ;; the current total supply (which does not need to be a constant) + (get-total-supply () (response uint uint)) + + ;; an optional URI that represents metadata of this token + (get-token-uri () (response (optional (string-utf8 256)) uint)) + ) +) +``` + +Now let's see how we might implement this in Clarity, just like we would an NFT. + +### Clarity Code + +```clarity +(impl-trait 'SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE.sip-010-trait-ft-standard.sip-010-trait) + + +(define-constant contract-owner tx-sender) +(define-constant err-owner-only (err u100)) +(define-constant err-not-token-owner (err u101)) + +;; No maximum supply! +(define-fungible-token clarity-coin) + +(define-public (transfer (amount uint) (sender principal) (recipient principal) (memo (optional (buff 34)))) + (begin + (asserts! (is-eq tx-sender sender) err-not-token-owner) + (try! (ft-transfer? clarity-coin amount sender recipient)) + (match memo to-print (print to-print) 0x) + (ok true) + ) +) + +(define-read-only (get-name) + (ok "Clarity Coin") +) + +(define-read-only (get-symbol) + (ok "CC") +) + +(define-read-only (get-decimals) + (ok u6) +) + +(define-read-only (get-balance (who principal)) + (ok (ft-get-balance clarity-coin who)) +) + +(define-read-only (get-total-supply) + (ok (ft-get-supply clarity-coin)) +) + + +(define-read-only (get-token-uri) + (ok none) +) + + +(define-public (mint (amount uint) (recipient principal)) + (begin + (asserts! (is-eq tx-sender contract-owner) err-owner-only) + (ft-mint? clarity-coin amount recipient) + ) +) +``` diff --git a/tutorials/tokens/creating-a-nft.md b/tutorials/tokens/creating-a-nft.md new file mode 100644 index 0000000000..7d35e3aa3c --- /dev/null +++ b/tutorials/tokens/creating-a-nft.md @@ -0,0 +1,82 @@ +# Creating a NFT + +Clarity makes creating NFTs incredibly easy. With built-in functions for creating and working with the token, you can have an NFT created in less than 10 minutes of work. + +Let's see how. + +:::tip For a more in-depth discussion of NFTs in Clarity and how to create them, check out the [NFTs chapter in the Clarity book](https://book.clarity-lang.org/ch10-01-sip009-nft-standard.html). ::: + +### Trait + +The first thing we need when we create an NFT is a trait. A trait is an interface that allows us to create an NFT with a defined set of functions. Its primary purpose is to ensure that NFTs are composable and different tools know how to interact with them. + +By implementing a trait that the community agrees on, all protocols and products know how they can interact with an NFT. + +The official mainnet trait can be [found on the Stacks Explorer](https://explorer.stacks.co/txid/0x80eb693e5e2a9928094792080b7f6d69d66ea9cc881bc465e8d9c5c621bd4d07?chain=mainnet) and looks like this: + +```clarity +(define-trait nft-trait + ( + ;; Last token ID, limited to uint range + (get-last-token-id () (response uint uint)) + + ;; URI for metadata associated with the token + (get-token-uri (uint) (response (optional (string-ascii 256)) uint)) + + ;; Owner of a given token identifier + (get-owner (uint) (response (optional principal) uint)) + + ;; Transfer from the sender to a new principal + (transfer (uint principal principal) (response bool uint)) + ) +) +``` + +All we are doing here is defining the function signatures for functions we'll need to implement in out Clarity contract, which we can see a simple version of below. + +### Clarity Code + +This is the Clarity code we need in order to create an NFT, with one additional function, `mint` that allows us to actually create a new NFT. This `mint` function is not needed to adhere to the trait. + +```clarity +(impl-trait 'SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.nft-trait.nft-trait) + +(define-non-fungible-token amazing-aardvarks uint) + +(define-data-var last-token-id uint u0) + +(define-constant contract-owner tx-sender) +(define-constant err-owner-only (err u100)) +(define-constant err-not-token-owner (err u101)) + +(define-read-only (get-last-token-id) + (ok (var-get last-token-id)) +) + +(define-read-only (get-token-uri (token-id uint)) + (ok none) +) + +(define-read-only (get-owner (token-id uint)) + (ok (nft-get-owner? amazing-aardvarks token-id)) +) + +(define-public (transfer (token-id uint) (sender principal) (recipient principal)) + (begin + (asserts! (is-eq tx-sender sender) err-not-token-owner) + (nft-transfer? amazing-aardvarks token-id sender recipient) + ) +) + +(define-public (mint (recipient principal)) + (let + ( + (token-id (+ (var-get last-token-id) u1)) + ) + (asserts! (is-eq tx-sender contract-owner) err-owner-only) + (try! (nft-mint? amazing-aardvarks token-id recipient)) + (var-set last-token-id token-id) + (ok token-id) + ) +) +```