Skip to content

Commit cdb2fc4

Browse files
Merge pull request #30 from babylonchain/main
merge back to main
2 parents 919666f + 6102461 commit cdb2fc4

File tree

11 files changed

+571
-34
lines changed

11 files changed

+571
-34
lines changed

.circleci/config.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ jobs:
1818
command: "go env"
1919
- go/load-cache:
2020
key: go-mod-v6-{{ checksum "go.sum" }}
21-
- add_ssh_keys
2221
- go/mod-download
2322
- go/save-cache:
2423
key: go-mod-v6-{{ checksum "go.sum" }}
@@ -46,15 +45,13 @@ jobs:
4645
resource_class: large
4746
steps:
4847
- checkout
49-
- add_ssh_keys
5048
- aws-ecr/build-image:
5149
push-image: false
5250
dockerfile: Dockerfile
5351
path: ./
5452
build-path: ./
5553
tag: "$CIRCLE_SHA1,$CIRCLE_TAG"
5654
repo: "$CIRCLE_PROJECT_REPONAME"
57-
extra-build-args: "--secret id=sshKey,src=/home/circleci/.ssh/$DEPLOY_KEY_NAME"
5855
- run:
5956
name: Save Docker image to export it to workspace
6057
command: |
@@ -93,6 +90,10 @@ workflows:
9390
filters:
9491
tags:
9592
only: /.*/
93+
branches:
94+
only:
95+
- main
96+
- dev
9697
- push_docker:
9798
requires:
9899
- build_docker

Dockerfile

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,11 @@ RUN apk add --no-cache --update openssh git make build-base linux-headers libc-d
1111
pkgconfig zeromq-dev musl-dev alpine-sdk libsodium-dev \
1212
libzmq-static libsodium-static gcc
1313

