Skip to content

Commit 6df8be8

Browse files
committed
update README to follow new API std
1 parent 94033f3 commit 6df8be8

File tree

2 files changed

+121
-40
lines changed

2 files changed

+121
-40
lines changed

README.md

+118-37
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,13 @@
22

33
## Layout
44

5-
`/` defines all of the functions/types/constants that are common to all underlying chains.
5+
`/` declares the assets and chains that exist, but provides no chain-specific implementations.
66

7-
`/compat` contains all of the Compat APIs. These APIs are the interfaces required to be implemented by underlying chains that are similar to each other. For example, the `/compat/bitcoincompat` folder defines the Bitcoin Compat API. The `Address`, `Client`, `Tx`, and `TxBuilder` interfaces must be implemented by all underlying chains that want to be compatible with the Bitcoin runtime. It also defines implementations that are likely to be common to these underlying chains (although, each underlying chain can override whatever it needs to).
7+
`/infra` defines a local deployment of the multichain using `docker-compose`. All underlying chains provide a `Dockerfile` and service definition to make running node instances easy. All chains need to add a `Dockerfile` and service definition that allows the multichain to spin up a local development-mode instance of the chain. This is necessary for running comprehensive local test suites.
88

9-
`/chain` contains all the implementations of the Compat APIs. Each chain has its own sub-package. For example, Bitcoin, Bitcoin Cash, Dogecoin, and Zcash are all underyling chains that implement the Bitcoin Compat API (defined in `/compat/bitcoincompat`), and each of these implementations are in `/chain/bitcoin`, `/chain/bitcoincash`, `/chain/dogecoin`, and `/chain/zcash` respectively.
9+
`/api` defines the different compatibility APIs that exist: Account, Address, Contract, Gas, and UTXO. Chains should implement the APIs that are relevant to them. For example, Bitcoin (and its forks) implements the Address, Gas, and UTXO APIs. No actual implementations should be added to this folder.
1010

11-
`/docker` defines a local deployment of the multichain using `docker-compose`. All underlying chains provide a `Dockerfile` and service definition to make running node instances easy.
12-
13-
`/runtime` contains all of the Runtime modules. There is exactly one Runtime module for each Compat API, and you can think of a Runtime module as a way of accessing all of the Compat APIs in one place. This is the primary package used by users of the multichain (including the RenVM execution engine). If your chain implements one of the existing Compat APIs, you will *not* need to modify any of the Runtime modules.
11+
`/chain` defines all of the chain-specific implementations of the APIs. Each chain has its own sub-package. For example, Bitcoin, Bitcoin Cash, Dogecoin, and Zcash are all chains that implement the Address, Gas, and UTXO APIs, and each of these implementations are in `/chain/bitcoin`, `/chain/bitcoincash`, `/chain/dogecoin`, and `/chain/zcash` respectively.
1412

1513
## Example
1614

