Files
dchain/relay/envelope_test.go
vsecoder 7e7393e4f8 chore: initial commit for v0.0.1
DChain single-node blockchain + React Native messenger client.

Core:
- PBFT consensus with multi-sig validator admission + equivocation slashing
- BadgerDB + schema migration scaffold (CurrentSchemaVersion=0)
- libp2p gossipsub (tx/v1, blocks/v1, relay/v1, version/v1)
- Native Go contracts (username_registry) alongside WASM (wazero)
- WebSocket gateway with topic-based fanout + Ed25519-nonce auth
- Relay mailbox with NaCl envelope encryption (X25519 + Ed25519)
- Prometheus /metrics, per-IP rate limit, body-size cap

Deployment:
- Single-node compose (deploy/single/) with Caddy TLS + optional Prometheus
- 3-node dev compose (docker-compose.yml) with mocked internet topology
- 3-validator prod compose (deploy/prod/) for federation
- Auto-update from Gitea via /api/update-check + systemd timer
- Build-time version injection (ldflags → node --version)
- UI / Swagger toggle flags (DCHAIN_DISABLE_UI, DCHAIN_DISABLE_SWAGGER)

Client (client-app/):
- Expo / React Native / NativeWind
- E2E NaCl encryption, typing indicator, contact requests
- Auto-discovery of canonical contracts, chain_id aware, WS reconnect on node switch

