multichain

package module
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 11, 2021 License: GPL-3.0 Imports: 6 Imported by: 16

README

🔗 multichain

Layout

/ declares the assets and chains that exist, but provides no chain-specific implementations.

/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.

/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.

/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.

Example

The 🔗 multichain is designed to be flexible enough to support any kind of chain. Anyone is free to contribute to the 🔗 multichain by adding support for a new chain, or improving support for an existing chain. To show how this is done, we will walk-through an example: adding support for Dogecoin.

Chains and Assets

Before doing anything else, let's add an enumeration for the Asset and Chain types, which can be found in package multichain. To avoid favouritism, all assets and chains are listed in alphabetical order. Unless otherwise advised by an offiical team member, the names and tickers found on https://coinmarketcap.com must be used.

Adding an Asset:

// Enumeration of supported assets. When introducing a new chain, or new asset
// from an existing chain, you must add a human-readable string to this set of
// enumerated values. Assets must be listed in alphabetical order.
const (
    BCH  = Asset("BCH")  // Bitcoin Cash
    BTC  = Asset("BTC")  // Bitcoin
    DOGE = Asset("DOGE") // Dogecoin (This is our new asset!)
    ETH  = Asset("ETH")  // Ether
    ZEC  = Asset("ZEC")  // Zcash
)

Adding a Chain:

// Enumeration of supported chains. When introducing a new chain, you must add a
// human-readable string to this set of enumerated values. Chains must be listed
// in alphabetical order.
const (
    Acala       = Chain("Acala")
    Bitcoin     = Chain("Bitcoin")
    BitcoinCash = Chain("BitcoinCash")
    Dogecoin    = Chain("Dogecoin") // (This is our new chain!)
    Ethereum    = Chain("Ethereum")
    Zcash       = Chain("Zcash")
)
Docker

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.

First, we create a new dogecoin/ folder in the /infra folder:

/infra
|-- /bitcoin
|-- /bitcoincash
|-- /dogecoin         # This is our new folder!
|   |-- Dockerfile    # This is our new Dockerfile!
|   |-- dogecoin.conf
|   |-- run.sh        # This is our new run file!
|-- /zcash
|-- .env
|-- docker-compose.yaml

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.)

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:

#
# Dogecoin
#

# Address that will receive mining rewards. Generally, this is set to an address
# for which the private key is known by a test suite. This allows the test suite
# access to plenty of testing funds.
export DOGECOIN_PK=cRZnRgH2ztcJupCzkWbq2mjiT8PSFAmtYRYb1phg1vSRRcNBX4w4
export DOGECOIN_ADDRESS=n3PSSpR4zqUKWH4tcRjP9aTwJ4GmixQXmt

Last, we add a service to the docker-compose.yaml file. This allows the node to boot alongside the other nodes in the multichain. This entry must expose the node for use in tests, and must not overlap with other nodes that already exist (ports are reserved on a first-come-first-serve basis). We will define the service as:

##
## Dogecoin
##

dogecoin:
  build:
    context: ./dogecoin
  ports:
    - "0.0.0.0:18332:18332"
  entrypoint:
    - "./root/run.sh"
    - "${DOGECOIN_ADDRESS}"
Address API

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:

package dogecoin

import "github.com/renproject/multichain/chain/bitcoin"

type (
	AddressEncoder       = bitcoin.AddressEncoder
	AddressDecoder       = bitcoin.AddressDecoder
	AddressEncodeDecoder = bitcoin.AddressEncodeDecoder
)

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.

Gas API

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:

package dogecoin

import "github.com/renproject/multichain/chain/bitcoin"

type GasEstimator = bitcoin.GasEstimator

var NewGasEstimator = bitcoin.NewGasEstimator

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.

UTXO API

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.

Again, the implementation for Dogecoin is trivial. In /chain/dogecoin/utxo, we have:

package dogecoin

import "github.com/renproject/multichain/chain/bitcoin"

type (
	Tx            = bitcoin.Tx
	TxBuilder     = bitcoin.TxBuilder
	Client        = bitcoin.Client
	ClientOptions = bitcoin.ClientOptions
)

var (
	NewTxBuilder         = bitcoin.NewTxBuilder
	NewClient            = bitcoin.NewClient
	DefaultClientOptions = bitcoin.DefaultClientOptions
)

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:

package dogecoin

import (
	"github.com/btcsuite/btcd/chaincfg"
)

func init() {
	if err := chaincfg.Register(&MainNetParams); err != nil {
		panic(err)
	}
	if err := chaincfg.Register(&RegressionNetParams); err != nil {
		panic(err)
	}
}

