Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
N
nebula
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
exchain
nebula
Commits
5f1c1478
Unverified
Commit
5f1c1478
authored
Jun 22, 2023
by
mergify[bot]
Committed by
GitHub
Jun 22, 2023
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'develop' into refcell/first/stylestack
parents
bddc53d6
38003570
Changes
18
Hide whitespace changes
Inline
Side-by-side
Showing
18 changed files
with
491 additions
and
180 deletions
+491
-180
README.md
cannon/docs/README.md
+2
-3
evm_test.go
cannon/mipsevm/evm_test.go
+2
-2
memory.go
cannon/mipsevm/memory.go
+1
-2
mips.go
cannon/mipsevm/mips.go
+0
-9
alphabet_provider_test.go
op-challenger/fault/alphabet_provider_test.go
+32
-0
position.go
op-challenger/fault/cmd/examples/position.go
+35
-0
solver.go
op-challenger/fault/cmd/examples/solver.go
+83
-0
main.go
op-challenger/fault/cmd/main.go
+4
-35
game.go
op-challenger/fault/game.go
+13
-0
solver.go
op-challenger/fault/solver.go
+91
-0
solver_test.go
op-challenger/fault/solver_test.go
+63
-0
types.go
op-challenger/fault/types.go
+8
-0
price_bump_test.go
op-service/txmgr/price_bump_test.go
+10
-9
txmgr.go
op-service/txmgr/txmgr.go
+63
-33
txmgr_test.go
op-service/txmgr/txmgr_test.go
+26
-20
.gas-snapshot
packages/contracts-bedrock/.gas-snapshot
+1
-1
Bytes.t.sol
packages/contracts-bedrock/contracts/test/Bytes.t.sol
+55
-53
server.go
proxyd/server.go
+2
-13
No files found.
cannon/docs/README.md
View file @
5f1c1478
...
...
@@ -32,9 +32,8 @@ And as it executes each step, it can optionally produce the witness data for the
The Cannon CLI is used to load a program into an initial state,
transition it N steps quickly without witness generation, and 1 step while producing a witness.
`mipsevm`
is backed by the Unicorn emulator, but instrumented for proof generation,
and handles delay-slots by isolating each individual instruction and tracking
`nextPC`
to emulate the delayed
`PC`
changes after delay-slot execution.
`mipsevm`
is instrumented for proof generation and handles delay-slots by isolating each individual instruction
and tracking
`nextPC`
to emulate the delayed
`PC`
changes after delay-slot execution.
## Witness Data
...
...
cannon/mipsevm/evm_test.go
View file @
5f1c1478
...
...
@@ -108,7 +108,7 @@ func TestEVM(t *testing.T) {
// TODO: maybe more readable to decode the evmPost state, and do attribute-wise comparison.
uniPost
:=
us
.
state
.
EncodeWitness
()
require
.
Equal
(
t
,
hexutil
.
Bytes
(
uniPost
)
.
String
(),
hexutil
.
Bytes
(
evmPost
)
.
String
(),
"
unicorn
produced different state than EVM"
)
"
mipsevm
produced different state than EVM"
)
}
require
.
Equal
(
t
,
uint32
(
endAddr
),
state
.
PC
,
"must reach end"
)
// inspect test result
...
...
@@ -175,7 +175,7 @@ func TestHelloEVM(t *testing.T) {
// TODO: maybe more readable to decode the evmPost state, and do attribute-wise comparison.
uniPost
:=
us
.
state
.
EncodeWitness
()
require
.
Equal
(
t
,
hexutil
.
Bytes
(
uniPost
)
.
String
(),
hexutil
.
Bytes
(
evmPost
)
.
String
(),
"
unicorn
produced different state than EVM"
)
"
mipsevm
produced different state than EVM"
)
}
end
:=
time
.
Now
()
delta
:=
end
.
Sub
(
start
)
...
...
cannon/mipsevm/memory.go
View file @
5f1c1478
...
...
@@ -11,8 +11,7 @@ import (
"github.com/ethereum/go-ethereum/crypto"
)
// Note: 2**12 = 4 KiB, the minimum page-size in Unicorn for mmap
// as well as the Go runtime min phys page size.
// Note: 2**12 = 4 KiB, the min phys page size in the Go runtime.
const
(
PageAddrSize
=
12
PageKeySize
=
32
-
PageAddrSize
...
...
cannon/mipsevm/mips.go
View file @
5f1c1478
...
...
@@ -56,15 +56,6 @@ func (m *InstrumentedState) handleSyscall() error {
v0
=
a0
//fmt.Printf("mmap hint 0x%x size 0x%x\n", v0, sz)
}
// Go does this thing where it first gets memory with PROT_NONE,
// and then mmaps with a hint with prot=3 (PROT_READ|WRITE).
// We can ignore the NONE case, to avoid duplicate/overlapping mmap calls to unicorn.
//prot := a2
//if prot != 0 {
// if err := mu.MemMap(uint64(v0), uint64(sz)); err != nil {
// log.Fatalf("mmap fail: %v", err)
// }
//}
case
4045
:
// brk
v0
=
0x40000000
case
4120
:
// clone (not supported)
...
...
op-challenger/fault/alphabet_provider_test.go
View file @
5f1c1478
...
...
@@ -8,6 +8,38 @@ import (
"github.com/stretchr/testify/require"
)
// TestAlphabetProvider_Get_ClaimsByTraceIndex tests the [fault.AlphabetProvider] Get function.
func
TestAlphabetProvider_Get_ClaimsByTraceIndex
(
t
*
testing
.
T
)
{
// Create a new alphabet provider.
canonicalProvider
:=
NewAlphabetProvider
(
"abcdefgh"
,
uint64
(
3
))
// Build a list of traces.
traces
:=
[]
struct
{
traceIndex
uint64
expectedHash
common
.
Hash
}{
{
7
,
common
.
HexToHash
(
"0x0000000000000000000000000000000000000000000000000000000000000768"
),
},
{
3
,
common
.
HexToHash
(
"0x0000000000000000000000000000000000000000000000000000000000000364"
),
},
{
5
,
common
.
HexToHash
(
"0x0000000000000000000000000000000000000000000000000000000000000566"
),
},
}
// Execute each trace and check the alphabet provider returns the expected hash.
for
_
,
trace
:=
range
traces
{
expectedHash
,
err
:=
canonicalProvider
.
Get
(
trace
.
traceIndex
)
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
trace
.
expectedHash
,
expectedHash
)
}
}
// FuzzIndexToBytes tests the IndexToBytes function.
func
FuzzIndexToBytes
(
f
*
testing
.
F
)
{
f
.
Fuzz
(
func
(
t
*
testing
.
T
,
index
uint64
)
{
...
...
op-challenger/fault/cmd/examples/position.go
0 → 100644
View file @
5f1c1478
package
examples
import
(
"github.com/ethereum-optimism/optimism/op-challenger/fault"
)
func
PositionExampleOne
()
{
// Example 1
// abcdefgh
// abcdexyz
// go left to d, then right to f, then left to e
p
:=
fault
.
Position
{}
p
.
Print
(
3
)
p
.
Attack
()
p
.
Print
(
3
)
p
.
Defend
()
p
.
Print
(
3
)
p
.
Attack
()
p
.
Print
(
3
)
}
func
PositionExampleTwo
()
{
// Example 2
// abcdefgh
// abqrstuv
// go left r, then left to b, then right to q
p
:=
fault
.
Position
{}
p
.
Print
(
3
)
p
.
Attack
()
p
.
Print
(
3
)
p
.
Attack
()
p
.
Print
(
3
)
p
.
Defend
()
p
.
Print
(
3
)
}
op-challenger/fault/cmd/examples/solver.go
0 → 100644
View file @
5f1c1478
package
examples
import
(
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum-optimism/optimism/op-challenger/fault"
)
// SolverExampleOne uses the [fault.Solver] with a [fault.AlphabetProvider]
// to print out fault game traces for the "abcdexyz" counter-state.
func
SolverExampleOne
()
{
fmt
.
Println
()
fmt
.
Println
(
"Solver: Example 1"
)
fmt
.
Println
()
// Construct the fault position.
canonical
:=
"abcdefgh"
disputed
:=
"abcdexyz"
maxDepth
:=
3
parent
:=
fault
.
Claim
{
Value
:
common
.
HexToHash
(
"0x0000000000000000000000000000000000000000000000000000000000000768"
),
Position
:
fault
.
NewPosition
(
0
,
0
),
}
canonicalProvider
:=
fault
.
NewAlphabetProvider
(
canonical
,
uint64
(
maxDepth
))
disputedProvider
:=
fault
.
NewAlphabetProvider
(
disputed
,
uint64
(
maxDepth
))
// Create a solver with the canonical provider.
solver
:=
fault
.
NewSolver
(
maxDepth
,
canonicalProvider
)
// Print the initial state.
fmt
.
Println
(
"Canonical state: "
,
canonical
)
fmt
.
Println
(
"Disputed state: "
,
disputed
)
fmt
.
Println
()
fmt
.
Println
(
"Proceeding with the following moves:"
)
fmt
.
Println
(
"go left to d, then right to f, then left to e"
)
fmt
.
Println
()
// Get the claim from the disputed provider.
claim
,
err
:=
disputedProvider
.
Get
(
3
)
if
err
!=
nil
{
fmt
.
Printf
(
"error getting claim from disputed provider: %v"
,
err
)
}
firstDisputedClaim
:=
fault
.
Claim
{
Value
:
claim
,
Position
:
fault
.
NewPosition
(
1
,
0
),
}
res
,
err
:=
solver
.
NextMove
(
firstDisputedClaim
,
parent
)
if
err
!=
nil
{
fmt
.
Printf
(
"error getting next move: %v"
,
err
)
}
fmt
.
Printf
(
"Disputed claim: %s
\n
"
,
claim
)
fmt
.
Printf
(
"Expected claim: %s
\n
"
,
parent
.
Value
)
fmt
.
Printf
(
"Response: [Attack: %v, Value: %s]
\n
"
,
res
.
Attack
,
res
.
Value
)
fmt
.
Println
()
// Get the next claim from the disputed provider.
claim
,
err
=
disputedProvider
.
Get
(
5
)
if
err
!=
nil
{
fmt
.
Printf
(
"error getting claim from disputed provider: %v"
,
err
)
}
firstDisputedClaim
=
fault
.
Claim
{
Value
:
claim
,
Position
:
fault
.
NewPosition
(
2
,
2
),
}
res
,
err
=
solver
.
NextMove
(
firstDisputedClaim
,
parent
)
if
err
!=
nil
{
fmt
.
Printf
(
"error getting next move: %v"
,
err
)
}
fmt
.
Printf
(
"Disputed claim: %s
\n
"
,
claim
)
fmt
.
Printf
(
"Expected claim: %s
\n
"
,
parent
.
Value
)
fmt
.
Printf
(
"Response: [Attack: %v, Value: %s]
\n
"
,
res
.
Attack
,
res
.
Value
)
fmt
.
Println
()
// This marks the end of the game!
if
res
.
Attack
{
fmt
.
Println
(
"Game successfully completed!"
)
}
else
{
fmt
.
Println
(
"Game failed!"
)
}
fmt
.
Println
()
}
op-challenger/fault/cmd/main.go
View file @
5f1c1478
package
main
import
(
"github.com/ethereum-optimism/optimism/op-challenger/fault"
"github.com/ethereum-optimism/optimism/op-challenger/fault
/cmd/examples
"
)
func
main
()
{
// Example 1
// abcdefgh
// abcdexyz
// go left to d, then right to f, then left to e
p
:=
fault
.
Position
{}
p
.
Print
(
3
)
p
.
Attack
()
p
.
Print
(
3
)
p
.
Defend
()
p
.
Print
(
3
)
p
.
Attack
()
p
.
Print
(
3
)
examples
.
SolverExampleOne
()
// GIN: 1 Trace Position is 0 Trace Depth is: 0 Trace Index is: 8
// GIN: 10 Trace Position is 0 Trace Depth is: 1 Trace Index is: 4
// GIN: 110 Trace Position is 10 Trace Depth is: 2 Trace Index is: 6
// GIN: 1100 Trace Position is 100 Trace Depth is: 3 Trace Index is: 5
// Example 2
// abcdefgh
// abqrstuv
// go left r, then left to b, then right to q
p
=
fault
.
Position
{}
p
.
Print
(
3
)
p
.
Attack
()
p
.
Print
(
3
)
p
.
Attack
()
p
.
Print
(
3
)
p
.
Defend
()
p
.
Print
(
3
)
// Trace Position is 0000 Trace Depth is: 0 Trace Index is: 8
// Trace Position is 0000 Trace Depth is: 1 Trace Index is: 4
// Trace Position is 0000 Trace Depth is: 2 Trace Index is: 2
// Trace Position is 0010 Trace Depth is: 3 Trace Index is: 3
// examples.PositionExampleOne()
// examples.PositionExampleTwo()
}
op-challenger/fault/game.go
0 → 100644
View file @
5f1c1478
package
fault
// Game is an interface that represents the state of a dispute game.
type
Game
interface
{
// Put adds a claim into the game state and returns its parent claim.
Put
(
claim
Claim
)
(
Claim
,
error
)
// ClaimPairs returns a list of claim pairs.
ClaimPairs
()
[]
struct
{
claim
Claim
parent
Claim
}
}
op-challenger/fault/solver.go
0 → 100644
View file @
5f1c1478
package
fault
import
(
"errors"
"github.com/ethereum/go-ethereum/common"
)
// Solver uses a [TraceProvider] to determine the moves to make in a dispute game.
type
Solver
struct
{
TraceProvider
gameDepth
int
}
// NewSolver creates a new [Solver] using the provided [TraceProvider].
func
NewSolver
(
gameDepth
int
,
traceProvider
TraceProvider
)
*
Solver
{
return
&
Solver
{
traceProvider
,
gameDepth
,
}
}
// NextMove returns the next move to make given the current state of the game.
func
(
s
*
Solver
)
NextMove
(
claim
Claim
,
parent
Claim
)
(
*
Response
,
error
)
{
parentCorrect
,
err
:=
s
.
agreeWithClaim
(
parent
)
if
err
!=
nil
{
return
nil
,
err
}
claimCorrect
,
err
:=
s
.
agreeWithClaim
(
claim
)
if
err
!=
nil
{
return
nil
,
err
}
if
parentCorrect
&&
claimCorrect
{
// We agree with the parent, but the claim is disagreeing with it.
// Since we agree with the claim, the difference must be to the right of the claim
return
s
.
defend
(
claim
)
}
else
if
parentCorrect
&&
!
claimCorrect
{
// We agree with the parent, but the claim disagrees with it.
// Since we disagree with the claim, the difference must be to the left of the claim
return
s
.
attack
(
claim
)
}
else
if
!
parentCorrect
&&
claimCorrect
{
// Do nothing, we disagree with the parent, but this claim has correctly countered it
return
s
.
doNothing
()
}
else
if
!
parentCorrect
&&
!
claimCorrect
{
// We disagree with the parent so want to counter it (which the claim is doing)
// but we also disagree with the claim so there must be a difference to the left of claim
// Note that we will create the correct counter-claim for parent when it is evaluated, no need to do it here
return
s
.
attack
(
claim
)
}
// This should not be reached
return
nil
,
errors
.
New
(
"no next move"
)
}
func
(
s
*
Solver
)
doNothing
()
(
*
Response
,
error
)
{
return
nil
,
nil
}
// attack returns a response that attacks the claim.
func
(
s
*
Solver
)
attack
(
claim
Claim
)
(
*
Response
,
error
)
{
claim
.
Position
.
Attack
()
value
,
err
:=
s
.
traceAtPosition
(
&
claim
.
Position
)
if
err
!=
nil
{
return
nil
,
err
}
return
&
Response
{
Attack
:
true
,
Value
:
value
},
nil
}
// defend returns a response that defends the claim.
func
(
s
*
Solver
)
defend
(
claim
Claim
)
(
*
Response
,
error
)
{
claim
.
Position
.
Defend
()
value
,
err
:=
s
.
traceAtPosition
(
&
claim
.
Position
)
if
err
!=
nil
{
return
nil
,
err
}
return
&
Response
{
Attack
:
false
,
Value
:
value
},
nil
}
// agreeWithClaim returns true if the [Claim] is correct according to the internal [TraceProvider].
func
(
s
*
Solver
)
agreeWithClaim
(
claim
Claim
)
(
bool
,
error
)
{
ourValue
,
err
:=
s
.
traceAtPosition
(
&
claim
.
Position
)
return
ourValue
==
claim
.
Value
,
err
}
// traceAtPosition returns the [common.Hash] from internal [TraceProvider] at the given [Position].
func
(
s
*
Solver
)
traceAtPosition
(
p
*
Position
)
(
common
.
Hash
,
error
)
{
// todo: update TraceIndex to return a uint64 to keep types consistent
index
:=
p
.
TraceIndex
(
s
.
gameDepth
)
hash
,
err
:=
s
.
Get
(
uint64
(
index
))
return
hash
,
err
}
op-challenger/fault/solver_test.go
0 → 100644
View file @
5f1c1478
package
fault
import
(
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require"
)
// TestSolver_NextMove_Opponent tests the [Solver] NextMove function
// with an [fault.AlphabetProvider] as the [TraceProvider].
func
TestSolver_NextMove_Opponent
(
t
*
testing
.
T
)
{
// Construct the solver.
maxDepth
:=
3
canonicalProvider
:=
NewAlphabetProvider
(
"abcdefgh"
,
uint64
(
maxDepth
))
solver
:=
NewSolver
(
maxDepth
,
canonicalProvider
)
// The following claims are created using the state: "abcdexyz".
// The responses are the responses we expect from the solver.
indices
:=
[]
struct
{
traceIndex
int
claim
Claim
parent
Claim
response
*
Response
}{
{
3
,
Claim
{
Value
:
common
.
HexToHash
(
"0x0000000000000000000000000000000000000000000000000000000000000364"
),
Position
:
NewPosition
(
1
,
0
),
},
Claim
{
Value
:
common
.
HexToHash
(
"0x0000000000000000000000000000000000000000000000000000000000000768"
),
Position
:
NewPosition
(
0
,
0
),
},
&
Response
{
Attack
:
false
,
Value
:
common
.
HexToHash
(
"0x0000000000000000000000000000000000000000000000000000000000000566"
),
},
},
{
5
,
Claim
{
Value
:
common
.
HexToHash
(
"0x0000000000000000000000000000000000000000000000000000000000000578"
),
Position
:
NewPosition
(
2
,
2
),
},
Claim
{
Value
:
common
.
HexToHash
(
"0x0000000000000000000000000000000000000000000000000000000000000768"
),
Position
:
NewPosition
(
1
,
1
),
},
&
Response
{
Attack
:
true
,
Value
:
common
.
HexToHash
(
"0x0000000000000000000000000000000000000000000000000000000000000465"
),
},
},
}
for
_
,
test
:=
range
indices
{
res
,
err
:=
solver
.
NextMove
(
test
.
claim
,
test
.
parent
)
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
test
.
response
,
res
)
}
}
op-challenger/fault/types.go
View file @
5f1c1478
package
fault
import
(
"context"
"errors"
"github.com/ethereum/go-ethereum/common"
...
...
@@ -26,4 +27,11 @@ type Claim struct {
type
Response
struct
{
Attack
bool
// note: can we flip this to true == going right / defending??
Value
common
.
Hash
Parent
Claim
}
// Responder takes a response action & executes.
// For full op-challenger this means executing the transaction on chain.
type
Responder
interface
{
Respond
(
ctx
context
.
Context
,
response
Response
)
error
}
op-service/txmgr/price_bump_test.go
View file @
5f1c1478
...
...
@@ -30,51 +30,52 @@ func (tc *priceBumpTest) run(t *testing.T) {
}
func
TestUpdateFees
(
t
*
testing
.
T
)
{
require
.
Equal
(
t
,
int64
(
10
),
priceBump
,
"test must be updated if priceBump is adjusted"
)
tests
:=
[]
priceBumpTest
{
{
prevGasTip
:
100
,
prevBasefee
:
1000
,
newGasTip
:
90
,
newBasefee
:
900
,
expectedTip
:
1
00
,
expectedFC
:
210
0
,
expectedTip
:
1
10
,
expectedFC
:
231
0
,
},
{
prevGasTip
:
100
,
prevBasefee
:
1000
,
newGasTip
:
101
,
newBasefee
:
1000
,
expectedTip
:
11
5
,
expectedFC
:
2415
,
expectedTip
:
11
0
,
expectedFC
:
2310
,
},
{
prevGasTip
:
100
,
prevBasefee
:
1000
,
newGasTip
:
100
,
newBasefee
:
1001
,
expectedTip
:
11
5
,
expectedFC
:
2415
,
expectedTip
:
11
0
,
expectedFC
:
2310
,
},
{
prevGasTip
:
100
,
prevBasefee
:
1000
,
newGasTip
:
101
,
newBasefee
:
900
,
expectedTip
:
11
5
,
expectedFC
:
2415
,
expectedTip
:
11
0
,
expectedFC
:
2310
,
},
{
prevGasTip
:
100
,
prevBasefee
:
1000
,
newGasTip
:
90
,
newBasefee
:
1010
,
expectedTip
:
11
5
,
expectedFC
:
2415
,
expectedTip
:
11
0
,
expectedFC
:
2310
,
},
{
prevGasTip
:
100
,
prevBasefee
:
1000
,
newGasTip
:
101
,
newBasefee
:
2000
,
expectedTip
:
11
5
,
expectedFC
:
4115
,
expectedTip
:
11
0
,
expectedFC
:
4110
,
},
{
prevGasTip
:
100
,
prevBasefee
:
1000
,
newGasTip
:
120
,
newBasefee
:
900
,
expectedTip
:
120
,
expectedFC
:
2
415
,
expectedTip
:
120
,
expectedFC
:
2
310
,
},
{
prevGasTip
:
100
,
prevBasefee
:
1000
,
newGasTip
:
120
,
newBasefee
:
1100
,
expectedTip
:
120
,
expectedFC
:
2
415
,
expectedTip
:
120
,
expectedFC
:
2
320
,
},
{
prevGasTip
:
100
,
prevBasefee
:
1000
,
newGasTip
:
120
,
newBasefee
:
1140
,
expectedTip
:
120
,
expectedFC
:
24
15
,
expectedTip
:
120
,
expectedFC
:
24
00
,
},
{
prevGasTip
:
100
,
prevBasefee
:
1000
,
...
...
op-service/txmgr/txmgr.go
View file @
5f1c1478
...
...
@@ -20,9 +20,13 @@ import (
"github.com/ethereum-optimism/optimism/op-service/txmgr/metrics"
)
// Geth defaults the priceBump to 10
// Set it to 15% to be more aggressive about including transactions
const
priceBump
int64
=
15
const
(
// Geth requires a minimum fee bump of 10% for tx resubmission
priceBump
int64
=
10
// The multiplier applied to fee suggestions to put a hard limit on fee increases
feeLimitMultiplier
=
5
)
// new = old * (100 + priceBump) / 100
var
priceBumpPercent
=
big
.
NewInt
(
100
+
priceBump
)
...
...
@@ -280,7 +284,15 @@ func (m *SimpleTxManager) sendTx(ctx context.Context, tx *types.Transaction) (*t
return
nil
,
errors
.
New
(
"aborted transaction sending"
)
}
// Increase the gas price & submit the new transaction
tx
=
m
.
increaseGasPrice
(
ctx
,
tx
)
newTx
,
err
:=
m
.
increaseGasPrice
(
ctx
,
tx
)
if
err
!=
nil
||
sendState
.
IsWaitingForConfirmation
()
{
// there is a chance the previous tx goes into "waiting for confirmation" state
// during the increaseGasPrice call. In some (but not all) cases increaseGasPrice
// will error out during gas estimation. In either case we should continue waiting
// rather than resubmit the tx.
continue
}
tx
=
newTx
wg
.
Add
(
1
)
bumpCounter
+=
1
go
sendTxAsync
(
tx
)
...
...
@@ -418,46 +430,67 @@ func (m *SimpleTxManager) queryReceipt(ctx context.Context, txHash common.Hash,
return
nil
}
// increaseGasPrice takes the previous transaction & potentially clones then signs it with a higher tip.
// If the tip + basefee suggested by the network are not greater than the previous values, the same transaction
// will be returned. If they are greater, this function will ensure that they are at least greater by 15% than
// the previous transaction's value to ensure that the price bump is large enough.
//
// We do not re-estimate the amount of gas used because for some stateful transactions (like output proposals) the
// act of including the transaction renders the repeat of the transaction invalid.
//
// If it encounters an error with creating the new transaction, it will return the old transaction.
func
(
m
*
SimpleTxManager
)
increaseGasPrice
(
ctx
context
.
Context
,
tx
*
types
.
Transaction
)
*
types
.
Transaction
{
// increaseGasPrice takes the previous transaction, clones it, and returns it with fee values that
// are at least `priceBump` percent higher than the previous ones to satisfy Geth's replacement
// rules, and no lower than the values returned by the fee suggestion algorithm to ensure it
// doesn't linger in the mempool. Finally to avoid runaway price increases, fees are capped at a
// `feeLimitMultiplier` multiple of the suggested values.
func
(
m
*
SimpleTxManager
)
increaseGasPrice
(
ctx
context
.
Context
,
tx
*
types
.
Transaction
)
(
*
types
.
Transaction
,
error
)
{
m
.
l
.
Info
(
"bumping gas price for tx"
,
"hash"
,
tx
.
Hash
(),
"tip"
,
tx
.
GasTipCap
(),
"fee"
,
tx
.
GasFeeCap
(),
"gaslimit"
,
tx
.
Gas
())
tip
,
basefee
,
err
:=
m
.
suggestGasPriceCaps
(
ctx
)
if
err
!=
nil
{
m
.
l
.
Warn
(
"failed to get suggested gas tip and basefee"
,
"err"
,
err
)
return
tx
return
nil
,
err
}
gasTipCap
,
gasFeeCap
:=
updateFees
(
tx
.
GasTipCap
(),
tx
.
GasFeeCap
(),
tip
,
basefee
,
m
.
l
)
bumpedTip
,
bumpedFee
:=
updateFees
(
tx
.
GasTipCap
(),
tx
.
GasFeeCap
(),
tip
,
basefee
,
m
.
l
)
if
tx
.
GasTipCapIntCmp
(
gasTipCap
)
==
0
&&
tx
.
GasFeeCapIntCmp
(
gasFeeCap
)
==
0
{
return
tx
// Make sure increase is at most 5x the suggested values
maxTip
:=
new
(
big
.
Int
)
.
Mul
(
tip
,
big
.
NewInt
(
feeLimitMultiplier
))
if
bumpedTip
.
Cmp
(
maxTip
)
>
0
{
m
.
l
.
Warn
(
fmt
.
Sprintf
(
"bumped tip getting capped at %dx multiple of the suggested value"
,
feeLimitMultiplier
),
"bumped"
,
bumpedTip
,
"suggestion"
,
tip
)
bumpedTip
.
Set
(
maxTip
)
}
maxFee
:=
calcGasFeeCap
(
new
(
big
.
Int
)
.
Mul
(
basefee
,
big
.
NewInt
(
feeLimitMultiplier
)),
maxTip
)
if
bumpedFee
.
Cmp
(
maxFee
)
>
0
{
m
.
l
.
Warn
(
"bumped fee getting capped at multiple of the implied suggested value"
,
"bumped"
,
bumpedFee
,
"suggestion"
,
maxFee
)
bumpedFee
.
Set
(
maxFee
)
}
rawTx
:=
&
types
.
DynamicFeeTx
{
ChainID
:
tx
.
ChainId
(),
Nonce
:
tx
.
Nonce
(),
GasTipCap
:
gasTipCap
,
GasFeeCap
:
gasFeeCap
,
Gas
:
tx
.
Gas
(),
GasTipCap
:
bumpedTip
,
GasFeeCap
:
bumpedFee
,
To
:
tx
.
To
(),
Value
:
tx
.
Value
(),
Data
:
tx
.
Data
(),
AccessList
:
tx
.
AccessList
(),
}
// Re-estimate gaslimit in case things have changed or a previous gaslimit estimate was wrong
gas
,
err
:=
m
.
backend
.
EstimateGas
(
ctx
,
ethereum
.
CallMsg
{
From
:
m
.
cfg
.
From
,
To
:
rawTx
.
To
,
GasFeeCap
:
bumpedTip
,
GasTipCap
:
bumpedFee
,
Data
:
rawTx
.
Data
,
})
if
err
!=
nil
{
m
.
l
.
Warn
(
"failed to re-estimate gas"
,
"err"
,
err
,
"gaslimit"
,
tx
.
Gas
())
return
nil
,
err
}
if
tx
.
Gas
()
!=
gas
{
m
.
l
.
Info
(
"re-estimated gas differs"
,
"oldgas"
,
tx
.
Gas
(),
"newgas"
,
gas
)
}
rawTx
.
Gas
=
gas
ctx
,
cancel
:=
context
.
WithTimeout
(
ctx
,
m
.
cfg
.
NetworkTimeout
)
defer
cancel
()
newTx
,
err
:=
m
.
cfg
.
Signer
(
ctx
,
m
.
cfg
.
From
,
types
.
NewTx
(
rawTx
))
if
err
!=
nil
{
m
.
l
.
Warn
(
"failed to sign new transaction"
,
"err"
,
err
)
return
tx
return
tx
,
nil
}
return
newTx
return
newTx
,
nil
}
// suggestGasPriceCaps suggests what the new tip & new basefee should be based on the current L1 conditions
...
...
@@ -490,18 +523,15 @@ func calcThresholdValue(x *big.Int) *big.Int {
return
threshold
}
// updateFees takes the old tip/basefee & the new tip/basefee and then suggests
// a gasTipCap and gasFeeCap that satisfies geth's required fee bumps
// Geth: FC and Tip must be bumped if any increase
// updateFees takes an old transaction's tip & fee cap plus a new tip & basefee, and returns
// a suggested tip and fee cap such that:
//
// (a) each satisfies geth's required tx-replacement fee bumps (we use a 10% increase), and
// (b) gasTipCap is no less than new tip, and
// (c) gasFeeCap is no less than calcGasFee(newBaseFee, newTip)
func
updateFees
(
oldTip
,
oldFeeCap
,
newTip
,
newBaseFee
*
big
.
Int
,
lgr
log
.
Logger
)
(
*
big
.
Int
,
*
big
.
Int
)
{
newFeeCap
:=
calcGasFeeCap
(
newBaseFee
,
newTip
)
lgr
=
lgr
.
New
(
"old_tip"
,
oldTip
,
"old_feecap"
,
oldFeeCap
,
"new_tip"
,
newTip
,
"new_feecap"
,
newFeeCap
)
// If the new prices are less than the old price, reuse the old prices
if
oldTip
.
Cmp
(
newTip
)
>=
0
&&
oldFeeCap
.
Cmp
(
newFeeCap
)
>=
0
{
lgr
.
Debug
(
"Reusing old tip and feecap"
)
return
oldTip
,
oldFeeCap
}
// Determine if we need to increase the suggested values
thresholdTip
:=
calcThresholdValue
(
oldTip
)
thresholdFeeCap
:=
calcThresholdValue
(
oldFeeCap
)
if
newTip
.
Cmp
(
thresholdTip
)
>=
0
&&
newFeeCap
.
Cmp
(
thresholdFeeCap
)
>=
0
{
...
...
op-service/txmgr/txmgr_test.go
View file @
5f1c1478
...
...
@@ -734,12 +734,14 @@ func doGasPriceIncrease(t *testing.T, txTipCap, txFeeCap, newTip, newBaseFee int
GasTipCap
:
big
.
NewInt
(
txTipCap
),
GasFeeCap
:
big
.
NewInt
(
txFeeCap
),
})
newTx
:=
mgr
.
increaseGasPrice
(
context
.
Background
(),
tx
)
newTx
,
err
:=
mgr
.
increaseGasPrice
(
context
.
Background
(),
tx
)
require
.
NoError
(
t
,
err
)
return
tx
,
newTx
}
func
TestIncreaseGasPrice
(
t
*
testing
.
T
)
{
// t.Parallel()
require
.
Equal
(
t
,
int64
(
10
),
priceBump
,
"test must be updated if priceBump is adjusted"
)
tests
:=
[]
struct
{
name
string
run
func
(
t
*
testing
.
T
)
...
...
@@ -781,22 +783,16 @@ func TestIncreaseGasPrice(t *testing.T) {
run
:
func
(
t
*
testing
.
T
)
{
_
,
newTx
:=
doGasPriceIncrease
(
t
,
100
,
2200
,
120
,
1050
)
require
.
True
(
t
,
newTx
.
GasTipCap
()
.
Cmp
(
big
.
NewInt
(
120
))
==
0
,
"new tx tip must be equal L1"
)
require
.
True
(
t
,
newTx
.
GasFeeCap
()
.
Cmp
(
big
.
NewInt
(
2
53
0
))
==
0
,
"new tx fee cap must be equal to the threshold value"
)
require
.
True
(
t
,
newTx
.
GasFeeCap
()
.
Cmp
(
big
.
NewInt
(
2
42
0
))
==
0
,
"new tx fee cap must be equal to the threshold value"
)
},
},
{
name
:
"uses L1 FC when larger and threshold tip"
,
run
:
func
(
t
*
testing
.
T
)
{
_
,
newTx
:=
doGasPriceIncrease
(
t
,
100
,
2200
,
100
,
2000
)
require
.
True
(
t
,
newTx
.
GasTipCap
()
.
Cmp
(
big
.
NewInt
(
115
))
==
0
,
"new tx tip must be equal the threshold value"
)
require
.
True
(
t
,
newTx
.
GasFeeCap
()
.
Cmp
(
big
.
NewInt
(
4115
))
==
0
,
"new tx fee cap must be equal L1"
)
},
},
{
name
:
"reuses tx when no bump"
,
run
:
func
(
t
*
testing
.
T
)
{
tx
,
newTx
:=
doGasPriceIncrease
(
t
,
10
,
100
,
10
,
45
)
require
.
Equal
(
t
,
tx
.
Hash
(),
newTx
.
Hash
(),
"tx hash must be the same"
)
require
.
True
(
t
,
newTx
.
GasTipCap
()
.
Cmp
(
big
.
NewInt
(
110
))
==
0
,
"new tx tip must be equal the threshold value"
)
t
.
Log
(
"Vals:"
,
newTx
.
GasFeeCap
())
require
.
True
(
t
,
newTx
.
GasFeeCap
()
.
Cmp
(
big
.
NewInt
(
4110
))
==
0
,
"new tx fee cap must be equal L1"
)
},
},
}
...
...
@@ -811,11 +807,12 @@ func TestIncreaseGasPrice(t *testing.T) {
func
TestIncreaseGasPriceNotExponential
(
t
*
testing
.
T
)
{
t
.
Parallel
()
borkedTip
:=
int64
(
10
)
borkedFee
:=
int64
(
45
)
borkedBackend
:=
failingBackend
{
gasTip
:
big
.
NewInt
(
10
),
baseFee
:
big
.
NewInt
(
45
),
gasTip
:
big
.
NewInt
(
borkedTip
),
baseFee
:
big
.
NewInt
(
borkedFee
),
}
feeCap
:=
calcGasFeeCap
(
borkedBackend
.
baseFee
,
borkedBackend
.
gasTip
)
mgr
:=
&
SimpleTxManager
{
cfg
:
Config
{
...
...
@@ -839,14 +836,23 @@ func TestIncreaseGasPriceNotExponential(t *testing.T) {
})
// Run IncreaseGasPrice a bunch of times in a row to simulate a very fast resubmit loop.
for
i
:=
0
;
i
<
20
;
i
++
{
var
err
error
for
i
:=
0
;
i
<
30
;
i
++
{
ctx
:=
context
.
Background
()
newTx
:=
mgr
.
increaseGasPrice
(
ctx
,
tx
)
require
.
True
(
t
,
newTx
.
GasFeeCap
()
.
Cmp
(
feeCap
)
==
0
,
"new tx fee cap must be equal L1"
)
require
.
True
(
t
,
newTx
.
GasTipCap
()
.
Cmp
(
borkedBackend
.
gasTip
)
==
0
,
"new tx tip must be equal L1"
)
tx
=
newTx
tx
,
err
=
mgr
.
increaseGasPrice
(
ctx
,
tx
)
require
.
NoError
(
t
,
err
)
}
lastTip
,
lastFee
:=
tx
.
GasTipCap
(),
tx
.
GasFeeCap
()
require
.
Equal
(
t
,
lastTip
.
Int64
(),
feeLimitMultiplier
*
borkedTip
)
require
.
Equal
(
t
,
lastFee
.
Int64
(),
feeLimitMultiplier
*
(
borkedTip
+
2
*
borkedFee
))
// Confirm that fees stop rising
for
i
:=
0
;
i
<
5
;
i
++
{
ctx
:=
context
.
Background
()
tx
,
err
:=
mgr
.
increaseGasPrice
(
ctx
,
tx
)
require
.
NoError
(
t
,
err
)
require
.
True
(
t
,
tx
.
GasTipCap
()
.
Cmp
(
lastTip
)
==
0
,
"suggested tx tip must stop increasing"
)
require
.
True
(
t
,
tx
.
GasFeeCap
()
.
Cmp
(
lastFee
)
==
0
,
"suggested tx fee must stop increasing"
)
}
}
func
TestErrStringMatch
(
t
*
testing
.
T
)
{
...
...
packages/contracts-bedrock/.gas-snapshot
View file @
5f1c1478
Bytes_slice_Test:test_slice_acrossMultipleWords_works() (gas: 9413)
Bytes_slice_Test:test_slice_acrossWords_works() (gas: 1430)
Bytes_slice_Test:test_slice_fromNonZeroIdx_works() (gas: 17240)
Bytes_slice_Test:test_slice_fromZeroIdx_works() (gas: 208
26
)
Bytes_slice_Test:test_slice_fromZeroIdx_works() (gas: 208
04
)
Bytes_toNibbles_Test:test_toNibbles_expectedResult128Bytes_works() (gas: 78882)
Bytes_toNibbles_Test:test_toNibbles_expectedResult5Bytes_works() (gas: 3992)
Bytes_toNibbles_Test:test_toNibbles_zeroLengthInput_works() (gas: 823)
...
...
packages/contracts-bedrock/contracts/test/Bytes.t.sol
View file @
5f1c1478
...
...
@@ -70,59 +70,6 @@ contract Bytes_slice_Test is Test {
assertEq(Bytes.slice(input, 31, 34), expected);
}
/**
* @notice Tests that, when given an input bytes array of length `n`, the `slice` function will
* always revert if `_start + _length > n`.
*/
function testFuzz_slice_outOfBounds_reverts(
bytes memory _input,
uint256 _start,
uint256 _length
) public {
// We want a valid start index and a length that will not overflow.
vm.assume(_start < _input.length && _length < type(uint256).max - 31);
// But, we want an invalid slice length.
vm.assume(_start + _length > _input.length);
vm.expectRevert("slice_outOfBounds");
Bytes.slice(_input, _start, _length);
}
/**
* @notice Tests that, when given a length `n` that is greater than `type(uint256).max - 31`,
* the `slice` function reverts.
*/
function testFuzz_slice_lengthOverflows_reverts(
bytes memory _input,
uint256 _start,
uint256 _length
) public {
// Ensure that the `_length` will overflow if a number >= 31 is added to it.
vm.assume(_length > type(uint256).max - 31);
vm.expectRevert("slice_overflow");
Bytes.slice(_input, _start, _length);
}
/**
* @notice Tests that, when given a start index `n` that is greater than
* `type(uint256).max - n`, the `slice` function reverts.
*/
function testFuzz_slice_rangeOverflows_reverts(
bytes memory _input,
uint256 _start,
uint256 _length
) public {
// Ensure that `_length` is a realistic length of a slice. This is to make sure
// we revert on the correct require statement.
vm.assume(_length < _input.length);
// Ensure that `_start` will overflow if `_length` is added to it.
vm.assume(_start > type(uint256).max - _length);
vm.expectRevert("slice_overflow");
Bytes.slice(_input, _start, _length);
}
/**
* @notice Tests that the `slice` function correctly updates the free memory pointer depending
* on the length of the slice.
...
...
@@ -181,6 +128,61 @@ contract Bytes_slice_Test is Test {
}
}
contract Bytes_slice_TestFail is Test {
/**
* @notice Tests that, when given an input bytes array of length `n`, the `slice` function will
* always revert if `_start + _length > n`.
*/
function testFuzz_slice_outOfBounds_reverts(
bytes memory _input,
uint256 _start,
uint256 _length
) public {
// We want a valid start index and a length that will not overflow.
vm.assume(_start < _input.length && _length < type(uint256).max - 31);
// But, we want an invalid slice length.
vm.assume(_start + _length > _input.length);
vm.expectRevert("slice_outOfBounds");
Bytes.slice(_input, _start, _length);
}
/**
* @notice Tests that, when given a length `n` that is greater than `type(uint256).max - 31`,
* the `slice` function reverts.
*/
function testFuzz_slice_lengthOverflows_reverts(
bytes memory _input,
uint256 _start,
uint256 _length
) public {
// Ensure that the `_length` will overflow if a number >= 31 is added to it.
vm.assume(_length > type(uint256).max - 31);
vm.expectRevert("slice_overflow");
Bytes.slice(_input, _start, _length);
}
/**
* @notice Tests that, when given a start index `n` that is greater than
* `type(uint256).max - n`, the `slice` function reverts.
*/
function testFuzz_slice_rangeOverflows_reverts(
bytes memory _input,
uint256 _start,
uint256 _length
) public {
// Ensure that `_length` is a realistic length of a slice. This is to make sure
// we revert on the correct require statement.
vm.assume(_length < _input.length);
// Ensure that `_start` will overflow if `_length` is added to it.
vm.assume(_start > type(uint256).max - _length);
vm.expectRevert("slice_overflow");
Bytes.slice(_input, _start, _length);
}
}
contract Bytes_toNibbles_Test is Test {
/**
* @notice Tests that, given an input of 5 bytes, the `toNibbles` function returns an array of
...
...
proxyd/server.go
View file @
5f1c1478
...
...
@@ -16,11 +16,9 @@ import (
"sync"
"time"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/go-redis/redis/v8"
"github.com/gorilla/mux"
...
...
@@ -580,16 +578,7 @@ func (s *Server) populateContext(w http.ResponseWriter, r *http.Request) context
}
ctx
:=
context
.
WithValue
(
r
.
Context
(),
ContextKeyXForwardedFor
,
xff
)
// nolint:staticcheck
if
len
(
s
.
authenticatedPaths
)
==
0
{
// handle the edge case where auth is disabled
// but someone sends in an auth key anyway
if
authorization
!=
""
{
log
.
Info
(
"blocked authenticated request against unauthenticated proxy"
)
httpResponseCodesTotal
.
WithLabelValues
(
"404"
)
.
Inc
()
w
.
WriteHeader
(
404
)
return
nil
}
}
else
{
if
len
(
s
.
authenticatedPaths
)
>
0
{
if
authorization
==
""
||
s
.
authenticatedPaths
[
authorization
]
==
""
{
log
.
Info
(
"blocked unauthorized request"
,
"authorization"
,
authorization
)
httpResponseCodesTotal
.
WithLabelValues
(
"401"
)
.
Inc
()
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment