D30-F05-A06 / Released engineering topic

Signed Ledger Checkpoints: Verify Exact Bytes, Key Authority and Retained Evidence Separately

Build strict Ed25519 checkpoint verification with exact C/E framing, independently trusted key policy, retained-copy comparison and real Python/TypeScript/browser tests.

Signed Ledger Checkpoints: Verify Exact Bytes, Key Authority and Retained Evidence SeparatelyD30 / D30-F05

A valid signature answers a narrow question: do these bytes verify under this public key? It does not establish that the key was authorized for this ledger, that the checkpoint matches an independently retained copy, or that its signed ledger history is financially correct.

You will build a verifier that keeps those questions visible. It authenticates exact checkpoint bytes, evaluates a supplied current key policy and compares the entire payload with independent expected evidence. You will also re-sign an edited checkpoint and see the signature turn valid while retained correspondence still fails.

The Cedar ledger and trust receipts are synthetic. The signing seeds used here are published RFC test fixtures and are public to everyone. They are never production credentials.

Start with the checkpoint, not a green badge

Hash-Chained Journal and Merkle-Batched Journal Commitment establish content commitments. This topic adds signer authentication and explicit authority policy. It does not rerun those history checks inside the signature function.

The checkpoint has eleven exact fields: version, checkpoint ID, stream, ledger, entity, creation claim, ending journal sequence, group count, chain head, Merkle root and Merkle size. The shared Cedar vector describes three complete groups containing four journals. Its correction group contains two journals, so group count and sequence are different quantities.

JSON
{
  "schema_version": "d30-integrity-checkpoint-v1",
  "checkpoint_id": "CHECKPOINT-4",
  "stream_id": "CB-GENERAL-EXPORT-V1",
  "ledger_id": "CB-GENERAL",
  "entity_id": "CB-A",
  "created_at": "2026-10-24T12:00:00.000Z",
  "through_sequence": "4",
  "group_count": "3",
  "chain_head": "8af37be6b7835169129ae57b561d98c0fe36e231921c76029f5aff8c9540beea",
  "merkle_root": "34ad17bffd657e7227adb7083eb1be5d0a7f9bcd5ba6b404313dce2242393192",
  "merkle_size": "3"
}

These fields are bounded and structurally checked. Counts are canonical decimal strings, hashes are lowercase 64-character hex and clocks are real Gregorian UTC milliseconds. Unknown fields reject, so an unsigned trust label cannot quietly accompany the payload. The source adapter must separately establish that the claimed sequence ends a complete prefix and that group receipts precede checkpoint creation.

Freeze the bytes before signing

Our authored framing uses a four-byte unsigned big-endian length U(n). A string S(t) contains U(number of UTF-8 octets), followed by those exact octets. The checkpoint byte string C begins with the ASCII tag D30-INTEGRITY-CHECKPOINT-V1 and one zero octet, then S of each field in its frozen order.

This is a versioned D30 encoding, not canonical JSON or a Certificate Transparency protocol implementation. JSON property delivery order does not matter. No pretty-printing, locale, trimming or Unicode normalization chooses the bytes. Checkpoint IDs are restricted to the shared ASCII profile; the shared framing vectors separately exercise multi-byte strings and normalization differences.

Pure Ed25519 signs C itself. Passing SHA256(C) as the message would sign a different message. Ed25519ctx and Ed25519ph are also different variants and are outside this envelope. RFC 8032 defines the primitive and supplies the published vectors used in the tests. RFC 8032.

The 300-byte checkpoint C is signed directly; the 524-byte envelope E is the anchor content

Open the full-size signed-byte and envelope figure.

The envelope E frames its schema, key ID, algorithm, C's length and bytes, and the lowercase signature hex. Ed25519's 64 signature bytes become 128 hex characters in this wire. The independent vector has C length 300 and E length 524 bytes.

The anchor content digest is:

Plain text
SHA256(E) =
8c3d6bb3a098a957a5b555064cda453528aff09995a63a944fd902076f523972

Changing the key label changes E even if C and its signature remain identical. Hashing C alone, pretty JSON, a bare chain digest or a storage ETag cannot be substituted for this envelope digest. Whether an external service actually retained E is another check in External WORM/Blockchain Anchor.

Use a strict, declared Ed25519 profile