var MainNetParams = chaincfg.Params{
	Name: "mainnet",
	Net:  0xc0c0c0c0,

	// Address encoding magics
	PubKeyHashAddrID: 30,
	ScriptHashAddrID: 22,
	PrivateKeyID:     158,

	// BIP32 hierarchical deterministic extended key magics
	HDPrivateKeyID: [4]byte{0x02, 0xfa, 0xc3, 0x98}, // starts with xprv
	HDPublicKeyID:  [4]byte{0x02, 0xfa, 0xca, 0xfd}, // starts with xpub

	// Human-readable part for Bech32 encoded segwit addresses, as defined in
	// BIP 173. Dogecoin does not actually support this, but we do not want to
	// collide with real addresses, so we specify it.
	Bech32HRPSegwit: "doge",
}

var RegressionNetParams = chaincfg.Params{
	Name: "regtest",

	// Dogecoin has 0xdab5bffa as RegTest (same as Bitcoin's RegTest).
	// Setting it to an arbitrary value (leet_hex(dogecoin)), so that we can
	// register the regtest network.
	Net: 0xd063c017,

	// Address encoding magics
	PubKeyHashAddrID: 111,
	ScriptHashAddrID: 196,
	PrivateKeyID:     239,

	// BIP32 hierarchical deterministic extended key magics
	HDPrivateKeyID: [4]byte{0x04, 0x35, 0x83, 0x94}, // starts with xprv
	HDPublicKeyID:  [4]byte{0x04, 0x35, 0x87, 0xcf}, // starts with xpub

	// Human-readable part for Bech32 encoded segwit addresses, as defined in
	// BIP 173. Dogecoin does not actually support this, but we do not want to
	// collide with real addresses, so we specify it.
	Bech32HRPSegwit: "dogert",
}

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.

Test Suite

  1. Install Docker
  2. Install Docker Compose
  3. Run Docker
  4. Run ./test.sh

Example output:

Creating network "docker_default" with the default driver
Building bitcoin

...

Successfully built 1ebb03faa04f
Successfully tagged docker_bitcoin:latest
Building bitcoincash

...

Successfully built e12e98011869
Successfully tagged docker_bitcoincash:latest
Building zcash

...

Successfully built 56231a29ca2e
Successfully tagged docker_zcash:latest
docker_bitcoin_1 is up-to-date
docker_bitcoincash_1 is up-to-date
docker_zcash_1 is up-to-date
Waiting for multichain to boot...
=== RUN   TestMultichain
Running Suite: Multichain Suite
===============================

...

Stopping docker_bitcoincash_1 ... done
Stopping docker_zcash_1       ... done
Stopping docker_bitcoin_1     ... done
Removing docker_bitcoincash_1 ... done
Removing docker_zcash_1       ... done
Removing docker_bitcoin_1     ... done
Removing network docker_default
Done!

Documentation

Overview

Package multichain defines all supported assets and chains. It also re-exports the individual multichain APIs.

Index

Constants

View Source
const (
	BCH  = Asset("BCH")  // Bitcoin Cash
	BNB  = Asset("BNB")  // Binance Coin
	BTC  = Asset("BTC")  // Bitcoin
	CELO = Asset("CELO") // Celo
	DGB  = Asset("DGB")  // DigiByte
	DOGE = Asset("DOGE") // Dogecoin
	ETH  = Asset("ETH")  // Ether
	FIL  = Asset("FIL")  // Filecoin
	FTM  = Asset("FTM")  // Fantom
	SOL  = Asset("SOL")  // Solana
	LUNA = Asset("LUNA") // Luna
	ZEC  = Asset("ZEC")  // Zcash

	AMOCK1 = Asset("AMOCK1") // Account-based mock asset
	AMOCK2 = Asset("AMOCK2") // Account-based mock asset
	UMOCK  = Asset("UMOCK")  // UTXO-based mock asset
)

Enumeration of supported assets. When introducing a new chain, or new asset from an existing chain, you must add a human-readable string to this set of enumerated values. Assets must be listed in alphabetical order.

View Source
const (
	Acala             = Chain("Acala")
	BinanceSmartChain = Chain("BinanceSmartChain")
	Bitcoin           = Chain("Bitcoin")
	BitcoinCash       = Chain("BitcoinCash")
	Celo              = Chain("Celo")
	DigiByte          = Chain("DigiByte")
	Dogecoin          = Chain("Dogecoin")
	Ethereum          = Chain("Ethereum")
	Fantom            = Chain("Fantom")
	Filecoin          = Chain("Filecoin")
	Solana            = Chain("Solana")
	Terra             = Chain("Terra")
	Zcash             = Chain("Zcash")

	AccountMocker1 = Chain("AccountMocker1")
	AccountMocker2 = Chain("AccountMocker2")
	UTXOMocker     = Chain("UTXOMocker")
)

