Vote for your favorite SkillMD. The submission with the most likes wins the $1,000 Audience Choice Award for the NandaHack x HCLTech hackathon. Voting is open through October 25.Vote now →
in reviewhumanPR #301Payments

[Fellowship Challenge] payments: add bound.v1, escrow that pays only the seller it names

Submission for the Hexaware x Project NANDA Fellowship Technical Challenge

Author

Venkata-Sai-Vishwanath-robo avatar

@Venkata-Sai-Vishwanath-robo

github profile →
Status
In review
Opened on
Oct 1
Branch
challenge/Venkata-Sai-Vishwanath-robo-fair-exchange

Description

The pitch.

Submission for the Hexaware x Project NANDA Fellowship Technical Challenge

| | |
|---|---|
| **Gap** | Escrow in `ledger.v1` never records who a hold is for. A buyer can take delivery and then release the escrow to itself, or refund it, and the town records a settled payment while `ledger_conserved` passes. |
| **Layer** | `payments` (Lab). New plugin `bound.v1`; `ledger.v1` is unchanged and remains the default. |
| **Fault** | Two hostile buyer roles: `self_paying_buyer` releases escrow to itself after delivery, `self_refunding_buyer` refunds itself after delivery. A delayed delivery is also tested. |
| **Invariant** | Goods leave a seller only against escrow locked to that seller for the agreed total, and that escrow can only ever pay that seller. Once a delivery reaches the buyer, the buyer cannot get the money back. |
| **Negative control** | `nandatown compare fair_exchange --swap payments=ledger.v1`: same agents, same seed, only the payments layer differs. Fails `goods_paid_for` and `escrow_bound` while `ledger_conserved` still passes. |
| **Bundle fingerprint** | `sha256:fb0679f7c870941866886f47bd62672131bd11406157433a83d4e51b2acf7a9e` (bundle `sim-cd27859a50c1`, `bound.v1`) |
| **Control fingerprint** | `sha256:da5a4e0037ea315765c61c56eab3e146aa0e7d09fae306d6ea6e39a535a77425` (bundle `sim-df7339d7af83`, `ledger.v1`) |
| **Profile fingerprints** | `bound.v1`: `sha256:e8852000527338936d736207fdb87d7611a489fd28630872c5c3b93f4d8138bd`; `ledger.v1` control: `sha256:632a3d15efa8ba731b13a0540e9c9a071b3cdf223168daa882618a3b6c5114cb`. The bundle fingerprint changes with every run (new run id); the profile fingerprint does not. |
| **Base** | `upstream/main` at `36e7f6c`; branch commit `8be2efc` |

## The gap

Trades between strangers need a neutral third party, and Nanda Town has one: escrow. On `main` it does not act as one.

1. `layers/payments.py` `ledger.v1.hold(frm, cents, ref)` stores the payer and the amount. It never stores a payee.
2. `release(ref, to)` pays whoever the caller names, so the payer chooses the payee after the goods have arrived.
3. `refund` returns a held escrow to the payer at any time, including after delivery.
4. `sim/agents.py` `Seller.handle_purchase_order` ships without checking that any escrow exists.
5. `ledger_conserved` checks only that total money is unchanged. A buyer paying itself conserves money, so it passes.

### Reproduced on main (36e7f6c), before this change

One seller and one buyer, seed 42, scratch role and scenario, no repo changes:

```
escrow_held      order-mallory-1 {"cents": 8975, "from": "mallory"}
message_sent     [seller-a] delivery order-mallory-1 quantity 5 -> mallory
escrow_released  order-mallory-1 {"cents": 8975, "to": "mallory"}
payment_settled  order-mallory-1 {"cents": 8975, "from": "mallory", "to": "mallory", "via": "escrow"}

goods_paid_for    Failed   seller-a shipped order-mallory-1 and was not paid (escrow went to ['mallory'])
ledger_conserved  Passed   10000 cents conserved across every movement
```

The same baseline with an honest buyer passes, with the escrow released to `seller-a`. A buyer that never funds escrow also receives goods, because the seller does not check.

The town records a buyer paying itself as a settled payment, and its money check is green.

## The change

`bound.v1` is a new payments plugin, scaffolded with `nandatown new plugin payments bound.v1`. It subclasses `ledger.v1` and declares `binds_payee = True`.

1. **Locked to a payee.** A hold that names a payee can be released only to that payee. Any other target is refused and recorded as `escrow_release_refused`.
2. **No refund after delivery.** Once the town has recorded the payee's delivery of that order reaching the payer, a refund is refused (`escrow_refund_refused`). A delivery that was dropped or failed authentication does not count, so a buyer who received nothing can still be refunded.
3. **Paid on proof of delivery.** The payee c

…

Try it

Open PR on GitHubView diff

Checkout locally

git fetch origin pull/301/head:pr-301
git checkout pr-301