Skip to content

Commit

Permalink
Enhancement: NewTxFromReader and VarInt datatype (#88)
Browse files Browse the repository at this point in the history
* added newtxfromreader func. added varint data type

* better error messaging

* fixed linter

* removed unneeded brackets

* unexported test data dir

* lint

* better test name
  • Loading branch information
tigh-latte authored Dec 2, 2021
1 parent 792a281 commit 83c90f3
Show file tree
Hide file tree
Showing 13 changed files with 320 additions and 51 deletions.
55 changes: 53 additions & 2 deletions input.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package bt

import (
"encoding/binary"
"encoding/hex"
"fmt"
"io"

"github.com/libsv/go-bt/v2/bscript"
"github.com/pkg/errors"
)

/*
Expand Down Expand Up @@ -35,6 +38,54 @@ type Input struct {
SequenceNumber uint32
}

// ReadFrom reads from the `io.Reader` into the `bt.Input`.
func (i *Input) ReadFrom(r io.Reader) (int64, error) {
*i = Input{}
var bytesRead int64

previousTxID := make([]byte, 32)
n, err := io.ReadFull(r, previousTxID)
bytesRead += int64(n)
if err != nil {
return bytesRead, errors.Wrapf(err, "previousTxID(32): got %d bytes", n)
}

prevIndex := make([]byte, 4)
n, err = io.ReadFull(r, prevIndex)
bytesRead += int64(n)
if err != nil {
return bytesRead, errors.Wrapf(err, "previousTxID(4): got %d bytes", n)
}

var l VarInt
n64, err := l.ReadFrom(r)
bytesRead += n64
if err != nil {
return bytesRead, err
}

script := make([]byte, l)
n, err = io.ReadFull(r, script)
bytesRead += int64(n)
if err != nil {
return bytesRead, errors.Wrapf(err, "script(%d): got %d bytes", l, n)
}

sequence := make([]byte, 4)
n, err = io.ReadFull(r, sequence)
bytesRead += int64(n)
if err != nil {
return bytesRead, errors.Wrapf(err, "sequence(4): got %d bytes", n)
}

i.previousTxID = ReverseBytes(previousTxID)
i.PreviousTxOutIndex = binary.LittleEndian.Uint32(prevIndex)
i.UnlockingScript = bscript.NewFromBytes(script)
i.SequenceNumber = binary.LittleEndian.Uint32(sequence)

return bytesRead, nil
}

// PreviousTxIDAdd will add the supplied txID bytes to the Input,
// if it isn't a valid transaction id an ErrInvalidTxID error will be returned.
func (i *Input) PreviousTxIDAdd(txID []byte) error {
Expand Down Expand Up @@ -93,9 +144,9 @@ func (i *Input) Bytes(clear bool) []byte {
h = append(h, 0x00)
} else {
if i.UnlockingScript == nil {
h = append(h, VarInt(0)...)
h = append(h, VarInt(0).Bytes()...)
} else {
h = append(h, VarInt(uint64(len(*i.UnlockingScript)))...)
h = append(h, VarInt(uint64(len(*i.UnlockingScript))).Bytes()...)
h = append(h, *i.UnlockingScript...)
}
}
Expand Down
38 changes: 36 additions & 2 deletions output.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import (
"encoding/binary"
"encoding/hex"
"fmt"
"io"

"github.com/libsv/go-bt/v2/bscript"
"github.com/pkg/errors"
)

/*
Expand All @@ -26,6 +28,38 @@ type Output struct {
LockingScript *bscript.Script
}

// ReadFrom reads from the `io.Reader` into the `bt.Output`.
func (o *Output) ReadFrom(r io.Reader) (int64, error) {
*o = Output{}
var bytesRead int64

satoshis := make([]byte, 8)
n, err := io.ReadFull(r, satoshis)
bytesRead += int64(n)
if err != nil {
return bytesRead, errors.Wrapf(err, "satoshis(8): got %d bytes", n)
}

var l VarInt
n64, err := l.ReadFrom(r)
bytesRead += n64
if err != nil {
return bytesRead, err
}

script := make([]byte, l)
n, err = io.ReadFull(r, script)
bytesRead += int64(n)
if err != nil {
return bytesRead, errors.Wrapf(err, "lockingScript(%d): got %d bytes", l, n)
}

o.Satoshis = binary.LittleEndian.Uint64(satoshis)
o.LockingScript = bscript.NewFromBytes(script)

return bytesRead, nil
}

// LockingScriptHexString returns the locking script
// of an output encoded as a hex string.
func (o *Output) LockingScriptHexString() string {
Expand All @@ -46,7 +80,7 @@ func (o *Output) Bytes() []byte {

h := make([]byte, 0)
h = append(h, b...)
h = append(h, VarInt(uint64(len(*o.LockingScript)))...)
h = append(h, VarInt(uint64(len(*o.LockingScript))).Bytes()...)
h = append(h, *o.LockingScript...)

return h
Expand All @@ -61,7 +95,7 @@ func (o *Output) BytesForSigHash() []byte {
binary.LittleEndian.PutUint64(satoshis, o.Satoshis)
buf = append(buf, satoshis...)

buf = append(buf, VarInt(uint64(len(*o.LockingScript)))...)
buf = append(buf, VarInt(uint64(len(*o.LockingScript))).Bytes()...)
buf = append(buf, *o.LockingScript...)

return buf
Expand Down
10 changes: 5 additions & 5 deletions signaturehash.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ func (tx *Tx) CalcInputPreimage(inputNumber uint32, sigHashFlag sighash.Flag) ([
buf = append(buf, oi...)

// scriptCode of the input (serialised as scripts inside CTxOuts)
buf = append(buf, VarInt(uint64(len(*in.PreviousTxScript)))...)
buf = append(buf, VarInt(uint64(len(*in.PreviousTxScript))).Bytes()...)
buf = append(buf, *in.PreviousTxScript...)

// value of the output spent by this input (8-byte little endian)
Expand Down Expand Up @@ -226,29 +226,29 @@ func (tx *Tx) CalcInputPreimageLegacy(inputNumber uint32, shf sighash.Flag) ([]b
binary.LittleEndian.PutUint32(v, tx.Version)
buf = append(buf, v...)

buf = append(buf, VarInt(uint64(len(txCopy.Inputs)))...)
buf = append(buf, VarInt(uint64(len(txCopy.Inputs))).Bytes()...)
for _, in := range txCopy.Inputs {
buf = append(buf, ReverseBytes(in.PreviousTxID())...)

oi := make([]byte, 4)
binary.LittleEndian.PutUint32(oi, in.PreviousTxOutIndex)
buf = append(buf, oi...)

buf = append(buf, VarInt(uint64(len(*in.PreviousTxScript)))...)
buf = append(buf, VarInt(uint64(len(*in.PreviousTxScript))).Bytes()...)
buf = append(buf, *in.PreviousTxScript...)

sq := make([]byte, 4)
binary.LittleEndian.PutUint32(sq, in.SequenceNumber)
buf = append(buf, sq...)
}

buf = append(buf, VarInt(uint64(len(txCopy.Outputs)))...)
buf = append(buf, VarInt(uint64(len(txCopy.Outputs))).Bytes()...)
for _, out := range txCopy.Outputs {
st := make([]byte, 8)
binary.LittleEndian.PutUint64(st, out.Satoshis)
buf = append(buf, st...)

buf = append(buf, VarInt(uint64(len(*out.LockingScript)))...)
buf = append(buf, VarInt(uint64(len(*out.LockingScript))).Bytes()...)
buf = append(buf, *out.LockingScript...)
}

Expand Down
32 changes: 32 additions & 0 deletions testing/data/data.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package data

import (
"embed"
"io/fs"
"path"
)

// testDataDir a directory container test data.
type testDataDir struct {
prefix string
fs embed.FS
}

//go:embed tx/bin/*
var txBinData embed.FS

// TxBinData data for binary txs.
var TxBinData = testDataDir{
prefix: "tx/bin",
fs: txBinData,
}

// Open a file.
func (d *testDataDir) Open(file string) (fs.File, error) {
return d.fs.Open(path.Join(d.prefix, file))
}

// Load the data of a file.
func (d *testDataDir) Load(file string) ([]byte, error) {
return d.fs.ReadFile(path.Join(d.prefix, file))
}
Binary file added testing/data/tx/bin/block.bin
Binary file not shown.
78 changes: 70 additions & 8 deletions tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"encoding/binary"
"encoding/hex"
"io"

"github.com/libsv/go-bk/crypto"

Expand Down Expand Up @@ -91,14 +92,14 @@ func NewTxFromStream(b []byte) (*Tx, int, error) {
}
offset += 4

inputCount, size := DecodeVarInt(b[offset:])
inputCount, size := NewVarIntFromBytes(b[offset:])
offset += size

// create Inputs
var i uint64
var err error
var input *Input
for ; i < inputCount; i++ {
for ; i < uint64(inputCount); i++ {
input, size, err = newInputFromBytes(b[offset:])
if err != nil {
return nil, 0, err
Expand All @@ -108,11 +109,11 @@ func NewTxFromStream(b []byte) (*Tx, int, error) {
}

// create Outputs
var outputCount uint64
var outputCount VarInt
var output *Output
outputCount, size = DecodeVarInt(b[offset:])
outputCount, size = NewVarIntFromBytes(b[offset:])
offset += size
for i = 0; i < outputCount; i++ {
for i = 0; i < uint64(outputCount); i++ {
output, size, err = newOutputFromBytes(b[offset:])
if err != nil {
return nil, 0, err
Expand All @@ -127,6 +128,67 @@ func NewTxFromStream(b []byte) (*Tx, int, error) {
return &t, offset, nil
}

// ReadFrom reads from the `io.Reader` into the `bt.Tx`.
func (tx *Tx) ReadFrom(r io.Reader) (int64, error) {
*tx = Tx{}
var bytesRead int64

version := make([]byte, 4)
n, err := io.ReadFull(r, version)
bytesRead += int64(n)
if err != nil {
return bytesRead, err
}

tx.Version = binary.LittleEndian.Uint32(version)

var inputCount VarInt
n64, err := inputCount.ReadFrom(r)
bytesRead += n64
if err != nil {
return bytesRead, err
}

// create Inputs
for i := uint64(0); i < uint64(inputCount); i++ {
input := new(Input)
n64, err = input.ReadFrom(r)
bytesRead += n64
if err != nil {
return bytesRead, err
}
tx.Inputs = append(tx.Inputs, input)
}

var outputCount VarInt
n64, err = outputCount.ReadFrom(r)
bytesRead += n64
if err != nil {
return bytesRead, err
}

for i := uint64(0); i < uint64(outputCount); i++ {
output := new(Output)
n64, err = output.ReadFrom(r)
bytesRead += n64
if err != nil {
return bytesRead, err
}

tx.Outputs = append(tx.Outputs, output)
}

locktime := make([]byte, 4)
n, err = io.ReadFull(r, locktime)
bytesRead += int64(n)
if err != nil {
return bytesRead, err
}
tx.LockTime = binary.LittleEndian.Uint32(locktime)

return bytesRead, nil
}

// HasDataOutputs returns true if the transaction has
// at least one data (OP_RETURN) output in it.
func (tx *Tx) HasDataOutputs() bool {
Expand Down Expand Up @@ -258,19 +320,19 @@ func (tx *Tx) toBytesHelper(index int, lockingScript []byte) []byte {

h = append(h, LittleEndianBytes(tx.Version, 4)...)

h = append(h, VarInt(uint64(len(tx.Inputs)))...)
h = append(h, VarInt(uint64(len(tx.Inputs))).Bytes()...)

for i, in := range tx.Inputs {
s := in.Bytes(lockingScript != nil)
if i == index && lockingScript != nil {
h = append(h, VarInt(uint64(len(lockingScript)))...)
h = append(h, VarInt(uint64(len(lockingScript))).Bytes()...)
h = append(h, lockingScript...)
} else {
h = append(h, s...)
}
}

h = append(h, VarInt(uint64(len(tx.Outputs)))...)
h = append(h, VarInt(uint64(len(tx.Outputs))).Bytes()...)
for _, out := range tx.Outputs {
h = append(h, out.Bytes()...)
}
Expand Down
Loading

0 comments on commit 83c90f3

Please sign in to comment.