Proposal for gas payment in the resource machine

Spawned by a discussion with @isheff and others in the state architecture ART review call, I propose that we specify a mechanism for gas payment (or, more specifically, payment for transaction function execution) as part of the resource machine specs, which would work as follows:

  1. We introduce a new datatype TransactionFunctionWithPayment, which consists of three elements:
    a. A transaction function (as currently in the RM specs).
    b. A transaction object (the “payment transaction”), which includes in a special application data field the hash of the transaction function and the gas limit.
    c. A gas limit (natural number).
  2. When executing a TransactionFunctionWithPayment, the executor takes the following steps:
    a. Checks that the payment transaction is “simple”. What exactly this means can be executor-specific, but roughly “simple” means “inexpensive to verify”. A basic (very restrictive) check could be that the payment transaction has exactly one consumed resource and nothing else.
    b. Decide whether this gas payment is sufficient. This decision can be controller-specific (maybe there are certain assets and certain prices accepted for gas).
    c. Alter the payment transaction, adding new resources assigned to the executor (or whoever is supposed to receive the gas payments) as necessary to make the payment transaction balanced.
    d. Check that the payment transaction is fully valid and balanced (normal transaction rules), including in a special application data field the hash of the transaction function and the gas limit.
    e. Execute the payment transaction (apply the state changes).
    f. Compute the transaction function, limited by the specified gas limit.
    g. If the transaction function computation finishes within the gas limit (returning a transaction object), check that the transaction object is valid and balanced, and if so apply it to state (as previously in the RM).

I like this approach for a few reasons:

  1. It does the basic thing we want (a simple transaction pays for the potentially costly execution of the transaction function).
  2. It’s pretty simple and uses mostly data structures and logic which we already have.
  3. It’s quite general, doesn’t fix a particular token which must be used to pay for gas, and even allows for solving as part of the gas payment (e.g. controller accepts token X, user wants to pay for gas in token Y, a solver can match this with another X → Y intent and produce a valid payment transaction).

I’m looking for quick feedback from @isheff @vveiln and @degregat. If this looks good, we can add it to the specs next week (in full detail). Otherwise, raise any questions or concerns here.

Some thoughts on

and

The authorization mechanism of the token would need to allow “adding new resources assigned to the executor (…) as necessary to make the payment transaction balanced”.
The current Kudos token authorization mechanism requires a signature over a message including the mustBeCreated commitments and mustBeConsumed nullifiers from the owner. The logic then checks that mustBeCreated commitments and mustBeConsumed nullifiers are subsets of the action commitments and nullifiers.

Similar to a swap, we could require an ephemeral intent resource in the mustBeConsumed set in which further constraints are expressed.
This gas payment intent resource could also be the right place to specify the gas limit (and maybe other things, such as an expiration time).
TransactionFunctionWithPayment could have an app data field pointing to the intent resource commitment. Maybe we don’t even need a special transaction function type at all if the intent resource is transparent and has a standardized kind.

1 Like

Why? The owner of the token would just be authorizing “I am happy to consume a resource with X units of token Y if the transaction function with hash A is executed with gas limit B”. The executor would add e.g. a created resource of X units of token Y to a different action, which would not be visible to the first action (but would make the payment transaction as a whole balanced).

Where is the logic located making sure that this constraint is met? I’m argueing that this constraint is not located on the token (because tokens should have a more general logic and not a specific one) but rather on an intent resource that the token requires to be present (like we do it in the case of a swap).

Ah I see - an intent resource should not really be necessary, the user should sign over an authorization which binds to particular public inputs (in this case, the information about the transaction function execution being paid for), where the proof is valid only if those public inputs are as expected.

the user should sign over an authorization which binds to particular public inputs (in this case, the information about the transaction function execution being paid for), where the proof is valid only if those public inputs are as expected.

Can you help me to see where the signed message over the public inputs gets checked if this doesn’t happen on an (intent) resource?

1 Like

The token resource should allow for a kind of authorization where an external identity signs over a commitment to a predicate, and where the predicate must return true in order for the resource logic to accept. In this case, the predicate should check that the appropriate data is present in the application data, specifically the hash of the transaction function and the gas limit. Does that make sense? We need intent resources to carry constraints across actions, but we shouldn’t need them just for this kind of authorization in a single action.

How it relates to transaction with metadata? What is the hierarchy there?

What is a transaction object? Is it what is called just a transaction in the spec?

Does it only work for finalised transactions that won’t be composed anymore?

Ah, so this gas payment transaction initially is not balanced? Is it proven? What is this resource contained in the transaction, what is the logic of it?

Is it always the case that the transaction function hash is sufficient to bind the gas payment transaction to the future transaction resulting from the transaction function? I can imagine the transaction function won’t be unique, do we care about uniqueness there? What stops the executor node to apply the state changes from the gas payment transaction, then discard the transaction function associated with it or replace the computed transaction with some other transaction?

1 Like

Good question. I would probably say that we can just make the payment field part of TransactionWithMetadata for now (instead of having a separate TransactionFunctionWithPayment structure), they’ll always be sent together (and the payment can always be empty).

Yes.

Yes, this payment applies specifically to paying a controller to execute a transaction function and attempt to apply the resulting transaction, which would happen after any solving, composition, etc.

It is proven, but not necessarily balanced (as the creator of the payment transaction might not necessarily know exactly who will be receiving the payment). The executor might just add a new token owned by themselves.

Nothing stops the executor from doing arbitrary things - there’s just an implicit contract (implemented in the actual execution code) that checks this payment transaction, only executes the authorized transaction function, and only applies the transaction which results from computing it. Eventually, we can make this contract explicit with service commitments, but even before that, it will be obvious if this contract is broken (it would be a fork - compare e.g. if an Ethereum validator took your transaction and replaced some of the EVM opcodes with different ones).

1 Like