Update go.mod module line, all require/replace directives, and every .go import path from forge.lthn.ai/core/go-blockchain to dappco.re/go/core/blockchain. Add replace directives to bridge dappco.re paths to existing forge.lthn.ai registry during migration. Update CLAUDE.md, README, and docs to reflect the new module path. Co-Authored-By: Virgil <virgil@lethean.io>
126 lines
2.9 KiB
Go
126 lines
2.9 KiB
Go
// 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 (
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"dappco.re/go/core/blockchain/rpc"
|
|
"dappco.re/go/core/blockchain/types"
|
|
)
|
|
|
|
func TestRPCRingSelector(t *testing.T) {
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
type entry struct {
|
|
GlobalIndex uint64 `json:"global_index"`
|
|
PublicKey string `json:"public_key"`
|
|
}
|
|
resp := struct {
|
|
Outs []entry `json:"outs"`
|
|
Status string `json:"status"`
|
|
}{Status: "OK"}
|
|
|
|
for i := 0; i < 15; i++ {
|
|
var key types.PublicKey
|
|
key[0] = byte(i + 1)
|
|
resp.Outs = append(resp.Outs, entry{
|
|
GlobalIndex: uint64((i + 1) * 100),
|
|
PublicKey: hex.EncodeToString(key[:]),
|
|
})
|
|
}
|
|
json.NewEncoder(w).Encode(resp)
|
|
}))
|
|
defer srv.Close()
|
|
|
|
client := rpc.NewClient(srv.URL)
|
|
selector := NewRPCRingSelector(client)
|
|
|
|
members, err := selector.SelectRing(1000, 500, 10)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(members) != 10 {
|
|
t.Fatalf("got %d ring members, want 10", len(members))
|
|
}
|
|
|
|
seen := make(map[uint64]bool)
|
|
for _, m := range members {
|
|
if seen[m.GlobalIndex] {
|
|
t.Fatalf("duplicate global index %d", m.GlobalIndex)
|
|
}
|
|
seen[m.GlobalIndex] = true
|
|
}
|
|
|
|
for _, m := range members {
|
|
if m.GlobalIndex == 500 {
|
|
t.Fatal("real global index should be excluded from decoys")
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRPCRingSelectorExcludesReal(t *testing.T) {
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
type entry struct {
|
|
GlobalIndex uint64 `json:"global_index"`
|
|
PublicKey string `json:"public_key"`
|
|
}
|
|
resp := struct {
|
|
Outs []entry `json:"outs"`
|
|
Status string `json:"status"`
|
|
}{Status: "OK"}
|
|
|
|
for i := 0; i < 15; i++ {
|
|
var key types.PublicKey
|
|
key[0] = byte(i + 1)
|
|
gidx := uint64((i + 1) * 100)
|
|
if i == 3 {
|
|
gidx = 42 // this is the real output
|
|
}
|
|
resp.Outs = append(resp.Outs, entry{
|
|
GlobalIndex: gidx,
|
|
PublicKey: hex.EncodeToString(key[:]),
|
|
})
|
|
}
|
|
json.NewEncoder(w).Encode(resp)
|
|
}))
|
|
defer srv.Close()
|
|
|
|
client := rpc.NewClient(srv.URL)
|
|
selector := NewRPCRingSelector(client)
|
|
|
|
members, err := selector.SelectRing(1000, 42, 10)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, m := range members {
|
|
if m.GlobalIndex == 42 {
|
|
t.Fatal("real output should be excluded")
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRPCRingSelectorInsufficientDecoys(t *testing.T) {
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
resp := struct {
|
|
Outs []struct{} `json:"outs"`
|
|
Status string `json:"status"`
|
|
}{Status: "OK"}
|
|
json.NewEncoder(w).Encode(resp)
|
|
}))
|
|
defer srv.Close()
|
|
|
|
client := rpc.NewClient(srv.URL)
|
|
selector := NewRPCRingSelector(client)
|
|
|
|
_, err := selector.SelectRing(1000, 0, 10)
|
|
if err == nil {
|
|
t.Fatal("expected insufficient decoys error")
|
|
}
|
|
}
|