sdk(P1.5): on-chain anchor verification with zero heavy dependencies
verify_anchor_onchain / verifyAnchorOnchain / VerifyAnchorOnchain check an anchor epoch against the chain itself in all three SDKs: one raw JSON-RPC eth_call to the anchoring contract's getCommitment(batchId) comparing the on-chain merkle root with the anchor's merkle_root, plus one eth_getTransactionReceipt confirming status == 0x1 in the expected block. The customer chooses the RPC endpoint — nothing asks Attesto to confirm Attesto, and no web3/ethers dependency is added anywhere. The getCommitment(string) selector (keccak256 first 4 bytes = a7b09e2a) is pinned as a constant with the dynamic-string ABI encoding done manually; a worked calldata example (computed once against web3 keccak) is asserted in all three test suites, and APSProvenance.abi.json is copied into each SDK's testdata with a test that flags the pinned selector for review if the ABI's getCommitment signature ever changes. The contract address is read from the anchor epoch's hashed payload (payload.contract_address). Mocked-RPC tests cover match / root-mismatch / failed-tx / wrong-block / missing-fields in each language with identical problem strings; a live test against the production contract runs only when ATTESTO_LIVE_RPC_URL is set. Go CLI gains `attesto anchors verify <id> --rpc-url <url>` (API fetch + on-chain check in one step; existing get/remote-verify behavior unchanged). READMEs updated per SDK. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,7 @@ import (
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
attesto "git.rotz.ai/rotzmediagroup/attesto-v1/sdk/go"
|
||||
"git.rotz.ai/rotzmediagroup/attesto-v1/sdk/go/connectorkit"
|
||||
@@ -124,7 +125,7 @@ func (a *app) dispatch(ctx context.Context, args []string) error {
|
||||
case "witnesses":
|
||||
return a.witnesses(ctx, args[1:])
|
||||
case "anchors":
|
||||
return a.verifyableObject(ctx, "anchors", args[1:], attesto.VerifyAnchor, "/v2/anchors/")
|
||||
return a.anchors(ctx, args[1:])
|
||||
case "bundles":
|
||||
return a.bundles(ctx, args[1:])
|
||||
case "verify":
|
||||
@@ -410,6 +411,54 @@ func (a *app) verifyableObject(ctx context.Context, group string, args []string,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *app) anchors(ctx context.Context, args []string) error {
|
||||
// `anchors verify <anchor_epoch_id> --rpc-url <url>` chains an API fetch
|
||||
// with the on-chain check (eth_call getCommitment + tx receipt) against a
|
||||
// customer-chosen RPC endpoint. Without --rpc-url, all subcommands keep the
|
||||
// existing get / remote-verify behavior.
|
||||
if len(args) > 0 && args[0] == "verify" {
|
||||
rest := args[1:]
|
||||
positional := ""
|
||||
if len(rest) > 0 && !strings.HasPrefix(rest[0], "-") {
|
||||
positional, rest = rest[0], rest[1:]
|
||||
}
|
||||
fs := flag.NewFlagSet("anchors verify", flag.ContinueOnError)
|
||||
fs.SetOutput(a.err)
|
||||
id := fs.String("id", positional, "anchor epoch id")
|
||||
rpcURL := fs.String("rpc-url", "", "JSON-RPC endpoint for the anchor's chain")
|
||||
timeoutS := fs.Int("timeout-s", 15, "RPC timeout in seconds")
|
||||
file := fs.String("file", "", "proof object JSON file (remote verify mode)")
|
||||
publicKeyHex := fs.String("public-key-hex", "", "Ed25519 public key hex (remote verify mode)")
|
||||
if err := fs.Parse(rest); err != nil {
|
||||
return err
|
||||
}
|
||||
if *rpcURL == "" {
|
||||
fallback := []string{}
|
||||
if *file != "" {
|
||||
fallback = append(fallback, "--file", *file)
|
||||
}
|
||||
if *publicKeyHex != "" {
|
||||
fallback = append(fallback, "--public-key-hex", *publicKeyHex)
|
||||
}
|
||||
return a.remoteVerify(ctx, fallback, attesto.VerifyAnchor)
|
||||
}
|
||||
if *id == "" {
|
||||
return errors.New("anchor epoch id is required (positional or --id)")
|
||||
}
|
||||
client, err := a.systemClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
anchor, err := client.GetAnchorEpoch(ctx, *id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
report := attesto.VerifyAnchorOnchain(ctx, anchor, *rpcURL, time.Duration(*timeoutS)*time.Second)
|
||||
return a.write(report)
|
||||
}
|
||||
return a.verifyableObject(ctx, "anchors", args, attesto.VerifyAnchor, "/v2/anchors/")
|
||||
}
|
||||
|
||||
func (a *app) checkpoints(ctx context.Context, args []string) error {
|
||||
if len(args) == 0 {
|
||||
return errors.New("checkpoints subcommand required")
|
||||
|
||||
Reference in New Issue
Block a user