Calling an API named Ed25519 is not a complete validation policy. During this package's investigation, the installed cryptography 40.0.2/OpenSSL, Node/OpenSSL and Chromium WebCrypto backends all accepted an identity-public-key forgery in isolation. The W3C draft documents that implementations differ on small-order checks. This concrete test prompted an additional vetted validation layer; it is not a claim that every Ed25519 implementation behaves identically. W3C Web Cryptography Level 2, section 25.

The executable strict-main-subgroup-ed25519-v1 profile requires canonical nonidentity main-subgroup public key A and signature point R. Python uses pinned PyNaCl 1.6.2's patched libsodium point validator, then detached signature verification. TypeScript uses pinned noble-curves 2.4.0 strict point parsing, subgroup and small-order checks, strict zip215: false verification, and actual WebCrypto verification of C. We write no curve arithmetic or homemade small-order blocklist.

The dependency versions matter. PyNaCl 1.6.2 bundles the stable libsodium build that fixes the earlier mixed-order point-validation issue. An unpatched library with a similar version number is not equivalent. The official release and maintainer's explanation establish the patch provenance. PyNaCl release, libsodium maintainer explanation. The TypeScript profile uses the pinned library's public validation methods and current release. noble-curves 2.4.0.

This stronger profile rejects identity, small-order, mixed-order and noncanonical A/R encodings, and invalid scalar/equation cases. It supports general ordinary public keys, including a tested third generated key; verification is not limited to the two RFC demo keys. Missing required cryptography remains indeterminate, and TypeScript cannot silently pass using a weaker or absent WebCrypto backend.

Separate signature mathematics from current authority

The logical API takes three data inputs:

Python
result = verify_signed_checkpoint(request, envelope, trust_registry)
TypeScript
const result = await verifySignedCheckpoint(request, envelope, trustRegistry);

The request pins stream, ledger, entity, verification time, current-policy profile and an independently expected checkpoint. The trusted registry supplies scoped key IDs, raw public keys, validity intervals, revocation times and its observation receipt. A public key embedded in the submitted envelope is rejected as an unknown field.

Registry trust comes from the host's independent enrollment and policy channel. A caller cannot create that trust merely by supplying strings such as “authorized key” or “independent receipt.” The API checks consistent supplied facts; it does not provision a trust store or authenticate its transport.

This tutorial chooses a conservative current-trust-at-verification policy. The selected key must be authorized at both the requested verification time and the checkpoint's claimed created_at. The interval includes valid_from and excludes valid_until. A revocation at verification equality denies; a future revocation does not apply early. The supplied registry must cover the requested policy observation.

A retired or revoked key can therefore fail current policy even if it might have legitimately signed an older checkpoint. The authority failure is distinct from an invalid mathematical signature. The signed created_at remains the signer's assertion. It cannot independently prove when signing occurred or reconstruct historical revocation knowledge.

Three independent checks preserve signature, current key authority and retained expected binding

Open the full-size layered-evidence figure.

Keep missing evidence different from failed evidence

The complete result preserves three named checks, each with its own status and code:

LayerExample passDefinite failureMissing or unsupported evidence
SignatureStrict valid equation on exact CInvalid signature or public pointMissing key material or required crypto
AuthoritySupplied scoped current key policy authorizesWrong scope, known revocation or validity denialMissing/malformed registry, unknown key or stale observation
BindingEvery submitted checkpoint field equals independent expectedDifferent full payload or request scope; future creation claimExpected checkpoint absent

A verifiable invalid signature remains a failure even if another layer is missing. A valid signature with no expected copy remains overall indeterminate for retained correspondence. All three passes yield TRUSTED_CHECKPOINT_BOUND: exact signed payload authentication under the supplied trusted key policy plus expected-payload equality. The code name does not mean full ledger verification.

Structural failures return no partially validated checkpoint or framing. Otherwise, the result includes exact C and E hex and SHA256(E), plus detached checkpoint/key views. The full data contract specifies every field, boundary and deterministic precedence. The shared byte contract defines the cross-topic C/E wire.

Re-sign a rewrite and watch binding fail

Start with the independent Cedar fixture: signature passes, authority passes and retained binding passes. Change the signed chain head to another well-formed digest while keeping the original signature. Signature and expected binding fail.

