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
This commit is contained in:
191
relay/envelope_test.go
Normal file
191
relay/envelope_test.go
Normal file
@@ -0,0 +1,191 @@
|
||||
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")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user