From e25e3e73e71f7de2acdc8dcc10433c08dafe6238 Mon Sep 17 00:00:00 2001 From: Virgil Date: Sat, 4 Apr 2026 20:39:45 +0000 Subject: [PATCH] refactor(wire): deduplicate output target encoding Co-Authored-By: Charon --- wire/transaction.go | 155 ++++++++++++++++++-------------------------- 1 file changed, 63 insertions(+), 92 deletions(-) diff --git a/wire/transaction.go b/wire/transaction.go index 3014b57..28cd8ed 100644 --- a/wire/transaction.go +++ b/wire/transaction.go @@ -271,6 +271,63 @@ func decodeKeyOffsets(dec *Decoder) []types.TxOutRef { return refs } +func encodeTxOutTarget(enc *Encoder, target types.TxOutTarget, context string) bool { + switch t := target.(type) { + case types.TxOutToKey: + enc.WriteVariantTag(types.TargetTypeToKey) + enc.WriteBlob32((*[32]byte)(&t.Key)) + enc.WriteUint8(t.MixAttr) + case types.TxOutMultisig: + enc.WriteVariantTag(types.TargetTypeMultisig) + enc.WriteVarint(t.MinimumSigs) + enc.WriteVarint(uint64(len(t.Keys))) + for i := range t.Keys { + enc.WriteBlob32((*[32]byte)(&t.Keys[i])) + } + case types.TxOutHTLC: + enc.WriteVariantTag(types.TargetTypeHTLC) + enc.WriteBlob32((*[32]byte)(&t.HTLCHash)) + enc.WriteUint8(t.Flags) + enc.WriteVarint(t.Expiration) + enc.WriteBlob32((*[32]byte)(&t.PKRedeem)) + enc.WriteBlob32((*[32]byte)(&t.PKRefund)) + default: + enc.err = coreerr.E(context, fmt.Sprintf("wire: unsupported output target type %T", target), nil) + return false + } + return true +} + +func decodeTxOutTarget(dec *Decoder, tag uint8, context string) types.TxOutTarget { + switch tag { + case types.TargetTypeToKey: + var t types.TxOutToKey + dec.ReadBlob32((*[32]byte)(&t.Key)) + t.MixAttr = dec.ReadUint8() + return t + case types.TargetTypeMultisig: + var t types.TxOutMultisig + t.MinimumSigs = dec.ReadVarint() + keyCount := dec.ReadVarint() + t.Keys = make([]types.PublicKey, keyCount) + for i := uint64(0); i < keyCount; i++ { + dec.ReadBlob32((*[32]byte)(&t.Keys[i])) + } + return t + case types.TargetTypeHTLC: + var t types.TxOutHTLC + dec.ReadBlob32((*[32]byte)(&t.HTLCHash)) + t.Flags = dec.ReadUint8() + t.Expiration = dec.ReadVarint() + dec.ReadBlob32((*[32]byte)(&t.PKRedeem)) + dec.ReadBlob32((*[32]byte)(&t.PKRefund)) + return t + default: + dec.err = coreerr.E(context, fmt.Sprintf("wire: unsupported target tag 0x%02x", tag), nil) + return nil + } +} + // --- outputs --- // encodeOutputsV1 serialises v0/v1 outputs. In v0/v1, outputs are tx_out_bare @@ -282,27 +339,7 @@ func encodeOutputsV1(enc *Encoder, vout []types.TxOutput) { case types.TxOutputBare: enc.WriteVarint(v.Amount) // Target is a variant (txout_target_v) - switch t := v.Target.(type) { - case types.TxOutToKey: - enc.WriteVariantTag(types.TargetTypeToKey) - enc.WriteBlob32((*[32]byte)(&t.Key)) - enc.WriteUint8(t.MixAttr) - case types.TxOutMultisig: - enc.WriteVariantTag(types.TargetTypeMultisig) - enc.WriteVarint(t.MinimumSigs) - enc.WriteVarint(uint64(len(t.Keys))) - for k := range t.Keys { - enc.WriteBlob32((*[32]byte)(&t.Keys[k])) - } - case types.TxOutHTLC: - enc.WriteVariantTag(types.TargetTypeHTLC) - enc.WriteBlob32((*[32]byte)(&t.HTLCHash)) - enc.WriteUint8(t.Flags) - enc.WriteVarint(t.Expiration) - enc.WriteBlob32((*[32]byte)(&t.PKRedeem)) - enc.WriteBlob32((*[32]byte)(&t.PKRefund)) - default: - enc.err = coreerr.E("encodeOutputsV1", fmt.Sprintf("wire: unsupported output target type %T", v.Target), nil) + if !encodeTxOutTarget(enc, v.Target, "encodeOutputsV1") { return } } @@ -322,31 +359,8 @@ func decodeOutputsV1(dec *Decoder) []types.TxOutput { if dec.Err() != nil { return vout } - switch tag { - case types.TargetTypeToKey: - var t types.TxOutToKey - dec.ReadBlob32((*[32]byte)(&t.Key)) - t.MixAttr = dec.ReadUint8() - out.Target = t - case types.TargetTypeMultisig: - var t types.TxOutMultisig - t.MinimumSigs = dec.ReadVarint() - keyCount := dec.ReadVarint() - t.Keys = make([]types.PublicKey, keyCount) - for k := uint64(0); k < keyCount; k++ { - dec.ReadBlob32((*[32]byte)(&t.Keys[k])) - } - out.Target = t - case types.TargetTypeHTLC: - var t types.TxOutHTLC - dec.ReadBlob32((*[32]byte)(&t.HTLCHash)) - t.Flags = dec.ReadUint8() - t.Expiration = dec.ReadVarint() - dec.ReadBlob32((*[32]byte)(&t.PKRedeem)) - dec.ReadBlob32((*[32]byte)(&t.PKRefund)) - out.Target = t - default: - dec.err = coreerr.E("decodeOutputsV1", fmt.Sprintf("wire: unsupported target tag 0x%02x", tag), nil) + out.Target = decodeTxOutTarget(dec, tag, "decodeOutputsV1") + if dec.Err() != nil { return vout } vout = append(vout, out) @@ -362,27 +376,7 @@ func encodeOutputsV2(enc *Encoder, vout []types.TxOutput) { switch v := out.(type) { case types.TxOutputBare: enc.WriteVarint(v.Amount) - switch t := v.Target.(type) { - case types.TxOutToKey: - enc.WriteVariantTag(types.TargetTypeToKey) - enc.WriteBlob32((*[32]byte)(&t.Key)) - enc.WriteUint8(t.MixAttr) - case types.TxOutMultisig: - enc.WriteVariantTag(types.TargetTypeMultisig) - enc.WriteVarint(t.MinimumSigs) - enc.WriteVarint(uint64(len(t.Keys))) - for k := range t.Keys { - enc.WriteBlob32((*[32]byte)(&t.Keys[k])) - } - case types.TxOutHTLC: - enc.WriteVariantTag(types.TargetTypeHTLC) - enc.WriteBlob32((*[32]byte)(&t.HTLCHash)) - enc.WriteUint8(t.Flags) - enc.WriteVarint(t.Expiration) - enc.WriteBlob32((*[32]byte)(&t.PKRedeem)) - enc.WriteBlob32((*[32]byte)(&t.PKRefund)) - default: - enc.err = coreerr.E("encodeOutputsV2", fmt.Sprintf("wire: unsupported output target type %T", v.Target), nil) + if !encodeTxOutTarget(enc, v.Target, "encodeOutputsV2") { return } case types.TxOutputZarcanum: @@ -412,31 +406,8 @@ func decodeOutputsV2(dec *Decoder) []types.TxOutput { var out types.TxOutputBare out.Amount = dec.ReadVarint() targetTag := dec.ReadVariantTag() - switch targetTag { - case types.TargetTypeToKey: - var t types.TxOutToKey - dec.ReadBlob32((*[32]byte)(&t.Key)) - t.MixAttr = dec.ReadUint8() - out.Target = t - case types.TargetTypeMultisig: - var t types.TxOutMultisig - t.MinimumSigs = dec.ReadVarint() - keyCount := dec.ReadVarint() - t.Keys = make([]types.PublicKey, keyCount) - for k := uint64(0); k < keyCount; k++ { - dec.ReadBlob32((*[32]byte)(&t.Keys[k])) - } - out.Target = t - case types.TargetTypeHTLC: - var t types.TxOutHTLC - dec.ReadBlob32((*[32]byte)(&t.HTLCHash)) - t.Flags = dec.ReadUint8() - t.Expiration = dec.ReadVarint() - dec.ReadBlob32((*[32]byte)(&t.PKRedeem)) - dec.ReadBlob32((*[32]byte)(&t.PKRefund)) - out.Target = t - default: - dec.err = coreerr.E("decodeOutputsV2", fmt.Sprintf("wire: unsupported target tag 0x%02x", targetTag), nil) + out.Target = decodeTxOutTarget(dec, targetTag, "decodeOutputsV2") + if dec.Err() != nil { return vout } vout = append(vout, out)