Browse Source

chore: removed Stacks 1.0 documentation

fix/wrong-prop-name
Patrick Gray 4 years ago
committed by Patrick Gray
parent
commit
e1e1bf77ac
  1. 9
      src/common/navigation.yaml
  2. 178
      src/pages/understand-stacks/atlas-how-it-works.md
  3. 121
      src/pages/understand-stacks/atlas-overview.md
  4. 119
      src/pages/understand-stacks/atlas-usage.md
  5. 62
      src/pages/understand-stacks/best-practices.md
  6. 44
      src/pages/understand-stacks/installing-memcached.md
  7. 10
      src/pages/understand-stacks/stacks-1.0-info.md
  8. 550
      src/pages/understand-stacks/wire-format.md

9
src/common/navigation.yaml

@ -26,15 +26,6 @@ sections:
- path: /integrate-stacking-delegation
- path: /stacking-using-CLI
- title: Stacks 1.0
pages:
- path: /stacks-1.0-info
- path: /best-practices
- path: /wire-format
- path: /atlas-overview
- path: /atlas-how-it-works
- path: /atlas-usage
- path: /installing-memcached
- path: /write-smart-contracts
pages:
- path: /overview

178
src/pages/understand-stacks/atlas-how-it-works.md

@ -1,178 +0,0 @@
---
title: How Atlas works
description: Atlas was designed to overcome the structural weaknesses inherent to all distributed hash tables.
---
## Introduction
Atlas was designed to overcome the structural weaknesses inherent to all
distributed hash tables. In particular, it uses an unstructured peer network to
maximize resilience against network link failure, and it uses the underlying
blockchain (through BNS) to rate-limit chunk announcements.
## Peer Selection
Atlas peers self-organize into an unstructured peer-to-peer network.
The Atlas peer network is a [random K-regular
graph](https://en.wikipedia.org/wiki/Random_regular_graph). Each node maintains
_K_ neighbors chosen at random from the set of Atlas peers.
Atlas nodes select peers by carrying out an unbiased random walk of the peer
graph. When "visiting" a node _N_, it will ask for _N_'s neighbors and then
"step" to one of them with a probability dependent on _N_'s out-degree and the
neighbor's in-degree.
The sampling algorithm is based on the Metropolis-Hastings (MH) random graph walk
algorithm, but with a couple key differences. In particular, the algorithm
attempts to calculate an unbiased peer graph sample that accounts for the fact
that most nodes will be short-lived or unreliable, while a few persistent nodes
will remain online for long periods of time. The sampling algorithm accounts
for this with the following tweaks:
- If the neighbors of the visited node _N_ are all unresponsive, the random
walk resets to a randomly chosen known neighbor. There is no back-tracking on
the peer graph in this case.
- The transition probability from _N_ to a live neighbor is _NOT_ `min(1, degree(neighbor)/degree(N))` like it is in the vanilla MH algorithm. Instead,
the transition probability discourages backtracking to the previous neighbor _N_prev_,
but in a way that still guarantees that the sampling will remain unbiased.
- A peer does not report its entire neighbor set when queried,
but only reports a random subset of peers that have met a minimum health threshold.
- A new neighbor is only selected if it belongs to the same [BNS
fork-set](/naming-services/overview#bns-forks) (for example, it reports
as having a recent valid consensus hash).
The algorithm was adapted from the work from [Lee, Xu, and
Eun](https://arxiv.org/pdf/1204.4140.pdf) in the proceedings of
ACM SIGMETRICS 2012.
## Comparison to DHTs
The reason Atlas uses an unstructured random peer network
instead of a [distributed hash table](https://en.wikipedia.org/wiki/Distributed_hash_table)
(DHT) is that DHTs are susceptible to Sybil attacks. An adaptive adversary can
insert malicious nodes into the DHT in order to stop victims from
resolving chunks or finding honest neighbors.
### Chunk Censorship
In a DHT, an attacker can censor a chunk by inserting nodes into the peers' routing tables
such that the attacker takes control over all of the chunk's hash buckets.
It can do so at any point in time after the chunk was first stored,
because only the peers who maintain the chunk's hash bucket have to store it.
This is a _fundamental_ problem with structured overlay networks
that perform request routing based on content hash---they give the attacker
insight as to the paths the queries take through the peer graph, and thus
reduce the number of paths the attacker must disrupt in order to censor the
chunk.
Atlas uses an unstructured overlay network combined with a 100% chunk
replication strategy in order to maximize
the amount of work an adversary has to do to censor a chunk.
In Atlas, all peers replicate a chunk, and the paths the chunk take through the
network are _independent_ of the content and _randomized_ by the software
(so the paths cannot be predicted in advance). The attacker's only
recourse is to quickly identify the nodes that can serve the chunk and partition them from
the rest of the network in order to carry out a censorship attack.
This requires them to have visibility into the vast majority of network links in
the Atlas network (which is extremely difficult to do, because in practice Atlas
peers maintain knowledge of up to 65536 neighbors and only report 10 random peers
when asked).
### Neighbor Censorship
Another problem with DHTs is that their overlay
network structure is determined by preferential attachment. Not every peer that
contacts a given DHT node has an equal chance of becoming its neighbor.
The node will instead rank a set of peers as being more or less ideal
for being neighbors. In DHTs, the degree of preference a node exhibits to
another node is usually a function of the node's self-given node identifier
(for example a node might want to select neighbors based on proximity in the key
space).
The preferential attachment property means that an adaptive adversary can game the node's
neighbor selection algorithm by inserting malicious nodes that do not
forward routing or lookup requests. The attacker does not even have to eclipse
the victim node---the victim node will simply prefer to talk to the attacker's unhelpful nodes
instead of helpful honest nodes. In doing so, the attacker can prevent honest peers from discovering each
other and each other's chunks.
Atlas's neighbor selection strategy does not exhibit preferential attachment
based on any self-reported node properties. A
node is selected as a neighbor only if it is reached through an unbiased random graph
walk, and if it responds to queries correctly.
In doing so, an attacker is forced to completely eclipse a set of nodes
in order to cut them off from the rest of the network.
## Chunk Propagation
Atlas nodes maintain an _inventory_ of chunks that are known to exist. Each
node independently calculates the chunk inventory from its BNS database.
Because the history of name operations in BNS is linearized, each node can
construct a linearized sub-history of name operations that can set chunk
hashes as their name state. This gives them a linearized sequence of chunks,
and every Atlas peer will independently arrive at the same sequence by reading
the same blockchain.
Atlas peers keep track of which chunks are present and which are absent. They
each construct an _inventory vector_ of chunks _V_ such that _V[i]_ is set to 1
if the node has the chunk whose hash is in the *i*th position in the chunk
sequence (and set to 0 if it is absent).
Atlas peers exchange their inventory vectors with their neighbors in order to
find out which chunks they each have. Atlas nodes download chunks from
neighbors in rarest-first order in order to prioritize data replication for the
chunks that are currently most at-risk for disappearing due to node failure.
```
Name operation | chunk hashes | chunk data | Inventory
history | as name state | | vector
+-------------------+
| NAME_PREORDER |
+-------------------+----------------+
| NAME_REGISTRATION | chunk hash | "0123abcde..." 1
+-------------------+----------------+
| NAME_UPDATE | chunk hash | (null) 0
+-------------------+----------------+
| NAME_TRANSFER |
+-------------------+
| NAME_PREORDER |
+-------------------+----------------+
| NAME_IMPORT | chunk hash | "4567fabcd..." 1
+-------------------+----------------+
| NAME_TRANSFER |
+-------------------|
. . .
Figure 2: Relationship between Atlas node chunk inventory and BNS name state.
Some name operations announce name state in the blockchain, which Atlas
interprets as a chunk hash. The Atlas node builds up a vector of which chunks
it has and which ones it does not, and announces it to other Atlas peers so
they can fetch chunks they are missing. In this example, the node's
inventory vector is [1, 0, 1], since the 0th and 2nd chunks are present
but the 1st chunk is missing.
```
## Querying Chunk Inventories
Developers can query a node's inventory vector as follows:
```py
>>> import blockstack
>>> result = blockstack.lib.client.get_zonefile_inventory("https://node.blockstack.org:6263", 0, 524288)
>>> print len(result['inv'])
11278
>>>
```
The variable `result['inv']` here is a big-endian bit vector, where the *i*th
bit is set to 1 if the *i*th chunk in the chunk sequence is present. The bit at
`i=0` (the earliest chunk) refers to the leftmost bit.
A sample program that inspects a set of Atlas nodes' inventory vectors and determines
which ones are missing which chunks can be found
[here](https://github.com/blockstack/atlas/blob/master/atlas/atlas-test).

121
src/pages/understand-stacks/atlas-overview.md

@ -1,121 +0,0 @@
---
title: Overview of the Atlas network
description: This document describes the Atlas network, a peer-to-peer content-addressed storage system whose chunks' hashes are announced on a public blockchain.
---
## Introduction
This document describes the Atlas network, a peer-to-peer content-addressed
storage system whose chunks' hashes are announced on a public blockchain. Atlas
allows users and developers to **permanently store** chunks of data that are
**replicated across every peer.** As long as at least one Atlas peer is online,
all chunks are available to clients.
This document is aimed at developers and technical users.
The reader of this document is expected to be familiar with the [Blockchain Naming Service (BNS)](/build-apps/references/bns),
as well as Stacks's storage system [Gaia](https://github.com/blockstack/gaia). We advise the reader
to familiarize themselves with both systems before approaching this document.
## Architecture
Atlas is designed to integrate with BNS in order to allow users to
store name state off-chain, encoded as a DNS zone file.
The common use-cases in Stacks are:
- Storing a name's routing information for its owners' [Gaia](https://github.com/blockstack/gaia)
datastores.
- Storing BNS subdomain transactions and associated state.
Atlas is a middleware system in Stacks. Most developers do not
interact with it directly. BNS clients like the
[Blockstack Browser](https://github.com/blockstack/blockstack-browser)
automatically generate zone files for the names they register, and automatically
propagate them to the Atlas network. BNS API endpoints, including our
[public endpoint](https://core.blockstack.org) and the
[blockstack.js](https://github.com/blockstack/blockstack.js) library,
will automatically fetch zone files from Atlas when they need to look
up data in Gaia (such as profiles and app data).
```
+--------------+ +---------------+ +----------------+
clients |Authenticator | | Stacks.js | | BNS API module |
+--------------+ +---------------+ +----------------+
^ ^ ^ ^ ^ ^
| | | | | |
| | | | | |
V | V | V |
+----------+ | +----------+ | +----------+ |
Gaia | Gaia hub | | | Gaia hub | | | Gaia hub | |
+----------+ | +----------+ | +----------+ |
| | |
| | |
V V V
+---------------------------------------------------------------+
Atlas | Atlas Peer Network |
+----------+------+----------+-----+----------+------+----------+
BNS | BNS node | | BNS node | | BNS node | | BNS node |
+----------+ +----------+ +----------+ +----------+
^ ^ ^ ^
| (indexing | | |
| blockchain) | | |
+---------------------------------------------------------------+
Blockchain | Blockchain Peer Network |
+---------------------------------------------------------------+
Figure 1: Location of Atlas in the Stacks architecture Each BNS node
implements an Atlas peer An Atlas peer treats a name state value in BNS as
the hash of a DNS zone file Atlas peers exchange zone files with one another
until they each have a full replica of all known zone files Clients can look
up zone files for names using the name's stat value as a zone file hash Clients
can broadcast zone files to the network if they match a previously announced
hash In practice, zone files store URLs to a name owner's Gaia hubs, thereby
allowing Stacks apps to read and write data in Gaia.
```
Nevertheless, Atlas is a general-purpose content-addressed storage
system that advanced developers can use to **host data in an immutable
and durable manner.** Beyond its default use-case in Stacks,
Atlas is ideal for tasks like:
- Announcing PGP public keys under a human-readable name
- Storing package hashes for a software release
- Securely deploying shell scripts to remote VMs
- Binding human-readable names to Tor .onion addresses
([example](https://github.com/blockstack-packages/blockstack-tor))
## Motivation
Atlas was designed to augment BNS. BNS allows each name to store a small
amount of state---on the order of 20 bytes. The size is so small because the
state must be recorded to a public blockchain, where the cost per byte is
high and the blockchain protocol limits the size of transactions.
To compensate for this, we developed an off-chain storage system allows BNS
names to bind and store a large amount of state to each name in a way that
_preserves the security properties of having written that state to the
blockchain_. Instead of storing 20 bytes of data on the blockchain, a BNS name
owner would store the _cryptographic hash_ of its state, and then store the actual state
Atlas. This decouples the name's state size from the blockchain.
The reference implementation of Atlas currently allows up to 40kb of state to be
bound to a BNS name, instead of a measly 20 bytes. The 40kb of data is
replicated to each BNS node, where it is stored forever.
## Feature Comparison
Atlas is not the only peer-to-peer content-addressible chunk store in existence. The following
feature table describes Atlas in relation to other popular chunk stores.
| **Features** | Atlas | BitTorrent | [DAT](https://datproject.org/) | [IPFS](https://ipfs.io) | [Swarm](https://github.com/ethersphere/swarm) |
| ------------------------------- | ----- | ---------- | ------------------------------ | ----------------------- | --------------------------------------------- |
| Each peer stores all chunks | X | X | | | |
| Replicas are permanent [^1] | X | X | X | | |
| Replicas are free | | X | X | X | |
| Sybil-resistant chunk discovery | X | X | | | X |
| Sybil-resistant peer discovery | X | | | | |
| Fixed chunk size | X | | X | X | X |
[^1] Here, "permanent" means that once a peer has data, they will never evict it
as part of the protocol.

119
src/pages/understand-stacks/atlas-usage.md

@ -1,119 +0,0 @@
---
title: How to use the Atlas network
description: This section teaches you how to use the Atlas network.
---
## The API
While the Stacks software stack expects that Atlas-hosted data is made up of
DNS zone files, Atlas itself does not enforce this (nor does it care about the
format of its chunks). It is designed as a general-purpose chunk store.
Nevertheless, the ubiquitous use of Atlas to store data as DNS zone files has
had an influence on its API design---fields and method names frequently allude
to zone files and zone file hashes. This is intentional.
The [public BNS API endpoint](https://core.blockstack.org) does not support
resolving Atlas chunks that do not encode Gaia routing information or subdomain
information. To directly interact with Atlas, developers will need to install
[Stacks Node](https://github.com/blockstack/blockstack-core) and use its
Python client libraries for these examples.
## Looking up Chunks
All Atlas chunks are addressed by the RIPEMD160 hash of the SHA256 hash of the
chunk data. A client can query up to 100 chunks in one RPC call.
A client can look up a chunk with the `get_zonefiles()` method. If successful,
the returned payload will be a `dict` with a `zonefiles` key that maps the chunk
hashes to their respective data.
```py
>>> import blockstack
>>> data = blockstack.lib.client.get_zonefiles('https://node.blockstack.org:6263', ['1b89a685f4c4ea245ce9433d0b29166c22175ab4'])
>>> print data['zonefiles']['1b89a685f4c4ea245ce9433d0b29166c22175ab4']
$ORIGIN duckduckgo_tor.id
$TTL 3600
tor TXT "3g2upl4pq6kufc4m.onion"
>>>
```
(This particular chunk happens to be associated with the BNS name
`duckduckgo_tor.id`).
## Adding a New Chunk
The only way to add a chunk to Atlas is to do so through an on-chain name in
BNS. Adding a new chunk is a two-step process:
- The name owner announces the chunk hash as a name's state
via a `NAME_REGISTRATION`, `NAME_UPDATE`, `NAME_RENEWAL`, or `NAME_IMPORT` transaction.
- Once the transaction is confirmed and processed by BNS, the name owner
broadcasts the matching zone file.
Setting a name's state to be the hash of a chunk is beyond the scope of this
document, since it needs to be done through a BNS client.
See the relevant documentation for
[blockstack.js](https://github.com/blockstack/blockstack.js) and the [Blockstack
Browser](https://github.com/blockstack/blockstack-browser) for doing this.
Once the name operation is confirmed, you can announce the data to the
Atlas network. You can do so with the Python client as follows:
```py
>>> import blockstack
>>> import base64
>>> data = "..." # this is the chunk data you will announce
>>> data_b64 = base64.b64encode(data)
>>> result = blockstack.lib.client.put_zonefiles('https://node.blockstack.org:6263', [data_b64])
>>> assert result['saved'][0] == 1
>>>
```
At most five chunks can be announced in one RPC call.
Note that the data must be base64-encoded before it can be announced.
When the `put_zonefiles()` method succeeds, it returns a `dict` with a list
under the `saved` key. Here, `result['saved'][i]` will be 1 if the `i`th
chunk given to `put_zonefiles()` was saved by the node, and 0 if not.
The node will not save a chunk if it is too big, or if it has not yet processed
the name operation that contained the chunk's hash.
The `put_zonefiles()` method is idempotent.
## Propagating Chunks
Atlas peers will each store a copy of the chunks you announce. In the
background, they will asynchronously announce to one another which chunks they
have available, and replicate them to one another in a rarest-first order (much
like how BitTorrent works). Eventually, every Atlas peer will receive the
chunk.
However, developers can accelerate this process by eagerly propagating chunks.
To do so, they can ask an Atlas peer for its immediate neighbors in the Atlas
peer graph, and replicate the chunk to each of them as well.
For example, this code will replicate the chunk to not only
`https://node.blockstack.org:6263`, but also to its immediate neighbors.
```py
>>> import blockstack
>>> import base64
>>> data = "..." # this is the chunk you will replicate widely
>>> data_b64 = base64.b64encode(data)
>>>
>>> result = blockstack.lib.client.get_atlas_peers('https://node.blockstack.org:6263')
>>> neighbors = result['peers']
>>> print ", ".join(neighbors)
13.65.207.163:6264, 52.225.128.191:6264, node.blockstack.org:6264, 23.102.162.7:6264, 52.167.230.235:6264, 23.102.162.124:6264, 52.151.59.26:6264, 13.92.134.106:6264
>>>
>>> for neighbor in neighbors:
.. result = blockstack.lib.client.put_zonefiles(neighbor, [data_b64])
... assert result['saved'][0] == 1
...
>>>
```
This is not strictly necessary, but it does help accelerate chunk replication
and makes it less likely that a chunk will get lost due to individual node
failures.

62
src/pages/understand-stacks/best-practices.md

@ -1,62 +0,0 @@
---
title: Best practices
description: Helpful tips for getting a core node up and running.
---
## Hardware and OS requirements
- A 64-bit CPU running at at least 1 GHz is _highly_ recommended (but not strictly required)
- You will need ~250 MB RAM and ~10 GB disk free. Do **not** attempt to use a network-attached disk for this.
- You should have at least 30,000 inodes free in your filesystem. Unless you are using a very small VM image, you almost certainly have enough (you can check with `df -i`).
- TCP port 6264 should be open and support bidirectional traffic. If you want to use SSL, then port 6263 should be open.
- A reliable Internet connection of DSL-like quality or higher
## Deployment
### The Easy Way
- Install from `pip`, source code, or Docker
- Run `blockstack-core fast_sync`
- Run `blockstack-core start`
### The Less Easy Way
- Install from `pip`, source code, or Docker
- Run `blockstack-core start`
- Wait a few days
#### Best Practices for the Less Easy Way
- Take a `blockstack-server.snapshots` database from a known-good node and pass `--expected_snapshots=/path/to/blockstack-server.snapshots`. This will force your bootstrapping node to verify that it reaches the same sequence of consensus hashes as it bootstraps (that is, your node will detect any divergence from Blockstack's name history and abort early, instead of wasting your time).
- Make sure you're in a position to leave the node online at 100% CPU use for the duration of its bootstrapping period
### The Hard Way
- Install `bitcoind` (version 0.16.x is recommended for now)
- Start `bitcoind` as `bitcoind -daemon -txindex=1`
- Wait for `bitcoind` to download the entire blockchain. This can take between 1 hour and 1 day.
- Install `blockstack-core` from source, `pip`, or Docker
- Run `blockstack-core configure` and enter your `bitcoind` node's IP address, port, RPC username, and RPC password when prompted
- Run `blockstack-core start`
- Wait a few days
#### Best Practices for the Hard Way
- You're going to need ~500 GB of space for the Bitcoin blockchain state
- You can safely store its chain state on a network-attached disk, if you're doing this in a cloud-hosted environment
- Your `bitcoind` host will need TCP:8332-8333 open for bidirectional traffic
## Troubleshooting
### The node stops responding to TCP:6264
- Check `dmesg` for TCP SYN flooding. The solution here is to kill and restart the node.
- To mitigate, install a rate-limiting proxy HTTP server in front of the node. We have a sample config for `nginx` [here](https://github.com/blockstack/atlas/blob/master/public_fleet/node/default).
### No other Stacks nodes contact my node
- Verify that your IP address is publicly routable, and that peers can communicate on TCP:6264
### People are attacking my Bitcoin node
- Stick an `nginx` reverse proxy in front of your `bitcoind` node, and use our [nginx](https://github.com/blockstack/atlas/tree/master/public_fleet/bitcoind) scripts to limit API access to only the JSON-RPC methods Stacks actually needs. Better yet, do what we do---build a statically linked `bitcoind` binary from source that simply omits all of the RPC methods except the ones listed in the linked config file.

44
src/pages/understand-stacks/installing-memcached.md

@ -1,44 +0,0 @@
---
title: Installing Memcached
description: Learn how to install Memcached to improve performance of a local Stacks API instance.
---
The Stacks API optionally uses memcached and pylibmc for scaling read-only
calls. If you want to enable this functionality then you should have memcached
running locally.
### Memcached on Debian & Ubuntu:
```bash
sudo apt-get install -y python-dev libmemcached-dev zlib1g-dev
pip install pylibmc
```
### Memcached on macOS:
Easiest way to install memcached on macOS is by using [Homebrew](https://brew.sh/).
After installing Homebrew:
```bash
brew install memcached
brew install libmemcached
pip install pylibmc --install-option="--with-libmemcached=/usr/local/Cellar/libmemcached/1.0.18_1/"
```
After installing, you can start memcached and check if it's running properly:
```bash
memcached -d
echo stats | nc localhost 11211
```
### Memcached on Heroku
To deploy on Heroku:
```bash
heroku create
heroku addons:add memcachedcloud
git push heroku master
```

10
src/pages/understand-stacks/stacks-1.0-info.md

@ -1,10 +0,0 @@
---
title: Stacks 1.0 Info
description: Get familiar with the old Stacks 1.0 architecture
---
## Information
Stacks 1.0 is an older blockchain architecture Stacks.
!> Stacks 1.0 architecture is not compatible with Stacks 2.0. The information in this chapter is in reference to **deprecated** implementation details.

550
src/pages/understand-stacks/wire-format.md

@ -1,550 +0,0 @@
---
title: Bitcoin wire format
description: Learn about the format of transactions used for name operations in the Stacks network.
---
## Introduction
This page is for organizations who want to be able to create and send name operation transactions to the blockchains Stacks supports.
It describes the transaction formats for the Bitcoin blockchain.
## Transaction format
Each Bitcoin transaction for Stacks contains signatures from two sets of keys: the name owner, and the payer. The owner `scriptSig` and `scriptPubKey` fields are generated from the keys that own the given name. The payer `scriptSig` and `scriptPubKey` fields are used to _subsidize_ the operation. The owner keys do not pay for any operations; the owner keys only control the minimum amount of BTC required to make the transaction standard. The payer keys only pay for the transaction's fees, and (when required) they pay the name fee.
This construction is meant to allow the payer to be wholly separate from the owner. The principal that owns the name can fund their own transactions, or they can create a signed transaction that carries out the desired operation and request some other principal (for example a parent organization) to actually pay for and broadcast the transaction.
The general transaction layout is as follows:
| **Inputs** | **Outputs** |
| ------------------------ | ------------------------- |
| Owner scriptSig (1) | `OP_RETURN <payload>` (2) |
| Payment scriptSig | Owner scriptPubKey (3) |
| Payment scriptSig... (4) |
| ... (4) | ... (5) |
(1) The owner `scriptSig` is _always_ the first input.
(2) The `OP_RETURN` script that describes the name operation is _always_ the first output.
(3) The owner `scriptPubKey` is _always_ the second output.
(4) The payer can use as many payment inputs as they like.
(5) At most one output will be the "change" `scriptPubKey` for the payer.
Different operations require different outputs.
## Payload Format
Each Stacks transaction in Bitcoin describes the name operation within an `OP_RETURN` output. It encodes name ownership, name fees, and payments as `scriptPubKey` outputs. The specific operations are described below.
Each `OP_RETURN` payload _always_ starts with the two-byte string `id` (called the "magic" bytes in this document), followed by a one-byte `op` that describes the operation.
### NAME_PREORDER
Op: `?`
Description: This transaction commits to the _hash_ of a name. It is the first
transaction of two transactions that must be sent to register a name in BNS.
Example: [`6730ae09574d5935ffabe3dd63a9341ea54fafae62fde36c27738e9ee9c4e889`](https://www.blocktrail.com/BTC/tx/6730ae09574d5935ffabe3dd63a9341ea54fafae62fde36c27738e9ee9c4e889)
`OP_RETURN` wire format:
```
0 2 3 23 39
|-----|--|--------------------------------------------------|--------------|
magic op hash_name(name.ns_id,script_pubkey,register_addr) consensus hash
```
Inputs:
- Payment `scriptSig`'s
Outputs:
- `OP_RETURN` payload
- Payment `scriptPubkey` script for change
- `p2pkh` `scriptPubkey` to the burn address (0x00000000000000000000000000000000000000)
Notes:
- `register_addr` is a base58check-encoded `ripemd160(sha256(pubkey))` (that is, an address). This address **must not** have been used before in the underlying blockchain.
- `script_pubkey` is either a `p2pkh` or `p2sh` compiled Bitcoin script for the payer's address.
### NAME_REGISTRATION
Op: `:`
Description: This transaction reveals the name whose hash was announced by a
previous `NAME_PREORDER`. It is the second of two transactions that must be
sent to register a name in BNS.
Example: [`55b8b42fc3e3d23cbc0f07d38edae6a451dfc512b770fd7903725f9e465b2925`](https://www.blocktrail.com/BTC/tx/55b8b42fc3e3d23cbc0f07d38edae6a451dfc512b770fd7903725f9e465b2925)
`OP_RETURN` wire format (2 variations allowed):
Variation 1:
```
0 2 3 39
|----|--|-----------------------------|
magic op name.ns_id (37 bytes)
```
Variation 2:
```
0 2 3 39 59
|----|--|----------------------------------|-------------------|
magic op name.ns_id (37 bytes, 0-padded) value
```
Inputs:
- Payer `scriptSig`'s
Outputs:
- `OP_RETURN` payload
- `scriptPubkey` for the owner's address
- `scriptPubkey` for the payer's change
Notes:
- Variation 1 simply registers the name. Variation 2 will register the name and
set a name value simultaneously. This is used in practice to set a zone file
hash for a name without the extra `NAME_UPDATE` transaction.
- Both variations are supported. Variation 1 was designed for the time when
Bitcoin only supported 40-byte `OP_RETURN` outputs.
### NAME_RENEWAL
Op: `:`
Description: This transaction renews a name in BNS. The name must still be
registered and not expired, and owned by the transaction sender.
Example: [`e543211b18e5d29fd3de7c0242cb017115f6a22ad5c6d51cf39e2b87447b7e65`](https://www.blocktrail.com/BTC/tx/e543211b18e5d29fd3de7c0242cb017115f6a22ad5c6d51cf39e2b87447b7e65)
`OP_RETURN` wire format (2 variations allowed):
Variation 1:
```
0 2 3 39
|----|--|-----------------------------|
magic op name.ns_id (37 bytes)
```
Variation 2:
```
0 2 3 39 59
|----|--|----------------------------------|-------------------|
magic op name.ns_id (37 bytes, 0-padded) value
```
Inputs:
- Payer `scriptSig`'s
Outputs:
- `OP_RETURN` payload
- `scriptPubkey` for the owner's addess. This can be a different address than
the current name owner (in which case, the name is renewed and transferred).
- `scriptPubkey` for the payer's change
- `scriptPubkey` for the burn address (to pay the name cost)
Notes:
- This transaction is identical to a `NAME_REGISTRATION`, except for the presence of the fourth output that pays for the name cost (to the burn address).
- Variation 1 simply renews the name. Variation 2 will both renew the name and
set a new name value (in practice, the hash of a new zone file).
- Both variations are supported. Variation 1 was designed for the time when
Bitcoin only supported 40-byte `OP_RETURN` outputs.
- This operation can be used to transfer a name to a new address by setting the
second output (the first `scriptPubkey`) to be the `scriptPubkey` of the new
owner key.
### NAME_UPDATE
Op: `+`
Description: This transaction sets the name state for a name to the given
`value`. In practice, this is used to announce new DNS zone file hashes to the [Atlas
network](/understand-stacks/atlas-overview).
Example: [`e2029990fa75e9fc642f149dad196ac6b64b9c4a6db254f23a580b7508fc34d7`](https://www.blocktrail.com/BTC/tx/e2029990fa75e9fc642f149dad196ac6b64b9c4a6db254f23a580b7508fc34d7)
`OP_RETURN` wire format:
```
0 2 3 19 39
|-----|--|-----------------------------------|-----------------------|
magic op hash128(name.ns_id,consensus hash) zone file hash
```
Note that `hash128(name.ns_id, consensus hash)` is the first 16 bytes of a SHA256 hash over the name concatenated to the hexadecimal string of the consensus hash (not the bytes corresponding to that hex string).
See the [Method Glossary](#method-glossary) below.
Example: `hash128("jude.id" + "8d8762c37d82360b84cf4d87f32f7754") == "d1062edb9ec9c85ad1aca6d37f2f5793"`.
The 20 byte zone file hash is computed from zone file data by using `ripemd160(sha56(zone file data))`
Inputs:
- owner `scriptSig`
- payment `scriptSig`'s
Outputs:
- `OP_RETURN` payload
- owner's `scriptPubkey`
- payment `scriptPubkey` change
### NAME_TRANSFER
Op: `>`
Description: This transaction changes the public key hash that owns the name in
BNS.
Example: [`7a0a3bb7d39b89c3638abc369c85b5c028d0a55d7804ba1953ff19b0125f3c24`](https://www.blocktrail.com/BTC/tx/7a0a3bb7d39b89c3638abc369c85b5c028d0a55d7804ba1953ff19b0125f3c24)
`OP_RETURN` wire format:
```
0 2 3 4 20 36
|-----|--|----|-------------------|---------------|
magic op keep hash128(name.ns_id) consensus hash
data?
```
Inputs:
- Owner `scriptSig`
- Payment `scriptSig`'s
Outputs:
- `OP_RETURN` payload
- new name owner's `scriptPubkey`
- old name owner's `scriptPubkey`
- payment `scriptPubkey` change
Notes:
- The `keep data?` byte controls whether or not the name's 20-byte value is preserved. This value is either `>` to preserve it, or `~` to delete it.
### NAME_REVOKE
Op: `~`
Description: This transaction destroys a registered name. Its name state value
in BNS will be cleared, and no further transactions will be able to affect the
name until it expires (if its namespace allows it to expire at all).
Example: [`eb2e84a45cf411e528185a98cd5fb45ed349843a83d39fd4dff2de47adad8c8f`](https://www.blocktrail.com/BTC/tx/eb2e84a45cf411e528185a98cd5fb45ed349843a83d39fd4dff2de47adad8c8f)
`OP_RETURN` wire format:
```
0 2 3 39
|----|--|-----------------------------|
magic op name.ns_id (37 bytes)
```
Inputs:
- owner `scriptSig`
- payment `scriptSig`'s
Outputs:
- `OP_RETURN` payload
- owner `scriptPubkey`
- payment `scriptPubkey` change
### ANNOUNCE
Op: `#`
Description: This transaction does not affect any names in BNS, but it allows a
user to send a message to other BNS nodes. In order for the message to be
received, the following must be true:
- The sender must have a BNS name
- The BNS nodes must list the sender's BNS name as being a "trusted message
sender"
- The message must have already been propagated through the [Atlas
network](/understand-stacks/atlas-overview). This transaction references it by content hash.
`OP_RETURN` wire format:
```
0 2 3 23
|----|--|-----------------------------|
magic op ripemd160(sha256(message))
```
Inputs:
- The payer `scriptSig`'s
Outputs:
- `OP_RETURN` payload
- change `scriptPubKey`
Notes:
- The payer key should be an owner key for an existing name, since Stacks users can subscribe to announcements from specific name-owners.
### NAMESPACE_PREORDER
Op: `*`
Description: This transaction announces the _hash_ of a new namespace. It is the
first of three transactions that must be sent to create a namespace.
Example: [`5f00b8e609821edd6f3369ee4ee86e03ea34b890e242236cdb66ef6c9c6a1b28`](https://www.blocktrail.com/BTC/tx/5f00b8e609821edd6f3369ee4ee86e03ea34b890e242236cdb66ef6c9c6a1b28)
`OP_RETURN` wire format:
```
0 2 3 23 39
|-----|---|-----------------------------------------|----------------|
magic op hash_name(ns_id,script_pubkey,reveal_addr) consensus hash
```
Inputs:
- Namespace payer `scriptSig`
Outputs:
- `OP_RETURN` payload
- Namespace payer `scriptPubkey` change address
- `p2pkh` script to the burn address `1111111111111111111114oLvT2`, whose public key hash is 0x00000000000000000000000000000000
Notes:
- The `reveal_addr` field is the address of the namespace revealer public key. The revealer private key will be used to generate `NAME_IMPORT` transactions.
### NAMESPACE_REVEAL
Op: `&`
Description: This transaction reveals the namespace ID and namespace rules
for a previously anounced namespace hash (sent by a previous `NAMESPACE_PREORDER`).
Example: [`ab54b1c1dd5332dc86b24ca2f88b8ca0068485edf0c322416d104c5b84133a32`](https://www.blocktrail.com/BTC/tx/ab54b1c1dd5332dc86b24ca2f88b8ca0068485edf0c322416d104c5b84133a32)
`OP_RETURN` wire format:
```
0 2 3 7 8 9 10 11 12 13 14 15 16 17 18 20 39
|-----|---|--------|-----|-----|----|----|----|----|----|-----|-----|-----|--------|----------|-------------------------|
magic op life coeff. base 1-2 3-4 5-6 7-8 9-10 11-12 13-14 15-16 nonalpha version namespace ID
bucket exponents no-vowel
discounts
```
Inputs:
- Namespace payer `scriptSig`s
Outputs:
- `OP_RETURN` payload
- namespace revealer `scriptPubkey`
- namespace payer change `scriptPubkey`
Notes:
- This transaction must be sent within 1 day of the `NAMESPACE_PREORDER`
- The second output (with the namespace revealer) **must** be a `p2pkh` script
- The address of the second output **must** be the `reveal_addr` in the `NAMESPACE_PREORDER`
Pricing:
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+:
With the above example configuration, the following are true:
- The price of `john` would be 2 \* 10^8, since `john` falls into bucket 4 and has no punctuation or numerics.
- The price of `john1` would be 2 \* 10^6, since `john1` falls into bucket 5 but has a number (and thus receives a 10x discount)
- The price of `john_1` would be 2 \* 10^6, since `john_1` falls into bucket 6 but has a number and punctuation (and thus receives a 10x discount)
- The price of `j0hn_1` would be 2 \* 10^5, since `j0hn_1` falls into bucket 6 but has a number and punctuation and lacks vowels (and thus receives a 100x discount)
### NAME_IMPORT
Op: `;`
Description: This transaction registers a name and some name state into a
namespace that has been revealed, but not been launched. Only the namespace
creator can import names. See the [namespace creation section](/technology/naming-system) for details.
Example: [`c698ac4b4a61c90b2c93dababde867dea359f971e2efcf415c37c9a4d9c4f312`](https://www.blocktrail.com/BTC/tx/c698ac4b4a61c90b2c93dababde867dea359f971e2efcf415c37c9a4d9c4f312)
`OP_RETURN` wire format:
```
0 2 3 39
|----|--|-----------------------------|
magic op name.ns_id (37 bytes)
```
Inputs:
- The namespace reveal `scriptSig` (with the namespace revealer's public key), or one of its first 300 extended public keys
- Any payment inputs
Outputs:
- `OP_RETURN` payload
- recipient `scriptPubKey`
- zone file hash (using the 20-byte hash in a standard `p2pkh` script)
- payment change `scriptPubKey`
Notes:
- These transactions can only be sent between the `NAMESPACE_REVEAL` and `NAMESPACE_READY`.
- The first `NAME_IMPORT` transaction **must** have a `scriptSig` input that matches the `NAMESPACE_REVEAL`'s second output (that is, the reveal output).
- Any subsequent `NAME_IMPORT` transactions **may** have a `scriptSig` input whose public key is one of the first 300 extended public keys from the `NAMESPACE_REVEAL`'s `scriptSig` public key.
### NAMESPACE_READY
Op: `!`
Description: This transaction launches a namesapce. Only the namespace creator
can send this transaction. Once sent, anyone can register names in the
namespace.
Example: [`2bf9a97e3081886f96c4def36d99a677059fafdbd6bdb6d626c0608a1e286032`](https://www.blocktrail.com/BTC/tx/2bf9a97e3081886f96c4def36d99a677059fafdbd6bdb6d626c0608a1e286032)
`OP_RETURN` wire format:
```
0 2 3 4 23
|-----|--|--|------------|
magic op . ns_id
```
Inputs:
- Namespace revealer's `scriptSig`s
Outputs:
- `OP_RETURN` payload
- Change output to the namespace revealer's `p2pkh` script
Notes:
- This transaction must be sent within 1 year of the corresponding `NAMESPACE_REVEAL` to be accepted.
### TOKEN_TRANSFER
Op: `$`
Description: This transaction transfers tokens from one account to another. Only `STACKS` tokens can be transferred at this time. The transaction encodes the number of _micro-Stacks_ to send.
Example: [`093983ca71a6a9dd041c0bdb8b3012824d726ee26fe51da8335a06e8a08c2798`](https://www.blocktrail.com/BTC/tx/093983ca71a6a9dd041c0bdb8b3012824d726ee26fe51da8335a06e8a08c2798)
`OP_RETURN` wire format:
```
0 2 3 19 38 46 80
|-----|--|--------------|----------|-----------|-------------------------|
magic op consensus_hash token_type amount (LE) scratch area
```
Inputs:
- Sender's scriptSig's
Outputs:
- `OP_RETURN` payload
- Recipient scriptPubKey (encodes the address of the receiving account)
- Change address for the sender
Notes:
- The `amount` field is an 8-byte litte-endian number that encodes the number of micro-Stacks.
- The `token_type` field must be `STACKS`. All other unused bytes in this field must be `\x00`.
- The `scratch area` field is optional -- it can be up to 34 bytes, and include any data you want.
## Method Glossary
Some hashing primitives are used to construct the wire-format representation of each name operation. They are enumerated here:
```
B40_REGEX = '^[a-z0-9\-_.+]*$'
def is_b40(s):
return isinstance(s, str) and re.match(B40_REGEX, s) is not None
def b40_to_bin(s):
if not is_b40(s):
raise ValueError('{} must only contain characters in the b40 char set'.format(s))
return unhexlify(charset_to_hex(s, B40_CHARS))
def hexpad(x):
return ('0' * (len(x) % 2)) + x
def charset_to_hex(s, original_charset):
return hexpad(change_charset(s, original_charset, B16_CHARS))
def bin_hash160(s, hex_format=False):
""" s is in hex or binary format
"""
if hex_format and is_hex(s):
s = unhexlify(s)
return hashlib.new('ripemd160', bin_sha256(s)).digest()
def hex_hash160(s, hex_format=False):
""" s is in hex or binary format
"""
if hex_format and is_hex(s):
s = unhexlify(s)
return hexlify(bin_hash160(s))
def hash_name(name, script_pubkey, register_addr=None):
"""
Generate the hash over a name and hex-string script pubkey.
Returns the hex-encoded string RIPEMD160(SHA256(x)), where
x is the byte string composed of the concatenation of the
binary
"""
bin_name = b40_to_bin(name)
name_and_pubkey = bin_name + unhexlify(script_pubkey)
if register_addr is not None:
name_and_pubkey += str(register_addr)
# make hex-encoded hash
return hex_hash160(name_and_pubkey)
def hash128(data):
"""
Hash a string of data by taking its 256-bit sha256 and truncating it to the
first 16 bytes
"""
return hexlify(bin_sha256(data)[0:16])
```
Loading…
Cancel
Save