forked from cerc-io/plugeth
Merge tag 'v1.10.8' into develop
This commit is contained in:
@@ -248,8 +248,6 @@ func (g *Genesis) configOrDefault(ghash common.Hash) *params.ChainConfig {
|
||||
return params.RinkebyChainConfig
|
||||
case ghash == params.GoerliGenesisHash:
|
||||
return params.GoerliChainConfig
|
||||
case ghash == params.CalaverasGenesisHash:
|
||||
return params.CalaverasChainConfig
|
||||
default:
|
||||
return params.AllEthashProtocolChanges
|
||||
}
|
||||
@@ -399,18 +397,6 @@ func DefaultGoerliGenesisBlock() *Genesis {
|
||||
}
|
||||
}
|
||||
|
||||
func DefaultCalaverasGenesisBlock() *Genesis {
|
||||
// Full genesis: https://gist.github.com/holiman/c6ed9269dce28304ad176314caa75e97
|
||||
return &Genesis{
|
||||
Config: params.CalaverasChainConfig,
|
||||
Timestamp: 0x60b3877f,
|
||||
ExtraData: hexutil.MustDecode("0x00000000000000000000000000000000000000000000000000000000000000005211cea3870c7ba7c6c44b185e62eecdb864cd8c560228ce57d31efbf64c200b2c200aacec78cf17a7148e784fe95a7a750335f8b9572ee28d72e7650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
|
||||
GasLimit: 0x47b760,
|
||||
Difficulty: big.NewInt(1),
|
||||
Alloc: decodePrealloc(calaverasAllocData),
|
||||
}
|
||||
}
|
||||
|
||||
// DeveloperGenesisBlock returns the 'geth --dev' genesis block.
|
||||
func DeveloperGenesisBlock(period uint64, faucet common.Address) *Genesis {
|
||||
// Override the default period to the user requested one
|
||||
|
||||
@@ -185,10 +185,6 @@ func TestGenesisHashes(t *testing.T) {
|
||||
genesis: DefaultRinkebyGenesisBlock(),
|
||||
hash: params.RinkebyGenesisHash,
|
||||
},
|
||||
{
|
||||
genesis: DefaultCalaverasGenesisBlock(),
|
||||
hash: params.CalaverasGenesisHash,
|
||||
},
|
||||
}
|
||||
for i, c := range cases {
|
||||
b := c.genesis.MustCommit(rawdb.NewMemoryDatabase())
|
||||
|
||||
@@ -89,6 +89,11 @@ func (db *nofreezedb) Ancient(kind string, number uint64) ([]byte, error) {
|
||||
return nil, errNotSupported
|
||||
}
|
||||
|
||||
// ReadAncients returns an error as we don't have a backing chain freezer.
|
||||
func (db *nofreezedb) ReadAncients(kind string, start, max, maxByteSize uint64) ([][]byte, error) {
|
||||
return nil, errNotSupported
|
||||
}
|
||||
|
||||
// Ancients returns an error as we don't have a backing chain freezer.
|
||||
func (db *nofreezedb) Ancients() (uint64, error) {
|
||||
return 0, errNotSupported
|
||||
|
||||
+13
-1
@@ -180,6 +180,18 @@ func (f *freezer) Ancient(kind string, number uint64) ([]byte, error) {
|
||||
return nil, errUnknownTable
|
||||
}
|
||||
|
||||
// ReadAncients retrieves multiple items in sequence, starting from the index 'start'.
|
||||
// It will return
|
||||
// - at most 'max' items,
|
||||
// - at least 1 item (even if exceeding the maxByteSize), but will otherwise
|
||||
// return as many items as fit into maxByteSize.
|
||||
func (f *freezer) ReadAncients(kind string, start, count, maxBytes uint64) ([][]byte, error) {
|
||||
if table := f.tables[kind]; table != nil {
|
||||
return table.RetrieveItems(start, count, maxBytes)
|
||||
}
|
||||
return nil, errUnknownTable
|
||||
}
|
||||
|
||||
// Ancients returns the length of the frozen items.
|
||||
func (f *freezer) Ancients() (uint64, error) {
|
||||
return atomic.LoadUint64(&f.frozen), nil
|
||||
@@ -403,7 +415,7 @@ func (f *freezer) freeze(db ethdb.KeyValueStore) {
|
||||
}
|
||||
batch.Reset()
|
||||
|
||||
// Wipe out side chains also and track dangling side chians
|
||||
// Wipe out side chains also and track dangling side chains
|
||||
var dangling []common.Hash
|
||||
for number := first; number < f.frozen; number++ {
|
||||
// Always keep the genesis block in active database
|
||||
|
||||
+163
-51
@@ -70,6 +70,19 @@ func (i *indexEntry) marshallBinary() []byte {
|
||||
return b
|
||||
}
|
||||
|
||||
// bounds returns the start- and end- offsets, and the file number of where to
|
||||
// read there data item marked by the two index entries. The two entries are
|
||||
// assumed to be sequential.
|
||||
func (start *indexEntry) bounds(end *indexEntry) (startOffset, endOffset, fileId uint32) {
|
||||
if start.filenum != end.filenum {
|
||||
// If a piece of data 'crosses' a data-file,
|
||||
// it's actually in one piece on the second data-file.
|
||||
// We return a zero-indexEntry for the second file as start
|
||||
return 0, end.offset, end.filenum
|
||||
}
|
||||
return start.offset, end.offset, end.filenum
|
||||
}
|
||||
|
||||
// freezerTable represents a single chained data table within the freezer (e.g. blocks).
|
||||
// It consists of a data file (snappy encoded arbitrary data blobs) and an indexEntry
|
||||
// file (uncompressed 64 bit indices into the data file).
|
||||
@@ -546,84 +559,183 @@ func (t *freezerTable) append(item uint64, encodedBlob []byte, wlock bool) (bool
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// getBounds returns the indexes for the item
|
||||
// returns start, end, filenumber and error
|
||||
func (t *freezerTable) getBounds(item uint64) (uint32, uint32, uint32, error) {
|
||||
buffer := make([]byte, indexEntrySize)
|
||||
var startIdx, endIdx indexEntry
|
||||
// Read second index
|
||||
if _, err := t.index.ReadAt(buffer, int64((item+1)*indexEntrySize)); err != nil {
|
||||
return 0, 0, 0, err
|
||||
// getIndices returns the index entries for the given from-item, covering 'count' items.
|
||||
// N.B: The actual number of returned indices for N items will always be N+1 (unless an
|
||||
// error is returned).
|
||||
// OBS: This method assumes that the caller has already verified (and/or trimmed) the range
|
||||
// so that the items are within bounds. If this method is used to read out of bounds,
|
||||
// it will return error.
|
||||
func (t *freezerTable) getIndices(from, count uint64) ([]*indexEntry, error) {
|
||||
// Apply the table-offset
|
||||
from = from - uint64(t.itemOffset)
|
||||
// For reading N items, we need N+1 indices.
|
||||
buffer := make([]byte, (count+1)*indexEntrySize)
|
||||
if _, err := t.index.ReadAt(buffer, int64(from*indexEntrySize)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
endIdx.unmarshalBinary(buffer)
|
||||
// Read first index (unless it's the very first item)
|
||||
if item != 0 {
|
||||
if _, err := t.index.ReadAt(buffer, int64(item*indexEntrySize)); err != nil {
|
||||
return 0, 0, 0, err
|
||||
}
|
||||
startIdx.unmarshalBinary(buffer)
|
||||
} else {
|
||||
var (
|
||||
indices []*indexEntry
|
||||
offset int
|
||||
)
|
||||
for i := from; i <= from+count; i++ {
|
||||
index := new(indexEntry)
|
||||
index.unmarshalBinary(buffer[offset:])
|
||||
offset += indexEntrySize
|
||||
indices = append(indices, index)
|
||||
}
|
||||
if from == 0 {
|
||||
// Special case if we're reading the first item in the freezer. We assume that
|
||||
// the first item always start from zero(regarding the deletion, we
|
||||
// only support deletion by files, so that the assumption is held).
|
||||
// This means we can use the first item metadata to carry information about
|
||||
// the 'global' offset, for the deletion-case
|
||||
return 0, endIdx.offset, endIdx.filenum, nil
|
||||
indices[0].offset = 0
|
||||
indices[0].filenum = indices[1].filenum
|
||||
}
|
||||
if startIdx.filenum != endIdx.filenum {
|
||||
// If a piece of data 'crosses' a data-file,
|
||||
// it's actually in one piece on the second data-file.
|
||||
// We return a zero-indexEntry for the second file as start
|
||||
return 0, endIdx.offset, endIdx.filenum, nil
|
||||
}
|
||||
return startIdx.offset, endIdx.offset, endIdx.filenum, nil
|
||||
return indices, nil
|
||||
}
|
||||
|
||||
// Retrieve looks up the data offset of an item with the given number and retrieves
|
||||
// the raw binary blob from the data file.
|
||||
func (t *freezerTable) Retrieve(item uint64) ([]byte, error) {
|
||||
blob, err := t.retrieve(item)
|
||||
items, err := t.RetrieveItems(item, 1, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if t.noCompression {
|
||||
return blob, nil
|
||||
}
|
||||
return snappy.Decode(nil, blob)
|
||||
return items[0], nil
|
||||
}
|
||||
|
||||
// retrieve looks up the data offset of an item with the given number and retrieves
|
||||
// the raw binary blob from the data file. OBS! This method does not decode
|
||||
// compressed data.
|
||||
func (t *freezerTable) retrieve(item uint64) ([]byte, error) {
|
||||
// RetrieveItems returns multiple items in sequence, starting from the index 'start'.
|
||||
// It will return at most 'max' items, but will abort earlier to respect the
|
||||
// 'maxBytes' argument. However, if the 'maxBytes' is smaller than the size of one
|
||||
// item, it _will_ return one element and possibly overflow the maxBytes.
|
||||
func (t *freezerTable) RetrieveItems(start, count, maxBytes uint64) ([][]byte, error) {
|
||||
// First we read the 'raw' data, which might be compressed.
|
||||
diskData, sizes, err := t.retrieveItems(start, count, maxBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var (
|
||||
output = make([][]byte, 0, count)
|
||||
offset int // offset for reading
|
||||
outputSize int // size of uncompressed data
|
||||
)
|
||||
// Now slice up the data and decompress.
|
||||
for i, diskSize := range sizes {
|
||||
item := diskData[offset : offset+diskSize]
|
||||
offset += diskSize
|
||||
decompressedSize := diskSize
|
||||
if !t.noCompression {
|
||||
decompressedSize, _ = snappy.DecodedLen(item)
|
||||
}
|
||||
if i > 0 && uint64(outputSize+decompressedSize) > maxBytes {
|
||||
break
|
||||
}
|
||||
if !t.noCompression {
|
||||
data, err := snappy.Decode(nil, item)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
output = append(output, data)
|
||||
} else {
|
||||
output = append(output, item)
|
||||
}
|
||||
outputSize += decompressedSize
|
||||
}
|
||||
return output, nil
|
||||
}
|
||||
|
||||
// retrieveItems reads up to 'count' items from the table. It reads at least
|
||||
// one item, but otherwise avoids reading more than maxBytes bytes.
|
||||
// It returns the (potentially compressed) data, and the sizes.
|
||||
func (t *freezerTable) retrieveItems(start, count, maxBytes uint64) ([]byte, []int, error) {
|
||||
t.lock.RLock()
|
||||
defer t.lock.RUnlock()
|
||||
// Ensure the table and the item is accessible
|
||||
if t.index == nil || t.head == nil {
|
||||
return nil, errClosed
|
||||
return nil, nil, errClosed
|
||||
}
|
||||
if atomic.LoadUint64(&t.items) <= item {
|
||||
return nil, errOutOfBounds
|
||||
itemCount := atomic.LoadUint64(&t.items) // max number
|
||||
// Ensure the start is written, not deleted from the tail, and that the
|
||||
// caller actually wants something
|
||||
if itemCount <= start || uint64(t.itemOffset) > start || count == 0 {
|
||||
return nil, nil, errOutOfBounds
|
||||
}
|
||||
// Ensure the item was not deleted from the tail either
|
||||
if uint64(t.itemOffset) > item {
|
||||
return nil, errOutOfBounds
|
||||
if start+count > itemCount {
|
||||
count = itemCount - start
|
||||
}
|
||||
startOffset, endOffset, filenum, err := t.getBounds(item - uint64(t.itemOffset))
|
||||
var (
|
||||
output = make([]byte, maxBytes) // Buffer to read data into
|
||||
outputSize int // Used size of that buffer
|
||||
)
|
||||
// readData is a helper method to read a single data item from disk.
|
||||
readData := func(fileId, start uint32, length int) error {
|
||||
// In case a small limit is used, and the elements are large, may need to
|
||||
// realloc the read-buffer when reading the first (and only) item.
|
||||
if len(output) < length {
|
||||
output = make([]byte, length)
|
||||
}
|
||||
dataFile, exist := t.files[fileId]
|
||||
if !exist {
|
||||
return fmt.Errorf("missing data file %d", fileId)
|
||||
}
|
||||
if _, err := dataFile.ReadAt(output[outputSize:outputSize+length], int64(start)); err != nil {
|
||||
return err
|
||||
}
|
||||
outputSize += length
|
||||
return nil
|
||||
}
|
||||
// Read all the indexes in one go
|
||||
indices, err := t.getIndices(start, count)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
dataFile, exist := t.files[filenum]
|
||||
if !exist {
|
||||
return nil, fmt.Errorf("missing data file %d", filenum)
|
||||
var (
|
||||
sizes []int // The sizes for each element
|
||||
totalSize = 0 // The total size of all data read so far
|
||||
readStart = indices[0].offset // Where, in the file, to start reading
|
||||
unreadSize = 0 // The size of the as-yet-unread data
|
||||
)
|
||||
|
||||
for i, firstIndex := range indices[:len(indices)-1] {
|
||||
secondIndex := indices[i+1]
|
||||
// Determine the size of the item.
|
||||
offset1, offset2, _ := firstIndex.bounds(secondIndex)
|
||||
size := int(offset2 - offset1)
|
||||
// Crossing a file boundary?
|
||||
if secondIndex.filenum != firstIndex.filenum {
|
||||
// If we have unread data in the first file, we need to do that read now.
|
||||
if unreadSize > 0 {
|
||||
if err := readData(firstIndex.filenum, readStart, unreadSize); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
unreadSize = 0
|
||||
}
|
||||
readStart = 0
|
||||
}
|
||||
if i > 0 && uint64(totalSize+size) > maxBytes {
|
||||
// About to break out due to byte limit being exceeded. We don't
|
||||
// read this last item, but we need to do the deferred reads now.
|
||||
if unreadSize > 0 {
|
||||
if err := readData(secondIndex.filenum, readStart, unreadSize); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
// Defer the read for later
|
||||
unreadSize += size
|
||||
totalSize += size
|
||||
sizes = append(sizes, size)
|
||||
if i == len(indices)-2 || uint64(totalSize) > maxBytes {
|
||||
// Last item, need to do the read now
|
||||
if err := readData(secondIndex.filenum, readStart, unreadSize); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
// Retrieve the data itself, decompress and return
|
||||
blob := make([]byte, endOffset-startOffset)
|
||||
if _, err := dataFile.ReadAt(blob, int64(startOffset)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.readMeter.Mark(int64(len(blob) + 2*indexEntrySize))
|
||||
return blob, nil
|
||||
return output[:outputSize], sizes, nil
|
||||
}
|
||||
|
||||
// has returns an indicator whether the specified number data
|
||||
|
||||
@@ -74,7 +74,7 @@ func TestFreezerBasics(t *testing.T) {
|
||||
exp := getChunk(15, y)
|
||||
got, err := f.Retrieve(uint64(y))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
t.Fatalf("reading item %d: %v", y, err)
|
||||
}
|
||||
if !bytes.Equal(got, exp) {
|
||||
t.Fatalf("test %d, got \n%x != \n%x", y, got, exp)
|
||||
@@ -692,3 +692,118 @@ func TestAppendTruncateParallel(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestSequentialRead does some basic tests on the RetrieveItems.
|
||||
func TestSequentialRead(t *testing.T) {
|
||||
rm, wm, sg := metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge()
|
||||
fname := fmt.Sprintf("batchread-%d", rand.Uint64())
|
||||
{ // Fill table
|
||||
f, err := newCustomTable(os.TempDir(), fname, rm, wm, sg, 50, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Write 15 bytes 30 times
|
||||
for x := 0; x < 30; x++ {
|
||||
data := getChunk(15, x)
|
||||
f.Append(uint64(x), data)
|
||||
}
|
||||
f.DumpIndex(0, 30)
|
||||
f.Close()
|
||||
}
|
||||
{ // Open it, iterate, verify iteration
|
||||
f, err := newCustomTable(os.TempDir(), fname, rm, wm, sg, 50, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
items, err := f.RetrieveItems(0, 10000, 100000)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if have, want := len(items), 30; have != want {
|
||||
t.Fatalf("want %d items, have %d ", want, have)
|
||||
}
|
||||
for i, have := range items {
|
||||
want := getChunk(15, i)
|
||||
if !bytes.Equal(want, have) {
|
||||
t.Fatalf("data corruption: have\n%x\n, want \n%x\n", have, want)
|
||||
}
|
||||
}
|
||||
f.Close()
|
||||
}
|
||||
{ // Open it, iterate, verify byte limit. The byte limit is less than item
|
||||
// size, so each lookup should only return one item
|
||||
f, err := newCustomTable(os.TempDir(), fname, rm, wm, sg, 40, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
items, err := f.RetrieveItems(0, 10000, 10)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if have, want := len(items), 1; have != want {
|
||||
t.Fatalf("want %d items, have %d ", want, have)
|
||||
}
|
||||
for i, have := range items {
|
||||
want := getChunk(15, i)
|
||||
if !bytes.Equal(want, have) {
|
||||
t.Fatalf("data corruption: have\n%x\n, want \n%x\n", have, want)
|
||||
}
|
||||
}
|
||||
f.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// TestSequentialReadByteLimit does some more advanced tests on batch reads.
|
||||
// These tests check that when the byte limit hits, we correctly abort in time,
|
||||
// but also properly do all the deferred reads for the previous data, regardless
|
||||
// of whether the data crosses a file boundary or not.
|
||||
func TestSequentialReadByteLimit(t *testing.T) {
|
||||
rm, wm, sg := metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge()
|
||||
fname := fmt.Sprintf("batchread-2-%d", rand.Uint64())
|
||||
{ // Fill table
|
||||
f, err := newCustomTable(os.TempDir(), fname, rm, wm, sg, 100, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Write 10 bytes 30 times,
|
||||
// Splitting it at every 100 bytes (10 items)
|
||||
for x := 0; x < 30; x++ {
|
||||
data := getChunk(10, x)
|
||||
f.Append(uint64(x), data)
|
||||
}
|
||||
f.Close()
|
||||
}
|
||||
for i, tc := range []struct {
|
||||
items uint64
|
||||
limit uint64
|
||||
want int
|
||||
}{
|
||||
{9, 89, 8},
|
||||
{10, 99, 9},
|
||||
{11, 109, 10},
|
||||
{100, 89, 8},
|
||||
{100, 99, 9},
|
||||
{100, 109, 10},
|
||||
} {
|
||||
{
|
||||
f, err := newCustomTable(os.TempDir(), fname, rm, wm, sg, 100, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
items, err := f.RetrieveItems(0, tc.items, tc.limit)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if have, want := len(items), tc.want; have != want {
|
||||
t.Fatalf("test %d: want %d items, have %d ", i, want, have)
|
||||
}
|
||||
for ii, have := range items {
|
||||
want := getChunk(10, ii)
|
||||
if !bytes.Equal(want, have) {
|
||||
t.Fatalf("test %d: data corruption item %d: have\n%x\n, want \n%x\n", i, ii, have, want)
|
||||
}
|
||||
}
|
||||
f.Close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +62,12 @@ func (t *table) Ancient(kind string, number uint64) ([]byte, error) {
|
||||
return t.db.Ancient(kind, number)
|
||||
}
|
||||
|
||||
// ReadAncients is a noop passthrough that just forwards the request to the underlying
|
||||
// database.
|
||||
func (t *table) ReadAncients(kind string, start, count, maxBytes uint64) ([][]byte, error) {
|
||||
return t.db.ReadAncients(kind, start, count, maxBytes)
|
||||
}
|
||||
|
||||
// Ancients is a noop passthrough that just forwards the request to the underlying
|
||||
// database.
|
||||
func (t *table) Ancients() (uint64, error) {
|
||||
|
||||
@@ -74,7 +74,7 @@ type Message interface {
|
||||
Value() *big.Int
|
||||
|
||||
Nonce() uint64
|
||||
CheckNonce() bool
|
||||
IsFake() bool
|
||||
Data() []byte
|
||||
AccessList() types.AccessList
|
||||
}
|
||||
@@ -212,8 +212,9 @@ func (st *StateTransition) buyGas() error {
|
||||
}
|
||||
|
||||
func (st *StateTransition) preCheck() error {
|
||||
// Make sure this transaction's nonce is correct.
|
||||
if st.msg.CheckNonce() {
|
||||
// Only check transactions that are not fake
|
||||
if !st.msg.IsFake() {
|
||||
// Make sure this transaction's nonce is correct.
|
||||
stNonce := st.state.GetNonce(st.msg.From())
|
||||
if msgNonce := st.msg.Nonce(); stNonce < msgNonce {
|
||||
return fmt.Errorf("%w: address %v, tx: %d state: %d", ErrNonceTooHigh,
|
||||
@@ -222,11 +223,11 @@ func (st *StateTransition) preCheck() error {
|
||||
return fmt.Errorf("%w: address %v, tx: %d state: %d", ErrNonceTooLow,
|
||||
st.msg.From().Hex(), msgNonce, stNonce)
|
||||
}
|
||||
}
|
||||
// Make sure the sender is an EOA
|
||||
if codeHash := st.state.GetCodeHash(st.msg.From()); codeHash != emptyCodeHash && codeHash != (common.Hash{}) {
|
||||
return fmt.Errorf("%w: address %v, codehash: %s", ErrSenderNoEOA,
|
||||
st.msg.From().Hex(), codeHash)
|
||||
// Make sure the sender is an EOA
|
||||
if codeHash := st.state.GetCodeHash(st.msg.From()); codeHash != emptyCodeHash && codeHash != (common.Hash{}) {
|
||||
return fmt.Errorf("%w: address %v, codehash: %s", ErrSenderNoEOA,
|
||||
st.msg.From().Hex(), codeHash)
|
||||
}
|
||||
}
|
||||
// Make sure that transaction gasFeeCap is greater than the baseFee (post london)
|
||||
if st.evm.ChainConfig().IsLondon(st.evm.Context.BlockNumber) {
|
||||
|
||||
@@ -579,10 +579,10 @@ type Message struct {
|
||||
gasTipCap *big.Int
|
||||
data []byte
|
||||
accessList AccessList
|
||||
checkNonce bool
|
||||
isFake bool
|
||||
}
|
||||
|
||||
func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice, gasFeeCap, gasTipCap *big.Int, data []byte, accessList AccessList, checkNonce bool) Message {
|
||||
func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice, gasFeeCap, gasTipCap *big.Int, data []byte, accessList AccessList, isFake bool) Message {
|
||||
return Message{
|
||||
from: from,
|
||||
to: to,
|
||||
@@ -594,7 +594,7 @@ func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *b
|
||||
gasTipCap: gasTipCap,
|
||||
data: data,
|
||||
accessList: accessList,
|
||||
checkNonce: checkNonce,
|
||||
isFake: isFake,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -610,7 +610,7 @@ func (tx *Transaction) AsMessage(s Signer, baseFee *big.Int) (Message, error) {
|
||||
amount: tx.Value(),
|
||||
data: tx.Data(),
|
||||
accessList: tx.AccessList(),
|
||||
checkNonce: true,
|
||||
isFake: false,
|
||||
}
|
||||
// If baseFee provided, set gasPrice to effectiveGasPrice.
|
||||
if baseFee != nil {
|
||||
@@ -631,4 +631,4 @@ func (m Message) Gas() uint64 { return m.gasLimit }
|
||||
func (m Message) Nonce() uint64 { return m.nonce }
|
||||
func (m Message) Data() []byte { return m.data }
|
||||
func (m Message) AccessList() AccessList { return m.accessList }
|
||||
func (m Message) CheckNonce() bool { return m.checkNonce }
|
||||
func (m Message) IsFake() bool { return m.isFake }
|
||||
|
||||
+78
-16
@@ -16,17 +16,49 @@
|
||||
|
||||
package vm
|
||||
|
||||
const (
|
||||
set2BitsMask = uint16(0b1100_0000_0000_0000)
|
||||
set3BitsMask = uint16(0b1110_0000_0000_0000)
|
||||
set4BitsMask = uint16(0b1111_0000_0000_0000)
|
||||
set5BitsMask = uint16(0b1111_1000_0000_0000)
|
||||
set6BitsMask = uint16(0b1111_1100_0000_0000)
|
||||
set7BitsMask = uint16(0b1111_1110_0000_0000)
|
||||
)
|
||||
|
||||
// bitvec is a bit vector which maps bytes in a program.
|
||||
// An unset bit means the byte is an opcode, a set bit means
|
||||
// it's data (i.e. argument of PUSHxx).
|
||||
type bitvec []byte
|
||||
|
||||
func (bits *bitvec) set(pos uint64) {
|
||||
(*bits)[pos/8] |= 0x80 >> (pos % 8)
|
||||
var lookup = [8]byte{
|
||||
0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1,
|
||||
}
|
||||
func (bits *bitvec) set8(pos uint64) {
|
||||
(*bits)[pos/8] |= 0xFF >> (pos % 8)
|
||||
(*bits)[pos/8+1] |= ^(0xFF >> (pos % 8))
|
||||
|
||||
func (bits bitvec) set1(pos uint64) {
|
||||
bits[pos/8] |= lookup[pos%8]
|
||||
}
|
||||
|
||||
func (bits bitvec) setN(flag uint16, pos uint64) {
|
||||
a := flag >> (pos % 8)
|
||||
bits[pos/8] |= byte(a >> 8)
|
||||
if b := byte(a); b != 0 {
|
||||
// If the bit-setting affects the neighbouring byte, we can assign - no need to OR it,
|
||||
// since it's the first write to that byte
|
||||
bits[pos/8+1] = b
|
||||
}
|
||||
}
|
||||
|
||||
func (bits bitvec) set8(pos uint64) {
|
||||
a := byte(0xFF >> (pos % 8))
|
||||
bits[pos/8] |= a
|
||||
bits[pos/8+1] = ^a
|
||||
}
|
||||
|
||||
func (bits bitvec) set16(pos uint64) {
|
||||
a := byte(0xFF >> (pos % 8))
|
||||
bits[pos/8] |= a
|
||||
bits[pos/8+1] = 0xFF
|
||||
bits[pos/8+2] = ^a
|
||||
}
|
||||
|
||||
// codeSegment checks if the position is in a code segment.
|
||||
@@ -40,22 +72,52 @@ func codeBitmap(code []byte) bitvec {
|
||||
// ends with a PUSH32, the algorithm will push zeroes onto the
|
||||
// bitvector outside the bounds of the actual code.
|
||||
bits := make(bitvec, len(code)/8+1+4)
|
||||
return codeBitmapInternal(code, bits)
|
||||
}
|
||||
|
||||
// codeBitmapInternal is the internal implementation of codeBitmap.
|
||||
// It exists for the purpose of being able to run benchmark tests
|
||||
// without dynamic allocations affecting the results.
|
||||
func codeBitmapInternal(code, bits bitvec) bitvec {
|
||||
for pc := uint64(0); pc < uint64(len(code)); {
|
||||
op := OpCode(code[pc])
|
||||
|
||||
if op >= PUSH1 && op <= PUSH32 {
|
||||
numbits := op - PUSH1 + 1
|
||||
pc++
|
||||
pc++
|
||||
if op < PUSH1 || op > PUSH32 {
|
||||
continue
|
||||
}
|
||||
numbits := op - PUSH1 + 1
|
||||
if numbits >= 8 {
|
||||
for ; numbits >= 16; numbits -= 16 {
|
||||
bits.set16(pc)
|
||||
pc += 16
|
||||
}
|
||||
for ; numbits >= 8; numbits -= 8 {
|
||||
bits.set8(pc) // 8
|
||||
bits.set8(pc)
|
||||
pc += 8
|
||||
}
|
||||
for ; numbits > 0; numbits-- {
|
||||
bits.set(pc)
|
||||
pc++
|
||||
}
|
||||
} else {
|
||||
pc++
|
||||
}
|
||||
switch numbits {
|
||||
case 1:
|
||||
bits.set1(pc)
|
||||
pc += 1
|
||||
case 2:
|
||||
bits.setN(set2BitsMask, pc)
|
||||
pc += 2
|
||||
case 3:
|
||||
bits.setN(set3BitsMask, pc)
|
||||
pc += 3
|
||||
case 4:
|
||||
bits.setN(set4BitsMask, pc)
|
||||
pc += 4
|
||||
case 5:
|
||||
bits.setN(set5BitsMask, pc)
|
||||
pc += 5
|
||||
case 6:
|
||||
bits.setN(set6BitsMask, pc)
|
||||
pc += 6
|
||||
case 7:
|
||||
bits.setN(set7BitsMask, pc)
|
||||
pc += 7
|
||||
}
|
||||
}
|
||||
return bits
|
||||
|
||||
@@ -47,10 +47,10 @@ func TestJumpDestAnalysis(t *testing.T) {
|
||||
{[]byte{byte(PUSH32)}, 0xFF, 1},
|
||||
{[]byte{byte(PUSH32)}, 0xFF, 2},
|
||||
}
|
||||
for _, test := range tests {
|
||||
for i, test := range tests {
|
||||
ret := codeBitmap(test.code)
|
||||
if ret[test.which] != test.exp {
|
||||
t.Fatalf("expected %x, got %02x", test.exp, ret[test.which])
|
||||
t.Fatalf("test %d: expected %x, got %02x", i, test.exp, ret[test.which])
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,3 +73,23 @@ func BenchmarkJumpdestHashing_1200k(bench *testing.B) {
|
||||
}
|
||||
bench.StopTimer()
|
||||
}
|
||||
|
||||
func BenchmarkJumpdestOpAnalysis(bench *testing.B) {
|
||||
var op OpCode
|
||||
bencher := func(b *testing.B) {
|
||||
code := make([]byte, 32*b.N)
|
||||
for i := range code {
|
||||
code[i] = byte(op)
|
||||
}
|
||||
bits := make(bitvec, len(code)/8+1+4)
|
||||
b.ResetTimer()
|
||||
codeBitmapInternal(code, bits)
|
||||
}
|
||||
for op = PUSH1; op <= PUSH32; op++ {
|
||||
bench.Run(op.String(), bencher)
|
||||
}
|
||||
op = JUMPDEST
|
||||
bench.Run(op.String(), bencher)
|
||||
op = STOP
|
||||
bench.Run(op.String(), bencher)
|
||||
}
|
||||
|
||||
@@ -669,6 +669,7 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt
|
||||
}
|
||||
stack.push(&temp)
|
||||
if err == nil || err == ErrExecutionReverted {
|
||||
ret = common.CopyBytes(ret)
|
||||
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||
}
|
||||
scope.Contract.Gas += returnGas
|
||||
@@ -703,6 +704,7 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
|
||||
}
|
||||
stack.push(&temp)
|
||||
if err == nil || err == ErrExecutionReverted {
|
||||
ret = common.CopyBytes(ret)
|
||||
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||
}
|
||||
scope.Contract.Gas += returnGas
|
||||
@@ -730,6 +732,7 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext
|
||||
}
|
||||
stack.push(&temp)
|
||||
if err == nil || err == ErrExecutionReverted {
|
||||
ret = common.CopyBytes(ret)
|
||||
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||
}
|
||||
scope.Contract.Gas += returnGas
|
||||
@@ -757,6 +760,7 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
|
||||
}
|
||||
stack.push(&temp)
|
||||
if err == nil || err == ErrExecutionReverted {
|
||||
ret = common.CopyBytes(ret)
|
||||
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||
}
|
||||
scope.Contract.Gas += returnGas
|
||||
|
||||
@@ -262,7 +262,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
||||
// if the operation clears the return data (e.g. it has returning data)
|
||||
// set the last return to the result of the operation.
|
||||
if operation.returns {
|
||||
in.returnData = common.CopyBytes(res)
|
||||
in.returnData = res
|
||||
}
|
||||
|
||||
switch {
|
||||
|
||||
Reference in New Issue
Block a user