Block stuffing is a type of attack in blockchains where an attacker submits
transactions that deliberately fill up the block’s gas limit and stall other
transactions. To ensure inclusion of their transactions by miners, the
attacker can choose to pay higher transaction fees. By controlling the
amount of gas spent by their transactions, the attacker can influence the number
of transactions that get to be included in the block.
To control
the amount of gas spent by the transaction, the attacker utilizes a special
contract. There is a function in the contract which takes as input the amount of
gas that the attacker wants to burn. The function runs meaningless
instructions in a loop, and either returns or throws an error when the desired
amount is burned.
For example let’s say that the average gas price has been 5 Gwei in the last
10 blocks. In order to exert influence over the next block, the attacker needs to
submit transactions with gas prices higher than that, say 100 Gwei. The higher
the gas price, the higher the chance of inclusion by miners.
The attacker can choose to divide the task of using 8,000,000 gas—current
gas limit for blocks—into as
many transactions as they want. This could be 80 transactions with
100,000 gas expenditure, or 4 transactions with 2,000,000 gas expenditure.
Deciding on how to divide the task is a matter of maximizing the chance of
inclusion, and depends on the factors outline below.
Miners’ strategy for selecting transactions
Miners want to maximize their
profit by including transactions with highest fees. In the current PoW
implementation of Ethereum, mining the block takes significantly more time
than executing the transactions. So let’s assume
all transactions in the pool are trivially executed as soon as they arrive
and miners know the amount of gas each one uses.
For miners, maximizing profit is an
optimum packing problem.
Miners want to choose a subset of the transaction pool that gives them
maximum profit per block. Since there are at least tens of thousands of
transactions in the pool at any given time, the problem can’t be solved by
brute-forcing every combination. Miners use algorithms that test a
feasible number of combinations and select the one giving the highest reward.
A block stuffer’s main goal is to target the selection process by
crafting a set of transactions that has the highest chance of being picked
up by miners in a way that will deplete blocks’ gas limits.
They can’t devise a 100% guaranteed strategy since each miner
can use a different algorithm, but they can find a sweet spot by testing out the
whole network.
(In a PoS system, our assumptions would be wrong since executing
transactions is not trivial compared to validating blocks. Validators would
need to develop more complex strategies depending on the PoS implementation.)
The transactions the attacker wants to stall:
It could be so that the
attacker wants to stall transactions with a specific contract. If the
function calls to that contract use a distinctively high amount of gas, say
between 300,000 and 500,000, then the attacker has to stuff the block in a
way that targets that range.
For example, the attacker can periodically submit n transactions
{T1,T2,…,Tn−1,Tn} with very high prices where
i=1∑nTigas≈8,000,000.
If the attacker is targeting transactions within a range of
(Rlower,Rupper), they can choose
the first n−1 transactions to deplete 8,000,000−Rupper gas
in short steps, and submit Tn to deplete the remaining Rupper
gas with a relatively higher price. Note that the revenue from including a
single transaction is
tx_fee=gas_price×gas_usage.
As gas usage decreases, the
probability of being picked up by miners decreases, so prices should increase
to compensate.
Example: Fomo3D
Fomo3D
is a gambling game where players buy keys from a contract and their money
goes into a pot. At the beginning of each round, a time counter is initiated
which starts counting back from 24 hours. Each bought key adds 30 seconds to the
counter. When the counter hits 0, the last player to have bought a key wins the
majority of the pot and the rest is distributed to others. The
way the pot is distributed depends on the team that the winner belongs to.
Key price increases with increasing key supply, which makes it harder and harder
to buy a key and ensures the round will end after some point. In time, the stakes
increase and the counter reduces to a minimum, like 2 minutes. At this
point, the players pay both high gas and key prices to be “it” and win the game.
Players program bots to buy keys for them, and winning becomes a matter of coding the
right strategy. As you can understand from the subject, the
first round was won through a block stuffing attack.
On August 22 2018, the address
0xa16…f85 won
10,469 ETH from the first round by following the strategy I outlined above. The
winner managed to be the last buyer in
block 6191896 and managed to stall
transactions with Fomo3D until block 6191909 for 175 seconds,
ending the round. Some details:
The user addresses above were scraped from the Ethereum transaction graph as being
linked to a primary account which supplied them with funds. The contract
addresses were scraped from 0-valued transactions sent from user addresses.
These have a distance
of 1, there may be other addresses involved with greater distances.
Below are details of the last 4 blocks preceding the end of the round. The rows
highlighted with yellow are transactions submitted by the attacker. The crossed
out rows are failed transactions. All transactions by the attacker were
submitted with a 501 Gwei gas price, and stuffing a single block costed around 4
ETH. The calls to buy keys generally spend around 300,000~500,000 gas, depending
on which function was called.
Below, you see the successfully stuffed
block 6191906.
Block 6191907 was a close call for the winner, because their transactions picked
up for the block did not amount up to 8,000,000 and the other transaction was a
call to Fomo3D by an opponent to buy keys. Note that it has a gas price of 5559
Gwei, which means either the bot or person who submitted the transaction was
presumably aware of the attack.
The transaction failed due to low gas limit, presumably due to a miscalculation
by the bot or the person.
Transactions in block 6191908 belonged to the attacker except for one irrelevant
transfer. This block is also considered successfully stuffed, since the
7,970,000 gas usage by the attacker leaves no space for a call to buy keys.
By block 6191909, the counter has struck zero—more like current UTC time
surpassed the round end variable stored in the contract—and any call to Fomo3D
would be the one to end the round and distribute the pot. And the first transaction
in the block is—wait for it—a call to Fomo3D to buy keys by the opponent
whose transaction failed a few blocks earlier, submitted with 5562 Gwei. So the
guy basically paid 1.7 ETH to declare the attacker the winner!
Another thing to note is that the attacker probably crafted the spender contract
to stop the attack when the round has ended, presumably to cut costs. So the 37,633
gas used by the contract are probably to call the Fomo3D contract to check
round status. All these point out to the fact that the attacker is an
experienced programmer who knows their way around Ethereum.
Here, you can see the details of the 100
blocks preceding the end of the round, with the additional information of ABI
calls and events fired in transactions.
Since the end of the first round, 2 more rounds ended with attacks similar to
this one. I didn’t analyze all of them because it’s too much for this post, but
here are some details if you want to do it yourselves.
A thing to note in the following rounds is that participation in the game
and amount of pot gradually decreased, presumably owing to the fact that
the way of beating the game has been systematized. Although anyone can attempt
such an attack, knowing how it will be won takes the “fun” factor out of it.
Credit: Although I’ve found previous instances of the term “block stuffing”
online, Nic Carter is the first
one to use it in
this context.
A bonding curve is a financial instrument proposed by Simon de la Rouviere
in his Mediumarticles. ETH is bonded in a smart contract to mint tokens, and unbonded to burn them. Every bonding and unbonding changes the price of the token according to a predefined formula. The “curves” represent the relationship between the price of a single token and the token supply. The result is an ETH-backed token that rewards early adopters.
An example supply versus price graph. The area below the curve is equal to the amount of ETH E that must be spent to increase the supply from S0 to S1, or that is going to be received when S1−S0 tokens are unbonded.
Inside a transaction, the price paid/received per token is not constant and depends on the amount that is bonded or unbonded. This complicates the calculations.
Let’s say for an initial supply of S0, we want to bond T tokens which are added to the new supply S1=S0+T. The ETH E that must be spent for this bonding is defined as
E=∫S0S1PdS
which is illustrated in the figure above. If one wanted to unbond T tokens, the upper limit for the integral would be S0 and the lower S0−T, with E corresponding to the amount of ETH received for the unbonding.
Linear Curves
A linear relationship for the bonding curves are defined as
P(S)=P0+SIp
where P0 is the initial price of the token and Ip is the price increment per token.
Bonding Tokens
Let us have E ETH which we want to bond tokens with. Substituting P into the integral above with the limits S0→S0+T, we obtain E in terms of the tokens T that we want to bond:
E(S,T)=TP0+TIpS+21T2Ip
where S is the supply before the bonding. Solving this for T, we obtain the tokens received in a bonding as a function of the supply and ETH spent:
T(S,E)=IpS2Ip2+2EIp+2SP0Ip+P02−P0−S.
Unbonding Tokens
Let us have T tokens which we want to unbond for ETH. Unbonding T tokens decreases the supply from S0 to S0−T, which we apply as limits for the above integral and obtain:
E(S,T)=TP0+TIpS−21T2Ip.
Breaking Even in PoWH3D
PoWH3D is one of the applications of bonding curves with a twist: 1/10th of every transaction is distributed among token holders as dividends. When you bond tokens with E ETH, you receive 9/10E worth of tokens and 1/10E is distributed to everybody else in proportion to the amount they hold.
This means you are at a loss when you bond P3D (the token used by PoWH3D). If you were to unbond immediately, you would only receive 81% of your money. Given the situation, one wonders when exactly one can break even with their investment. The activity in PoWH3D isn’t deterministic; nonetheless we can deduce sufficient but not necessary conditions for breaking even in PoWH3D.
Sufficient Bonding
Let us spend E1 ETH to bond tokens at supply S0. The following calculations are
done with the assumption that the tokens received
T1=T(S0,9E1/10)
are small enough to be
neglected, that is T1≪S0 and S1≈S0. In other words, this only
holds for non-whale bondings.
Then let others spend E2 ETH to bond tokens and raise the supply to S2.
The objective is to find an E2 large enough to earn us dividends and make us
break even when we unbond our tokens at S2.
We have
S2=S0+T(S0,E2).
Our new share of the P3D pool is T1/S2 and the dividends we earn from
the bonding is equal to
101S2T1E2.
Then the condition for breaking even is
109E(S2,T1)+101S2T1E2≥E1.
This inequality has a lengthy analytic solution which is impractical to
typeset. The definition should be enough:
E_2}\left\{\frac{9}{10} E(S_2, T_1) + \frac{1}{10}\frac{T_1}{S_2}E_2 = E_1\right\}E2suff(S0,E1):=solve for E_2{109E(S2,T1)+101S2T1E2=E1}
and
E2≥E2suff.
E2suff can be obtained from the source of this page in JavaScript from
the function sufficient_bonding. The function involves many power
and square operations and may yield inexact results for too high values of
S0 or too small values off E1, due to insufficient precision of the
underlying math functions. For this reason, the calculator is disabled
for sensitive input.
S0 versus E2suff for E1=100.
The relationship between the initial supply and sufficient bonding is roughly
quadratic, as seen from the graph above. This means that the difficulty of
breaking even increases quadratically as more people bond into P3D. As interest in
PoWH3D saturates, dividends received from the supply increase decreases
quadratically.
Logarithmic plot of S0 versus E2suff for changing values of E1.
The relationship is not exactly quadratic, as seen from the graph above. The
function is sensitive to E1 for small values of S0.
Sufficient Unbonding
Let us spend E1 ETH to bond tokens at supply S0 and receive T1 tokens.
Then let others unbond T2 P3D to lower the supply to S2. The objective is to find a T2 large enough to earn us dividends and make us break even when we unbond our tokens at S2. We have
S2=S0−T2.
Our new share of the P3D pool is T1/S2 and the dividends we earn from the bonding is equal to
T2suff can be obtained from the function sufficient_unbonding.
S0 versus T2suff for E1=100.
The relationship between S0 and T2suff is linear and insensitive to E1. Regardless of the ETH you invest, the amount of tokens that need to be unbonded to guarantee your break-even is roughly the same, depending on your entry point.
Calculator
Below is a calculator you can input S0 and E1 to calculate E2suff and T2suff.
S0
E1
E2suff
T2suff
For the default values above, we read this as:
For 100 ETH worth of P3D bonded
at 3,500,000 supply, either a bonding of ~31715 ETH
or an unbonding of ~3336785 P3D
made by other people is sufficient to
break even.
In order to follow these statistics, you can follow
this site.
Conclusion
Bonding curve calculations can get complicated because the price paid per token depends on the amount of intended bonding/unbonding. With this work, I aimed to clarify the logic behind PoWH3D. Use the formulation and calculator at your own risk.
The above conditions are only sufficient and not necessary to break even. As PoWH3D becomes more popular, it gets quadratically more difficult to break even from a supply increase. PoWH3D itself doesn’t generate any value or promise long-term returns for its holders. However every bond, unbond and transfer deliver dividends. According to its creators, P3D is intended to become the base token for a number of games that will be built upon PoWH3D, like FOMO3D.
When utilizing Galerkin-type solutions for
IBVPs, we often
have to compute integrals using numerical methods such as
Gauss quadrature. In
such a solution, we solve for the values of a function at mesh nodes, whereas
the integration takes place at the quadrature points. Depending on the case,
we may need to compute the values of a function at mesh nodes, given their
values at quadrature points, e.g. stress recovery for mechanical problems.
There are many ways of achieving this, such as
superconvergent patch recovery.
In this post, I wanted to document a widely used solution which is easy to
implement, and which is used in research oriented codebases such as
FEAP.
L2 Projection
Given a function u∈L2(Ω), its projection into a finite element space
Vh⊂L2(Ω) is defined through the following optimization
problem:
Find uh∈Vh such that
Π(uh):=21∥uh−u∥L2(Ω)2→min(1)
There is a unique solution to the problem since Π(⋅) is convex. Taking
its variation, we have
$DΠ(uh)⋅vh=⟨uh−u,vh⟩=0(2)$
for all vh∈Vh. Thus we have the following variational formulation
Thus L2 projection requires the solution of a linear system
Mu=b
which depending on the algorithm used, can have a complexity of at least
O(n2) and at most O(n3).
Lumped L2 Projection
The L2 projection requires the solution of a system which can be
computationally expensive. It is possible to convert the
matrix—called the mass matrix in literature—to a diagonal
form through a procedure called lumping.
The operator for row summation is defined as
rowsum(⋅)i:=j=1∑n(⋅)ij(7)
For the mass matrix, we have
rowsumMI=J=1∑n∫ΩNJNIdx=∫ΩNIdx=:mI(8)
since ∑J=1nNJ=1.
Substituting the lumped mass matrix allows us to decouple the linear system of
equations in (5) and instead write
mIuI=bI(9)
for I=1,…,n. The lumped L2 projection is then as simple as
uI=mIbI=∫ΩNIdx∫ΩuNIdx(10)
This results in a very efficient algorithm with O(n) complexity.
Conclusion
Lumped L2 projection is a faster working approximation to L2 projection that is
easy to implement for quick results. You can use it when developing a solution
for an IBVP, and don’t want to wait too long when debugging, while not
forgetting that it introduces some error.
where BI=∇NI are the gradients of the shape
functions NI and C is the linear elasticity tensor (you see the
contraction of their components in the equation).
Despite being of the most explicit form, these types of indicial expressions are
avoided in most texts on finite elements. There are two reasons for this:
Engineers are not taught the Einstein summation convention.
The presence of indices result in a seemingly cluttered expression.
They avoid the indicial expression by reshaping it into matrix multiplications.
In engineering notation, the left- and right-hand sides are reshaped as
Aαβ=∫ΩBγαCγδBδβdv(2)
which allows us to write
A=∫ΩB~TC~B~dv(3)
The matrices B~ and C~ are set on with tildes in order to
differentiate them from the boldface symbols used in the previous sections.
Here,
C~ is a matrix containing the unique components of the elasticity
tensor C, according to the Voigt notation.
In this reshaping, only the minor symmetries are taken into account.
If the dimension of the vectorial problem is d, then C~ is of the size
d(d+1)/2×d(d+1)/2. For example, if the problem is 3 dimensional, C~
is of the size 6×6:
B~ is a nd×d(d+1)/2 matrix whose components are
adjusted so that (2) is equivalent to (1). It
has the components of BI for I=1,…,n where n is the number of
basis functions. Since B~ is adjusted to account for the reshaping
of C, it has many zero components. A 3d example:
Although (3) looks nice on paper, it is much less optimal for
implementation. Implementing it requires the implementation of
(5), which adds another layer of complexity to the algorithm.
The same cannot be said for (4), because using Voigt
notation might be more efficient in inelastic problems. In the most complex
problems, the most efficient method is to implement (1) in
conjunction with Voigt notation.
To prove the inefficiency of (3) we can readily compare it with
(1) in terms of required number of iterations. Indices in
(1) have the following ranges:
I,J=1,…,nandi,j,k,l=1,…,d(6)
so n2d4 iterations are required. Indices in
(2) have the following ranges:
α,β=1,…,ndandγ,δ=1,…,d(d+1)/2(7)
so
(nd)2(2d(d+1))2=n2d44(d+1)2(8)
iterations are required. So engineering notation requires (d+1)2/4 times more
equations than index notation. For d=2, engineering notation is 2.25
times slower and for d=3 it is 4 times slower. For example, calculation of a
stiffness matrix for n=8 and d=3 requires 20736 iterations for
engineering notation, whereas it only requires 5184 iterations for index notation.
Although (3) seems less cluttered, what actually happens is
that one
trades off complexity in one expression for a much increased complexity in
another one, in this case (5).
And to make it worse, it results in a slower algorithm.
The only obstacle to the widespread adoption of
index notation seems to be its lack in undergraduate engineering curricula.
If engineers were taught the index notation and summation convention as well
as the formal notation, such expressions would not be as confusing at first
sight. A good place would be in elementary calculus and physics courses, where
one heavily uses vector calculus.
There are many books that give an outline of hyperelasticity, but there are few
that try to help the reader implement solutions, and even fewer that
manage to do it in a concise manner. Peter Wriggers’
Nonlinear Finite Element Methods
is a great reference for those who like to
roll up their sleeves and get lost in theory. It helped me understand a lot
about how solutions to hyperelastic and inelastic problems are implemented.
One thing did not quite fit my taste though—it was very formal in the way that
it didn’t give out indicial expressions. And if it wasn’t clear up until this
point, I love indicial expressions, because they pack enough information to
implement a solution in a single line. Almost all books skip these
because they seem cluttered and the professors who wrote them think they’re
trivial to derive. In fact, they are not.
So below, I’ll try to derive indicial expressions for the update equations of
hyperelasticity.
which describes the elastic energy stored in the solid, i.e. energy
density per unit mass of the reference configuration.
The total energy stored in B is described by the the stored energy
functional
E(φ):=∫BΨ(F)dm=∫Bρ0Ψ(F)dV(2)
The loads acting on the body also form a potential:
L(φ):=∫Bρ0Γˉ⋅φdV+∫∂BtTˉ⋅φdA(3)
where Γˉ and Tˉ are the prescribed body forces per unit mass and surface tractions
respectively, where T=PN with Cauchy’s stress theorem.
The potential energy of B for deformation φ is defined as
Π(φ):=E(φ)−L(φ)(4)
Thus the variational formulation reads
Find φ∈V such that the functional
Π(φ)=∫Bρ0Ψ(F)dV−∫Bρ0Γˉ⋅φdV−∫∂BtTˉ⋅φdA(5)
is minimized for φ=φˉ on ∂Bu.
The solution is one that minimizes the potential energy:
φ∗=argminφ∈VΠ(φ)(6)
A stationary point for Π means that its first variation vanishes: ΔΠ=0.
We can write a Eulerian version of this form by pushing-forward the stresses and
strains.
The Almansi strain e is the pull-back of the Green-Lagrange strain E and
vice versa:
e=φ∗(E)=F−TEF−1andE=φ∗(e)=FTEF(11)
Commutative diagram for the pull-back and push-forward relationships of the Green-Lagrange and
Almansi strain tensors.
Thus we can deduce the variation of the Almansi strain
Here, we substituted the following differential identities:
ρ0ΓdV=ργdv(18)
for the body forces, and
TdA=PNdA=σJF−TNdA=σnda=tda(19)
for the surface tractions, where we used the Piola identity.
Linearization of the Variational Formulation
We linearize G:
LinGφˉ=G(φˉ,δφ)+ΔGφˉ=0(20)
Then we have the variational setting
a(Δφ,δφ)=b(δφ)(21)
where
a(Δφ,δφ)=ΔGφˉandb(δφ)=−G(φˉ,δφ)(22)
Commutative diagram of the linearized solution procedure. Each
iteration brings the current iterate φˉ closer to the optimum
value φ∗.
Mappings between line elements belonging to the tangent spaces of
the linearization.
The variation ΔG is calculated as
ΔG=DφG⋅Δφ=∫B[ΔS:ΔE+S:Δ(ΔE)]dV(23)
Consecutive variations of the Green-Lagrange strain tensor is calculated as
Δ(ΔE)=DφΔE⋅Δφ=21[∇(δφ)T∇(Δφ)+∇(Δφ)T∇(δφ)](24)
The term on the left is calculated as
ΔS=DφS⋅Δφ=∂C∂S:ΔC=2∂C∂S:ΔE(25)
where we substitute the Lagrangian elasticity tensor
C:=2∂C∂S=4ρ0∂C∂C∂2Ψ(26)
and ΔE is calculated in the same manner as ΔE:
ΔE=21[∇(Δφ)TF+FT∇(Δφ)](27)
Then the variational forms of the linearized setting are
If we introduce the Cauchy stress tensor σ and corresponding elasticity tensor
cσ=c/J,
our variational formulation can be expressed completely in terms of Eulerian quantities:
It has been proven and all, but it’s still called magic because its reasoning is
not apparent at first glance. The magic formula is actually a superset of
the divergence theorem, generalized to discontinuous fields. But to make that
generalization, we need to abandon the standard formulation which starts by
creating a triangular mesh, and consider arbitrary partitionings of a domain.
A domain Ω is partitioned into parts Pi, i=1,…,n as follows:
Ω=i=1⋃nPiP={P1,…,Pn}
We call the set of parts P a partition of Ω.
Broken Hilbert Spaces
We allow the vector field u to be discontinuous at
boundaries∂Pi and continuous inPi, i=1,…,n.
To this end, we define the broken Hilbert
space over partition P
Hm(P):={v∈L2(Ω)nd∣∀P∈P,v∣P∈Hm(P)}(1)
It can be seen that Hm(P)⊆Hm(Ω).
Part Boundaries
Topologically, a part may share boundary with Ω, like P4.
In that case, the boundary of the part is divided into an
interior boundary and exterior boundary:
∂Pexti=∂Pi∩∂Ωand∂Pinti=∂Pi∖∂Pexti(2)
If a part has an exterior boundary, it is said to be an external part
(P3, P4, P5, P6). If it
does not have any exterior boundary, it is said to be an internal
part.(P1, P2).
Divergence theorem over parts
For a vector field v∈H1(P),
i=1,…,n, we can write the following integral as a sum and apply the
divergence theorem afterward
It’s obvious that the union of part external boundaries equals the domain boundary:
i=1⋃n∂Pexti=∂Ω(13)
which allows us to write
i=1∑n∫∂Pextiv⋅ndA=∫∂Ωv⋅ndA(14)
With the results obtained, we put forward a generalized version of the divergence
theorem: Let v∈H1(P) be a vector field. Then we have
∫ΩdivvdV=∫∂Ωv⋅ndA+Γ∈I∑∫Γ[[v]]⋅ndA(15)
Verbally,
the integral of the divergence of a vector field over a domainΩ
equals
its integral over the domain boundary∂Ω,
plus
the integral of its jump over part interfacesI.
In the case of a continuous field, the jumps vanish and this
reduces to the regular divergence theorem.
The Magic Formula
There are different versions of the magic formula for scalar, vector and tensor
fields, and for different IBVPs. I won’t try to derive them all, but give an
example: If we were substitute a linear mapping
Av
instead of v, we would have the jump
[[Av]] on the right-hand side.
We introduce the vector and tensor average operator {⋅}
{v}Γij=21(vij+vji)and{A}Γij=21(Aij+Aji)(16)
and tensor jump operator
[[⋅]]
[[A]]Γij=Aij−Aji(17)
We also note the boundary jump/average property which is used on the integral
over ∂Ω
{v}=[[v]]=von∂Ω(18)
(This property is used implicitly in many places, and often causes confusion).
These definitions allow us to write the identity
[[Av]]=[[A]]{v}+{A}[[v]](19)
which is easily proven when expanded.
The different versions of the magic formula are obtained by
substituting the identities above—or their analogs—in the discontinuous
divergence theorem.
Douglas N. Arnold. An interior penalty finite element method with discontinuous elements. SIAM J. Numer. Anal., 19(4):742–760, 1982. ↩