A note on prepared transactions

2018-04-24 - Future - Tony Finch

Some further refinements of the API behind shopping-cart style prepared transactions:

On the server side, the prepared transaction is a JSON-RPC request blob which can be updated with HTTP PUT or PATCH. Ideally the server should be able to verify that the result of the PATCH is a valid JSON-RPC blob so that it doesn't later try to perform an invalid request. I am planning to do API validity checks using JSON schema.

This design allows the prepared transaction storage to be just a simple JSON blob store, ignorant of what the blob is for except that it has to match a given schema. (I'm not super keen on nanoservices so I'll just use a table in the ipreg database to store it, but in principle there can be some nice decoupling here.)

It also suggests a more principled API design: An immediate transaction (typically requested by an API client) might look like the following (based on JSON-RPC version 1.0 system.multicall syntax):

    { jsonrpc: "2.0", id: 0,
      method: "rpc.transaction",
      params: [ { jsonrpc: "2.0", id: 1, method: ... },
                { jsonrpc: "2.0", id: 2, method: ... }, ... ] }

When a prepared transaction is requested (typically by the browser UI) it will look like:

    { jsonrpc: "2.0", id: 0,
      method: "rpc.transaction",
      params: { prepared: "#" } }

The "#" is a relative URI referring to the blob stored on the JSON-RPC endpoint (managed by the HTTP methods other than POST) - but it could in principle be any URI. (Tho this needs some thinking about SSRF security!) And I haven't yet decided if I should allow an arbitrary JSON pointer in the fragment identifier :-)

If we bring back rpc.multicall (JSON-RPC changed the reserved prefix from system. to rpc.) we gain support for prepared non-transactional batches. The native batch request format becomes a special case abbreviation of an in-line rpc.multicall request.