/
main.go
118 lines (95 loc) · 3.06 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
package main
import (
"bytes"
"encoding/hex"
"flag"
"fmt"
"io/ioutil"
"net/http"
"strconv"
"decimal"
"github.com/bcext/gcash/wire"
"github.com/pkg/errors"
"github.com/qshuai/tcolor"
"github.com/tidwall/gjson"
)
const (
bitcoinCashAPI = "https://bch-tchain.api.btc.com/v3"
)
func main() {
rawtx := flag.String("rawtx", "", "Please input the raw hexadecimal transaction string")
flag.Parse()
if rawtx == nil {
fmt.Println(tcolor.WithColor(tcolor.Red, "lack of raw hexadecimal transaction string"))
return
}
txBytes, err := hex.DecodeString(*rawtx)
if err != nil {
fmt.Println(tcolor.WithColor(tcolor.Red, "invalid hexadecimal string"))
return
}
var tx wire.MsgTx
err = tx.Deserialize(bytes.NewBuffer(txBytes))
if err != nil {
fmt.Println(tcolor.WithColor(tcolor.Red, "transaction deserialize failed"))
return
}
tx.TxHash()
// calculate fee
var inputValue, outputValue int64
var unconfirmedArray []string
for _, in := range tx.TxIn {
value, unconfirmed, err := getInputValue(in.PreviousOutPoint.Hash.String(), in.PreviousOutPoint.Index)
if err != nil {
fmt.Println(tcolor.WithColor(tcolor.Red, "calculate input total value err: "+err.Error()))
return
}
if unconfirmed {
unconfirmedArray = append(unconfirmedArray, in.PreviousOutPoint.Hash.String()+":"+
strconv.Itoa(int(in.PreviousOutPoint.Index)))
continue
}
inputValue += value
}
for _, out := range tx.TxOut {
outputValue += out.Value
}
// So we can get fee and feerate, or unconfirmed utxo if encounter
if unconfirmedArray != nil {
fmt.Println(tcolor.WithColor(tcolor.Yellow, "unconfirmed utxo in this transaction as following:"))
for _, item := range unconfirmedArray {
fmt.Println(tcolor.WithColor(tcolor.Green, item))
}
return
}
fee := inputValue - outputValue
txSize := len(*rawtx) / 2
feeRateWithSatoshi := decimal.New(fee, 0).Div(decimal.New(int64(txSize), 0)).IntPart()
feeRateWithBCH := decimal.New(fee, 0).Div(decimal.New(int64(txSize), 0)).Mul(decimal.New(1, -5)).Truncate(8).String()
fmt.Println(tcolor.WithColor(tcolor.Green, fmt.Sprintf("fee : %s", decimal.New(fee, 0).Mul(decimal.New(1, -8)).Truncate(8).String())))
fmt.Println(tcolor.WithColor(tcolor.Green, fmt.Sprintf("feeRate: %d Satoshi/Byte", feeRateWithSatoshi)))
fmt.Println(tcolor.WithColor(tcolor.Green, fmt.Sprintf("feeRate: %s BCH/KB", feeRateWithBCH)))
}
func getInputValue(hash string, vout uint32) (int64, bool, error) {
res, err := http.Get(bitcoinCashAPI + "/tx/" + hash)
if err != nil {
return 0, false, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return 0, false, errors.New("request failed with http failed code: " + strconv.Itoa(res.StatusCode))
}
b, err := ioutil.ReadAll(res.Body)
if err != nil {
return 0, false, err
}
// check whether this utxo is uncofirmed or not
if gjson.Get(string(b), "err_no").Int() == 1 {
return 0, true, nil
}
outs := gjson.Get(string(b), "data.outputs").Array()
if int(vout) >= len(outs) {
return 0, false, errors.New("output index is overflow")
}
return outs[vout].Get("value").Int(), false, nil
}