diff --git a/src/vector/agent-network.ts b/src/vector/agent-network.ts index 89dc85c..b3cf253 100644 --- a/src/vector/agent-network.ts +++ b/src/vector/agent-network.ts @@ -14,9 +14,10 @@ const VECTOR_SUBMIT_URL = process.env.VECTOR_SUBMIT_URL || 'https://submit.vecto const VECTOR_KOIOS_URL = process.env.VECTOR_KOIOS_URL || 'https://koios.vector.testnet.apexfusion.org/'; const VECTOR_EXPLORER_URL = process.env.VECTOR_EXPLORER_URL || 'https://vector.testnet.apexscan.org'; -// Registry constants -const REGISTRY_SCRIPT_CBOR = "59058101010029800aba2aba1aba0aab9faab9eaab9dab9a48888889660033001300337540112232330010010032259800800c52845660026006601400314a3133002002300b001401480424600e60106010003223232330010010042259800800c00e2646644b30013372200e00515980099b8f0070028800c01900944cc014014c03c0110091bae3008001375660120026016002804852f5bded8c1230073008300830083008001918039804000cdc3a4005370e90002444444453001300f0089807004496600266e1d2004300b3754600e60186ea80062946294100a4888c8cc88cc008008004896600200300389919912cc004cdc8803801456600266e3c01c00a20030064049133005005301800440486eb8c044004dd69809000980a000a02433009004003148001222232980098079baa00194c004006910100a44100400d301300548896600260120071323322598009806000c4c9660026036003132598009807180b9baa0018991919912cc004c08000e0111640746eb4c074004dd7180e801180e800980c1baa0018b202c301a0018b2030301637540091598009806800c566002602c6ea801200516405d16405080a0566002601460266ea800a2646644b30013301337586034602e6ea802c8cdd7980d980c1baa0010048acc004cc89660020030028acc004c074006264b30013371e6eb8c064004016260226eb4c0680062941018180e000c00901a203414a0660226eacc040c05cdd500580244cc04cdd61809180b9baa00b25980099baf301b301837540020051598009805800c566002601f30013756602060306ea800600b003402913371290406d620498039bab30103018375400314a080b229410164528202c8a50405514a080a8dca1bb30013374a90001980b99ba548008cc05cdd480125eb80cc05d300103d87a80004bd70180b980a1baa002899912cc004006005159800980d000c4cdc39bad301630190014800600480b90170a503300e3756601a60286ea802000501218099baa002375c602c60266ea80122b3001300a003899199119912cc004c0380062b30013018375400d0028b20328acc004c03c0062b30013018375400d0028b20328b202c40582b3001300c3015375400319800980c980b1baa001912cc004c038c05cdd5000c4c8c8cc004004dd6180e980f180f180f180f180f180f180f180f180d1baa0042259800800c528456600266e3cdd7180f000801c528c4cc008008c07c0050192038375c603660306ea80062941016488c8cc00400400c896600200314c0103d87a80008992cc004c010006266e9520003301d0014bd7044cc00c00cc07c009019180e800a0369192cc004c03cc05cdd5000c4dd7180d980c1baa0018b202c301a301737540029111192cc004c044c068dd5004c4c8c8c9660026600e0246042603c6ea80222660346eb0c064c078dd500912cc004cdd79811180f9baa0010048acc004c0480062b3001301698009bab3017301f37540030038012022899b89482036b1024c038dd5980b980f9baa0018a50407514a080ea294101d4528203832598009811000c4c966002602c6eb4c07c00626eb8c0780062c80e8c0840062c80f8cc05cc9660026028603a6ea800626eacc058c078dd5180b180f1baa3021301e37540031640706600a6eb0c080c074dd5008919baf3021301e37540020140026006002660026eb0c078c06cdd5007803c5660026600801e603c60366ea801626644b30010018014566002604200313370e6eb4c074c0800052001801203c40782940cc054dd5980a180d9baa00f3002330013758603c60366ea803c01e294101920322232598009809980e1baa00189810180e9baa3015301d37546040603a6ea80062c80d8cc0100088cdd79810180e9baa001002459014180a9baa00430170013017301800130133754009164044808860246026008452689b2b200201"; -const REGISTRY_POLICY_ID = "5dd5118943d5aa7329696181252a6565a27dbf2c6de92b02a6aae361"; +// Registry constants — agent-registry v2 (audited, compliant) +// Source blueprint: vector-ai-agents/agent-registry/deploy/agent-registry/plutus.json +const REGISTRY_SCRIPT_CBOR = "5909d101010029800aba2aba1aba0aab9faab9eaab9dab9a4888888966003300130033754011370e90004dc3a40052300730080019180398041804000c8c01cc020c020c020c02000644b30010018a40011337009001198010011804800a00c91803980418041804000c88c8c8cc004004010896600200300389919912cc004cdc8803801456600266e3c01c00a20030064025133005005300f00440246eb8c020004dd598048009805800a01214bd6f7b630488c8cc00400400c896600200314a115980098019805000c528c4cc008008c02c00500520109ba5480026e1d2004488888888888a60026026019301200c912cc004c034c040dd5000c4c8c8cc004004dd6180b180b980b980b980b980b980b980b980b98099baa0042259800800c528456600266e3cdd7180b800801c528c4cc008008c060005012202a375c602860226ea8006294100f48966002601a60206ea800a2646464646464653001375a6036003375c603600d375c603600b37586036009375c6036007375c60360049111112cc004c08801e2646644b3001301d002899192cc004c09c00a0071640906eb8c094004c084dd5001c566002603800513232598009813801400e2c8120dd7181280098109baa0038b203e407c603c6ea80044c8cc00400401489660020030118991980180198130011bae30240014088604201b16407c301b001301a00130190013018001301700130160013011375400516403d259800980618079baa0018a518a504039300600691119199119801001000912cc00400600713233225980099b910070028acc004cdc78038014400600c80b226600a00a603800880b0dd7180a8009bad3016001301800140586601000800629000488c8cc00400400c896600200314c103d87a80008992cc004c0100062600e6602c00297ae0899801801980c001202430160014050911111114c004c0680226034603601125980099b89371a6eb8c048c060dd5000a41000915980099b89371a6eb8c044c060dd5000a41002115980099b89371a6eb8c040c060dd5000a41000515980099b89371a6eb8c06cc070c070c070c070c070c060dd5000a41001115980099b8930043758601c60306ea80052040899198008009bac300f3019375400444b30010018a518acc004cdc49b8d375c603a00290400144cc008008c078006294101820368a50405914a080b229410164528202c8a50405929800800d220100a44100400c9111192cc004c0600062646644b3001301b0018992cc004c090006264b3001301d30203754003132323322598009814801c0222c8130dd698130009bae302600230260013021375400316407c6046003164084603e6ea80222b3001301a0018acc004c07cdd5004400a2c81022c80e901d0992cc004c068c074dd5003c4c8cc89660026602a6eb0c090c084dd5008919baf30253022375400200915980099912cc0040060051598009813800c4c96600266e3cdd71811800802c4c07cdd69812000c52820443026001801204840902940cc058dd5980c98109baa011005899912cc0040060051598009813800c4c966002602c60466ea8006264660260022b300130123028302537540031598009980a00a981418129baa00189806000c52820468a50408c604e60486ea80062c8110c070c08cdd51813000c009024204814a064660020026eb0c06cc088dd5009112cc004006297ae0899912cc0056600266ebcc0a0c094dd5001002c566002b3001301730243754603c604a6ea800a294629410234566002604130013756603e604a6ea800a013006404113371290406d620498059bab301f3025375400514a0811a29410234528204689981380119802002000c4cc01001000502318130009813800a0488a50407d14a080f8dca1bb300130020033021301e375400f1332259800800c00a2b30013024001899192cc004cdc39bad30220024800626464b300130203023375400313259800980b98121baa0018991980a00089980a80b181498131baa00130283025375400316408c603a60486ea8c078c090dd5181398121baa0018a5040886601a6eb0c098c08cdd500992cc004cdd7981398121baa301e302437540020051301f98009bab301e30243754603c60486ea8006011003403d14a08110c01401a2c8100dd718100009811800c009021204214a0660266eacc058c078dd5007001203823011330203374a9001198101ba90014bd7019810260103d87a80004bd70180e1baa006375c603e60386ea80122b30013017001899199119912cc004c0740062b3001302137540150028b20448acc004c0700062b3001302137540150028b20448b203e407c2b3001301b301e3754003198009811180f9baa00191192cc004c078c084dd5000c4c094c088dd5180e18111baa30253022375400316408066016004466ebcc094c088dd500080148c966002603860406ea800626eb8c090c084dd5000c5901f181198101baa00191192cc004c078c084dd5000c4dd5980e18111baa301c30223754604a60446ea80062c8100cc02c0088cdd7981298111baa00100291192cc004c098006264b3001301e375a60460031375c6044003164084604a00316408c6602c004002911112cc004c080c08cdd5006c4c8cc88cc896600266030032605860526ea802a2b3001302432330010013758605a60546ea8068896600200314800226644b30013375e6060605a6ea8c09cc0b4dd5001004c4cdc0000a4005100140ac605c00266004004605e0028162264b30013026302937540031323259800980f18159baa0018991980d8008acc004cdc39bad3005302d37540026eb4c014c0b4dd5007456600260346060605a6ea80062b30013014001899b89301300730133756604e605a6ea800e294102b452820568a5040ac605e60586ea80062c8150c090c0acdd5000981698151baa0018a5040a0660266eb0c088c0a4dd500c92cc004cdd7981698151baa00100689812cc004dd5981218151baa001802c00d015452820508a50409d14a08138cc0100040088c0acc0b0c0b0c0b0c0b0c0b0c0b0004c010004cc00cdd6181418129baa015008330043758604e60486ea805001e26464b30013301501630293026375400f15980099912cc0040060051598009816000c4c96600266e3cdd7181400080244cdc39bad30290014800629410271815800c009029205214a0660366eacc078c098dd500b00144c9660026046604c6ea8006264660386eb0c084c0a0dd500c12cc004cdd7981618149baa302c30293754002605860526ea800a266e252080dac409300f3756604660526ea80062941027180d19814980d19814981518139baa0014bd7019814a6103d87a80004bd704528204a30293026375400f14a08122294102419801198019bac30283025375402a0100026006660086eb0c09cc090dd500a003a04445901d180f1baa008302000130203021001301c375400916406880d0c068dd500188a4d13656400401"; +const REGISTRY_POLICY_ID = "be1a0a2912da180757ed3cd61b56bb8eab0188c19dc3c0e3912d2c01"; const MIN_AP3X_DEPOSIT = 10_000_000n; const AGENT_MESSAGE_LABEL = 674; @@ -45,10 +46,45 @@ function newProvider() { } // Derive NFT asset name = blake2b_256(CBOR(OutputReference)) -// Matches Aiken's derive_asset_name (verified via CBOR parity tests in agent-registry repo) -function deriveNftAssetName(txHash, outputIndex) { - const outRefCbor = Data.to(new Constr(0, [txHash, BigInt(outputIndex)])); - const hashBytes = blake2b(Buffer.from(outRefCbor, 'hex'), { dkLen: 32 }); +// v2 (Conway) requires INDEFINITE-length CBOR for the constructor field array +// (`D8 79 9F … FF`), matching Aiken's `cbor.serialise`. Lucid's Data.to emits +// definite-length arrays (`D8 79 82 …`) which v2 rejects, so we hand-roll the +// outer constructor and let Lucid encode the inner fields. +function cborUint(n: bigint): Buffer { + if (n < 0n) throw new Error('output_index must be non-negative'); + if (n < 24n) return Buffer.from([Number(n)]); + if (n < 0x100n) return Buffer.from([0x18, Number(n)]); + if (n < 0x10000n) return Buffer.from([0x19, Number(n >> 8n) & 0xff, Number(n) & 0xff]); + if (n < 0x100000000n) { + const b = Buffer.alloc(5); b[0] = 0x1a; b.writeUInt32BE(Number(n), 1); return b; + } + const b = Buffer.alloc(9); b[0] = 0x1b; b.writeBigUInt64BE(n, 1); return b; +} + +function cborBytes(hex: string): Buffer { + const raw = Buffer.from(hex, 'hex'); + const len = raw.length; + let header: Buffer; + if (len < 24) header = Buffer.from([0x40 | len]); + else if (len < 0x100) header = Buffer.from([0x58, len]); + else if (len < 0x10000) { header = Buffer.alloc(3); header[0] = 0x59; header.writeUInt16BE(len, 1); } + else { header = Buffer.alloc(5); header[0] = 0x5a; header.writeUInt32BE(len, 1); } + return Buffer.concat([header, raw]); +} + +export function deriveNftAssetName(txHash: string, outputIndex: number): string { + // Constr 0 with indefinite-length inner array: + // D8 79 (tag 121 = constr 0) + // 9F (indefinite array begin) + // + // FF (break) + const outRefCbor = Buffer.concat([ + Buffer.from([0xd8, 0x79, 0x9f]), + cborBytes(txHash), + cborUint(BigInt(outputIndex)), + Buffer.from([0xff]), + ]); + const hashBytes = blake2b(outRefCbor, { dkLen: 32 }); return Buffer.from(hashBytes).toString('hex'); } diff --git a/src/vector/governance.ts b/src/vector/governance.ts index 7f3f974..abe8714 100644 --- a/src/vector/governance.ts +++ b/src/vector/governance.ts @@ -20,7 +20,7 @@ const VECTOR_KOIOS_URL = process.env.VECTOR_KOIOS_URL || 'https://koios.vector.t const VECTOR_EXPLORER_URL = process.env.VECTOR_EXPLORER_URL || 'https://vector.testnet.apexscan.org'; // Agent Registry policy ID (shared across all modules) -const AGENT_REGISTRY_POLICY = process.env.AGENT_REGISTRY_POLICY || '5dd5118943d5aa7329696181252a6565a27dbf2c6de92b02a6aae361'; +const AGENT_REGISTRY_POLICY = process.env.AGENT_REGISTRY_POLICY || 'be1a0a2912da180757ed3cd61b56bb8eab0188c19dc3c0e3912d2c01'; // Governance contract hashes (from deploy_state.json) // These should be set via env vars in production diff --git a/test/derive-asset-name.test.ts b/test/derive-asset-name.test.ts new file mode 100644 index 0000000..e5ec521 --- /dev/null +++ b/test/derive-asset-name.test.ts @@ -0,0 +1,43 @@ +import { describe, test } from 'node:test'; +import assert from 'node:assert/strict'; +import { deriveNftAssetName } from '../src/vector/agent-network.ts'; + +// Parity vectors — verified against pycardano OutputReference.to_cbor() and +// agent-sdk-py plutus_serialise_data(0, [tx_hash_bytes, idx]). All three +// implementations produce identical bytes for the v2 contract on Conway. +// See plan file for the verification run output. +const VECTORS: Array<{ tx: string; idx: number; expected: string }> = [ + { + tx: '00'.repeat(32), + idx: 0, + expected: 'a2e5e227858e84f1a8f9b0c1246e6cbc9336d707d43ba43d0e1cb7c51c45f4c9', + }, + { + tx: '2703182e4d1151a32b7bcfc2362c91278bcac66a49a840d217ae5cb49f8b3649', + idx: 0, + expected: '24ed834d88890ce35e81a6b9c4f988fad3d8a0284db6f3e641416f44f5e95d75', + }, + { + tx: 'ff'.repeat(32), + idx: 23, + expected: 'df229a221c5a84f48c06d5b28a98deb6ea678c369ab166400e88dd3d2bdc7b9f', + }, + { + tx: 'ab'.repeat(32), + idx: 24, + expected: '3ddd07517205dd8a745887c33a09da0e4a5d63dca1f151ceeedaeeda0551c52b', + }, + { + tx: '01'.repeat(32), + idx: 256, + expected: 'e626a305779e4c1e74b7719d302d0fbc1b722ab33bd52da4c23e6b8cd1bd04ec', + }, +]; + +describe('deriveNftAssetName — Conway indefinite-CBOR parity', () => { + for (const { tx, idx, expected } of VECTORS) { + test(`tx=${tx.slice(0, 8)}… idx=${idx}`, () => { + assert.equal(deriveNftAssetName(tx, idx), expected); + }); + } +});