What you are checking
Three claims, in order. Each is independently falsifiable:1
The body is what was signed
Re-serialize the canonical body under the rules below and SHA-256 it. If your digest
equals
canonical.hash, the body has not been altered by so much as a byte since
signing.2
MicroCrop signed it
Recover the secp256k1 signer from
canonical.signature over that digest. If it equals
canonical.signer — and that address is the signer MicroCrop published to you out of
band — the determination is ours.3
It predates the dispute
If
notary is present, confirm the anchor transaction on the notary ledger. It commits
the same hash at a time nobody can move.Get the artifact
canonical is null, the record is a legacy EVM-only determination and cannot be
independently re-verified this way — verification.available will be false with a
reason. Any determination issued under the current schema carries a canonical block, and
the API’s determined.evidence.verifiable flag tells you before you fetch.
The canonical form
The body is serialized under a deliberately strict, portable subset of RFC 8785 (JCS). The subset exists so that two independent implementations cannot disagree:
The body additionally carries no chain domain and no USDC amount —
domain,
chainId, verifyingContract, and any key ending in usdc are structurally forbidden
anywhere in the tree. Assert that yourself: a determination that names a chain or a
settlement currency is not the artifact you were sold.
The signature
The signer signs the 32-byte SHA-256 digest as a raw digest — there is no EIP-191\x19Ethereum Signed Message prefix and no EIP-712 domain. So recovery is exactly:
recoverAddress("0x" + hash, signature). The result is a checksummed
address; compare case-insensitively.
Conformance vector
Pin this in your own test suite. If your implementation reproduces it byte-for-byte, it agrees with ours.assessedAt first and subject last, because the
keys are sorted — not the order they appear in the body above. Inside provenance, the
embedded JSON in paramsJson is a string, escaped as one; it is not re-canonicalized.
Key ordering is a plain lexicographic sort over the key strings. JavaScript sorts by
UTF-16 code unit and Python by Unicode code point; these differ only for characters
outside the Basic Multilingual Plane. Every key in the MicroCrop schema is ASCII, so the
two agree — but if you extend the schema, keep keys ASCII.
Runnable reproducer
Node 18+ andnpm i ethers. This is the whole verifier — about 50 lines, no MicroCrop
dependency, no network:
verify-determination.mjs
Test it against the vector
Save this asvector.json and run the reproducer on it. It is signed with a public,
well-known test key — it is a conformance fixture, not a MicroCrop determination, and
the signer address below is deliberately not one of ours:
vector.json
"4500" to "4501" and re-run. The hash comparison
must fail. If it passes, your canonicalizer is wrong.
The hash check in other languages
Step 1 needs only a JSON parser and SHA-256 — no crypto library, no dependencies:eth_keys or coincurve over the same 32-byte digest —
again with no message prefix.
Verifying the amount owed
The amount is derived, not asserted, so you can reproduce it from the policy you yourself sold:sumInsuredMinorispolicy.sumInsuredin minor units ofpolicy.currency(KES and GHS have exponent 2, so KES 100,000.00 →10000000).- Multiply before dividing, and use integer arithmetic throughout — a
BigInt, aDecimal, anint, never a float. - Truncate toward zero. Never round half-up; there is no tie-break rule to get wrong.
result.payoutAmount.amountMinor. Your derivation, the API’s settlement.amountOwed and
that signed figure should all agree. If the API’s derived figure disagrees with the signed
one, the response carries settlement.amountOwedDiscrepancy and you should stop and
reconcile before paying anyone.
What verification does and does not establish
It establishes that the determination body has not been altered since signing, that it was signed by the key MicroCrop published, that the inputs and their source are named, and — with the notary anchor — that it existed at a stated time. It does not establish that the underlying weather data is correct, that the methodology is appropriate for the risk, or that the amount is legally owed to the farmer. Those are questions about the source, the product design, and your contract respectively. The provenance block is what lets you attack the first of those on your own terms: re-fetchprovenance.url, apply provenance.paramsJson, recompute the index, and compare against
the evidence inputs in the signed body. If they disagree, you have found something real
and we want to hear about it.
[PLACEHOLDER — FOR COUNSEL] Any statement about the evidentiary weight of a
determination in a given jurisdiction, or about its admissibility in a dispute with a
policyholder or a regulator, must be drafted by legal counsel. This page describes what
the artifact cryptographically is, not what it legally means.
Determinations reference
Every field in the determination response.
Service tiers
Why the artifact, not the payout, is the product.