14-
# Load private repos SSH deploy key and configure ssh
15-
RUN mkdir -p /root/.ssh && ssh-keyscan github.com >> /root/.ssh/known_hosts
16-
RUN git config --global url."[email protected]:".insteadOf "https://github.com/"
17-
ENV GOPRIVATE=github.com/babylonchain/*
18-
1914
# Build
2015
WORKDIR /go/src/github.com/babylonchain/cli-tools
2116
# Cache dependencies
2217
COPY go.mod go.sum /go/src/github.com/babylonchain/cli-tools/
23-
RUN --mount=type=secret,id=sshKey,target=/root/.ssh/id_rsa go mod download
18+
RUN go mod download
2419
# Copy the rest of the files
2520
COPY ./ /go/src/github.com/babylonchain/cli-tools/
2621

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ $(BUILDDIR)/:
2929
mkdir -p $(BUILDDIR)/
3030

3131
build-docker:
32-
$(DOCKER) build --secret id=sshKey,src=${BBN_PRIV_DEPLOY_KEY} --tag babylonchain/cli-tools -f Dockerfile \
32+
$(DOCKER) build --tag babylonchain/cli-tools -f Dockerfile \
3333
$(shell git rev-parse --show-toplevel)
3434

3535
.PHONY: build build-docker install tests

cmd/createStakingTxCmd.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ func serializeBTCTx(tx *wire.MsgTx) ([]byte, error) {
7272
return txBuf.Bytes(), nil
7373
}
7474

75-
func serializeBTCTxToHex(tx *wire.MsgTx) (string, error) {
75+
func SerializeBTCTxToHex(tx *wire.MsgTx) (string, error) {
7676
bytes, err := serializeBTCTx(tx)
7777

7878
if err != nil {
@@ -302,7 +302,7 @@ var createStakingTxCmd = &cobra.Command{
302302
return err
303303
}
304304

305-
serializedTx, err := serializeBTCTxToHex(tx)
305+
serializedTx, err := SerializeBTCTxToHex(tx)
306306
if err != nil {
307307
return err
308308
}

cmd/createUnbondingTxCmd.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ var createUnbondingTxCmd = &cobra.Command{
225225

226226
unbondingTxHash := unbondingTx.TxHash()
227227

228-
unbondingTxHex, err := serializeBTCTxToHex(unbondingTx)
228+
unbondingTxHex, err := SerializeBTCTxToHex(unbondingTx)
229229

230230
if err != nil {
231231
return err

cmd/createWithdrawTxCmg.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ var createWithdrawCmd = &cobra.Command{
254254
}
255255

256256
// at this point we created unsigned withdraw tx lets create response
257-
serializedWithdrawTx, err := serializeBTCTxToHex(info.spendStakeTx)
257+
serializedWithdrawTx, err := SerializeBTCTxToHex(info.spendStakeTx)
258258

259259
if err != nil {
260260
return err
@@ -348,7 +348,7 @@ var createWithdrawCmd = &cobra.Command{
348348

349349
// serialize tx with witness
350350

351-
serializedWithdrawTx, err = serializeBTCTxToHex(info.spendStakeTx)
351+
serializedWithdrawTx, err = SerializeBTCTxToHex(info.spendStakeTx)
352352

353353
if err != nil {
354354
return err

cmd/timestampFileCmd.go

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
package cmd
2+
3+
import (
4+
"bytes"
5+
"crypto/sha256"
6+
"encoding/hex"
7+
"fmt"
8+
"io"
9+
"os"
10+
11+
"github.com/btcsuite/btcd/btcutil"
12+
"github.com/btcsuite/btcd/chaincfg"
13+
"github.com/btcsuite/btcd/txscript"
14+
"github.com/btcsuite/btcd/wire"
15+
"github.com/spf13/cobra"
16+
)
17+
18+
const (
19+
FlagFeeInTx = "fee-in-tx"
20+
)
21+
22+
type TimestampFileOutput struct {
23+
TimestampTx string `json:"timestamp_tx_hex"`
24+
FileHash string `json:"file_hash"`
25+
}
26+
27+
func init() {
28+
_ = btcTimestampFileCmd.Flags().Int64(FlagFeeInTx, 2000, "the amount of satoshi to pay as fee for the tx")
29+
_ = btcTimestampFileCmd.Flags().String(FlagNetwork, "signet", "network one of (mainnet, testnet3, regtest, simnet, signet)")
30+
31+
rootCmd.AddCommand(btcTimestampFileCmd)
32+
}
33+
34+
var btcTimestampFileCmd = &cobra.Command{
35+
Use: "create-timestamp-transaction [funded-tx-addr-hex] [file-path] [address]",
36+
Example: `cli-tools create-timestamp-transaction [funded-tx-addr-hex] ./path/to/file/to/timestamp 836e9fc730ff37de48f2ff3a76b3c2380fbabaf66d9e50754d86b2a2e2952156`,
37+
Short: "Creates a timestamp btc transaction by hashing the file input.",
38+
Long: `Creates a timestamp BTC transaction with 2 outputs and one input.
39+
One output is the nullDataScript of the file hash, as the file hash
40+
being the sha256 of the input file path. This output is the timestamp of the file.
41+
The other output is the pay to addr script which contains the pay to witness pubkey
42+
with the value as ({funded-tx-output-value} - {FlagFeeInTx}). This output is needed
43+
to continue to have spendable funds to the p2wpkh address.`,
44+
Args: cobra.ExactArgs(3),
45+
RunE: func(cmd *cobra.Command, args []string) error {
46+
fundedTxHex, inputFilePath, addressStr := args[0], args[1], args[2]
47+
flags := cmd.Flags()
48+
feeInTx, err := flags.GetInt64(FlagFeeInTx)
49+
if err != nil {
50+
return fmt.Errorf("failed to parse flag %s: %w", FlagFeeInTx, err)
51+
}
52+
53+
networkParamStr, err := flags.GetString(FlagNetwork)
54+
if err != nil {
55+
return fmt.Errorf("failed to parse flag %s: %w", FlagNetwork, err)
56+
}
57+
58+
btcParams, err := getBtcNetworkParams(networkParamStr)
59+
if err != nil {
60+
return fmt.Errorf("unable parse BTC network %s: %w", networkParamStr, err)
61+
}
62+
63+
timestampOutput, err := CreateTimestampTx(fundedTxHex, inputFilePath, addressStr, feeInTx, btcParams)
64+
if err != nil {
65+
return fmt.Errorf("failed to create timestamping tx: %w", err)
66+
}
67+
68+
PrintRespJSON(timestampOutput)
69+
return nil
70+
},
71+
}
72+
73+
func outputIndexForPkScript(pkScript []byte, tx *wire.MsgTx) (int, error) {
74+
for i, txOut := range tx.TxOut {
75+
if bytes.Equal(txOut.PkScript, pkScript) {
76+
return i, nil
77+
}
78+
}
79+
return -1, fmt.Errorf("unable to find output index for pk script")
80+
}
81+
82+
// CreateTimestampTx outputs the hash of file and BTC transaction that timestamp that
83+
// hash in one of the outputs. The funded tx needs to have one output with value
84+
// for the changeAddress p2wpkh. The changeAddress needs to be a EncodeAddress
85+
// which is the encoding of the payment address associated with the Address value,
86+
// to be used to generate a pay to address script pay-to-witness-pubkey-hash (P2WKH) format.
87+
func CreateTimestampTx(
88+
fundedTxHex, filePath, changeAddress string,
89+
fee int64,
90+
networkParams *chaincfg.Params,
91+
) (*TimestampFileOutput, error) {
92+
txOutFileHash, fileHash, err := txOutTimestampFile(filePath)
93+
if err != nil {
94+
return nil, fmt.Errorf("unable to create tx out with filepath %s: %w", filePath, err)
95+
}
96+
97+
fundingTx, _, err := newBTCTxFromHex(fundedTxHex)
98+
if err != nil {
99+
return nil, fmt.Errorf("unable parse BTC Tx %s: %w", fundedTxHex, err)
100+
}
101+
102+
address, err := btcutil.DecodeAddress(changeAddress, networkParams)
103+
if err != nil {
104+
return nil, fmt.Errorf("invalid address %s: %w", changeAddress, err)
105+
}
106+
107+
addressPkScript, err := txscript.PayToAddrScript(address)
108+
if err != nil {
109+
return nil, fmt.Errorf("unable to create pk script from address %s: %w", changeAddress, err)
110+
}
111+
112+
if !txscript.IsPayToWitnessPubKeyHash(addressPkScript) {
113+
return nil, fmt.Errorf("address %s is not a pay-to-witness-pubkey-hash", changeAddress)
114+
}
115+
116+
fundingOutputIdx, err := outputIndexForPkScript(addressPkScript, fundingTx)
117+
if err != nil {
118+
return nil, fmt.Errorf("unable to find output index for pk script: %w", err)
119+
}
120+
fundingTxHash := fundingTx.TxHash()
121+
fundingInput := wire.NewTxIn(
122+
wire.NewOutPoint(&fundingTxHash, uint32(fundingOutputIdx)),
123+
nil,
124+
nil,
125+
)
126+
127+
valueIn := fundingTx.TxOut[fundingOutputIdx].Value
128+
if valueIn < fee {
129+
return nil, fmt.Errorf("the value of input in %d is bigger than the fee %d", valueIn, fee)
130+
}
131+
132+
changeOutput := wire.NewTxOut(
133+
valueIn-fee,
134+
addressPkScript,
135+
)
136+
137+
timestampTx := wire.NewMsgTx(2)
138+
timestampTx.AddTxIn(fundingInput)
139+
timestampTx.AddTxOut(changeOutput)
140+
timestampTx.AddTxOut(txOutFileHash)
141+
142+
txHex, err := SerializeBTCTxToHex(timestampTx)
143+
if err != nil {
144+
return nil, fmt.Errorf("failed to serialize timestamping tx: %w", err)
145+
}
146+
147+
return &TimestampFileOutput{
148+
TimestampTx: txHex,
149+
FileHash: hex.EncodeToString(fileHash),
150+
}, nil
151+
}
152+
153+
func txOutTimestampFile(filePath string) (txOut *wire.TxOut, fileHash []byte, err error) {
154+
fileHash, err = hashFromFile(filePath)
155+
if err != nil {
156+
return nil, nil, fmt.Errorf("failed to generate hash from file %s: %w", filePath, err)
157+
}
158+
159+
dataScript, err := txscript.NullDataScript(fileHash)
160+
if err != nil {
161+
return nil, nil, fmt.Errorf("failed to create op return with hash from file %s: %w", fileHash, err)
162+
}
163+
164+
return wire.NewTxOut(0, dataScript), fileHash, nil
165+
}
166+
167+
func hashFromFile(filePath string) ([]byte, error) {
168+
h := sha256.New()
169+
170+
f, err := os.Open(filePath)
171+
if err != nil {
172+
return nil, fmt.Errorf("failed to open the file %s: %w", filePath, err)
173+
}
174+
defer f.Close()
175+
176+
if _, err := io.Copy(h, f); err != nil {
177+
return nil, err
178+
}
179+
180+
return h.Sum(nil), nil
181+
}

0 commit comments

Comments
 (0)