Skip to content

Commit

Permalink
Add support for Zcoin
Browse files Browse the repository at this point in the history
  • Loading branch information
riordant committed Jul 21, 2020
1 parent 56a1158 commit f0ecd87
Show file tree
Hide file tree
Showing 9 changed files with 262 additions and 9 deletions.
16 changes: 16 additions & 0 deletions chain/zcoin/zcoin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package zcoin

import (
"github.com/renproject/multichain/chain/bitcoin"
"github.com/renproject/multichain/compat/bitcoincompat"
)

// NewTxBuilder returns an implementation of the transaction builder interface
// from the Bitcoin Compat API, and exposes the functionality to build simple
// Zcoin transactions.
func NewTxBuilder() bitcoincompat.TxBuilder {
return bitcoin.NewTxBuilder()
}

// The Tx type is copied from Bitcoin.
type Tx = bitcoin.Tx
13 changes: 13 additions & 0 deletions chain/zcoin/zcoin_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package zcoin_test

import (
"testing"

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

func TestZcoin(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Zcoin Suite")
}
118 changes: 118 additions & 0 deletions chain/zcoin/zcoin_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package zcoin_test

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

"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcutil"
"github.com/renproject/id"
"github.com/renproject/multichain/chain/zcoin"
"github.com/renproject/multichain/compat/bitcoincompat"
"github.com/renproject/pack"

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

var _ = Describe("Zcoin", func() {
Context("when submitting transactions", func() {
Context("when sending XZC 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 ZCOIN_PK to the
// value specified in the `./multichaindeploy/.env` file.
pkEnv := os.Getenv("ZCOIN_PK")
if pkEnv == "" {
panic("ZCOIN_PK is undefined")
}
wif, err := btcutil.DecodeWIF(pkEnv)
Expect(err).ToNot(HaveOccurred())

// PKH
pkhAddr, err := btcutil.NewAddressPubKeyHash(btcutil.Hash160(wif.PrivKey.PubKey().SerializeCompressed()), &chaincfg.RegressionNetParams)
Expect(err).ToNot(HaveOccurred())
pkhAddrUncompressed, err := btcutil.NewAddressPubKeyHash(btcutil.Hash160(wif.PrivKey.PubKey().SerializeUncompressed()), &chaincfg.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 := bitcoincompat.NewClient(bitcoincompat.DefaultClientOptions().WithHost("http://127.0.0.1:19232"))
outputs, err := client.UnspentOutputs(context.Background(), 0, 999999999, pkhAddr)
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.
recipients := []bitcoincompat.Recipient{
{
Address: pkhAddr,
Value: pack.NewU64((output.Value.Uint64() - 1000) / 2),
},
{
Address: pkhAddrUncompressed,
Value: pack.NewU64((output.Value.Uint64() - 1000) / 2),
},
}
tx, err := zcoin.NewTxBuilder().BuildTx([]bitcoincompat.Output{output}, 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 Zcoin node. Again, this
// should be running a la `./multichaindeploy`.
txHash, 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 Zcoin 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())
})
})
})
})
12 changes: 11 additions & 1 deletion docker/docker-compose.env
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,14 @@ export DOGECOIN_ADDRESS=mwjUmhAW68zCtgZpW5b1xD5g7MZew6xPV4
# for which the private key is known by a test suite. This allows the test suite
# access to plenty of testing funds.
export ZCASH_PK=cNSVbbsAcBQ6BAmMr6yH6DLWr7QTDptHwdzpy4GYxGDkNZeKnczK
export ZCASH_ADDRESS=tmCTReBSJEDMWfFCkXXPMSB3EfuPg6SE9dw
export ZCASH_ADDRESS=tmCTReBSJEDMWfFCkXXPMSB3EfuPg6SE9dw

#
# Zcoin
#

