diff --git a/block.go b/block.go index bb96451..ce117b9 100644 --- a/block.go +++ b/block.go @@ -35,7 +35,7 @@ func (b *Block) Bytes() []byte { bytes = append(bytes, b.BlockHeader.Bytes()...) txCount := uint64(len(b.Txs)) - bytes = append(bytes, bt.VarInt(txCount)...) + bytes = append(bytes, bt.VarInt(txCount).Bytes()...) for _, tx := range b.Txs { bytes = append(bytes, tx.Bytes()...) @@ -73,7 +73,7 @@ func NewBlockFromBytes(b []byte) (*Block, error) { } offset += 80 - txCount, size := bt.DecodeVarInt(b[offset:]) + txCount, size := bt.NewVarIntFromBytes(b[offset:]) offset += size var txs []*bt.Tx diff --git a/coinbase.go b/coinbase.go index 5bf86ba..01a74f9 100644 --- a/coinbase.go +++ b/coinbase.go @@ -88,7 +88,7 @@ func makeCoinbaseOutputTransactions(coinbaseValue uint64, defaultWitnessCommitme binary.LittleEndian.PutUint64(buf[0:], coinbaseValue) - buf = append(buf, bt.VarInt(uint64(len(*o.LockingScript)))...) + buf = append(buf, bt.VarInt(uint64(len(*o.LockingScript))).Bytes()...) buf = append(buf, *o.LockingScript...) numberOfTransactions := 1 @@ -101,7 +101,7 @@ func makeCoinbaseOutputTransactions(coinbaseValue uint64, defaultWitnessCommitme log.Printf("Error decoding witness commitment: %+v", err) return nil, err } - wcl := bt.VarInt(uint64(len(wc))) + wcl := bt.VarInt(uint64(len(wc))).Bytes() buf = append(buf, wcl...) buf = append(buf, wc...) } @@ -111,11 +111,11 @@ func makeCoinbaseOutputTransactions(coinbaseValue uint64, defaultWitnessCommitme byteArr := make([]byte, 8) // 8 bytes of 0 = 0 satoshis. buf = append(buf, byteArr...) - buf = append(buf, bt.VarInt(uint64(len(minerIDBytes)))...) + buf = append(buf, bt.VarInt(uint64(len(minerIDBytes))).Bytes()...) buf = append(buf, minerIDBytes...) } - buf = append(bt.VarInt(uint64(numberOfTransactions)), buf...) + buf = append(bt.VarInt(uint64(numberOfTransactions)).Bytes(), buf...) return buf, nil } @@ -143,7 +143,7 @@ func makeCoinbase1(height uint32, coinbaseText string) []byte { buf = append(buf, make([]byte, 32)...) // Transaction hash - 4 bytes all bits are zero buf = append(buf, []byte{0xff, 0xff, 0xff, 0xff}...) // Coinbase data size - 4 bytes - All bits are ones: 0xFFFFFFFF (ffffffff) - buf = append(buf, bt.VarInt(uint64(len(arbitraryData)+spaceForExtraNonce))...) // Length of the coinbase data, from 2 to 100 bytes + buf = append(buf, bt.VarInt(uint64(len(arbitraryData)+spaceForExtraNonce)).Bytes()...) // Length of the coinbase data, from 2 to 100 bytes buf = append(buf, arbitraryData...) return buf diff --git a/go.mod b/go.mod index 48dd9c0..2d259ae 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.17 require ( github.com/libsv/go-bk v0.1.5 - github.com/libsv/go-bt/v2 v2.1.0-beta.1 + github.com/libsv/go-bt/v2 v2.1.0-beta.2 github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.7.0 ) diff --git a/go.sum b/go.sum index 26ec98c..233fe25 100644 --- a/go.sum +++ b/go.sum @@ -14,6 +14,8 @@ github.com/libsv/go-bk v0.1.5 h1:fqbWy8nwVM/ayM8Nxe+lM7fN0FaUMMD1w5MCpwit7XQ= github.com/libsv/go-bk v0.1.5/go.mod h1:xbDkeFFpP0uyFaPLnP6TwaLpAsHaslZ0LftTdWlB6HI= github.com/libsv/go-bt/v2 v2.1.0-beta.1 h1:3nSvhUnrCOM4Cel+zVlLjCoAITCJ7dbYJuPGj6x8958= github.com/libsv/go-bt/v2 v2.1.0-beta.1/go.mod h1:u5g3GmVLffBV8sWvMqHR3JekC51OR9XYvmQp1h/XoiQ= +github.com/libsv/go-bt/v2 v2.1.0-beta.2 h1:oq6BQQtNeZiG/esfoY/7RyYF+dDj996xqNfvoQfH6n4= +github.com/libsv/go-bt/v2 v2.1.0-beta.2/go.mod h1:u5g3GmVLffBV8sWvMqHR3JekC51OR9XYvmQp1h/XoiQ= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= diff --git a/merkleproof.go b/merkleproof.go index b75b94c..8912f8b 100644 --- a/merkleproof.go +++ b/merkleproof.go @@ -71,7 +71,7 @@ func (mp *MerkleProof) Bytes() ([]byte, error) { // set bit at index 0 flags |= 1 << 0 - txLength = bt.VarInt(uint64(len(txOrID))) + txLength = bt.VarInt(uint64(len(txOrID))).Bytes() } if mp.TargetType == "header" { @@ -86,7 +86,7 @@ func (mp *MerkleProof) Bytes() ([]byte, error) { bytes := []byte{} bytes = append(bytes, flags) - bytes = append(bytes, index...) + bytes = append(bytes, index.Bytes()...) bytes = append(bytes, txLength...) bytes = append(bytes, txOrID...) bytes = append(bytes, target...) diff --git a/spv/ancestors_binary.go b/spv/ancestors_binary.go index e2f7742..d77fd42 100644 --- a/spv/ancestors_binary.go +++ b/spv/ancestors_binary.go @@ -45,9 +45,9 @@ func NewAncestryFromBytes(b []byte) (*Ancestry, error) { offset := uint64(1) total := uint64(len(b)) - l, size := bt.DecodeVarInt(b[offset:]) + l, size := bt.NewVarIntFromBytes(b[offset:]) offset += uint64(size) - paymentTx, err := bt.NewTxFromBytes(b[offset : offset+l]) + paymentTx, err := bt.NewTxFromBytes(b[offset : offset+uint64(l)]) if err != nil { return nil, err } @@ -55,7 +55,7 @@ func NewAncestryFromBytes(b []byte) (*Ancestry, error) { PaymentTx: paymentTx, Ancestors: make(map[[32]byte]*Ancestor), } - offset += l + offset += uint64(l) var TxID [32]byte @@ -104,13 +104,13 @@ func parseChunk(b []byte, start uint64) (binaryChunk, uint64) { offset := start typeOfNextData := b[offset] offset++ - l, size := bt.DecodeVarInt(b[offset:]) + l, size := bt.NewVarIntFromBytes(b[offset:]) offset += uint64(size) chunk := binaryChunk{ ContentType: typeOfNextData, - Data: b[offset : offset+l], + Data: b[offset : offset+uint64(l)], } - offset += l + offset += uint64(l) return chunk, offset - start } @@ -128,10 +128,10 @@ func parseMapiCallbacks(b []byte) ([]*bc.MapiCallback, error) { var responses = [][]byte{} for allBinary > internalOffset { - l, size := bt.DecodeVarInt(b[internalOffset:]) + l, size := bt.NewVarIntFromBytes(b[internalOffset:]) internalOffset += uint64(size) - response := b[internalOffset : internalOffset+l] - internalOffset += l + response := b[internalOffset : internalOffset+uint64(l)] + internalOffset += uint64(l) responses = append(responses, response) } diff --git a/spv/ancestors_json.go b/spv/ancestors_json.go index 15d6562..98eb089 100644 --- a/spv/ancestors_json.go +++ b/spv/ancestors_json.go @@ -70,7 +70,7 @@ func (j *AncestryJSON) Bytes() ([]byte, error) { return nil, err } length := bt.VarInt(uint64(len(paymentTx))) - binaryTxContext = append(binaryTxContext, length...) + binaryTxContext = append(binaryTxContext, length.Bytes()...) binaryTxContext = append(binaryTxContext, paymentTx...) // follow with the list of ancestors, including their proof or mapi responses if present. @@ -81,7 +81,7 @@ func (j *AncestryJSON) Bytes() ([]byte, error) { } length := bt.VarInt(uint64(len(rawTx))) binaryTxContext = append(binaryTxContext, flagTx) - binaryTxContext = append(binaryTxContext, length...) + binaryTxContext = append(binaryTxContext, length.Bytes()...) binaryTxContext = append(binaryTxContext, rawTx...) if ancestor.Proof != nil { rawProof, err := ancestor.Proof.Bytes() @@ -90,20 +90,20 @@ func (j *AncestryJSON) Bytes() ([]byte, error) { } length := bt.VarInt(uint64(len(rawProof))) binaryTxContext = append(binaryTxContext, flagProof) - binaryTxContext = append(binaryTxContext, length...) + binaryTxContext = append(binaryTxContext, length.Bytes()...) binaryTxContext = append(binaryTxContext, rawProof...) } if ancestor.MapiResponses != nil && len(ancestor.MapiResponses) > 0 { binaryTxContext = append(binaryTxContext, flagMapi) numOfMapiResponses := bt.VarInt(uint64(len(ancestor.MapiResponses))) - binaryTxContext = append(binaryTxContext, numOfMapiResponses...) + binaryTxContext = append(binaryTxContext, numOfMapiResponses.Bytes()...) for _, mapiResponse := range ancestor.MapiResponses { mapiR, err := mapiResponse.Bytes() if err != nil { return nil, err } dataLength := bt.VarInt(uint64(len(mapiR))) - binaryTxContext = append(binaryTxContext, dataLength...) + binaryTxContext = append(binaryTxContext, dataLength.Bytes()...) binaryTxContext = append(binaryTxContext, mapiR...) } } diff --git a/spv/ancestors_json_test.go b/spv/ancestors_json_test.go index ae1cf28..3513368 100644 --- a/spv/ancestors_json_test.go +++ b/spv/ancestors_json_test.go @@ -4,7 +4,6 @@ import ( "bytes" "encoding/hex" "encoding/json" - "fmt" "testing" "github.com/stretchr/testify/assert" @@ -47,10 +46,8 @@ func TestAncestryBinaryToJSON(t *testing.T) { j, err := spv.NewAncestoryJSONFromBytes(binary) assert.NoError(t, err, "expected no error when transforming to json struct") - jsonBytes, err := json.Marshal(j) + _, err = json.Marshal(j) assert.NoError(t, err, "expected no error when transforming to json bytes") - fmt.Println(string(jsonBytes)) - assert.NoError(t, err) } }) } diff --git a/spv/envelope.go b/spv/envelope.go index 61bda92..09ebf52 100644 --- a/spv/envelope.go +++ b/spv/envelope.go @@ -73,20 +73,20 @@ func serializeParents(parents map[string]*Envelope, flake *[]byte, root bool) er if !root { *flake = append(*flake, flagTx) // first data will always be a rawTx. } - *flake = append(*flake, dataLength...) // of this length. - *flake = append(*flake, currentTx...) // the data. + *flake = append(*flake, dataLength.Bytes()...) // of this length. + *flake = append(*flake, currentTx...) // the data. if input.MapiResponses != nil && len(input.MapiResponses) > 0 { *flake = append(*flake, flagMapi) // next data will be a mapi response. numMapis := bt.VarInt(uint64(len(input.MapiResponses))) - *flake = append(*flake, numMapis...) // number of mapi reponses which follow + *flake = append(*flake, numMapis.Bytes()...) // number of mapi reponses which follow for _, mapiResponse := range input.MapiResponses { mapiR, err := mapiResponse.Bytes() if err != nil { return err } dataLength := bt.VarInt(uint64(len(mapiR))) - *flake = append(*flake, dataLength...) // of this length. - *flake = append(*flake, mapiR...) // the data. + *flake = append(*flake, dataLength.Bytes()...) // of this length. + *flake = append(*flake, mapiR...) // the data. } } if input.Proof != nil { @@ -95,9 +95,9 @@ func serializeParents(parents map[string]*Envelope, flake *[]byte, root bool) er return errors.Wrap(err, "Failed to serialise this input's proof struct") } proofLength := bt.VarInt(uint64(len(proof))) - *flake = append(*flake, flagProof) // it's going to be a proof. - *flake = append(*flake, proofLength...) // of this length. - *flake = append(*flake, proof...) // the data. + *flake = append(*flake, flagProof) // it's going to be a proof. + *flake = append(*flake, proofLength.Bytes()...) // of this length. + *flake = append(*flake, proof...) // the data. } else if input.HasParents() { err = serializeParents(input.Parents, flake, false) if err != nil { diff --git a/spv/verifymerkleproof.go b/spv/verifymerkleproof.go index 2937521..e4f00bc 100644 --- a/spv/verifymerkleproof.go +++ b/spv/verifymerkleproof.go @@ -251,17 +251,18 @@ type merkleProofBinary struct { func parseBinaryMerkleProof(proof []byte) (*merkleProofBinary, error) { mpb := &merkleProofBinary{} - var offset, size int + var offset int // flags is first byte mpb.flags = proof[offset] offset++ // index is the next varint after the 1st byte - mpb.index, size = bt.DecodeVarInt(proof[offset:]) + index, size := bt.NewVarIntFromBytes(proof[offset:]) + mpb.index = uint64(index) offset += size - var txLength uint64 + var txLength bt.VarInt // if bit 1 of flags is NOT set, txOrId should contain txid (= 32 bytes) if mpb.flags&1 == 0 { txLength = 32 @@ -270,7 +271,7 @@ func parseBinaryMerkleProof(proof []byte) (*merkleProofBinary, error) { // if bit 1 of flags is set, txOrId should contain tx hex (> 32 bytes) if mpb.flags&1 == 1 { // txLength is the next varint after the 1st byte + index size - txLength, size = bt.DecodeVarInt(proof[offset:]) + txLength, size = bt.NewVarIntFromBytes(proof[offset:]) offset += size if txLength <= 32 { return nil, errors.New("invalid tx length (should be greater than 32 bytes)") @@ -297,7 +298,7 @@ func parseBinaryMerkleProof(proof []byte) (*merkleProofBinary, error) { return nil, ErrInvalidMerkleFlags } - nodeCount, size := bt.DecodeVarInt(proof[offset:]) + nodeCount, size := bt.NewVarIntFromBytes(proof[offset:]) offset += size if mpb.index >= 1< 0xffffffff { + return 0 + } + + if l == 0 { + return 1 + } + + if l == 1 { + // data can be represented as Op1 to Op16, or OpNegate + if bb[0] <= 16 || bb[0] == 0x81 { + // OpX + return 1 + } + // OP_DATA_1 + data + return 2 + } + + // OP_DATA_X + data + if l <= 75 { + return l + 1 + } + // OP_PUSHDATA1 + length byte + data + if l <= 0xff { + return l + 2 + } + // OP_PUSHDATA2 + two length bytes + data + if l <= 0xffff { + return l + 3 + } + + // OP_PUSHDATA4 + four length bytes + data + return l + 5 +} + // MarshalJSON convert script into json. func (s *Script) MarshalJSON() ([]byte, error) { return []byte(fmt.Sprintf(`"%s"`, s.String())), nil diff --git a/vendor/github.com/libsv/go-bt/v2/fees.go b/vendor/github.com/libsv/go-bt/v2/fees.go index ca209b0..6c2f4a6 100644 --- a/vendor/github.com/libsv/go-bt/v2/fees.go +++ b/vendor/github.com/libsv/go-bt/v2/fees.go @@ -200,6 +200,13 @@ func (f *FeeQuote) AddQuote(ft FeeType, fee *Fee) *FeeQuote { return f } +// Expiry will return the expiry timestamp for the `bt.FeeQuote` in a threadsafe manner. +func (f *FeeQuote) Expiry() time.Time { + f.mu.RLock() + defer f.mu.RUnlock() + return f.expiryTime +} + // UpdateExpiry will update the expiry time of the quotes, this will be // used when you fetch a fresh set of quotes from a MAPI server which // should return an expiration time. diff --git a/vendor/github.com/libsv/go-bt/v2/input.go b/vendor/github.com/libsv/go-bt/v2/input.go index 2de84a0..93452bd 100644 --- a/vendor/github.com/libsv/go-bt/v2/input.go +++ b/vendor/github.com/libsv/go-bt/v2/input.go @@ -1,10 +1,13 @@ package bt import ( + "encoding/binary" "encoding/hex" "fmt" + "io" "github.com/libsv/go-bt/v2/bscript" + "github.com/pkg/errors" ) /* @@ -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 { @@ -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...) } } diff --git a/vendor/github.com/libsv/go-bt/v2/output.go b/vendor/github.com/libsv/go-bt/v2/output.go index c485960..2a48910 100644 --- a/vendor/github.com/libsv/go-bt/v2/output.go +++ b/vendor/github.com/libsv/go-bt/v2/output.go @@ -4,8 +4,10 @@ import ( "encoding/binary" "encoding/hex" "fmt" + "io" "github.com/libsv/go-bt/v2/bscript" + "github.com/pkg/errors" ) /* @@ -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 { @@ -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 @@ -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 diff --git a/vendor/github.com/libsv/go-bt/v2/signaturehash.go b/vendor/github.com/libsv/go-bt/v2/signaturehash.go index e7988ce..60af39f 100644 --- a/vendor/github.com/libsv/go-bt/v2/signaturehash.go +++ b/vendor/github.com/libsv/go-bt/v2/signaturehash.go @@ -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) @@ -226,7 +226,7 @@ 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())...) @@ -234,7 +234,7 @@ func (tx *Tx) CalcInputPreimageLegacy(inputNumber uint32, shf sighash.Flag) ([]b 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) @@ -242,13 +242,13 @@ func (tx *Tx) CalcInputPreimageLegacy(inputNumber uint32, shf sighash.Flag) ([]b 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...) } diff --git a/vendor/github.com/libsv/go-bt/v2/tx.go b/vendor/github.com/libsv/go-bt/v2/tx.go index 052eaf8..232034f 100644 --- a/vendor/github.com/libsv/go-bt/v2/tx.go +++ b/vendor/github.com/libsv/go-bt/v2/tx.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/binary" "encoding/hex" + "io" "github.com/libsv/go-bk/crypto" @@ -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 @@ -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 @@ -127,6 +128,95 @@ 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 +} + +// ReadFrom txs from a block in a `bt.Txs`. This assumes a preceding varint detailing +// the total number of txs that the reader will provide. +func (tt *Txs) ReadFrom(r io.Reader) (int64, error) { + var bytesRead int64 + + var txCount VarInt + n, err := txCount.ReadFrom(r) + bytesRead += n + if err != nil { + return bytesRead, err + } + + *tt = make([]*Tx, txCount) + + for i := uint64(0); i < uint64(txCount); i++ { + tx := new(Tx) + n, err := tx.ReadFrom(r) + bytesRead += n + if err != nil { + return bytesRead, err + } + + (*tt)[i] = tx + } + + return bytesRead, nil +} + // HasDataOutputs returns true if the transaction has // at least one data (OP_RETURN) output in it. func (tx *Tx) HasDataOutputs() bool { @@ -258,19 +348,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()...) } diff --git a/vendor/github.com/libsv/go-bt/v2/txchange.go b/vendor/github.com/libsv/go-bt/v2/txchange.go index 2de4107..1a7debf 100644 --- a/vendor/github.com/libsv/go-bt/v2/txchange.go +++ b/vendor/github.com/libsv/go-bt/v2/txchange.go @@ -96,7 +96,7 @@ func (tx *Tx) change(f *FeeQuote, output *changeOutput) (uint64, bool, error) { // - not enough funds for change // We also return the change output fee amount, if we can add change func (tx *Tx) canAddChange(txFees *TxFees, standardFees *Fee) (uint64, bool) { - varIntUpper := VarIntUpperLimitInc(uint64(tx.OutputCount())) + varIntUpper := VarInt(uint64(tx.OutputCount())).UpperLimitInc() if varIntUpper == -1 { return 0, false // upper limit of Outputs in one tx reached } diff --git a/vendor/github.com/libsv/go-bt/v2/txinput.go b/vendor/github.com/libsv/go-bt/v2/txinput.go index 7dac747..937898d 100644 --- a/vendor/github.com/libsv/go-bt/v2/txinput.go +++ b/vendor/github.com/libsv/go-bt/v2/txinput.go @@ -29,7 +29,7 @@ func newInputFromBytes(bytes []byte) (*Input, int, error) { } offset := 36 - l, size := DecodeVarInt(bytes[offset:]) + l, size := NewVarIntFromBytes(bytes[offset:]) offset += size totalLength := offset + int(l) + 4 // 4 bytes for nSeq diff --git a/vendor/github.com/libsv/go-bt/v2/txoutput.go b/vendor/github.com/libsv/go-bt/v2/txoutput.go index 0d90597..e6a241e 100644 --- a/vendor/github.com/libsv/go-bt/v2/txoutput.go +++ b/vendor/github.com/libsv/go-bt/v2/txoutput.go @@ -18,7 +18,7 @@ func newOutputFromBytes(bytes []byte) (*Output, int, error) { } offset := 8 - l, size := DecodeVarInt(bytes[offset:]) + l, size := NewVarIntFromBytes(bytes[offset:]) offset += size totalLength := offset + int(l) @@ -135,21 +135,18 @@ func (tx *Tx) AddHashPuzzleOutput(secret, publicKeyHash string, satoshis uint64) s := &bscript.Script{} - s.AppendOpCode(bscript.OpHASH160) + _ = s.AppendOpcodes(bscript.OpHASH160) secretBytesHash := crypto.Hash160([]byte(secret)) if err = s.AppendPushData(secretBytesHash); err != nil { return err } - s.AppendOpCode(bscript.OpEQUALVERIFY). - AppendOpCode(bscript.OpDUP). - AppendOpCode(bscript.OpHASH160) + _ = s.AppendOpcodes(bscript.OpEQUALVERIFY, bscript.OpDUP, bscript.OpHASH160) if err = s.AppendPushData(publicKeyHashBytes); err != nil { return err } - s.AppendOpCode(bscript.OpEQUALVERIFY). - AppendOpCode(bscript.OpCHECKSIG) + _ = s.AppendOpcodes(bscript.OpEQUALVERIFY, bscript.OpCHECKSIG) tx.AddOutput(&Output{ Satoshis: satoshis, @@ -184,10 +181,8 @@ func (tx *Tx) AddOpReturnPartsOutput(data [][]byte) error { func createOpReturnOutput(data [][]byte) (*Output, error) { s := &bscript.Script{} - s.AppendOpCode(bscript.OpFALSE) - s.AppendOpCode(bscript.OpRETURN) - err := s.AppendPushDataArray(data) - if err != nil { + _ = s.AppendOpcodes(bscript.OpFALSE, bscript.OpRETURN) + if err := s.AppendPushDataArray(data); err != nil { return nil, err } diff --git a/vendor/github.com/libsv/go-bt/v2/varint.go b/vendor/github.com/libsv/go-bt/v2/varint.go index d345f0b..edd4f75 100644 --- a/vendor/github.com/libsv/go-bt/v2/varint.go +++ b/vendor/github.com/libsv/go-bt/v2/varint.go @@ -2,64 +2,115 @@ package bt import ( "encoding/binary" + "io" + + "github.com/pkg/errors" ) -// VarInt takes an unsigned integer and returns a byte array in VarInt format. +// VarInt (variable integer) is a field used in transaction data to indicate the number of +// upcoming fields, or the length of an upcoming field. +// See http://learnmeabitcoin.com/glossary/varint +type VarInt uint64 + +// NewVarIntFromBytes takes a byte array in VarInt format and returns the +// decoded unsigned integer value of the length, and it's size in bytes. // See http://learnmeabitcoin.com/glossary/varint -func VarInt(i uint64) []byte { +func NewVarIntFromBytes(bb []byte) (VarInt, int) { + switch bb[0] { + case 0xff: + return VarInt(binary.LittleEndian.Uint64(bb[1:9])), 9 + case 0xfe: + return VarInt(binary.LittleEndian.Uint32(bb[1:5])), 5 + case 0xfd: + return VarInt(binary.LittleEndian.Uint16(bb[1:3])), 3 + default: + return VarInt(binary.LittleEndian.Uint16([]byte{bb[0], 0x00})), 1 + } +} + +// Length return the length of the underlying byte representation of the `bt.VarInt`. +func (v VarInt) Length() int { + if v < 253 { + return 1 + } + if v < 65536 { + return 3 + } + if v < 4294967296 { + return 5 + } + return 9 +} + +// Bytes takes the underlying unsigned integer and returns a byte array in VarInt format. +// See http://learnmeabitcoin.com/glossary/varint +func (v VarInt) Bytes() []byte { b := make([]byte, 9) - if i < 0xfd { - b[0] = byte(i) + if v < 0xfd { + b[0] = byte(v) return b[:1] } - if i < 0x10000 { + if v < 0x10000 { b[0] = 0xfd - binary.LittleEndian.PutUint16(b[1:3], uint16(i)) + binary.LittleEndian.PutUint16(b[1:3], uint16(v)) return b[:3] } - if i < 0x100000000 { + if v < 0x100000000 { b[0] = 0xfe - binary.LittleEndian.PutUint32(b[1:5], uint32(i)) + binary.LittleEndian.PutUint32(b[1:5], uint32(v)) return b[:5] } b[0] = 0xff - binary.LittleEndian.PutUint64(b[1:9], i) + binary.LittleEndian.PutUint64(b[1:9], uint64(v)) return b } -// DecodeVarInt takes a byte array in VarInt format and returns the -// decoded unsigned integer value of the length, and it's size in bytes. -// See http://learnmeabitcoin.com/glossary/varint -func DecodeVarInt(b []byte) (result uint64, size int) { +// ReadFrom reads the next varint from the io.Reader and assigned it to itself. +func (v *VarInt) ReadFrom(r io.Reader) (int64, error) { + b := make([]byte, 1) + if _, err := io.ReadFull(r, b); err != nil { + return 0, errors.Wrap(err, "could not read varint type") + } + switch b[0] { case 0xff: - result = binary.LittleEndian.Uint64(b[1:9]) - size = 9 + bb := make([]byte, 8) + if n, err := io.ReadFull(r, bb); err != nil { + return 9, errors.Wrapf(err, "varint(8): got %d bytes", n) + } + *v = VarInt(binary.LittleEndian.Uint64(bb)) + return 9, nil case 0xfe: - result = uint64(binary.LittleEndian.Uint32(b[1:5])) - size = 5 + bb := make([]byte, 4) + if n, err := io.ReadFull(r, bb); err != nil { + return 5, errors.Wrapf(err, "varint(4): got %d bytes", n) + } + *v = VarInt(binary.LittleEndian.Uint32(bb)) + return 5, nil case 0xfd: - result = uint64(binary.LittleEndian.Uint16(b[1:3])) - size = 3 + bb := make([]byte, 2) + if n, err := io.ReadFull(r, bb); err != nil { + return 3, errors.Wrapf(err, "varint(2): got %d bytes", n) + } + *v = VarInt(binary.LittleEndian.Uint16(bb)) + return 3, nil default: - result = uint64(binary.LittleEndian.Uint16([]byte{b[0], 0x00})) - size = 1 + *v = VarInt(binary.LittleEndian.Uint16([]byte{b[0], 0x00})) + return 1, nil } - - return } -// VarIntUpperLimitInc returns true if a number is at the +// UpperLimitInc returns true if a number is at the // upper limit of a VarInt and will result in a VarInt // length change if incremented. The value returned will // indicate how many bytes will be increase if the length // in incremented. -1 will be returned when the upper limit // of VarInt is reached. -func VarIntUpperLimitInc(length uint64) int { - switch length { +func (v VarInt) UpperLimitInc() int { + switch uint64(v) { case 252, 65535: return 2 case 4294967295: @@ -67,5 +118,6 @@ func VarIntUpperLimitInc(length uint64) int { case 18446744073709551615: return -1 } + return 0 } diff --git a/vendor/modules.txt b/vendor/modules.txt index 721e1ed..cedd609 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -10,7 +10,7 @@ github.com/libsv/go-bk/bec github.com/libsv/go-bk/bip32 github.com/libsv/go-bk/chaincfg github.com/libsv/go-bk/crypto -# github.com/libsv/go-bt/v2 v2.1.0-beta.1 +# github.com/libsv/go-bt/v2 v2.1.0-beta.2 ## explicit; go 1.17 github.com/libsv/go-bt/v2 github.com/libsv/go-bt/v2/bscript