Transaction functions (when run post-ordering) need a way to query data, such as:
the current resource of kind k
the value at content-addressed storage location h (h is a hash)
possibly historical data or other data
I suggest that we add a very simple interface, where transaction functions are passed a pointer to a query function:
type QueryFunction := (Bytes -> Bool) -> Set Bytes
The query function takes a predicate over data blobs and returns the set of blobs which satisfy that predicate (a similar idea has been previously discussed here, and here). This signature is extremely general, but in practice we can start by supporting only a small, finite set of query functions, e.g.:
\x -> hash x == y (query content-addressed storage by hash)
\x -> inCurrentCommitments (hash x) && kind x == y (pseudocode - query the current resource with kind y)
The surrounding “resource machine shell” will simply analyze the provided function, see if it matches one of these specific ones, and fail if not. Then we can easily add more sophisticated query options in the future without changing the interface.
What do you mean by “the current resource of kind k”? In which context would a transaction function query “current resources”? Are you thinking about, for example, a time-carrier resource as discussed here Time Constraints in Resource Logics - #7 by cwgoes)?
Maybe examples could help me to understand the context. (I tried to look at the nspec repo to read more about the context but I didn’t find the right place. Can someone maybe point me to the relevant pages?)
My intuition is that queries run by controller/block proposers post-ordering must be fast.
Can these queries fail/return nothing so that the transaction becomes invalid? If this is the case, what happens with the transaction and all other, ordered transactions?
I mean “the resource which has been created but not yet consumed on this controller of kind k”. A typical example would be a counter resource.
Yes, these queries must be able to return results immediately (synchronously), so queries for unsupported functions will fail immediately, causing the transaction to fail (just as if you read from a non-existent storage location in the EVM, say).
So the shell takes a predicate as input and checks that it is one of the hardcoded two (for now)? Do we need that kind of check for other functions that have a more general signature but in practice are limited to a few options (e.g., deletion criterion)? From the report perspective, since right now all functions of the resource machine are described on the core level, i’ll need to describe the connection between the shell and the core if we are adding this (just fyi)
The concept of “current” being confusing and problematic, we’ve avoided it. A transaction has a concept of “as of my own execution”, which is a lot more specific and actionable; transactions are serializable and there’s an as-if-serialized correct global system state corresponding to each transaction. No “current”; no one knows what this word means and they start doing nontransactional raw reads into private implementation details.
The concept of “fast” being undefined, we’ve avoided it. I know I measure microseconds a lot but this is a sanity check, performance tuning looks very different. Semantics don’t have time bounds outside real-time computing which is not relevant here; implementations have them.
The concept of “synchronous” is more promising, but you don’t want it either. It’s just a simple, normal failure semantic to fail sometimes. (It’s a “soft” fail, which despite being the soft one is the one that fails the whole transaction… a “nontermination” fail, really. The one that admits retries 20 years later.)
Moving on to the actual question, broadly speaking, defining it this way is fine at a high level but I note no one’s going to pass physical functions around and analyze them, Yoneda lemma nonwithstanding; you’re looking at either a domain-specific query language or what amounts to some kind of compiler to same. (These are good things, this isn’t a dismissal as impractical!)
(And you do satisfy a RTOS’s semantics when you single-step through it nanosecond by nanosecond in your simluator. Not even that has a time bound. What you do have at a high level is the concept that some routes are foreclosed because they e.g. blow up combinatorially and can never be made efficient without a research breakthrough. But this looks, again, nothing like performance tuning…)
Agreed, I did not intend “current” to be read as a precise technical descriptor.
By “synchronous” I just means “returns results immediately, from the perspective of the caller” - I think we’re on the same page.
The structure required for analysis from the perspective of indexing is probably not entirely dissimilar to a circuit - namely, one wants the data dependencies to be explicit - so I’m not sure that this is entirely infeasible, but we can start with a simple DSL.