Another design pattern that I think will be very common in Ergo’s model is parallelizing actions in separate UTXOs and collecting all of the data/tokens together as inputs in a tx which folds the data/tokens down into one (or potentially more depending on the protocol) box.
This is in contrast to having 1 box that holds data pertaining to a dApp/protocol which everyone must sequentially spend in order to add their piece of data to. Instead all users can create their own box with the data in the registers (which can happen parallelized in the same block or across multiple) and once all have posted, then an accumulation transaction can be created which consumes all of them and produces a final box that has all of the data/tokens.
This is in essence how data accumulation pools work but this is a general design pattern that allows for building more efficient dApp protocols that have the potential to scale (and potentially just work better for certain use cases).
This is probably so well-known it doesn’t really warrant a note, but just in case, the commit/reveal pattern is useful for many contracts.
In the context of prizes for solving puzzles, e.g. in Mathematical Fun with ErgoScript, it is best to require that a commitment to the solution is mined first before the solution is revealed. Without a commitment, revealing the solution in an unconfirmed transaction is vulnerable to theft because the solution can be copied into a new transaction, and the reward sent somewhere else, assuming it gets mined.
In other examples, Namecoin uses this to prevent “front-running” attacks, where someone picks a domain and a miner steals that. And “rock papers scissors” in Ethereum and Ergo also use this to prevent second player from cheating.
It may be well known but people still fail to take this into account (see Section 4.2 attached paper).
Hence, good to explicitly list it out.
Another useful design pattern (developed upon ideas proposed by @robert) is the following.
To check that some given box (not necessarily SELF) was created after a certain height h:
Check that box.creationHeight > h (creation height is stored in in R3 and the protocol requires that creationHeight be <= actual height when the box is mined).
It seems more tricky to check that some given box was created before some height h. Probably there is some solution in ErgoScript which I have missed.
Another useful design pattern is to emulate executeFromVar or executeFromRegister in ErgoScript.
Currently ErgoScript does not support the above two instructions but ErgoTree does.
executeFromVar allows the box spender to specify some arbitrary script in a context var that will be executed.
executeFromRegister allows the box creator to specify some arbitrary script in a register of that box, which will be executed later on when the box is spent.
In order to emulate this in ErgoScript until it is supported at a later time, we can use the following logic:
If a box is given as part of the INPUTS array, the script in it will be executed
(Note: if a box is given as part of the DATA-INPUTS array, the script in it will not be executed)
Let us look at executeFromRegister for some given box B where we want to execute the script in R4. Instead of the script, this box will now contain a hash of the script for compactness.
Some contracts might have some kind of “daily withdrawal limit”. This is easily implemented by keeping track of the remaining limit within the current 24-hour period (which can be based on block height i.e. every 720 blocks). Once a new period starts, the limit can be reset.
In some situations, context variables may not be possible (for example, currently Appkit does not support them). We can achieve the same thing by encoding the data in context variables in a register of one of the outputs. For example, suppose we have the code
val x = getVar(0)[Int].get
This can be emulated by creating a new output box (or reusing an existing one) at some index, say 1, and storing that data in its register R4. Then the above code can be replaced by
Now that we have the above repo, I was thinking that we could also try to make many of these design patterns more approachable for new devs by making them into full fledged examples and submitting them to the repo. It’s a bit more effort on each of our parts, but I think it’d really help this become the go-to repo for all devs, and even be a good reference for any EUTXO blockchain potentially as well.
Ensuring that each box protected by a particular script has its own output
This is important in cases where a box requires that some condition is present on a specific output. Now, if two such boxes are spent as inputs to the same transaction, without careful attention, the condition may hold for both input boxes on the same output. This can be exploited in cases where the condition is something like output.value >= SELF.value.
Preventing this class of attack can be done by ensuring that each output has a register containing a value which is unique to each input.
It would use less space to use the input box index, which is an integer and is unique for each input box, but note that selfBoxIndex is currently broken (it always returns -1):
The only thing that needs to be done is to issue a token in a transaction and put it in the output in which you want to prove you have created, also put the INPUTS(0).bytes in one of its registers!
Obviously, the token id is equal to the id of the first input of the transaction, hence a contract can make sure that the box is created by a particular person (in this case “9gAKe…”) by:
bytesToBox is not a real function, please let me know the correct way to get Box from its serialized bytes (box.bytes).
The above does not necessarily make sure that INPUTS(0) is actually created by “9gAKe…” but makes sure that “9gAKe…” has participated in the chain of transactions leading to here!
The very use case that came to my mind and made me think about such a thing is for a particular person to give permission for some operation by following this design pattern (for example giving permission for spending some box to the assembler service), hence outsourcing it.
In other words, in a well-designed system in which it doesn’t allow the token to get in the hands of an attacker, we can assume that the person is giving permission for some predefined operation because no one but her could have created such a box!
However, how to prevent other tokens issued by that person to be interpreted as following this design pattern is kind of unsolved I think but I believe there are simple solutions to that as well.
This can be useful in various circumstances, for example, imagine a website that will let the user do a certain activity only if he owns the right token in his wallet. In order for the user to prove to the website that he owns the token, he can create a transaction and spend the box that contains the token (sending it to himself) and send the transaction (no broadcasting is needed) to the website, proving that he owns that token.
This was the simplest case possible; to make it practical, the website can send some randomness to the user and the user will have to put the randomness in one of the output’s registers, proving that he is providing the proof only for this specific interaction.
In ErgoScript (and ErgoTree), proveDlog(a) is defined for the default generator which is fixed in the protocol. If we want to use a different generator h, then we can emulate this using proveDHTuple as follows
Discovered by @anon_real :
As is it possible to store box in a register (and context extension of an input), it is also possible to prove that spent box had some properties. For that, we need to issue certificate NFT during spending, to check then that corresponding box had some properties (while certificate box is not spent). Used in auctions contracts by @anon_real to get original author of NFT to pay royalty.
Kind of meta-pattern: check in a contract if it interacts with other contracts (e.g. LP with order contract, hodl pool with proxy etc) only state (outputs) related to the contract and leave checking other outputs to counter-parties. So in case of LP contract interacting with an order contract, LP would check only correctness of LP output, leaving checking user’s output(s) to the order contract.