diff --git a/chain/ring.go b/chain/ring.go index 975e1a0..0278863 100644 --- a/chain/ring.go +++ b/chain/ring.go @@ -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) } diff --git a/tui/explorer_model.go b/tui/explorer_model.go index 0316d20..4120188 100644 --- a/tui/explorer_model.go +++ b/tui/explorer_model.go @@ -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: diff --git a/types/transaction.go b/types/transaction.go index d8308ac..71b74f0 100644 --- a/types/transaction.go +++ b/types/transaction.go @@ -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. diff --git a/wallet/builder_test.go b/wallet/builder_test.go index e7ac9ff..9eadc8f 100644 --- a/wallet/builder_test.go +++ b/wallet/builder_test.go @@ -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") } diff --git a/wallet/scanner.go b/wallet/scanner.go index da272f8..ff0a48c 100644 --- a/wallet/scanner.go +++ b/wallet/scanner.go @@ -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 } diff --git a/wire/transaction.go b/wire/transaction.go index 759e9e5..36bf0fd 100644 --- a/wire/transaction.go +++ b/wire/transaction.go @@ -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 diff --git a/wire/transaction_test.go b/wire/transaction_test.go index 853631a..562bb35 100644 --- a/wire/transaction_test.go +++ b/wire/transaction_test.go @@ -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]) } }