Now use the public demo seed to re-sign the changed C. Signature mathematics passes again; retained binding still fails because the independent expected checkpoint was preserved. This is the distinction a one-badge demo would hide. A malicious authorized signer can generate a new valid signature over changed history. Independent retention or another separately trusted observation is needed to expose that disagreement.

Open the guided signed-checkpoint lab. It retains all eleven fields and all three checks across twelve scenario families. Change any signed field, inspect a known revoked key, remove expected evidence, rotate to an authorized key or simulate unavailable WebCrypto. Step changes the inspected layer, not which evidence the verifier evaluates. The re-sign button calls actual WebCrypto with a public fixture seed.

Reproduce the evidence, including the dependencies

The package includes pinned installation and bundle instructions, Python requirements, npm lock, observed artifact hashes and upstream licenses. The standalone lab embeds the actual TypeScript/noble bundle and requires no CDN. The repository's task-local installation preserves global and project manifests; a separately downloaded module can use its own environment with the same declared dependencies.

There are 88 independently authored complete expected outputs. The first 71 preceded code; 17 later independent regressions add scalar boundaries, mixed-order R, a general generated key and strict clock/wire edges. Python and TypeScript agree on all output branches, errors, source scope, key views, layer conclusions and full framing. Root-built C/E vectors match byte for byte. Published RFC 8032 tests verify raw signing and verification separately from our authored framing.

The actual browser also executed all 88 full fixtures and both RFC vectors. Guided checks covered 108 layer observations and 30 signed-field mutations at 390, 768 and 1440 pixels, including actual re-signing, async cancellation, controls, reset, keyboard, reduced motion and touch targets. All 66 available-backend captured lab outputs match Python; three additional captures deliberately simulate missing WebCrypto and stay indeterminate. Validation record and commands.

Carry the conclusion into the next layer

A signed checkpoint can authenticate false accounting, an incomplete source prefix or an internally consistent rewrite if the surrounding independent evidence is wrong or absent. This verifier does not claim otherwise. Keep the chain, Merkle, coverage, accounting, key authority, retained payload and external-anchor results separate when building End-to-End Ledger Integrity Verification.

References and provenance

The primary source roles distinguish RFC primitive definitions, draft WebCrypto APIs, patched library behavior and authored D30 policy. Historical deployment evidence is not useful for this executable example without independently retained bytes and key-policy history. RFC vectors are cryptographic test fixtures; Cedar is synthetic. No production keys, external writes, independent timestamp or encoded/published video are created by this package.

ReferencesPrimary sources and evidence notes

Expand the source trail, evidence role, and limitations behind the engineering choices.

Accessed: 25 October 2026. All Cedar ledger/group/checkpoint/trust evidence is synthetic. Public cryptographic seeds are official fixtures, never credentials. These references support primitives and library behavior; the D30 wire, trust policy, diagnostics and resource bounds are authored choices.

Primary sourceVersion / directly read roleBoundary
RFC8032January 2017 informational; §§3–5 pure Ed25519, §7.1 TEST1/TEST2, §8 considerationsNo D30 framing/trust-store/timestamp policy claimed as an RFC standard
Verified RFC8032 errataEID5968 verified27 January 2026 aligns scalar endpoints0<=S<L with §5.1.7No custom scalar or curve primitive written here
W3C Web Cryptography Level222 April 2025 First Public Working Draft §25: Ed25519 sign/verify/raw import and noted small-order implementation differencesDraft status retained; actual runtime testing required, no universal browser-support claim
PyNaCl1.6.2 release and official changelog1 January 2026; libsodium1.0.20-stable2025-12-31 build fixes CVE-2025-69277 valid-point mixed-order issueActual Python verification runtime pinned1.6.2; not unpatched1.0.20
libsodium point-validation API and maintainer fix explanationCanonical curve/main-subgroup/non-small-order checks; maintainer explains mixed-order bug and post 30 December 2025 stable fixVetted API called for A/R; no workaround curve arithmetic copied or implemented
noble-curves2.4.0 and versioned READMEOfficial current release, explicit Ed25519 zip215:false, Point parsing/subgroup/small-order public APIs; audit scope recorded in upstream docsActual task-local locked2.4.0 code used; upstream audit is not an audit of this tutorial
Node cryptoOfficial one-shot Ed25519 uses null algorithm; available operations depend on runtime/backendPage identifies26.12.1; actual installed Node22.22.0 separately recorded. Learner TS uses actual WebCrypto plus noble.
cryptography40.0.2 Ed25519Version-pinned raw key/sign/verify APIsDiagnostic investigation only. Actual Python implementation uses patched PyNaCl, not cryptography40.

