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
// a byte buffer instead of a generic reader and returns a slice containing the start and length of
// each transaction within the raw data that is being deserialized.
// a byte buffer instead of a generic reader and returns a slice containing the
// start and length of each transaction within the raw data that is being
// deserialized.
func (msg *MsgBlock) DeserializeTxLoc(r *bytes.Buffer) ([]TxLoc, error) {
fullLen := r.Len()

View file

@ -430,6 +430,43 @@ func (msg *MsgTx) MaxPayloadLength(pver uint32) uint32 {
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
// 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

View file

@ -389,15 +389,17 @@ func TestTxSerialize(t *testing.T) {
}
tests := []struct {
in *wire.MsgTx // Message to encode
out *wire.MsgTx // Expected decoded message
buf []byte // Serialized data
in *wire.MsgTx // Message to encode
out *wire.MsgTx // Expected decoded message
buf []byte // Serialized data
pkScriptLocs []int // Expected output script locations
}{
// No transactions.
{
noTx,
noTx,
noTxEncoded,
nil,
},
// Multiple transactions.
@ -405,6 +407,7 @@ func TestTxSerialize(t *testing.T) {
multiTx,
multiTx,
multiTxEncoded,
multiTxPkScriptLocs,
},
}
@ -436,6 +439,25 @@ func TestTxSerialize(t *testing.T) {
spew.Sdump(&tx), spew.Sdump(test.out))
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},
// Transcaction with an input and an output.
{multiTx, 134},
{multiTx, 210},
}
t.Logf("Running %d tests", len(tests))
@ -657,6 +679,22 @@ var multiTx = &wire.MsgTx{
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,
}
@ -674,7 +712,7 @@ var multiTxEncoded = []byte{
0x07, // Varint for length of signature script
0x04, 0x31, 0xdc, 0x00, 0x1b, 0x01, 0x62, // Signature script
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
0x43, // Varint for length of pk script
0x41, // OP_DATA_65
@ -686,7 +724,24 @@ var multiTxEncoded = []byte{
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
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
0xac, // OP_CHECKSIG
0x00, 0x00, 0x00, 0x00, // Lock time
}
// multiTxPkScriptLocs is the location information for the public key scripts
// located in multiTx.
var multiTxPkScriptLocs = []int{63, 139}