Enumeration of supported chains. When introducing a new chain, you must add a human-readable string to this set of enumerated values. Chains must be listed in alphabetical order.

View Source
const (
	// ChainTypeAccountBased is an identifier for all account-based chains,
	// namely, BinanceSmartChain, Ethereum, Filecoin, and so on.
	ChainTypeAccountBased = ChainType("Account")

	// ChainTypeUTXOBased is an identifier for all utxo-based chains, namely,
	// Bitcoin, BitcoinCash, DigiByte, and so on.
	ChainTypeUTXOBased = ChainType("UTXO")
)
View Source
const (
	// NetworkLocalnet represents a local network for chains. It is usually only
	// accessible from the device running the network, and is not accessible
	// over the Internet.  Chain rules are often slightly different to allow for
	// faster block times and easier access to testing funds. This is also
	// sometimes referred to as "regnet" or "regression network". It should only
	// be used for local testing.
	NetworkLocalnet = Network("localnet")

	// NetworkDevnet represents the development network for chains. This network
	// is typically a deployed version of the localnet. Chain rules are often
	// slightly different to allow for faster block times and easier access to
	// testing funds.
	NetworkDevnet = Network("devnet")

	// NetworkTestnet represents the test network for chains. This network is
	// typically a publicly accessible network that has the same, or very
	// similar, chain rules compared to mainnet. Assets on this type of network
	// are usually not considered to have value.
	NetworkTestnet = Network("testnet")

	// NetworkMainnet represents the main network for chains.
	NetworkMainnet = Network("mainnet")
)

Variables

This section is empty.

Functions

This section is empty.

Types

type AccountClient added in v0.2.0

type AccountClient = account.Client

The AccountClient interface defines the functionality required to interact with a chain over RPC.

type AccountTx added in v0.2.0

type AccountTx = account.Tx

The AccountTx interface defines the functionality that must be exposed by account-based transactions.

type AccountTxBuilder added in v0.2.0

type AccountTxBuilder = account.TxBuilder

The AccountTxBuilder interface defines the functionality required to build account-based transactions. Most chain implementations require additional information, and this should be accepted during the construction of the chain-specific transaction builder.

type Address added in v0.2.0

type Address = address.Address

An Address is a human-readable representation of a public identity. It can be the address of an external account, contract, or script.

type AddressEncodeDecoder added in v0.2.1

type AddressEncodeDecoder = address.EncodeDecoder

The AddressEncodeDecoder interfaces combines encoding and decoding functionality into one interface.

type Asset

type Asset string

An Asset uniquely identifies assets using human-readable strings.

func (Asset) ChainType added in v0.2.6

func (asset Asset) ChainType() ChainType

ChainType returns the chain-type (Account or UTXO) for the given asset

func (Asset) Marshal

func (asset Asset) Marshal(buf []byte, rem int) ([]byte, int, error)

Marshal the asset to binary.

func (Asset) OriginChain added in v0.2.0

func (asset Asset) OriginChain() Chain

OriginChain returns the chain upon which the asset originates. For example, the origin chain of BTC is Bitcoin.

func (Asset) SizeHint

func (asset Asset) SizeHint() int

SizeHint returns the number of bytes required to represent the asset in binary.

func (*Asset) Unmarshal

func (asset *Asset) Unmarshal(buf []byte, rem int) ([]byte, int, error)

Unmarshal the asset from binary.

type Chain

type Chain string

A Chain uniquely identifies a blockchain using a human-readable string.

func (Chain) ChainType added in v0.2.2

func (chain Chain) ChainType() ChainType

ChainType returns the chain type (whether account-based or utxo-based chain) for the chain.

func (Chain) IsAccountBased added in v0.2.2

func (chain Chain) IsAccountBased() bool

IsAccountBased returns true when invoked on an account-based chain, otherwise returns false.

func (Chain) IsUTXOBased added in v0.2.2

func (chain Chain) IsUTXOBased() bool

IsUTXOBased returns true when invoked on a utxo-based chain, otherwise returns false.

func (Chain) Marshal

func (chain Chain) Marshal(buf []byte, rem int) ([]byte, int, error)

Marshal the chain to binary. You should not call this function directly, unless you are implementing marshalling for a container type.

func (Chain) NativeAsset added in v0.2.8

