From d60af8a28ef9005fd6040de52d7425e94464ec75 Mon Sep 17 00:00:00 2001 From: Jacob Gadikian Date: Thu, 30 Jul 2020 23:23:12 +0700 Subject: [PATCH 01/12] Create Dockerfile --- docker/peercoin/Dockerfile | 106 +++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 docker/peercoin/Dockerfile diff --git a/docker/peercoin/Dockerfile b/docker/peercoin/Dockerfile new file mode 100644 index 00000000..4631d4e9 --- /dev/null +++ b/docker/peercoin/Dockerfile @@ -0,0 +1,106 @@ +# Build stage for BerkeleyDB +FROM alpine as berkeleydb + +RUN apk --no-cache add autoconf +RUN apk --no-cache add automake +RUN apk --no-cache add build-base +RUN apk --no-cache add openssl + +ENV BERKELEYDB_VERSION=db-4.8.30.NC +ENV BERKELEYDB_PREFIX=/opt/berkleydb + +RUN wget -q https://download.oracle.com/berkeley-db/${BERKELEYDB_VERSION}.tar.gz +RUN tar -xzf *.tar.gz +RUN sed s/__atomic_compare_exchange/__atomic_compare_exchange_db/g -i ${BERKELEYDB_VERSION}/dbinc/atomic.h +RUN mkdir -p ${BERKELEYDB_PREFIX} + +WORKDIR /${BERKELEYDB_VERSION}/build_unix + +RUN ../dist/configure --enable-cxx --disable-shared --with-pic --prefix=${BERKELEYDB_PREFIX} +RUN make -j4 +RUN make install +RUN rm -rf ${BERKELEYDB_PREFIX}/docs + +# Build stage for Peercoind +FROM alpine as peercoin-build + +COPY --from=berkeleydb /opt/ /opt/ + +RUN apk --no-cache add autoconf \ + automake \ + boost-dev \ + build-base \ + chrpath \ + file \ + libevent-dev \ + openssl \ + openssl-dev \ + libtool \ + linux-headers \ + zeromq-dev \ + protobuf-dev + +ENV PEERCOIN_VERSION=0.9.0ppc \ + PEERCOIN_SHA=95f4d50706f0eb2c14e3f3b149cbb3e7bfbc71ea33ef3e778dc9a477841a35b5 \ + PEERCOIN_PREFIX=/opt/peercoin + +RUN wget -q -O peercoin.tar.gz https://github.com/peercoin/peercoin/archive/v${PEERCOIN_VERSION}.tar.gz \ + && echo "${PEERCOIN_SHA} peercoin.tar.gz" | sha256sum -c - + +RUN tar -xzf peercoin.tar.gz + +WORKDIR /peercoin-${PEERCOIN_VERSION} + +# ./configure can't find berkley db unless we do this +RUN ln -s /opt/berkleydb /usr/include/db4.8 \ + && ln -s /opt/berkleydb/include/* /usr/include \ + && ln -s /opt/berkleydb/lib/* /usr/lib + +RUN ./autogen.sh +RUN ./configure \ + --prefix=${PEERCOIN_PREFIX} \ + --mandir=/usr/share/man \ + --disable-tests \ + --disable-bench \ + --disable-ccache \ + --with-gui=no \ + --with-utils \ + --with-libs \ + --with-daemon +RUN make -j4 +RUN make install +RUN strip ${PEERCOIN_PREFIX}/bin/peercoin-cli +RUN strip ${PEERCOIN_PREFIX}/bin/peercoin-tx +RUN strip ${PEERCOIN_PREFIX}/bin/peercoind +RUN strip ${PEERCOIN_PREFIX}/lib/libpeercoinconsensus.a +RUN strip ${PEERCOIN_PREFIX}/lib/libpeercoinconsensus.so.0.0.0 + +# Build stage for compiled artifacts +FROM alpine + +RUN adduser -S peercoin && \ + apk --no-cache add \ + boost \ + boost-program_options \ + libevent \ + openssl \ + libzmq \ + su-exec \ + bash + +ENV PPC_DATA=/data +ENV PEERCOIN_PREFIX=/opt/peercoin +ENV PATH=${PEERCOIN_PREFIX}/bin:$PATH +ENV RPC_PASSWORD=ppcpass +ENV RPC_USER=ppcuser + +COPY --from=peercoin-build /opt /opt +COPY docker-entrypoint.sh /entrypoint.sh + +VOLUME [${PPC_DATA}] + +EXPOSE 9901 9902 9903 9904 + +ENTRYPOINT ["/entrypoint.sh"] + +CMD ["peercoind"] From e15c536bfe695c7eb8c2a4858c0090928b7af842 Mon Sep 17 00:00:00 2001 From: Jacob Gadikian Date: Fri, 31 Jul 2020 01:10:21 +0700 Subject: [PATCH 02/12] Update multichain.go add Peercoin and PPC --- multichain.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/multichain.go b/multichain.go index 5339f502..05ca46af 100644 --- a/multichain.go +++ b/multichain.go @@ -15,6 +15,7 @@ const ( DOGE = Asset("DOGE") // Dogecoin ETH = Asset("ETH") // Ether ZEC = Asset("ZEC") // Zcash + PPC = Asset("PPC") // Peercoin ) // SizeHint returns the number of bytes required to represent the asset in @@ -46,6 +47,7 @@ const ( DigiByte = Chain("DigiByte") Ethereum = Chain("Ethereum") Zcash = Chain("Zcash") + Peercoin = Chain("Peercoin") ) // SizeHint returns the number of bytes required to represent the chain in From 8f3a45d3e0e7342b5c97f3f19799b2aec6a0ac74 Mon Sep 17 00:00:00 2001 From: Jacob Gadikian Date: Mon, 3 Aug 2020 18:40:22 +0700 Subject: [PATCH 03/12] added keypair --- docker/docker-compose.env | 7 +++++++ docker/docker-compose.yaml | 12 ++++++++++++ 2 files changed, 19 insertions(+) diff --git a/docker/docker-compose.env b/docker/docker-compose.env index 0bb3de10..030d2e7d 100644 --- a/docker/docker-compose.env +++ b/docker/docker-compose.env @@ -47,3 +47,10 @@ export ZCASH_ADDRESS=tmCTReBSJEDMWfFCkXXPMSB3EfuPg6SE9dw # suite access to plenty of testing funds. export DIGIBYTE_PK=eaeBaSNZmieYKXBwbwjUwzRazT45kVwi9Rubb13RJ1NgfDEUXXH8 export DIGIBYTE_ADDRESS=snhzjRDRwL5LD4ognxvjaP2St8G3MEExQp + +# +# Peercoin +# + +export PEERCOIN_PK=PJ27Wd2un9TrjDtcmpmx3tPn74xmCcxVy6 +export PEERCOIN_ADDRESS=U7BQX4wWxhoZaNxv91w27Uz7PeJY94Szwjr1U6tGbFENroeVby6N diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 73cdbd8f..cb36a84b 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -59,3 +59,15 @@ services: entrypoint: - "./root/run.sh" - "${ZCASH_ADDRESS}" + + ## + ## Zcash + ## + peercoin: + build: + context: ./zcash + ports: + - "0.0.0.0:6969:18232" + entrypoint: + - "./root/run.sh" + - "${PEERCOIN_ADDRESS}" \ No newline at end of file From d9163cfd9976adad708e1a850d0bfdcadc0caf63 Mon Sep 17 00:00:00 2001 From: Jacob Gadikian Date: Thu, 6 Aug 2020 23:27:25 +0700 Subject: [PATCH 04/12] not finished..... --- docker/docker-compose.yaml | 2 +- docker/peercoin/Dockerfile | 110 ++++------------------------------ docker/peercoin/peercoin.conf | 10 ++++ docker/peercoin/run.sh | 0 4 files changed, 22 insertions(+), 100 deletions(-) create mode 100644 docker/peercoin/peercoin.conf create mode 100644 docker/peercoin/run.sh diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index cb36a84b..ea1472d6 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -65,7 +65,7 @@ services: ## peercoin: build: - context: ./zcash + context: ./peercoin ports: - "0.0.0.0:6969:18232" entrypoint: diff --git a/docker/peercoin/Dockerfile b/docker/peercoin/Dockerfile index 4631d4e9..50830e2a 100644 --- a/docker/peercoin/Dockerfile +++ b/docker/peercoin/Dockerfile @@ -1,106 +1,18 @@ -# Build stage for BerkeleyDB -FROM alpine as berkeleydb +FROM debian:10 -RUN apk --no-cache add autoconf -RUN apk --no-cache add automake -RUN apk --no-cache add build-base -RUN apk --no-cache add openssl +RUN apt-get update +RUN apt-get install -y apt-transport-https wget gnupg -ENV BERKELEYDB_VERSION=db-4.8.30.NC -ENV BERKELEYDB_PREFIX=/opt/berkleydb +RUN sh -c "echo 'deb https://peercoin.github.io/deb-repo/ buster main' >> /etc/apt/sources.list.d/peercoin.list" && \ + wget -O - https://peercoin.github.io/deb-repo/peercoin.apt.key | apt-key add - -RUN wget -q https://download.oracle.com/berkeley-db/${BERKELEYDB_VERSION}.tar.gz -RUN tar -xzf *.tar.gz -RUN sed s/__atomic_compare_exchange/__atomic_compare_exchange_db/g -i ${BERKELEYDB_VERSION}/dbinc/atomic.h -RUN mkdir -p ${BERKELEYDB_PREFIX} +RUN apt-get update && \ + apt-get install -y peercoind peercoin-tx -WORKDIR /${BERKELEYDB_VERSION}/build_unix - -RUN ../dist/configure --enable-cxx --disable-shared --with-pic --prefix=${BERKELEYDB_PREFIX} -RUN make -j4 -RUN make install -RUN rm -rf ${BERKELEYDB_PREFIX}/docs - -# Build stage for Peercoind -FROM alpine as peercoin-build - -COPY --from=berkeleydb /opt/ /opt/ - -RUN apk --no-cache add autoconf \ - automake \ - boost-dev \ - build-base \ - chrpath \ - file \ - libevent-dev \ - openssl \ - openssl-dev \ - libtool \ - linux-headers \ - zeromq-dev \ - protobuf-dev - -ENV PEERCOIN_VERSION=0.9.0ppc \ - PEERCOIN_SHA=95f4d50706f0eb2c14e3f3b149cbb3e7bfbc71ea33ef3e778dc9a477841a35b5 \ - PEERCOIN_PREFIX=/opt/peercoin - -RUN wget -q -O peercoin.tar.gz https://github.com/peercoin/peercoin/archive/v${PEERCOIN_VERSION}.tar.gz \ - && echo "${PEERCOIN_SHA} peercoin.tar.gz" | sha256sum -c - - -RUN tar -xzf peercoin.tar.gz - -WORKDIR /peercoin-${PEERCOIN_VERSION} - -# ./configure can't find berkley db unless we do this -RUN ln -s /opt/berkleydb /usr/include/db4.8 \ - && ln -s /opt/berkleydb/include/* /usr/include \ - && ln -s /opt/berkleydb/lib/* /usr/lib - -RUN ./autogen.sh -RUN ./configure \ - --prefix=${PEERCOIN_PREFIX} \ - --mandir=/usr/share/man \ - --disable-tests \ - --disable-bench \ - --disable-ccache \ - --with-gui=no \ - --with-utils \ - --with-libs \ - --with-daemon -RUN make -j4 -RUN make install -RUN strip ${PEERCOIN_PREFIX}/bin/peercoin-cli -RUN strip ${PEERCOIN_PREFIX}/bin/peercoin-tx -RUN strip ${PEERCOIN_PREFIX}/bin/peercoind -RUN strip ${PEERCOIN_PREFIX}/lib/libpeercoinconsensus.a -RUN strip ${PEERCOIN_PREFIX}/lib/libpeercoinconsensus.so.0.0.0 - -# Build stage for compiled artifacts -FROM alpine - -RUN adduser -S peercoin && \ - apk --no-cache add \ - boost \ - boost-program_options \ - libevent \ - openssl \ - libzmq \ - su-exec \ - bash - -ENV PPC_DATA=/data -ENV PEERCOIN_PREFIX=/opt/peercoin -ENV PATH=${PEERCOIN_PREFIX}/bin:$PATH -ENV RPC_PASSWORD=ppcpass -ENV RPC_USER=ppcuser - -COPY --from=peercoin-build /opt /opt -COPY docker-entrypoint.sh /entrypoint.sh - -VOLUME [${PPC_DATA}] +COPY peercoin.conf /root/.peercoin/peercoin.conf +COPY run.sh /root/run.sh +RUN chmod +x /root/run.sh EXPOSE 9901 9902 9903 9904 -ENTRYPOINT ["/entrypoint.sh"] - -CMD ["peercoind"] +ENTRYPOINT ["./root/run.sh"] diff --git a/docker/peercoin/peercoin.conf b/docker/peercoin/peercoin.conf new file mode 100644 index 00000000..db039118 --- /dev/null +++ b/docker/peercoin/peercoin.conf @@ -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 \ No newline at end of file diff --git a/docker/peercoin/run.sh b/docker/peercoin/run.sh new file mode 100644 index 00000000..e69de29b From f1942013ae13d955e075fd214f74728db3a069bf Mon Sep 17 00:00:00 2001 From: Jacob Gadikian Date: Sun, 9 Aug 2020 12:04:37 +0700 Subject: [PATCH 05/12] fixed naming --- docker/docker-compose.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index ea1472d6..9dd6dfc9 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -61,7 +61,7 @@ services: - "${ZCASH_ADDRESS}" ## - ## Zcash + ## Peercoin ## peercoin: build: From c78c5532f9ad00d30234f414342c41fc91ac948b Mon Sep 17 00:00:00 2001 From: Jacob Gadikian Date: Sun, 23 Aug 2020 18:27:11 +0700 Subject: [PATCH 06/12] Task should now be reduced to adding a timestamp to transactions --- .DS_Store | Bin 0 -> 6148 bytes chain/peercoin/address.go | 54 +++++ chain/peercoin/address_test.go | 1 + chain/peercoin/bitcoin.go | 297 +++++++++++++++++++++++++++ chain/peercoin/bitcoin_suite_test.go | 13 ++ chain/peercoin/bitcoin_test.go | 140 +++++++++++++ chain/peercoin/gas.go | 32 +++ chain/peercoin/gas_test.go | 1 + chain/peercoin/utxo.go | 210 +++++++++++++++++++ chain/peercoin/utxo_test.go | 1 + go.mod | 14 +- 11 files changed, 756 insertions(+), 7 deletions(-) create mode 100644 .DS_Store create mode 100644 chain/peercoin/address.go create mode 100644 chain/peercoin/address_test.go create mode 100644 chain/peercoin/bitcoin.go create mode 100644 chain/peercoin/bitcoin_suite_test.go create mode 100644 chain/peercoin/bitcoin_test.go create mode 100644 chain/peercoin/gas.go create mode 100644 chain/peercoin/gas_test.go create mode 100644 chain/peercoin/utxo.go create mode 100644 chain/peercoin/utxo_test.go diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..87eec981ad7e7716a801c01e6d72b387d2f537af GIT binary patch literal 6148 zcmeHKOR54f5bS;`2yR@u%oW}Zg6Ik60*ZnPGJ@b+&&qRoG^;= uint32(len(resp.Vout)) { + return utxo.Output{}, pack.NewU64(0), fmt.Errorf("bad index: %v is out of range", outpoint.Index) + } + vout := resp.Vout[outpoint.Index.Uint32()] + amount, err := btcutil.NewAmount(vout.Value) + if err != nil { + return utxo.Output{}, pack.NewU64(0), fmt.Errorf("bad amount: %v", err) + } + if amount < 0 { + return utxo.Output{}, pack.NewU64(0), fmt.Errorf("bad amount: %v", amount) + } + pubKeyScript, err := hex.DecodeString(vout.ScriptPubKey.Hex) + if err != nil { + return utxo.Output{}, pack.NewU64(0), fmt.Errorf("bad pubkey script: %v", err) + } + output := utxo.Output{ + Outpoint: outpoint, + Value: pack.NewU256FromU64(pack.NewU64(uint64(amount))), + PubKeyScript: pack.NewBytes(pubKeyScript), + } + return output, pack.NewU64(resp.Confirmations), nil +} + +// SubmitTx to the Bitcoin network. +func (client *client) SubmitTx(ctx context.Context, tx utxo.Tx) error { + serial, err := tx.Serialize() + if err != nil { + return fmt.Errorf("bad tx: %v", err) + } + resp := "" + if err := client.send(ctx, &resp, "sendrawtransaction", hex.EncodeToString(serial)); err != nil { + return fmt.Errorf("bad \"sendrawtransaction\": %v", err) + } + return nil +} + +// UnspentOutputs spendable by the given address. +func (client *client) UnspentOutputs(ctx context.Context, minConf, maxConf int64, addr address.Address) ([]utxo.Output, error) { + resp := []btcjson.ListUnspentResult{} + if err := client.send(ctx, &resp, "listunspent", minConf, maxConf, []string{string(addr)}); err != nil && err != io.EOF { + return []utxo.Output{}, fmt.Errorf("bad \"listunspent\": %v", err) + } + outputs := make([]utxo.Output, len(resp)) + for i := range outputs { + amount, err := btcutil.NewAmount(resp[i].Amount) + if err != nil { + return []utxo.Output{}, fmt.Errorf("bad amount: %v", err) + } + if amount < 0 { + return []utxo.Output{}, fmt.Errorf("bad amount: %v", amount) + } + pubKeyScript, err := hex.DecodeString(resp[i].ScriptPubKey) + if err != nil { + return []utxo.Output{}, fmt.Errorf("bad pubkey script: %v", err) + } + txid, err := chainhash.NewHashFromStr(resp[i].TxID) + if err != nil { + return []utxo.Output{}, fmt.Errorf("bad txid: %v", err) + } + outputs[i] = utxo.Output{ + Outpoint: utxo.Outpoint{ + Hash: pack.NewBytes(txid[:]), + Index: pack.NewU32(resp[i].Vout), + }, + Value: pack.NewU256FromU64(pack.NewU64(uint64(amount))), + PubKeyScript: pack.NewBytes(pubKeyScript), + } + } + return outputs, nil +} + +// Confirmations of a transaction in the Bitcoin network. +func (client *client) Confirmations(ctx context.Context, txHash pack.Bytes) (int64, error) { + resp := btcjson.GetTransactionResult{} + + size := len(txHash) + txHashReversed := make([]byte, size) + copy(txHashReversed[:], txHash[:]) + for i := 0; i < size/2; i++ { + txHashReversed[i], txHashReversed[size-1-i] = txHashReversed[size-1-i], txHashReversed[i] + } + + if err := client.send(ctx, &resp, "gettransaction", hex.EncodeToString(txHashReversed)); err != nil { + return 0, fmt.Errorf("bad \"gettransaction\": %v", err) + } + confirmations := resp.Confirmations + if confirmations < 0 { + confirmations = 0 + } + return confirmations, nil +} + +func (client *client) send(ctx context.Context, resp interface{}, method string, params ...interface{}) error { + // Encode the request. + data, err := encodeRequest(method, params) + if err != nil { + return err + } + + return retry(ctx, client.opts.TimeoutRetry, func() error { + // Create request and add basic authentication headers. The context is + // not attached to the request, and instead we all each attempt to run + // for the timeout duration, and we keep attempting until success, or + // the context is done. + req, err := http.NewRequest("POST", client.opts.Host, bytes.NewBuffer(data)) + if err != nil { + return fmt.Errorf("building http request: %v", err) + } + req.SetBasicAuth(client.opts.User, client.opts.Password) + + // Send the request and decode the response. + res, err := client.httpClient.Do(req) + if err != nil { + return fmt.Errorf("sending http request: %v", err) + } + defer res.Body.Close() + if err := decodeResponse(resp, res.Body); err != nil { + return fmt.Errorf("decoding http response: %v", err) + } + return nil + }) +} + +func encodeRequest(method string, params []interface{}) ([]byte, error) { + rawParams, err := json.Marshal(params) + if err != nil { + return nil, fmt.Errorf("encoding params: %v", err) + } + req := struct { + Version string `json:"version"` + ID int `json:"id"` + Method string `json:"method"` + Params json.RawMessage `json:"params"` + }{ + Version: "2.0", + ID: rand.Int(), + Method: method, + Params: rawParams, + } + rawReq, err := json.Marshal(req) + if err != nil { + return nil, fmt.Errorf("encoding request: %v", err) + } + return rawReq, nil +} + +func decodeResponse(resp interface{}, r io.Reader) error { + res := struct { + Version string `json:"version"` + ID int `json:"id"` + Result *json.RawMessage `json:"result"` + Error *json.RawMessage `json:"error"` + }{} + if err := json.NewDecoder(r).Decode(&res); err != nil { + return fmt.Errorf("decoding response: %v", err) + } + if res.Error != nil { + return fmt.Errorf("decoding response: %v", string(*res.Error)) + } + if res.Result == nil { + return fmt.Errorf("decoding result: result is nil") + } + if err := json.Unmarshal(*res.Result, resp); err != nil { + return fmt.Errorf("decoding result: %v", err) + } + return nil +} + +func retry(ctx context.Context, dur time.Duration, f func() error) error { + ticker := time.NewTicker(dur) + err := f() + for err != nil { + log.Printf("retrying: %v", err) + select { + case <-ctx.Done(): + return ctx.Err() + case <-ticker.C: + err = f() + } + } + return nil +} diff --git a/chain/peercoin/bitcoin_suite_test.go b/chain/peercoin/bitcoin_suite_test.go new file mode 100644 index 00000000..62769f86 --- /dev/null +++ b/chain/peercoin/bitcoin_suite_test.go @@ -0,0 +1,13 @@ +package peercoin_test + +import ( + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +func TestPeercoin(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Bitcoin Suite") +} diff --git a/chain/peercoin/bitcoin_test.go b/chain/peercoin/bitcoin_test.go new file mode 100644 index 00000000..aefdc097 --- /dev/null +++ b/chain/peercoin/bitcoin_test.go @@ -0,0 +1,140 @@ +package peercoin_test + +import ( + "context" + "log" + "os" + "reflect" + "time" + + "github.com/btcsuite/btcd/chaincfg" + "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/bitcoin" + "github.com/renproject/pack" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Bitcoin", func() { + Context("when submitting transactions", func() { + Context("when sending BTC 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 BITCOIN_PK to the + // value specified in the `./multichaindeploy/.env` file. + pkEnv := os.Getenv("BITCOIN_PK") + if pkEnv == "" { + panic("BITCOIN_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()) + + // WPKH + wpkAddr, err := btcutil.NewAddressWitnessPubKeyHash([]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19}, &chaincfg.RegressionNetParams) + Expect(err).ToNot(HaveOccurred()) + log.Printf("WPKH %v", wpkAddr.EncodeAddress()) + + // Setup the client and load the unspent transaction outputs. + client := bitcoin.NewClient(bitcoin.DefaultClientOptions()) + 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: utxo.Output{ + Outpoint: utxo.Outpoint{ + Hash: output.Outpoint.Hash[:], + Index: output.Outpoint.Index, + }, + PubKeyScript: output.PubKeyScript, + Value: output.Value, + }}, + } + recipients := []utxo.Recipient{ + { + To: address.Address(pkhAddr.EncodeAddress()), + Value: pack.NewU256FromU64(pack.NewU64((output.Value.Int().Uint64() - 1000) / 3)), + }, + { + To: address.Address(pkhAddrUncompressed.EncodeAddress()), + Value: pack.NewU256FromU64(pack.NewU64((output.Value.Int().Uint64() - 1000) / 3)), + }, + { + To: address.Address(wpkAddr.EncodeAddress()), + Value: pack.NewU256FromU64(pack.NewU64((output.Value.Int().Uint64() - 1000) / 3)), + }, + } + tx, err := bitcoin.NewTxBuilder(&chaincfg.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 Bitcoin 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 Bitcoin 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()) + }) + }) + }) +}) diff --git a/chain/peercoin/gas.go b/chain/peercoin/gas.go new file mode 100644 index 00000000..dd9bb2ce --- /dev/null +++ b/chain/peercoin/gas.go @@ -0,0 +1,32 @@ +package peercoin + +import ( + "context" + + "github.com/renproject/pack" +) + +// A GasEstimator returns the SATs-per-byte that is needed in order to confirm +// transactions with an estimated maximum delay of one block. In distributed +// networks that collectively build, sign, and submit transactions, it is +// important that all nodes in the network have reached consensus on the +// SATs-per-byte. +type GasEstimator struct { + satsPerByte pack.U256 +} + +// NewGasEstimator returns a simple gas estimator that always returns the given +// number of SATs-per-byte. +func NewGasEstimator(satsPerByte pack.U256) GasEstimator { + return GasEstimator{ + satsPerByte: satsPerByte, + } +} + +// GasPrice returns the number of SATs-per-byte that is needed in order to +// confirm transactions with an estimated maximum delay of one block. It is the +// responsibility of the caller to know the number of bytes in their +// transaction. +func (gasEstimator GasEstimator) GasPrice(_ context.Context) (pack.U256, error) { + return gasEstimator.satsPerByte, nil +} diff --git a/chain/peercoin/gas_test.go b/chain/peercoin/gas_test.go new file mode 100644 index 00000000..82bc2209 --- /dev/null +++ b/chain/peercoin/gas_test.go @@ -0,0 +1 @@ +package peercoin_test diff --git a/chain/peercoin/utxo.go b/chain/peercoin/utxo.go new file mode 100644 index 00000000..03ab6d16 --- /dev/null +++ b/chain/peercoin/utxo.go @@ -0,0 +1,210 @@ +package peercoin + +import ( + "bytes" + "fmt" + "math/big" + + "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" + "github.com/renproject/multichain/api/utxo" + "github.com/renproject/pack" +) + +// Version of Bitcoin transactions supported by the multichain. +const Version int32 = 2 + +// The TxBuilder is an implementation of a UTXO-compatible transaction builder +// for Bitcoin. +type TxBuilder struct { + params *chaincfg.Params +} + +// NewTxBuilder returns a transaction builder that builds UTXO-compatible +// Bitcoin transactions for the given chain configuration (this means that it +// can be used for regnet, testnet, and mainnet, but also for networks that are +// minimally modified forks of the Bitcoin network). +func NewTxBuilder(params *chaincfg.Params) TxBuilder { + return TxBuilder{params: params} +} + +// BuildTx returns a Bitcoin transaction that consumes funds from the given +// inputs, and sends them to the given recipients. The difference in the sum +// value of the inputs and the sum value of the recipients is paid as a fee to +// the Bitcoin network. This fee must be calculated independently of this +// function. Outputs produced for recipients will use P2PKH, P2SH, P2WPKH, or +// P2WSH scripts as the pubkey script, based on the format of the recipient +// address. +func (txBuilder TxBuilder) BuildTx(inputs []utxo.Input, recipients []utxo.Recipient) (utxo.Tx, error) { + msgTx := wire.NewMsgTx(Version) + + // Inputs + for _, input := range inputs { + hash := chainhash.Hash{} + copy(hash[:], input.Hash) + index := input.Index.Uint32() + msgTx.AddTxIn(wire.NewTxIn(wire.NewOutPoint(&hash, index), nil, nil)) + } + + // Outputs + for _, recipient := range recipients { + addr, err := btcutil.DecodeAddress(string(recipient.To), txBuilder.params) + if err != nil { + return nil, err + } + script, err := txscript.PayToAddrScript(addr) + if err != nil { + return nil, err + } + value := recipient.Value.Int().Int64() + if value < 0 { + return nil, fmt.Errorf("expected value >= 0, got value %v", value) + } + msgTx.AddTxOut(wire.NewTxOut(value, script)) + } + + return &Tx{inputs: inputs, recipients: recipients, msgTx: msgTx, signed: false}, nil +} + +// Tx represents a simple Bitcoin transaction that implements the Bitcoin Compat +// API. +type Tx struct { + inputs []utxo.Input + recipients []utxo.Recipient + + msgTx *wire.MsgTx + + signed bool +} + +func (tx *Tx) Hash() (pack.Bytes, error) { + txhash := tx.msgTx.TxHash() + return pack.NewBytes(txhash[:]), nil +} + +func (tx *Tx) Inputs() ([]utxo.Input, error) { + return tx.inputs, nil +} + +func (tx *Tx) Outputs() ([]utxo.Output, error) { + hash, err := tx.Hash() + if err != nil { + return nil, fmt.Errorf("bad hash: %v", err) + } + outputs := make([]utxo.Output, len(tx.msgTx.TxOut)) + for i := range outputs { + outputs[i].Outpoint = utxo.Outpoint{ + Hash: hash, + Index: pack.NewU32(uint32(i)), + } + outputs[i].PubKeyScript = pack.Bytes(tx.msgTx.TxOut[i].PkScript) + if tx.msgTx.TxOut[i].Value < 0 { + return nil, fmt.Errorf("bad output %v: value is less than zero", i) + } + outputs[i].Value = pack.NewU256FromU64(pack.NewU64(uint64(tx.msgTx.TxOut[i].Value))) + } + return outputs, nil +} + +// Sighashes returns the digests that must be signed before the transaction +// can be submitted by the client. All transactions assume that the f +func (tx *Tx) Sighashes() ([]pack.Bytes32, error) { + sighashes := make([]pack.Bytes32, len(tx.inputs)) + + for i, txin := range tx.inputs { + pubKeyScript := txin.PubKeyScript + sigScript := txin.SigScript + value := txin.Value.Int().Int64() + if value < 0 { + return []pack.Bytes32{}, fmt.Errorf("expected value >= 0, got value %v", value) + } + + var hash []byte + var err error + if sigScript == nil { + if txscript.IsPayToWitnessPubKeyHash(pubKeyScript) { + hash, err = txscript.CalcWitnessSigHash(pubKeyScript, txscript.NewTxSigHashes(tx.msgTx), txscript.SigHashAll, tx.msgTx, i, value) + } else { + hash, err = txscript.CalcSignatureHash(pubKeyScript, txscript.SigHashAll, tx.msgTx, i) + } + } else { + if txscript.IsPayToWitnessScriptHash(pubKeyScript) { + hash, err = txscript.CalcWitnessSigHash(sigScript, txscript.NewTxSigHashes(tx.msgTx), txscript.SigHashAll, tx.msgTx, i, value) + } else { + hash, err = txscript.CalcSignatureHash(sigScript, txscript.SigHashAll, tx.msgTx, i) + } + } + if err != nil { + return []pack.Bytes32{}, err + } + + sighash := [32]byte{} + copy(sighash[:], hash) + sighashes[i] = pack.NewBytes32(sighash) + } + + return sighashes, nil +} + +func (tx *Tx) Sign(signatures []pack.Bytes65, pubKey pack.Bytes) error { + if tx.signed { + return fmt.Errorf("already signed") + } + if len(signatures) != len(tx.msgTx.TxIn) { + return fmt.Errorf("expected %v signatures, got %v signatures", len(tx.msgTx.TxIn), len(signatures)) + } + + for i, rsv := range signatures { + var err error + + // Decode the signature and the pubkey script. + r := new(big.Int).SetBytes(rsv[:32]) + s := new(big.Int).SetBytes(rsv[32:64]) + signature := btcec.Signature{ + R: r, + S: s, + } + pubKeyScript := tx.inputs[i].Output.PubKeyScript + sigScript := tx.inputs[i].SigScript + + // Support segwit. + if sigScript == nil { + if txscript.IsPayToWitnessPubKeyHash(pubKeyScript) || txscript.IsPayToWitnessScriptHash(pubKeyScript) { + tx.msgTx.TxIn[i].Witness = wire.TxWitness([][]byte{append(signature.Serialize(), byte(txscript.SigHashAll)), pubKey}) + continue + } + } else { + if txscript.IsPayToWitnessScriptHash(sigScript) || txscript.IsPayToWitnessScriptHash(sigScript) { + tx.msgTx.TxIn[i].Witness = wire.TxWitness([][]byte{append(signature.Serialize(), byte(txscript.SigHashAll)), pubKey, sigScript}) + continue + } + } + + // Support non-segwit + builder := txscript.NewScriptBuilder() + builder.AddData(append(signature.Serialize(), byte(txscript.SigHashAll))) + builder.AddData(pubKey) + if sigScript != nil { + builder.AddData(sigScript) + } + tx.msgTx.TxIn[i].SignatureScript, err = builder.Script() + if err != nil { + return err + } + } + + tx.signed = true + return nil +} + +func (tx *Tx) Serialize() (pack.Bytes, error) { + buf := new(bytes.Buffer) + if err := tx.msgTx.Serialize(buf); err != nil { + return pack.Bytes{}, err + } + return pack.NewBytes(buf.Bytes()), nil +} diff --git a/chain/peercoin/utxo_test.go b/chain/peercoin/utxo_test.go new file mode 100644 index 00000000..82bc2209 --- /dev/null +++ b/chain/peercoin/utxo_test.go @@ -0,0 +1 @@ +package peercoin_test diff --git a/go.mod b/go.mod index 9d1029f0..0929ab7f 100644 --- a/go.mod +++ b/go.mod @@ -6,31 +6,31 @@ require ( github.com/btcsuite/btcd v0.20.1-beta github.com/btcsuite/btcutil v1.0.2 github.com/codahale/blake2 v0.0.0-20150924215134-8d10d0420cbf - github.com/cosmos/cosmos-sdk v0.39.1 + github.com/cosmos/cosmos-sdk v0.39.1 // indirect github.com/drand/drand v1.0.3-0.20200714175734-29705eaf09d4 // indirect github.com/ethereum/go-ethereum v1.9.19 - github.com/filecoin-project/go-address v0.0.3 + github.com/filecoin-project/go-address v0.0.3 // indirect github.com/filecoin-project/go-amt-ipld v0.0.0-20191205011053-79efc22d6cdc // indirect github.com/filecoin-project/go-amt-ipld/v2 v2.1.0 // indirect github.com/filecoin-project/go-bitfield v0.1.0 // indirect github.com/filecoin-project/go-data-transfer v0.5.0 // indirect github.com/filecoin-project/go-fil-markets v0.3.2 // indirect - github.com/filecoin-project/lotus v0.4.1 + github.com/filecoin-project/lotus v0.4.1 // indirect github.com/filecoin-project/sector-storage v0.0.0-20200723200950-ed2e57dde6df // indirect - github.com/filecoin-project/specs-actors v0.6.2-0.20200724193152-534b25bdca30 + github.com/filecoin-project/specs-actors v0.6.2-0.20200724193152-534b25bdca30 // indirect github.com/hannahhoward/cbor-gen-for v0.0.0-20200723175505-5892b522820a // indirect github.com/ipfs/go-ds-badger2 v0.1.1-0.20200708190120-187fc06f714e // indirect github.com/ipfs/go-hamt-ipld v0.1.1 // indirect github.com/lib/pq v1.7.0 // indirect - github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 + github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 // indirect github.com/onsi/ginkgo v1.14.0 github.com/onsi/gomega v1.10.1 github.com/raulk/clock v1.1.0 // indirect github.com/renproject/id v0.4.2 github.com/renproject/pack v0.2.3 github.com/renproject/surge v1.2.5 - github.com/tendermint/tendermint v0.33.8 - github.com/terra-project/core v0.3.7 + github.com/tendermint/tendermint v0.33.8 // indirect + github.com/terra-project/core v0.3.7 // indirect github.com/xorcare/golden v0.6.1-0.20191112154924-b87f686d7542 // indirect go.uber.org/zap v1.15.0 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 From 6aa5d96e47828b4befc42477533017d3ceed1e3e Mon Sep 17 00:00:00 2001 From: Jacob Gadikian Date: Sun, 23 Aug 2020 19:26:48 +0700 Subject: [PATCH 07/12] Peercoin integration --- chain/peercoin/{bitcoin.go => peercoin.go} | 6 ++--- ...n_suite_test.go => peercoin_suite_test.go} | 0 .../{bitcoin_test.go => peercoin_test.go} | 0 go.mod | 2 ++ go.sum | 4 ++++ infra/peercoin/run.sh | 22 +++++++++++++++++++ test.sh | 6 ++--- 7 files changed, 34 insertions(+), 6 deletions(-) rename chain/peercoin/{bitcoin.go => peercoin.go} (98%) rename chain/peercoin/{bitcoin_suite_test.go => peercoin_suite_test.go} (100%) rename chain/peercoin/{bitcoin_test.go => peercoin_test.go} (100%) diff --git a/chain/peercoin/bitcoin.go b/chain/peercoin/peercoin.go similarity index 98% rename from chain/peercoin/bitcoin.go rename to chain/peercoin/peercoin.go index a366de21..8a353232 100644 --- a/chain/peercoin/bitcoin.go +++ b/chain/peercoin/peercoin.go @@ -12,9 +12,9 @@ import ( "net/http" "time" - "github.com/btcsuite/btcd/btcjson" - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcutil" + "github.com/ppcsuite/ppcd/btcjson" + "github.com/ppcsuite/ppcd/chaincfg/chainhash" + "github.com/ppcsuite/btcutil" "github.com/renproject/multichain/api/address" "github.com/renproject/multichain/api/utxo" "github.com/renproject/pack" diff --git a/chain/peercoin/bitcoin_suite_test.go b/chain/peercoin/peercoin_suite_test.go similarity index 100% rename from chain/peercoin/bitcoin_suite_test.go rename to chain/peercoin/peercoin_suite_test.go diff --git a/chain/peercoin/bitcoin_test.go b/chain/peercoin/peercoin_test.go similarity index 100% rename from chain/peercoin/bitcoin_test.go rename to chain/peercoin/peercoin_test.go diff --git a/go.mod b/go.mod index 0929ab7f..2d2a1895 100644 --- a/go.mod +++ b/go.mod @@ -25,6 +25,8 @@ require ( github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 // indirect github.com/onsi/ginkgo v1.14.0 github.com/onsi/gomega v1.10.1 + github.com/ppcsuite/btcutil v0.0.0-20190610081709-8b47fe3bbbff + github.com/ppcsuite/ppcd v0.0.0-20190610081647-e4b42de6f07b github.com/raulk/clock v1.1.0 // indirect github.com/renproject/id v0.4.2 github.com/renproject/pack v0.2.3 diff --git a/go.sum b/go.sum index 690ddb94..22283960 100644 --- a/go.sum +++ b/go.sum @@ -1072,6 +1072,10 @@ github.com/polydawn/refmt v0.0.0-20190408063855-01bf1e26dd14/go.mod h1:uIp+gprXx github.com/polydawn/refmt v0.0.0-20190807091052-3d65705ee9f1/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o= github.com/polydawn/refmt v0.0.0-20190809202753-05966cbd336a/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/ppcsuite/btcutil v0.0.0-20190610081709-8b47fe3bbbff h1:T4NTgqtQTBV2EarPGWWamqzRPZJHN2BEkjQbfX6Ovi4= +github.com/ppcsuite/btcutil v0.0.0-20190610081709-8b47fe3bbbff/go.mod h1:JGdyKoOvrtQNw3D9HTVr6tbgBqHtSGred/tI/6K/B5k= +github.com/ppcsuite/ppcd v0.0.0-20190610081647-e4b42de6f07b h1:GG93OuO35ulaSJ4n7TJEMcv/vWFJ14Orne9M8nThBak= +github.com/ppcsuite/ppcd v0.0.0-20190610081647-e4b42de6f07b/go.mod h1:HMuaHlBY91GvRvDCyAuZyohCsojm3M/h0Ksz1hluSCI= github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= diff --git a/infra/peercoin/run.sh b/infra/peercoin/run.sh index e69de29b..dc27826f 100644 --- a/infra/peercoin/run.sh +++ b/infra/peercoin/run.sh @@ -0,0 +1,22 @@ +#!/bin/bash +ADDRESS=$1 + +# Start +/app/bin/peercoind +sleep 10 + +# Print setup +echo "BITCOIN_ADDRESS=$ADDRESS" + +# Import the address +/app/bin/peercoin-cli importaddress $ADDRESS + +# Generate enough block to pass the maturation time +/app/bin/peercoin-cli generatetoaddress 101 $ADDRESS + +# Simulate mining +while : +do + /app/bin/peercoin-cli generatetoaddress 1 $ADDRESS + sleep 10 +done \ No newline at end of file diff --git a/test.sh b/test.sh index dce14a35..e0b03f1e 100755 --- a/test.sh +++ b/test.sh @@ -1,7 +1,7 @@ -source ./docker/docker-compose.env -docker-compose -f ./docker/docker-compose.yaml up --build -d +source ./infra/.env +docker-compose -f ./infra/docker-compose.yaml up --build -d echo "Waiting for multichain to boot..." sleep 30 go test -v ./... -docker-compose -f ./docker/docker-compose.yaml down +docker-compose -f ./infra/docker-compose.yaml down echo "Done!" \ No newline at end of file From 70c3580293dd995566d19f5da62ec02cf2df3063 Mon Sep 17 00:00:00 2001 From: Jacob Gadikian Date: Sun, 23 Aug 2020 19:37:13 +0700 Subject: [PATCH 08/12] add peercoin, fix ports --- infra/docker-compose.yaml | 49 ++++++++++++++++++++------------------- multichain.go | 4 +++- 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/infra/docker-compose.yaml b/infra/docker-compose.yaml index 0c525292..258a9850 100644 --- a/infra/docker-compose.yaml +++ b/infra/docker-compose.yaml @@ -1,3 +1,5 @@ +# As in other parts of multichain, chains should be in alphabetical order. + version: "2" services: # @@ -73,42 +75,29 @@ services: - "/root/run.sh" # - # Solana + # Bitcoin # - solana: + peercoin: build: - context: ./solana + context: ./peercoin ports: - - "0.0.0.0:8899:8899" - - "0.0.0.0:8900:8900" - - "0.0.0.0:9900:9900" + - "0.0.0.0:9902:9902" entrypoint: - "./root/run.sh" + - "${PEERCOIN_ADDRESS}" # - # Zcash + # Solana # - zcash: - build: - context: ./zcash - ports: - - "0.0.0.0:18232:18232" - entrypoint: - - "./root/run.sh" - - "${ZCASH_ADDRESS}" - - ## - ## Peercoin - ## - peercoin: + solana: build: - context: ./peercoin + context: ./solana ports: - - "0.0.0.0:6969:18232" + - "0.0.0.0:8899:8899" + - "0.0.0.0:8900:8900" + - "0.0.0.0:9900:9900" entrypoint: - "./root/run.sh" - - "${PEERCOIN_ADDRESS}" - ## ## Terra @@ -121,3 +110,15 @@ services: entrypoint: - "./root/run.sh" - "${TERRA_ADDRESS}" + + # + # Zcash + # + zcash: + build: + context: ./zcash + ports: + - "0.0.0.0:18232:18232" + entrypoint: + - "./root/run.sh" + - "${ZCASH_ADDRESS}" \ No newline at end of file diff --git a/multichain.go b/multichain.go index 844f662b..73d93e20 100644 --- a/multichain.go +++ b/multichain.go @@ -85,6 +85,8 @@ func (asset Asset) OriginChain() Chain { return Filecoin case LUNA: return Terra + case PPC: + return Peercoin case SOL: return Solana case ZEC: @@ -117,7 +119,6 @@ type Chain string // human-readable string to this set of enumerated values. Chains must be listed // in alphabetical order. const ( - Peercoin = Chain("Peercoin") Acala = Chain("Acala") BinanceSmartChain = Chain("BinanceSmartChain") Bitcoin = Chain("Bitcoin") @@ -127,6 +128,7 @@ const ( Dogecoin = Chain("Dogecoin") Ethereum = Chain("Ethereum") Filecoin = Chain("Filecoin") + Peercoin = Chain("Peercoin") Solana = Chain("Solana") Terra = Chain("Terra") Zcash = Chain("Zcash") From 45cdeb2132a319945d0dc9a301e04cfd8167ce4d Mon Sep 17 00:00:00 2001 From: Jacob Gadikian Date: Sun, 23 Aug 2020 20:49:07 +0700 Subject: [PATCH 09/12] missed some things in the first go --- chain/peercoin/address.go | 6 +++--- chain/peercoin/peercoin_test.go | 17 +++++++++-------- chain/peercoin/utxo.go | 12 ++++++------ 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/chain/peercoin/address.go b/chain/peercoin/address.go index fd3311b2..ab33b387 100644 --- a/chain/peercoin/address.go +++ b/chain/peercoin/address.go @@ -1,9 +1,9 @@ package peercoin import ( - "github.com/btcsuite/btcd/chaincfg" - "github.com/btcsuite/btcutil" - "github.com/btcsuite/btcutil/base58" + "github.com/ppcsuite/ppcd/chaincfg" + "github.com/ppcsuite/btcutil" + "github.com/ppcsuite/btcutil/base58" "github.com/renproject/multichain/api/address" "github.com/renproject/pack" ) diff --git a/chain/peercoin/peercoin_test.go b/chain/peercoin/peercoin_test.go index aefdc097..2810f813 100644 --- a/chain/peercoin/peercoin_test.go +++ b/chain/peercoin/peercoin_test.go @@ -2,33 +2,34 @@ package peercoin_test import ( "context" + "github.com/renproject/multichain/chain/bitcoin" "log" "os" "reflect" "time" - "github.com/btcsuite/btcd/chaincfg" - "github.com/btcsuite/btcutil" + "github.com/ppcsuite/ppcd/chaincfg" + "github.com/ppcsuite/btcutil" "github.com/renproject/id" "github.com/renproject/multichain/api/address" "github.com/renproject/multichain/api/utxo" - "github.com/renproject/multichain/chain/bitcoin" + "github.com/renproject/multichain/chain/peercoin" "github.com/renproject/pack" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) -var _ = Describe("Bitcoin", func() { +var _ = Describe("Peercoin", func() { Context("when submitting transactions", func() { - Context("when sending BTC to multiple addresses", func() { + Context("when sending PPC 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 BITCOIN_PK to the // value specified in the `./multichaindeploy/.env` file. - pkEnv := os.Getenv("BITCOIN_PK") + pkEnv := os.Getenv("PEERCOIN_PK") if pkEnv == "" { - panic("BITCOIN_PK is undefined") + panic("PEERCOIN_PK is undefined") } wif, err := btcutil.DecodeWIF(pkEnv) Expect(err).ToNot(HaveOccurred()) @@ -47,7 +48,7 @@ var _ = Describe("Bitcoin", func() { log.Printf("WPKH %v", wpkAddr.EncodeAddress()) // Setup the client and load the unspent transaction outputs. - client := bitcoin.NewClient(bitcoin.DefaultClientOptions()) + client := peercoin.NewClient(peercoin.DefaultClientOptions()) outputs, err := client.UnspentOutputs(context.Background(), 0, 999999999, address.Address(pkhAddr.EncodeAddress())) Expect(err).ToNot(HaveOccurred()) Expect(len(outputs)).To(BeNumerically(">", 0)) diff --git a/chain/peercoin/utxo.go b/chain/peercoin/utxo.go index 03ab6d16..53c57946 100644 --- a/chain/peercoin/utxo.go +++ b/chain/peercoin/utxo.go @@ -5,12 +5,12 @@ import ( "fmt" "math/big" - "github.com/btcsuite/btcd/btcec" - "github.com/btcsuite/btcd/chaincfg" - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/txscript" - "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcutil" + "github.com/ppcsuite/ppcd/btcec" + "github.com/ppcsuite/ppcd/chaincfg" + "github.com/ppcsuite/ppcd/chaincfg/chainhash" + "github.com/ppcsuite/ppcd/txscript" + "github.com/ppcsuite/ppcd/wire" + "github.com/ppcsuite/btcutil" "github.com/renproject/multichain/api/utxo" "github.com/renproject/pack" ) From ad83e20e2d493285ccda6c4b02200c230a9b8b9b Mon Sep 17 00:00:00 2001 From: Jacob Gadikian Date: Sun, 23 Aug 2020 20:50:47 +0700 Subject: [PATCH 10/12] used correct peercoin library --- chain/peercoin/peercoin_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/chain/peercoin/peercoin_test.go b/chain/peercoin/peercoin_test.go index 2810f813..a9e00807 100644 --- a/chain/peercoin/peercoin_test.go +++ b/chain/peercoin/peercoin_test.go @@ -2,7 +2,6 @@ package peercoin_test import ( "context" - "github.com/renproject/multichain/chain/bitcoin" "log" "os" "reflect" @@ -87,7 +86,7 @@ var _ = Describe("Peercoin", func() { Value: pack.NewU256FromU64(pack.NewU64((output.Value.Int().Uint64() - 1000) / 3)), }, } - tx, err := bitcoin.NewTxBuilder(&chaincfg.RegressionNetParams).BuildTx(inputs, recipients) + tx, err := peercoin.NewTxBuilder(&chaincfg.RegressionNetParams).BuildTx(inputs, recipients) Expect(err).ToNot(HaveOccurred()) // Get the digests that need signing from the transaction, and From c72a8048be93b6bcb19ae8e712555d7389fa3000 Mon Sep 17 00:00:00 2001 From: Jacob Gadikian Date: Sun, 23 Aug 2020 21:00:23 +0700 Subject: [PATCH 11/12] cleanup --- chain/peercoin/peercoin.go | 16 ++++++++-------- chain/peercoin/peercoin_suite_test.go | 2 +- chain/peercoin/peercoin_test.go | 6 +++--- chain/peercoin/utxo.go | 14 +++++++------- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/chain/peercoin/peercoin.go b/chain/peercoin/peercoin.go index 8a353232..44584239 100644 --- a/chain/peercoin/peercoin.go +++ b/chain/peercoin/peercoin.go @@ -60,13 +60,13 @@ func DefaultClientOptions() ClientOptions { } } -// WithHost sets the URL of the Bitcoin node. +// WithHost sets the URL of the Peercoin node. func (opts ClientOptions) WithHost(host string) ClientOptions { opts.Host = host return opts } -// WithUser sets the username that will be used to authenticate with the Bitcoin +// WithUser sets the username that will be used to authenticate with the Peercoin // node. func (opts ClientOptions) WithUser(user string) ClientOptions { opts.User = user @@ -74,19 +74,19 @@ func (opts ClientOptions) WithUser(user string) ClientOptions { } // WithPassword sets the password that will be used to authenticate with the -// Bitcoin node. +// Peercoin node. func (opts ClientOptions) WithPassword(password string) ClientOptions { opts.Password = password return opts } -// A Client interacts with an instance of the Bitcoin network using the RPC -// interface exposed by a Bitcoin node. +// A Client interacts with an instance of the Peercoin network using the RPC +// interface exposed by a Peercoin node. type Client interface { utxo.Client // UnspentOutputs spendable by the given address. UnspentOutputs(ctx context.Context, minConf, maxConf int64, address address.Address) ([]utxo.Output, error) - // Confirmations of a transaction in the Bitcoin network. + // Confirmations of a transaction in the Peercoin network. Confirmations(ctx context.Context, txHash pack.Bytes) (int64, error) } @@ -136,7 +136,7 @@ func (client *client) Output(ctx context.Context, outpoint utxo.Outpoint) (utxo. return output, pack.NewU64(resp.Confirmations), nil } -// SubmitTx to the Bitcoin network. +// SubmitTx to the Peercoin network. func (client *client) SubmitTx(ctx context.Context, tx utxo.Tx) error { serial, err := tx.Serialize() if err != nil { @@ -184,7 +184,7 @@ func (client *client) UnspentOutputs(ctx context.Context, minConf, maxConf int64 return outputs, nil } -// Confirmations of a transaction in the Bitcoin network. +// Confirmations of a transaction in the Peercoin network. func (client *client) Confirmations(ctx context.Context, txHash pack.Bytes) (int64, error) { resp := btcjson.GetTransactionResult{} diff --git a/chain/peercoin/peercoin_suite_test.go b/chain/peercoin/peercoin_suite_test.go index 62769f86..907dd11b 100644 --- a/chain/peercoin/peercoin_suite_test.go +++ b/chain/peercoin/peercoin_suite_test.go @@ -9,5 +9,5 @@ import ( func TestPeercoin(t *testing.T) { RegisterFailHandler(Fail) - RunSpecs(t, "Bitcoin Suite") + RunSpecs(t, "Peercoin Suite") } diff --git a/chain/peercoin/peercoin_test.go b/chain/peercoin/peercoin_test.go index a9e00807..f1b1f6b5 100644 --- a/chain/peercoin/peercoin_test.go +++ b/chain/peercoin/peercoin_test.go @@ -24,7 +24,7 @@ var _ = Describe("Peercoin", func() { Context("when sending PPC 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 BITCOIN_PK to the + // funds to spend. You can do this by setting PEERCOIN_PK to the // value specified in the `./multichaindeploy/.env` file. pkEnv := os.Getenv("PEERCOIN_PK") if pkEnv == "" { @@ -105,7 +105,7 @@ var _ = Describe("Peercoin", func() { } Expect(tx.Sign(signatures, pack.NewBytes(wif.SerializePubKey()))).To(Succeed()) - // Submit the transaction to the Bitcoin node. Again, this + // Submit the transaction to the Peercoin node. Again, this // should be running a la `./multichaindeploy`. txHash, err := tx.Hash() Expect(err).ToNot(HaveOccurred()) @@ -118,7 +118,7 @@ var _ = Describe("Peercoin", func() { // 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 Bitcoin transaction! + // submit a Peercoin transaction! confs, err := client.Confirmations(context.Background(), txHash) Expect(err).ToNot(HaveOccurred()) log.Printf(" %v/3 confirmations", confs) diff --git a/chain/peercoin/utxo.go b/chain/peercoin/utxo.go index 53c57946..d41355d1 100644 --- a/chain/peercoin/utxo.go +++ b/chain/peercoin/utxo.go @@ -15,27 +15,27 @@ import ( "github.com/renproject/pack" ) -// Version of Bitcoin transactions supported by the multichain. +// Version of Peercoin transactions supported by the multichain. const Version int32 = 2 // The TxBuilder is an implementation of a UTXO-compatible transaction builder -// for Bitcoin. +// for Peercoin. type TxBuilder struct { params *chaincfg.Params } // NewTxBuilder returns a transaction builder that builds UTXO-compatible -// Bitcoin transactions for the given chain configuration (this means that it +// Peercoin transactions for the given chain configuration (this means that it // can be used for regnet, testnet, and mainnet, but also for networks that are -// minimally modified forks of the Bitcoin network). +// minimally modified forks of the Peercoin network). func NewTxBuilder(params *chaincfg.Params) TxBuilder { return TxBuilder{params: params} } -// BuildTx returns a Bitcoin transaction that consumes funds from the given +// BuildTx returns a Peercoin transaction that consumes funds from the given // inputs, and sends them to the given recipients. The difference in the sum // value of the inputs and the sum value of the recipients is paid as a fee to -// the Bitcoin network. This fee must be calculated independently of this +// the Peercoin network. This fee must be calculated independently of this // function. Outputs produced for recipients will use P2PKH, P2SH, P2WPKH, or // P2WSH scripts as the pubkey script, based on the format of the recipient // address. @@ -70,7 +70,7 @@ func (txBuilder TxBuilder) BuildTx(inputs []utxo.Input, recipients []utxo.Recipi return &Tx{inputs: inputs, recipients: recipients, msgTx: msgTx, signed: false}, nil } -// Tx represents a simple Bitcoin transaction that implements the Bitcoin Compat +// Tx represents a simple Peercoin transaction that implements the Peercoin Compat // API. type Tx struct { inputs []utxo.Input From f9ea0d60d16929ea58a60f11d76e540b66f8e643 Mon Sep 17 00:00:00 2001 From: Jacob Gadikian Date: Sun, 23 Aug 2020 21:11:38 +0700 Subject: [PATCH 12/12] reformat --- chain/peercoin/address.go | 2 +- chain/peercoin/peercoin.go | 2 +- chain/peercoin/peercoin_test.go | 2 +- chain/peercoin/utxo.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/chain/peercoin/address.go b/chain/peercoin/address.go index ab33b387..8c491743 100644 --- a/chain/peercoin/address.go +++ b/chain/peercoin/address.go @@ -1,9 +1,9 @@ package peercoin import ( - "github.com/ppcsuite/ppcd/chaincfg" "github.com/ppcsuite/btcutil" "github.com/ppcsuite/btcutil/base58" + "github.com/ppcsuite/ppcd/chaincfg" "github.com/renproject/multichain/api/address" "github.com/renproject/pack" ) diff --git a/chain/peercoin/peercoin.go b/chain/peercoin/peercoin.go index 44584239..009f4f93 100644 --- a/chain/peercoin/peercoin.go +++ b/chain/peercoin/peercoin.go @@ -12,9 +12,9 @@ import ( "net/http" "time" + "github.com/ppcsuite/btcutil" "github.com/ppcsuite/ppcd/btcjson" "github.com/ppcsuite/ppcd/chaincfg/chainhash" - "github.com/ppcsuite/btcutil" "github.com/renproject/multichain/api/address" "github.com/renproject/multichain/api/utxo" "github.com/renproject/pack" diff --git a/chain/peercoin/peercoin_test.go b/chain/peercoin/peercoin_test.go index f1b1f6b5..0a631d76 100644 --- a/chain/peercoin/peercoin_test.go +++ b/chain/peercoin/peercoin_test.go @@ -7,8 +7,8 @@ import ( "reflect" "time" - "github.com/ppcsuite/ppcd/chaincfg" "github.com/ppcsuite/btcutil" + "github.com/ppcsuite/ppcd/chaincfg" "github.com/renproject/id" "github.com/renproject/multichain/api/address" "github.com/renproject/multichain/api/utxo" diff --git a/chain/peercoin/utxo.go b/chain/peercoin/utxo.go index d41355d1..2b304cf8 100644 --- a/chain/peercoin/utxo.go +++ b/chain/peercoin/utxo.go @@ -5,12 +5,12 @@ import ( "fmt" "math/big" + "github.com/ppcsuite/btcutil" "github.com/ppcsuite/ppcd/btcec" "github.com/ppcsuite/ppcd/chaincfg" "github.com/ppcsuite/ppcd/chaincfg/chainhash" "github.com/ppcsuite/ppcd/txscript" "github.com/ppcsuite/ppcd/wire" - "github.com/ppcsuite/btcutil" "github.com/renproject/multichain/api/utxo" "github.com/renproject/pack" )