forked from cerc-io/plugeth
Merge commit 'dc34fe829' into merge/geth-v1.13.2
This commit is contained in:
+104
-87
@@ -17,6 +17,8 @@
|
||||
package asm
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
@@ -30,7 +32,7 @@ import (
|
||||
// and holds the tokens for the program.
|
||||
type Compiler struct {
|
||||
tokens []token
|
||||
binary []interface{}
|
||||
out []byte
|
||||
|
||||
labels map[string]int
|
||||
|
||||
@@ -50,12 +52,10 @@ func NewCompiler(debug bool) *Compiler {
|
||||
// Feed feeds tokens in to ch and are interpreted by
|
||||
// the compiler.
|
||||
//
|
||||
// feed is the first pass in the compile stage as it
|
||||
// collects the used labels in the program and keeps a
|
||||
// program counter which is used to determine the locations
|
||||
// of the jump dests. The labels can than be used in the
|
||||
// second stage to push labels and determine the right
|
||||
// position.
|
||||
// feed is the first pass in the compile stage as it collects the used labels in the
|
||||
// program and keeps a program counter which is used to determine the locations of the
|
||||
// jump dests. The labels can than be used in the second stage to push labels and
|
||||
// determine the right position.
|
||||
func (c *Compiler) Feed(ch <-chan token) {
|
||||
var prev token
|
||||
for i := range ch {
|
||||
@@ -79,7 +79,6 @@ func (c *Compiler) Feed(ch <-chan token) {
|
||||
c.pc++
|
||||
}
|
||||
}
|
||||
|
||||
c.tokens = append(c.tokens, i)
|
||||
prev = i
|
||||
}
|
||||
@@ -88,12 +87,11 @@ func (c *Compiler) Feed(ch <-chan token) {
|
||||
}
|
||||
}
|
||||
|
||||
// Compile compiles the current tokens and returns a
|
||||
// binary string that can be interpreted by the EVM
|
||||
// and an error if it failed.
|
||||
// Compile compiles the current tokens and returns a binary string that can be interpreted
|
||||
// by the EVM and an error if it failed.
|
||||
//
|
||||
// compile is the second stage in the compile phase
|
||||
// which compiles the tokens to EVM instructions.
|
||||
// compile is the second stage in the compile phase which compiles the tokens to EVM
|
||||
// instructions.
|
||||
func (c *Compiler) Compile() (string, []error) {
|
||||
var errors []error
|
||||
// continue looping over the tokens until
|
||||
@@ -105,16 +103,8 @@ func (c *Compiler) Compile() (string, []error) {
|
||||
}
|
||||
|
||||
// turn the binary to hex
|
||||
var bin strings.Builder
|
||||
for _, v := range c.binary {
|
||||
switch v := v.(type) {
|
||||
case vm.OpCode:
|
||||
bin.WriteString(fmt.Sprintf("%x", []byte{byte(v)}))
|
||||
case []byte:
|
||||
bin.WriteString(fmt.Sprintf("%x", v))
|
||||
}
|
||||
}
|
||||
return bin.String(), errors
|
||||
h := hex.EncodeToString(c.out)
|
||||
return h, errors
|
||||
}
|
||||
|
||||
// next returns the next token and increments the
|
||||
@@ -156,87 +146,114 @@ func (c *Compiler) compileLine() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// compileNumber compiles the number to bytes
|
||||
func (c *Compiler) compileNumber(element token) {
|
||||
num := math.MustParseBig256(element.text).Bytes()
|
||||
if len(num) == 0 {
|
||||
num = []byte{0}
|
||||
// parseNumber compiles the number to bytes
|
||||
func parseNumber(tok token) ([]byte, error) {
|
||||
if tok.typ != number {
|
||||
panic("parseNumber of non-number token")
|
||||
}
|
||||
c.pushBin(num)
|
||||
num, ok := math.ParseBig256(tok.text)
|
||||
if !ok {
|
||||
return nil, errors.New("invalid number")
|
||||
}
|
||||
bytes := num.Bytes()
|
||||
if len(bytes) == 0 {
|
||||
bytes = []byte{0}
|
||||
}
|
||||
return bytes, nil
|
||||
}
|
||||
|
||||
// compileElement compiles the element (push & label or both)
|
||||
// to a binary representation and may error if incorrect statements
|
||||
// where fed.
|
||||
func (c *Compiler) compileElement(element token) error {
|
||||
// check for a jump. jumps must be read and compiled
|
||||
// from right to left.
|
||||
if isJump(element.text) {
|
||||
rvalue := c.next()
|
||||
switch rvalue.typ {
|
||||
case number:
|
||||
// TODO figure out how to return the error properly
|
||||
c.compileNumber(rvalue)
|
||||
case stringValue:
|
||||
// strings are quoted, remove them.
|
||||
c.pushBin(rvalue.text[1 : len(rvalue.text)-2])
|
||||
case label:
|
||||
c.pushBin(vm.PUSH4)
|
||||
pos := big.NewInt(int64(c.labels[rvalue.text])).Bytes()
|
||||
pos = append(make([]byte, 4-len(pos)), pos...)
|
||||
c.pushBin(pos)
|
||||
case lineEnd:
|
||||
c.pos--
|
||||
default:
|
||||
return compileErr(rvalue, rvalue.text, "number, string or label")
|
||||
}
|
||||
// push the operation
|
||||
c.pushBin(toBinary(element.text))
|
||||
switch {
|
||||
case isJump(element.text):
|
||||
return c.compileJump(element.text)
|
||||
case isPush(element.text):
|
||||
return c.compilePush()
|
||||
default:
|
||||
c.outputOpcode(toBinary(element.text))
|
||||
return nil
|
||||
} else if isPush(element.text) {
|
||||
// handle pushes. pushes are read from left to right.
|
||||
var value []byte
|
||||
|
||||
rvalue := c.next()
|
||||
switch rvalue.typ {
|
||||
case number:
|
||||
value = math.MustParseBig256(rvalue.text).Bytes()
|
||||
if len(value) == 0 {
|
||||
value = []byte{0}
|
||||
}
|
||||
case stringValue:
|
||||
value = []byte(rvalue.text[1 : len(rvalue.text)-1])
|
||||
case label:
|
||||
value = big.NewInt(int64(c.labels[rvalue.text])).Bytes()
|
||||
value = append(make([]byte, 4-len(value)), value...)
|
||||
default:
|
||||
return compileErr(rvalue, rvalue.text, "number, string or label")
|
||||
}
|
||||
|
||||
if len(value) > 32 {
|
||||
return fmt.Errorf("%d type error: unsupported string or number with size > 32", rvalue.lineno)
|
||||
}
|
||||
|
||||
c.pushBin(vm.OpCode(int(vm.PUSH1) - 1 + len(value)))
|
||||
c.pushBin(value)
|
||||
} else {
|
||||
c.pushBin(toBinary(element.text))
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Compiler) compileJump(jumpType string) error {
|
||||
rvalue := c.next()
|
||||
switch rvalue.typ {
|
||||
case number:
|
||||
numBytes, err := parseNumber(rvalue)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.outputBytes(numBytes)
|
||||
|
||||
case stringValue:
|
||||
// strings are quoted, remove them.
|
||||
str := rvalue.text[1 : len(rvalue.text)-2]
|
||||
c.outputBytes([]byte(str))
|
||||
|
||||
case label:
|
||||
c.outputOpcode(vm.PUSH4)
|
||||
pos := big.NewInt(int64(c.labels[rvalue.text])).Bytes()
|
||||
pos = append(make([]byte, 4-len(pos)), pos...)
|
||||
c.outputBytes(pos)
|
||||
|
||||
case lineEnd:
|
||||
// push without argument is supported, it just takes the destination from the stack.
|
||||
c.pos--
|
||||
|
||||
default:
|
||||
return compileErr(rvalue, rvalue.text, "number, string or label")
|
||||
}
|
||||
// push the operation
|
||||
c.outputOpcode(toBinary(jumpType))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Compiler) compilePush() error {
|
||||
// handle pushes. pushes are read from left to right.
|
||||
var value []byte
|
||||
rvalue := c.next()
|
||||
switch rvalue.typ {
|
||||
case number:
|
||||
value = math.MustParseBig256(rvalue.text).Bytes()
|
||||
if len(value) == 0 {
|
||||
value = []byte{0}
|
||||
}
|
||||
case stringValue:
|
||||
value = []byte(rvalue.text[1 : len(rvalue.text)-1])
|
||||
case label:
|
||||
value = big.NewInt(int64(c.labels[rvalue.text])).Bytes()
|
||||
value = append(make([]byte, 4-len(value)), value...)
|
||||
default:
|
||||
return compileErr(rvalue, rvalue.text, "number, string or label")
|
||||
}
|
||||
if len(value) > 32 {
|
||||
return fmt.Errorf("%d: string or number size > 32 bytes", rvalue.lineno+1)
|
||||
}
|
||||
c.outputOpcode(vm.OpCode(int(vm.PUSH1) - 1 + len(value)))
|
||||
c.outputBytes(value)
|
||||
return nil
|
||||
}
|
||||
|
||||
// compileLabel pushes a jumpdest to the binary slice.
|
||||
func (c *Compiler) compileLabel() {
|
||||
c.pushBin(vm.JUMPDEST)
|
||||
c.outputOpcode(vm.JUMPDEST)
|
||||
}
|
||||
|
||||
// pushBin pushes the value v to the binary stack.
|
||||
func (c *Compiler) pushBin(v interface{}) {
|
||||
func (c *Compiler) outputOpcode(op vm.OpCode) {
|
||||
if c.debug {
|
||||
fmt.Printf("%d: %v\n", len(c.binary), v)
|
||||
fmt.Printf("%d: %v\n", len(c.out), op)
|
||||
}
|
||||
c.binary = append(c.binary, v)
|
||||
c.out = append(c.out, byte(op))
|
||||
}
|
||||
|
||||
// output pushes the value v to the binary stack.
|
||||
func (c *Compiler) outputBytes(b []byte) {
|
||||
if c.debug {
|
||||
fmt.Printf("%d: %x\n", len(c.out), b)
|
||||
}
|
||||
c.out = append(c.out, b...)
|
||||
}
|
||||
|
||||
// isPush returns whether the string op is either any of
|
||||
@@ -263,13 +280,13 @@ type compileError struct {
|
||||
}
|
||||
|
||||
func (err compileError) Error() string {
|
||||
return fmt.Sprintf("%d syntax error: unexpected %v, expected %v", err.lineno, err.got, err.want)
|
||||
return fmt.Sprintf("%d: syntax error: unexpected %v, expected %v", err.lineno, err.got, err.want)
|
||||
}
|
||||
|
||||
func compileErr(c token, got, want string) error {
|
||||
return compileError{
|
||||
got: got,
|
||||
want: want,
|
||||
lineno: c.lineno,
|
||||
lineno: c.lineno + 1,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,14 @@ func TestCompiler(t *testing.T) {
|
||||
`,
|
||||
output: "6300000006565b",
|
||||
},
|
||||
{
|
||||
input: `
|
||||
JUMP @label
|
||||
label: ;; comment
|
||||
ADD ;; comment
|
||||
`,
|
||||
output: "6300000006565b01",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
ch := Lex([]byte(test.input), false)
|
||||
|
||||
@@ -72,6 +72,16 @@ func TestLexer(t *testing.T) {
|
||||
input: "@label123",
|
||||
tokens: []token{{typ: lineStart}, {typ: label, text: "label123"}, {typ: eof}},
|
||||
},
|
||||
// comment after label
|
||||
{
|
||||
input: "@label123 ;; comment",
|
||||
tokens: []token{{typ: lineStart}, {typ: label, text: "label123"}, {typ: eof}},
|
||||
},
|
||||
// comment after instruction
|
||||
{
|
||||
input: "push 3 ;; comment\nadd",
|
||||
tokens: []token{{typ: lineStart}, {typ: element, text: "push"}, {typ: number, text: "3"}, {typ: lineEnd, text: "\n"}, {typ: lineStart, lineno: 1}, {typ: element, lineno: 1, text: "add"}, {typ: eof, lineno: 1}},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
||||
+12
-28
@@ -42,6 +42,8 @@ type token struct {
|
||||
// is able to parse and return.
|
||||
type tokenType int
|
||||
|
||||
//go:generate go run golang.org/x/tools/cmd/stringer -type tokenType
|
||||
|
||||
const (
|
||||
eof tokenType = iota // end of file
|
||||
lineStart // emitted when a line starts
|
||||
@@ -52,31 +54,13 @@ const (
|
||||
labelDef // label definition is emitted when a new label is found
|
||||
number // number is emitted when a number is found
|
||||
stringValue // stringValue is emitted when a string has been found
|
||||
|
||||
Numbers = "1234567890" // characters representing any decimal number
|
||||
HexadecimalNumbers = Numbers + "aAbBcCdDeEfF" // characters representing any hexadecimal
|
||||
Alpha = "abcdefghijklmnopqrstuwvxyzABCDEFGHIJKLMNOPQRSTUWVXYZ" // characters representing alphanumeric
|
||||
)
|
||||
|
||||
// String implements stringer
|
||||
func (it tokenType) String() string {
|
||||
if int(it) > len(stringtokenTypes) {
|
||||
return "invalid"
|
||||
}
|
||||
return stringtokenTypes[it]
|
||||
}
|
||||
|
||||
var stringtokenTypes = []string{
|
||||
eof: "EOF",
|
||||
lineStart: "new line",
|
||||
lineEnd: "end of line",
|
||||
invalidStatement: "invalid statement",
|
||||
element: "element",
|
||||
label: "label",
|
||||
labelDef: "label definition",
|
||||
number: "number",
|
||||
stringValue: "string",
|
||||
}
|
||||
const (
|
||||
decimalNumbers = "1234567890" // characters representing any decimal number
|
||||
hexNumbers = decimalNumbers + "aAbBcCdDeEfF" // characters representing any hexadecimal
|
||||
alpha = "abcdefghijklmnopqrstuwvxyzABCDEFGHIJKLMNOPQRSTUWVXYZ" // characters representing alphanumeric
|
||||
)
|
||||
|
||||
// lexer is the basic construct for parsing
|
||||
// source code and turning them in to tokens.
|
||||
@@ -200,7 +184,6 @@ func lexLine(l *lexer) stateFn {
|
||||
l.emit(lineEnd)
|
||||
l.ignore()
|
||||
l.lineno++
|
||||
|
||||
l.emit(lineStart)
|
||||
case r == ';' && l.peek() == ';':
|
||||
return lexComment
|
||||
@@ -225,6 +208,7 @@ func lexLine(l *lexer) stateFn {
|
||||
// of the line and discards the text.
|
||||
func lexComment(l *lexer) stateFn {
|
||||
l.acceptRunUntil('\n')
|
||||
l.backup()
|
||||
l.ignore()
|
||||
|
||||
return lexLine
|
||||
@@ -234,7 +218,7 @@ func lexComment(l *lexer) stateFn {
|
||||
// the lex text state function to advance the parsing
|
||||
// process.
|
||||
func lexLabel(l *lexer) stateFn {
|
||||
l.acceptRun(Alpha + "_" + Numbers)
|
||||
l.acceptRun(alpha + "_" + decimalNumbers)
|
||||
|
||||
l.emit(label)
|
||||
|
||||
@@ -253,9 +237,9 @@ func lexInsideString(l *lexer) stateFn {
|
||||
}
|
||||
|
||||
func lexNumber(l *lexer) stateFn {
|
||||
acceptance := Numbers
|
||||
acceptance := decimalNumbers
|
||||
if l.accept("xX") {
|
||||
acceptance = HexadecimalNumbers
|
||||
acceptance = hexNumbers
|
||||
}
|
||||
l.acceptRun(acceptance)
|
||||
|
||||
@@ -265,7 +249,7 @@ func lexNumber(l *lexer) stateFn {
|
||||
}
|
||||
|
||||
func lexElement(l *lexer) stateFn {
|
||||
l.acceptRun(Alpha + "_" + Numbers)
|
||||
l.acceptRun(alpha + "_" + decimalNumbers)
|
||||
|
||||
if l.peek() == ':' {
|
||||
l.emit(labelDef)
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
// Code generated by "stringer -type tokenType"; DO NOT EDIT.
|
||||
|
||||
package asm
|
||||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[eof-0]
|
||||
_ = x[lineStart-1]
|
||||
_ = x[lineEnd-2]
|
||||
_ = x[invalidStatement-3]
|
||||
_ = x[element-4]
|
||||
_ = x[label-5]
|
||||
_ = x[labelDef-6]
|
||||
_ = x[number-7]
|
||||
_ = x[stringValue-8]
|
||||
}
|
||||
|
||||
const _tokenType_name = "eoflineStartlineEndinvalidStatementelementlabellabelDefnumberstringValue"
|
||||
|
||||
var _tokenType_index = [...]uint8{0, 3, 12, 19, 35, 42, 47, 55, 61, 72}
|
||||
|
||||
func (i tokenType) String() string {
|
||||
if i < 0 || i >= tokenType(len(_tokenType_index)-1) {
|
||||
return "tokenType(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _tokenType_name[_tokenType_index[i]:_tokenType_index[i+1]]
|
||||
}
|
||||
+19
-35
@@ -337,17 +337,17 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
||||
if err := bc.loadLastState(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Make sure the state associated with the block is available
|
||||
// Make sure the state associated with the block is available, or log out
|
||||
// if there is no available state, waiting for state sync.
|
||||
head := bc.CurrentBlock()
|
||||
if !bc.HasState(head.Root) {
|
||||
if head.Number.Uint64() == 0 {
|
||||
// The genesis state is missing, which is only possible in the path-based
|
||||
// scheme. This situation occurs when the state syncer overwrites it.
|
||||
//
|
||||
// The solution is to reset the state to the genesis state. Although it may not
|
||||
// match the sync target, the state healer will later address and correct any
|
||||
// inconsistencies.
|
||||
bc.resetState()
|
||||
// scheme. This situation occurs when the initial state sync is not finished
|
||||
// yet, or the chain head is rewound below the pivot point. In both scenario,
|
||||
// there is no possible recovery approach except for rerunning a snap sync.
|
||||
// Do nothing here until the state syncer picks it up.
|
||||
log.Info("Genesis state is missing, wait state sync")
|
||||
} else {
|
||||
// Head state is missing, before the state recovery, find out the
|
||||
// disk layer point of snapshot(if it's enabled). Make sure the
|
||||
@@ -630,28 +630,6 @@ func (bc *BlockChain) SetSafe(header *types.Header) {
|
||||
}
|
||||
}
|
||||
|
||||
// resetState resets the persistent state to genesis state if it's not present.
|
||||
func (bc *BlockChain) resetState() {
|
||||
// Short circuit if the genesis state is already present.
|
||||
root := bc.genesisBlock.Root()
|
||||
if bc.HasState(root) {
|
||||
return
|
||||
}
|
||||
// Reset the state database to empty for committing genesis state.
|
||||
// Note, it should only happen in path-based scheme and Reset function
|
||||
// is also only call-able in this mode.
|
||||
if bc.triedb.Scheme() == rawdb.PathScheme {
|
||||
if err := bc.triedb.Reset(types.EmptyRootHash); err != nil {
|
||||
log.Crit("Failed to clean state", "err", err) // Shouldn't happen
|
||||
}
|
||||
}
|
||||
// Write genesis state into database.
|
||||
if err := CommitGenesisState(bc.db, bc.triedb, bc.genesisBlock.Hash()); err != nil {
|
||||
log.Crit("Failed to commit genesis state", "err", err)
|
||||
}
|
||||
log.Info("Reset state to genesis", "root", root)
|
||||
}
|
||||
|
||||
// setHeadBeyondRoot rewinds the local chain to a new head with the extra condition
|
||||
// that the rewind must pass the specified state root. This method is meant to be
|
||||
// used when rewinding with snapshots enabled to ensure that we go back further than
|
||||
@@ -687,7 +665,6 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha
|
||||
if newHeadBlock == nil {
|
||||
log.Error("Gap in the chain, rewinding to genesis", "number", header.Number, "hash", header.Hash())
|
||||
newHeadBlock = bc.genesisBlock
|
||||
bc.resetState()
|
||||
} else {
|
||||
// Block exists, keep rewinding until we find one with state,
|
||||
// keeping rewinding until we exceed the optional threshold
|
||||
@@ -715,16 +692,14 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha
|
||||
}
|
||||
}
|
||||
if beyondRoot || newHeadBlock.NumberU64() == 0 {
|
||||
if newHeadBlock.NumberU64() == 0 {
|
||||
bc.resetState()
|
||||
} else if !bc.HasState(newHeadBlock.Root()) {
|
||||
if !bc.HasState(newHeadBlock.Root()) && bc.stateRecoverable(newHeadBlock.Root()) {
|
||||
// Rewind to a block with recoverable state. If the state is
|
||||
// missing, run the state recovery here.
|
||||
if err := bc.triedb.Recover(newHeadBlock.Root()); err != nil {
|
||||
log.Crit("Failed to rollback state", "err", err) // Shouldn't happen
|
||||
}
|
||||
log.Debug("Rewound to block with state", "number", newHeadBlock.NumberU64(), "hash", newHeadBlock.Hash())
|
||||
}
|
||||
log.Debug("Rewound to block with state", "number", newHeadBlock.NumberU64(), "hash", newHeadBlock.Hash())
|
||||
break
|
||||
}
|
||||
log.Debug("Skipping block with threshold state", "number", newHeadBlock.NumberU64(), "hash", newHeadBlock.Hash(), "root", newHeadBlock.Root())
|
||||
@@ -739,6 +714,15 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha
|
||||
// to low, so it's safe to update in-memory markers directly.
|
||||
bc.currentBlock.Store(newHeadBlock.Header())
|
||||
headBlockGauge.Update(int64(newHeadBlock.NumberU64()))
|
||||
|
||||
// The head state is missing, which is only possible in the path-based
|
||||
// scheme. This situation occurs when the chain head is rewound below
|
||||
// the pivot point. In this scenario, there is no possible recovery
|
||||
// approach except for rerunning a snap sync. Do nothing here until the
|
||||
// state syncer picks it up.
|
||||
if !bc.HasState(newHeadBlock.Root()) {
|
||||
log.Info("Chain is stateless, wait state sync", "number", newHeadBlock.Number(), "hash", newHeadBlock.Hash())
|
||||
}
|
||||
}
|
||||
// Rewind the snap block in a simpleton way to the target head
|
||||
if currentSnapBlock := bc.CurrentSnapBlock(); currentSnapBlock != nil && header.Number.Uint64() < currentSnapBlock.Number.Uint64() {
|
||||
@@ -838,7 +822,7 @@ func (bc *BlockChain) SnapSyncCommitHead(hash common.Hash) error {
|
||||
// Reset the trie database with the fresh snap synced state.
|
||||
root := block.Root()
|
||||
if bc.triedb.Scheme() == rawdb.PathScheme {
|
||||
if err := bc.triedb.Reset(root); err != nil {
|
||||
if err := bc.triedb.Enable(root); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -630,13 +630,16 @@ func (s *MatcherSession) Multiplex(batch int, wait time.Duration, mux chan chan
|
||||
request <- &Retrieval{Bit: bit, Sections: sections, Context: s.ctx}
|
||||
|
||||
result := <-request
|
||||
|
||||
// Deliver a result before s.Close() to avoid a deadlock
|
||||
s.deliverSections(result.Bit, result.Sections, result.Bitsets)
|
||||
|
||||
if result.Error != nil {
|
||||
s.errLock.Lock()
|
||||
s.err = result.Error
|
||||
s.errLock.Unlock()
|
||||
s.Close()
|
||||
}
|
||||
s.deliverSections(result.Bit, result.Sections, result.Bitsets)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,6 +107,16 @@ func TestCreation(t *testing.T) {
|
||||
{1735372, 1677557088, ID{Hash: checksumToBytes(0xf7f9bc08), Next: 0}}, // First Shanghai block
|
||||
},
|
||||
},
|
||||
// Holesky test cases
|
||||
{
|
||||
params.HoleskyChainConfig,
|
||||
core.DefaultHoleskyGenesisBlock().ToBlock(),
|
||||
[]testcase{
|
||||
{0, 0, ID{Hash: checksumToBytes(0xc61a6098), Next: 1696000704}}, // Unsynced, last Frontier, Homestead, Tangerine, Spurious, Byzantium, Constantinople, Petersburg, Istanbul, Berlin, London, Paris block
|
||||
{123, 0, ID{Hash: checksumToBytes(0xc61a6098), Next: 1696000704}}, // First MergeNetsplit block
|
||||
{123, 1696000704, ID{Hash: checksumToBytes(0xfd4f016b), Next: 0}}, // Last MergeNetsplit block
|
||||
},
|
||||
},
|
||||
}
|
||||
for i, tt := range tests {
|
||||
for j, ttt := range tt.cases {
|
||||
|
||||
+1
-2
@@ -587,10 +587,9 @@ func DefaultHoleskyGenesisBlock() *Genesis {
|
||||
return &Genesis{
|
||||
Config: params.HoleskyChainConfig,
|
||||
Nonce: 0x1234,
|
||||
ExtraData: hexutil.MustDecode("0x686f77206d7563682069732074686520666973683f"),
|
||||
GasLimit: 0x17d7840,
|
||||
Difficulty: big.NewInt(0x01),
|
||||
Timestamp: 1694786100,
|
||||
Timestamp: 1695902100,
|
||||
Alloc: decodePrealloc(holeskyAllocData),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,3 +76,25 @@ func DeleteSkeletonHeader(db ethdb.KeyValueWriter, number uint64) {
|
||||
log.Crit("Failed to delete skeleton header", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
StateSyncUnknown = uint8(0) // flags the state snap sync is unknown
|
||||
StateSyncRunning = uint8(1) // flags the state snap sync is not completed yet
|
||||
StateSyncFinished = uint8(2) // flags the state snap sync is completed
|
||||
)
|
||||
|
||||
// ReadSnapSyncStatusFlag retrieves the state snap sync status flag.
|
||||
func ReadSnapSyncStatusFlag(db ethdb.KeyValueReader) uint8 {
|
||||
blob, err := db.Get(snapSyncStatusFlagKey)
|
||||
if err != nil || len(blob) != 1 {
|
||||
return StateSyncUnknown
|
||||
}
|
||||
return blob[0]
|
||||
}
|
||||
|
||||
// WriteSnapSyncStatusFlag stores the state snap sync status flag into database.
|
||||
func WriteSnapSyncStatusFlag(db ethdb.KeyValueWriter, flag uint8) {
|
||||
if err := db.Put(snapSyncStatusFlagKey, []byte{flag}); err != nil {
|
||||
log.Crit("Failed to store sync status flag", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,6 +89,16 @@ func HasAccountTrieNode(db ethdb.KeyValueReader, path []byte, hash common.Hash)
|
||||
return h.hash(data) == hash
|
||||
}
|
||||
|
||||
// ExistsAccountTrieNode checks the presence of the account trie node with the
|
||||
// specified node path, regardless of the node hash.
|
||||
func ExistsAccountTrieNode(db ethdb.KeyValueReader, path []byte) bool {
|
||||
has, err := db.Has(accountTrieNodeKey(path))
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return has
|
||||
}
|
||||
|
||||
// WriteAccountTrieNode writes the provided account trie node into database.
|
||||
func WriteAccountTrieNode(db ethdb.KeyValueWriter, path []byte, node []byte) {
|
||||
if err := db.Put(accountTrieNodeKey(path), node); err != nil {
|
||||
@@ -127,6 +137,16 @@ func HasStorageTrieNode(db ethdb.KeyValueReader, accountHash common.Hash, path [
|
||||
return h.hash(data) == hash
|
||||
}
|
||||
|
||||
// ExistsStorageTrieNode checks the presence of the storage trie node with the
|
||||
// specified account hash and node path, regardless of the node hash.
|
||||
func ExistsStorageTrieNode(db ethdb.KeyValueReader, accountHash common.Hash, path []byte) bool {
|
||||
has, err := db.Has(storageTrieNodeKey(accountHash, path))
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return has
|
||||
}
|
||||
|
||||
// WriteStorageTrieNode writes the provided storage trie node into database.
|
||||
func WriteStorageTrieNode(db ethdb.KeyValueWriter, accountHash common.Hash, path []byte, node []byte) {
|
||||
if err := db.Put(storageTrieNodeKey(accountHash, path), node); err != nil {
|
||||
|
||||
@@ -253,7 +253,7 @@ func NewDatabaseWithFreezer(db ethdb.KeyValueStore, ancient string, namespace st
|
||||
break
|
||||
}
|
||||
}
|
||||
// We are about to exit on error. Print database metdata beore exiting
|
||||
// We are about to exit on error. Print database metadata before exiting
|
||||
printChainMetadata(db)
|
||||
return nil, fmt.Errorf("gap in the chain between ancients [0 - #%d] and leveldb [#%d - #%d] ",
|
||||
frozen-1, number, head)
|
||||
@@ -555,7 +555,7 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
|
||||
lastPivotKey, fastTrieProgressKey, snapshotDisabledKey, SnapshotRootKey, snapshotJournalKey,
|
||||
snapshotGeneratorKey, snapshotRecoveryKey, txIndexTailKey, fastTxLookupLimitKey,
|
||||
uncleanShutdownKey, badBlockKey, transitionStatusKey, skeletonSyncStatusKey,
|
||||
persistentStateIDKey, trieJournalKey, snapshotSyncStatusKey,
|
||||
persistentStateIDKey, trieJournalKey, snapshotSyncStatusKey, snapSyncStatusFlagKey,
|
||||
} {
|
||||
if bytes.Equal(key, meta) {
|
||||
metadata.Add(size)
|
||||
|
||||
@@ -108,7 +108,11 @@ func NewFreezer(datadir string, namespace string, readonly bool, maxTableSize ui
|
||||
// Leveldb uses LOCK as the filelock filename. To prevent the
|
||||
// name collision, we use FLOCK as the lock name.
|
||||
lock := flock.New(flockFile)
|
||||
if locked, err := lock.TryLock(); err != nil {
|
||||
tryLock := lock.TryLock
|
||||
if readonly {
|
||||
tryLock = lock.TryRLock
|
||||
}
|
||||
if locked, err := tryLock(); err != nil {
|
||||
return nil, err
|
||||
} else if !locked {
|
||||
return nil, errors.New("locking failed")
|
||||
|
||||
@@ -212,6 +212,9 @@ func (t *freezerTable) repair() error {
|
||||
}
|
||||
// Ensure the index is a multiple of indexEntrySize bytes
|
||||
if overflow := stat.Size() % indexEntrySize; overflow != 0 {
|
||||
if t.readonly {
|
||||
return fmt.Errorf("index file(path: %s, name: %s) size is not a multiple of %d", t.path, t.name, indexEntrySize)
|
||||
}
|
||||
truncateFreezerFile(t.index, stat.Size()-overflow) // New file can't trigger this path
|
||||
}
|
||||
// Retrieve the file sizes and prepare for truncation
|
||||
@@ -270,6 +273,9 @@ func (t *freezerTable) repair() error {
|
||||
// Keep truncating both files until they come in sync
|
||||
contentExp = int64(lastIndex.offset)
|
||||
for contentExp != contentSize {
|
||||
if t.readonly {
|
||||
return fmt.Errorf("freezer table(path: %s, name: %s, num: %d) is corrupted", t.path, t.name, lastIndex.filenum)
|
||||
}
|
||||
verbose = true
|
||||
// Truncate the head file to the last offset pointer
|
||||
if contentExp < contentSize {
|
||||
|
||||
@@ -289,6 +289,57 @@ func TestFreezerReadonlyValidate(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestFreezerConcurrentReadonly(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tables := map[string]bool{"a": true}
|
||||
dir := t.TempDir()
|
||||
|
||||
f, err := NewFreezer(dir, "", false, 2049, tables)
|
||||
if err != nil {
|
||||
t.Fatal("can't open freezer", err)
|
||||
}
|
||||
var item = make([]byte, 1024)
|
||||
batch := f.tables["a"].newBatch()
|
||||
items := uint64(10)
|
||||
for i := uint64(0); i < items; i++ {
|
||||
require.NoError(t, batch.AppendRaw(i, item))
|
||||
}
|
||||
require.NoError(t, batch.commit())
|
||||
if loaded := f.tables["a"].items.Load(); loaded != items {
|
||||
t.Fatalf("unexpected number of items in table, want: %d, have: %d", items, loaded)
|
||||
}
|
||||
require.NoError(t, f.Close())
|
||||
|
||||
var (
|
||||
wg sync.WaitGroup
|
||||
fs = make([]*Freezer, 5)
|
||||
errs = make([]error, 5)
|
||||
)
|
||||
for i := 0; i < 5; i++ {
|
||||
wg.Add(1)
|
||||
go func(i int) {
|
||||
defer wg.Done()
|
||||
|
||||
f, err := NewFreezer(dir, "", true, 2049, tables)
|
||||
if err == nil {
|
||||
fs[i] = f
|
||||
} else {
|
||||
errs[i] = err
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
for i := range fs {
|
||||
if err := errs[i]; err != nil {
|
||||
t.Fatal("failed to open freezer", err)
|
||||
}
|
||||
require.NoError(t, fs[i].Close())
|
||||
}
|
||||
}
|
||||
|
||||
func newFreezerForTesting(t *testing.T, tables map[string]bool) (*Freezer, string) {
|
||||
t.Helper()
|
||||
|
||||
|
||||
@@ -91,6 +91,9 @@ var (
|
||||
// transitionStatusKey tracks the eth2 transition status.
|
||||
transitionStatusKey = []byte("eth2-transition")
|
||||
|
||||
// snapSyncStatusFlagKey flags that status of snap sync.
|
||||
snapSyncStatusFlagKey = []byte("SnapSyncStatus")
|
||||
|
||||
// Data item prefixes (use single byte to avoid mixing data types, avoid `i`, used for indexes).
|
||||
headerPrefix = []byte("h") // headerPrefix + num (uint64 big endian) + hash -> header
|
||||
headerTDSuffix = []byte("t") // headerPrefix + num (uint64 big endian) + hash + headerTDSuffix -> td
|
||||
|
||||
@@ -446,6 +446,10 @@ func (dl *diskLayer) generateRange(ctx *generatorContext, trieId *trie.ID, prefi
|
||||
internal += time.Since(istart)
|
||||
}
|
||||
if iter.Err != nil {
|
||||
// Trie errors should never happen. Still, in case of a bug, expose the
|
||||
// error here, as the outer code will presume errors are interrupts, not
|
||||
// some deeper issues.
|
||||
log.Error("State snapshotter failed to iterate trie", "err", err)
|
||||
return false, nil, iter.Err
|
||||
}
|
||||
// Delete all stale snapshot states remaining
|
||||
|
||||
@@ -355,7 +355,13 @@ func (p *BlobPool) Init(gasTip *big.Int, head *types.Header, reserve txpool.Addr
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Initialize the state with head block, or fallback to empty one in
|
||||
// case the head state is not available(might occur when node is not
|
||||
// fully synced).
|
||||
state, err := p.chain.StateAt(head.Root)
|
||||
if err != nil {
|
||||
state, err = p.chain.StateAt(types.EmptyRootHash)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ type evictHeap struct {
|
||||
index map[common.Address]int // Indices into the heap for replacements
|
||||
}
|
||||
|
||||
// newPriceHeap creates a new heap of cheapets accounts in the blob pool to evict
|
||||
// newPriceHeap creates a new heap of cheapest accounts in the blob pool to evict
|
||||
// from in case of over saturation.
|
||||
func newPriceHeap(basefee *uint256.Int, blobfee *uint256.Int, index *map[common.Address][]*blobTxMeta) *evictHeap {
|
||||
heap := &evictHeap{
|
||||
|
||||
@@ -143,7 +143,7 @@ func (l *limbo) push(tx *types.Transaction, block uint64) error {
|
||||
return errors.New("already tracked blob transaction")
|
||||
}
|
||||
if err := l.setAndIndex(tx, block); err != nil {
|
||||
log.Error("Failed to set and index liboed blobs", "tx", tx, "err", err)
|
||||
log.Error("Failed to set and index limboed blobs", "tx", tx, "err", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -191,7 +191,7 @@ func (l *limbo) update(txhash common.Hash, block uint64) {
|
||||
log.Trace("Blob transaction unchanged in limbo", "tx", txhash, "block", block)
|
||||
return
|
||||
}
|
||||
// Retrieve the old blobs from the data store and write tehm back with a new
|
||||
// Retrieve the old blobs from the data store and write them back with a new
|
||||
// block number. IF anything fails, there's not much to do, go on.
|
||||
item, err := l.getAndDrop(id)
|
||||
if err != nil {
|
||||
|
||||
@@ -27,7 +27,7 @@ import (
|
||||
var log2_1_125 = math.Log2(1.125)
|
||||
|
||||
// evictionPriority calculates the eviction priority based on the algorithm
|
||||
// described in the BlobPool docs for a both fee components.
|
||||
// described in the BlobPool docs for both fee components.
|
||||
//
|
||||
// This method takes about 8ns on a very recent laptop CPU, recalculating about
|
||||
// 125 million transaction priority values per second.
|
||||
|
||||
@@ -52,6 +52,6 @@ var (
|
||||
ErrOversizedData = errors.New("oversized data")
|
||||
|
||||
// ErrFutureReplacePending is returned if a future transaction replaces a pending
|
||||
// transaction. Future transactions should only be able to replace other future transactions.
|
||||
// one. Future transactions should only be able to replace other future transactions.
|
||||
ErrFutureReplacePending = errors.New("future transaction tries to replace pending")
|
||||
)
|
||||
|
||||
@@ -298,7 +298,20 @@ func (pool *LegacyPool) Init(gasTip *big.Int, head *types.Header, reserve txpool
|
||||
|
||||
// Set the basic pool parameters
|
||||
pool.gasTip.Store(gasTip)
|
||||
pool.reset(nil, head)
|
||||
|
||||
// Initialize the state with head block, or fallback to empty one in
|
||||
// case the head state is not available(might occur when node is not
|
||||
// fully synced).
|
||||
statedb, err := pool.chain.StateAt(head.Root)
|
||||
if err != nil {
|
||||
statedb, err = pool.chain.StateAt(types.EmptyRootHash)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pool.currentHead.Store(head)
|
||||
pool.currentState = statedb
|
||||
pool.pendingNonces = newNoncer(statedb)
|
||||
|
||||
// Start the reorg loop early, so it can handle requests generated during
|
||||
// journal loading.
|
||||
@@ -406,7 +419,7 @@ func (pool *LegacyPool) Close() error {
|
||||
}
|
||||
|
||||
// Reset implements txpool.SubPool, allowing the legacy pool's internal state to be
|
||||
// kept in sync with the main transacion pool's internal state.
|
||||
// kept in sync with the main transaction pool's internal state.
|
||||
func (pool *LegacyPool) Reset(oldHead, newHead *types.Header) {
|
||||
wait := pool.requestReset(oldHead, newHead)
|
||||
<-wait
|
||||
@@ -637,7 +650,7 @@ func (pool *LegacyPool) validateTx(tx *types.Transaction, local bool) error {
|
||||
// pending or queued one, it overwrites the previous transaction if its price is higher.
|
||||
//
|
||||
// If a newly added transaction is marked as local, its sending account will be
|
||||
// be added to the allowlist, preventing any associated transaction from being dropped
|
||||
// added to the allowlist, preventing any associated transaction from being dropped
|
||||
// out of the pool due to pricing constraints.
|
||||
func (pool *LegacyPool) add(tx *types.Transaction, local bool) (replaced bool, err error) {
|
||||
// If the transaction is already known, discard it
|
||||
@@ -901,7 +914,7 @@ func (pool *LegacyPool) promoteTx(addr common.Address, hash common.Hash, tx *typ
|
||||
}
|
||||
|
||||
// addLocals enqueues a batch of transactions into the pool if they are valid, marking the
|
||||
// senders as a local ones, ensuring they go around the local pricing constraints.
|
||||
// senders as local ones, ensuring they go around the local pricing constraints.
|
||||
//
|
||||
// This method is used to add transactions from the RPC API and performs synchronous pool
|
||||
// reorganization and event propagation.
|
||||
@@ -943,7 +956,7 @@ func (pool *LegacyPool) addRemoteSync(tx *types.Transaction) error {
|
||||
}
|
||||
|
||||
// Add enqueues a batch of transactions into the pool if they are valid. Depending
|
||||
// on the local flag, full pricing contraints will or will not be applied.
|
||||
// on the local flag, full pricing constraints will or will not be applied.
|
||||
//
|
||||
// If sync is set, the method will block until all internal maintenance related
|
||||
// to the add is finished. Only use this during tests for determinism!
|
||||
|
||||
@@ -70,7 +70,7 @@ type TxPool struct {
|
||||
reservations map[common.Address]SubPool // Map with the account to pool reservations
|
||||
reserveLock sync.Mutex // Lock protecting the account reservations
|
||||
|
||||
subs event.SubscriptionScope // Subscription scope to unscubscribe all on shutdown
|
||||
subs event.SubscriptionScope // Subscription scope to unsubscribe all on shutdown
|
||||
quit chan chan error // Quit channel to tear down the head updater
|
||||
}
|
||||
|
||||
@@ -404,7 +404,7 @@ func (p *TxPool) Locals() []common.Address {
|
||||
}
|
||||
|
||||
// Status returns the known status (unknown/pending/queued) of a transaction
|
||||
// identified by their hashes.
|
||||
// identified by its hash.
|
||||
func (p *TxPool) Status(hash common.Hash) TxStatus {
|
||||
for _, subpool := range p.subpools {
|
||||
if status := subpool.Status(hash); status != TxStatusUnknown {
|
||||
|
||||
@@ -114,7 +114,7 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types
|
||||
if sidecar == nil {
|
||||
return fmt.Errorf("missing sidecar in blob transaction")
|
||||
}
|
||||
// Ensure the number of items in the blob transaction and vairous side
|
||||
// Ensure the number of items in the blob transaction and various side
|
||||
// data match up before doing any expensive validations
|
||||
hashes := tx.BlobHashes()
|
||||
if len(hashes) == 0 {
|
||||
@@ -182,7 +182,7 @@ type ValidationOptionsWithState struct {
|
||||
// be rejected once the number of remaining slots reaches zero.
|
||||
UsedAndLeftSlots func(addr common.Address) (int, int)
|
||||
|
||||
// ExistingExpenditure is a mandatory callback to retrieve the cummulative
|
||||
// ExistingExpenditure is a mandatory callback to retrieve the cumulative
|
||||
// cost of the already pooled transactions to check for overdrafts.
|
||||
ExistingExpenditure func(addr common.Address) *big.Int
|
||||
|
||||
@@ -237,7 +237,7 @@ func ValidateTransactionWithState(tx *types.Transaction, signer types.Signer, op
|
||||
return fmt.Errorf("%w: balance %v, queued cost %v, tx cost %v, overshot %v", core.ErrInsufficientFunds, balance, spent, cost, new(big.Int).Sub(need, balance))
|
||||
}
|
||||
// Transaction takes a new nonce value out of the pool. Ensure it doesn't
|
||||
// overflow the number of permitted transactions from a single accoun
|
||||
// overflow the number of permitted transactions from a single account
|
||||
// (i.e. max cancellable via out-of-bound transaction).
|
||||
if used, left := opts.UsedAndLeftSlots(from); left <= 0 {
|
||||
return fmt.Errorf("%w: pooled %d txs", ErrAccountLimitExceeded, used)
|
||||
|
||||
@@ -251,6 +251,7 @@ func opKeccak256(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) (
|
||||
size.SetBytes(interpreter.hasherBuf[:])
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opAddress(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
scope.Stack.push(new(uint256.Int).SetBytes(scope.Contract.Address().Bytes()))
|
||||
return nil, nil
|
||||
@@ -267,6 +268,7 @@ func opOrigin(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
|
||||
scope.Stack.push(new(uint256.Int).SetBytes(interpreter.evm.Origin.Bytes()))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opCaller(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
scope.Stack.push(new(uint256.Int).SetBytes(scope.Contract.Caller().Bytes()))
|
||||
return nil, nil
|
||||
|
||||
Reference in New Issue
Block a user