Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions chain/dash/address.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package dash

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

type (
AddressEncoder = bitcoin.AddressEncoder
AddressDecoder = bitcoin.AddressDecoder
AddressEncodeDecoder = bitcoin.AddressEncodeDecoder
)
1 change: 1 addition & 0 deletions chain/dash/address_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package dash_test
80 changes: 80 additions & 0 deletions chain/dash/dash.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package dash

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

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

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


// Address encoding magics
PubKeyHashAddrID: 0x00, // starts with 1
ScriptHashAddrID: 0x05, // starts with 3
PrivateKeyID: 0x80, // starts with 5 (uncompressed) or K (compressed)

// BIP32 hierarchical deterministic extended key magics
HDPrivateKeyID: [4]byte{0x04, 0x88, 0xad, 0xe4}, // starts with xprv
HDPublicKeyID: [4]byte{0x04, 0x88, 0xb2, 0x1e}, // starts with xpub

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

var TestNetParams = chaincfg.Params{
Name: "testnet",
Net: 0x0709110b,


// Address encoding magics
PubKeyHashAddrID: 0x6f, // starts with m or n
ScriptHashAddrID: 0xc4, // starts with 2
PrivateKeyID: 0xef, // starts with 9 (uncompressed) or c (compressed)

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

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

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

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

// Address encoding magics
PubKeyHashAddrID: 0x6f, // starts with m or n
ScriptHashAddrID: 0xc4, // starts with 2
PrivateKeyID: 0xef, // starts with 9 (uncompressed) or c (compressed)

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

// Human-readable part for Bech32 encoded segwit addresses, as defined in
// BIP 173. Dash does not actually support this, but we do not want to
// collide with real addresses, so we specify it.
Bech32HRPSegwit: "dashrt",
}
13 changes: 13 additions & 0 deletions chain/dash/dash_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package dash_test

