Make pay_agent actually transfer CSPR — payer session code + verified testnet settlement - #12
Open
syedhassan-aifinpay wants to merge 4 commits into
Conversation
… code The v2 branch replaced receipt-only bookkeeping with system::transfer_to_account, which is the right intent but cannot work. Proven on testnet, not argued: the call reverts with "Forged reference: URef(...)". Under EntryPointType::Called the contract resolves the account main purse, which is not in its access rights, and the contract has no purse of its own — call() never creates one. Passing the main purse in as an argument does not rescue it either; Casper attenuates a main purse handed to a contract down to deposit-only, and the transfer fails with ERR_TRANSFER_FAILED. Both failures are recorded on testnet. So the funds have to be placed in a purse the contract may legitimately debit, and that has to happen inside the same transaction or the settlement is not atomic — which is the whole point of this work. pay_agent now takes that purse as an argument, and the new session/ crate is the payer-side half: it creates a purse, moves exactly the payment into it from the payer's main purse, and calls pay_agent handing it over. One deploy, so either all of it happens or none does. The temporary purse holds exactly the payment, so the contract can never reach the rest of the payer's balance. Verified end to end on testnet. A real 2.5 CSPR settlement moved the provider balance by exactly 2.5000, payment_count went to 1, and the settlement record reads back SETTLED. Six adversarial cases were then rejected on chain with the right error codes: replay 102, forged payer 103, zero amount 106, self-payment 107, unregistered recipient 101, malformed identifier 105. None moved value. demo/testnet-verify.js and demo/testnet-negative.js reproduce both runs. Key directories and the node URL come from the environment; no key material is committed.
…erclaim Two gaps in the v2 release gate. There were no negative on-chain tests. testnet-verify.js proves the happy path moves CSPR; nothing proved the paths that must not. demo/testnet-negative.js covers forged payer, zero amount, self-payment, malformed identifier, unregistered recipient and replay, then settles a real payment and checks the recipient's balance delta is exactly the amount. Each case asserts the specific User error from src/main.rs rather than just "the deploy failed". Accepting any revert would also accept an out-of-gas run, which would report a broken guard as working. The valid payment is included for the same reason: a suite that rejects everything, including what it should accept, would otherwise look like a pass. Second, deployments/casper-v2.json is what trusted-contract.js consults before allowing a payment, so every field in it is a security claim — and it could be filled in by hand. scripts/deployment-manifest.mjs is now the sanctioned way to write it and refuses claims it cannot substantiate: stage builds and records both Wasm digests against the exact source commit, refusing to run on a dirty worktree because the commit would not then describe the built bytes. It leaves status "built" with the contract and deploy hashes null, since only a real deployment may fill those. verify additionally requires the chain to confirm the contract exists, and fails closed when the node is unreachable — an unreachable node is not evidence of a good deployment. check is the CI gate and now runs on every build: 7 tests cover advancing the status without build evidence, claiming a deployment with no hashes, claiming verified with no timestamp, a truncated contract hash, an unknown status and the wrong contract version. 21 demo tests passing, was 14. Manifest stays source_only, so Casper payments remain quarantined. Nothing deployed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this fixes
Casper settlement must be submitted as payer session ModuleBytes. A direct stored-contract call cannot access the payer's main purse safely. The session creates a temporary purse, funds the exact amount and calls
pay_agentatomically.The verifier now accepts only a successful deploy containing the exact payer-session ModuleBytes and rejects direct stored-contract settlement.
Integrated surfaces
Evidence
Candidate SHA:
bd6db3320df9d0cc4387d2df9f31329a0d465d3bGitHub CI run
31280849717— success.Local: both Wasm builds, clippy with
-D warnings, 14 tests.7a2f18b0c4745e548a701fe6a11cb57d3258baa771fdec7fc77ccc9fa58d2b04f7c05b2a73cd0d6a446d4ce5f43d9e032e1921107407db6855c95c9c8304bcc8Release boundary
The manifest intentionally remains source-only. No mainnet deploy is included; settlement stays disabled until a verified deployment binds these exact hashes.