wire: Add func to get pkscript locs from a tx.

This commit provides a new function named PkScriptLocs on the MsgTx type
which can be used to efficiently retrieve a list of offsets for the public
key scripts for the serialized form of the transaction.

This is useful for certain applications which store fully serialized
transactions and want to be able to quickly index into the serialized
transaction to extract a give public key script directly thereby avoiding
the need to deserialize the entire transaction.
This commit is contained in:
Dave Collins 2015-03-09 21:49:01 -05:00
parent def0ef6af6
commit 62432a6f90
3 changed files with 100 additions and 7 deletions

View File

@ -108,8 +108,9 @@ func (msg *MsgBlock) Deserialize(r io.Reader) error {
} }
// DeserializeTxLoc decodes r in the same manner Deserialize does, but it takes // DeserializeTxLoc decodes r in the same manner Deserialize does, but it takes
// a byte buffer instead of a generic reader and returns a slice containing the start and length of // a byte buffer instead of a generic reader and returns a slice containing the
// each transaction within the raw data that is being deserialized. // start and length of each transaction within the raw data that is being
// deserialized.
func (msg *MsgBlock) DeserializeTxLoc(r *bytes.Buffer) ([]TxLoc, error) { func (msg *MsgBlock) DeserializeTxLoc(r *bytes.Buffer) ([]TxLoc, error) {
fullLen := r.Len() fullLen := r.Len()

View File

@ -430,6 +430,43 @@ func (msg *MsgTx) MaxPayloadLength(pver uint32) uint32 {
return MaxBlockPayload return MaxBlockPayload
} }
// PkScriptLocs returns a slice containing the start of each public key script
// within the raw serialized transaction. The caller can easily obtain the
// length of each script by using len on the script available via the
// appropriate transaction output entry.
func (msg *MsgTx) PkScriptLocs() []int {
numTxOut := len(msg.TxOut)
if numTxOut == 0 {
return nil
}
// The starting offset in the serialized transaction of the first
// transaction output is:
//
// Version 4 bytes + serialized varint size for the number of
// transaction inputs and outputs + serialized size of each transaction
// input.
n := 4 + VarIntSerializeSize(uint64(len(msg.TxIn))) +
VarIntSerializeSize(uint64(numTxOut))
for _, txIn := range msg.TxIn {
n += txIn.SerializeSize()
}
// Calculate and set the appropriate offset for each public key script.
pkScriptLocs := make([]int, numTxOut)
for i, txOut := range msg.TxOut {
// The offset of the script in the transaction output is:
//
// Value 8 bytes + serialized varint size for the length of
// PkScript.
n += 8 + VarIntSerializeSize(uint64(len(txOut.PkScript)))
pkScriptLocs[i] = n
n += len(txOut.PkScript)
}
return pkScriptLocs
}
// NewMsgTx returns a new bitcoin tx message that conforms to the Message // NewMsgTx returns a new bitcoin tx message that conforms to the Message
// interface. The return instance has a default version of TxVersion and there // interface. The return instance has a default version of TxVersion and there
// are no transaction inputs or outputs. Also, the lock time is set to zero // are no transaction inputs or outputs. Also, the lock time is set to zero

View File

@ -389,15 +389,17 @@ func TestTxSerialize(t *testing.T) {
} }
tests := []struct { tests := []struct {
in *wire.MsgTx // Message to encode in *wire.MsgTx // Message to encode
out *wire.MsgTx // Expected decoded message out *wire.MsgTx // Expected decoded message
buf []byte // Serialized data buf []byte // Serialized data
pkScriptLocs []int // Expected output script locations
}{ }{
// No transactions. // No transactions.
{ {
noTx, noTx,
noTx, noTx,
noTxEncoded, noTxEncoded,
nil,
}, },
// Multiple transactions. // Multiple transactions.
@ -405,6 +407,7 @@ func TestTxSerialize(t *testing.T) {
multiTx, multiTx,
multiTx, multiTx,
multiTxEncoded, multiTxEncoded,
multiTxPkScriptLocs,
}, },
} }
@ -436,6 +439,25 @@ func TestTxSerialize(t *testing.T) {
spew.Sdump(&tx), spew.Sdump(test.out)) spew.Sdump(&tx), spew.Sdump(test.out))
continue continue
} }
// Ensure the public key script locations are accurate.
pkScriptLocs := test.in.PkScriptLocs()
if !reflect.DeepEqual(pkScriptLocs, test.pkScriptLocs) {
t.Errorf("PkScriptLocs #%d\n got: %s want: %s", i,
spew.Sdump(pkScriptLocs),
spew.Sdump(test.pkScriptLocs))
continue
}
for j, loc := range pkScriptLocs {
wantPkScript := test.in.TxOut[j].PkScript
gotPkScript := test.buf[loc : loc+len(wantPkScript)]
if !bytes.Equal(gotPkScript, wantPkScript) {
t.Errorf("PkScriptLocs #%d:%d\n unexpected "+
"script got: %s want: %s", i, j,
spew.Sdump(gotPkScript),
spew.Sdump(wantPkScript))
}
}
} }
} }
@ -611,7 +633,7 @@ func TestTxSerializeSize(t *testing.T) {
{noTx, 10}, {noTx, 10},
// Transcaction with an input and an output. // Transcaction with an input and an output.
{multiTx, 134}, {multiTx, 210},
} }
t.Logf("Running %d tests", len(tests)) t.Logf("Running %d tests", len(tests))
@ -657,6 +679,22 @@ var multiTx = &wire.MsgTx{
0xac, // OP_CHECKSIG 0xac, // OP_CHECKSIG
}, },
}, },
{
Value: 0x5f5e100,
PkScript: []byte{
0x41, // OP_DATA_65
0x04, 0xd6, 0x4b, 0xdf, 0xd0, 0x9e, 0xb1, 0xc5,
0xfe, 0x29, 0x5a, 0xbd, 0xeb, 0x1d, 0xca, 0x42,
0x81, 0xbe, 0x98, 0x8e, 0x2d, 0xa0, 0xb6, 0xc1,
0xc6, 0xa5, 0x9d, 0xc2, 0x26, 0xc2, 0x86, 0x24,
0xe1, 0x81, 0x75, 0xe8, 0x51, 0xc9, 0x6b, 0x97,
0x3d, 0x81, 0xb0, 0x1c, 0xc3, 0x1f, 0x04, 0x78,
0x34, 0xbc, 0x06, 0xd6, 0xd6, 0xed, 0xf6, 0x20,
0xd1, 0x84, 0x24, 0x1a, 0x6a, 0xed, 0x8b, 0x63,
0xa6, // 65-byte signature
0xac, // OP_CHECKSIG
},
},
}, },
LockTime: 0, LockTime: 0,
} }
@ -674,7 +712,7 @@ var multiTxEncoded = []byte{
0x07, // Varint for length of signature script 0x07, // Varint for length of signature script
0x04, 0x31, 0xdc, 0x00, 0x1b, 0x01, 0x62, // Signature script 0x04, 0x31, 0xdc, 0x00, 0x1b, 0x01, 0x62, // Signature script
0xff, 0xff, 0xff, 0xff, // Sequence 0xff, 0xff, 0xff, 0xff, // Sequence
0x01, // Varint for number of output transactions 0x02, // Varint for number of output transactions
0x00, 0xf2, 0x05, 0x2a, 0x01, 0x00, 0x00, 0x00, // Transaction amount 0x00, 0xf2, 0x05, 0x2a, 0x01, 0x00, 0x00, 0x00, // Transaction amount
0x43, // Varint for length of pk script 0x43, // Varint for length of pk script
0x41, // OP_DATA_65 0x41, // OP_DATA_65
@ -686,7 +724,24 @@ var multiTxEncoded = []byte{
0x3d, 0x81, 0xb0, 0x1c, 0xc3, 0x1f, 0x04, 0x78, 0x3d, 0x81, 0xb0, 0x1c, 0xc3, 0x1f, 0x04, 0x78,
0x34, 0xbc, 0x06, 0xd6, 0xd6, 0xed, 0xf6, 0x20, 0x34, 0xbc, 0x06, 0xd6, 0xd6, 0xed, 0xf6, 0x20,
0xd1, 0x84, 0x24, 0x1a, 0x6a, 0xed, 0x8b, 0x63, 0xd1, 0x84, 0x24, 0x1a, 0x6a, 0xed, 0x8b, 0x63,
0xa6, // 65-byte signature
0xac, // OP_CHECKSIG
0x00, 0xe1, 0xf5, 0x05, 0x00, 0x00, 0x00, 0x00, // Transaction amount
0x43, // Varint for length of pk script
0x41, // OP_DATA_65
0x04, 0xd6, 0x4b, 0xdf, 0xd0, 0x9e, 0xb1, 0xc5,
0xfe, 0x29, 0x5a, 0xbd, 0xeb, 0x1d, 0xca, 0x42,
0x81, 0xbe, 0x98, 0x8e, 0x2d, 0xa0, 0xb6, 0xc1,
0xc6, 0xa5, 0x9d, 0xc2, 0x26, 0xc2, 0x86, 0x24,
0xe1, 0x81, 0x75, 0xe8, 0x51, 0xc9, 0x6b, 0x97,
0x3d, 0x81, 0xb0, 0x1c, 0xc3, 0x1f, 0x04, 0x78,
0x34, 0xbc, 0x06, 0xd6, 0xd6, 0xed, 0xf6, 0x20,
0xd1, 0x84, 0x24, 0x1a, 0x6a, 0xed, 0x8b, 0x63,
0xa6, // 65-byte signature 0xa6, // 65-byte signature
0xac, // OP_CHECKSIG 0xac, // OP_CHECKSIG
0x00, 0x00, 0x00, 0x00, // Lock time 0x00, 0x00, 0x00, 0x00, // Lock time
} }
// multiTxPkScriptLocs is the location information for the public key scripts
// located in multiTx.
var multiTxPkScriptLocs = []int{63, 139}