mirror of
https://github.com/luxfi/bridge.git
synced 2026-09-24 00:26:46 +00:00
255 lines
7.1 KiB
Go
255 lines
7.1 KiB
Go
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package plugins
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
"math/big"
|
|
"testing"
|
|
)
|
|
|
|
func TestNewPolkadotPlugin(t *testing.T) {
|
|
p := NewPolkadotPlugin("http://dot:9933", "5GrwvaEF...")
|
|
if p.Name() != "polkadot" {
|
|
t.Errorf("Name() = %q, want polkadot", p.Name())
|
|
}
|
|
if p.ChainID() != PolkadotChainID {
|
|
t.Errorf("ChainID() = %d, want %d", p.ChainID(), PolkadotChainID)
|
|
}
|
|
}
|
|
|
|
func TestPolkadotChainIDValue(t *testing.T) {
|
|
// "DOT\x00" = 0x444F5400
|
|
const want uint64 = 0x444F5400
|
|
if PolkadotChainID != want {
|
|
t.Errorf("PolkadotChainID = 0x%X, want 0x%X", PolkadotChainID, want)
|
|
}
|
|
}
|
|
|
|
func TestScaleCompactDecode(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
data []byte
|
|
wantVal uint64
|
|
wantLen int
|
|
}{
|
|
{"single_byte_0", []byte{0x00}, 0, 1},
|
|
{"single_byte_1", []byte{0x04}, 1, 1},
|
|
{"single_byte_42", []byte{0xA8}, 42, 1},
|
|
{"single_byte_63", []byte{0xFC}, 63, 1},
|
|
{"two_byte_64", []byte{0x01, 0x01}, 64, 2},
|
|
{"two_byte_255", []byte{0xFD, 0x03}, 255, 2},
|
|
{"four_byte_16384", []byte{0x02, 0x00, 0x01, 0x00}, 16384, 4},
|
|
{"big_int_mode", []byte{0x03, 0x01, 0x00, 0x00, 0x00, 0x00}, 1, 1 + 4},
|
|
{"empty", nil, 0, 0},
|
|
{"too_short_two_byte", []byte{0x01}, 0, 0},
|
|
{"too_short_four_byte", []byte{0x02, 0x00}, 0, 0},
|
|
}
|
|
for _, tt := range tests {
|
|
val, n := scaleCompactDecode(tt.data)
|
|
if val != tt.wantVal {
|
|
t.Errorf("%s: value = %d, want %d", tt.name, val, tt.wantVal)
|
|
}
|
|
if n != tt.wantLen {
|
|
t.Errorf("%s: consumed = %d, want %d", tt.name, n, tt.wantLen)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestScaleCompactDecodeUint128(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
data []byte
|
|
wantVal *big.Int
|
|
wantLen int
|
|
}{
|
|
{"zero", []byte{0x00}, big.NewInt(0), 1},
|
|
{"one", []byte{0x04}, big.NewInt(1), 1},
|
|
{"two_byte_100", []byte{0x91, 0x01}, big.NewInt(100), 2},
|
|
{"four_byte", []byte{0x02, 0x00, 0x01, 0x00}, new(big.Int).SetUint64(16384), 4},
|
|
{
|
|
"big_int_16_bytes",
|
|
// mode 0x03, byteLen = (0x33>>2)+4 = 12+4 = 16, but let's use 4 bytes
|
|
// (0x03>>2)+4 = 4, data = [0x40, 0x42, 0x0F, 0x00] = 1_000_000 LE
|
|
append([]byte{0x03}, func() []byte {
|
|
b := make([]byte, 4)
|
|
binary.LittleEndian.PutUint32(b, 1_000_000)
|
|
return b
|
|
}()...),
|
|
new(big.Int).SetUint64(1_000_000),
|
|
5,
|
|
},
|
|
{"empty", nil, big.NewInt(0), 0},
|
|
}
|
|
for _, tt := range tests {
|
|
val, n := scaleCompactDecodeUint128(tt.data)
|
|
if val.Cmp(tt.wantVal) != 0 {
|
|
t.Errorf("%s: value = %s, want %s", tt.name, val, tt.wantVal)
|
|
}
|
|
if n != tt.wantLen {
|
|
t.Errorf("%s: consumed = %d, want %d", tt.name, n, tt.wantLen)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestScaleDecodeUint(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
data []byte
|
|
want *big.Int
|
|
}{
|
|
{"zero", []byte{0x00}, big.NewInt(0)},
|
|
{"one_LE", []byte{0x01, 0x00, 0x00, 0x00}, big.NewInt(1)},
|
|
{"256_LE", []byte{0x00, 0x01, 0x00, 0x00}, big.NewInt(256)},
|
|
{"1e9_LE", func() []byte {
|
|
b := make([]byte, 8)
|
|
binary.LittleEndian.PutUint64(b, 1_000_000_000)
|
|
return b
|
|
}(), new(big.Int).SetUint64(1_000_000_000)},
|
|
{"empty", nil, big.NewInt(0)},
|
|
}
|
|
for _, tt := range tests {
|
|
got := scaleDecodeUint(tt.data)
|
|
if got.Cmp(tt.want) != 0 {
|
|
t.Errorf("%s: scaleDecodeUint = %s, want %s", tt.name, got, tt.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestReverseBytes(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
in []byte
|
|
want []byte
|
|
}{
|
|
{"empty", nil, nil},
|
|
{"single", []byte{0xAA}, []byte{0xAA}},
|
|
{"pair", []byte{0x01, 0x02}, []byte{0x02, 0x01}},
|
|
{"triple", []byte{0x01, 0x02, 0x03}, []byte{0x03, 0x02, 0x01}},
|
|
{"four", []byte{0xDE, 0xAD, 0xBE, 0xEF}, []byte{0xEF, 0xBE, 0xAD, 0xDE}},
|
|
}
|
|
for _, tt := range tests {
|
|
b := make([]byte, len(tt.in))
|
|
copy(b, tt.in)
|
|
reverseBytes(b)
|
|
if len(b) != len(tt.want) {
|
|
t.Errorf("%s: len = %d, want %d", tt.name, len(b), len(tt.want))
|
|
continue
|
|
}
|
|
for i := range b {
|
|
if b[i] != tt.want[i] {
|
|
t.Errorf("%s: byte %d = 0x%02x, want 0x%02x", tt.name, i, b[i], tt.want[i])
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Test parseSubstrateDepositExtrinsic with a hand-crafted unsigned extrinsic.
|
|
func TestParseSubstrateDepositExtrinsic_Unsigned(t *testing.T) {
|
|
// Build an unsigned extrinsic:
|
|
// compact_length + version_byte(0x04, unsigned) + pallet(any) + call(0x00)
|
|
// + recipient(32) + amount(compact) + nonce(u64 LE)
|
|
var recip [20]byte
|
|
for i := range recip {
|
|
recip[i] = byte(0xA0 + i)
|
|
}
|
|
|
|
// Call data: pallet_index=0x05, call_index=0x00
|
|
// recipient: 12 zero bytes + 20 address bytes = 32 bytes
|
|
// amount: compact(1_000_000_000) = 0x02 0x00 0xCA 0x9A (4-byte mode, value >> 2)
|
|
// nonce: 42 as u64 LE
|
|
callData := []byte{0x05, 0x00} // pallet 5, call 0
|
|
|
|
recipField := make([]byte, 32)
|
|
copy(recipField[12:], recip[:])
|
|
callData = append(callData, recipField...)
|
|
|
|
// Compact encode 1_000_000_000: (1_000_000_000 << 2) | 0x02 = 4_000_000_002
|
|
// = 0xEE6B2802 LE => 0x02, 0x28, 0x6B, 0xEE
|
|
callData = append(callData, 0x02, 0x28, 0x6B, 0xEE)
|
|
|
|
nonce := make([]byte, 8)
|
|
binary.LittleEndian.PutUint64(nonce, 42)
|
|
callData = append(callData, nonce...)
|
|
|
|
// Version byte: 0x04 (version 4, unsigned)
|
|
body := append([]byte{0x04}, callData...)
|
|
|
|
// SCALE compact length prefix for the body.
|
|
bodyLen := len(body)
|
|
var lengthPrefix []byte
|
|
if bodyLen < 64 {
|
|
lengthPrefix = []byte{byte(bodyLen << 2)}
|
|
} else {
|
|
// Two-byte mode
|
|
val := uint16(bodyLen<<2) | 0x01
|
|
lengthPrefix = []byte{byte(val), byte(val >> 8)}
|
|
}
|
|
|
|
ext := append(lengthPrefix, body...)
|
|
|
|
// Encode as hex string.
|
|
hexStr := "0x"
|
|
for _, b := range ext {
|
|
hexStr += fmt.Sprintf("%02x", b)
|
|
}
|
|
|
|
ev, ok := parseSubstrateDepositExtrinsic(hexStr, 100)
|
|
if !ok {
|
|
t.Fatal("parseSubstrateDepositExtrinsic returned false for valid unsigned extrinsic")
|
|
}
|
|
if ev.SrcChainID != PolkadotChainID {
|
|
t.Errorf("SrcChainID = %d, want %d", ev.SrcChainID, PolkadotChainID)
|
|
}
|
|
if ev.Nonce != 42 {
|
|
t.Errorf("Nonce = %d, want 42", ev.Nonce)
|
|
}
|
|
if ev.Recipient != recip {
|
|
t.Errorf("Recipient = %x, want %x", ev.Recipient, recip)
|
|
}
|
|
wantAmount := big.NewInt(1_000_000_000)
|
|
if ev.Amount.Cmp(wantAmount) != 0 {
|
|
t.Errorf("Amount = %s, want %s", ev.Amount, wantAmount)
|
|
}
|
|
if ev.BlockHeight != 100 {
|
|
t.Errorf("BlockHeight = %d, want 100", ev.BlockHeight)
|
|
}
|
|
}
|
|
|
|
func TestParseSubstrateDepositExtrinsic_WrongCallIndex(t *testing.T) {
|
|
// Build an unsigned extrinsic with call_index=0x01 (not deposit).
|
|
callData := make([]byte, 2+32+4+8) // pallet + call + recip + amount + nonce
|
|
callData[0] = 0x05 // pallet
|
|
callData[1] = 0x01 // wrong call index
|
|
|
|
body := append([]byte{0x04}, callData...)
|
|
lengthPrefix := []byte{byte(len(body) << 2)}
|
|
ext := append(lengthPrefix, body...)
|
|
|
|
hexStr := "0x"
|
|
for _, b := range ext {
|
|
hexStr += fmt.Sprintf("%02x", b)
|
|
}
|
|
|
|
_, ok := parseSubstrateDepositExtrinsic(hexStr, 100)
|
|
if ok {
|
|
t.Error("expected false for non-deposit call index")
|
|
}
|
|
}
|
|
|
|
func TestParseSubstrateDepositExtrinsic_TooShort(t *testing.T) {
|
|
_, ok := parseSubstrateDepositExtrinsic("0x0401", 100)
|
|
if ok {
|
|
t.Error("expected false for short extrinsic")
|
|
}
|
|
}
|
|
|
|
func TestParseSubstrateDepositExtrinsic_BadHex(t *testing.T) {
|
|
_, ok := parseSubstrateDepositExtrinsic("not-hex", 100)
|
|
if ok {
|
|
t.Error("expected false for bad hex")
|
|
}
|
|
}
|