import (
"testing"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

func TestDash(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Dash Suite")
}
123 changes: 123 additions & 0 deletions chain/dash/dash_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package dash_test

import (
"context"
"log"
"os"
"reflect"
"time"

"github.com/btcsuite/btcutil"
"github.com/renproject/id"
"github.com/renproject/multichain/api/address"
"github.com/renproject/multichain/api/utxo"
"github.com/renproject/multichain/chain/dash"
"github.com/renproject/pack"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

var _ = Describe("Dash", func() {
Context("when submitting transactions", func() {
Context("when sending DASH to multiple addresses", func() {
It("should work", func() {
// Load private key, and assume that the associated address has
// funds to spend. You can do this by setting DASH_PK to the
// value specified in the `./multichaindeploy/.env` file.
pkEnv := os.Getenv("DASH_PK")
if pkEnv == "" {
panic("DASH_PK is undefined")
}
wif, err := btcutil.DecodeWIF(pkEnv)
Expect(err).ToNot(HaveOccurred())

// PKH
pkhAddr, err := btcutil.NewAddressPubKeyHash(btcutil.Hash160(wif.PrivKey.PubKey().SerializeCompressed()), &dash.RegressionNetParams)
Expect(err).ToNot(HaveOccurred())
pkhAddrUncompressed, err := btcutil.NewAddressPubKeyHash(btcutil.Hash160(wif.PrivKey.PubKey().SerializeUncompressed()), &dash.RegressionNetParams)
Expect(err).ToNot(HaveOccurred())
log.Printf("PKH %v", pkhAddr.EncodeAddress())
log.Printf("PKH (uncompressed) %v", pkhAddrUncompressed.EncodeAddress())

// Setup the client and load the unspent transaction outputs.
client := dash.NewClient(dash.DefaultClientOptions().WithHost("http://127.0.0.1:21443"))
outputs, err := client.UnspentOutputs(context.Background(), 0, 999999999, address.Address(pkhAddr.EncodeAddress()))
Expect(err).ToNot(HaveOccurred())
Expect(len(outputs)).To(BeNumerically(">", 0))
output := outputs[0]

// Check that we can load the output and that it is equal.
// Otherwise, something strange is happening with the RPC
// client.
output2, _, err := client.Output(context.Background(), output.Outpoint)
Expect(err).ToNot(HaveOccurred())
Expect(reflect.DeepEqual(output, output2)).To(BeTrue())

// Build the transaction by consuming the outputs and spending
// them to a set of recipients.
inputs := []utxo.Input{
{Output: output},
}
recipients := []utxo.Recipient{
{
To: address.Address(pkhAddr.EncodeAddress()),
Value: pack.NewU256FromU64(pack.NewU64((output.Value.Int().Uint64() - 1000) / 2)),
},
{
To: address.Address(pkhAddrUncompressed.EncodeAddress()),
Value: pack.NewU256FromU64(pack.NewU64((output.Value.Int().Uint64() - 1000) / 2)),
},
}
tx, err := dash.NewTxBuilder(&dash.RegressionNetParams).BuildTx(inputs, recipients)
Expect(err).ToNot(HaveOccurred())

// Get the digests that need signing from the transaction, and
// sign them. In production, this would be done using the RZL
// MPC algorithm, but for the purposes of this test, using an
// explicit privkey is ok.
sighashes, err := tx.Sighashes()
signatures := make([]pack.Bytes65, len(sighashes))
Expect(err).ToNot(HaveOccurred())
for i := range sighashes {
hash := id.Hash(sighashes[i])
privKey := (*id.PrivKey)(wif.PrivKey)
signature, err := privKey.Sign(&hash)
Expect(err).ToNot(HaveOccurred())
signatures[i] = pack.NewBytes65(signature)
}
Expect(tx.Sign(signatures, pack.NewBytes(wif.SerializePubKey()))).To(Succeed())

// Submit the transaction to the Dash node. Again, this
// should be running a la `./multichaindeploy`.
txHash, err := tx.Hash()
Expect(err).ToNot(HaveOccurred())
err = client.SubmitTx(context.Background(), tx)
Expect(err).ToNot(HaveOccurred())
log.Printf("TXID %v", txHash)

for {
// Loop until the transaction has at least a few
// confirmations. This implies that the transaction is
// definitely valid, and the test has passed. We were
// successfully able to use the multichain to construct and
// submit a Dash transaction!
confs, err := client.Confirmations(context.Background(), txHash)
Expect(err).ToNot(HaveOccurred())
log.Printf(" %v/3 confirmations", confs)
if confs >= 3 {
break
}
time.Sleep(10 * time.Second)
}

// Check that we can load the output and that it is equal.
// Otherwise, something strange is happening with the RPC
// client.
output2, _, err = client.Output(context.Background(), output.Outpoint)
Expect(err).ToNot(HaveOccurred())
Expect(reflect.DeepEqual(output, output2)).To(BeTrue())
})
})
})
})
7 changes: 7 additions & 0 deletions chain/dash/gas.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package dash

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

type GasEstimator = bitcoin.GasEstimator

var NewGasEstimator = bitcoin.NewGasEstimator
1 change: 1 addition & 0 deletions chain/dash/gas_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package dash_test
16 changes: 16 additions & 0 deletions chain/dash/utxo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package dash

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
)
1 change: 1 addition & 0 deletions chain/dash/utxo_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package dash_test
10 changes: 10 additions & 0 deletions infra/.env
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,16 @@ export BITCOIN_ADDRESS=mwjUmhAW68zCtgZpW5b1xD5g7MZew6xPV4
export BITCOINCASH_PK=cSEohZFQLKuemNeBVrzwxniouUJJxdcx7Tm6HpspYuxraVjytieW
export BITCOINCASH_ADDRESS=bchreg:qp6tejc0ghtjeejcxa97amzvxvzacjt4qczpy2n3gf

#
# Dash
#

# 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 DASH_PK=cR7WgPTaRZXA24XANeYKZyfiFqbrTwaNXm26UJLVez5Zmc3TNDYu
export DASH_ADDRESS=ycbPd6Uk4BDw5iVYvQFV4xBPfyo6cFmUF5

#
# DigiByte
#
Expand Down
16 changes: 16 additions & 0 deletions infra/dash/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
FROM ubuntu:xenial

RUN apt-get update --fix-missing && apt-get install --yes software-properties-common wget

RUN wget -c https://github.com/dashpay/dash/releases/download/v0.15.0.0/dashcore-0.15.0.0-x86_64-linux-gnu.tar.gz -O - | tar xz
RUN mv ./dashcore-0.15.0 /app
RUN chmod +x /app/bin/dashd
RUN chmod +x /app/bin/dash-cli

COPY dash.conf /root/.dashcore/dash.conf
COPY run.sh /root/run.sh
RUN chmod +x /root/run.sh

EXPOSE 21443

ENTRYPOINT ["./root/run.sh"]
10 changes: 10 additions & 0 deletions infra/dash/dash.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
daemon=1
regtest=1
rpcuser=user
rpcpassword=password
rpcallowip=0.0.0.0/0
server=1
txindex=1

[regtest]
rpcbind=0.0.0.0
25 changes: 25 additions & 0 deletions infra/dash/keygen.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package main

import (
"fmt"

"github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcutil"

"github.com/renproject/id"
"github.com/renproject/multichain/chain/dash"
)

func main() {
privKey := id.NewPrivKey()
wif, err := btcutil.NewWIF((*btcec.PrivateKey)(privKey), &dash.RegressionNetParams, true)
if err != nil {
panic(err)
}
addrPubKeyHash, err := btcutil.NewAddressPubKeyHash(btcutil.Hash160(wif.SerializePubKey()), &dash.RegressionNetParams)
if err != nil {
panic(err)
}
fmt.Printf("DASH_PK=%v\n", wif)
fmt.Printf("DASH_ADDRESS=%v\n", addrPubKeyHash)
}
22 changes: 22 additions & 0 deletions infra/dash/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/bin/bash
ADDRESS=$1

# Start
/app/bin/dashd -conf=/root/.dashcore/dash.conf # -server -rpcbind=0.0.0.0 -rpcallowip=0.0.0.0/0 -rpcuser=user -rpcpassword=password
sleep 10

# Print setup
echo "DASH_ADDRESS=$ADDRESS"

# Import the address
/app/bin/dash-cli importaddress $ADDRESS

# Generate enough block to pass the maturation time
/app/bin/dash-cli generatetoaddress 101 $ADDRESS

# Simulate mining
while :
do
/app/bin/dash-cli generatetoaddress 1 $ADDRESS
sleep 10
done
12 changes: 12 additions & 0 deletions infra/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,18 @@ services:
- "./root/run.sh"
- "${BITCOINCASH_ADDRESS}"

#
# Dash
#
dash:
build:
context: ./dash
ports:
- "0.0.0.0:21443:21443"
entrypoint:
- "./root/run.sh"
- "${DASH_ADDRESS}"

#
# DigiByte
#
Expand Down
2 changes: 2 additions & 0 deletions multichain.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ const (
BNB = Asset("BNB") // Binance Coin
BTC = Asset("BTC") // Bitcoin
CELO = Asset("CELO") // Celo
DASH = Asset("DASH") // Dash Digital Cash
DGB = Asset("DGB") // DigiByte
DOGE = Asset("DOGE") // Dogecoin
ETH = Asset("ETH") // Ether
Expand Down Expand Up @@ -212,6 +213,7 @@ const (
Bitcoin = Chain("Bitcoin")
BitcoinCash = Chain("BitcoinCash")
Celo = Chain("Celo")
Dash = Chain("Dash")
DigiByte = Chain("DigiByte")
Dogecoin = Chain("Dogecoin")
Ethereum = Chain("Ethereum")
Expand Down