Check if a box is guarded by a script
Hi devs,
I need to check if a box is currently locked (guarded) in a smart contract.
I basically need to distinguish when a box comes from a wallet and when it comes from a smart contract.
Is there an easy way to do it in ErgoScript?
Thanks!
20 Replies
Use the
.propositionBytes call on Box's to get the guarding script of the box. Use .propBytes call on SigmaProps to get the propositionBytes of the Sigma Proposition. Then check equality against the proposition bytes of whatever you wish to compare.Hey @Cheese Enthusiast , thanks for the reply!
What I need to do is to ensure a Box is currently NOT guarded by a script (so it means it comes from a user utxo).
Does a box have .propositionBytes defined only when it's being guarded by a script?
All boxes have scripts, including p2pk / user boxes.
Your goal is to check specifically for a p2pk box?
Yes, basically it is to avoid the "double spending" attack with a clone contract
It's an important vulnerability that in Cardano is addressed ensuring that only 1 input comes from a smart contract (so only the input currently being guarded)
So by this I assume you're talking about having two inputs under the contract you are writing in the same transaction?
There is likely an easier way to solve it then to check for p2pk boxes only, but I suppose both ways are fine.
not only that, among all the inputs only 1 input must come from a contract. If 2 inputs come from different contracts it should give an error. If all inputs except 1 come from a contract then it should pass
I'm still not super expert on Ergo terminology, so I'm not sure p2pk boxes mean they come from smart contracts addresses?
That is a strange notion in Ergo, as everything is a contract here 🙂
P2PK - Pay to Public Key, the type of contract which guards your wallet address.
I suppose there are ways to ensure that some of your input boxes are only p2pk / wallet utxos, but to be honest I see very little reason to ever check for this. I would say that doing this is usually unnecessary, and that maybe there is some misunderstanding of how ErgoScript works if you are trying to do this.
What vulnerability do you forsee from having potential smart contracts in your inputs? If your smart contract and one of the inputs have conflicting propositions, then the transaction simply won't go through.
If the issue is clone contracts, then one simple fix is to check for
SELF.id on R4[Coll[Byte]] of OUTPUTS(0) or whichever outputs your specific smart contract is dealing with
Adding this check would ensure only one of your contracts would successfully execute. Adding more inputs with the same contract would cause the transaction to fail.Could be, this is why your knowledge is very valuable for me! 🙂
Let me explain with an example:
I'm building the typical NFT marketplace.
As seller I lock my NFT in a utxo in the marketplace address and if you send me the correct amount of ERG you can buy my NFT.
Now another NFT marketplace is there and the same seller creates another NFT listing there.
The first smart contract checks that enough ERG are sent to the seller and the second marketplace has the same identical check.
This means that an attacker (the buyer) can pay the highest of the 2 amounts and get both listings with 1 transaction.
The only way to avoid this is to ensure only 1 input in the tx comes from a smart contract (so you cannot spent 1 listing for each marketplace paying only for the highest listing)
In this case, I would do the check I mentioned above. Lets assume that both contracts are looking at
OUTPUTS(0) as the UTXO with ERG being sent to the seller.
The check in this case would be the following: OUTPUTS(0).R4[Coll[Byte]].get == SELF.id
If both contracts are used as inputs to the transaction, the above condition cannot satisfy both contracts. If the id of the first input is put on OUTPUTS(0), then the second input will evaluate to false. If the id of the second input is put on OUTPUTS(0), then the first input will evaluate to false.That makes sense, so the idea is to use one of the free registers to write down the id of the contract?
Id of the input utxo, but yes. Since the id is unique, it would be impossible to have both contracts be true at the same time.
Oh so the first output of the tx must have in R4 the id of the input utxo being guarded
Yep, that's correct
That's a very cool solution and probably very clean, unluckily currently I'm already using all the 6 registers for the contract state, so are we 100% sure there's no easy way to distinguish between P2PK and custom smart contracts?
You are using all 6 registers on the output utxo already?
There is a way to distinguish between them, you would want to input a standard p2pk contract and use
substBytes, I just think it is likely over-complicated and unnecessary in most cases.Yes, because what I'm actually doing is moving a utxo from one SC to another
As for using all 6 registers, you can always use a pair on one of the registers to store an additional data type
That's true, that may do the trick 👀
For example if you have R4 of type
Int, make it of type (Int, Coll[Byte]) and check for SELF.id == OUTPUTS(0).R4[(Int, Coll[Byte])].get._2
This would just about achieve the same resultsIt makes sense, I will try this way, many thanks @Cheese Enthusiast !