Arbitrary state access is a read or write to a storage slot specified by another storage slot.
This example is a loose approximation of an auction contract. Each new highest bidder locks in Ether as their bid, and the previous bidder is refunded.
refund_addr: address
refund_amount: wei_value
@public
@payable
def bid(new_refund_addr: address):
assert msg.value > self.refund_amount
send(self.refund_addr, self.refund_amount)
self.refund_addr = new_refund_addr
self.refund_amount = msg.value
To show how this is DSA, imagine a scenario given the two following transactions {and their witnesses}:
bid('0xB', 4)
{refund_addr == 0xA && refund_amount == 3 && (0xA).balance == 2
}bid('0xC', 5)
{refund_addr == 0xA && refund_amount == 3 && (0xA).balance == 2
}
If bid('0xB', 4)
is included in the block first, the witnesses included by bid('0xC', 5)
will be missing the state at 0xB
.
A branched state access is a read or write to a storage slot selected from a list known ahead of time.
troz: int128
poit: int128
narf: int128
@public
def add_value(value: int128):
if self.troz < 5:
self.troz = self.poit + value
else:
self.troz = self.narf + value
To show how this is DSA, imagine a scenario given the two following transactions {and their witnesses}:
add_value(2)
{troz == 3 && poit == 5
}add_value(3)
{troz == 3 && poit == 5
}
If add_value(2)
is included in the block first, the witnesses included by add_value(3)
will be missing the state for narf
.
Unlike Arbitrary State Access, it is possible to create a witness that is guaranteed to be sufficient by unconditionally adding poit
and narf
:
add_value(2)
{troz == 3 && poit == 5 && narf == 11
}add_value(3)
{troz == 3 && poit == 5 && narf == 11
}
previous_addr: address
@public
@payable
def dweep():
send(self.previous_addr, msg.value)
self.previous_addr = msg.sender
While dweep
contains DSA, a transaction cannot be invalidated by a preceding transaction in the same block.
Regardless of whether dweep()[value=11,sender=0xB]
is included before dweep()[value=23,sender=0xC]
or vise versa, every state access will have a value (provided by the preceding transaction.)
Explanation
Consider these transactions {and their witnesses}:
dweep()[value=11,sender=0xB]
{previous_addr = 0xA && (0xB).balance = 13
}dweep()[value=23,sender=0xC]
{previous_addr = 0xA && (0xC).balance = 29
}
If dweep()[value=11,sender=0xB]
is included first, the state accesses for dweep()[value=23,sender=0xC]
look like:
(0xC).balance == 29
is included indweep()[value=23,sender=0xC]
's witness.previous_addr == 0xB
is known afterdweep()[value=11,sender=0xB]
is executed.(0xB).balance == 2
is known afterdweep()[value=11,sender=0xB]
is executed.
On the other hand, if dweep(23)
is included first, the state accesses for dweep(11)
look like:
(0xB).balance == 13
is included indweep()[value=11,sender=0xB]
's witness.previous_addr == 0xC
is known afterdweep()[value=23,sender=0xC]
is executed.(0xC).balance == 6
is known afterdweep()[value=23,sender=0xC]
is executed.