func (chain Chain) NativeAsset() Asset

NativeAsset returns the underlying native asset for a chain. For example, the root asset of Bitcoin chain is BTC.

func (Chain) SizeHint

func (chain Chain) SizeHint() int

SizeHint returns the number of bytes required to represent the chain in binary.

func (*Chain) Unmarshal

func (chain *Chain) Unmarshal(buf []byte, rem int) ([]byte, int, error)

Unmarshal the chain from binary. You should not call this function directly, unless you are implementing unmarshalling for a container type.

type ChainType added in v0.2.2

type ChainType string

ChainType represents the type of chain (whether account-based or utxo-based)

func (ChainType) Marshal added in v0.2.2

func (chainType ChainType) Marshal(buf []byte, rem int) ([]byte, int, error)

Marshal the chain type to binary. You should not call this function directly, unless you are implementing marshalling for a container type.

func (ChainType) SizeHint added in v0.2.2

func (chainType ChainType) SizeHint() int

SizeHint returns the number of bytes required to represent the chain type in binary.

func (*ChainType) Unmarshal added in v0.2.2

func (chainType *ChainType) Unmarshal(buf []byte, rem int) ([]byte, int, error)

Unmarshal the chain type from binary. You should not call this function directly, unless you are implementing unmarshalling for a container type.

type ContractCallData added in v0.2.0

type ContractCallData = contract.CallData

ContractCallData is used to specify a function and its parameters when invoking business logic on a contract.

type ContractCaller added in v0.2.0

type ContractCaller = contract.Caller

The ContractCaller interface defines the functionality required to call readonly functions on a contract. Calling functions that mutate contract state should be done using the Account API.

type GasEstimator added in v0.2.0

type GasEstimator = gas.Estimator

The GasEstimator interface defines the functionality required to know the current recommended gas prices.

type Network added in v0.2.4

type Network string

Network identifies the network type for the multichain deployment

func (Network) Marshal added in v0.2.4

func (net Network) Marshal(buf []byte, rem int) ([]byte, int, error)

Marshal the network to binary. You should not call this function directly, unless you are implementing marshalling for a container type.

func (Network) SizeHint added in v0.2.4

func (net Network) SizeHint() int

SizeHint returns the number of bytes required to represent the network in binary.

func (*Network) Unmarshal added in v0.2.4

func (net *Network) Unmarshal(buf []byte, rem int) ([]byte, int, error)

Unmarshal the network from binary. You should not call this function directly, unless you are implementing unmarshalling for a container type.

type RawAddress added in v0.2.0

type RawAddress = address.RawAddress

RawAddress is an address that has been decoded into its binary form.

type UTXOClient added in v0.2.0

type UTXOClient = utxo.Client

A UTXOClient interface defines the functionality required to interact with a chain over RPC.

type UTXOInput added in v0.2.0

type UTXOInput = utxo.Input

A UTXOInput specifies an existing output, produced by a previous transaction, to be consumed by another transaction. It includes the script that meets the conditions specified by the consumed output (called the sig script, based on Bitcoin).

type UTXORecipient added in v0.2.0

type UTXORecipient = utxo.Recipient

A UTXORecipient specifies an address, and an amount, for which a transaction will produce an output. Depending on the output, the address can take on different formats (e.g. in Bitcoin, addresses can be P2PK, P2PKH, or P2SH).

type UTXOTx added in v0.2.0

type UTXOTx = utxo.Tx

A UTXOTx interfaces defines the functionality that must be exposed by utxo-based transactions.

type UTXOTxBuilder added in v0.2.0

type UTXOTxBuilder = utxo.TxBuilder

A UTXOTxBuilder interface defines the functionality required to build account-based transactions. Most chain implementations require additional information, and this should be accepted during the construction of the chain-specific transaction builder.

type UTXOutpoint added in v0.2.2

type UTXOutpoint = utxo.Outpoint

A UTXOutpoint identifies a specific output produced by a transaction.

type UTXOutput added in v0.2.2

type UTXOutput = utxo.Output

A UTXOutput is produced by a transaction. It includes the conditions required to spend the output (called the pubkey script, based on Bitcoin).

Directories

Path Synopsis
api
account
Package account defines the Account API.
Package account defines the Account API.
address
Package address defines the Address API.
Package address defines the Address API.
contract
Package contract defines the Contract API.
Package contract defines the Contract API.
gas
Package gas defines the Gas API.
Package gas defines the Gas API.
utxo
Package utxo defines the UTXO API.
Package utxo defines the UTXO API.
chain
infra

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL