Can you provide use cases where single non-aggregated entries are useful?
The biggest case I have in mind is automatic release of on-chain escrow, based on pre-agreed validation conditions. Alice wants to buy a job from Bob, who she doesn’t necessarily trust to correctly perform the job, and Bob doesn’t trust that Alice will pay after he does the job, so they can agree beforehand that Alice deposits escrow which is released when
- a consortium of voters agrees that Bob’s result is valid
- a mutually trusted third party re-runs the job and determines that Bob’s result is valid
- Bob submits a cryptographic proof that he ran the job in a TEE
- optimistic mediation, where the escrow is released by default after a certain time unless Alice disputes it, in which case one of the above mechanisms is used
- …
Any of these conditions can be represented by a single non-aggregated validation. The release conditions could be a generic parameter to any escrow contract ((address arbiter, bytes demand)
, where there’s some other interface with a function check(task, demand) => bool
). So any of the “basic on-chain aggregation” processes you describe could also be implemented in release conditions, either individually or in combination. So escrows could demand e.g. “a validation Response above X from any of a list of mutually trusted third parties” or “an average Response above X from a list of trusted third parties”.
Which data would you save on-chain? Only the Rating and Response integers or other data structures too?
Where? Registry storage?
I wrote an example of an on-chain escrow contract working in combination with Sumeet’s reference implementation, which actually does store Response integers on-chain. I see two main flaws with it:
- The things to be validated are only identified by a dataHash, so they can’t be referred to before they concretely exist. This means you can’t distinguish between different validations from the same validator for the same server. Even if the contract stored already claimed dataHashes to prevent double claiming, servers could potentially get a validation for a different, easier task than the one escrowed for. The only workaround I could see in the current design would be for agentValidatorIds or agentServerIds to be essentially single-use, which seems to go against their intended design.
- Escrow demands can only be parametrized by the fields in the ValidationRequest or ValidationResponse struct, of which the only useful fields for parameterizing an escrow release condition are agentValidatorId, agentServerId, and the response Status integer. This isn’t as big of an issue though if you have something like a taskId, since you could implement different validator contracts representing different conditions (including aggregations of other validations), and have custom demand parameters implemented on each validator contract, where the validator contract submits a Response to the validation registry at the end of its internal process.
Compare this with the implementation of escrows with generically parameterized demands that I linked in the previous comment, where the on-chain interface is a function
interface IArbiter {
function checkObligation(
Attestation memory obligation,
bytes memory demand,
bytes32 counteroffer
) external view returns (bool);
}
which is agnostic to where data is stored, and could be implemented transiently if the check is simple enough to perform in one tx (e.g., just checking that the fulfilling counterparty is a particular address).
My suggestions for the ERC would be
- add an identifier which can be arbitrarily requested, by which agent tasks can be referenced before they concretely exist.
- add a (perhaps optional) on-chain function
function getValidation(uint256 taskId) returns (int status)
(orreturns (Response response)
, or something to associate taskId with dataHash, and still getting Response by dataHash like in the reference implementation.)
Tentatively, I’d also consider having pre-dataHash references for tasks be a generic demand (as bytes or bytes32 hash) rather than an integer id. But I’m not sure if there are use cases where this enables something that wouldn’t be possible with just an id (by putting anything demand-related on individual validator contracts or off-band), and it’s probably best to keep the ERC implementation requirements as lightweight as possible.