Supports: exact pure-Ed25519 primitive and fixtures, documented import/verification APIs, current patch provenance and the explicit stronger profile. Limitations: no independently authenticated registry, source journals, custody system, real signing timestamp, non-equivocation or external retention supplied by these sources.

Historical example decision: not useful without retained bytes and independent key-policy evidence. RFC vectors are included as official test fixtures; Cedar is synthetic. The code and visual conclusions are author-derived and tested, not quoted from a standard. No full copyrighted specification is reproduced.

signed-checkpoint.ts
/** General strict-main-subgroup profile. Requires @noble/curves 2.4.0 plus
 * actual WebCrypto Ed25519. No sibling runtime imports or custom curve math. */
import {ed25519} from '@noble/curves/ed25519.js';
type Obj=Record<string,any>;
const CHECKPOINT='schema_version checkpoint_id stream_id ledger_id entity_id created_at through_sequence group_count chain_head merkle_root merkle_size'.split(' '),ENVELOPE='schema_version key_id algorithm checkpoint signature_hex'.split(' '),REQUEST='schema_version stream_id ledger_id entity_id verification_at policy expected_checkpoint'.split(' '),REGISTRY='schema_version stream_id ledger_id entity_id observed_at receipt_ref keys'.split(' '),KEY='key_id algorithm public_key_hex valid_from valid_until revoked_at'.split(' '),SCOPE=['stream_id','ledger_id','entity_id'];
const SEEDS=new Set(['9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60','4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb']);
const clone=<T>(x:T):T=>JSON.parse(JSON.stringify(x)),exact=(x:any,keys:string[]):x is Obj=>x!==null&&typeof x==='object'&&!Array.isArray(x)&&Object.keys(x).length===keys.length&&keys.every(k=>Object.hasOwn(x,k));
const full=(r:RegExp,x:any)=>typeof x==='string'&&r.exec(x)?.[0]===x,identifier=(x:any)=>full(/^[A-Za-z0-9][A-Za-z0-9._:-]{0,63}$/,x),hexwire=(x:any,n:number)=>full(new RegExp('^[0-9a-f]{'+n+'}$'),x);
function stamp(x:any):boolean{if(!full(/^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$/,x)||x.slice(0,4)==='0000')return false;const d=new Date(x);return Number.isFinite(d.getTime())&&d.toISOString()===x;}
const decimal=(x:any,max:number)=>typeof x==='string'&&x.length<=4&&full(/^(0|[1-9][0-9]*)$/,x)&&Number(x)<=max;
function checkpointOk(x:any):boolean{if(!exact(x,CHECKPOINT)||x.schema_version!=='d30-integrity-checkpoint-v1'||!['checkpoint_id',...SCOPE].every(k=>identifier(x[k]))||!stamp(x.created_at)||!['chain_head','merkle_root'].every(k=>hexwire(x[k],64))||!decimal(x.group_count,4096)||!decimal(x.merkle_size,4096)||!decimal(x.through_sequence,8192))return false;const n=Number(x.group_count),s=Number(x.through_sequence);return x.merkle_size===x.group_count&&((n===0&&s===0)||(n>0&&n<=s&&s<=2*n));}
const envelopeOk=(x:any)=>exact(x,ENVELOPE)&&x.schema_version==='d30-signed-checkpoint-v1'&&identifier(x.key_id)&&x.algorithm==='Ed25519'&&hexwire(x.signature_hex,128);
function requestOk(x:any):boolean{if(!exact(x,REQUEST)||x.schema_version!=='d30-signed-checkpoint-verification-v1'||x.policy!=='current-trust-at-verification-v1'||!SCOPE.every(k=>identifier(x[k]))||!stamp(x.verification_at))return false;const e=x.expected_checkpoint;return e===null||(checkpointOk(e)&&SCOPE.every(k=>e[k]===x[k])&&e.created_at<=x.verification_at);}
function registryOk(x:any):boolean{if(!exact(x,REGISTRY)||x.schema_version!=='d30-checkpoint-trust-registry-v1'||![...SCOPE,'receipt_ref'].every(k=>identifier(x[k]))||!stamp(x.observed_at)||!Array.isArray(x.keys)||x.keys.length>64)return false;const ids=new Set();for(const k of x.keys){if(!exact(k,KEY)||!identifier(k.key_id)||ids.has(k.key_id)||k.algorithm!=='Ed25519'||!hexwire(k.public_key_hex,64)||!stamp(k.valid_from)||!stamp(k.valid_until)||k.valid_from>=k.valid_until||(k.revoked_at!==null&&(!stamp(k.revoked_at)||k.revoked_at<k.valid_from)))return false;ids.add(k.key_id);}return true;}
const u=(n:number)=>{const b=new Uint8Array(4);new DataView(b.buffer).setUint32(0,n,false);return b;},cat=(...bs:Uint8Array[])=>{const r=new Uint8Array(bs.reduce((n,b)=>n+b.length,0));let p=0;for(const b of bs){r.set(b,p);p+=b.length;}return r;},text=(x:string)=>new TextEncoder().encode(x),s=(x:string)=>{const b=text(x);return cat(u(b.length),b);},hex=(x:Uint8Array)=>Array.from(x,n=>n.toString(16).padStart(2,'0')).join(''),unhex=(x:string)=>Uint8Array.from(x.match(/../g)!,h=>parseInt(h,16)),buffer=(x:Uint8Array):ArrayBuffer=>x.slice().buffer as ArrayBuffer;
export function encodeCheckpoint(checkpoint:any):Uint8Array{if(!checkpointOk(checkpoint))throw new Error('INVALID_CHECKPOINT');return cat(text('D30-INTEGRITY-CHECKPOINT-V1\0'),...CHECKPOINT.map(k=>s(checkpoint[k])));}
export function encodeEnvelope(envelope:any):Uint8Array{if(!envelopeOk(envelope))throw new Error('INVALID_ENVELOPE');const c=encodeCheckpoint(envelope.checkpoint);return cat(text('D30-SIGNED-CHECKPOINT-V1\0'),s(envelope.schema_version),s(envelope.key_id),s(envelope.algorithm),u(c.length),c,s(envelope.signature_hex));}
const check=(status:string,code:string)=>({status,code}),structural=(code:string)=>({status:'fail',code,scope:null,checkpoint:null,key:null,checks:Object.fromEntries(['signature','authority','binding'].map(k=>[k,check('not_evaluated','NOT_EVALUATED')])),framing:null});
function point(raw:Uint8Array):boolean{try{const p=ed25519.Point.fromBytes(raw,false);p.assertValidity();return p.isTorsionFree()&&!p.isSmallOrder();}catch{return false;}}
export async function verifySignedCheckpoint(request:any,envelope:any,trustRegistry:any,cryptoProvider:Crypto|undefined=globalThis.crypto):Promise<any>{
 if(!requestOk(request))return structural('INVALID_REQUEST');if(!envelopeOk(envelope))return structural('INVALID_ENVELOPE');if(!checkpointOk(envelope.checkpoint))return structural('INVALID_CHECKPOINT');const q=request,c=envelope.checkpoint,r=trustRegistry,cb=encodeCheckpoint(c),eb=encodeEnvelope(envelope);let digest:string|null=null;try{if(typeof cryptoProvider?.subtle?.digest==='function')digest=hex(new Uint8Array(await cryptoProvider.subtle.digest('SHA-256',buffer(eb))));}catch(error){if(!(error instanceof DOMException)||!['NotSupportedError','OperationError','SecurityError'].includes(error.name))throw error;}
 const out:any={status:'indeterminate',code:'NOT_EVALUATED',scope:Object.fromEntries([...SCOPE,'verification_at','policy'].map(k=>[k,q[k]])),checkpoint:clone(c),key:null,checks:{},framing:{checkpoint_hex:hex(cb),envelope_hex:hex(eb),envelope_sha256:digest}};
 let binding;if(SCOPE.some(k=>c[k]!==q[k]))binding=check('fail','CHECKPOINT_SCOPE_MISMATCH');else if(c.created_at>q.verification_at)binding=check('fail','CHECKPOINT_FROM_FUTURE');else if(q.expected_checkpoint===null)binding=check('indeterminate','EXPECTED_CHECKPOINT_MISSING');else if(CHECKPOINT.some(k=>c[k]!==q.expected_checkpoint[k]))binding=check('fail','EXPECTED_CHECKPOINT_MISMATCH');else binding=check('pass','EXPECTED_CHECKPOINT_MATCH');
 const validRegistry=registryOk(r),key=validRegistry?r.keys.find((k:any)=>k.key_id===envelope.key_id):null,publicKey=key?unhex(key.public_key_hex):null,validPoint=publicKey?point(publicKey):null;if(key)out.key=Object.fromEntries(['key_id','algorithm','public_key_hex'].map(k=>[k,key[k]]));
 let authority;if(r===null)authority=check('indeterminate','TRUST_REGISTRY_MISSING');else if(!validRegistry)authority=check('indeterminate','INVALID_TRUST_REGISTRY');else if(SCOPE.some(k=>r[k]!==q[k]))authority=check('fail','TRUST_SCOPE_MISMATCH');else if(!key)authority=check('indeterminate','KEY_NOT_FOUND');else if(!validPoint)authority=check('fail','INVALID_PUBLIC_KEY');else if(key.revoked_at!==null&&key.revoked_at<=q.verification_at)authority=check('fail','KEY_REVOKED');else if(!(key.valid_from<=c.created_at&&c.created_at<key.valid_until))authority=check('fail','KEY_NOT_VALID_AT_CREATED');else if(!(key.valid_from<=q.verification_at&&q.verification_at<key.valid_until))authority=check('fail','KEY_NOT_CURRENT');else if(r.observed_at<q.verification_at)authority=check('indeterminate','TRUST_SNAPSHOT_STALE');else authority=check('pass','KEY_AUTHORIZED');
 let signature;if(!key)signature=check('indeterminate','KEY_MATERIAL_MISSING');else if(!validPoint)signature=check('fail','INVALID_PUBLIC_KEY');else{const raw=unhex(envelope.signature_hex);let strict=false;try{strict=point(raw.slice(0,32))&&ed25519.verify(raw,cb,publicKey!,{zip215:false});}catch{}if(!strict)signature=check('fail','INVALID_SIGNATURE');else if(!cryptoProvider?.subtle)signature=check('indeterminate','CRYPTO_UNAVAILABLE');else try{const imported=await cryptoProvider.subtle.importKey('raw',buffer(publicKey!),'Ed25519',false,['verify']);signature=check(await cryptoProvider.subtle.verify('Ed25519',imported,buffer(raw),buffer(cb))?'pass':'fail','SIGNATURE_VALID');if(signature.status==='fail')signature.code='INVALID_SIGNATURE';}catch{signature=check('indeterminate','CRYPTO_UNAVAILABLE');}}
 out.checks={signature,authority,binding};const decisive=[binding,signature,authority].find(v=>v.status==='fail')??(digest===null?check('indeterminate','CRYPTO_UNAVAILABLE'):undefined)??[signature,authority,binding].find(v=>v.status==='indeterminate');out.status=decisive?.status??'pass';out.code=decisive?.code??'TRUSTED_CHECKPOINT_BOUND';return out;
}
export async function signDemoCheckpoint(checkpoint:any,keyId:string,publicTestSeedHex:string,cryptoProvider:Crypto|undefined=globalThis.crypto):Promise<any>{const c=encodeCheckpoint(checkpoint);if(!identifier(keyId)||!SEEDS.has(publicTestSeedHex))throw new Error('PUBLIC_DEMO_SEED_REQUIRED');if(!cryptoProvider?.subtle)throw new Error('CRYPTO_UNAVAILABLE');const seed=unhex(publicTestSeedHex),privateKey=await cryptoProvider.subtle.importKey('pkcs8',buffer(cat(unhex('302e020100300506032b657004220420'),seed)),'Ed25519',false,['sign']);const signature=new Uint8Array(await cryptoProvider.subtle.sign('Ed25519',privateKey,buffer(c)));return {schema_version:'d30-signed-checkpoint-v1',key_id:keyId,algorithm:'Ed25519',checkpoint:clone(checkpoint),signature_hex:hex(signature)};}
Full-height labsigned checkpoint labOpen full screen
Written by

Fintech engineer building market-data and financial systems, and the author of every article, glossary record, and reference implementation on The Fintech Builder.