refactor(types): change TxOutputBare.Target to TxOutTarget interface
Some checks failed
Security Scan / security (push) Successful in 9s
Test / Test (push) Failing after 15s

Prepares for HF1 output target types (TxOutMultisig, TxOutHTLC).
All call sites updated to type-assert TxOutToKey where needed.

Modified files:
- types/transaction.go: TxOutputBare.Target is now TxOutTarget
- wire/transaction.go: encode/decode use type switch on target
- chain/ring.go: type-assert target to TxOutToKey for key extraction
- wallet/scanner.go: type-assert target before key comparison
- tui/explorer_model.go: type-assert target for display
- wire/transaction_test.go: type-assert in assertions
- wallet/builder_test.go: type-assert in assertions

Co-Authored-By: Charon <charon@lethean.io>
This commit is contained in:
Claude 2026-03-16 20:23:27 +00:00
parent 6eabe2a64d
commit 0408d2f3fa
No known key found for this signature in database
GPG key ID: AF404715446AEB41
7 changed files with 55 additions and 20 deletions

View file

@ -35,7 +35,11 @@ func (c *Chain) GetRingOutputs(amount uint64, offsets []uint64) ([]types.PublicK
switch out := tx.Vout[outNo].(type) {
case types.TxOutputBare:
pubs[i] = out.Target.Key
toKey, ok := out.Target.(types.TxOutToKey)
if !ok {
return nil, fmt.Errorf("ring output %d: unsupported target type %T", i, out.Target)
}
pubs[i] = toKey.Key
default:
return nil, fmt.Errorf("ring output %d: unsupported output type %T", i, out)
}

View file

@ -325,7 +325,11 @@ func (m *ExplorerModel) viewTxDetail() string {
for i, out := range tx.Vout {
switch v := out.(type) {
case types.TxOutputBare:
b.WriteString(fmt.Sprintf(" [%d] bare amount=%d key=%x\n", i, v.Amount, v.Target.Key[:4]))
if toKey, ok := v.Target.(types.TxOutToKey); ok {
b.WriteString(fmt.Sprintf(" [%d] bare amount=%d key=%x\n", i, v.Amount, toKey.Key[:4]))
} else {
b.WriteString(fmt.Sprintf(" [%d] bare amount=%d target=%T\n", i, v.Amount, v.Target))
}
case types.TxOutputZarcanum:
b.WriteString(fmt.Sprintf(" [%d] zarcanum stealth=%x\n", i, v.StealthAddress[:4]))
default:

View file

@ -183,8 +183,9 @@ type TxOutputBare struct {
// Amount in atomic units.
Amount uint64
// Target is the one-time output destination (key + mix attribute).
Target TxOutToKey
// Target is the output destination. Before HF1 this is always TxOutToKey;
// after HF1 it may also be TxOutMultisig or TxOutHTLC.
Target TxOutTarget
}
// OutputType returns the wire variant tag for bare outputs.

View file

@ -126,13 +126,21 @@ func TestV1BuilderBasic(t *testing.T) {
}
// Output keys must be non-zero and unique.
if out0.Target.Key == (types.PublicKey{}) {
toKey0, ok := out0.Target.(types.TxOutToKey)
if !ok {
t.Fatalf("output 0 target type: got %T, want TxOutToKey", out0.Target)
}
toKey1, ok := out1.Target.(types.TxOutToKey)
if !ok {
t.Fatalf("output 1 target type: got %T, want TxOutToKey", out1.Target)
}
if toKey0.Key == (types.PublicKey{}) {
t.Error("output 0 key is zero")
}
if out1.Target.Key == (types.PublicKey{}) {
if toKey1.Key == (types.PublicKey{}) {
t.Error("output 1 key is zero")
}
if out0.Target.Key == out1.Target.Key {
if toKey0.Key == toKey1.Key {
t.Error("output keys are identical; derivation broken")
}

View file

@ -64,7 +64,11 @@ func (s *V1Scanner) ScanTransaction(tx *types.Transaction, txHash types.Hash,
continue
}
if types.PublicKey(expectedPub) != bare.Target.Key {
toKey, ok := bare.Target.(types.TxOutToKey)
if !ok {
continue
}
if types.PublicKey(expectedPub) != toKey.Key {
continue
}

View file

@ -250,9 +250,12 @@ func encodeOutputsV1(enc *Encoder, vout []types.TxOutput) {
case types.TxOutputBare:
enc.WriteVarint(v.Amount)
// Target is a variant (txout_target_v)
enc.WriteVariantTag(types.TargetTypeToKey)
enc.WriteBlob32((*[32]byte)(&v.Target.Key))
enc.WriteUint8(v.Target.MixAttr)
switch tgt := v.Target.(type) {
case types.TxOutToKey:
enc.WriteVariantTag(types.TargetTypeToKey)
enc.WriteBlob32((*[32]byte)(&tgt.Key))
enc.WriteUint8(tgt.MixAttr)
}
}
}
}
@ -272,8 +275,10 @@ func decodeOutputsV1(dec *Decoder) []types.TxOutput {
}
switch tag {
case types.TargetTypeToKey:
dec.ReadBlob32((*[32]byte)(&out.Target.Key))
out.Target.MixAttr = dec.ReadUint8()
var tgt types.TxOutToKey
dec.ReadBlob32((*[32]byte)(&tgt.Key))
tgt.MixAttr = dec.ReadUint8()
out.Target = tgt
default:
dec.err = fmt.Errorf("wire: unsupported target tag 0x%02x", tag)
return vout
@ -291,9 +296,12 @@ func encodeOutputsV2(enc *Encoder, vout []types.TxOutput) {
switch v := out.(type) {
case types.TxOutputBare:
enc.WriteVarint(v.Amount)
enc.WriteVariantTag(types.TargetTypeToKey)
enc.WriteBlob32((*[32]byte)(&v.Target.Key))
enc.WriteUint8(v.Target.MixAttr)
switch tgt := v.Target.(type) {
case types.TxOutToKey:
enc.WriteVariantTag(types.TargetTypeToKey)
enc.WriteBlob32((*[32]byte)(&tgt.Key))
enc.WriteUint8(tgt.MixAttr)
}
case types.TxOutputZarcanum:
enc.WriteBlob32((*[32]byte)(&v.StealthAddress))
enc.WriteBlob32((*[32]byte)(&v.ConcealingPoint))
@ -322,8 +330,10 @@ func decodeOutputsV2(dec *Decoder) []types.TxOutput {
out.Amount = dec.ReadVarint()
targetTag := dec.ReadVariantTag()
if targetTag == types.TargetTypeToKey {
dec.ReadBlob32((*[32]byte)(&out.Target.Key))
out.Target.MixAttr = dec.ReadUint8()
var tgt types.TxOutToKey
dec.ReadBlob32((*[32]byte)(&tgt.Key))
tgt.MixAttr = dec.ReadUint8()
out.Target = tgt
} else {
dec.err = fmt.Errorf("wire: unsupported target tag 0x%02x", targetTag)
return vout

View file

@ -65,8 +65,12 @@ func TestCoinbaseTxEncodeDecode_Good(t *testing.T) {
if bare.Amount != 1000000 {
t.Errorf("amount: got %d, want 1000000", bare.Amount)
}
if bare.Target.Key[0] != 0xDE || bare.Target.Key[1] != 0xAD {
t.Errorf("target key: got %x, want DE AD...", bare.Target.Key[:2])
toKey, ok := bare.Target.(types.TxOutToKey)
if !ok {
t.Fatalf("target type: got %T, want TxOutToKey", bare.Target)
}
if toKey.Key[0] != 0xDE || toKey.Key[1] != 0xAD {
t.Errorf("target key: got %x, want DE AD...", toKey.Key[:2])
}
}