@@ -53,26 +51,26 @@ const (
5351

5452
### Docker
5553

56-
Next, we need to setup a Docker container in the `docker/` folder. This is needed for local test suites, allowing for end-to-end integrated testing directly against a node. Doing this requires a couple of steps.
54+
Next, we need to setup a Docker container in the `/infra` folder. This is needed for local test suites, allowing for end-to-end integrated testing directly against a node. Doing this requires a couple of steps.
5755

58-
First, we create a new `dogecoin/` folder in the `docker/` folder:
56+
First, we create a new `dogecoin/` folder in the `/infra` folder:
5957

6058
```
61-
docker/
62-
|-- bitcoin/
63-
|-- bitcoincash/
64-
|-- dogecoin/ # This is our new folder!
59+
/infra
60+
|-- /bitcoin
61+
|-- /bitcoincash
62+
|-- /dogecoin # This is our new folder!
6563
| |-- Dockerfile # This is our new Dockerfile!
6664
| |-- dogecoin.conf
6765
| |-- run.sh # This is our new run file!
68-
|-- zcash/
69-
|-- docker-compose.env
66+
|-- /zcash
67+
|-- .env
7068
|-- docker-compose.yaml
7169
```
7270

73-
The new folder _must_ at least contain a `Dockerfile` that installs the node, and a `run.sh` file that runs the nodes. The node _should_ be run in test mode. The new folder can also contain other files that are specific to the needs of the chain being added. In our case, the `dogecoin.conf` file is also needed to configure the node. (We will omit showing all the code here, since there is quite a bit of it, but you can check it out in the `docker/dogecoin/` folder.)
71+
The new folder _must_ at least contain a `Dockerfile` that installs the node, and a `run.sh` file that runs the nodes. The node _should_ be run in test mode. The new folder can also contain other files that are specific to the needs of the chain being added. In our case, the `dogecoin.conf` file is also needed to configure the node. (We will omit showing all the code here, since there is quite a bit of it, but you can check it out in the `/infra/dogecoin` folder.)
7472

75-
Second, we add an entry to the `docker-compose.env` file. Our entry _must_ include a private key that will have access to funds, and the public address associated with that private key. We will add:
73+
Second, we add an entry to the `.env` file. Our entry _must_ include a private key that will have access to funds, and the public address associated with that private key. We will add:
7674

7775
```sh
7876
#
@@ -103,44 +101,127 @@ dogecoin:
103101
- "${DOGECOIN_ADDRESS}"
104102
```
105103
106-
### Runtime
104+
### Address API
105+
106+
All chains _should_ implement the Address API. Luckily for Dogecoin, it is so similar to Bitcoin that we can re-export the Bitcoin implementation without the need for custom modifications. In `/chain/dogecoin/address.go` we add:
107+
108+
```go
109+
package dogecoin
110+
111+
import "github.com/renproject/multichain/chain/bitcoin"
112+
113+
type (
114+
AddressEncoder = bitcoin.AddressEncoder
115+
AddressDecoder = bitcoin.AddressDecoder
116+
AddressEncodeDecoder = bitcoin.AddressEncodeDecoder
117+
)
118+
```
119+
120+
These three interfaces allow users of the `🔗 multichain` to easily encode and decode Dogecoin addresses. Other chains will need to provide their own implementations, based on their chains address standards.
121+
122+
### Gas API
123+
124+
Most, but not all, chains _should_ implement the Gas API. Again, Dogecoin is so similar to Bitcoin that we can re-export the Bitcoin implementation in `/chain/dogecoin/gas.go`:
125+
126+
```go
127+
package dogecoin
128+
129+
import "github.com/renproject/multichain/chain/bitcoin"
130+
131+
type GasEstimator = bitcoin.GasEstimator
132+
133+
var NewGasEstimator = bitcoin.NewGasEstimator
134+
```
135+
136+
The interface allows users of the `🔗 multichain` to estimate gas prices (although, the current implementation is _very_ simple). The associated function allows users to construct an instance of the interface for Dogecoin.
107137

108-
The final thing that is required before the `🔗 multichain` supports our new chain is an integration into the runtime, defined in `package runtime`. The exact requirements for integration into the runtime vary from chain-to-chain. To make life easier, there is a set of common interfaces, known as the _Compat API_, that can be implemented by new chains. The Compat API is a set of interfaces, designed with the intention for multiple implementations to exist. For example, the Bitcoin Compat API is used by Bitcoin, Bitcoin Cash, and Zcash.
138+
### UTXO API
109139

110-
The Compat API is defined by `package compat` (and is used by the `Runtime` type in `package runtime`). All of the interfaces in `package bitcoincompat` belong to the Bitcoin Compat API, all of the interfaces in `package ethereumcompat` belong to the Ethereum Compat API, all of the interfaces in `package substratecompat` belong to the Substrate Compat API, and so on. Similarly, the `BitcoinXX`, `EthereumXXX`, and `SubstrateXXX` methods (defined by the `Runtime` type in `package runtime`) are all abstractions over the respective Compat APIs, but do not need to be modified!
140+
Generally speaking, chains fall into two categories: account-based or UTXO-based (and some can even be both). Bitcoin, and its forks, are all UTXO-based chains. As a fork of Bitcoin, Dogecoin is a UTXO-based chain, so we implement the UTXO API. To implement the UTXO API, we must implement the `Tx`, `TxBuilder`, and `Client` interfaces. More information can be found in the comments of `/api/utxo` folder.
111141

112-
Dogecoin is a fork of Bitcoin, so it is natural that we will support it by implementing the Bitcoin Compat API. Dogecoin is, in fact, so similar to Bitcoin that the implementation is trivial. All implementations belond in `package chain`, so we will create a new `package dogecoin` in that directory. Here, we create the `dogecoin.go` file and fill it with:
142+
Again, the implementation for Dogecoin is trivial. In `/chain/dogecoin/utxo`, we have:
143+
144+
```go
145+
package dogecoin
146+
147+
import "github.com/renproject/multichain/chain/bitcoin"
148+
149+
type (
150+
Tx = bitcoin.Tx
151+
TxBuilder = bitcoin.TxBuilder
152+
Client = bitcoin.Client
153+
ClientOptions = bitcoin.ClientOptions
154+
)
155+
156+
var (
157+
NewTxBuilder = bitcoin.NewTxBuilder
158+
NewClient = bitcoin.NewClient
159+
DefaultClientOptions = bitcoin.DefaultClientOptions
160+
)
161+
```
162+
163+
Up to this point, we have done nothing but re-export Bitcoin. So what makes Dogecoin different? And how can we express that difference? Well, the `/chain/dogecoin` folder is the place where we must define anything else Dogecoin users will need. In the case of Dogecoin, the only thing that differentiates it from Bitcoin is the `*chaincfg.Param` object. We define this in `/chain/dogecoin/dogecoin.go`:
113164

114165
```go
115166
package dogecoin
116167
117168
import (
118-
"github.com/renproject/multichain/chain/bitcoin"
119-
"github.com/renproject/multichain/compat/bitcoincompat"
169+
"github.com/btcsuite/btcd/chaincfg"
120170
)
121171
122-
// NewTxBuilder returns an implementation of the transaction builder interface
123-
// from the Bitcoin Compat API, and exposes the functionality to build simple
124-
// Dogecoin transactions.
125-
func NewTxBuilder() bitcoincompat.TxBuilder {
126-
return bitcoin.NewTxBuilder()
172+
func init() {
173+
if err := chaincfg.Register(&MainNetParams); err != nil {
174+
panic(err)
175+
}
176+
if err := chaincfg.Register(&RegressionNetParams); err != nil {
177+
panic(err)
178+
}
127179
}
128180
129-
// The Tx type is copied from Bitcoin.
130-
type Tx = bitcoin.Tx
131-
```
181+
var MainNetParams = chaincfg.Params{
182+
Name: "mainnet",
183+
Net: 0xc0c0c0c0,
184+
185+
// Address encoding magics
186+
PubKeyHashAddrID: 30,
187+
ScriptHashAddrID: 22,
188+
PrivateKeyID: 158,
132189
133-
For a coin as simple as Dogecoin, nothing else is required! For more complex examples, you can checkout `package bitcoincash` and `package zcash` which need to define their own address and transaction formats.
190+
// BIP32 hierarchical deterministic extended key magics
191+
HDPrivateKeyID: [4]byte{0x02, 0xfa, 0xc3, 0x98}, // starts with xprv
192+
HDPublicKeyID: [4]byte{0x02, 0xfa, 0xca, 0xfd}, // starts with xpub
193+
194+
// Human-readable part for Bech32 encoded segwit addresses, as defined in
195+
// BIP 173. Dogecoin does not actually support this, but we do not want to
196+
// collide with real addresses, so we specify it.
197+
Bech32HRPSegwit: "doge",
198+
}
134199
135-
### Custom Runtimes
200+
var RegressionNetParams = chaincfg.Params{
201+
Name: "regtest",
136202
137-
Not all chains are as simple as the Dogecoin chain, and an existing Compat API may not be sufficient for your needs. In these scenarios, a little more work is required.
203+
// Dogecoin has 0xdab5bffa as RegTest (same as Bitcoin's RegTest).
204+
// Setting it to an arbitrary value (leet_hex(dogecoin)), so that we can
205+
// register the regtest network.
206+
Net: 0xd063c017,
138207
139-
1. Define your own compat package (e.g. `package myawesomechaincompat`) in the `compat/` folder.
140-
2. Define your own compat interfaces in your new compat package.
141-
3. Define your own compat methods on the `Runtime` type in `package runtime`. You will always need a `MyAwesomeChainDecodeAddress(pack.String) (myawesoemcompat.Address, error)` method. If your blockchain is programmable, then defining methods for querying relevant events is usually sufficient. Otherwise, building/submitting transactions is probably going to be required.
208+
// Address encoding magics
209+
PubKeyHashAddrID: 111,
210+
ScriptHashAddrID: 196,
211+
PrivateKeyID: 239,
212+
213+
// BIP32 hierarchical deterministic extended key magics
214+
HDPrivateKeyID: [4]byte{0x04, 0x35, 0x83, 0x94}, // starts with xprv
215+
HDPublicKeyID: [4]byte{0x04, 0x35, 0x87, 0xcf}, // starts with xpub
216+
217+
// Human-readable part for Bech32 encoded segwit addresses, as defined in
218+
// BIP 173. Dogecoin does not actually support this, but we do not want to
219+
// collide with real addresses, so we specify it.
220+
Bech32HRPSegwit: "dogert",
221+
}
222+
```
142223

143-
If in doubt, get in touch with the Ren team at https://t.me/renproject and we will help you out!
224+
Most of the functions that we have re-exported expected `*chaincfg.Params` as an argument. By defining one for regnet and mainnet, users can construct Dogecoin instances of the UTXO API by using these params.
144225

145226
## Test Suite
146227

test.sh

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
source ./docker/docker-compose.env
2-
docker-compose -f ./docker/docker-compose.yaml up --build -d
1+
source ./infra/.env
2+
docker-compose -f ./infra/docker-compose.yaml up --build -d
33
echo "Waiting for multichain to boot..."
44
sleep 30
55
go test -v ./...
6-
docker-compose -f ./docker/docker-compose.yaml down
6+
docker-compose -f ./infra/docker-compose.yaml down
77
echo "Done!"

0 commit comments

Comments
 (0)