Skip to content

Commit

Permalink
wire: add proper types for flag field and improve docs
Browse files Browse the repository at this point in the history
Summary of changes:

- Add a new const TxFlagMarker to indicate the flag prefix byte.
- Add a new TxFlag type to enumerate the flags supported by the
  tx parser.

  This allows us to avoid hardcoded magics, and will make it easier
  to support new flags in future.
- Improve code comments.

Closes #1598.
  • Loading branch information
onyb committed Sep 13, 2020
1 parent 9ef973c commit 07ba686
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 30 deletions.
70 changes: 46 additions & 24 deletions wire/msgtx.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,14 +109,38 @@ const (
maxWitnessItemSize = 11000
)

// witnessMarkerBytes are a pair of bytes specific to the witness encoding. If
// this sequence is encoutered, then it indicates a transaction has iwtness
// data. The first byte is an always 0x00 marker byte, which allows decoders to
// distinguish a serialized transaction with witnesses from a regular (legacy)
// one. The second byte is the Flag field, which at the moment is always 0x01,
// but may be extended in the future to accommodate auxiliary non-committed
// fields.
var witessMarkerBytes = []byte{0x00, 0x01}
// TxFlagMarker is the first byte of the FLAG field in a bitcoin tx
// message. It allows decoders to distinguish a regular serialized
// transaction from one that would require a different parsing logic.
//
// Position of FLAG in a bitcoin tx message:
// ┌─────────┬────────────────────┬─────────────┬─────┐
// │ VERSION │ FLAG │ TX-IN-COUNT │ ... │
// │ 4 bytes │ 2 bytes (optional) │ varint │ │
// └─────────┴────────────────────┴─────────────┴─────┘
//
// Zooming into the FLAG field:
// ┌── FLAG ─────────────┬────────┐
// │ TxFlagMarker (0x00) │ TxFlag │
// │ 1 byte │ 1 byte │
// └─────────────────────┴────────┘
const TxFlagMarker = 0x00

// TxFlag is the second byte of the FLAG field in a bitcoin tx message.
// It indicates the decoding logic to use in the transaction parser, if
// TxFlagMarker is detected in the tx message.
//
// As of writing this, only the witness flag (0x01) is supported, but may be
// extended in the future to accommodate auxiliary non-committed fields.
type TxFlag = byte

const (
// WitnessFlag is a flag specific to witness encoding. If the TxFlagMarker
// is encountered followed by the WitnessFlag, then it indicates a
// transaction has witness data. This allows decoders to distinguish a
// serialized transaction with witnesses from a legacy one.
WitnessFlag TxFlag = 0x01
)

// scriptFreeList defines a free list of byte slices (up to the maximum number
// defined by the freeListMaxItems constant) that have a cap according to the
Expand Down Expand Up @@ -420,18 +444,19 @@ func (msg *MsgTx) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error
return err
}

// A count of zero (meaning no TxIn's to the uninitiated) indicates
// this is a transaction with witness data.
var flag [1]byte
if count == 0 && enc == WitnessEncoding {
// Next, we need to read the flag, which is a single byte.
// A count of zero (meaning no TxIn's to the uninitiated) means that the
// value is a TxFlagMarker, and hence indicates the presence of a flag.
var flag [1]TxFlag
if count == TxFlagMarker && enc == WitnessEncoding {
// The count varint was in fact the flag marker byte. Next, we need to
// read the flag value, which is a single byte.
if _, err = io.ReadFull(r, flag[:]); err != nil {
return err
}

// At the moment, the flag MUST be 0x01. In the future other
// flag types may be supported.
if flag[0] != 0x01 {
// At the moment, the flag MUST be WitnessFlag (0x01). In the future
// other flag types may be supported.
if flag[0] != WitnessFlag {
str := fmt.Sprintf("witness tx but flag byte is %x", flag)
return messageError("MsgTx.BtcDecode", str)
}
Expand Down Expand Up @@ -690,14 +715,11 @@ func (msg *MsgTx) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error
// defined in BIP0144.
doWitness := enc == WitnessEncoding && msg.HasWitness()
if doWitness {
// After the txn's Version field, we include two additional
// bytes specific to the witness encoding. The first byte is an
// always 0x00 marker byte, which allows decoders to
// distinguish a serialized transaction with witnesses from a
// regular (legacy) one. The second byte is the Flag field,
// which at the moment is always 0x01, but may be extended in
// the future to accommodate auxiliary non-committed fields.
if _, err := w.Write(witessMarkerBytes); err != nil {
// After the transaction's Version field, we include two additional
// bytes specific to the witness encoding. This byte sequence is known
// as a flag. The first byte is a marker byte (TxFlagMarker) and the
// second one is the flag value to indicate presence of witness data.
if _, err := w.Write([]byte{TxFlagMarker, WitnessFlag}); err != nil {
return err
}
}
Expand Down
12 changes: 6 additions & 6 deletions wire/msgtx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -935,9 +935,9 @@ var multiWitnessTx = &MsgTx{
// tests.
var multiWitnessTxEncoded = []byte{
0x1, 0x0, 0x0, 0x0, // Version
0x0, // Marker byte indicating 0 inputs, or a segwit encoded tx
0x1, // Flag byte
0x1, // Varint for number of inputs
TxFlagMarker, // Marker byte indicating 0 inputs, or a segwit encoded tx
WitnessFlag, // Flag byte
0x1, // Varint for number of inputs
0xa5, 0x33, 0x52, 0xd5, 0x13, 0x57, 0x66, 0xf0,
0x30, 0x76, 0x59, 0x74, 0x18, 0x26, 0x3d, 0xa2,
0xd9, 0xc9, 0x58, 0x31, 0x59, 0x68, 0xfe, 0xa8,
Expand Down Expand Up @@ -978,9 +978,9 @@ var multiWitnessTxEncoded = []byte{
// being set to 0x01, the flag is 0x00, which should trigger a decoding error.
var multiWitnessTxEncodedNonZeroFlag = []byte{
0x1, 0x0, 0x0, 0x0, // Version
0x0, // Marker byte indicating 0 inputs, or a segwit encoded tx
0x0, // Incorrect flag byte (should be 0x01)
0x1, // Varint for number of inputs
TxFlagMarker, // Marker byte indicating 0 inputs, or a segwit encoded tx
0x0, // Incorrect flag byte (should be 0x01)
0x1, // Varint for number of inputs
0xa5, 0x33, 0x52, 0xd5, 0x13, 0x57, 0x66, 0xf0,
0x30, 0x76, 0x59, 0x74, 0x18, 0x26, 0x3d, 0xa2,
0xd9, 0xc9, 0x58, 0x31, 0x59, 0x68, 0xfe, 0xa8,
Expand Down

0 comments on commit 07ba686

Please sign in to comment.