# 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 ZCOIN_PK=cRgCPDGWj9mCKaZ9cd6VkUuVZZakomga4nVkLCH5r3xUXbxKNSci
export ZCOIN_ADDRESS=TTnPecgXLVLQedi2Y7ZrPSh54hnDGHmf1M
30 changes: 22 additions & 8 deletions docker/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,14 @@ services:
## Dogecoin
##

dogecoin:
build:
context: ./dogecoin
ports:
- "0.0.0.0:18332:18332"
entrypoint:
- "./root/run.sh"
- "${DOGECOIN_ADDRESS}"
#dogecoin:
# build:
# context: ./dogecoin
# ports:
# - "0.0.0.0:18332:18332"
# entrypoint:
# - "./root/run.sh"
# - "${DOGECOIN_ADDRESS}"
# ##
# ## Zcash
# ##
Expand All @@ -49,3 +49,17 @@ services:
# entrypoint:
# - "./root/run.sh"
# - "${ZCASH_ADDRESS}"

# ##
# ## Zcoin
# ##

zcoin:
build:
context: ./zcoin
ports:
- "0.0.0.0:19232:19232"
entrypoint:
- "./root/run.sh"
- "${ZCOIN_ADDRESS}"

48 changes: 48 additions & 0 deletions docker/zcoin/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
FROM ubuntu:xenial

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

# Install all of the dependencies that are needed to build zcoind and
# zcoin-cli from source.
RUN apt-get update && apt-get install -y \
automake \
bsdmainutils \
curl \
g++ \
git \
libboost-all-dev \
libevent-dev \
libssl-dev \
libtool \
libzmq3-dev \
make \
openjdk-8-jdk \
pkg-config \
zlib1g-dev \
libminizip-dev \
cmake \
libgmp-dev

# Install Berkeley DB 4.8
RUN curl -L http://download.oracle.com/berkeley-db/db-4.8.30.tar.gz | tar -xz -C /tmp && \
cd /tmp/db-4.8.30/build_unix && \
../dist/configure --enable-cxx --includedir=/usr/include/bdb4.8 --libdir=/usr/lib && \
make && make install && \
cd / && rm -rf /tmp/db-4.8.30

# Clone the repository and build.
RUN git clone https://github.com/zcoinofficial/zcoin && \
cd zcoin && \
./autogen.sh && \
./configure --without-gui --without-miniupnpc && \
make && \
make install

COPY zcoin.conf /root/.zcoin/zcoin.conf
COPY run.sh /root/run.sh
RUN chmod +x /root/run.sh

# Regtest network port
EXPOSE 19232

ENTRYPOINT ["./root/run.sh"]
22 changes: 22 additions & 0 deletions docker/zcoin/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/bin/bash
ADDRESS=$1

# Start
zcoind -conf=/root/.zcoin/zcoin.conf # -server -rpcbind=0.0.0.0 -rpcallowip=0.0.0.0/0 -rpcuser=user -rpcpassword=password
sleep 10

# Print setup
echo "ZCOIN_ADDRESS=$ADDRESS"

# Import the address
zcoin-cli importaddress $ADDRESS

# Generate enough blocks to pass the maturation time
zcoin-cli generatetoaddress 101 $ADDRESS

# Simulate mining
while :
do
zcoin-cli generatetoaddress 1 $ADDRESS
sleep 10
done
10 changes: 10 additions & 0 deletions docker/zcoin/zcoin.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]
rpcport=19232
2 changes: 2 additions & 0 deletions multichain.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const (
BTC = Asset("BTC") // Bitcoin
DOGE = Asset("DOGE") // Dogecoin
ETH = Asset("ETH") // Ether
XZC = Asset("XZC") // Zcoin
ZEC = Asset("ZEC") // Zcash
)

Expand Down Expand Up @@ -44,6 +45,7 @@ const (
BitcoinCash = Chain("BitcoinCash")
Ethereum = Chain("Ethereum")
Zcash = Chain("Zcash")
Zcoin = Chain("Zcoin")
)

// SizeHint returns the number of bytes required to represent the chain in
Expand Down

0 comments on commit f0ecd87

Please sign in to comment.