A very experimental PLC implementation which uses BFT consensus for decentralization

Bring in interop test fixtures from reference implementation

gbl08ma.com a30cbf5e 02523b03

verified
+1360
+1
plc/interop_tests/README.md
··· 1 + NOTE: these test cases are copied from https://github.com/did-method-plc/go-didplc/tree/main/testdata, which is their canonical home for now.
+59
plc/interop_tests/audit_log/invalid/log_invalid_nullification_reused_key.json
··· 1 + [ 2 + { 3 + "did": "did:plc:srb3vhjq32vxonzcdl3t63vi", 4 + "operation": { 5 + "type": "plc_operation", 6 + "rotationKeys": [ 7 + "did:key:zQ3shkfZnxgzNW3Kby5yA7R1JdpCE3gHiaWmKMD5XdwLU6zMY", 8 + "did:key:zQ3shracm7zHFnzbtAuapGAUPknDHJnKxj3Ts6riTA3FsG9Xd" 9 + ], 10 + "verificationMethods": {}, 11 + "alsoKnownAs": [ 12 + "at://foo" 13 + ], 14 + "services": {}, 15 + "prev": null, 16 + "sig": "N6a_JR7MKurT7U9gYxy6Eg9c_0Fnk12_pNSb0XHj8pdOhJP9sBX0U0E3oBLByQpZQ4PrbIcUlitRdTGlyAdrUw" 17 + }, 18 + "cid": "bafyreieuio5j2mg6vn3toiq2647w5kefg72iiwgvbxqdjingzu62yezisy", 19 + "nullified": false, 20 + "createdAt": "1970-01-01T00:00:00.000Z" 21 + }, 22 + { 23 + "did": "did:plc:srb3vhjq32vxonzcdl3t63vi", 24 + "operation": { 25 + "prev": "bafyreieuio5j2mg6vn3toiq2647w5kefg72iiwgvbxqdjingzu62yezisy", 26 + "type": "plc_operation", 27 + "services": {}, 28 + "alsoKnownAs": [ 29 + "at://foo" 30 + ], 31 + "rotationKeys": [], 32 + "verificationMethods": {}, 33 + "sig": "vUxZqHrveFdsuOHra9oGZo7Q-skqCyzKjf66C_nk3Xo8526iSfVTsVV1UhbFVfCYYQXxCf1x0yAjvcQChLoU8Q" 34 + }, 35 + "cid": "bafyreicxkw5765kbyzfeelqthv6frykkzoykxacqlwo743xf5p4yhtzq64", 36 + "nullified": true, 37 + "createdAt": "1970-01-01T00:00:01.000Z" 38 + }, 39 + { 40 + "did": "did:plc:srb3vhjq32vxonzcdl3t63vi", 41 + "operation": { 42 + "prev": "bafyreieuio5j2mg6vn3toiq2647w5kefg72iiwgvbxqdjingzu62yezisy", 43 + "type": "plc_operation", 44 + "services": {}, 45 + "alsoKnownAs": [ 46 + "at://foo" 47 + ], 48 + "rotationKeys": [ 49 + "did:key:zQ3shkfZnxgzNW3Kby5yA7R1JdpCE3gHiaWmKMD5XdwLU6zMY", 50 + "did:key:zQ3shracm7zHFnzbtAuapGAUPknDHJnKxj3Ts6riTA3FsG9Xd" 51 + ], 52 + "verificationMethods": {}, 53 + "sig": "rxygOAYyKNZ_oSIiM8B24jMmXhBeS9XZSyyqQCrE8GMjVr3wFSscaf63NlbI4VNeiO0CkeOfowinL_DsEr8J7w" 54 + }, 55 + "cid": "bafyreieawqh27uftebe2n56s5qjonuqdrih6lii2xoo65czmpwigvwm32q", 56 + "nullified": false, 57 + "createdAt": "1970-01-01T00:00:02.000Z" 58 + } 59 + ]
+59
plc/interop_tests/audit_log/invalid/log_invalid_nullification_too_slow.json
··· 1 + [ 2 + { 3 + "did": "did:plc:oposgzvemyuhu3pcwow4ysjw", 4 + "operation": { 5 + "type": "plc_operation", 6 + "rotationKeys": [ 7 + "did:key:zQ3shQ4pJUy63hdGwyxjMbpVHjpsGtZQxEDVCvHWqbUAwqfjB", 8 + "did:key:zQ3shRURF844SXVaMHarhBZmdfRxgEDw2KaGuRZYMREUzRp9y" 9 + ], 10 + "verificationMethods": {}, 11 + "alsoKnownAs": [ 12 + "at://foo" 13 + ], 14 + "services": {}, 15 + "prev": null, 16 + "sig": "rLbkRO-IDfN_HVaDaf-_uirfV0SntYU6dKoZ5sJXzzoZOeLenFqx2xkXaIz0pFMUXjYxm-UutuTz21HCtILwNg" 17 + }, 18 + "cid": "bafyreidt3urwnjdgfb5g3yvtvxgesnuydskk5gowux3o4ehtolgxpkkwwq", 19 + "nullified": false, 20 + "createdAt": "1970-01-01T00:00:00.000Z" 21 + }, 22 + { 23 + "did": "did:plc:oposgzvemyuhu3pcwow4ysjw", 24 + "operation": { 25 + "prev": "bafyreidt3urwnjdgfb5g3yvtvxgesnuydskk5gowux3o4ehtolgxpkkwwq", 26 + "type": "plc_operation", 27 + "services": {}, 28 + "alsoKnownAs": [ 29 + "at://foo" 30 + ], 31 + "rotationKeys": [], 32 + "verificationMethods": {}, 33 + "sig": "fa5xNxqH2sPqcUKmtDVRWs4G6EjojgElwfrOTaHBKpBd5rd1wqFl_-ufa6vOHyZn8dMxICsWnFS9ia2wBRQuug" 34 + }, 35 + "cid": "bafyreidb42dmd56qmvywbm2dxoig2a7qatcqpihhoz4epyxfcgnhyauvca", 36 + "nullified": true, 37 + "createdAt": "1970-01-01T00:00:01.000Z" 38 + }, 39 + { 40 + "did": "did:plc:oposgzvemyuhu3pcwow4ysjw", 41 + "operation": { 42 + "prev": "bafyreidt3urwnjdgfb5g3yvtvxgesnuydskk5gowux3o4ehtolgxpkkwwq", 43 + "type": "plc_operation", 44 + "services": {}, 45 + "alsoKnownAs": [ 46 + "at://foo" 47 + ], 48 + "rotationKeys": [ 49 + "did:key:zQ3shQ4pJUy63hdGwyxjMbpVHjpsGtZQxEDVCvHWqbUAwqfjB", 50 + "did:key:zQ3shRURF844SXVaMHarhBZmdfRxgEDw2KaGuRZYMREUzRp9y" 51 + ], 52 + "verificationMethods": {}, 53 + "sig": "crBZ1tt7ZvhgIo1adilehQvQHe45g9wzQrDhtXiLelA0MY-pxoWthTQtlarmq_IYcZMCwYsbtfFVbqxhmfDr5A" 54 + }, 55 + "cid": "bafyreigw3xnh4jjxlpmsfckdwcfqgjhoydrl6odup3ge7c64hrvmz3fqr4", 56 + "nullified": false, 57 + "createdAt": "1970-01-04T00:00:02.000Z" 58 + } 59 + ]
+21
plc/interop_tests/audit_log/invalid/log_invalid_sig_b64_newline.json
··· 1 + [ 2 + { 3 + "did": "did:plc:vyuosvns3imj4hqisd77qfop", 4 + "operation": { 5 + "type": "plc_operation", 6 + "rotationKeys": [ 7 + "did:key:zDnaefqvvRQsME5c7LvxdghHeARNWEFbg2PfrAYiHhKVf9rH9" 8 + ], 9 + "verificationMethods": {}, 10 + "alsoKnownAs": [ 11 + "at://weird_sig_padding" 12 + ], 13 + "services": {}, 14 + "prev": null, 15 + "sig": "j0i5fYQ3fJfjXzIAcb1RoiA-F9xtzU838cU890NEC9xYmHFO4nbQMrZ6ihRu7kZPGf22q4590-3N5Q9IS-BdiA\n" 16 + }, 17 + "cid": "bafyreifofduvlmw2dcpb4ceq774blt4bcnxatrndv6erta34xcuv2v3kxy", 18 + "nullified": false, 19 + "createdAt": "1970-01-01T00:00:00.000Z" 20 + } 21 + ]
+21
plc/interop_tests/audit_log/invalid/log_invalid_sig_b64_padding_bits.json
··· 1 + [ 2 + { 3 + "did": "did:plc:3hhmazwcb2hbcjn3thxamwrk", 4 + "operation": { 5 + "type": "plc_operation", 6 + "rotationKeys": [ 7 + "did:key:zDnaefqvvRQsME5c7LvxdghHeARNWEFbg2PfrAYiHhKVf9rH9" 8 + ], 9 + "verificationMethods": {}, 10 + "alsoKnownAs": [ 11 + "at://weird_sig_padding" 12 + ], 13 + "services": {}, 14 + "prev": null, 15 + "sig": "yRcZOnrdCdTMIWVbM-HX8ByF0Rk1SOkivl3A3YbmzT4saxyk_nERJSJ-CqKlNP4CmMdVL5Cvc1vSM_Fe5HBbmR" 16 + }, 17 + "cid": "bafyreigzz3agnqqoryislo4z5ydfukwzcax3nslasimanzm7qhomnasrie", 18 + "nullified": false, 19 + "createdAt": "1970-01-01T00:00:00.000Z" 20 + } 21 + ]
+21
plc/interop_tests/audit_log/invalid/log_invalid_sig_b64_padding_chars.json
··· 1 + [ 2 + { 3 + "did": "did:plc:vllxembwgplesifkkab6dopf", 4 + "operation": { 5 + "type": "plc_operation", 6 + "rotationKeys": [ 7 + "did:key:zDnaefqvvRQsME5c7LvxdghHeARNWEFbg2PfrAYiHhKVf9rH9" 8 + ], 9 + "verificationMethods": {}, 10 + "alsoKnownAs": [ 11 + "at://weird_sig_padding" 12 + ], 13 + "services": {}, 14 + "prev": null, 15 + "sig": "QHEeFBpqab-LHQzXL59DpQohJxaZt0tjwRqRGegX6vYfo3g9KlZZskecnZZietchQIBsN8DwBLgfkCGSxS7YKA==" 16 + }, 17 + "cid": "bafyreifk25zdanrt2zesbksqapq3tzpxbxukaihb5sthlaljably6hfgoe", 18 + "nullified": false, 19 + "createdAt": "1970-01-01T00:00:00.000Z" 20 + } 21 + ]
+28
plc/interop_tests/audit_log/invalid/log_invalid_sig_der.json
··· 1 + [ 2 + { 3 + "did": "did:plc:ru3i4kqbprtzmfydm3vmn57w", 4 + "operation": { 5 + "sig": "MD4CHRiHVHmnqoitzAIq5NZXEkmBbtbfayatOLATwgsOAh1OFK0fSdkgNJGXpMkXMumSu0smNfVZPzElCm1jkA", 6 + "prev": null, 7 + "type": "plc_operation", 8 + "services": { 9 + "atproto_pds": { 10 + "type": "AtprotoPersonalDataServer", 11 + "endpoint": "https://mrSTdKI0ilD_bR23p5wCOfKGUSsbOSnI.example.com" 12 + } 13 + }, 14 + "alsoKnownAs": [ 15 + "at://merry.xmas.from.retr0.id" 16 + ], 17 + "rotationKeys": [ 18 + "did:key:zQ3shaTh3deVkniqT1r413817rJ3JDvTG61bzGX13dSwCkvGC" 19 + ], 20 + "verificationMethods": { 21 + "atproto": "did:key:zQ3shaTh3deVkniqT1r413817rJ3JDvTG61bzGX13dSwCkvGC" 22 + } 23 + }, 24 + "cid": "bafyreieng2hcual4m6lboa3g5ldpp5xuh4jgvsvoi6zvtwwkmgk5ta2lea", 25 + "nullified": false, 26 + "createdAt": "2024-12-25T11:43:00.507Z" 27 + } 28 + ]
+21
plc/interop_tests/audit_log/invalid/log_invalid_sig_k256_high_s.json
··· 1 + [ 2 + { 3 + "did": "did:plc:t63fj6hb7mahe7qcvgbqhdnk", 4 + "operation": { 5 + "type": "plc_operation", 6 + "rotationKeys": [ 7 + "did:key:zQ3shTBEaSruX6DJNAH3W9Weu95PYwrdQvmrHCSW9bLLZXrnw" 8 + ], 9 + "verificationMethods": {}, 10 + "alsoKnownAs": [ 11 + "at://high_s_k256" 12 + ], 13 + "services": {}, 14 + "prev": null, 15 + "sig": "cDdgXcFwiJFhTzUyEhmvG4uCqy_UEqTXiWN29O3CiMXtfPnNo7Mg_BsK1iv4VBt010RwJg5mnDOK20T6m8ltDw" 16 + }, 17 + "cid": "bafyreie7wzkpryp3abzh4avjqmby3kuznuz3dwlqqgyfui25pecveba5oe", 18 + "nullified": false, 19 + "createdAt": "1970-01-01T00:00:00.000Z" 20 + } 21 + ]
+21
plc/interop_tests/audit_log/invalid/log_invalid_sig_p256_high_s.json
··· 1 + [ 2 + { 3 + "did": "did:plc:svbmkmqgxdr4i5cnt5y5lq5h", 4 + "operation": { 5 + "type": "plc_operation", 6 + "rotationKeys": [ 7 + "did:key:zDnaefqvvRQsME5c7LvxdghHeARNWEFbg2PfrAYiHhKVf9rH9" 8 + ], 9 + "verificationMethods": {}, 10 + "alsoKnownAs": [ 11 + "at://high_s_p256" 12 + ], 13 + "services": {}, 14 + "prev": null, 15 + "sig": "v5pzK205KUu88-3lrYRoLmw_3TJL0xK3uCNUJXged7zEEjAQcndRh4rlglwVeMQa_UH-3XArH1Oh5T_3QGLzbg" 16 + }, 17 + "cid": "bafyreievilctebvy4pchitm7ohk4hj2tfuzcouax6gnx7obc5mp5unwvea", 18 + "nullified": false, 19 + "createdAt": "1970-01-01T00:00:00.000Z" 20 + } 21 + ]
+102
plc/interop_tests/audit_log/invalid/log_invalid_update_nullified.json
··· 1 + [ 2 + { 3 + "did": "did:plc:4v3qers3fn55uitqeuwokfwy", 4 + "operation": { 5 + "sig": "_h3yBs4fXKilIYIAUJpB5AoE4E_aXmGYHzm7Na5B53EmoOpDnFd6H9dg9oFvKKYwFPkQkr8HMQT32HT7vgIYuw", 6 + "prev": null, 7 + "type": "plc_operation", 8 + "services": {}, 9 + "alsoKnownAs": [ 10 + "at://op0" 11 + ], 12 + "rotationKeys": [ 13 + "did:key:zQ3shSeJv85PFZJTHmLtRF7eKcD94NBYSrvsxy6xRMUWMq9To", 14 + "did:key:zQ3shbxVAaoHKvxeP9Ra2C2i8szs4pK7oyZxmr8dGGWCtBCbd" 15 + ], 16 + "verificationMethods": {} 17 + }, 18 + "cid": "bafyreihfo4bemwzlppnce4bfftsrnwgtj76cku6bdtgwmf6hc7sfrt2tji", 19 + "nullified": false, 20 + "createdAt": "2025-07-25T14:33:23.810Z" 21 + }, 22 + { 23 + "did": "did:plc:4v3qers3fn55uitqeuwokfwy", 24 + "operation": { 25 + "sig": "h21ooBoOMqXoHxiS_bWtgPiGPc9RF1oR7nm6C26gsQd1Kz8-aAvigfer9rvTctbOZY5o9OGw-TI5Smu3lqy5ag", 26 + "prev": "bafyreihfo4bemwzlppnce4bfftsrnwgtj76cku6bdtgwmf6hc7sfrt2tji", 27 + "type": "plc_operation", 28 + "services": {}, 29 + "alsoKnownAs": [ 30 + "at://op1" 31 + ], 32 + "rotationKeys": [ 33 + "did:key:zQ3shRDdqSg2h43c8k4pCLi8yS1FpZdrEV7cHN8HvRYd9MUcD", 34 + "did:key:zQ3shUB1soE1ijB3NohA4esPNusMdpLQfibdUq9KhW9nab5Re" 35 + ], 36 + "verificationMethods": {} 37 + }, 38 + "cid": "bafyreieekauxgid3iwwfa5ywmzk62ccka66d777nz6hsdnbjgas36ikh6i", 39 + "nullified": false, 40 + "createdAt": "2025-07-25T14:33:24.203Z" 41 + }, 42 + { 43 + "did": "did:plc:4v3qers3fn55uitqeuwokfwy", 44 + "operation": { 45 + "sig": "jQN_ksPQqr3slUyALtQnNDbg7b6jagOOI127aId3kQcKEyJeSwNraCgecIz2K1jO0NfFcVguVHmC9zKSGrXv-Q", 46 + "prev": "bafyreieekauxgid3iwwfa5ywmzk62ccka66d777nz6hsdnbjgas36ikh6i", 47 + "type": "plc_operation", 48 + "services": {}, 49 + "alsoKnownAs": [ 50 + "at://op2" 51 + ], 52 + "rotationKeys": [ 53 + "did:key:zQ3shesZHWBAh4UCXbGQ1XrEu3DEBJK4isHb8XEcBiMatYbjs", 54 + "did:key:zQ3shY6PYLasew5NYpLzCMy4hehrTadBQAKjivkDBjGbJ4oEi" 55 + ], 56 + "verificationMethods": {} 57 + }, 58 + "cid": "bafyreifd5fjnolmvvsjqk5dcexqvhmm6xljppbof2bdniz7pz7dn5ncdha", 59 + "nullified": true, 60 + "createdAt": "2025-07-25T14:33:24.589Z" 61 + }, 62 + { 63 + "did": "did:plc:4v3qers3fn55uitqeuwokfwy", 64 + "operation": { 65 + "sig": "M0bV7tNe6YkQ55NX12p_2GXveNOiqcoffSIbT-r5Zp5ZYHvje3GAHh4YRtuVPKboqmO3yL7D5CseWYBiKrpcbA", 66 + "prev": "bafyreieekauxgid3iwwfa5ywmzk62ccka66d777nz6hsdnbjgas36ikh6i", 67 + "type": "plc_operation", 68 + "services": {}, 69 + "alsoKnownAs": [ 70 + "at://op3" 71 + ], 72 + "rotationKeys": [ 73 + "did:key:zQ3shdEwwjTRYxEntN3bCWVfvLTawVNf7FiZY7gahFZ5jRt6b", 74 + "did:key:zQ3shrjSBoLEWUJcDSJSdKCdhkstmyg3GKnAfbPDunZDN8uTv" 75 + ], 76 + "verificationMethods": {} 77 + }, 78 + "cid": "bafyreifviyelylmzecx3a4bi4imrq742xyxivu7yvplcsyomsanpkoge6e", 79 + "nullified": false, 80 + "createdAt": "2025-07-25T14:33:24.967Z" 81 + }, 82 + { 83 + "did": "did:plc:4v3qers3fn55uitqeuwokfwy", 84 + "operation": { 85 + "type": "plc_operation", 86 + "rotationKeys": [ 87 + "did:key:zQ3shoHCAecmKC3naChFg6gpePieh8YU4VjS16knqKWvQChBV", 88 + "did:key:zQ3shZd6DcRqomk4x2mW4BAwgBzexvVwxbM93GSEgJ7n4AW3v" 89 + ], 90 + "verificationMethods": {}, 91 + "alsoKnownAs": [ 92 + "at://op4" 93 + ], 94 + "services": {}, 95 + "prev": "bafyreifd5fjnolmvvsjqk5dcexqvhmm6xljppbof2bdniz7pz7dn5ncdha", 96 + "sig": "gObrmMH4xksEZ1HloufSifAIGmn35u36mM0CNyUq8fkk-MRhCj0tHSUazIybeNAYYEF-XTm3611XNUAUD3eXhA" 97 + }, 98 + "cid": "bafyreiabwwuzzavmsvz6zkl335p3eaaizaa2dg74xpazlbyehlpwzplqxq", 99 + "nullified": false, 100 + "createdAt": "2025-07-25T14:33:24.967Z" 101 + } 102 + ]
+53
plc/interop_tests/audit_log/invalid/log_invalid_update_tombstoned.json
··· 1 + [ 2 + { 3 + "did": "did:plc:b7y2odnrysmtzrynlrqatcpz", 4 + "operation": { 5 + "sig": "sCbeUr9rRo3_GS3ryYFTVMMIoQCxPFqPz-hnHWS0Mk9FMOY-3oPgDMg6wOr9eWdVWxRTHB2zN6YncjaAhn3uWA", 6 + "prev": null, 7 + "type": "plc_operation", 8 + "services": {}, 9 + "alsoKnownAs": [ 10 + "at://op0" 11 + ], 12 + "rotationKeys": [ 13 + "did:key:zQ3shdUgePXuKcWPouBfa3Pxq1pDQRUbdWwP9L3F7DeUJB4KG", 14 + "did:key:zQ3shfLEW369HgR9wrF3uccmFPduGoCi9pS4vJSomc9y4ZM3e" 15 + ], 16 + "verificationMethods": {} 17 + }, 18 + "cid": "bafyreiap6gtq3moete6modk4maeyt6ns4abftkk5lnaqb5rsyp7fwebdnu", 19 + "nullified": false, 20 + "createdAt": "2025-07-28T13:07:23.620Z" 21 + }, 22 + { 23 + "did": "did:plc:b7y2odnrysmtzrynlrqatcpz", 24 + "operation": { 25 + "sig": "Qf7L4_fWzsKolaHiVjVBeLBcNF4ULDeUWzp2sxuyi5dYTOVzCyFGxOVGlAOKV41eUQ4wUP_q5JDly8tAnQQzMw", 26 + "prev": "bafyreiap6gtq3moete6modk4maeyt6ns4abftkk5lnaqb5rsyp7fwebdnu", 27 + "type": "plc_tombstone" 28 + }, 29 + "cid": "bafyreihwvjlobt4dyrxidxxxxfbqfqjqv7j7432ci3anis3zdtwkdn22ya", 30 + "nullified": false, 31 + "createdAt": "2025-07-28T13:07:23.993Z" 32 + }, 33 + { 34 + "did": "did:plc:b7y2odnrysmtzrynlrqatcpz", 35 + "operation": { 36 + "type": "plc_operation", 37 + "rotationKeys": [ 38 + "did:key:zQ3shrwJx44p966nxnxpmw33DPNGVGR3hfbXunzuF5NmdCtm8", 39 + "did:key:zQ3sha6vCiYnwohoYfxHLWmQEBR5iG5acWgXToYjr9oUAjWCH" 40 + ], 41 + "verificationMethods": {}, 42 + "alsoKnownAs": [ 43 + "at://op2" 44 + ], 45 + "services": {}, 46 + "prev": "bafyreihwvjlobt4dyrxidxxxxfbqfqjqv7j7432ci3anis3zdtwkdn22ya", 47 + "sig": "KLMS5W-XHQ0T8eWiOQYI7sUvjXvOBVZbbjZAVWyPLlNlio-z3gtaP-K3ZMT3Sl7ew2l1lzPLj5rU66kTzltfRA" 48 + }, 49 + "cid": "bafyreihpojylqxoz4r5ahmazmbmh4xbxbw6mm7jftlhxmkszkxwgjq3o4e", 50 + "nullified": false, 51 + "createdAt": "2025-07-28T13:08:23.993Z" 52 + } 53 + ]
+54
plc/interop_tests/audit_log/valid/log_bnewbold_robocracy.json
··· 1 + [ 2 + { 3 + "did": "did:plc:nhxcyu4ewwhl5pqil4dotqjo", 4 + "operation": { 5 + "sig": "v9rHEhW4XVwMKRSd2yeFgk4-mZthHSZwJ4tShNPqDP4NH3w79CkxIOmJ393D6MEyWZLN1qxS1qBIbFEGtfoDDw", 6 + "prev": null, 7 + "type": "plc_operation", 8 + "services": { 9 + "atproto_pds": { 10 + "type": "AtprotoPersonalDataServer", 11 + "endpoint": "https://pds.robocracy.org" 12 + } 13 + }, 14 + "alsoKnownAs": [ 15 + "at://bnewbold.pds.robocracy.org" 16 + ], 17 + "rotationKeys": [ 18 + "did:key:zQ3shcciz4AvrLyDnUdZLpQys3kyCsesojRNzJAieyDStGxGo" 19 + ], 20 + "verificationMethods": { 21 + "atproto": "did:key:zQ3shazA2airLo8gNJvxGMFZWPJDRkLGNR6mn9Txsc8YYndwy" 22 + } 23 + }, 24 + "cid": "bafyreidj5ywfhbfvr27l4cc7a3u4clup5vx343ufuzdliethuhzzxjbg4q", 25 + "nullified": false, 26 + "createdAt": "2024-02-22T04:31:08.867Z" 27 + }, 28 + { 29 + "did": "did:plc:nhxcyu4ewwhl5pqil4dotqjo", 30 + "operation": { 31 + "sig": "P8TrUomEKSnJpyuoyqdaqv-KilKbQKoi6MNf8DNN8LdFn1cA3_BtkqVYAjmucpQ8DDSze-jG4YDvC6HFK9QPOA", 32 + "prev": "bafyreidj5ywfhbfvr27l4cc7a3u4clup5vx343ufuzdliethuhzzxjbg4q", 33 + "type": "plc_operation", 34 + "services": { 35 + "atproto_pds": { 36 + "type": "AtprotoPersonalDataServer", 37 + "endpoint": "https://pds.robocracy.org" 38 + } 39 + }, 40 + "alsoKnownAs": [ 41 + "at://bnewbold.robocracy.org" 42 + ], 43 + "rotationKeys": [ 44 + "did:key:zQ3shcciz4AvrLyDnUdZLpQys3kyCsesojRNzJAieyDStGxGo" 45 + ], 46 + "verificationMethods": { 47 + "atproto": "did:key:zQ3shazA2airLo8gNJvxGMFZWPJDRkLGNR6mn9Txsc8YYndwy" 48 + } 49 + }, 50 + "cid": "bafyreiemmb2ephqyt7orhiv4vp7gmdohnzhgkehwzfrvirgvg7fg352ysi", 51 + "nullified": false, 52 + "createdAt": "2024-02-22T04:39:57.282Z" 53 + } 54 + ]
+56
plc/interop_tests/audit_log/valid/log_bskyapp.json
··· 1 + [ 2 + { 3 + "did": "did:plc:z72i7hdynmk6r22z27h6tvur", 4 + "operation": { 5 + "sig": "9NuYV7AqwHVTc0YuWzNV3CJafsSZWH7qCxHRUIP2xWlB-YexXC1OaYAnUayiCXLVzRQ8WBXIqF-SvZdNalwcjA", 6 + "prev": null, 7 + "type": "plc_operation", 8 + "services": { 9 + "atproto_pds": { 10 + "type": "AtprotoPersonalDataServer", 11 + "endpoint": "https://bsky.social" 12 + } 13 + }, 14 + "alsoKnownAs": [ 15 + "at://bluesky-team.bsky.social" 16 + ], 17 + "rotationKeys": [ 18 + "did:key:zQ3shhCGUqDKjStzuDxPkTxN6ujddP4RkEKJJouJGRRkaLGbg", 19 + "did:key:zQ3shpKnbdPx3g3CmPf5cRVTPe1HtSwVn5ish3wSnDPQCbLJK" 20 + ], 21 + "verificationMethods": { 22 + "atproto": "did:key:zQ3shXjHeiBuRCKmM36cuYnm7YEMzhGnCmCyW92sRJ9pribSF" 23 + } 24 + }, 25 + "cid": "bafyreigp6shzy6dlcxuowwoxz7u5nemdrkad2my5zwzpwilcnhih7bw6zm", 26 + "nullified": false, 27 + "createdAt": "2023-04-12T04:53:57.057Z" 28 + }, 29 + { 30 + "did": "did:plc:z72i7hdynmk6r22z27h6tvur", 31 + "operation": { 32 + "sig": "1mEWzRtFOgeRXH-YCSPTxb990JOXxa__n8Qw6BOKl7Ndm6OFFmwYKiiMqMCpAbxpnGjF5abfIsKc7u3a77Cbnw", 33 + "prev": "bafyreigp6shzy6dlcxuowwoxz7u5nemdrkad2my5zwzpwilcnhih7bw6zm", 34 + "type": "plc_operation", 35 + "services": { 36 + "atproto_pds": { 37 + "type": "AtprotoPersonalDataServer", 38 + "endpoint": "https://bsky.social" 39 + } 40 + }, 41 + "alsoKnownAs": [ 42 + "at://bsky.app" 43 + ], 44 + "rotationKeys": [ 45 + "did:key:zQ3shhCGUqDKjStzuDxPkTxN6ujddP4RkEKJJouJGRRkaLGbg", 46 + "did:key:zQ3shpKnbdPx3g3CmPf5cRVTPe1HtSwVn5ish3wSnDPQCbLJK" 47 + ], 48 + "verificationMethods": { 49 + "atproto": "did:key:zQ3shXjHeiBuRCKmM36cuYnm7YEMzhGnCmCyW92sRJ9pribSF" 50 + } 51 + }, 52 + "cid": "bafyreihmuvr3frdvd6vmdhucih277prdcfcezf67lasg5oekxoimnunjoq", 53 + "nullified": false, 54 + "createdAt": "2023-04-12T17:26:46.468Z" 55 + } 56 + ]
+20
plc/interop_tests/audit_log/valid/log_duplicate_rotation_keys.json
··· 1 + [ 2 + { 3 + "did": "did:plc:fjhag55cvrg4no2optwsld2d", 4 + "operation": { 5 + "sig": "eYGQcdzFhATHh1wn9eNBjL5TRd7U-K_D3eyY3KPp2BcFrz_CMZ5PPoIN1P_HSO7m_66pmRcDiPPvBclDHQZ6tQ", 6 + "prev": null, 7 + "type": "plc_operation", 8 + "services": {}, 9 + "alsoKnownAs": [], 10 + "rotationKeys": [ 11 + "did:key:zDnaet3CLvFwasyRn1qaCJec6XDqh2pXE4fUoJpG19H9xauUf", 12 + "did:key:zDnaet3CLvFwasyRn1qaCJec6XDqh2pXE4fUoJpG19H9xauUf" 13 + ], 14 + "verificationMethods": {} 15 + }, 16 + "cid": "bafyreibkjybxpivmjxdlwtt45usy6q6nutr3clatlbfejlnusqvmpsxm5y", 17 + "nullified": false, 18 + "createdAt": "2025-07-23T11:52:57.706Z" 19 + } 20 + ]
+34
plc/interop_tests/audit_log/valid/log_empty_rotation_keys.json
··· 1 + [ 2 + { 3 + "did": "did:plc:7az4y43gpd5iz3uz5aoiimpn", 4 + "operation": { 5 + "sig": "kWGr1tLkLeFbi2sM1ECfIXVLE11U8F_2JbIjP6SO504Z2YVilrvr37AgtsfcPww_EuBacJoJDYbmwVTRoz3cPQ", 6 + "prev": null, 7 + "type": "plc_operation", 8 + "services": {}, 9 + "alsoKnownAs": [], 10 + "rotationKeys": [ 11 + "did:key:zDnaet3CLvFwasyRn1qaCJec6XDqh2pXE4fUoJpG19H9xauUf" 12 + ], 13 + "verificationMethods": {} 14 + }, 15 + "cid": "bafyreihygpghgzty7kgo5gpidscdd3p7krovysfy76uqfyildy5qy6xyqi", 16 + "nullified": false, 17 + "createdAt": "2025-07-22T16:42:57.772Z" 18 + }, 19 + { 20 + "did": "did:plc:7az4y43gpd5iz3uz5aoiimpn", 21 + "operation": { 22 + "sig": "EJmgtA4PoFz6sXWz324x4f-46PbQJV0NR16_B69fbWVJb40wSBAakxJKUqvAQnvgMMZnWiL8LUdyEgFejSwdKA", 23 + "prev": "bafyreihygpghgzty7kgo5gpidscdd3p7krovysfy76uqfyildy5qy6xyqi", 24 + "type": "plc_operation", 25 + "services": {}, 26 + "alsoKnownAs": [], 27 + "rotationKeys": [], 28 + "verificationMethods": {} 29 + }, 30 + "cid": "bafyreidhcvt64va2y7igfy5soymc4xtpoghdgv2oujv5f76js6sdwby6ta", 31 + "nullified": false, 32 + "createdAt": "2025-07-22T16:44:08.313Z" 33 + } 34 + ]
+125
plc/interop_tests/audit_log/valid/log_legacy_dholms.json
··· 1 + [ 2 + { 3 + "did": "did:plc:yk4dd2qkboz2yv6tpubpc6co", 4 + "operation": { 5 + "sig": "7QTzqO1BcL3eDzP4P_YBxMmv5U4brHzAItkM9w5o8gZA7ElZkrVYEwsfQCfk5EoWLk58Z1y6fyNP9x1pthJnlw", 6 + "prev": null, 7 + "type": "create", 8 + "handle": "dan.bsky.social", 9 + "service": "https://bsky.social", 10 + "signingKey": "did:key:zQ3shP5TBe1sQfSttXty15FAEHV1DZgcxRZNxvEWnPfLFwLxJ", 11 + "recoveryKey": "did:key:zQ3shhCGUqDKjStzuDxPkTxN6ujddP4RkEKJJouJGRRkaLGbg" 12 + }, 13 + "cid": "bafyreigcxay6ucqlwowfpu35alyxqtv3c4vsj7gmdtmnidsnqs6nblyarq", 14 + "nullified": false, 15 + "createdAt": "2022-11-17T01:07:13.996Z" 16 + }, 17 + { 18 + "did": "did:plc:yk4dd2qkboz2yv6tpubpc6co", 19 + "operation": { 20 + "sig": "n-VWsPZY4xkFN8wlg-kJBU_yzWTNd2oBnbjkjxXu3HdjbBLaEB7K39JHIPn_DZVALKRjts6bUicjSEecZy8eIw", 21 + "prev": "bafyreigcxay6ucqlwowfpu35alyxqtv3c4vsj7gmdtmnidsnqs6nblyarq", 22 + "type": "plc_operation", 23 + "services": { 24 + "atproto_pds": { 25 + "type": "AtprotoPersonalDataServer", 26 + "endpoint": "https://bsky.social" 27 + } 28 + }, 29 + "alsoKnownAs": [ 30 + "at://dholms.xyz" 31 + ], 32 + "rotationKeys": [ 33 + "did:key:zQ3shhCGUqDKjStzuDxPkTxN6ujddP4RkEKJJouJGRRkaLGbg", 34 + "did:key:zQ3shP5TBe1sQfSttXty15FAEHV1DZgcxRZNxvEWnPfLFwLxJ" 35 + ], 36 + "verificationMethods": { 37 + "atproto": "did:key:zQ3shP5TBe1sQfSttXty15FAEHV1DZgcxRZNxvEWnPfLFwLxJ" 38 + } 39 + }, 40 + "cid": "bafyreiho5sanautvnw3det66jcwic4vkeabc35y7iou3ygwj2l3xqcxdau", 41 + "nullified": false, 42 + "createdAt": "2023-03-06T18:47:09.501Z" 43 + }, 44 + { 45 + "did": "did:plc:yk4dd2qkboz2yv6tpubpc6co", 46 + "operation": { 47 + "sig": "HWgrfQXxUN3mhR5TR-nrwGJwVr9RDbyDn6eCmqBg32x2zIjhe98YxOtFOLI9jQkBlTTzqzUOwJh1KZd4O2pDOw", 48 + "prev": "bafyreiho5sanautvnw3det66jcwic4vkeabc35y7iou3ygwj2l3xqcxdau", 49 + "type": "plc_operation", 50 + "services": { 51 + "atproto_pds": { 52 + "type": "AtprotoPersonalDataServer", 53 + "endpoint": "https://bsky.social" 54 + } 55 + }, 56 + "alsoKnownAs": [ 57 + "at://dholms.bsky.social" 58 + ], 59 + "rotationKeys": [ 60 + "did:key:zQ3shhCGUqDKjStzuDxPkTxN6ujddP4RkEKJJouJGRRkaLGbg", 61 + "did:key:zQ3shP5TBe1sQfSttXty15FAEHV1DZgcxRZNxvEWnPfLFwLxJ" 62 + ], 63 + "verificationMethods": { 64 + "atproto": "did:key:zQ3shP5TBe1sQfSttXty15FAEHV1DZgcxRZNxvEWnPfLFwLxJ" 65 + } 66 + }, 67 + "cid": "bafyreic3am2nmgykxtwsxwigzn6faibxv5ef5kalcv7li3eatcqldcqrku", 68 + "nullified": false, 69 + "createdAt": "2023-03-06T19:50:49.987Z" 70 + }, 71 + { 72 + "did": "did:plc:yk4dd2qkboz2yv6tpubpc6co", 73 + "operation": { 74 + "sig": "9Fy2iHCSK5mtgLNCkS9CyI0r7lu6H1SVgusaD1jQdsMUySUU6apde0z7SobpYZKp4sThk4hxOWtO-bXhu1cNjg", 75 + "prev": "bafyreic3am2nmgykxtwsxwigzn6faibxv5ef5kalcv7li3eatcqldcqrku", 76 + "type": "plc_operation", 77 + "services": { 78 + "atproto_pds": { 79 + "type": "AtprotoPersonalDataServer", 80 + "endpoint": "https://bsky.social" 81 + } 82 + }, 83 + "alsoKnownAs": [ 84 + "at://dholms.xyz" 85 + ], 86 + "rotationKeys": [ 87 + "did:key:zQ3shhCGUqDKjStzuDxPkTxN6ujddP4RkEKJJouJGRRkaLGbg", 88 + "did:key:zQ3shP5TBe1sQfSttXty15FAEHV1DZgcxRZNxvEWnPfLFwLxJ" 89 + ], 90 + "verificationMethods": { 91 + "atproto": "did:key:zQ3shP5TBe1sQfSttXty15FAEHV1DZgcxRZNxvEWnPfLFwLxJ" 92 + } 93 + }, 94 + "cid": "bafyreicwybxr6h6vkxpoarismso3liozdzswshmzcvl4tyckdazn5lxjte", 95 + "nullified": false, 96 + "createdAt": "2023-03-06T19:51:09.950Z" 97 + }, 98 + { 99 + "did": "did:plc:yk4dd2qkboz2yv6tpubpc6co", 100 + "operation": { 101 + "sig": "lBXd8rHZ84hCuQysGdi_5A9C8yPHTHasPibO4DZiuZVrehs2hiBcjAL0srLSTsF1kvsHTw1ddai-QwH0Wd_drQ", 102 + "prev": "bafyreicwybxr6h6vkxpoarismso3liozdzswshmzcvl4tyckdazn5lxjte", 103 + "type": "plc_operation", 104 + "services": { 105 + "atproto_pds": { 106 + "type": "AtprotoPersonalDataServer", 107 + "endpoint": "https://bsky.social" 108 + } 109 + }, 110 + "alsoKnownAs": [ 111 + "at://dholms.xyz" 112 + ], 113 + "rotationKeys": [ 114 + "did:key:zQ3shhCGUqDKjStzuDxPkTxN6ujddP4RkEKJJouJGRRkaLGbg", 115 + "did:key:zQ3shpKnbdPx3g3CmPf5cRVTPe1HtSwVn5ish3wSnDPQCbLJK" 116 + ], 117 + "verificationMethods": { 118 + "atproto": "did:key:zQ3shXjHeiBuRCKmM36cuYnm7YEMzhGnCmCyW92sRJ9pribSF" 119 + } 120 + }, 121 + "cid": "bafyreidfrpuegbqd5r56shka4duythb7phb6d7i3bck2dkeb5fjppwd7gi", 122 + "nullified": false, 123 + "createdAt": "2023-03-09T23:18:31.709Z" 124 + } 125 + ]
+59
plc/interop_tests/audit_log/valid/log_nullification.json
··· 1 + [ 2 + { 3 + "did": "did:plc:2s2mvm52ttz6r4hocmrq7x27", 4 + "operation": { 5 + "type": "plc_operation", 6 + "rotationKeys": [ 7 + "did:key:zQ3shwPdax6jKMbhtzbueGwSjc7RnjsmPcNB1vQUpbKUCN1t1", 8 + "did:key:zQ3shmMcsFLvSZQy2pSZFFKZNmQSNmyuSepPXMHpgTYrontu2" 9 + ], 10 + "verificationMethods": {}, 11 + "alsoKnownAs": [ 12 + "at://foo" 13 + ], 14 + "services": {}, 15 + "prev": null, 16 + "sig": "1pTjQD5-LA4gN6tz1t7-28Tzct9NI-oYMN8QVUQkB7493j-vKeZnZO0YqNNBCXsORatmfKUXwteC9zW82mi_yw" 17 + }, 18 + "cid": "bafyreiguwtflhou46pupb3qtemh56x6o3fn3y7ym2q3jgoms35tdu2d5ga", 19 + "nullified": false, 20 + "createdAt": "1970-01-01T00:00:00.000Z" 21 + }, 22 + { 23 + "did": "did:plc:2s2mvm52ttz6r4hocmrq7x27", 24 + "operation": { 25 + "prev": "bafyreiguwtflhou46pupb3qtemh56x6o3fn3y7ym2q3jgoms35tdu2d5ga", 26 + "type": "plc_operation", 27 + "services": {}, 28 + "alsoKnownAs": [ 29 + "at://foo" 30 + ], 31 + "rotationKeys": [], 32 + "verificationMethods": {}, 33 + "sig": "XfbRKnfzmJyRXUrajAISgdiJWNdNFMFgtDnlKvj-bIZhpfqGd39xiylJVXXu5Usw2cc2Js4EeSPh2xfhUQNxSA" 34 + }, 35 + "cid": "bafyreicbxgoelatj24iyqnevm7v4f22utxb75p756ztyu5v7cjxlwti3oa", 36 + "nullified": true, 37 + "createdAt": "1970-01-01T00:00:01.000Z" 38 + }, 39 + { 40 + "did": "did:plc:2s2mvm52ttz6r4hocmrq7x27", 41 + "operation": { 42 + "prev": "bafyreiguwtflhou46pupb3qtemh56x6o3fn3y7ym2q3jgoms35tdu2d5ga", 43 + "type": "plc_operation", 44 + "services": {}, 45 + "alsoKnownAs": [ 46 + "at://foo" 47 + ], 48 + "rotationKeys": [ 49 + "did:key:zQ3shwPdax6jKMbhtzbueGwSjc7RnjsmPcNB1vQUpbKUCN1t1", 50 + "did:key:zQ3shmMcsFLvSZQy2pSZFFKZNmQSNmyuSepPXMHpgTYrontu2" 51 + ], 52 + "verificationMethods": {}, 53 + "sig": "zCcbDgFbv5b0aGggLepLcARzNzH0sG6uuvq2GacyPsAsjDsLMZn6KmOxa50iWZEMRknQHsMYyMTS99eeTPu0fQ" 54 + }, 55 + "cid": "bafyreidef3g2vvlshwzf7vxjyxg3lraahujvhtkpo7y3g4br52ft3lzwem", 56 + "nullified": false, 57 + "createdAt": "1970-01-01T00:00:02.000Z" 58 + } 59 + ]
+82
plc/interop_tests/audit_log/valid/log_nullification_at_exactly_72h.json
··· 1 + [ 2 + { 3 + "did": "did:plc:huyin2bxfj3bpg3gjq64nazd", 4 + "operation": { 5 + "sig": "SMt9lbnrIZphaVysWweU24bobCCZQmROnpSE4_9ZcUZda05TW9kR3povEIWL76_5l28LPphh9zOBnzuT6CsfJA", 6 + "prev": null, 7 + "type": "plc_operation", 8 + "services": {}, 9 + "alsoKnownAs": [ 10 + "at://op0" 11 + ], 12 + "rotationKeys": [ 13 + "did:key:zQ3shbhi3Ecu99Fo6apTVVAzV15RExDaDgoY3mTuQXQsCPGky", 14 + "did:key:zQ3shdDRwdy9yjepHMnhAmHXZ1sNHikyRUNbJBFmJdEHBSkm1" 15 + ], 16 + "verificationMethods": {} 17 + }, 18 + "cid": "bafyreib5gcdoqnzkoylzwzsmhxdigizzl7nlwoe2fujd6aqkgn4yn5juee", 19 + "nullified": false, 20 + "createdAt": "2025-07-25T14:40:41.627Z" 21 + }, 22 + { 23 + "did": "did:plc:huyin2bxfj3bpg3gjq64nazd", 24 + "operation": { 25 + "sig": "5C6Wb29_El2d96XLoaJyBYczpSi4Y0iFiKx5vG6czV5eltBwPeQ2TT8B_EieXy_G3O_aRZrobUBKijQeQfop3g", 26 + "prev": "bafyreib5gcdoqnzkoylzwzsmhxdigizzl7nlwoe2fujd6aqkgn4yn5juee", 27 + "type": "plc_operation", 28 + "services": {}, 29 + "alsoKnownAs": [ 30 + "at://op1" 31 + ], 32 + "rotationKeys": [ 33 + "did:key:zQ3shhZw6NmJJhdWcWqxGfaLKBV7LbW5RVVxbMuQQt5EZExEv", 34 + "did:key:zQ3shQPH612ZLpKXu6wJ8Nq3xJcZ1KBngAYzV1ifvGFjyfGGM" 35 + ], 36 + "verificationMethods": {} 37 + }, 38 + "cid": "bafyreidygw4qxncobfaz54dujsdamppw5eyzfsjscvzzh62s4lgfdtwkhm", 39 + "nullified": false, 40 + "createdAt": "2025-07-26T00:00:00.000Z" 41 + }, 42 + { 43 + "did": "did:plc:huyin2bxfj3bpg3gjq64nazd", 44 + "operation": { 45 + "sig": "2JUYqTo-dhMxjvIZad2E7dspTfd4UJjIF1m4MecIaNAi3YwVERLQT6DBH4JGirSZOUZPsI9kRPFADG3FvRROOA", 46 + "prev": "bafyreidygw4qxncobfaz54dujsdamppw5eyzfsjscvzzh62s4lgfdtwkhm", 47 + "type": "plc_operation", 48 + "services": {}, 49 + "alsoKnownAs": [ 50 + "at://op2" 51 + ], 52 + "rotationKeys": [ 53 + "did:key:zQ3shjndSKfWo811gPc6w7xgTQ1QZ1pSzRh9nFyYNTr2f8vad", 54 + "did:key:zQ3shNu1MTAyn751xRrPYgzYHMjmgxqJX7MaKD1sRyqmEG7Lr" 55 + ], 56 + "verificationMethods": {} 57 + }, 58 + "cid": "bafyreifwu6mpycnlcwcp33pvfmwlba3bsinrvaiemjied2hotb24z7wk2y", 59 + "nullified": true, 60 + "createdAt": "2025-07-27T00:00:00.000Z" 61 + }, 62 + { 63 + "did": "did:plc:huyin2bxfj3bpg3gjq64nazd", 64 + "operation": { 65 + "sig": "FFWR27ys_W2YaEOEQIFLaEWhf3V8rVR5Lh6F20UMn8AvsgS5eoT4nrY3D34hGJKBtkoy6FgNX2LjegFmwedmZQ", 66 + "prev": "bafyreidygw4qxncobfaz54dujsdamppw5eyzfsjscvzzh62s4lgfdtwkhm", 67 + "type": "plc_operation", 68 + "services": {}, 69 + "alsoKnownAs": [ 70 + "at://op3" 71 + ], 72 + "rotationKeys": [ 73 + "did:key:zQ3shXNdU3wForFqrQSGGL4XpRhUTb3RAdQmYx3RFx6WqyiHR", 74 + "did:key:zQ3shQFNHvyMfEWFsEycbyU24KdLQxuHtMvDRLVnjwMQhTfGU" 75 + ], 76 + "verificationMethods": {} 77 + }, 78 + "cid": "bafyreicethnkrcbd2xojssb76sppdnfxlwzzjkrf6f2fiixxglv7sik65e", 79 + "nullified": false, 80 + "createdAt": "2025-07-30T00:00:00.000Z" 81 + } 82 + ]
+162
plc/interop_tests/audit_log/valid/log_nullification_nontrivial.json
··· 1 + [ 2 + { 3 + "did": "did:plc:ana7n2ppeqk57pay7azhjs24", 4 + "operation": { 5 + "sig": "IypAT0szH_8Ygl7QrnXZrxemIcg_RkW_GyYbjih_9BEe5DRGgLxmQGeWsvB0TfDqm50c0xB8mGraOf25IA25qA", 6 + "prev": null, 7 + "type": "plc_operation", 8 + "services": {}, 9 + "alsoKnownAs": [ 10 + "at://op0" 11 + ], 12 + "rotationKeys": [ 13 + "did:key:zQ3shhiASLv8GN7ob3iWqsAxP7nE95LUPWsNEb2jTfW7e2yCt", 14 + "did:key:zQ3shsnWCW7HC7bYVnRcY5XxQpKArfafpB5S39DoqTkexuiTu" 15 + ], 16 + "verificationMethods": {} 17 + }, 18 + "cid": "bafyreiadih3ot3zecxp3yghygj2mwxh3szrrifwer2z4iodazbrtuvmmgm", 19 + "nullified": false, 20 + "createdAt": "2025-07-25T14:14:48.962Z" 21 + }, 22 + { 23 + "did": "did:plc:ana7n2ppeqk57pay7azhjs24", 24 + "operation": { 25 + "sig": "eabKT5-1i2MW8Z7iWXHp2tlEYn1y6HmbuHexP-fgOzQq6RuNBGlWWOjMygxl8BK98FCNU8wvX9eWkM-soBrfow", 26 + "prev": "bafyreiadih3ot3zecxp3yghygj2mwxh3szrrifwer2z4iodazbrtuvmmgm", 27 + "type": "plc_operation", 28 + "services": {}, 29 + "alsoKnownAs": [ 30 + "at://op1" 31 + ], 32 + "rotationKeys": [ 33 + "did:key:zQ3shXPgp1NrzoXSpa7YTn9ZFUVco39X8ygph16Bj6Ku4NJd2", 34 + "did:key:zQ3shd4eC3ykuq1GfpuyosoBLSX3JASu9qFXggqWGWwH9MXg6" 35 + ], 36 + "verificationMethods": {} 37 + }, 38 + "cid": "bafyreieshd5ujokbnxfzqmaoyimbdsiuwklji2st7ffga2vkhsozxn324u", 39 + "nullified": false, 40 + "createdAt": "2025-07-25T14:14:49.353Z" 41 + }, 42 + { 43 + "did": "did:plc:ana7n2ppeqk57pay7azhjs24", 44 + "operation": { 45 + "sig": "hHkc9Mn_nE-P60GsZJG6jm6xdlBUbaxonGsir-O95P1ibH16jK-FyVO2KtF-J2llULLDZID_mbKkh2QOpBc-TA", 46 + "prev": "bafyreieshd5ujokbnxfzqmaoyimbdsiuwklji2st7ffga2vkhsozxn324u", 47 + "type": "plc_operation", 48 + "services": {}, 49 + "alsoKnownAs": [ 50 + "at://op2" 51 + ], 52 + "rotationKeys": [ 53 + "did:key:zQ3shco8HcC3YKPeoCJ55S9XQ7e7enn6qqCeyx1jhxGE8hRAX", 54 + "did:key:zQ3shQDMSvL951qcX2Tpdp4s6tL39pjMzVowM9XU4v6NMkG92" 55 + ], 56 + "verificationMethods": {} 57 + }, 58 + "cid": "bafyreifpq7r5ipo3rdpfrippmir5ne6he36mfab2shtz5g25tjm6z655rq", 59 + "nullified": true, 60 + "createdAt": "2025-07-25T14:14:49.751Z" 61 + }, 62 + { 63 + "did": "did:plc:ana7n2ppeqk57pay7azhjs24", 64 + "operation": { 65 + "sig": "MoCicrrAaerQdkyGeZT5QFKQ5hUbwoELlpJG1yq8e1onpuIECPlDkRn0MSTSPK8t546W8jZNvrtHRuNCBoXQUA", 66 + "prev": "bafyreifpq7r5ipo3rdpfrippmir5ne6he36mfab2shtz5g25tjm6z655rq", 67 + "type": "plc_operation", 68 + "services": {}, 69 + "alsoKnownAs": [ 70 + "at://op3" 71 + ], 72 + "rotationKeys": [ 73 + "did:key:zQ3shX4Tw63vH57eanTngtGeGXDTjDYqovNd8DfpNpcJsHRwK", 74 + "did:key:zQ3shTPSgYbFTfmV5wNDV6oHHogTsDemWRdwWm6ZKZB2wXDQc" 75 + ], 76 + "verificationMethods": {} 77 + }, 78 + "cid": "bafyreih55jjrlxemxyusbyqhwkydvew5seldzlzflhuiqhsuwienbuvxmu", 79 + "nullified": true, 80 + "createdAt": "2025-07-25T14:14:50.126Z" 81 + }, 82 + { 83 + "did": "did:plc:ana7n2ppeqk57pay7azhjs24", 84 + "operation": { 85 + "sig": "lD4kyQq8GZbvqaUHLt91wCBZgxeFvXQdVLM7Go0083cLE8HeG1Rj5HnsEBadYqzoCOcLgDWPMCwdAuM_GCojxA", 86 + "prev": "bafyreih55jjrlxemxyusbyqhwkydvew5seldzlzflhuiqhsuwienbuvxmu", 87 + "type": "plc_operation", 88 + "services": {}, 89 + "alsoKnownAs": [ 90 + "at://op4" 91 + ], 92 + "rotationKeys": [ 93 + "did:key:zQ3shW9AesYJE95cyuPwWCmJhvPgP7Rkva8SgNoJ2wuAA7gDd", 94 + "did:key:zQ3shaw1oqEGsigCdjMvgUD6sN4w6r3h3EdG7Eqp54c7oo6LD" 95 + ], 96 + "verificationMethods": {} 97 + }, 98 + "cid": "bafyreicnmhhlbyh5qhslaub3l4j7hydcv3lbmxyorkge5y7xgpvdlxzjlu", 99 + "nullified": true, 100 + "createdAt": "2025-07-25T14:14:50.515Z" 101 + }, 102 + { 103 + "did": "did:plc:ana7n2ppeqk57pay7azhjs24", 104 + "operation": { 105 + "sig": "LjSlpZATuTm28kcSAkJJmgP6_pDoq2YA5J408aQJTmVyTsDkwyJ3gKB-pcMiYKlNptD3BxdiXnIKaSP5vI6D8w", 106 + "prev": "bafyreih55jjrlxemxyusbyqhwkydvew5seldzlzflhuiqhsuwienbuvxmu", 107 + "type": "plc_operation", 108 + "services": {}, 109 + "alsoKnownAs": [ 110 + "at://op5" 111 + ], 112 + "rotationKeys": [ 113 + "did:key:zQ3shvv4uX5wmumTa3U5pgZxt1VNZmHY3UhX3KhEwHk7dk7PB", 114 + "did:key:zQ3shvKqwoT1wtjHbCki566vQFk86UvJc8T5WfGpmCm5jyCN7" 115 + ], 116 + "verificationMethods": {} 117 + }, 118 + "cid": "bafyreihveyvdjaslphmjftrb6uw2ftqklefi6jod2m7lv7pw2aitu6lrqu", 119 + "nullified": true, 120 + "createdAt": "2025-07-25T14:14:50.904Z" 121 + }, 122 + { 123 + "did": "did:plc:ana7n2ppeqk57pay7azhjs24", 124 + "operation": { 125 + "sig": "tjVslQYlmnJrZifS9TqxhH7e4YolwvI40FU_X93mFVRy85W64ClwJ_1X-eeQiNh5on0TxdqoBmPcvERocrEEPA", 126 + "prev": "bafyreihveyvdjaslphmjftrb6uw2ftqklefi6jod2m7lv7pw2aitu6lrqu", 127 + "type": "plc_operation", 128 + "services": {}, 129 + "alsoKnownAs": [ 130 + "at://op6" 131 + ], 132 + "rotationKeys": [ 133 + "did:key:zQ3shjaNAXa4S2zupVtaRfiiiZL8BNsz3Gx2X7u4KJP4xxVfR", 134 + "did:key:zQ3shRiasZG5nANgQH2MYA2RJcZo4K6J7zL7EeoG2q46HjUyi" 135 + ], 136 + "verificationMethods": {} 137 + }, 138 + "cid": "bafyreihdethy553d6czdke36vlfmn7tzhjumndlkwmfksqubutzeaihcuy", 139 + "nullified": true, 140 + "createdAt": "2025-07-25T14:14:51.281Z" 141 + }, 142 + { 143 + "did": "did:plc:ana7n2ppeqk57pay7azhjs24", 144 + "operation": { 145 + "sig": "AFq-UKqZtLA88ZssGg0jV7BUm1zju4V79W4KG35VdXMB7MH9AfHvBNLOlxmzEW_ATs1tmeX60U00meDI8oppRg", 146 + "prev": "bafyreieshd5ujokbnxfzqmaoyimbdsiuwklji2st7ffga2vkhsozxn324u", 147 + "type": "plc_operation", 148 + "services": {}, 149 + "alsoKnownAs": [ 150 + "at://op7" 151 + ], 152 + "rotationKeys": [ 153 + "did:key:zQ3shi3dE5REVqBR25JcENfLA5qvKPy6yADo9EQCtNVGwo9Xn", 154 + "did:key:zQ3shfePFkQKQMPDpfKpH5frZuk6aLsqWhLaQZQ62KsQwQziz" 155 + ], 156 + "verificationMethods": {} 157 + }, 158 + "cid": "bafyreihdmait2kmgp3zm7uixh4b56ekufakqwqlfgh7ji6m2aphensd3dy", 159 + "nullified": false, 160 + "createdAt": "2025-07-25T14:14:51.652Z" 161 + } 162 + ]
+73
plc/interop_tests/audit_log/valid/log_nullified_tombstone.json
··· 1 + [ 2 + { 3 + "did": "did:plc:7iqnimg5hvigsckpbqrqktoh", 4 + "operation": { 5 + "sig": "GI8N37O5s6xr4QgWJ0zJdBIdS8xZ-G107B8jaaM_efJmkF8MYpIG0v3XJBW8tVzFS3xkvoOj46ufvmn9Z6KKtg", 6 + "prev": null, 7 + "type": "plc_operation", 8 + "services": {}, 9 + "alsoKnownAs": [ 10 + "at://op0" 11 + ], 12 + "rotationKeys": [ 13 + "did:key:zQ3shYZd89t3Lke7N9NbDs8fcaTyAm7fAux3aVjDELLLSVmVQ", 14 + "did:key:zQ3shuK7Yt3GQoh8BoyEzSNZeWdovfAkqjjKJMUKHYC1878vm" 15 + ], 16 + "verificationMethods": {} 17 + }, 18 + "cid": "bafyreih2edkdbxj5kbuqstymemcu3r3mxj4gtgxcmuxpmj5mtnpazdocvm", 19 + "nullified": false, 20 + "createdAt": "2025-07-25T15:47:47.372Z" 21 + }, 22 + { 23 + "did": "did:plc:7iqnimg5hvigsckpbqrqktoh", 24 + "operation": { 25 + "sig": "8kZqyCA19y3ORbAS88hOBzzKqe-QB8c_WtTfJTlrxxcvQRJ9KauzBVa70pIPR62m_aNc7kH50MKXjw9N0MWWmQ", 26 + "prev": "bafyreih2edkdbxj5kbuqstymemcu3r3mxj4gtgxcmuxpmj5mtnpazdocvm", 27 + "type": "plc_operation", 28 + "services": {}, 29 + "alsoKnownAs": [ 30 + "at://op1" 31 + ], 32 + "rotationKeys": [ 33 + "did:key:zQ3shVec44WTCPYCMZb4Ka5ajjDKKLmfjWUxyRptht4knaMq8", 34 + "did:key:zQ3shmXuxJHxmy5SsXSdg3EWLsbrD4gztdU3TDhNq5QM7HyZr" 35 + ], 36 + "verificationMethods": {} 37 + }, 38 + "cid": "bafyreidfdc7xu5wvkyq2dwr6fuierhb7ybcdc3dnfmzf5u22hukttm75iq", 39 + "nullified": false, 40 + "createdAt": "2025-07-25T15:47:47.749Z" 41 + }, 42 + { 43 + "did": "did:plc:7iqnimg5hvigsckpbqrqktoh", 44 + "operation": { 45 + "sig": "0nvoKq0K3s6mcEutE4kZG9VhG7ptabCaxI_Hf9MdbuV0h00TBrYyDwa5VZGR334P_1Mhidnq9zMVb3o_XZ-PGg", 46 + "prev": "bafyreidfdc7xu5wvkyq2dwr6fuierhb7ybcdc3dnfmzf5u22hukttm75iq", 47 + "type": "plc_tombstone" 48 + }, 49 + "cid": "bafyreidsszc2f6szlqfw3gsud7o7prfgihwuefyl7hlihgopnex44owwdy", 50 + "nullified": true, 51 + "createdAt": "2025-07-25T15:47:48.128Z" 52 + }, 53 + { 54 + "did": "did:plc:7iqnimg5hvigsckpbqrqktoh", 55 + "operation": { 56 + "sig": "RG7_y1y8d1vx_z_DHrmPVBaN_SBoFx_PJMaLAzkx2J1Y7a44USpLhFelEDupo3Kn90H447eWIoQWUFM1splTiQ", 57 + "prev": "bafyreidfdc7xu5wvkyq2dwr6fuierhb7ybcdc3dnfmzf5u22hukttm75iq", 58 + "type": "plc_operation", 59 + "services": {}, 60 + "alsoKnownAs": [ 61 + "at://op3" 62 + ], 63 + "rotationKeys": [ 64 + "did:key:zQ3shWWPEB81oEwNJ1mCVfvMPNFsdsat9u9FmGFteyjNAHh2A", 65 + "did:key:zQ3shXhSnMgAtK7JoaLAgEYf1aWqgXwwSfVC2Nx2WRCGGp6Si" 66 + ], 67 + "verificationMethods": {} 68 + }, 69 + "cid": "bafyreidmokii52hymid6kt3u7npdd5opfndn7asvt2ckwoe7g5osjw5jba", 70 + "nullified": false, 71 + "createdAt": "2025-07-25T15:47:48.511Z" 72 + } 73 + ]
+33
plc/interop_tests/audit_log/valid/log_tombstone.json
··· 1 + [ 2 + { 3 + "did": "did:plc:6adr3q2labdllanslzhqkqd3", 4 + "operation": { 5 + "sig": "ZznbxHinpBI3NgEYWzXUXLA65s2U1ezJooreZlscHYQRfd4EMlijqhpikGMabs-81Tsy4Mt9Iscpmk7Uz13aAg", 6 + "prev": null, 7 + "type": "plc_operation", 8 + "services": {}, 9 + "alsoKnownAs": [ 10 + "at://op0" 11 + ], 12 + "rotationKeys": [ 13 + "did:key:zQ3shmWf4f6ZwzNyjUDYw4oFQjgHWZoYZDjJYtz75YfYYrphB", 14 + "did:key:zQ3shr2PwdoF6kbuYYymyZq6YbGWShiTXAkdMioCPdSdPL9NV" 15 + ], 16 + "verificationMethods": {} 17 + }, 18 + "cid": "bafyreihqa4o4gsyai22ydms6j4cua6yr6tuc7trq2temvnycadk2daniru", 19 + "nullified": false, 20 + "createdAt": "2025-07-25T15:50:58.440Z" 21 + }, 22 + { 23 + "did": "did:plc:6adr3q2labdllanslzhqkqd3", 24 + "operation": { 25 + "sig": "iKFOLyEujXdHWujAAnwFhKfb9kkIcpMDGOV15eNWqlxLM2GtdQD3lVWCshTKWi0dF7InXGseVFDexS0kg0KmEQ", 26 + "prev": "bafyreihqa4o4gsyai22ydms6j4cua6yr6tuc7trq2temvnycadk2daniru", 27 + "type": "plc_tombstone" 28 + }, 29 + "cid": "bafyreifafe44xylxagcom47xb3lrya5pysu3b7zfjmmmitiuimhmgmerze", 30 + "nullified": false, 31 + "createdAt": "2025-07-25T15:50:58.837Z" 32 + } 33 + ]
+255
plc/operation_validator_test.go
··· 1 + package plc_test 2 + 3 + import ( 4 + "embed" 5 + "encoding/json" 6 + "io/fs" 7 + "testing" 8 + "time" 9 + 10 + "github.com/bluesky-social/indigo/atproto/syntax" 11 + "github.com/did-method-plc/go-didplc" 12 + "github.com/stretchr/testify/require" 13 + "tangled.org/gbl08ma.com/didplcbft/plc" 14 + "tangled.org/gbl08ma.com/didplcbft/testutil" 15 + ) 16 + 17 + //go:embed interop_tests 18 + var interopFS embed.FS 19 + 20 + // TestInteropValidLogs tests that valid interop test logs are accepted 21 + func TestInteropValidLogs(t *testing.T) { 22 + validDir, err := fs.Sub(interopFS, "interop_tests/audit_log/valid") 23 + require.NoError(t, err) 24 + 25 + entries, err := fs.ReadDir(validDir, ".") 26 + require.NoError(t, err) 27 + 28 + ctx := t.Context() 29 + txFactory, _, _ := testutil.NewTestTxFactory(t) 30 + testPLC := plc.NewPLC() 31 + 32 + for _, entry := range entries { 33 + if entry.IsDir() { 34 + continue 35 + } 36 + testName := entry.Name() 37 + 38 + t.Run(testName, func(t *testing.T) { 39 + content, err := fs.ReadFile(validDir, testName) 40 + require.NoError(t, err) 41 + 42 + var testcase []didplc.LogEntry 43 + err = json.Unmarshal(content, &testcase) 44 + require.NoError(t, err) 45 + 46 + for _, logEntry := range testcase { 47 + at := syntax.Datetime(logEntry.CreatedAt).Time() 48 + readTx := txFactory.ReadWorking(at) 49 + writeTx, err := readTx.Upgrade() 50 + require.NoError(t, err) 51 + 52 + opBytes, err := json.Marshal(&logEntry.Operation) 53 + require.NoError(t, err) 54 + 55 + err = testPLC.ValidateOperation(ctx, readTx, logEntry.DID, opBytes) 56 + require.NoError(t, err, "validation should pass for valid interop test case") 57 + 58 + err = testPLC.ExecuteOperation(ctx, writeTx, logEntry.DID, opBytes) 59 + require.NoError(t, err, "execution should pass for valid interop test case") 60 + 61 + err = writeTx.Commit() 62 + require.NoError(t, err) 63 + } 64 + }) 65 + } 66 + } 67 + 68 + // TestInteropInvalidLogs tests that invalid interop test logs are rejected 69 + func TestInteropInvalidLogs(t *testing.T) { 70 + invalidDir, err := fs.Sub(interopFS, "interop_tests/audit_log/invalid") 71 + require.NoError(t, err) 72 + 73 + entries, err := fs.ReadDir(invalidDir, ".") 74 + require.NoError(t, err) 75 + 76 + for _, entry := range entries { 77 + if entry.IsDir() { 78 + continue 79 + } 80 + testName := entry.Name() 81 + t.Run(testName, func(t *testing.T) { 82 + content, err := fs.ReadFile(invalidDir, testName) 83 + require.NoError(t, err) 84 + 85 + var testcase []didplc.LogEntry 86 + err = json.Unmarshal(content, &testcase) 87 + require.NoError(t, err) 88 + 89 + ctx := t.Context() 90 + txFactory, _, _ := testutil.NewTestTxFactory(t) 91 + testPLC := plc.NewPLC() 92 + 93 + var lastErr error 94 + for _, logEntry := range testcase { 95 + at := syntax.Datetime(logEntry.CreatedAt).Time() 96 + readTx := txFactory.ReadWorking(at) 97 + writeTx, err := readTx.Upgrade() 98 + require.NoError(t, err) 99 + 100 + opBytes, err := json.Marshal(&logEntry.Operation) 101 + require.NoError(t, err) 102 + 103 + err = testPLC.ValidateOperation(ctx, readTx, logEntry.DID, opBytes) 104 + if err != nil { 105 + lastErr = err 106 + break 107 + } 108 + 109 + err = testPLC.ExecuteOperation(ctx, writeTx, logEntry.DID, opBytes) 110 + if err != nil { 111 + lastErr = err 112 + // For invalid logs, we expect an error at some point 113 + break 114 + } 115 + 116 + err = writeTx.Commit() 117 + require.NoError(t, err) 118 + } 119 + 120 + require.Error(t, lastErr, "invalid interop test case should be rejected") 121 + }) 122 + } 123 + } 124 + 125 + // TestOperationValidatorInvalidSignature tests that invalid signatures are rejected 126 + func TestOperationValidatorInvalidSignature(t *testing.T) { 127 + ctx := t.Context() 128 + txFactory, _, _ := testutil.NewTestTxFactory(t) 129 + validator := plc.NewV0OperationValidator() 130 + 131 + // Create a test operation with invalid signature 132 + testOp := `{ 133 + "sig": "INVALID_SIGNATURE_THAT_IS_NOT_BASE64", 134 + "prev": null, 135 + "type": "plc_operation", 136 + "services": {}, 137 + "alsoKnownAs": [], 138 + "rotationKeys": ["did:key:zQ3shhguVfzmkfgXHzrnSeDxzbAvw7NjiVUcu2nmkeiQUrZUM"], 139 + "verificationMethods": {} 140 + }` 141 + 142 + timestamp := time.Now() 143 + readTx := txFactory.ReadWorking(timestamp) 144 + ts := syntax.Datetime(timestamp.Format("2006-01-02T15:04:05.000Z")) 145 + _, err := validator.Validate(ctx, readTx, ts, "did:plc:test", []byte(testOp), false) 146 + require.Error(t, err, "invalid signature should be rejected") 147 + } 148 + 149 + // TestOperationValidatorMissingPrevInTombstone tests that tombstone operations require a prev 150 + func TestOperationValidatorMissingPrevInTombstone(t *testing.T) { 151 + ctx := t.Context() 152 + txFactory, _, _ := testutil.NewTestTxFactory(t) 153 + validator := plc.NewV0OperationValidator() 154 + 155 + // Create a tombstone operation without prev 156 + tombstoneOp := `{ 157 + "sig": "test", 158 + "prev": null, 159 + "type": "plc_tombstone" 160 + }` 161 + 162 + timestamp := time.Now() 163 + readTx := txFactory.ReadWorking(timestamp) 164 + ts := syntax.Datetime(timestamp.Format("2006-01-02T15:04:05.000Z")) 165 + _, err := validator.Validate(ctx, readTx, ts, "did:plc:test", []byte(tombstoneOp), false) 166 + require.Error(t, err, "tombstone without prev should be rejected") 167 + require.Contains(t, err.Error(), "prev", "error should mention prev") 168 + } 169 + 170 + // TestOperationValidatorGenesisCannotBeTombstone tests that genesis operations cannot be tombstone 171 + func TestOperationValidatorGenesisCannotBeTombstone(t *testing.T) { 172 + ctx := t.Context() 173 + txFactory, _, _ := testutil.NewTestTxFactory(t) 174 + validator := plc.NewV0OperationValidator() 175 + 176 + // Create a genesis tombstone operation (should fail since DID doesn't exist yet) 177 + tombstoneOp := `{ 178 + "sig": "test", 179 + "prev": null, 180 + "type": "plc_tombstone" 181 + }` 182 + 183 + timestamp := time.Now() 184 + readTx := txFactory.ReadWorking(timestamp) 185 + ts := syntax.Datetime(timestamp.Format("2006-01-02T15:04:05.000Z")) 186 + _, err := validator.Validate(ctx, readTx, ts, "did:plc:test", []byte(tombstoneOp), false) 187 + require.Error(t, err, "genesis tombstone should be rejected") 188 + } 189 + 190 + // TestOperationValidatorInvalidPrev tests that operations with invalid prev are rejected 191 + func TestOperationValidatorInvalidPrev(t *testing.T) { 192 + ctx := t.Context() 193 + txFactory, _, _ := testutil.NewTestTxFactory(t) 194 + validator := plc.NewV0OperationValidator() 195 + 196 + // First create a valid genesis operation 197 + genesisOp := `{ 198 + "sig": "kWGr1tLkLeFbi2sM1ECfIXVLE11U8F_2JbIjP6SO504Z2YVilrvr37AgtsfcPww_EuBacJoJDYbmwVTRoz3cPQ", 199 + "prev": null, 200 + "type": "plc_operation", 201 + "services": {}, 202 + "alsoKnownAs": [], 203 + "rotationKeys": ["did:key:zDnaet3CLvFwasyRn1qaCJec6XDqh2pXE4fUoJpG19H9xauUf"], 204 + "verificationMethods": {} 205 + }` 206 + 207 + timestamp := time.Now() 208 + readTx := txFactory.ReadWorking(timestamp) 209 + ts := syntax.Datetime(timestamp.Format("2006-01-02T15:04:05.000Z")) 210 + effects, err := validator.Validate(ctx, readTx, ts, "did:plc:7az4y43gpd5iz3uz5aoiimpn", []byte(genesisOp), false) 211 + require.NoError(t, err) 212 + require.Equal(t, "did:plc:7az4y43gpd5iz3uz5aoiimpn", effects.NewLogEntry.DID) 213 + 214 + // Now try to add an operation with wrong prev 215 + updateOp := `{ 216 + "sig": "test", 217 + "prev": "bafyreihygpghgzty7kgo5gpidscdd3p7krovysfy76uqfyildy5qy6xyqi", 218 + "type": "plc_operation", 219 + "services": {}, 220 + "alsoKnownAs": [], 221 + "rotationKeys": [], 222 + "verificationMethods": {} 223 + }` 224 + 225 + // Try with a wrong prev CID 226 + readTx = txFactory.ReadWorking(timestamp.Add(time.Minute)) 227 + ts = syntax.Datetime(timestamp.Add(time.Minute).Format("2006-01-02T15:04:05.000Z")) 228 + _, err = validator.Validate(ctx, readTx, ts, "did:plc:7az4y43gpd5iz3uz5aoiimpn", []byte(updateOp), false) 229 + require.Error(t, err, "operation with wrong prev should be rejected") 230 + } 231 + 232 + // TestOperationValidatorMissingRotationKeys tests that operations without rotation keys fail validation 233 + func TestOperationValidatorMissingRotationKeys(t *testing.T) { 234 + ctx := t.Context() 235 + txFactory, _, _ := testutil.NewTestTxFactory(t) 236 + validator := plc.NewV0OperationValidator() 237 + 238 + // Create an operation with empty rotation keys (not for genesis) 239 + op := `{ 240 + "sig": "EJmgtA4PoFz6sXWz324x4f-46PbQJV0NR16_B69fbWVJb40wSBAakxJKUqvAQnvgMMZnWiL8LUdyEgFejSwdKA", 241 + "prev": "bafyreihygpghgzty7kgo5gpidscdd3p7krovysfy76uqfyildy5qy6xyqi", 242 + "type": "plc_operation", 243 + "services": {}, 244 + "alsoKnownAs": [], 245 + "rotationKeys": [], 246 + "verificationMethods": {} 247 + }` 248 + 249 + timestamp := time.Date(2025, 7, 22, 16, 44, 8, 0, time.UTC) 250 + readTx := txFactory.ReadWorking(timestamp) 251 + ts := syntax.Datetime(timestamp.Format("2006-01-02T15:04:05.000Z")) 252 + _, err := validator.Validate(ctx, readTx, ts, "did:plc:7az4y43gpd5iz3uz5aoiimpn", []byte(op), false) 253 + // This should fail because non-genesis operations need valid rotation keys 254 + require.Error(t, err, "non-genesis operation without rotation keys should be rejected") 255 + }