When withdrawing from L2
to L1
there is a feature called auto withdraw
where instead of consuming the msg from the Starknet messaging protocol, it is consumed automatically by the Bridge by Admins. Where they can add the message from L2
to be auto withdrawn in case it is not taken by the Starknet
sequencer.
The Bridge owner can add msgHash into auto-withdraw messages if that message is not added.
When consuming the msg, we calculate its hash and if it was added into auto withdrew messages it will get consumed.
When consuming the message we calculate it using its inputs on L2, and then set that msg to WITHDRAW_AUTO_CONSUMED
.
The problem is that this logic is correct if the msg is not replayable. i.e the msgHash cannot be replayed by implementing nonce
or something. But this is not true.
When computing the hash we are computing it using the sender from L2
, L1bridge
, and the payload data.
The same L2sender
can do more than one transaction from L2
to L1Bridge
is always the same. and now we need to see if the payload data is the same or not.
Payload is the Hash of the Request
object which consists of the following parameters.
This is the Request object in solidity which is the same in Starknet L2
Bridge. All the variables will be the same if the message sent from layer2
provides the same parameters (i.e he will send the NFT from L2
to L1
to the same address on L1
). and now only the hash can achieve the uniqueness of the message hash.
The Hash also, is not unique where it is calculated using salt
, collection
, to_l1_address
, and tokenIds
. and as we said, all these things can be replayed in more than one message, and for the salt
itself, it is written by the user which can be written as the value in the previous message he requested.
So in brief, the message from L2 can be Replayed and msgHash will be the same. Now what will happen if a message is autoWithdrawed
and the same message is initiated before on L2
.
First: The message can't be auto withdrawn as it is value in the mapping is CONSUMED
and to add it to auto withdrawal, it should have auto withdraw to NONE
not CONSUMED
.
Second: The message will still be unable to get consumed by Starknet protocol, as when consuming the message from Starknet, the message should not be auto-withdrawn.
We should keep in mind that replaying the message from Starknet to Ethereum is a normal thing is StarknetMessaging
protocol, which supports replaying the messages more than one time without a problem.
This will result in preventing consuming the message, and withdrawing NFTs if the msgHash
is the same.
This issue affects msgHash
which was pointed to be autoWithdraw
before or that is added to autoWithdraw
.
MsgA was AutoWithdrawen message.
UserA initiated a message that is the same as MsgA.
The MsgA has the same hash as the msg initiated by UserA.
Starknet Messaging protocol accepted the message, and add it.
UserA called L1Bridge::withdrawTokens()
.
The tx reverted as that msgHash was auto-withdrawn before.
The NFTs in that message are lost and can't be withdrawn.
Losing of NFTs in the messages that has a msgHash
matchs on of the Autowithdrawed messages.
Unable to auto withdraw the same msgHash twice, which is an acceptable thing in StarknetMessaging protocol.
Manual Review
There are a lot of possible solutions for this issue, the best thing is to support the replaying of the msg from L2
the same as Starknet Messaging
protocol works. But for simple mitigation, we can implement a function to reset the msg to NONE
by admins. So if this thing occurs, the Admins can reset the msgHash into NONE
in auto, which will allow consuming the message either by Starknet or auto.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.