2026-02-20 23:02:13 +00:00
|
|
|
// Copyright (c) 2017-2026 Lethean (https://lt.hn)
|
|
|
|
|
//
|
|
|
|
|
// Licensed under the European Union Public Licence (EUPL) version 1.2.
|
|
|
|
|
// SPDX-License-Identifier: EUPL-1.2
|
|
|
|
|
|
|
|
|
|
package wallet
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"testing"
|
|
|
|
|
|
2026-03-22 01:49:26 +00:00
|
|
|
"dappco.re/go/core/blockchain/types"
|
|
|
|
|
"dappco.re/go/core/blockchain/wire"
|
2026-02-20 23:02:13 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func TestParseTxExtraPublicKey(t *testing.T) {
|
|
|
|
|
var key types.PublicKey
|
|
|
|
|
for i := range key {
|
|
|
|
|
key[i] = byte(i + 1)
|
|
|
|
|
}
|
|
|
|
|
raw := buildTestExtra(key, 0, 0)
|
|
|
|
|
extra, err := ParseTxExtra(raw)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if extra.TxPublicKey != key {
|
|
|
|
|
t.Fatalf("tx public key mismatch: got %x", extra.TxPublicKey)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestParseTxExtraUnlockTime(t *testing.T) {
|
|
|
|
|
var key types.PublicKey
|
|
|
|
|
key[0] = 0xAA
|
|
|
|
|
raw := buildTestExtra(key, 500, 0)
|
|
|
|
|
extra, err := ParseTxExtra(raw)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if extra.UnlockTime != 500 {
|
|
|
|
|
t.Fatalf("unlock_time = %d, want 500", extra.UnlockTime)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestParseTxExtraDerivationHint(t *testing.T) {
|
|
|
|
|
var key types.PublicKey
|
|
|
|
|
key[0] = 0xBB
|
|
|
|
|
raw := buildTestExtra(key, 0, 0x1234)
|
|
|
|
|
extra, err := ParseTxExtra(raw)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if extra.DerivationHint != 0x1234 {
|
|
|
|
|
t.Fatalf("derivation_hint = %04x, want 1234", extra.DerivationHint)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestParseTxExtraAllFields(t *testing.T) {
|
|
|
|
|
var key types.PublicKey
|
|
|
|
|
for i := range key {
|
|
|
|
|
key[i] = byte(0xFF - i)
|
|
|
|
|
}
|
|
|
|
|
raw := buildTestExtra(key, 1000, 0xABCD)
|
|
|
|
|
extra, err := ParseTxExtra(raw)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if extra.TxPublicKey != key {
|
|
|
|
|
t.Fatalf("tx public key mismatch: got %x", extra.TxPublicKey)
|
|
|
|
|
}
|
|
|
|
|
if extra.UnlockTime != 1000 {
|
|
|
|
|
t.Fatalf("unlock_time = %d, want 1000", extra.UnlockTime)
|
|
|
|
|
}
|
|
|
|
|
if extra.DerivationHint != 0xABCD {
|
|
|
|
|
t.Fatalf("derivation_hint = %04x, want ABCD", extra.DerivationHint)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestParseTxExtraEmpty(t *testing.T) {
|
|
|
|
|
raw := wire.EncodeVarint(0)
|
|
|
|
|
extra, err := ParseTxExtra(raw)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if !extra.TxPublicKey.IsZero() {
|
|
|
|
|
t.Fatal("expected zero tx public key for empty extra")
|
|
|
|
|
}
|
|
|
|
|
if extra.UnlockTime != 0 {
|
|
|
|
|
t.Fatalf("unlock_time = %d, want 0", extra.UnlockTime)
|
|
|
|
|
}
|
|
|
|
|
if extra.DerivationHint != 0 {
|
|
|
|
|
t.Fatalf("derivation_hint = %d, want 0", extra.DerivationHint)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestParseTxExtraNilInput(t *testing.T) {
|
|
|
|
|
extra, err := ParseTxExtra(nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if !extra.TxPublicKey.IsZero() {
|
|
|
|
|
t.Fatal("expected zero tx public key for nil input")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestParseTxExtraPreservesRaw(t *testing.T) {
|
|
|
|
|
var key types.PublicKey
|
|
|
|
|
key[0] = 0xCC
|
|
|
|
|
raw := buildTestExtra(key, 100, 0x5678)
|
|
|
|
|
extra, err := ParseTxExtra(raw)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if len(extra.Raw) != len(raw) {
|
|
|
|
|
t.Fatalf("raw length = %d, want %d", len(extra.Raw), len(raw))
|
|
|
|
|
}
|
|
|
|
|
// Verify it is a copy, not a reference to the same backing array.
|
|
|
|
|
raw[0] = 0xFF
|
|
|
|
|
if extra.Raw[0] == 0xFF {
|
|
|
|
|
t.Fatal("Raw should be a defensive copy, not a reference")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestParseTxExtraTruncatedKey(t *testing.T) {
|
|
|
|
|
// Build a raw extra with a public key tag but only 16 bytes instead of 32.
|
|
|
|
|
raw := wire.EncodeVarint(1)
|
|
|
|
|
raw = append(raw, extraTagPublicKey)
|
|
|
|
|
raw = append(raw, make([]byte, 16)...) // only 16 bytes
|
|
|
|
|
_, err := ParseTxExtra(raw)
|
|
|
|
|
if err == nil {
|
|
|
|
|
t.Fatal("expected error for truncated public key")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestBuildTxExtraRoundTrip(t *testing.T) {
|
|
|
|
|
var key types.PublicKey
|
|
|
|
|
for i := range key {
|
|
|
|
|
key[i] = byte(i + 10)
|
|
|
|
|
}
|
|
|
|
|
raw := BuildTxExtra(key)
|
|
|
|
|
extra, err := ParseTxExtra(raw)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if extra.TxPublicKey != key {
|
|
|
|
|
t.Fatal("round-trip key mismatch")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestBuildTxExtraLength(t *testing.T) {
|
|
|
|
|
var key types.PublicKey
|
|
|
|
|
key[0] = 0x42
|
|
|
|
|
raw := BuildTxExtra(key)
|
|
|
|
|
// Expected: varint(1) + tag(1) + key(32) = 34 bytes.
|
|
|
|
|
if len(raw) != 34 {
|
|
|
|
|
t.Fatalf("BuildTxExtra length = %d, want 34", len(raw))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// buildTestExtra constructs a raw extra with the given fields.
|
|
|
|
|
func buildTestExtra(txPubKey types.PublicKey, unlockTime uint64, hint uint16) []byte {
|
|
|
|
|
var count uint64
|
|
|
|
|
var elements []byte
|
|
|
|
|
|
|
|
|
|
if !txPubKey.IsZero() {
|
|
|
|
|
count++
|
|
|
|
|
elements = append(elements, extraTagPublicKey)
|
|
|
|
|
elements = append(elements, txPubKey[:]...)
|
|
|
|
|
}
|
|
|
|
|
if unlockTime > 0 {
|
|
|
|
|
count++
|
|
|
|
|
elements = append(elements, extraTagUnlockTime)
|
|
|
|
|
elements = append(elements, wire.EncodeVarint(unlockTime)...)
|
|
|
|
|
}
|
|
|
|
|
if hint > 0 {
|
|
|
|
|
count++
|
|
|
|
|
elements = append(elements, extraTagDerivationHint)
|
|
|
|
|
elements = append(elements, wire.EncodeVarint(2)...)
|
|
|
|
|
elements = append(elements, byte(hint&0xFF), byte(hint>>8))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
raw := wire.EncodeVarint(count)
|
|
|
|
|
raw = append(raw, elements...)
|
|
|
|
|
return raw
|
|
|
|
|
}
|