Just to drop few thoughts re. read-only inputs. What problem are we aiming to solve?
- Give contracts indiscriminate read-only access to UTXO state (with open question of whether there should be a flag to toggle direct evaluation or just reference it for other input’s use through introspection)
- Global function tables
If we have 1) we can emulate 2), but if we want 2) why not just go for 2)? Becuase 1) is a kind of hacky way to get 2). Having both would be useful: 1) for read-only access to general UTXO state 2) for having an efficient global function table
Global Function Tables
Extend the UTXO model with special global function definition UTXOs. Imagine if you could create an output with locking bytecode: PFX_DEFINE <bytes> <lifetime>. This output skips the UTXO database and is treated as unspendable (similar to OP_RETURNs).
Once mined, the output is added to another database, a global function table, a simple key-value store: hash(bytes), bytes, sum(lifetime). Each re-definition (people creating outputs with exact same <bytes>) just extends the lifetime. If the lifetime expires, we could allow garbage collection with a special input unlocking bytecode: PFX_UNDEFINE <hash(bytes)> that adds to TX fee. This would allow the option for the network to price adding these definitions (min. dust requirement based on lifetime & size) and expired balance would be paid to miners. It could be re-defined later, and it will again get the same hash. So contracts relying on it can’t be broken by the dependency being purged - you can just create a new TX to re-define the functions your contract needs.
Usage: <hash(bytes)> OP_INVOKE from any Script. If the entry exists and hasn’t expired then the function is retrieved and evaluated. If the entry has expired or doesn’t exist then the OP_INVOKE call fails.
Alternative to read-only inputs: global UTXO introspection opcodes
This avoids having to carve exceptions to avoid old contracts introspecting other inputs being fooled by read only inputs. Just allow direct read opcodes, without having to add input references to TX at all:
<txid:vout> OP_EXTERNALUTXOVALUE
<txid:vout> OP_EXTERNALUTXOBYTECODE
<txid:vout> OP_EXTERNALUTXOTOKENCATEGORY
<txid:vout> OP_EXTERNALUTXOTOKENCOMMITMENT
<txid:vout> OP_EXTERNALUTXOTOKENAMOUNT
or just overload existing opcodes:
<txid:vout> OP_UTXOVALUE
<txid:vout> OP_UTXOBYTECODE
<txid:vout> OP_UTXOTOKENCATEGORY
<txid:vout> OP_UTXOTOKENCOMMITMENT
<txid:vout> OP_UTXOTOKENAMOUNT
which would be forward-compatible with any future fields/introspection we may add to UTXOs.
Implementations could cache these external UTXO retrievals, load them once into context on first call, and with VM limits framework in place we could budget these calls by number of unique UTXOs loaded.
With this we lose garbage collection. If used for global function definitions and to avoid contracts getting broken users would have the incentive to create indestructible UTXOs for their function definitions, so there’d be a monotonically growing number of these indestructible zombie UTXOs. Probably won’t ever become a problem, how many of these could people possibly create?