aboutsummaryrefslogtreecommitdiff
path: root/internal/checkpoint/checkpoint_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/checkpoint/checkpoint_test.go')
-rw-r--r--internal/checkpoint/checkpoint_test.go105
1 files changed, 105 insertions, 0 deletions
diff --git a/internal/checkpoint/checkpoint_test.go b/internal/checkpoint/checkpoint_test.go
new file mode 100644
index 0000000..9ca9b2a
--- /dev/null
+++ b/internal/checkpoint/checkpoint_test.go
@@ -0,0 +1,105 @@
+package checkpoint
+
+import (
+ "crypto/ed25519"
+ "crypto/rand"
+ "encoding/base64"
+ "os"
+ "path/filepath"
+ "testing"
+
+ "github.com/Fuwn/plutia/internal/storage"
+ "github.com/Fuwn/plutia/internal/types"
+)
+
+func TestBuildAndStoreCheckpoint(t *testing.T) {
+ tmp := t.TempDir()
+ store, err := storage.OpenPebble(tmp)
+ if err != nil {
+ t.Fatalf("open pebble: %v", err)
+ }
+ defer store.Close()
+
+ pub, priv, err := ed25519.GenerateKey(rand.Reader)
+ if err != nil {
+ t.Fatalf("generate key: %v", err)
+ }
+ keyPath := filepath.Join(tmp, "mirror.key")
+ if err := os.WriteFile(keyPath, []byte(base64.RawURLEncoding.EncodeToString(priv)), 0o600); err != nil {
+ t.Fatalf("write key: %v", err)
+ }
+
+ mgr := NewManager(store, tmp, keyPath)
+ states := []types.StateV1{
+ {Version: 1, DID: "did:plc:a", ChainTipHash: "tip-a"},
+ {Version: 1, DID: "did:plc:b", ChainTipHash: "tip-b"},
+ }
+ cp, err := mgr.BuildAndStore(42, states, []string{"abc", "def"})
+ if err != nil {
+ t.Fatalf("build checkpoint: %v", err)
+ }
+ if cp.Signature == "" || cp.CheckpointHash == "" {
+ t.Fatalf("expected signed checkpoint")
+ }
+ payload, err := marshalCheckpointPayload(cp)
+ if err != nil {
+ t.Fatalf("marshal payload: %v", err)
+ }
+ rawSig, err := base64.RawURLEncoding.DecodeString(cp.Signature)
+ if err != nil {
+ t.Fatalf("decode signature: %v", err)
+ }
+ if !ed25519.Verify(pub, payload, rawSig) {
+ t.Fatalf("signature verification failed")
+ }
+
+ stored, ok, err := store.GetCheckpoint(cp.Sequence)
+ if err != nil || !ok {
+ t.Fatalf("stored checkpoint missing: ok=%v err=%v", ok, err)
+ }
+ if stored.CheckpointHash != cp.CheckpointHash {
+ t.Fatalf("checkpoint hash mismatch")
+ }
+}
+
+func TestCheckpointRootStability(t *testing.T) {
+ tmp := t.TempDir()
+ store, err := storage.OpenPebble(tmp)
+ if err != nil {
+ t.Fatalf("open pebble: %v", err)
+ }
+ defer store.Close()
+
+ if err := store.PutState(types.StateV1{Version: 1, DID: "did:plc:b", ChainTipHash: "tip-b"}); err != nil {
+ t.Fatalf("put state b: %v", err)
+ }
+ if err := store.PutState(types.StateV1{Version: 1, DID: "did:plc:a", ChainTipHash: "tip-a"}); err != nil {
+ t.Fatalf("put state a: %v", err)
+ }
+
+ _, priv, err := ed25519.GenerateKey(rand.Reader)
+ if err != nil {
+ t.Fatalf("generate key: %v", err)
+ }
+ keyPath := filepath.Join(tmp, "mirror.key")
+ if err := os.WriteFile(keyPath, []byte(base64.RawURLEncoding.EncodeToString(priv)), 0o600); err != nil {
+ t.Fatalf("write key: %v", err)
+ }
+
+ mgr := NewManager(store, tmp, keyPath)
+ cp1, err := mgr.BuildAndStoreFromStore(100, []string{"abc"})
+ if err != nil {
+ t.Fatalf("build checkpoint 1: %v", err)
+ }
+ cp2, err := mgr.BuildAndStoreFromStore(200, []string{"abc"})
+ if err != nil {
+ t.Fatalf("build checkpoint 2: %v", err)
+ }
+
+ if cp1.DIDMerkleRoot != cp2.DIDMerkleRoot {
+ t.Fatalf("did root changed for identical state: %s vs %s", cp1.DIDMerkleRoot, cp2.DIDMerkleRoot)
+ }
+ if cp1.BlockMerkleRoot != cp2.BlockMerkleRoot {
+ t.Fatalf("block root changed for identical block set: %s vs %s", cp1.BlockMerkleRoot, cp2.BlockMerkleRoot)
+ }
+}