Documentation:
- README.md, CHANGELOG.md, CONTEXT.md
- deploy/single/README.md with 6 operator scenarios
- deploy/UPDATE_STRATEGY.md with 4-layer forward-compat design
- docs/contracts/*.md per contract
2026-04-17 14:16:44 +03:00

192 lines
5.3 KiB
Go

package relay_test
import (
"bytes"
"testing"
"time"
"go-blockchain/identity"
"go-blockchain/relay"
)
func mustGenerateKeyPair(t *testing.T) *relay.KeyPair {
t.Helper()
kp, err := relay.GenerateKeyPair()
if err != nil {
t.Fatalf("GenerateKeyPair: %v", err)
}
return kp
}
func mustGenerateIdentity(t *testing.T) *identity.Identity {
t.Helper()
id, err := identity.Generate()
if err != nil {
t.Fatalf("identity.Generate: %v", err)
}
return id
}
// TestSealOpenRoundTrip seals a message and opens it with the correct recipient key.
func TestSealOpenRoundTrip(t *testing.T) {
sender := mustGenerateKeyPair(t)
recipient := mustGenerateKeyPair(t)
plaintext := []byte("hello relay world")
env, err := relay.Seal(sender, nil, recipient.Pub, plaintext, 0, time.Now().Unix())
if err != nil {
t.Fatalf("Seal: %v", err)
}
got, err := relay.Open(recipient, env)
if err != nil {
t.Fatalf("Open: %v", err)
}
if !bytes.Equal(got, plaintext) {
t.Errorf("plaintext mismatch: got %q, want %q", got, plaintext)
}
}
// TestOpenWrongKey attempts to open an envelope with a different keypair.
func TestOpenWrongKey(t *testing.T) {
sender := mustGenerateKeyPair(t)
recipient := mustGenerateKeyPair(t)
wrong := mustGenerateKeyPair(t)
env, err := relay.Seal(sender, nil, recipient.Pub, []byte("secret"), 0, time.Now().Unix())
if err != nil {
t.Fatalf("Seal: %v", err)
}
_, err = relay.Open(wrong, env)
if err == nil {
t.Fatal("Open with wrong key should return an error")
}
}
// TestIsAddressedTo checks that IsAddressedTo returns true for the correct
// recipient and false for a different keypair.
func TestIsAddressedTo(t *testing.T) {
sender := mustGenerateKeyPair(t)
recipient := mustGenerateKeyPair(t)
other := mustGenerateKeyPair(t)
env, err := relay.Seal(sender, nil, recipient.Pub, []byte("msg"), 0, time.Now().Unix())
if err != nil {
t.Fatalf("Seal: %v", err)
}
if !env.IsAddressedTo(recipient) {
t.Error("IsAddressedTo should return true for the correct recipient")
}
if env.IsAddressedTo(other) {
t.Error("IsAddressedTo should return false for a different keypair")
}
}
// TestSealWithFee seals a message with a positive fee and a real sender identity.
// FeeSig must be non-nil and SenderEd25519PubKey must be populated.
func TestSealWithFee(t *testing.T) {
sender := mustGenerateKeyPair(t)
senderID := mustGenerateIdentity(t)
recipient := mustGenerateKeyPair(t)
env, err := relay.Seal(sender, senderID, recipient.Pub, []byte("paid msg"), 1000, time.Now().Unix())
if err != nil {
t.Fatalf("Seal: %v", err)
}
if len(env.FeeSig) == 0 {
t.Error("FeeSig should be non-nil when feeUT > 0 and senderID is provided")
}
if env.SenderEd25519PubKey == "" {
t.Error("SenderEd25519PubKey should be set when senderID is provided")
}
if env.SenderEd25519PubKey != senderID.PubKeyHex() {
t.Errorf("SenderEd25519PubKey mismatch: got %s, want %s",
env.SenderEd25519PubKey, senderID.PubKeyHex())
}
if env.FeeUT != 1000 {
t.Errorf("FeeUT: got %d, want 1000", env.FeeUT)
}
}
// TestSealNilSenderID seals with nil senderID and feeUT=0.
// FeeSig should be nil and SenderEd25519PubKey should be empty.
func TestSealNilSenderID(t *testing.T) {
sender := mustGenerateKeyPair(t)
recipient := mustGenerateKeyPair(t)
env, err := relay.Seal(sender, nil, recipient.Pub, []byte("free msg"), 0, time.Now().Unix())
if err != nil {
t.Fatalf("Seal: %v", err)
}
if env.FeeSig != nil {
t.Error("FeeSig should be nil for zero-fee envelope with nil senderID")
}
if env.SenderEd25519PubKey != "" {
t.Errorf("SenderEd25519PubKey should be empty, got %s", env.SenderEd25519PubKey)
}
}
// TestEnvelopeIDUnique verifies that two seals of the same message produce
// different IDs because random nonces are used each time.
func TestEnvelopeIDUnique(t *testing.T) {
sender := mustGenerateKeyPair(t)
recipient := mustGenerateKeyPair(t)
msg := []byte("same message")
sentAt := time.Now().Unix()
env1, err := relay.Seal(sender, nil, recipient.Pub, msg, 0, sentAt)
if err != nil {
t.Fatalf("Seal 1: %v", err)
}
env2, err := relay.Seal(sender, nil, recipient.Pub, msg, 0, sentAt)
if err != nil {
t.Fatalf("Seal 2: %v", err)
}
if env1.ID == env2.ID {
t.Error("two seals of the same message should produce different IDs")
}
}
// TestOpenTamperedCiphertext flips a byte in the ciphertext and expects Open to fail.
func TestOpenTamperedCiphertext(t *testing.T) {
sender := mustGenerateKeyPair(t)
recipient := mustGenerateKeyPair(t)
env, err := relay.Seal(sender, nil, recipient.Pub, []byte("tamper me"), 0, time.Now().Unix())
if err != nil {
t.Fatalf("Seal: %v", err)
}
// Flip the first byte of the ciphertext.
env.Ciphertext[0] ^= 0xFF
_, err = relay.Open(recipient, env)
if err == nil {
t.Fatal("Open with tampered ciphertext should return an error")
}
}
// TestOpenTamperedNonce flips a byte in the nonce and expects Open to fail.
func TestOpenTamperedNonce(t *testing.T) {
sender := mustGenerateKeyPair(t)
recipient := mustGenerateKeyPair(t)
env, err := relay.Seal(sender, nil, recipient.Pub, []byte("tamper nonce"), 0, time.Now().Unix())
if err != nil {
t.Fatalf("Seal: %v", err)
}
// Flip the first byte of the nonce.
env.Nonce[0] ^= 0xFF
_, err = relay.Open(recipient, env)
if err == nil {
t.Fatal("Open with tampered nonce should return an error")
}
}