diff --git a/accounts/accounts.go b/accounts/accounts.go
index 7bd911577a..6249beed0d 100644
--- a/accounts/accounts.go
+++ b/accounts/accounts.go
@@ -24,8 +24,8 @@ import (
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/crypto/keccak"
"github.com/ethereum/go-ethereum/event"
- "golang.org/x/crypto/sha3"
)
// Account represents an Ethereum account located at a specific location defined
@@ -196,7 +196,7 @@ func TextHash(data []byte) []byte {
// This gives context to the signed message and prevents signing of transactions.
func TextAndHash(data []byte) ([]byte, string) {
msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(data), data)
- hasher := sha3.NewLegacyKeccak256()
+ hasher := keccak.NewLegacyKeccak256()
hasher.Write([]byte(msg))
return hasher.Sum(nil), msg
}
diff --git a/accounts/usbwallet/hub.go b/accounts/usbwallet/hub.go
index 81457b7da2..6f8ac0d8d9 100644
--- a/accounts/usbwallet/hub.go
+++ b/accounts/usbwallet/hub.go
@@ -43,6 +43,14 @@ const refreshCycle = time.Second
// trashing.
const refreshThrottling = 500 * time.Millisecond
+const (
+ // deviceUsagePage identifies Ledger devices by HID usage page (0xffa0) on Windows and macOS.
+ // See: https://github.com/LedgerHQ/ledger-live/blob/05a2980e838955a11a1418da638ef8ac3df4fb74/libs/ledgerjs/packages/hw-transport-node-hid-noevents/src/TransportNodeHid.ts
+ deviceUsagePage = 0xffa0
+ // deviceInterface identifies Ledger devices by USB interface number (0) on Linux.
+ deviceInterface = 0
+)
+
// Hub is a accounts.Backend that can find and handle generic USB hardware wallets.
type Hub struct {
scheme string // Protocol scheme prefixing account and wallet URLs.
@@ -82,6 +90,7 @@ func NewLedgerHub() (*Hub, error) {
0x0005, /* Ledger Nano S Plus */
0x0006, /* Ledger Nano FTS */
0x0007, /* Ledger Flex */
+ 0x0008, /* Ledger Nano Gen5 */
0x0000, /* WebUSB Ledger Blue */
0x1000, /* WebUSB Ledger Nano S */
@@ -89,7 +98,8 @@ func NewLedgerHub() (*Hub, error) {
0x5000, /* WebUSB Ledger Nano S Plus */
0x6000, /* WebUSB Ledger Nano FTS */
0x7000, /* WebUSB Ledger Flex */
- }, 0xffa0, 0, newLedgerDriver)
+ 0x8000, /* WebUSB Ledger Nano Gen5 */
+ }, deviceUsagePage, deviceInterface, newLedgerDriver)
}
// NewTrezorHubWithHID creates a new hardware wallet manager for Trezor devices.
diff --git a/cmd/era/main.go b/cmd/era/main.go
index 35a889d4dc..1c26f44ad4 100644
--- a/cmd/era/main.go
+++ b/cmd/era/main.go
@@ -30,6 +30,8 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/internal/era"
+ "github.com/ethereum/go-ethereum/internal/era/execdb"
+ "github.com/ethereum/go-ethereum/internal/era/onedb"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/internal/flags"
"github.com/ethereum/go-ethereum/params"
@@ -53,7 +55,7 @@ var (
eraSizeFlag = &cli.IntFlag{
Name: "size",
Usage: "number of blocks per era",
- Value: era.MaxEra1Size,
+ Value: era.MaxSize,
}
txsFlag = &cli.BoolFlag{
Name: "txs",
@@ -131,7 +133,7 @@ func block(ctx *cli.Context) error {
return nil
}
-// info prints some high-level information about the era1 file.
+// info prints some high-level information about the era file.
func info(ctx *cli.Context) error {
epoch, err := strconv.ParseUint(ctx.Args().First(), 10, 64)
if err != nil {
@@ -142,33 +144,34 @@ func info(ctx *cli.Context) error {
return err
}
defer e.Close()
- acc, err := e.Accumulator()
- if err != nil {
- return fmt.Errorf("error reading accumulator: %w", err)
+ var (
+ accHex string
+ tdStr string
+ )
+ if acc, err := e.Accumulator(); err == nil {
+ accHex = acc.Hex()
}
- td, err := e.InitialTD()
- if err != nil {
- return fmt.Errorf("error reading total difficulty: %w", err)
+ if td, err := e.InitialTD(); err == nil {
+ tdStr = td.String()
}
info := struct {
- Accumulator common.Hash `json:"accumulator"`
- TotalDifficulty *big.Int `json:"totalDifficulty"`
- StartBlock uint64 `json:"startBlock"`
- Count uint64 `json:"count"`
+ Accumulator string `json:"accumulator,omitempty"`
+ TotalDifficulty string `json:"totalDifficulty,omitempty"`
+ StartBlock uint64 `json:"startBlock"`
+ Count uint64 `json:"count"`
}{
- acc, td, e.Start(), e.Count(),
+ accHex, tdStr, e.Start(), e.Count(),
}
b, _ := json.MarshalIndent(info, "", " ")
fmt.Println(string(b))
return nil
}
-// open opens an era1 file at a certain epoch.
-func open(ctx *cli.Context, epoch uint64) (*era.Era, error) {
- var (
- dir = ctx.String(dirFlag.Name)
- network = ctx.String(networkFlag.Name)
- )
+// open opens an era file at a certain epoch.
+func open(ctx *cli.Context, epoch uint64) (era.Era, error) {
+ dir := ctx.String(dirFlag.Name)
+ network := ctx.String(networkFlag.Name)
+
entries, err := era.ReadDir(dir, network)
if err != nil {
return nil, fmt.Errorf("error reading era dir: %w", err)
@@ -176,7 +179,28 @@ func open(ctx *cli.Context, epoch uint64) (*era.Era, error) {
if epoch >= uint64(len(entries)) {
return nil, fmt.Errorf("epoch out-of-bounds: last %d, want %d", len(entries)-1, epoch)
}
- return era.Open(filepath.Join(dir, entries[epoch]))
+ path := filepath.Join(dir, entries[epoch])
+ return openByPath(path)
+}
+
+// openByPath tries to open a single file as either eraE or era1 based on extension,
+// falling back to the other reader if needed.
+func openByPath(path string) (era.Era, error) {
+ switch strings.ToLower(filepath.Ext(path)) {
+ case ".erae":
+ if e, err := execdb.Open(path); err != nil {
+ return nil, err
+ } else {
+ return e, nil
+ }
+ case ".era1":
+ if e, err := onedb.Open(path); err != nil {
+ return nil, err
+ } else {
+ return e, nil
+ }
+ }
+ return nil, fmt.Errorf("unsupported or unreadable era file: %s", path)
}
// verify checks each era1 file in a directory to ensure it is well-formed and
@@ -203,18 +227,58 @@ func verify(ctx *cli.Context) error {
return fmt.Errorf("error reading %s: %w", dir, err)
}
- if len(entries) != len(roots) {
- return errors.New("number of era1 files should match the number of accumulator hashes")
+ // Build the verification list respecting the rule:
+ // era1: must have accumulator, always verify
+ // erae: verify only if accumulator exists (pre-merge)
+
+ // Build list of files to verify.
+ verify := make([]string, 0, len(entries))
+
+ for _, name := range entries {
+ path := filepath.Join(dir, name)
+ ext := strings.ToLower(filepath.Ext(name))
+
+ switch ext {
+ case ".era1":
+ e, err := onedb.Open(path)
+ if err != nil {
+ return fmt.Errorf("error opening era1 file %s: %w", name, err)
+ }
+ _, accErr := e.Accumulator()
+ e.Close()
+ if accErr != nil {
+ return fmt.Errorf("era1 file %s missing accumulator: %w", name, accErr)
+ }
+ verify = append(verify, path)
+
+ case ".erae":
+ e, err := execdb.Open(path)
+ if err != nil {
+ return fmt.Errorf("error opening erae file %s: %w", name, err)
+ }
+ _, accErr := e.Accumulator()
+ e.Close()
+ if accErr == nil {
+ verify = append(verify, path) // pre-merge only
+ }
+ default:
+ return fmt.Errorf("unsupported era file: %s", name)
+ }
+ }
+
+ if len(verify) != len(roots) {
+ return fmt.Errorf("mismatch between eras to verify (%d) and provided roots (%d)", len(verify), len(roots))
}
// Verify each epoch matches the expected root.
for i, want := range roots {
// Wrap in function so defers don't stack.
err := func() error {
- name := entries[i]
- e, err := era.Open(filepath.Join(dir, name))
+ path := verify[i]
+ name := filepath.Base(path)
+ e, err := openByPath(path)
if err != nil {
- return fmt.Errorf("error opening era1 file %s: %w", name, err)
+ return fmt.Errorf("error opening era file %s: %w", name, err)
}
defer e.Close()
// Read accumulator and check against expected.
@@ -243,7 +307,7 @@ func verify(ctx *cli.Context) error {
}
// checkAccumulator verifies the accumulator matches the data in the Era.
-func checkAccumulator(e *era.Era) error {
+func checkAccumulator(e era.Era) error {
var (
err error
want common.Hash
@@ -257,7 +321,7 @@ func checkAccumulator(e *era.Era) error {
if td, err = e.InitialTD(); err != nil {
return fmt.Errorf("error reading total difficulty: %w", err)
}
- it, err := era.NewIterator(e)
+ it, err := e.Iterator()
if err != nil {
return fmt.Errorf("error making era iterator: %w", err)
}
@@ -290,9 +354,13 @@ func checkAccumulator(e *era.Era) error {
if rr != block.ReceiptHash() {
return fmt.Errorf("receipt root in block %d mismatch: want %s, got %s", block.NumberU64(), block.ReceiptHash(), rr)
}
- hashes = append(hashes, block.Hash())
- td.Add(td, block.Difficulty())
- tds = append(tds, new(big.Int).Set(td))
+ // Only include pre-merge blocks in accumulator calculation.
+ // Post-merge blocks have difficulty == 0.
+ if block.Difficulty().Sign() > 0 {
+ hashes = append(hashes, block.Hash())
+ td.Add(td, block.Difficulty())
+ tds = append(tds, new(big.Int).Set(td))
+ }
}
if it.Error() != nil {
return fmt.Errorf("error reading block %d: %w", it.Number(), it.Error())
diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go
index 44f15c322c..693ec9f4a9 100644
--- a/cmd/evm/internal/t8ntool/execution.go
+++ b/cmd/evm/internal/t8ntool/execution.go
@@ -33,6 +33,7 @@ import (
"github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
+ "github.com/ethereum/go-ethereum/crypto/keccak"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
@@ -40,7 +41,6 @@ import (
"github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/triedb"
"github.com/holiman/uint256"
- "golang.org/x/crypto/sha3"
)
type Prestate struct {
@@ -415,7 +415,7 @@ func MakePreState(db ethdb.Database, accounts types.GenesisAlloc, isBintrie bool
}
func rlpHash(x any) (h common.Hash) {
- hw := sha3.NewLegacyKeccak256()
+ hw := keccak.NewLegacyKeccak256()
rlp.Encode(hw, x)
hw.Sum(h[:0])
return h
diff --git a/cmd/evm/internal/t8ntool/transaction.go b/cmd/evm/internal/t8ntool/transaction.go
index 0f39df0753..4ba7b5f130 100644
--- a/cmd/evm/internal/t8ntool/transaction.go
+++ b/cmd/evm/internal/t8ntool/transaction.go
@@ -115,9 +115,6 @@ func Transaction(ctx *cli.Context) error {
}
var results []result
for it.Next() {
- if err := it.Err(); err != nil {
- return NewError(ErrorIO, err)
- }
var tx types.Transaction
err := rlp.DecodeBytes(it.Value(), &tx)
if err != nil {
@@ -188,6 +185,10 @@ func Transaction(ctx *cli.Context) error {
}
results = append(results, r)
}
+ if err := it.Err(); err != nil {
+ return NewError(ErrorIO, err)
+ }
+
out, err := json.MarshalIndent(results, "", " ")
fmt.Println(string(out))
return err
diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go
index 9d04dd0f1b..1ccb78d622 100644
--- a/cmd/geth/chaincmd.go
+++ b/cmd/geth/chaincmd.go
@@ -20,6 +20,7 @@ import (
"encoding/json"
"errors"
"fmt"
+ "io"
"os"
"path/filepath"
"regexp"
@@ -43,6 +44,8 @@ import (
"github.com/ethereum/go-ethereum/internal/debug"
"github.com/ethereum/go-ethereum/internal/era"
"github.com/ethereum/go-ethereum/internal/era/eradl"
+ "github.com/ethereum/go-ethereum/internal/era/execdb"
+ "github.com/ethereum/go-ethereum/internal/era/onedb"
"github.com/ethereum/go-ethereum/internal/flags"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node"
@@ -153,7 +156,7 @@ be gzipped.`,
Name: "import-history",
Usage: "Import an Era archive",
ArgsUsage: "
",
- Flags: slices.Concat([]cli.Flag{utils.TxLookupLimitFlag, utils.TransactionHistoryFlag}, utils.DatabaseFlags, utils.NetworkFlags),
+ Flags: slices.Concat([]cli.Flag{utils.TxLookupLimitFlag, utils.TransactionHistoryFlag, utils.EraFormatFlag}, utils.DatabaseFlags, utils.NetworkFlags),
Description: `
The import-history command will import blocks and their corresponding receipts
from Era archives.
@@ -164,7 +167,7 @@ from Era archives.
Name: "export-history",
Usage: "Export blockchain history to Era archives",
ArgsUsage: " ",
- Flags: utils.DatabaseFlags,
+ Flags: slices.Concat([]cli.Flag{utils.EraFormatFlag}, utils.DatabaseFlags),
Description: `
The export-history command will export blocks and their corresponding receipts
into Era archives. Eras are typically packaged in steps of 8192 blocks.
@@ -516,15 +519,27 @@ func importHistory(ctx *cli.Context) error {
network = networks[0]
}
- if err := utils.ImportHistory(chain, dir, network); err != nil {
+ var (
+ format = ctx.String(utils.EraFormatFlag.Name)
+ from func(era.ReadAtSeekCloser) (era.Era, error)
+ )
+ switch format {
+ case "era1", "era":
+ from = onedb.From
+ case "erae":
+ from = execdb.From
+ default:
+ return fmt.Errorf("unknown --era.format %q (expected 'era1' or 'erae')", format)
+ }
+ if err := utils.ImportHistory(chain, dir, network, from); err != nil {
return err
}
+
fmt.Printf("Import done in %v\n", time.Since(start))
return nil
}
-// exportHistory exports chain history in Era archives at a specified
-// directory.
+// exportHistory exports chain history in Era archives at a specified directory.
func exportHistory(ctx *cli.Context) error {
if ctx.Args().Len() != 3 {
utils.Fatalf("usage: %s", ctx.Command.ArgsUsage)
@@ -550,10 +565,26 @@ func exportHistory(ctx *cli.Context) error {
if head := chain.CurrentSnapBlock(); uint64(last) > head.Number.Uint64() {
utils.Fatalf("Export error: block number %d larger than head block %d\n", uint64(last), head.Number.Uint64())
}
- err := utils.ExportHistory(chain, dir, uint64(first), uint64(last), uint64(era.MaxEra1Size))
- if err != nil {
+
+ var (
+ format = ctx.String(utils.EraFormatFlag.Name)
+ filename func(network string, epoch int, root common.Hash) string
+ newBuilder func(w io.Writer) era.Builder
+ )
+ switch format {
+ case "era1", "era":
+ newBuilder = func(w io.Writer) era.Builder { return onedb.NewBuilder(w) }
+ filename = func(network string, epoch int, root common.Hash) string { return onedb.Filename(network, epoch, root) }
+ case "erae":
+ newBuilder = func(w io.Writer) era.Builder { return execdb.NewBuilder(w) }
+ filename = func(network string, epoch int, root common.Hash) string { return execdb.Filename(network, epoch, root) }
+ default:
+ return fmt.Errorf("unknown archive format %q (use 'era1' or 'erae')", format)
+ }
+ if err := utils.ExportHistory(chain, dir, uint64(first), uint64(last), newBuilder, filename); err != nil {
utils.Fatalf("Export error: %v\n", err)
}
+
fmt.Printf("Export done in %v\n", time.Since(start))
return nil
}
diff --git a/cmd/geth/config.go b/cmd/geth/config.go
index dcd65a2b78..7943d95d65 100644
--- a/cmd/geth/config.go
+++ b/cmd/geth/config.go
@@ -405,9 +405,9 @@ func applyMetricConfig(ctx *cli.Context, cfg *gethConfig) {
ctx.IsSet(utils.MetricsInfluxDBBucketFlag.Name)
if enableExport && v2FlagIsSet {
- utils.Fatalf("Flags --influxdb.metrics.organization, --influxdb.metrics.token, --influxdb.metrics.bucket are only available for influxdb-v2")
+ utils.Fatalf("Flags --%s, --%s, --%s are only available for influxdb-v2", utils.MetricsInfluxDBOrganizationFlag.Name, utils.MetricsInfluxDBTokenFlag.Name, utils.MetricsInfluxDBBucketFlag.Name)
} else if enableExportV2 && v1FlagIsSet {
- utils.Fatalf("Flags --influxdb.metrics.username, --influxdb.metrics.password are only available for influxdb-v1")
+ utils.Fatalf("Flags --%s, --%s are only available for influxdb-v1", utils.MetricsInfluxDBUsernameFlag.Name, utils.MetricsInfluxDBPasswordFlag.Name)
}
}
}
diff --git a/cmd/utils/cmd.go b/cmd/utils/cmd.go
index 3e337a3d00..995724e6fc 100644
--- a/cmd/utils/cmd.go
+++ b/cmd/utils/cmd.go
@@ -57,6 +57,8 @@ const (
importBatchSize = 2500
)
+type EraFileFormat int
+
// ErrImportInterrupted is returned when the user interrupts the import process.
var ErrImportInterrupted = errors.New("interrupted")
@@ -250,7 +252,7 @@ func readList(filename string) ([]string, error) {
// ImportHistory imports Era1 files containing historical block information,
// starting from genesis. The assumption is held that the provided chain
// segment in Era1 file should all be canonical and verified.
-func ImportHistory(chain *core.BlockChain, dir string, network string) error {
+func ImportHistory(chain *core.BlockChain, dir string, network string, from func(f era.ReadAtSeekCloser) (era.Era, error)) error {
if chain.CurrentSnapBlock().Number.BitLen() != 0 {
return errors.New("history import only supported when starting from genesis")
}
@@ -263,42 +265,49 @@ func ImportHistory(chain *core.BlockChain, dir string, network string) error {
return fmt.Errorf("unable to read checksums.txt: %w", err)
}
if len(checksums) != len(entries) {
- return fmt.Errorf("expected equal number of checksums and entries, have: %d checksums, %d entries", len(checksums), len(entries))
+ return fmt.Errorf("expected equal number of checksums and entries, have: %d checksums, %d entries",
+ len(checksums), len(entries))
}
+
var (
start = time.Now()
reported = time.Now()
imported = 0
h = sha256.New()
- buf = bytes.NewBuffer(nil)
+ scratch = bytes.NewBuffer(nil)
)
- for i, filename := range entries {
+
+ for i, file := range entries {
err := func() error {
- f, err := os.Open(filepath.Join(dir, filename))
+ path := filepath.Join(dir, file)
+
+ // validate against checksum file in directory
+ f, err := os.Open(path)
if err != nil {
- return fmt.Errorf("unable to open era: %w", err)
+ return fmt.Errorf("open %s: %w", path, err)
}
defer f.Close()
-
- // Validate checksum.
if _, err := io.Copy(h, f); err != nil {
- return fmt.Errorf("unable to recalculate checksum: %w", err)
- }
- if have, want := common.BytesToHash(h.Sum(buf.Bytes()[:])).Hex(), checksums[i]; have != want {
- return fmt.Errorf("checksum mismatch: have %s, want %s", have, want)
+ return fmt.Errorf("checksum %s: %w", path, err)
}
+ got := common.BytesToHash(h.Sum(scratch.Bytes()[:])).Hex()
+ want := checksums[i]
h.Reset()
- buf.Reset()
+ scratch.Reset()
+ if got != want {
+ return fmt.Errorf("%s checksum mismatch: have %s want %s", file, got, want)
+ }
// Import all block data from Era1.
- e, err := era.From(f)
+ e, err := from(f)
if err != nil {
return fmt.Errorf("error opening era: %w", err)
}
- it, err := era.NewIterator(e)
+ it, err := e.Iterator()
if err != nil {
- return fmt.Errorf("error making era reader: %w", err)
+ return fmt.Errorf("error creating iterator: %w", err)
}
+
for it.Next() {
block, err := it.Block()
if err != nil {
@@ -311,26 +320,28 @@ func ImportHistory(chain *core.BlockChain, dir string, network string) error {
if err != nil {
return fmt.Errorf("error reading receipts %d: %w", it.Number(), err)
}
- encReceipts := types.EncodeBlockReceiptLists([]types.Receipts{receipts})
- if _, err := chain.InsertReceiptChain([]*types.Block{block}, encReceipts, math.MaxUint64); err != nil {
+ enc := types.EncodeBlockReceiptLists([]types.Receipts{receipts})
+ if _, err := chain.InsertReceiptChain([]*types.Block{block}, enc, math.MaxUint64); err != nil {
return fmt.Errorf("error inserting body %d: %w", it.Number(), err)
}
- imported += 1
+ imported++
- // Give the user some feedback that something is happening.
if time.Since(reported) >= 8*time.Second {
- log.Info("Importing Era files", "head", it.Number(), "imported", imported, "elapsed", common.PrettyDuration(time.Since(start)))
+ log.Info("Importing Era files", "head", it.Number(), "imported", imported,
+ "elapsed", common.PrettyDuration(time.Since(start)))
imported = 0
reported = time.Now()
}
}
+ if err := it.Error(); err != nil {
+ return err
+ }
return nil
}()
if err != nil {
return err
}
}
-
return nil
}
@@ -389,7 +400,6 @@ func ExportAppendChain(blockchain *core.BlockChain, fn string, first uint64, las
return err
}
defer fh.Close()
-
var writer io.Writer = fh
if strings.HasSuffix(fn, ".gz") {
writer = gzip.NewWriter(writer)
@@ -405,7 +415,7 @@ func ExportAppendChain(blockchain *core.BlockChain, fn string, first uint64, las
// ExportHistory exports blockchain history into the specified directory,
// following the Era format.
-func ExportHistory(bc *core.BlockChain, dir string, first, last, step uint64) error {
+func ExportHistory(bc *core.BlockChain, dir string, first, last uint64, newBuilder func(io.Writer) era.Builder, filename func(network string, epoch int, lastBlockHash common.Hash) string) error {
log.Info("Exporting blockchain history", "dir", dir)
if head := bc.CurrentBlock().Number.Uint64(); head < last {
log.Warn("Last block beyond head, setting last = head", "head", head, "last", last)
@@ -418,76 +428,100 @@ func ExportHistory(bc *core.BlockChain, dir string, first, last, step uint64) er
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
return fmt.Errorf("error creating output directory: %w", err)
}
+
var (
start = time.Now()
reported = time.Now()
h = sha256.New()
buf = bytes.NewBuffer(nil)
+ td = new(big.Int)
checksums []string
)
- td := new(big.Int)
- for i := uint64(0); i < first; i++ {
- td.Add(td, bc.GetHeaderByNumber(i).Difficulty)
+
+ // Compute initial TD by accumulating difficulty from genesis to first-1.
+ // This is necessary because TD is no longer stored in the database. Only
+ // compute if a segment of the export is pre-merge.
+ b := bc.GetBlockByNumber(first)
+ if b == nil {
+ return fmt.Errorf("block #%d not found", first)
}
- for i := first; i <= last; i += step {
- err := func() error {
- filename := filepath.Join(dir, era.Filename(network, int(i/step), common.Hash{}))
- f, err := os.Create(filename)
+ if first > 0 && b.Difficulty().Sign() != 0 {
+ log.Info("Computing initial total difficulty", "from", 0, "to", first-1)
+ for i := uint64(0); i < first; i++ {
+ b := bc.GetBlockByNumber(i)
+ if b == nil {
+ return fmt.Errorf("block #%d not found while computing initial TD", i)
+ }
+ td.Add(td, b.Difficulty())
+ }
+ log.Info("Initial total difficulty computed", "td", td)
+ }
+
+ for batch := first; batch <= last; batch += uint64(era.MaxSize) {
+ idx := int(batch / uint64(era.MaxSize))
+ tmpPath := filepath.Join(dir, filename(network, idx, common.Hash{}))
+
+ if err := func() error {
+ f, err := os.Create(tmpPath)
if err != nil {
- return fmt.Errorf("could not create era file: %w", err)
+ return err
}
defer f.Close()
- w := era.NewBuilder(f)
- for j := uint64(0); j < step && j <= last-i; j++ {
- var (
- n = i + j
- block = bc.GetBlockByNumber(n)
- )
+ builder := newBuilder(f)
+
+ for j := uint64(0); j < uint64(era.MaxSize) && batch+j <= last; j++ {
+ n := batch + j
+ block := bc.GetBlockByNumber(n)
if block == nil {
- return fmt.Errorf("export failed on #%d: not found", n)
+ return fmt.Errorf("block #%d not found", n)
}
- receipts := bc.GetReceiptsByHash(block.Hash())
- if receipts == nil {
- return fmt.Errorf("export failed on #%d: receipts not found", n)
+ receipt := bc.GetReceiptsByHash(block.Hash())
+ if receipt == nil {
+ return fmt.Errorf("receipts for #%d missing", n)
}
- td.Add(td, block.Difficulty())
- if err := w.Add(block, receipts, new(big.Int).Set(td)); err != nil {
+
+ // For pre-merge blocks, pass accumulated TD.
+ // For post-merge blocks (difficulty == 0), pass nil TD.
+ var blockTD *big.Int
+ if block.Difficulty().Sign() != 0 {
+ td.Add(td, block.Difficulty())
+ blockTD = new(big.Int).Set(td)
+ }
+
+ if err := builder.Add(block, receipt, blockTD); err != nil {
return err
}
}
- root, err := w.Finalize()
+ id, err := builder.Finalize()
if err != nil {
- return fmt.Errorf("export failed to finalize %d: %w", step/i, err)
+ return err
}
- // Set correct filename with root.
- os.Rename(filename, filepath.Join(dir, era.Filename(network, int(i/step), root)))
-
- // Compute checksum of entire Era1.
if _, err := f.Seek(0, io.SeekStart); err != nil {
return err
}
- if _, err := io.Copy(h, f); err != nil {
- return fmt.Errorf("unable to calculate checksum: %w", err)
- }
- checksums = append(checksums, common.BytesToHash(h.Sum(buf.Bytes()[:])).Hex())
h.Reset()
buf.Reset()
- return nil
- }()
- if err != nil {
+ if _, err := io.Copy(h, f); err != nil {
+ return err
+ }
+ checksums = append(checksums, common.BytesToHash(h.Sum(buf.Bytes()[:])).Hex())
+
+ // Close before rename. It's required on Windows.
+ f.Close()
+ final := filepath.Join(dir, filename(network, idx, id))
+ return os.Rename(tmpPath, final)
+ }(); err != nil {
return err
}
+
if time.Since(reported) >= 8*time.Second {
- log.Info("Exporting blocks", "exported", i, "elapsed", common.PrettyDuration(time.Since(start)))
+ log.Info("export progress", "exported", batch, "elapsed", common.PrettyDuration(time.Since(start)))
reported = time.Now()
}
}
- os.WriteFile(filepath.Join(dir, "checksums.txt"), []byte(strings.Join(checksums, "\n")), os.ModePerm)
-
- log.Info("Exported blockchain to", "dir", dir)
-
+ _ = os.WriteFile(filepath.Join(dir, "checksums.txt"), []byte(strings.Join(checksums, "\n")), os.ModePerm)
return nil
}
diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go
index 4fd58c0518..3cb365b108 100644
--- a/cmd/utils/flags.go
+++ b/cmd/utils/flags.go
@@ -1086,6 +1086,11 @@ Please note that --` + MetricsHTTPFlag.Name + ` must be set to start the server.
Value: 1.0,
Category: flags.APICategory,
}
+ // Era flags are a group of flags related to the era archive format.
+ EraFormatFlag = &cli.StringFlag{
+ Name: "era.format",
+ Usage: "Archive format: 'era1' or 'erae'",
+ }
)
var (
diff --git a/cmd/utils/history_test.go b/cmd/utils/history_test.go
index 994756eda5..6631946129 100644
--- a/cmd/utils/history_test.go
+++ b/cmd/utils/history_test.go
@@ -33,6 +33,8 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/internal/era"
+ "github.com/ethereum/go-ethereum/internal/era/execdb"
+ "github.com/ethereum/go-ethereum/internal/era/onedb"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/triedb"
@@ -44,136 +46,148 @@ var (
)
func TestHistoryImportAndExport(t *testing.T) {
- var (
- key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
- address = crypto.PubkeyToAddress(key.PublicKey)
- genesis = &core.Genesis{
- Config: params.TestChainConfig,
- Alloc: types.GenesisAlloc{address: {Balance: big.NewInt(1000000000000000000)}},
- }
- signer = types.LatestSigner(genesis.Config)
- )
-
- // Generate chain.
- db, blocks, _ := core.GenerateChainWithGenesis(genesis, ethash.NewFaker(), int(count), func(i int, g *core.BlockGen) {
- if i == 0 {
- return
- }
- tx, err := types.SignNewTx(key, signer, &types.DynamicFeeTx{
- ChainID: genesis.Config.ChainID,
- Nonce: uint64(i - 1),
- GasTipCap: common.Big0,
- GasFeeCap: g.PrevBlock(0).BaseFee(),
- Gas: 50000,
- To: &common.Address{0xaa},
- Value: big.NewInt(int64(i)),
- Data: nil,
- AccessList: nil,
- })
- if err != nil {
- t.Fatalf("error creating tx: %v", err)
- }
- g.AddTx(tx)
- })
-
- // Initialize BlockChain.
- chain, err := core.NewBlockChain(db, genesis, ethash.NewFaker(), nil)
- if err != nil {
- t.Fatalf("unable to initialize chain: %v", err)
- }
- if _, err := chain.InsertChain(blocks); err != nil {
- t.Fatalf("error inserting chain: %v", err)
- }
-
- // Make temp directory for era files.
- dir := t.TempDir()
-
- // Export history to temp directory.
- if err := ExportHistory(chain, dir, 0, count, step); err != nil {
- t.Fatalf("error exporting history: %v", err)
- }
-
- // Read checksums.
- b, err := os.ReadFile(filepath.Join(dir, "checksums.txt"))
- if err != nil {
- t.Fatalf("failed to read checksums: %v", err)
- }
- checksums := strings.Split(string(b), "\n")
-
- // Verify each Era.
- entries, _ := era.ReadDir(dir, "mainnet")
- for i, filename := range entries {
- func() {
- f, err := os.Open(filepath.Join(dir, filename))
- if err != nil {
- t.Fatalf("error opening era file: %v", err)
- }
+ for _, tt := range []struct {
+ name string
+ builder func(io.Writer) era.Builder
+ filename func(network string, epoch int, root common.Hash) string
+ from func(f era.ReadAtSeekCloser) (era.Era, error)
+ }{
+ {"era1", onedb.NewBuilder, onedb.Filename, onedb.From},
+ {"erae", execdb.NewBuilder, execdb.Filename, execdb.From},
+ } {
+ t.Run(tt.name, func(t *testing.T) {
var (
- h = sha256.New()
- buf = bytes.NewBuffer(nil)
+ key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
+ address = crypto.PubkeyToAddress(key.PublicKey)
+ genesis = &core.Genesis{
+ Config: params.TestChainConfig,
+ Alloc: types.GenesisAlloc{address: {Balance: big.NewInt(1000000000000000000)}},
+ }
+ signer = types.LatestSigner(genesis.Config)
)
- if _, err := io.Copy(h, f); err != nil {
- t.Fatalf("unable to recalculate checksum: %v", err)
- }
- if got, want := common.BytesToHash(h.Sum(buf.Bytes()[:])).Hex(), checksums[i]; got != want {
- t.Fatalf("checksum %d does not match: got %s, want %s", i, got, want)
- }
- e, err := era.From(f)
- if err != nil {
- t.Fatalf("error opening era: %v", err)
- }
- defer e.Close()
- it, err := era.NewIterator(e)
- if err != nil {
- t.Fatalf("error making era reader: %v", err)
- }
- for j := 0; it.Next(); j++ {
- n := i*int(step) + j
- if it.Error() != nil {
- t.Fatalf("error reading block entry %d: %v", n, it.Error())
+
+ // Generate chain.
+ db, blocks, _ := core.GenerateChainWithGenesis(genesis, ethash.NewFaker(), int(count), func(i int, g *core.BlockGen) {
+ if i == 0 {
+ return
}
- block, receipts, err := it.BlockAndReceipts()
+ tx, err := types.SignNewTx(key, signer, &types.DynamicFeeTx{
+ ChainID: genesis.Config.ChainID,
+ Nonce: uint64(i - 1),
+ GasTipCap: common.Big0,
+ GasFeeCap: g.PrevBlock(0).BaseFee(),
+ Gas: 50000,
+ To: &common.Address{0xaa},
+ Value: big.NewInt(int64(i)),
+ Data: nil,
+ AccessList: nil,
+ })
if err != nil {
- t.Fatalf("error reading block entry %d: %v", n, err)
- }
- want := chain.GetBlockByNumber(uint64(n))
- if want, got := uint64(n), block.NumberU64(); want != got {
- t.Fatalf("blocks out of order: want %d, got %d", want, got)
- }
- if want.Hash() != block.Hash() {
- t.Fatalf("block hash mismatch %d: want %s, got %s", n, want.Hash().Hex(), block.Hash().Hex())
- }
- if got := types.DeriveSha(block.Transactions(), trie.NewStackTrie(nil)); got != want.TxHash() {
- t.Fatalf("tx hash %d mismatch: want %s, got %s", n, want.TxHash(), got)
- }
- if got := types.CalcUncleHash(block.Uncles()); got != want.UncleHash() {
- t.Fatalf("uncle hash %d mismatch: want %s, got %s", n, want.UncleHash(), got)
- }
- if got := types.DeriveSha(receipts, trie.NewStackTrie(nil)); got != want.ReceiptHash() {
- t.Fatalf("receipt root %d mismatch: want %s, got %s", n, want.ReceiptHash(), got)
+ t.Fatalf("error creating tx: %v", err)
}
+ g.AddTx(tx)
+ })
+
+ // Initialize BlockChain.
+ chain, err := core.NewBlockChain(db, genesis, ethash.NewFaker(), nil)
+ if err != nil {
+ t.Fatalf("unable to initialize chain: %v", err)
+ }
+ if _, err := chain.InsertChain(blocks); err != nil {
+ t.Fatalf("error inserting chain: %v", err)
}
- }()
- }
- // Now import Era.
- db2, err := rawdb.Open(rawdb.NewMemoryDatabase(), rawdb.OpenOptions{})
- if err != nil {
- panic(err)
- }
- t.Cleanup(func() {
- db2.Close()
- })
+ // Make temp directory for era files.
+ dir := t.TempDir()
- genesis.MustCommit(db2, triedb.NewDatabase(db2, triedb.HashDefaults))
- imported, err := core.NewBlockChain(db2, genesis, ethash.NewFaker(), nil)
- if err != nil {
- t.Fatalf("unable to initialize chain: %v", err)
- }
- if err := ImportHistory(imported, dir, "mainnet"); err != nil {
- t.Fatalf("failed to import chain: %v", err)
- }
- if have, want := imported.CurrentHeader(), chain.CurrentHeader(); have.Hash() != want.Hash() {
- t.Fatalf("imported chain does not match expected, have (%d, %s) want (%d, %s)", have.Number, have.Hash(), want.Number, want.Hash())
+ // Export history to temp directory.
+ if err := ExportHistory(chain, dir, 0, count, tt.builder, tt.filename); err != nil {
+ t.Fatalf("error exporting history: %v", err)
+ }
+
+ // Read checksums.
+ b, err := os.ReadFile(filepath.Join(dir, "checksums.txt"))
+ if err != nil {
+ t.Fatalf("failed to read checksums: %v", err)
+ }
+ checksums := strings.Split(string(b), "\n")
+
+ // Verify each Era.
+ entries, _ := era.ReadDir(dir, "mainnet")
+ for i, filename := range entries {
+ func() {
+ f, err := os.Open(filepath.Join(dir, filename))
+ if err != nil {
+ t.Fatalf("error opening era file: %v", err)
+ }
+ var (
+ h = sha256.New()
+ buf = bytes.NewBuffer(nil)
+ )
+ if _, err := io.Copy(h, f); err != nil {
+ t.Fatalf("unable to recalculate checksum: %v", err)
+ }
+ if got, want := common.BytesToHash(h.Sum(buf.Bytes()[:])).Hex(), checksums[i]; got != want {
+ t.Fatalf("checksum %d does not match: got %s, want %s", i, got, want)
+ }
+ e, err := tt.from(f)
+ if err != nil {
+ t.Fatalf("error opening era: %v", err)
+ }
+ defer e.Close()
+ it, err := e.Iterator()
+ if err != nil {
+ t.Fatalf("error making era reader: %v", err)
+ }
+ for j := 0; it.Next(); j++ {
+ n := i*int(step) + j
+ if it.Error() != nil {
+ t.Fatalf("error reading block entry %d: %v", n, it.Error())
+ }
+ block, receipts, err := it.BlockAndReceipts()
+ if err != nil {
+ t.Fatalf("error reading block entry %d: %v", n, err)
+ }
+ want := chain.GetBlockByNumber(uint64(n))
+ if want, got := uint64(n), block.NumberU64(); want != got {
+ t.Fatalf("blocks out of order: want %d, got %d", want, got)
+ }
+ if want.Hash() != block.Hash() {
+ t.Fatalf("block hash mismatch %d: want %s, got %s", n, want.Hash().Hex(), block.Hash().Hex())
+ }
+ if got := types.DeriveSha(block.Transactions(), trie.NewStackTrie(nil)); got != want.TxHash() {
+ t.Fatalf("tx hash %d mismatch: want %s, got %s", n, want.TxHash(), got)
+ }
+ if got := types.CalcUncleHash(block.Uncles()); got != want.UncleHash() {
+ t.Fatalf("uncle hash %d mismatch: want %s, got %s", n, want.UncleHash(), got)
+ }
+ if got := types.DeriveSha(receipts, trie.NewStackTrie(nil)); got != want.ReceiptHash() {
+ t.Fatalf("receipt root %d mismatch: want %s, got %s", n, want.ReceiptHash(), got)
+ }
+ }
+ }()
+ }
+
+ // Now import Era.
+ db2, err := rawdb.Open(rawdb.NewMemoryDatabase(), rawdb.OpenOptions{})
+ if err != nil {
+ panic(err)
+ }
+ t.Cleanup(func() {
+ db2.Close()
+ })
+
+ genesis.MustCommit(db2, triedb.NewDatabase(db2, triedb.HashDefaults))
+ imported, err := core.NewBlockChain(db2, genesis, ethash.NewFaker(), nil)
+ if err != nil {
+ t.Fatalf("unable to initialize chain: %v", err)
+ }
+ if err := ImportHistory(imported, dir, "mainnet", tt.from); err != nil {
+ t.Fatalf("failed to import chain: %v", err)
+ }
+ if have, want := imported.CurrentHeader(), chain.CurrentHeader(); have.Hash() != want.Hash() {
+ t.Fatalf("imported chain does not match expected, have (%d, %s) want (%d, %s)", have.Number, have.Hash(), want.Number, want.Hash())
+ }
+ })
}
}
diff --git a/common/types.go b/common/types.go
index a96d6c7c83..308b8ed879 100644
--- a/common/types.go
+++ b/common/types.go
@@ -30,7 +30,7 @@ import (
"strings"
"github.com/ethereum/go-ethereum/common/hexutil"
- "golang.org/x/crypto/sha3"
+ "github.com/ethereum/go-ethereum/crypto/keccak"
)
// Lengths of hashes and addresses in bytes.
@@ -271,7 +271,7 @@ func (a *Address) checksumHex() []byte {
buf := a.hex()
// compute checksum
- sha := sha3.NewLegacyKeccak256()
+ sha := keccak.NewLegacyKeccak256()
sha.Write(buf[2:])
hash := sha.Sum(nil)
for i := 2; i < len(buf); i++ {
diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go
index a6f02c8c2b..87cd407a71 100644
--- a/consensus/clique/clique.go
+++ b/consensus/clique/clique.go
@@ -37,12 +37,12 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/crypto/keccak"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
- "golang.org/x/crypto/sha3"
)
const (
@@ -642,7 +642,7 @@ func (c *Clique) Close() error {
// SealHash returns the hash of a block prior to it being sealed.
func SealHash(header *types.Header) (hash common.Hash) {
- hasher := sha3.NewLegacyKeccak256()
+ hasher := keccak.NewLegacyKeccak256()
encodeSigHeader(hasher, header)
hasher.(crypto.KeccakState).Read(hash[:])
return hash
diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go
index 376cbac8c0..f90001fc1a 100644
--- a/consensus/ethash/consensus.go
+++ b/consensus/ethash/consensus.go
@@ -31,11 +31,11 @@ import (
"github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
+ "github.com/ethereum/go-ethereum/crypto/keccak"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
"github.com/holiman/uint256"
- "golang.org/x/crypto/sha3"
)
// Ethash proof-of-work protocol constants.
@@ -527,7 +527,7 @@ func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea
// SealHash returns the hash of a block prior to it being sealed.
func (ethash *Ethash) SealHash(header *types.Header) (hash common.Hash) {
- hasher := sha3.NewLegacyKeccak256()
+ hasher := keccak.NewLegacyKeccak256()
enc := []interface{}{
header.ParentHash,
diff --git a/consensus/misc/eip4844/eip4844.go b/consensus/misc/eip4844/eip4844.go
index 47b54d2e85..2ebf4f7155 100644
--- a/consensus/misc/eip4844/eip4844.go
+++ b/consensus/misc/eip4844/eip4844.go
@@ -53,9 +53,9 @@ func (bc *BlobConfig) blobPrice(excessBlobGas uint64) *big.Int {
return new(big.Int).Mul(f, big.NewInt(params.BlobTxBlobGasPerBlob))
}
-func latestBlobConfig(cfg *params.ChainConfig, time uint64) *BlobConfig {
+func latestBlobConfig(cfg *params.ChainConfig, time uint64) (BlobConfig, error) {
if cfg.BlobScheduleConfig == nil {
- return nil
+ return BlobConfig{}, errors.New("no blob config")
}
var (
london = cfg.LondonBlock
@@ -80,14 +80,14 @@ func latestBlobConfig(cfg *params.ChainConfig, time uint64) *BlobConfig {
case cfg.IsCancun(london, time) && s.Cancun != nil:
bc = s.Cancun
default:
- return nil
+ return BlobConfig{}, errors.New("no blob config")
}
- return &BlobConfig{
+ return BlobConfig{
Target: bc.Target,
Max: bc.Max,
UpdateFraction: bc.UpdateFraction,
- }
+ }, nil
}
// VerifyEIP4844Header verifies the presence of the excessBlobGas field and that
@@ -98,8 +98,8 @@ func VerifyEIP4844Header(config *params.ChainConfig, parent, header *types.Heade
panic("bad header pair")
}
- bcfg := latestBlobConfig(config, header.Time)
- if bcfg == nil {
+ bcfg, err := latestBlobConfig(config, header.Time)
+ if err != nil {
panic("called before EIP-4844 is active")
}
@@ -130,11 +130,14 @@ func VerifyEIP4844Header(config *params.ChainConfig, parent, header *types.Heade
// blobs on top of the excess blob gas.
func CalcExcessBlobGas(config *params.ChainConfig, parent *types.Header, headTimestamp uint64) uint64 {
isOsaka := config.IsOsaka(config.LondonBlock, headTimestamp)
- bcfg := latestBlobConfig(config, headTimestamp)
+ bcfg, err := latestBlobConfig(config, headTimestamp)
+ if err != nil {
+ panic("calculating excess blob gas on nil blob config")
+ }
return calcExcessBlobGas(isOsaka, bcfg, parent)
}
-func calcExcessBlobGas(isOsaka bool, bcfg *BlobConfig, parent *types.Header) uint64 {
+func calcExcessBlobGas(isOsaka bool, bcfg BlobConfig, parent *types.Header) uint64 {
var parentExcessBlobGas, parentBlobGasUsed uint64
if parent.ExcessBlobGas != nil {
parentExcessBlobGas = *parent.ExcessBlobGas
@@ -169,8 +172,8 @@ func calcExcessBlobGas(isOsaka bool, bcfg *BlobConfig, parent *types.Header) uin
// CalcBlobFee calculates the blobfee from the header's excess blob gas field.
func CalcBlobFee(config *params.ChainConfig, header *types.Header) *big.Int {
- blobConfig := latestBlobConfig(config, header.Time)
- if blobConfig == nil {
+ blobConfig, err := latestBlobConfig(config, header.Time)
+ if err != nil {
panic("calculating blob fee on unsupported fork")
}
return blobConfig.blobBaseFee(*header.ExcessBlobGas)
@@ -178,8 +181,8 @@ func CalcBlobFee(config *params.ChainConfig, header *types.Header) *big.Int {
// MaxBlobsPerBlock returns the max blobs per block for a block at the given timestamp.
func MaxBlobsPerBlock(cfg *params.ChainConfig, time uint64) int {
- blobConfig := latestBlobConfig(cfg, time)
- if blobConfig == nil {
+ blobConfig, err := latestBlobConfig(cfg, time)
+ if err != nil {
return 0
}
return blobConfig.Max
@@ -193,8 +196,8 @@ func MaxBlobGasPerBlock(cfg *params.ChainConfig, time uint64) uint64 {
// LatestMaxBlobsPerBlock returns the latest max blobs per block defined by the
// configuration, regardless of the currently active fork.
func LatestMaxBlobsPerBlock(cfg *params.ChainConfig) int {
- bcfg := latestBlobConfig(cfg, math.MaxUint64)
- if bcfg == nil {
+ bcfg, err := latestBlobConfig(cfg, math.MaxUint64)
+ if err != nil {
return 0
}
return bcfg.Max
@@ -202,8 +205,8 @@ func LatestMaxBlobsPerBlock(cfg *params.ChainConfig) int {
// TargetBlobsPerBlock returns the target blobs per block for a block at the given timestamp.
func TargetBlobsPerBlock(cfg *params.ChainConfig, time uint64) int {
- blobConfig := latestBlobConfig(cfg, time)
- if blobConfig == nil {
+ blobConfig, err := latestBlobConfig(cfg, time)
+ if err != nil {
return 0
}
return blobConfig.Target
diff --git a/core/evm.go b/core/evm.go
index 18d940fdd2..2e7e138b59 100644
--- a/core/evm.go
+++ b/core/evm.go
@@ -80,12 +80,9 @@ func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common
func NewEVMTxContext(msg *Message) vm.TxContext {
ctx := vm.TxContext{
Origin: msg.From,
- GasPrice: new(big.Int).Set(msg.GasPrice),
+ GasPrice: uint256.MustFromBig(msg.GasPrice),
BlobHashes: msg.BlobHashes,
}
- if msg.BlobGasFeeCap != nil {
- ctx.BlobFeeCap = new(big.Int).Set(msg.BlobGasFeeCap)
- }
return ctx
}
diff --git a/core/rawdb/accessors_chain.go b/core/rawdb/accessors_chain.go
index 6ae64fb2fd..14308dd698 100644
--- a/core/rawdb/accessors_chain.go
+++ b/core/rawdb/accessors_chain.go
@@ -424,7 +424,13 @@ func WriteBodyRLP(db ethdb.KeyValueWriter, hash common.Hash, number uint64, rlp
// HasBody verifies the existence of a block body corresponding to the hash.
func HasBody(db ethdb.Reader, hash common.Hash, number uint64) bool {
if isCanon(db, number, hash) {
- return true
+ // Block is in ancient store, but bodies can be pruned.
+ // Check if the block number is above the pruning tail.
+ tail, _ := db.Tail()
+ if number >= tail {
+ return true
+ }
+ return false
}
if has, err := db.Has(blockBodyKey(number, hash)); !has || err != nil {
return false
@@ -466,7 +472,13 @@ func DeleteBody(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
// to a block.
func HasReceipts(db ethdb.Reader, hash common.Hash, number uint64) bool {
if isCanon(db, number, hash) {
- return true
+ // Block is in ancient store, but receipts can be pruned.
+ // Check if the block number is above the pruning tail.
+ tail, _ := db.Tail()
+ if number >= tail {
+ return true
+ }
+ return false
}
if has, err := db.Has(blockReceiptsKey(number, hash)); !has || err != nil {
return false
diff --git a/core/rawdb/accessors_chain_test.go b/core/rawdb/accessors_chain_test.go
index 02d51f4dd2..280fc21e8f 100644
--- a/core/rawdb/accessors_chain_test.go
+++ b/core/rawdb/accessors_chain_test.go
@@ -28,9 +28,9 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/crypto/keccak"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
- "golang.org/x/crypto/sha3"
)
// Tests block header storage and retrieval operations.
@@ -52,10 +52,7 @@ func TestHeaderStorage(t *testing.T) {
if entry := ReadHeaderRLP(db, header.Hash(), header.Number.Uint64()); entry == nil {
t.Fatalf("Stored header RLP not found")
} else {
- hasher := sha3.NewLegacyKeccak256()
- hasher.Write(entry)
-
- if hash := common.BytesToHash(hasher.Sum(nil)); hash != header.Hash() {
+ if hash := crypto.Keccak256Hash(entry); hash != header.Hash() {
t.Fatalf("Retrieved RLP header mismatch: have %v, want %v", entry, header)
}
}
@@ -72,8 +69,7 @@ func TestBodyStorage(t *testing.T) {
// Create a test body to move around the database and make sure it's really new
body := &types.Body{Uncles: []*types.Header{{Extra: []byte("test header")}}}
-
- hasher := sha3.NewLegacyKeccak256()
+ hasher := keccak.NewLegacyKeccak256()
rlp.Encode(hasher, body)
hash := common.BytesToHash(hasher.Sum(nil))
@@ -90,10 +86,7 @@ func TestBodyStorage(t *testing.T) {
if entry := ReadBodyRLP(db, hash, 0); entry == nil {
t.Fatalf("Stored body RLP not found")
} else {
- hasher := sha3.NewLegacyKeccak256()
- hasher.Write(entry)
-
- if calc := common.BytesToHash(hasher.Sum(nil)); calc != hash {
+ if calc := crypto.Keccak256Hash(entry); calc != hash {
t.Fatalf("Retrieved RLP body mismatch: have %v, want %v", entry, body)
}
}
diff --git a/core/rawdb/accessors_indexes.go b/core/rawdb/accessors_indexes.go
index 10eb454015..8c8c3ec9bb 100644
--- a/core/rawdb/accessors_indexes.go
+++ b/core/rawdb/accessors_indexes.go
@@ -147,9 +147,6 @@ func findTxInBlockBody(blockbody rlp.RawValue, target common.Hash) (*types.Trans
}
txIndex := uint64(0)
for iter.Next() {
- if iter.Err() != nil {
- return nil, 0, iter.Err()
- }
// The preimage for the hash calculation of legacy transactions
// is just their RLP encoding. For typed (EIP-2718) transactions,
// which are encoded as byte arrays, the preimage is the content of
@@ -171,6 +168,9 @@ func findTxInBlockBody(blockbody rlp.RawValue, target common.Hash) (*types.Trans
}
txIndex++
}
+ if iter.Err() != nil {
+ return nil, 0, iter.Err()
+ }
return nil, 0, errors.New("transaction not found")
}
diff --git a/core/rawdb/ancient_utils.go b/core/rawdb/ancient_utils.go
index 0ed974b745..8c6b18df08 100644
--- a/core/rawdb/ancient_utils.go
+++ b/core/rawdb/ancient_utils.go
@@ -166,6 +166,7 @@ func InspectFreezerTable(ancient string, freezerName string, tableName string, s
if err != nil {
return err
}
+ defer table.Close()
table.dumpIndexStdout(start, end)
return nil
}
diff --git a/core/rawdb/chain_iterator.go b/core/rawdb/chain_iterator.go
index f846d4d16c..afa1aa7a4c 100644
--- a/core/rawdb/chain_iterator.go
+++ b/core/rawdb/chain_iterator.go
@@ -357,9 +357,9 @@ func unindexTransactions(db ethdb.Database, from uint64, to uint64, interrupt ch
}
select {
case <-interrupt:
- logger("Transaction unindexing interrupted", "blocks", blocks, "txs", txs, "tail", to, "elapsed", common.PrettyDuration(time.Since(start)))
+ logger("Transaction unindexing interrupted", "blocks", blocks, "txs", txs, "tail", nextNum, "elapsed", common.PrettyDuration(time.Since(start)))
default:
- logger("Unindexed transactions", "blocks", blocks, "txs", txs, "tail", to, "elapsed", common.PrettyDuration(time.Since(start)))
+ logger("Unindexed transactions", "blocks", blocks, "txs", txs, "tail", nextNum, "elapsed", common.PrettyDuration(time.Since(start)))
}
}
diff --git a/core/rawdb/eradb/eradb.go b/core/rawdb/eradb/eradb.go
index a552b94da9..d715c824ed 100644
--- a/core/rawdb/eradb/eradb.go
+++ b/core/rawdb/eradb/eradb.go
@@ -27,6 +27,7 @@ import (
"github.com/ethereum/go-ethereum/common/lru"
"github.com/ethereum/go-ethereum/internal/era"
+ "github.com/ethereum/go-ethereum/internal/era/onedb"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rlp"
)
@@ -51,7 +52,7 @@ type Store struct {
type fileCacheEntry struct {
refcount int // reference count. This is protected by Store.mu!
opened chan struct{} // signals opening of file has completed
- file *era.Era // the file
+ file *onedb.Era // the file
err error // error from opening the file
}
@@ -102,7 +103,7 @@ func (db *Store) Close() {
// GetRawBody returns the raw body for a given block number.
func (db *Store) GetRawBody(number uint64) ([]byte, error) {
- epoch := number / uint64(era.MaxEra1Size)
+ epoch := number / uint64(era.MaxSize)
entry := db.getEraByEpoch(epoch)
if entry.err != nil {
if errors.Is(entry.err, fs.ErrNotExist) {
@@ -117,7 +118,7 @@ func (db *Store) GetRawBody(number uint64) ([]byte, error) {
// GetRawReceipts returns the raw receipts for a given block number.
func (db *Store) GetRawReceipts(number uint64) ([]byte, error) {
- epoch := number / uint64(era.MaxEra1Size)
+ epoch := number / uint64(era.MaxSize)
entry := db.getEraByEpoch(epoch)
if entry.err != nil {
if errors.Is(entry.err, fs.ErrNotExist) {
@@ -249,7 +250,7 @@ func (db *Store) getCacheEntry(epoch uint64) (stat fileCacheStatus, entry *fileC
}
// fileOpened is called after an era file has been successfully opened.
-func (db *Store) fileOpened(epoch uint64, entry *fileCacheEntry, file *era.Era) {
+func (db *Store) fileOpened(epoch uint64, entry *fileCacheEntry, file *onedb.Era) {
db.mu.Lock()
defer db.mu.Unlock()
@@ -282,7 +283,7 @@ func (db *Store) fileFailedToOpen(epoch uint64, entry *fileCacheEntry, err error
entry.err = err
}
-func (db *Store) openEraFile(epoch uint64) (*era.Era, error) {
+func (db *Store) openEraFile(epoch uint64) (*onedb.Era, error) {
// File name scheme is --.
glob := fmt.Sprintf("*-%05d-*.era1", epoch)
matches, err := filepath.Glob(filepath.Join(db.datadir, glob))
@@ -297,17 +298,17 @@ func (db *Store) openEraFile(epoch uint64) (*era.Era, error) {
}
filename := matches[0]
- e, err := era.Open(filename)
+ e, err := onedb.Open(filename)
if err != nil {
return nil, err
}
// Sanity-check start block.
- if e.Start()%uint64(era.MaxEra1Size) != 0 {
+ if e.Start()%uint64(era.MaxSize) != 0 {
e.Close()
- return nil, fmt.Errorf("pre-merge era1 file has invalid boundary. %d %% %d != 0", e.Start(), era.MaxEra1Size)
+ return nil, fmt.Errorf("pre-merge era1 file has invalid boundary. %d %% %d != 0", e.Start(), era.MaxSize)
}
log.Debug("Opened era1 file", "epoch", epoch)
- return e, nil
+ return e.(*onedb.Era), nil
}
// doneWithFile signals that the caller has finished using a file.
diff --git a/core/rawdb/freezer_resettable.go b/core/rawdb/freezer_resettable.go
index f531e668c3..5494a648c8 100644
--- a/core/rawdb/freezer_resettable.go
+++ b/core/rawdb/freezer_resettable.go
@@ -221,13 +221,12 @@ func cleanup(path string) error {
if err != nil {
return err
}
+ defer dir.Close()
+
names, err := dir.Readdirnames(0)
if err != nil {
return err
}
- if cerr := dir.Close(); cerr != nil {
- return cerr
- }
for _, name := range names {
if name == filepath.Base(path)+tmpSuffix {
log.Info("Removed leftover freezer directory", "name", name)
diff --git a/core/rlp_test.go b/core/rlp_test.go
index 69efa82551..f3655bf533 100644
--- a/core/rlp_test.go
+++ b/core/rlp_test.go
@@ -25,9 +25,9 @@ import (
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/crypto/keccak"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
- "golang.org/x/crypto/sha3"
)
func getBlock(transactions int, uncles int, dataSize int) *types.Block {
@@ -147,7 +147,7 @@ func BenchmarkHashing(b *testing.B) {
blockRlp, _ = rlp.EncodeToBytes(block)
}
var got common.Hash
- var hasher = sha3.NewLegacyKeccak256()
+ var hasher = keccak.NewLegacyKeccak256()
b.Run("iteratorhashing", func(b *testing.B) {
for b.Loop() {
var hash common.Hash
diff --git a/core/state/journal.go b/core/state/journal.go
index f3f976f24f..a79bd7331a 100644
--- a/core/state/journal.go
+++ b/core/state/journal.go
@@ -39,7 +39,8 @@ type journalEntry interface {
revert(*StateDB)
// dirtied returns the Ethereum address modified by this journal entry.
- dirtied() *common.Address
+ // indicates false if no address was changed.
+ dirtied() (common.Address, bool)
// copy returns a deep-copied journal entry.
copy() journalEntry
@@ -100,8 +101,8 @@ func (j *journal) revertToSnapshot(revid int, s *StateDB) {
// append inserts a new modification entry to the end of the change journal.
func (j *journal) append(entry journalEntry) {
j.entries = append(j.entries, entry)
- if addr := entry.dirtied(); addr != nil {
- j.dirties[*addr]++
+ if addr, dirty := entry.dirtied(); dirty {
+ j.dirties[addr]++
}
}
@@ -113,9 +114,9 @@ func (j *journal) revert(statedb *StateDB, snapshot int) {
j.entries[i].revert(statedb)
// Drop any dirty tracking induced by the change
- if addr := j.entries[i].dirtied(); addr != nil {
- if j.dirties[*addr]--; j.dirties[*addr] == 0 {
- delete(j.dirties, *addr)
+ if addr, dirty := j.entries[i].dirtied(); dirty {
+ if j.dirties[addr]--; j.dirties[addr] == 0 {
+ delete(j.dirties, addr)
}
}
}
@@ -294,8 +295,8 @@ func (ch createObjectChange) revert(s *StateDB) {
delete(s.stateObjects, ch.account)
}
-func (ch createObjectChange) dirtied() *common.Address {
- return &ch.account
+func (ch createObjectChange) dirtied() (common.Address, bool) {
+ return ch.account, true
}
func (ch createObjectChange) copy() journalEntry {
@@ -308,8 +309,8 @@ func (ch createContractChange) revert(s *StateDB) {
s.getStateObject(ch.account).newContract = false
}
-func (ch createContractChange) dirtied() *common.Address {
- return nil
+func (ch createContractChange) dirtied() (common.Address, bool) {
+ return common.Address{}, false
}
func (ch createContractChange) copy() journalEntry {
@@ -325,8 +326,8 @@ func (ch selfDestructChange) revert(s *StateDB) {
}
}
-func (ch selfDestructChange) dirtied() *common.Address {
- return &ch.account
+func (ch selfDestructChange) dirtied() (common.Address, bool) {
+ return ch.account, true
}
func (ch selfDestructChange) copy() journalEntry {
@@ -340,8 +341,8 @@ var ripemd = common.HexToAddress("0000000000000000000000000000000000000003")
func (ch touchChange) revert(s *StateDB) {
}
-func (ch touchChange) dirtied() *common.Address {
- return &ch.account
+func (ch touchChange) dirtied() (common.Address, bool) {
+ return ch.account, true
}
func (ch touchChange) copy() journalEntry {
@@ -354,8 +355,8 @@ func (ch balanceChange) revert(s *StateDB) {
s.getStateObject(ch.account).setBalance(ch.prev)
}
-func (ch balanceChange) dirtied() *common.Address {
- return &ch.account
+func (ch balanceChange) dirtied() (common.Address, bool) {
+ return ch.account, true
}
func (ch balanceChange) copy() journalEntry {
@@ -369,8 +370,8 @@ func (ch nonceChange) revert(s *StateDB) {
s.getStateObject(ch.account).setNonce(ch.prev)
}
-func (ch nonceChange) dirtied() *common.Address {
- return &ch.account
+func (ch nonceChange) dirtied() (common.Address, bool) {
+ return ch.account, true
}
func (ch nonceChange) copy() journalEntry {
@@ -384,8 +385,8 @@ func (ch codeChange) revert(s *StateDB) {
s.getStateObject(ch.account).setCode(crypto.Keccak256Hash(ch.prevCode), ch.prevCode)
}
-func (ch codeChange) dirtied() *common.Address {
- return &ch.account
+func (ch codeChange) dirtied() (common.Address, bool) {
+ return ch.account, true
}
func (ch codeChange) copy() journalEntry {
@@ -399,8 +400,8 @@ func (ch storageChange) revert(s *StateDB) {
s.getStateObject(ch.account).setState(ch.key, ch.prevvalue, ch.origvalue)
}
-func (ch storageChange) dirtied() *common.Address {
- return &ch.account
+func (ch storageChange) dirtied() (common.Address, bool) {
+ return ch.account, true
}
func (ch storageChange) copy() journalEntry {
@@ -416,8 +417,8 @@ func (ch transientStorageChange) revert(s *StateDB) {
s.setTransientState(ch.account, ch.key, ch.prevalue)
}
-func (ch transientStorageChange) dirtied() *common.Address {
- return nil
+func (ch transientStorageChange) dirtied() (common.Address, bool) {
+ return common.Address{}, false
}
func (ch transientStorageChange) copy() journalEntry {
@@ -432,8 +433,8 @@ func (ch refundChange) revert(s *StateDB) {
s.refund = ch.prev
}
-func (ch refundChange) dirtied() *common.Address {
- return nil
+func (ch refundChange) dirtied() (common.Address, bool) {
+ return common.Address{}, false
}
func (ch refundChange) copy() journalEntry {
@@ -452,8 +453,8 @@ func (ch addLogChange) revert(s *StateDB) {
s.logSize--
}
-func (ch addLogChange) dirtied() *common.Address {
- return nil
+func (ch addLogChange) dirtied() (common.Address, bool) {
+ return common.Address{}, false
}
func (ch addLogChange) copy() journalEntry {
@@ -475,8 +476,8 @@ func (ch accessListAddAccountChange) revert(s *StateDB) {
s.accessList.DeleteAddress(ch.address)
}
-func (ch accessListAddAccountChange) dirtied() *common.Address {
- return nil
+func (ch accessListAddAccountChange) dirtied() (common.Address, bool) {
+ return common.Address{}, false
}
func (ch accessListAddAccountChange) copy() journalEntry {
@@ -489,8 +490,8 @@ func (ch accessListAddSlotChange) revert(s *StateDB) {
s.accessList.DeleteSlot(ch.address, ch.slot)
}
-func (ch accessListAddSlotChange) dirtied() *common.Address {
- return nil
+func (ch accessListAddSlotChange) dirtied() (common.Address, bool) {
+ return common.Address{}, false
}
func (ch accessListAddSlotChange) copy() journalEntry {
diff --git a/core/state/snapshot/generate_test.go b/core/state/snapshot/generate_test.go
index 4488630095..7fb4c152dc 100644
--- a/core/state/snapshot/generate_test.go
+++ b/core/state/snapshot/generate_test.go
@@ -25,6 +25,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rlp"
@@ -34,16 +35,10 @@ import (
"github.com/ethereum/go-ethereum/triedb/hashdb"
"github.com/ethereum/go-ethereum/triedb/pathdb"
"github.com/holiman/uint256"
- "golang.org/x/crypto/sha3"
)
func hashData(input []byte) common.Hash {
- var hasher = sha3.NewLegacyKeccak256()
- var hash common.Hash
- hasher.Reset()
- hasher.Write(input)
- hasher.Sum(hash[:0])
- return hash
+ return crypto.Keccak256Hash(input)
}
// Tests that snapshot generation from an empty database.
diff --git a/core/state/state_object.go b/core/state/state_object.go
index 2873c3cb8a..eecb72ce5d 100644
--- a/core/state/state_object.go
+++ b/core/state/state_object.go
@@ -48,11 +48,11 @@ func (s Storage) Copy() Storage {
// - Account values as well as storages can be accessed and modified through the object.
// - Finally, call commit to return the changes of storage trie and update account data.
type stateObject struct {
- db *StateDB
- address common.Address // address of ethereum account
- addrHash common.Hash // hash of ethereum address of the account
- origin *types.StateAccount // Account original data without any change applied, nil means it was not existent
- data types.StateAccount // Account data with all mutations applied in the scope of block
+ db *StateDB
+ address common.Address // address of ethereum account
+ addressHash *common.Hash // hash of ethereum address of the account
+ origin *types.StateAccount // Account original data without any change applied, nil means it was not existent
+ data types.StateAccount // Account data with all mutations applied in the scope of block
// Write caches.
trie Trie // storage trie, which becomes non-nil on first access
@@ -102,7 +102,6 @@ func newObject(db *StateDB, address common.Address, acct *types.StateAccount) *s
return &stateObject{
db: db,
address: address,
- addrHash: crypto.Keccak256Hash(address[:]),
origin: origin,
data: *acct,
originStorage: make(Storage),
@@ -112,6 +111,14 @@ func newObject(db *StateDB, address common.Address, acct *types.StateAccount) *s
}
}
+func (s *stateObject) addrHash() common.Hash {
+ if s.addressHash == nil {
+ h := crypto.Keccak256Hash(s.address[:])
+ s.addressHash = &h
+ }
+ return *s.addressHash
+}
+
func (s *stateObject) markSelfdestructed() {
s.selfDestructed = true
}
@@ -151,7 +158,7 @@ func (s *stateObject) getPrefetchedTrie() Trie {
return nil
}
// Attempt to retrieve the trie from the prefetcher
- return s.db.prefetcher.trie(s.addrHash, s.data.Root)
+ return s.db.prefetcher.trie(s.addrHash(), s.data.Root)
}
// GetState retrieves a value associated with the given storage key.
@@ -203,7 +210,7 @@ func (s *stateObject) GetCommittedState(key common.Hash) common.Hash {
// Schedule the resolved storage slots for prefetching if it's enabled.
if s.db.prefetcher != nil && s.data.Root != types.EmptyRootHash {
- if err = s.db.prefetcher.prefetch(s.addrHash, s.origin.Root, s.address, nil, []common.Hash{key}, true); err != nil {
+ if err = s.db.prefetcher.prefetch(s.addrHash(), s.origin.Root, s.address, nil, []common.Hash{key}, true); err != nil {
log.Error("Failed to prefetch storage slot", "addr", s.address, "key", key, "err", err)
}
}
@@ -264,7 +271,7 @@ func (s *stateObject) finalise() {
s.pendingStorage[key] = value
}
if s.db.prefetcher != nil && len(slotsToPrefetch) > 0 && s.data.Root != types.EmptyRootHash {
- if err := s.db.prefetcher.prefetch(s.addrHash, s.data.Root, s.address, nil, slotsToPrefetch, false); err != nil {
+ if err := s.db.prefetcher.prefetch(s.addrHash(), s.data.Root, s.address, nil, slotsToPrefetch, false); err != nil {
log.Error("Failed to prefetch slots", "addr", s.address, "slots", len(slotsToPrefetch), "err", err)
}
}
@@ -359,7 +366,7 @@ func (s *stateObject) updateTrie() (Trie, error) {
s.db.StorageDeleted.Add(1)
}
if s.db.prefetcher != nil {
- s.db.prefetcher.used(s.addrHash, s.data.Root, nil, used)
+ s.db.prefetcher.used(s.addrHash(), s.data.Root, nil, used)
}
s.uncommittedStorage = make(Storage) // empties the commit markers
return tr, nil
@@ -491,7 +498,7 @@ func (s *stateObject) deepCopy(db *StateDB) *stateObject {
obj := &stateObject{
db: db,
address: s.address,
- addrHash: s.addrHash,
+ addressHash: nil,
origin: s.origin,
data: s.data,
code: s.code,
diff --git a/core/state/statedb.go b/core/state/statedb.go
index 3d329bab64..3a2d9c9ac2 100644
--- a/core/state/statedb.go
+++ b/core/state/statedb.go
@@ -867,13 +867,13 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
witness := trie.Witness()
s.witness.AddState(witness)
if s.witnessStats != nil {
- s.witnessStats.Add(witness, obj.addrHash)
+ s.witnessStats.Add(witness, obj.addrHash())
}
} else if obj.trie != nil {
witness := obj.trie.Witness()
s.witness.AddState(witness)
if s.witnessStats != nil {
- s.witnessStats.Add(witness, obj.addrHash)
+ s.witnessStats.Add(witness, obj.addrHash())
}
}
}
@@ -891,13 +891,13 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
witness := trie.Witness()
s.witness.AddState(witness)
if s.witnessStats != nil {
- s.witnessStats.Add(witness, obj.addrHash)
+ s.witnessStats.Add(witness, obj.addrHash())
}
} else if obj.trie != nil {
witness := obj.trie.Witness()
s.witness.AddState(witness)
if s.witnessStats != nil {
- s.witnessStats.Add(witness, obj.addrHash)
+ s.witnessStats.Add(witness, obj.addrHash())
}
}
}
@@ -1284,7 +1284,7 @@ func (s *StateDB) commit(deleteEmptyObjects bool, noStorageWiping bool, blockNum
return err
}
lock.Lock()
- updates[obj.addrHash] = update
+ updates[obj.addrHash()] = update
s.StorageCommits = time.Since(start) // overwrite with the longest storage commit runtime
lock.Unlock()
return nil
diff --git a/core/state_processor_test.go b/core/state_processor_test.go
index 9d6cbdbc8b..3bf372800b 100644
--- a/core/state_processor_test.go
+++ b/core/state_processor_test.go
@@ -31,10 +31,10 @@ import (
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/crypto/keccak"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie"
"github.com/holiman/uint256"
- "golang.org/x/crypto/sha3"
)
func u64(val uint64) *uint64 { return &val }
@@ -398,7 +398,7 @@ func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Tr
var receipts []*types.Receipt
// The post-state result doesn't need to be correct (this is a bad block), but we do need something there
// Preferably something unique. So let's use a combo of blocknum + txhash
- hasher := sha3.NewLegacyKeccak256()
+ hasher := keccak.NewLegacyKeccak256()
hasher.Write(header.Number.Bytes())
var cumulativeGas uint64
var nBlobs int
diff --git a/core/txpool/blobpool/blobpool.go b/core/txpool/blobpool/blobpool.go
index 27441ac2e2..3947ba50a1 100644
--- a/core/txpool/blobpool/blobpool.go
+++ b/core/txpool/blobpool/blobpool.go
@@ -2096,6 +2096,11 @@ func (p *BlobPool) Clear() {
p.index = make(map[common.Address][]*blobTxMeta)
p.spent = make(map[common.Address]*uint256.Int)
+ // Reset counters and the gapped buffer
+ p.stored = 0
+ p.gapped = make(map[common.Address][]*types.Transaction)
+ p.gappedSource = make(map[common.Hash]common.Address)
+
var (
basefee = uint256.MustFromBig(eip1559.CalcBaseFee(p.chain.Config(), p.head.Load()))
blobfee = uint256.NewInt(params.BlobTxMinBlobGasprice)
diff --git a/core/txpool/legacypool/legacypool.go b/core/txpool/legacypool/legacypool.go
index e17b12f7f2..36970c820e 100644
--- a/core/txpool/legacypool/legacypool.go
+++ b/core/txpool/legacypool/legacypool.go
@@ -908,8 +908,8 @@ func (pool *LegacyPool) addRemoteSync(tx *types.Transaction) error {
func (pool *LegacyPool) Add(txs []*types.Transaction, sync bool) []error {
// Filter out known ones without obtaining the pool lock or recovering signatures
var (
- errs = make([]error, len(txs))
- news = make([]*types.Transaction, 0, len(txs))
+ hasValid bool
+ errs = make([]error, len(txs))
)
for i, tx := range txs {
// If the transaction is known, pre-set the error slot
@@ -927,26 +927,17 @@ func (pool *LegacyPool) Add(txs []*types.Transaction, sync bool) []error {
invalidTxMeter.Mark(1)
continue
}
- // Accumulate all unknown transactions for deeper processing
- news = append(news, tx)
+ hasValid = true
}
- if len(news) == 0 {
+ if !hasValid {
return errs
}
// Process all the new transaction and merge any errors into the original slice
pool.mu.Lock()
- newErrs, dirtyAddrs := pool.addTxsLocked(news)
+ dirtyAddrs := pool.addTxsLocked(txs, errs)
pool.mu.Unlock()
- var nilSlot = 0
- for _, err := range newErrs {
- for errs[nilSlot] != nil {
- nilSlot++
- }
- errs[nilSlot] = err
- nilSlot++
- }
// Reorg the pool internals if needed and return
done := pool.requestPromoteExecutables(dirtyAddrs)
if sync {
@@ -957,14 +948,19 @@ func (pool *LegacyPool) Add(txs []*types.Transaction, sync bool) []error {
// addTxsLocked attempts to queue a batch of transactions if they are valid.
// The transaction pool lock must be held.
-// Returns the error for each tx, and the set of accounts that might became promotable.
-func (pool *LegacyPool) addTxsLocked(txs []*types.Transaction) ([]error, *accountSet) {
+// Sets the error for each tx, and the set of accounts that might became promotable.
+// We only try to add txs that have no error set in the errs slice.
+// If adding the transaction returns an error, we set the error in the errs slice.
+// Requires len(txs) == len(errs).
+func (pool *LegacyPool) addTxsLocked(txs []*types.Transaction, errs []error) *accountSet {
var (
dirty = newAccountSet(pool.signer)
- errs = make([]error, len(txs))
valid int64
)
for i, tx := range txs {
+ if errs[i] != nil {
+ continue
+ }
replaced, err := pool.add(tx)
errs[i] = err
if err == nil {
@@ -975,7 +971,7 @@ func (pool *LegacyPool) addTxsLocked(txs []*types.Transaction) ([]error, *accoun
}
}
validTxMeter.Mark(valid)
- return errs, dirty
+ return dirty
}
// Status returns the status (unknown/pending/queued) of a batch of transactions
@@ -1397,7 +1393,7 @@ func (pool *LegacyPool) reset(oldHead, newHead *types.Header) {
// Inject any transactions discarded due to reorgs
log.Debug("Reinjecting stale transactions", "count", len(reinject))
core.SenderCacher().Recover(pool.signer, reinject)
- pool.addTxsLocked(reinject)
+ pool.addTxsLocked(reinject, make([]error, len(reinject)))
}
// promoteExecutables moves transactions that have become processable from the
diff --git a/core/types/receipt.go b/core/types/receipt.go
index 5b6669f274..ba7d9900f0 100644
--- a/core/types/receipt.go
+++ b/core/types/receipt.go
@@ -424,3 +424,33 @@ func EncodeBlockReceiptLists(receipts []Receipts) []rlp.RawValue {
}
return result
}
+
+// SlimReceipt is a wrapper around a Receipt with RLP serialization that omits
+// the Bloom field and includes the tx type. Used for era files.
+type SlimReceipt Receipt
+
+type slimReceiptRLP struct {
+ Type uint8
+ StatusEncoding []byte
+ CumulativeGasUsed uint64
+ Logs []*Log
+}
+
+// EncodeRLP implements rlp.Encoder, encoding the receipt as
+// [tx-type, post-state-or-status, cumulative-gas, logs].
+func (r *SlimReceipt) EncodeRLP(w io.Writer) error {
+ data := &slimReceiptRLP{r.Type, (*Receipt)(r).statusEncoding(), r.CumulativeGasUsed, r.Logs}
+ return rlp.Encode(w, data)
+}
+
+// DecodeRLP implements rlp.Decoder.
+func (r *SlimReceipt) DecodeRLP(s *rlp.Stream) error {
+ var data slimReceiptRLP
+ if err := s.Decode(&data); err != nil {
+ return err
+ }
+ r.Type = data.Type
+ r.CumulativeGasUsed = data.CumulativeGasUsed
+ r.Logs = data.Logs
+ return (*Receipt)(r).setStatus(data.StatusEncoding)
+}
diff --git a/core/types/receipt_test.go b/core/types/receipt_test.go
index 8f805ff096..676d9c3d30 100644
--- a/core/types/receipt_test.go
+++ b/core/types/receipt_test.go
@@ -512,6 +512,45 @@ func TestReceiptUnmarshalBinary(t *testing.T) {
}
}
+func TestSlimReceiptEncodingDecoding(t *testing.T) {
+ tests := []*Receipt{
+ legacyReceipt,
+ accessListReceipt,
+ eip1559Receipt,
+ {
+ Type: BlobTxType,
+ Status: ReceiptStatusSuccessful,
+ CumulativeGasUsed: 100,
+ Logs: []*Log{},
+ },
+ }
+ for i, want := range tests {
+ enc, err := rlp.EncodeToBytes((*SlimReceipt)(want))
+ if err != nil {
+ t.Fatalf("test %d: encode error: %v", i, err)
+ }
+ got := new(SlimReceipt)
+ if err := rlp.DecodeBytes(enc, got); err != nil {
+ t.Fatalf("test %d: decode error: %v", i, err)
+ }
+ if got.Type != want.Type {
+ t.Errorf("test %d: Type mismatch: got %d, want %d", i, got.Type, want.Type)
+ }
+ if got.Status != want.Status {
+ t.Errorf("test %d: Status mismatch: got %d, want %d", i, got.Status, want.Status)
+ }
+ if !bytes.Equal(got.PostState, want.PostState) {
+ t.Errorf("test %d: PostState mismatch: got %x, want %x", i, got.PostState, want.PostState)
+ }
+ if got.CumulativeGasUsed != want.CumulativeGasUsed {
+ t.Errorf("test %d: CumulativeGasUsed mismatch: got %d, want %d", i, got.CumulativeGasUsed, want.CumulativeGasUsed)
+ }
+ if len(got.Logs) != len(want.Logs) {
+ t.Errorf("test %d: Logs length mismatch: got %d, want %d", i, len(got.Logs), len(want.Logs))
+ }
+ }
+}
+
func clearComputedFieldsOnReceipts(receipts []*Receipt) []*Receipt {
r := make([]*Receipt, len(receipts))
for i, receipt := range receipts {
diff --git a/core/types/transaction_signing.go b/core/types/transaction_signing.go
index 5a624191cf..5854af07c5 100644
--- a/core/types/transaction_signing.go
+++ b/core/types/transaction_signing.go
@@ -330,7 +330,7 @@ func NewEIP2930Signer(chainId *big.Int) Signer {
// are replay-protected as well as unprotected homestead transactions.
// Deprecated: always use the Signer interface type
type EIP155Signer struct {
- chainId, chainIdMul *big.Int
+ chainId *big.Int
}
func NewEIP155Signer(chainId *big.Int) EIP155Signer {
@@ -338,8 +338,7 @@ func NewEIP155Signer(chainId *big.Int) EIP155Signer {
chainId = new(big.Int)
}
return EIP155Signer{
- chainId: chainId,
- chainIdMul: new(big.Int).Mul(chainId, big.NewInt(2)),
+ chainId: chainId,
}
}
@@ -365,7 +364,8 @@ func (s EIP155Signer) Sender(tx *Transaction) (common.Address, error) {
return common.Address{}, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, tx.ChainId(), s.chainId)
}
V, R, S := tx.RawSignatureValues()
- V = new(big.Int).Sub(V, s.chainIdMul)
+ V = new(big.Int).Sub(V, s.chainId)
+ V = new(big.Int).Sub(V, s.chainId)
V.Sub(V, big8)
return recoverPlain(s.Hash(tx), R, S, V, true)
}
@@ -382,7 +382,8 @@ func (s EIP155Signer) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big
}
if s.chainId.Sign() != 0 {
V = big.NewInt(int64(sig[64] + 35))
- V.Add(V, s.chainIdMul)
+ V.Add(V, s.chainId)
+ V.Add(V, s.chainId)
}
return R, S, V, nil
}
diff --git a/core/types/tx_blob.go b/core/types/tx_blob.go
index bbfd3c98db..31aadb5419 100644
--- a/core/types/tx_blob.go
+++ b/core/types/tx_blob.go
@@ -118,8 +118,8 @@ func (sc *BlobTxSidecar) ToV1() error {
}
if sc.Version == BlobSidecarVersion0 {
proofs := make([]kzg4844.Proof, 0, len(sc.Blobs)*kzg4844.CellProofsPerBlob)
- for _, blob := range sc.Blobs {
- cellProofs, err := kzg4844.ComputeCellProofs(&blob)
+ for i := range sc.Blobs {
+ cellProofs, err := kzg4844.ComputeCellProofs(&sc.Blobs[i])
if err != nil {
return err
}
diff --git a/core/vm/evm.go b/core/vm/evm.go
index 25a3318c02..c365f637d2 100644
--- a/core/vm/evm.go
+++ b/core/vm/evm.go
@@ -73,9 +73,8 @@ type BlockContext struct {
type TxContext struct {
// Message information
Origin common.Address // Provides information for ORIGIN
- GasPrice *big.Int // Provides information for GASPRICE (and is used to zero the basefee if NoBaseFee is set)
+ GasPrice *uint256.Int // Provides information for GASPRICE (and is used to zero the basefee if NoBaseFee is set)
BlobHashes []common.Hash // Provides information for BLOBHASH
- BlobFeeCap *big.Int // Is used to zero the blobbasefee if NoBaseFee is set
AccessEvents *state.AccessEvents // Capture all state accesses for this tx
}
@@ -125,9 +124,6 @@ type EVM struct {
// jumpDests stores results of JUMPDEST analysis.
jumpDests JumpDestCache
- hasher crypto.KeccakState // Keccak256 hasher instance shared across opcodes
- hasherBuf common.Hash // Keccak256 hasher result array shared across opcodes
-
readOnly bool // Whether to throw on stateful modifications
returnData []byte // Last CALL's return data for subsequent reuse
}
@@ -144,7 +140,6 @@ func NewEVM(blockCtx BlockContext, statedb StateDB, chainConfig *params.ChainCon
chainConfig: chainConfig,
chainRules: chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Random != nil, blockCtx.Time),
jumpDests: newMapJumpDests(),
- hasher: crypto.NewKeccakState(),
}
evm.precompiles = activePrecompiledContracts(evm.chainRules)
@@ -618,7 +613,7 @@ func (evm *EVM) Create(caller common.Address, code []byte, gas uint64, value *ui
// The different between Create2 with Create is Create2 uses keccak256(0xff ++ msg.sender ++ salt ++ keccak256(init_code))[12:]
// instead of the usual sender-and-nonce-hash as the address where the contract is initialized at.
func (evm *EVM) Create2(caller common.Address, code []byte, gas uint64, endowment *uint256.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
- inithash := crypto.HashData(evm.hasher, code)
+ inithash := crypto.Keccak256Hash(code)
contractAddr = crypto.CreateAddress2(caller, salt.Bytes32(), inithash[:])
return evm.create(caller, code, gas, endowment, contractAddr, CREATE2)
}
diff --git a/core/vm/instructions.go b/core/vm/instructions.go
index a57e9caa0f..a4c4b0703b 100644
--- a/core/vm/instructions.go
+++ b/core/vm/instructions.go
@@ -22,6 +22,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256"
)
@@ -233,14 +234,12 @@ func opKeccak256(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
offset, size := scope.Stack.pop(), scope.Stack.peek()
data := scope.Memory.GetPtr(offset.Uint64(), size.Uint64())
- evm.hasher.Reset()
- evm.hasher.Write(data)
- evm.hasher.Read(evm.hasherBuf[:])
+ hash := crypto.Keccak256Hash(data)
if evm.Config.EnablePreimageRecording {
- evm.StateDB.AddPreimage(evm.hasherBuf, data)
+ evm.StateDB.AddPreimage(hash, data)
}
- size.SetBytes(evm.hasherBuf[:])
+ size.SetBytes(hash[:])
return nil, nil
}
@@ -417,8 +416,7 @@ func opExtCodeHash(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
}
func opGasprice(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
- v, _ := uint256.FromBig(evm.GasPrice)
- scope.Stack.push(v)
+ scope.Stack.push(evm.GasPrice.Clone())
return nil, nil
}
diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go
index 56cb2686a6..3f776146f1 100644
--- a/core/vm/instructions_test.go
+++ b/core/vm/instructions_test.go
@@ -19,6 +19,7 @@ package vm
import (
"bytes"
"encoding/json"
+ "errors"
"fmt"
"math/big"
"os"
@@ -1013,10 +1014,11 @@ func TestEIP8024_Execution(t *testing.T) {
evm := NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{})
tests := []struct {
- name string
- codeHex string
- wantErr bool
- wantVals []uint64
+ name string
+ codeHex string
+ wantErr error
+ wantOpcode OpCode
+ wantVals []uint64
}{
{
name: "DUPN",
@@ -1069,55 +1071,70 @@ func TestEIP8024_Execution(t *testing.T) {
},
},
{
- name: "INVALID_SWAPN_LOW",
- codeHex: "e75b",
- wantErr: true,
+ name: "INVALID_SWAPN_LOW",
+ codeHex: "e75b",
+ wantErr: &ErrInvalidOpCode{},
+ wantOpcode: SWAPN,
},
{
name: "JUMP over INVALID_DUPN",
codeHex: "600456e65b",
- wantErr: false,
+ wantErr: nil,
+ },
+ {
+ name: "UNDERFLOW_DUPN_1",
+ codeHex: "6000808080808080808080808080808080e600",
+ wantErr: &ErrStackUnderflow{},
+ wantOpcode: DUPN,
},
// Additional test cases
{
- name: "INVALID_DUPN_LOW",
- codeHex: "e65b",
- wantErr: true,
+ name: "INVALID_DUPN_LOW",
+ codeHex: "e65b",
+ wantErr: &ErrInvalidOpCode{},
+ wantOpcode: DUPN,
},
{
- name: "INVALID_EXCHANGE_LOW",
- codeHex: "e850",
- wantErr: true,
+ name: "INVALID_EXCHANGE_LOW",
+ codeHex: "e850",
+ wantErr: &ErrInvalidOpCode{},
+ wantOpcode: EXCHANGE,
},
{
- name: "INVALID_DUPN_HIGH",
- codeHex: "e67f",
- wantErr: true,
+ name: "INVALID_DUPN_HIGH",
+ codeHex: "e67f",
+ wantErr: &ErrInvalidOpCode{},
+ wantOpcode: DUPN,
},
{
- name: "INVALID_SWAPN_HIGH",
- codeHex: "e77f",
- wantErr: true,
+ name: "INVALID_SWAPN_HIGH",
+ codeHex: "e77f",
+ wantErr: &ErrInvalidOpCode{},
+ wantOpcode: SWAPN,
},
{
- name: "INVALID_EXCHANGE_HIGH",
- codeHex: "e87f",
- wantErr: true,
+ name: "INVALID_EXCHANGE_HIGH",
+ codeHex: "e87f",
+ wantErr: &ErrInvalidOpCode{},
+ wantOpcode: EXCHANGE,
},
{
- name: "UNDERFLOW_DUPN",
- codeHex: "5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5fe600", // (n=17, need 17 items, have 16)
- wantErr: true,
+ name: "UNDERFLOW_DUPN_2",
+ codeHex: "5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5fe600", // (n=17, need 17 items, have 16)
+ wantErr: &ErrStackUnderflow{},
+ wantOpcode: DUPN,
},
{
- name: "UNDERFLOW_SWAPN",
- codeHex: "5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5fe700", // (n=17, need 18 items, have 17)
- wantErr: true,
+ name: "UNDERFLOW_SWAPN",
+ codeHex: "5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5fe700", // (n=17, need 18 items, have 17)
+ wantErr: &ErrStackUnderflow{},
+ wantOpcode: SWAPN,
},
{
- name: "UNDERFLOW_EXCHANGE",
- codeHex: "60016002e801", // (n,m)=(1,2), need 3 items, have 2
- wantErr: true,
+ name: "UNDERFLOW_EXCHANGE",
+ codeHex: "60016002e801", // (n,m)=(1,2), need 3 items, have 2
+ wantErr: &ErrStackUnderflow{},
+ wantOpcode: EXCHANGE,
},
{
name: "PC_INCREMENT",
@@ -1133,6 +1150,7 @@ func TestEIP8024_Execution(t *testing.T) {
pc := uint64(0)
scope := &ScopeContext{Stack: stack, Contract: &Contract{Code: code}}
var err error
+ var errOp OpCode
for pc < uint64(len(code)) && err == nil {
op := code[pc]
switch OpCode(op) {
@@ -1149,6 +1167,8 @@ func TestEIP8024_Execution(t *testing.T) {
_, err = opJumpdest(&pc, evm, scope)
case ISZERO:
_, err = opIszero(&pc, evm, scope)
+ case PUSH0:
+ _, err = opPush0(&pc, evm, scope)
case DUPN:
_, err = opDupN(&pc, evm, scope)
case SWAPN:
@@ -1156,14 +1176,37 @@ func TestEIP8024_Execution(t *testing.T) {
case EXCHANGE:
_, err = opExchange(&pc, evm, scope)
default:
- err = &ErrInvalidOpCode{opcode: OpCode(op)}
+ t.Fatalf("unexpected opcode %s at pc=%d", OpCode(op), pc)
+ }
+ if err != nil {
+ errOp = OpCode(op)
}
pc++
}
- if tc.wantErr {
+ if tc.wantErr != nil {
+ // Fail because we wanted an error, but didn't get one.
if err == nil {
t.Fatalf("expected error, got nil")
}
+ // Fail if the wrong opcode threw an error.
+ if errOp != tc.wantOpcode {
+ t.Fatalf("expected error from opcode %s, got %s", tc.wantOpcode, errOp)
+ }
+ // Fail if we don't get the error we expect.
+ switch tc.wantErr.(type) {
+ case *ErrInvalidOpCode:
+ var want *ErrInvalidOpCode
+ if !errors.As(err, &want) {
+ t.Fatalf("expected ErrInvalidOpCode, got %v", err)
+ }
+ case *ErrStackUnderflow:
+ var want *ErrStackUnderflow
+ if !errors.As(err, &want) {
+ t.Fatalf("expected ErrStackUnderflow, got %v", err)
+ }
+ default:
+ t.Fatalf("unsupported wantErr type %T", tc.wantErr)
+ }
return
}
if err != nil {
diff --git a/core/vm/runtime/env.go b/core/vm/runtime/env.go
index e54041f7e2..4181e08ee8 100644
--- a/core/vm/runtime/env.go
+++ b/core/vm/runtime/env.go
@@ -19,14 +19,14 @@ package runtime
import (
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/vm"
+ "github.com/holiman/uint256"
)
func NewEnv(cfg *Config) *vm.EVM {
txContext := vm.TxContext{
Origin: cfg.Origin,
- GasPrice: cfg.GasPrice,
+ GasPrice: uint256.MustFromBig(cfg.GasPrice),
BlobHashes: cfg.BlobHashes,
- BlobFeeCap: cfg.BlobFeeCap,
}
blockContext := vm.BlockContext{
CanTransfer: core.CanTransfer,
diff --git a/crypto/keccak.go b/crypto/keccak.go
index 0ad79a63c1..3fafddc92e 100644
--- a/crypto/keccak.go
+++ b/crypto/keccak.go
@@ -22,17 +22,17 @@ import (
"sync"
"github.com/ethereum/go-ethereum/common"
- "golang.org/x/crypto/sha3"
+ "github.com/ethereum/go-ethereum/crypto/keccak"
)
// NewKeccakState creates a new KeccakState
func NewKeccakState() KeccakState {
- return sha3.NewLegacyKeccak256().(KeccakState)
+ return keccak.NewLegacyKeccak256().(KeccakState)
}
var hasherPool = sync.Pool{
New: func() any {
- return sha3.NewLegacyKeccak256().(KeccakState)
+ return keccak.NewLegacyKeccak256().(KeccakState)
},
}
diff --git a/crypto/keccak/LICENSE b/crypto/keccak/LICENSE
new file mode 100644
index 0000000000..2a7cf70da6
--- /dev/null
+++ b/crypto/keccak/LICENSE
@@ -0,0 +1,27 @@
+Copyright 2009 The Go Authors.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google LLC nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/crypto/keccak/README.md b/crypto/keccak/README.md
new file mode 100644
index 0000000000..295a5b958c
--- /dev/null
+++ b/crypto/keccak/README.md
@@ -0,0 +1,6 @@
+This is a vendored and modified copy of golang.org/x/crypto/sha3, with an assembly
+implementation of keccak256. We wish to retain the assembly implementation,
+which was removed in v0.44.0.
+
+Ethereum uses a 'legacy' variant of Keccak, which was defined before it became SHA3. As
+such, we cannot use the standard library crypto/sha3 package.
diff --git a/crypto/keccak/hashes.go b/crypto/keccak/hashes.go
new file mode 100644
index 0000000000..c78c5fe992
--- /dev/null
+++ b/crypto/keccak/hashes.go
@@ -0,0 +1,44 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package keccak
+
+// This file provides functions for creating instances of the SHA-3
+// and SHAKE hash functions, as well as utility functions for hashing
+// bytes.
+
+import (
+ "hash"
+)
+
+const (
+ dsbyteSHA3 = 0b00000110
+ dsbyteKeccak = 0b00000001
+ dsbyteShake = 0b00011111
+ dsbyteCShake = 0b00000100
+
+ // rateK[c] is the rate in bytes for Keccak[c] where c is the capacity in
+ // bits. Given the sponge size is 1600 bits, the rate is 1600 - c bits.
+ rateK256 = (1600 - 256) / 8
+ rateK448 = (1600 - 448) / 8
+ rateK512 = (1600 - 512) / 8
+ rateK768 = (1600 - 768) / 8
+ rateK1024 = (1600 - 1024) / 8
+)
+
+// NewLegacyKeccak256 creates a new Keccak-256 hash.
+//
+// Only use this function if you require compatibility with an existing cryptosystem
+// that uses non-standard padding. All other users should use New256 instead.
+func NewLegacyKeccak256() hash.Hash {
+ return &state{rate: rateK512, outputLen: 32, dsbyte: dsbyteKeccak}
+}
+
+// NewLegacyKeccak512 creates a new Keccak-512 hash.
+//
+// Only use this function if you require compatibility with an existing cryptosystem
+// that uses non-standard padding. All other users should use New512 instead.
+func NewLegacyKeccak512() hash.Hash {
+ return &state{rate: rateK1024, outputLen: 64, dsbyte: dsbyteKeccak}
+}
diff --git a/crypto/keccak/keccakf.go b/crypto/keccak/keccakf.go
new file mode 100644
index 0000000000..82694fa4a3
--- /dev/null
+++ b/crypto/keccak/keccakf.go
@@ -0,0 +1,414 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build !amd64 || purego || !gc
+
+package keccak
+
+import "math/bits"
+
+// rc stores the round constants for use in the ι step.
+var rc = [24]uint64{
+ 0x0000000000000001,
+ 0x0000000000008082,
+ 0x800000000000808A,
+ 0x8000000080008000,
+ 0x000000000000808B,
+ 0x0000000080000001,
+ 0x8000000080008081,
+ 0x8000000000008009,
+ 0x000000000000008A,
+ 0x0000000000000088,
+ 0x0000000080008009,
+ 0x000000008000000A,
+ 0x000000008000808B,
+ 0x800000000000008B,
+ 0x8000000000008089,
+ 0x8000000000008003,
+ 0x8000000000008002,
+ 0x8000000000000080,
+ 0x000000000000800A,
+ 0x800000008000000A,
+ 0x8000000080008081,
+ 0x8000000000008080,
+ 0x0000000080000001,
+ 0x8000000080008008,
+}
+
+// keccakF1600 applies the Keccak permutation to a 1600b-wide
+// state represented as a slice of 25 uint64s.
+func keccakF1600(a *[25]uint64) {
+ // Implementation translated from Keccak-inplace.c
+ // in the keccak reference code.
+ var t, bc0, bc1, bc2, bc3, bc4, d0, d1, d2, d3, d4 uint64
+
+ for i := 0; i < 24; i += 4 {
+ // Combines the 5 steps in each round into 2 steps.
+ // Unrolls 4 rounds per loop and spreads some steps across rounds.
+
+ // Round 1
+ bc0 = a[0] ^ a[5] ^ a[10] ^ a[15] ^ a[20]
+ bc1 = a[1] ^ a[6] ^ a[11] ^ a[16] ^ a[21]
+ bc2 = a[2] ^ a[7] ^ a[12] ^ a[17] ^ a[22]
+ bc3 = a[3] ^ a[8] ^ a[13] ^ a[18] ^ a[23]
+ bc4 = a[4] ^ a[9] ^ a[14] ^ a[19] ^ a[24]
+ d0 = bc4 ^ (bc1<<1 | bc1>>63)
+ d1 = bc0 ^ (bc2<<1 | bc2>>63)
+ d2 = bc1 ^ (bc3<<1 | bc3>>63)
+ d3 = bc2 ^ (bc4<<1 | bc4>>63)
+ d4 = bc3 ^ (bc0<<1 | bc0>>63)
+
+ bc0 = a[0] ^ d0
+ t = a[6] ^ d1
+ bc1 = bits.RotateLeft64(t, 44)
+ t = a[12] ^ d2
+ bc2 = bits.RotateLeft64(t, 43)
+ t = a[18] ^ d3
+ bc3 = bits.RotateLeft64(t, 21)
+ t = a[24] ^ d4
+ bc4 = bits.RotateLeft64(t, 14)
+ a[0] = bc0 ^ (bc2 &^ bc1) ^ rc[i]
+ a[6] = bc1 ^ (bc3 &^ bc2)
+ a[12] = bc2 ^ (bc4 &^ bc3)
+ a[18] = bc3 ^ (bc0 &^ bc4)
+ a[24] = bc4 ^ (bc1 &^ bc0)
+
+ t = a[10] ^ d0
+ bc2 = bits.RotateLeft64(t, 3)
+ t = a[16] ^ d1
+ bc3 = bits.RotateLeft64(t, 45)
+ t = a[22] ^ d2
+ bc4 = bits.RotateLeft64(t, 61)
+ t = a[3] ^ d3
+ bc0 = bits.RotateLeft64(t, 28)
+ t = a[9] ^ d4
+ bc1 = bits.RotateLeft64(t, 20)
+ a[10] = bc0 ^ (bc2 &^ bc1)
+ a[16] = bc1 ^ (bc3 &^ bc2)
+ a[22] = bc2 ^ (bc4 &^ bc3)
+ a[3] = bc3 ^ (bc0 &^ bc4)
+ a[9] = bc4 ^ (bc1 &^ bc0)
+
+ t = a[20] ^ d0
+ bc4 = bits.RotateLeft64(t, 18)
+ t = a[1] ^ d1
+ bc0 = bits.RotateLeft64(t, 1)
+ t = a[7] ^ d2
+ bc1 = bits.RotateLeft64(t, 6)
+ t = a[13] ^ d3
+ bc2 = bits.RotateLeft64(t, 25)
+ t = a[19] ^ d4
+ bc3 = bits.RotateLeft64(t, 8)
+ a[20] = bc0 ^ (bc2 &^ bc1)
+ a[1] = bc1 ^ (bc3 &^ bc2)
+ a[7] = bc2 ^ (bc4 &^ bc3)
+ a[13] = bc3 ^ (bc0 &^ bc4)
+ a[19] = bc4 ^ (bc1 &^ bc0)
+
+ t = a[5] ^ d0
+ bc1 = bits.RotateLeft64(t, 36)
+ t = a[11] ^ d1
+ bc2 = bits.RotateLeft64(t, 10)
+ t = a[17] ^ d2
+ bc3 = bits.RotateLeft64(t, 15)
+ t = a[23] ^ d3
+ bc4 = bits.RotateLeft64(t, 56)
+ t = a[4] ^ d4
+ bc0 = bits.RotateLeft64(t, 27)
+ a[5] = bc0 ^ (bc2 &^ bc1)
+ a[11] = bc1 ^ (bc3 &^ bc2)
+ a[17] = bc2 ^ (bc4 &^ bc3)
+ a[23] = bc3 ^ (bc0 &^ bc4)
+ a[4] = bc4 ^ (bc1 &^ bc0)
+
+ t = a[15] ^ d0
+ bc3 = bits.RotateLeft64(t, 41)
+ t = a[21] ^ d1
+ bc4 = bits.RotateLeft64(t, 2)
+ t = a[2] ^ d2
+ bc0 = bits.RotateLeft64(t, 62)
+ t = a[8] ^ d3
+ bc1 = bits.RotateLeft64(t, 55)
+ t = a[14] ^ d4
+ bc2 = bits.RotateLeft64(t, 39)
+ a[15] = bc0 ^ (bc2 &^ bc1)
+ a[21] = bc1 ^ (bc3 &^ bc2)
+ a[2] = bc2 ^ (bc4 &^ bc3)
+ a[8] = bc3 ^ (bc0 &^ bc4)
+ a[14] = bc4 ^ (bc1 &^ bc0)
+
+ // Round 2
+ bc0 = a[0] ^ a[5] ^ a[10] ^ a[15] ^ a[20]
+ bc1 = a[1] ^ a[6] ^ a[11] ^ a[16] ^ a[21]
+ bc2 = a[2] ^ a[7] ^ a[12] ^ a[17] ^ a[22]
+ bc3 = a[3] ^ a[8] ^ a[13] ^ a[18] ^ a[23]
+ bc4 = a[4] ^ a[9] ^ a[14] ^ a[19] ^ a[24]
+ d0 = bc4 ^ (bc1<<1 | bc1>>63)
+ d1 = bc0 ^ (bc2<<1 | bc2>>63)
+ d2 = bc1 ^ (bc3<<1 | bc3>>63)
+ d3 = bc2 ^ (bc4<<1 | bc4>>63)
+ d4 = bc3 ^ (bc0<<1 | bc0>>63)
+
+ bc0 = a[0] ^ d0
+ t = a[16] ^ d1
+ bc1 = bits.RotateLeft64(t, 44)
+ t = a[7] ^ d2
+ bc2 = bits.RotateLeft64(t, 43)
+ t = a[23] ^ d3
+ bc3 = bits.RotateLeft64(t, 21)
+ t = a[14] ^ d4
+ bc4 = bits.RotateLeft64(t, 14)
+ a[0] = bc0 ^ (bc2 &^ bc1) ^ rc[i+1]
+ a[16] = bc1 ^ (bc3 &^ bc2)
+ a[7] = bc2 ^ (bc4 &^ bc3)
+ a[23] = bc3 ^ (bc0 &^ bc4)
+ a[14] = bc4 ^ (bc1 &^ bc0)
+
+ t = a[20] ^ d0
+ bc2 = bits.RotateLeft64(t, 3)
+ t = a[11] ^ d1
+ bc3 = bits.RotateLeft64(t, 45)
+ t = a[2] ^ d2
+ bc4 = bits.RotateLeft64(t, 61)
+ t = a[18] ^ d3
+ bc0 = bits.RotateLeft64(t, 28)
+ t = a[9] ^ d4
+ bc1 = bits.RotateLeft64(t, 20)
+ a[20] = bc0 ^ (bc2 &^ bc1)
+ a[11] = bc1 ^ (bc3 &^ bc2)
+ a[2] = bc2 ^ (bc4 &^ bc3)
+ a[18] = bc3 ^ (bc0 &^ bc4)
+ a[9] = bc4 ^ (bc1 &^ bc0)
+
+ t = a[15] ^ d0
+ bc4 = bits.RotateLeft64(t, 18)
+ t = a[6] ^ d1
+ bc0 = bits.RotateLeft64(t, 1)
+ t = a[22] ^ d2
+ bc1 = bits.RotateLeft64(t, 6)
+ t = a[13] ^ d3
+ bc2 = bits.RotateLeft64(t, 25)
+ t = a[4] ^ d4
+ bc3 = bits.RotateLeft64(t, 8)
+ a[15] = bc0 ^ (bc2 &^ bc1)
+ a[6] = bc1 ^ (bc3 &^ bc2)
+ a[22] = bc2 ^ (bc4 &^ bc3)
+ a[13] = bc3 ^ (bc0 &^ bc4)
+ a[4] = bc4 ^ (bc1 &^ bc0)
+
+ t = a[10] ^ d0
+ bc1 = bits.RotateLeft64(t, 36)
+ t = a[1] ^ d1
+ bc2 = bits.RotateLeft64(t, 10)
+ t = a[17] ^ d2
+ bc3 = bits.RotateLeft64(t, 15)
+ t = a[8] ^ d3
+ bc4 = bits.RotateLeft64(t, 56)
+ t = a[24] ^ d4
+ bc0 = bits.RotateLeft64(t, 27)
+ a[10] = bc0 ^ (bc2 &^ bc1)
+ a[1] = bc1 ^ (bc3 &^ bc2)
+ a[17] = bc2 ^ (bc4 &^ bc3)
+ a[8] = bc3 ^ (bc0 &^ bc4)
+ a[24] = bc4 ^ (bc1 &^ bc0)
+
+ t = a[5] ^ d0
+ bc3 = bits.RotateLeft64(t, 41)
+ t = a[21] ^ d1
+ bc4 = bits.RotateLeft64(t, 2)
+ t = a[12] ^ d2
+ bc0 = bits.RotateLeft64(t, 62)
+ t = a[3] ^ d3
+ bc1 = bits.RotateLeft64(t, 55)
+ t = a[19] ^ d4
+ bc2 = bits.RotateLeft64(t, 39)
+ a[5] = bc0 ^ (bc2 &^ bc1)
+ a[21] = bc1 ^ (bc3 &^ bc2)
+ a[12] = bc2 ^ (bc4 &^ bc3)
+ a[3] = bc3 ^ (bc0 &^ bc4)
+ a[19] = bc4 ^ (bc1 &^ bc0)
+
+ // Round 3
+ bc0 = a[0] ^ a[5] ^ a[10] ^ a[15] ^ a[20]
+ bc1 = a[1] ^ a[6] ^ a[11] ^ a[16] ^ a[21]
+ bc2 = a[2] ^ a[7] ^ a[12] ^ a[17] ^ a[22]
+ bc3 = a[3] ^ a[8] ^ a[13] ^ a[18] ^ a[23]
+ bc4 = a[4] ^ a[9] ^ a[14] ^ a[19] ^ a[24]
+ d0 = bc4 ^ (bc1<<1 | bc1>>63)
+ d1 = bc0 ^ (bc2<<1 | bc2>>63)
+ d2 = bc1 ^ (bc3<<1 | bc3>>63)
+ d3 = bc2 ^ (bc4<<1 | bc4>>63)
+ d4 = bc3 ^ (bc0<<1 | bc0>>63)
+
+ bc0 = a[0] ^ d0
+ t = a[11] ^ d1
+ bc1 = bits.RotateLeft64(t, 44)
+ t = a[22] ^ d2
+ bc2 = bits.RotateLeft64(t, 43)
+ t = a[8] ^ d3
+ bc3 = bits.RotateLeft64(t, 21)
+ t = a[19] ^ d4
+ bc4 = bits.RotateLeft64(t, 14)
+ a[0] = bc0 ^ (bc2 &^ bc1) ^ rc[i+2]
+ a[11] = bc1 ^ (bc3 &^ bc2)
+ a[22] = bc2 ^ (bc4 &^ bc3)
+ a[8] = bc3 ^ (bc0 &^ bc4)
+ a[19] = bc4 ^ (bc1 &^ bc0)
+
+ t = a[15] ^ d0
+ bc2 = bits.RotateLeft64(t, 3)
+ t = a[1] ^ d1
+ bc3 = bits.RotateLeft64(t, 45)
+ t = a[12] ^ d2
+ bc4 = bits.RotateLeft64(t, 61)
+ t = a[23] ^ d3
+ bc0 = bits.RotateLeft64(t, 28)
+ t = a[9] ^ d4
+ bc1 = bits.RotateLeft64(t, 20)
+ a[15] = bc0 ^ (bc2 &^ bc1)
+ a[1] = bc1 ^ (bc3 &^ bc2)
+ a[12] = bc2 ^ (bc4 &^ bc3)
+ a[23] = bc3 ^ (bc0 &^ bc4)
+ a[9] = bc4 ^ (bc1 &^ bc0)
+
+ t = a[5] ^ d0
+ bc4 = bits.RotateLeft64(t, 18)
+ t = a[16] ^ d1
+ bc0 = bits.RotateLeft64(t, 1)
+ t = a[2] ^ d2
+ bc1 = bits.RotateLeft64(t, 6)
+ t = a[13] ^ d3
+ bc2 = bits.RotateLeft64(t, 25)
+ t = a[24] ^ d4
+ bc3 = bits.RotateLeft64(t, 8)
+ a[5] = bc0 ^ (bc2 &^ bc1)
+ a[16] = bc1 ^ (bc3 &^ bc2)
+ a[2] = bc2 ^ (bc4 &^ bc3)
+ a[13] = bc3 ^ (bc0 &^ bc4)
+ a[24] = bc4 ^ (bc1 &^ bc0)
+
+ t = a[20] ^ d0
+ bc1 = bits.RotateLeft64(t, 36)
+ t = a[6] ^ d1
+ bc2 = bits.RotateLeft64(t, 10)
+ t = a[17] ^ d2
+ bc3 = bits.RotateLeft64(t, 15)
+ t = a[3] ^ d3
+ bc4 = bits.RotateLeft64(t, 56)
+ t = a[14] ^ d4
+ bc0 = bits.RotateLeft64(t, 27)
+ a[20] = bc0 ^ (bc2 &^ bc1)
+ a[6] = bc1 ^ (bc3 &^ bc2)
+ a[17] = bc2 ^ (bc4 &^ bc3)
+ a[3] = bc3 ^ (bc0 &^ bc4)
+ a[14] = bc4 ^ (bc1 &^ bc0)
+
+ t = a[10] ^ d0
+ bc3 = bits.RotateLeft64(t, 41)
+ t = a[21] ^ d1
+ bc4 = bits.RotateLeft64(t, 2)
+ t = a[7] ^ d2
+ bc0 = bits.RotateLeft64(t, 62)
+ t = a[18] ^ d3
+ bc1 = bits.RotateLeft64(t, 55)
+ t = a[4] ^ d4
+ bc2 = bits.RotateLeft64(t, 39)
+ a[10] = bc0 ^ (bc2 &^ bc1)
+ a[21] = bc1 ^ (bc3 &^ bc2)
+ a[7] = bc2 ^ (bc4 &^ bc3)
+ a[18] = bc3 ^ (bc0 &^ bc4)
+ a[4] = bc4 ^ (bc1 &^ bc0)
+
+ // Round 4
+ bc0 = a[0] ^ a[5] ^ a[10] ^ a[15] ^ a[20]
+ bc1 = a[1] ^ a[6] ^ a[11] ^ a[16] ^ a[21]
+ bc2 = a[2] ^ a[7] ^ a[12] ^ a[17] ^ a[22]
+ bc3 = a[3] ^ a[8] ^ a[13] ^ a[18] ^ a[23]
+ bc4 = a[4] ^ a[9] ^ a[14] ^ a[19] ^ a[24]
+ d0 = bc4 ^ (bc1<<1 | bc1>>63)
+ d1 = bc0 ^ (bc2<<1 | bc2>>63)
+ d2 = bc1 ^ (bc3<<1 | bc3>>63)
+ d3 = bc2 ^ (bc4<<1 | bc4>>63)
+ d4 = bc3 ^ (bc0<<1 | bc0>>63)
+
+ bc0 = a[0] ^ d0
+ t = a[1] ^ d1
+ bc1 = bits.RotateLeft64(t, 44)
+ t = a[2] ^ d2
+ bc2 = bits.RotateLeft64(t, 43)
+ t = a[3] ^ d3
+ bc3 = bits.RotateLeft64(t, 21)
+ t = a[4] ^ d4
+ bc4 = bits.RotateLeft64(t, 14)
+ a[0] = bc0 ^ (bc2 &^ bc1) ^ rc[i+3]
+ a[1] = bc1 ^ (bc3 &^ bc2)
+ a[2] = bc2 ^ (bc4 &^ bc3)
+ a[3] = bc3 ^ (bc0 &^ bc4)
+ a[4] = bc4 ^ (bc1 &^ bc0)
+
+ t = a[5] ^ d0
+ bc2 = bits.RotateLeft64(t, 3)
+ t = a[6] ^ d1
+ bc3 = bits.RotateLeft64(t, 45)
+ t = a[7] ^ d2
+ bc4 = bits.RotateLeft64(t, 61)
+ t = a[8] ^ d3
+ bc0 = bits.RotateLeft64(t, 28)
+ t = a[9] ^ d4
+ bc1 = bits.RotateLeft64(t, 20)
+ a[5] = bc0 ^ (bc2 &^ bc1)
+ a[6] = bc1 ^ (bc3 &^ bc2)
+ a[7] = bc2 ^ (bc4 &^ bc3)
+ a[8] = bc3 ^ (bc0 &^ bc4)
+ a[9] = bc4 ^ (bc1 &^ bc0)
+
+ t = a[10] ^ d0
+ bc4 = bits.RotateLeft64(t, 18)
+ t = a[11] ^ d1
+ bc0 = bits.RotateLeft64(t, 1)
+ t = a[12] ^ d2
+ bc1 = bits.RotateLeft64(t, 6)
+ t = a[13] ^ d3
+ bc2 = bits.RotateLeft64(t, 25)
+ t = a[14] ^ d4
+ bc3 = bits.RotateLeft64(t, 8)
+ a[10] = bc0 ^ (bc2 &^ bc1)
+ a[11] = bc1 ^ (bc3 &^ bc2)
+ a[12] = bc2 ^ (bc4 &^ bc3)
+ a[13] = bc3 ^ (bc0 &^ bc4)
+ a[14] = bc4 ^ (bc1 &^ bc0)
+
+ t = a[15] ^ d0
+ bc1 = bits.RotateLeft64(t, 36)
+ t = a[16] ^ d1
+ bc2 = bits.RotateLeft64(t, 10)
+ t = a[17] ^ d2
+ bc3 = bits.RotateLeft64(t, 15)
+ t = a[18] ^ d3
+ bc4 = bits.RotateLeft64(t, 56)
+ t = a[19] ^ d4
+ bc0 = bits.RotateLeft64(t, 27)
+ a[15] = bc0 ^ (bc2 &^ bc1)
+ a[16] = bc1 ^ (bc3 &^ bc2)
+ a[17] = bc2 ^ (bc4 &^ bc3)
+ a[18] = bc3 ^ (bc0 &^ bc4)
+ a[19] = bc4 ^ (bc1 &^ bc0)
+
+ t = a[20] ^ d0
+ bc3 = bits.RotateLeft64(t, 41)
+ t = a[21] ^ d1
+ bc4 = bits.RotateLeft64(t, 2)
+ t = a[22] ^ d2
+ bc0 = bits.RotateLeft64(t, 62)
+ t = a[23] ^ d3
+ bc1 = bits.RotateLeft64(t, 55)
+ t = a[24] ^ d4
+ bc2 = bits.RotateLeft64(t, 39)
+ a[20] = bc0 ^ (bc2 &^ bc1)
+ a[21] = bc1 ^ (bc3 &^ bc2)
+ a[22] = bc2 ^ (bc4 &^ bc3)
+ a[23] = bc3 ^ (bc0 &^ bc4)
+ a[24] = bc4 ^ (bc1 &^ bc0)
+ }
+}
diff --git a/crypto/keccak/keccakf_amd64.go b/crypto/keccak/keccakf_amd64.go
new file mode 100644
index 0000000000..cb6eca44c3
--- /dev/null
+++ b/crypto/keccak/keccakf_amd64.go
@@ -0,0 +1,13 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build amd64 && !purego && gc
+
+package keccak
+
+// This function is implemented in keccakf_amd64.s.
+
+//go:noescape
+
+func keccakF1600(a *[25]uint64)
diff --git a/crypto/keccak/keccakf_amd64.s b/crypto/keccak/keccakf_amd64.s
new file mode 100644
index 0000000000..99e2f16e97
--- /dev/null
+++ b/crypto/keccak/keccakf_amd64.s
@@ -0,0 +1,5419 @@
+// Code generated by command: go run keccakf_amd64_asm.go -out ../keccakf_amd64.s -pkg sha3. DO NOT EDIT.
+
+//go:build amd64 && !purego && gc
+
+// func keccakF1600(a *[25]uint64)
+TEXT ·keccakF1600(SB), $200-8
+ MOVQ a+0(FP), DI
+
+ // Convert the user state into an internal state
+ NOTQ 8(DI)
+ NOTQ 16(DI)
+ NOTQ 64(DI)
+ NOTQ 96(DI)
+ NOTQ 136(DI)
+ NOTQ 160(DI)
+
+ // Execute the KeccakF permutation
+ MOVQ (DI), SI
+ MOVQ 8(DI), BP
+ MOVQ 32(DI), R15
+ XORQ 40(DI), SI
+ XORQ 48(DI), BP
+ XORQ 72(DI), R15
+ XORQ 80(DI), SI
+ XORQ 88(DI), BP
+ XORQ 112(DI), R15
+ XORQ 120(DI), SI
+ XORQ 128(DI), BP
+ XORQ 152(DI), R15
+ XORQ 160(DI), SI
+ XORQ 168(DI), BP
+ MOVQ 176(DI), DX
+ MOVQ 184(DI), R8
+ XORQ 192(DI), R15
+
+ // Prepare round
+ MOVQ BP, BX
+ ROLQ $0x01, BX
+ MOVQ 16(DI), R12
+ XORQ 56(DI), DX
+ XORQ R15, BX
+ XORQ 96(DI), R12
+ XORQ 136(DI), DX
+ XORQ DX, R12
+ MOVQ R12, CX
+ ROLQ $0x01, CX
+ MOVQ 24(DI), R13
+ XORQ 64(DI), R8
+ XORQ SI, CX
+ XORQ 104(DI), R13
+ XORQ 144(DI), R8
+ XORQ R8, R13
+ MOVQ R13, DX
+ ROLQ $0x01, DX
+ MOVQ R15, R8
+ XORQ BP, DX
+ ROLQ $0x01, R8
+ MOVQ SI, R9
+ XORQ R12, R8
+ ROLQ $0x01, R9
+
+ // Result b
+ MOVQ (DI), R10
+ MOVQ 48(DI), R11
+ XORQ R13, R9
+ MOVQ 96(DI), R12
+ MOVQ 144(DI), R13
+ MOVQ 192(DI), R14
+ XORQ CX, R11
+ ROLQ $0x2c, R11
+ XORQ DX, R12
+ XORQ BX, R10
+ ROLQ $0x2b, R12
+ MOVQ R11, SI
+ MOVQ $0x0000000000000001, AX
+ ORQ R12, SI
+ XORQ R10, AX
+ XORQ AX, SI
+ MOVQ SI, (SP)
+ XORQ R9, R14
+ ROLQ $0x0e, R14
+ MOVQ R10, R15
+ ANDQ R11, R15
+ XORQ R14, R15
+ MOVQ R15, 32(SP)
+ XORQ R8, R13
+ ROLQ $0x15, R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 16(SP)
+ NOTQ R12
+ ORQ R10, R14
+ ORQ R13, R12
+ XORQ R13, R14
+ XORQ R11, R12
+ MOVQ R14, 24(SP)
+ MOVQ R12, 8(SP)
+ MOVQ R12, BP
+
+ // Result g
+ MOVQ 72(DI), R11
+ XORQ R9, R11
+ MOVQ 80(DI), R12
+ ROLQ $0x14, R11
+ XORQ BX, R12
+ ROLQ $0x03, R12
+ MOVQ 24(DI), R10
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ R8, R10
+ MOVQ 128(DI), R13
+ MOVQ 176(DI), R14
+ ROLQ $0x1c, R10
+ XORQ R10, AX
+ MOVQ AX, 40(SP)
+ XORQ AX, SI
+ XORQ CX, R13
+ ROLQ $0x2d, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 48(SP)
+ XORQ AX, BP
+ XORQ DX, R14
+ ROLQ $0x3d, R14
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 64(SP)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 72(SP)
+ NOTQ R14
+ XORQ R10, R15
+ ORQ R14, R13
+ XORQ R12, R13
+ MOVQ R13, 56(SP)
+
+ // Result k
+ MOVQ 8(DI), R10
+ MOVQ 56(DI), R11
+ MOVQ 104(DI), R12
+ MOVQ 152(DI), R13
+ MOVQ 160(DI), R14
+ XORQ DX, R11
+ ROLQ $0x06, R11
+ XORQ R8, R12
+ ROLQ $0x19, R12
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ CX, R10
+ ROLQ $0x01, R10
+ XORQ R10, AX
+ MOVQ AX, 80(SP)
+ XORQ AX, SI
+ XORQ R9, R13
+ ROLQ $0x08, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 88(SP)
+ XORQ AX, BP
+ XORQ BX, R14
+ ROLQ $0x12, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 96(SP)
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 104(SP)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 112(SP)
+ XORQ R10, R15
+
+ // Result m
+ MOVQ 40(DI), R11
+ XORQ BX, R11
+ MOVQ 88(DI), R12
+ ROLQ $0x24, R11
+ XORQ CX, R12
+ MOVQ 32(DI), R10
+ ROLQ $0x0a, R12
+ MOVQ R11, AX
+ MOVQ 136(DI), R13
+ ANDQ R12, AX
+ XORQ R9, R10
+ MOVQ 184(DI), R14
+ ROLQ $0x1b, R10
+ XORQ R10, AX
+ MOVQ AX, 120(SP)
+ XORQ AX, SI
+ XORQ DX, R13
+ ROLQ $0x0f, R13
+ MOVQ R12, AX
+ ORQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 128(SP)
+ XORQ AX, BP
+ XORQ R8, R14
+ ROLQ $0x38, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ORQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 136(SP)
+ ORQ R10, R11
+ XORQ R14, R11
+ MOVQ R11, 152(SP)
+ ANDQ R10, R14
+ XORQ R13, R14
+ MOVQ R14, 144(SP)
+ XORQ R11, R15
+
+ // Result s
+ MOVQ 16(DI), R10
+ MOVQ 64(DI), R11
+ MOVQ 112(DI), R12
+ XORQ DX, R10
+ MOVQ 120(DI), R13
+ ROLQ $0x3e, R10
+ XORQ R8, R11
+ MOVQ 168(DI), R14
+ ROLQ $0x37, R11
+ XORQ R9, R12
+ MOVQ R10, R9
+ XORQ CX, R14
+ ROLQ $0x02, R14
+ ANDQ R11, R9
+ XORQ R14, R9
+ MOVQ R9, 192(SP)
+ ROLQ $0x27, R12
+ XORQ R9, R15
+ NOTQ R11
+ XORQ BX, R13
+ MOVQ R11, BX
+ ANDQ R12, BX
+ XORQ R10, BX
+ MOVQ BX, 160(SP)
+ XORQ BX, SI
+ ROLQ $0x29, R13
+ MOVQ R12, CX
+ ORQ R13, CX
+ XORQ R11, CX
+ MOVQ CX, 168(SP)
+ XORQ CX, BP
+ MOVQ R13, DX
+ MOVQ R14, R8
+ ANDQ R14, DX
+ ORQ R10, R8
+ XORQ R12, DX
+ XORQ R13, R8
+ MOVQ DX, 176(SP)
+ MOVQ R8, 184(SP)
+
+ // Prepare round
+ MOVQ BP, BX
+ ROLQ $0x01, BX
+ MOVQ 16(SP), R12
+ XORQ 56(SP), DX
+ XORQ R15, BX
+ XORQ 96(SP), R12
+ XORQ 136(SP), DX
+ XORQ DX, R12
+ MOVQ R12, CX
+ ROLQ $0x01, CX
+ MOVQ 24(SP), R13
+ XORQ 64(SP), R8
+ XORQ SI, CX
+ XORQ 104(SP), R13
+ XORQ 144(SP), R8
+ XORQ R8, R13
+ MOVQ R13, DX
+ ROLQ $0x01, DX
+ MOVQ R15, R8
+ XORQ BP, DX
+ ROLQ $0x01, R8
+ MOVQ SI, R9
+ XORQ R12, R8
+ ROLQ $0x01, R9
+
+ // Result b
+ MOVQ (SP), R10
+ MOVQ 48(SP), R11
+ XORQ R13, R9
+ MOVQ 96(SP), R12
+ MOVQ 144(SP), R13
+ MOVQ 192(SP), R14
+ XORQ CX, R11
+ ROLQ $0x2c, R11
+ XORQ DX, R12
+ XORQ BX, R10
+ ROLQ $0x2b, R12
+ MOVQ R11, SI
+ MOVQ $0x0000000000008082, AX
+ ORQ R12, SI
+ XORQ R10, AX
+ XORQ AX, SI
+ MOVQ SI, (DI)
+ XORQ R9, R14
+ ROLQ $0x0e, R14
+ MOVQ R10, R15
+ ANDQ R11, R15
+ XORQ R14, R15
+ MOVQ R15, 32(DI)
+ XORQ R8, R13
+ ROLQ $0x15, R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 16(DI)
+ NOTQ R12
+ ORQ R10, R14
+ ORQ R13, R12
+ XORQ R13, R14
+ XORQ R11, R12
+ MOVQ R14, 24(DI)
+ MOVQ R12, 8(DI)
+ MOVQ R12, BP
+
+ // Result g
+ MOVQ 72(SP), R11
+ XORQ R9, R11
+ MOVQ 80(SP), R12
+ ROLQ $0x14, R11
+ XORQ BX, R12
+ ROLQ $0x03, R12
+ MOVQ 24(SP), R10
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ R8, R10
+ MOVQ 128(SP), R13
+ MOVQ 176(SP), R14
+ ROLQ $0x1c, R10
+ XORQ R10, AX
+ MOVQ AX, 40(DI)
+ XORQ AX, SI
+ XORQ CX, R13
+ ROLQ $0x2d, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 48(DI)
+ XORQ AX, BP
+ XORQ DX, R14
+ ROLQ $0x3d, R14
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 64(DI)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 72(DI)
+ NOTQ R14
+ XORQ R10, R15
+ ORQ R14, R13
+ XORQ R12, R13
+ MOVQ R13, 56(DI)
+
+ // Result k
+ MOVQ 8(SP), R10
+ MOVQ 56(SP), R11
+ MOVQ 104(SP), R12
+ MOVQ 152(SP), R13
+ MOVQ 160(SP), R14
+ XORQ DX, R11
+ ROLQ $0x06, R11
+ XORQ R8, R12
+ ROLQ $0x19, R12
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ CX, R10
+ ROLQ $0x01, R10
+ XORQ R10, AX
+ MOVQ AX, 80(DI)
+ XORQ AX, SI
+ XORQ R9, R13
+ ROLQ $0x08, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 88(DI)
+ XORQ AX, BP
+ XORQ BX, R14
+ ROLQ $0x12, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 96(DI)
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 104(DI)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 112(DI)
+ XORQ R10, R15
+
+ // Result m
+ MOVQ 40(SP), R11
+ XORQ BX, R11
+ MOVQ 88(SP), R12
+ ROLQ $0x24, R11
+ XORQ CX, R12
+ MOVQ 32(SP), R10
+ ROLQ $0x0a, R12
+ MOVQ R11, AX
+ MOVQ 136(SP), R13
+ ANDQ R12, AX
+ XORQ R9, R10
+ MOVQ 184(SP), R14
+ ROLQ $0x1b, R10
+ XORQ R10, AX
+ MOVQ AX, 120(DI)
+ XORQ AX, SI
+ XORQ DX, R13
+ ROLQ $0x0f, R13
+ MOVQ R12, AX
+ ORQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 128(DI)
+ XORQ AX, BP
+ XORQ R8, R14
+ ROLQ $0x38, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ORQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 136(DI)
+ ORQ R10, R11
+ XORQ R14, R11
+ MOVQ R11, 152(DI)
+ ANDQ R10, R14
+ XORQ R13, R14
+ MOVQ R14, 144(DI)
+ XORQ R11, R15
+
+ // Result s
+ MOVQ 16(SP), R10
+ MOVQ 64(SP), R11
+ MOVQ 112(SP), R12
+ XORQ DX, R10
+ MOVQ 120(SP), R13
+ ROLQ $0x3e, R10
+ XORQ R8, R11
+ MOVQ 168(SP), R14
+ ROLQ $0x37, R11
+ XORQ R9, R12
+ MOVQ R10, R9
+ XORQ CX, R14
+ ROLQ $0x02, R14
+ ANDQ R11, R9
+ XORQ R14, R9
+ MOVQ R9, 192(DI)
+ ROLQ $0x27, R12
+ XORQ R9, R15
+ NOTQ R11
+ XORQ BX, R13
+ MOVQ R11, BX
+ ANDQ R12, BX
+ XORQ R10, BX
+ MOVQ BX, 160(DI)
+ XORQ BX, SI
+ ROLQ $0x29, R13
+ MOVQ R12, CX
+ ORQ R13, CX
+ XORQ R11, CX
+ MOVQ CX, 168(DI)
+ XORQ CX, BP
+ MOVQ R13, DX
+ MOVQ R14, R8
+ ANDQ R14, DX
+ ORQ R10, R8
+ XORQ R12, DX
+ XORQ R13, R8
+ MOVQ DX, 176(DI)
+ MOVQ R8, 184(DI)
+
+ // Prepare round
+ MOVQ BP, BX
+ ROLQ $0x01, BX
+ MOVQ 16(DI), R12
+ XORQ 56(DI), DX
+ XORQ R15, BX
+ XORQ 96(DI), R12
+ XORQ 136(DI), DX
+ XORQ DX, R12
+ MOVQ R12, CX
+ ROLQ $0x01, CX
+ MOVQ 24(DI), R13
+ XORQ 64(DI), R8
+ XORQ SI, CX
+ XORQ 104(DI), R13
+ XORQ 144(DI), R8
+ XORQ R8, R13
+ MOVQ R13, DX
+ ROLQ $0x01, DX
+ MOVQ R15, R8
+ XORQ BP, DX
+ ROLQ $0x01, R8
+ MOVQ SI, R9
+ XORQ R12, R8
+ ROLQ $0x01, R9
+
+ // Result b
+ MOVQ (DI), R10
+ MOVQ 48(DI), R11
+ XORQ R13, R9
+ MOVQ 96(DI), R12
+ MOVQ 144(DI), R13
+ MOVQ 192(DI), R14
+ XORQ CX, R11
+ ROLQ $0x2c, R11
+ XORQ DX, R12
+ XORQ BX, R10
+ ROLQ $0x2b, R12
+ MOVQ R11, SI
+ MOVQ $0x800000000000808a, AX
+ ORQ R12, SI
+ XORQ R10, AX
+ XORQ AX, SI
+ MOVQ SI, (SP)
+ XORQ R9, R14
+ ROLQ $0x0e, R14
+ MOVQ R10, R15
+ ANDQ R11, R15
+ XORQ R14, R15
+ MOVQ R15, 32(SP)
+ XORQ R8, R13
+ ROLQ $0x15, R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 16(SP)
+ NOTQ R12
+ ORQ R10, R14
+ ORQ R13, R12
+ XORQ R13, R14
+ XORQ R11, R12
+ MOVQ R14, 24(SP)
+ MOVQ R12, 8(SP)
+ MOVQ R12, BP
+
+ // Result g
+ MOVQ 72(DI), R11
+ XORQ R9, R11
+ MOVQ 80(DI), R12
+ ROLQ $0x14, R11
+ XORQ BX, R12
+ ROLQ $0x03, R12
+ MOVQ 24(DI), R10
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ R8, R10
+ MOVQ 128(DI), R13
+ MOVQ 176(DI), R14
+ ROLQ $0x1c, R10
+ XORQ R10, AX
+ MOVQ AX, 40(SP)
+ XORQ AX, SI
+ XORQ CX, R13
+ ROLQ $0x2d, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 48(SP)
+ XORQ AX, BP
+ XORQ DX, R14
+ ROLQ $0x3d, R14
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 64(SP)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 72(SP)
+ NOTQ R14
+ XORQ R10, R15
+ ORQ R14, R13
+ XORQ R12, R13
+ MOVQ R13, 56(SP)
+
+ // Result k
+ MOVQ 8(DI), R10
+ MOVQ 56(DI), R11
+ MOVQ 104(DI), R12
+ MOVQ 152(DI), R13
+ MOVQ 160(DI), R14
+ XORQ DX, R11
+ ROLQ $0x06, R11
+ XORQ R8, R12
+ ROLQ $0x19, R12
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ CX, R10
+ ROLQ $0x01, R10
+ XORQ R10, AX
+ MOVQ AX, 80(SP)
+ XORQ AX, SI
+ XORQ R9, R13
+ ROLQ $0x08, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 88(SP)
+ XORQ AX, BP
+ XORQ BX, R14
+ ROLQ $0x12, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 96(SP)
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 104(SP)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 112(SP)
+ XORQ R10, R15
+
+ // Result m
+ MOVQ 40(DI), R11
+ XORQ BX, R11
+ MOVQ 88(DI), R12
+ ROLQ $0x24, R11
+ XORQ CX, R12
+ MOVQ 32(DI), R10
+ ROLQ $0x0a, R12
+ MOVQ R11, AX
+ MOVQ 136(DI), R13
+ ANDQ R12, AX
+ XORQ R9, R10
+ MOVQ 184(DI), R14
+ ROLQ $0x1b, R10
+ XORQ R10, AX
+ MOVQ AX, 120(SP)
+ XORQ AX, SI
+ XORQ DX, R13
+ ROLQ $0x0f, R13
+ MOVQ R12, AX
+ ORQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 128(SP)
+ XORQ AX, BP
+ XORQ R8, R14
+ ROLQ $0x38, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ORQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 136(SP)
+ ORQ R10, R11
+ XORQ R14, R11
+ MOVQ R11, 152(SP)
+ ANDQ R10, R14
+ XORQ R13, R14
+ MOVQ R14, 144(SP)
+ XORQ R11, R15
+
+ // Result s
+ MOVQ 16(DI), R10
+ MOVQ 64(DI), R11
+ MOVQ 112(DI), R12
+ XORQ DX, R10
+ MOVQ 120(DI), R13
+ ROLQ $0x3e, R10
+ XORQ R8, R11
+ MOVQ 168(DI), R14
+ ROLQ $0x37, R11
+ XORQ R9, R12
+ MOVQ R10, R9
+ XORQ CX, R14
+ ROLQ $0x02, R14
+ ANDQ R11, R9
+ XORQ R14, R9
+ MOVQ R9, 192(SP)
+ ROLQ $0x27, R12
+ XORQ R9, R15
+ NOTQ R11
+ XORQ BX, R13
+ MOVQ R11, BX
+ ANDQ R12, BX
+ XORQ R10, BX
+ MOVQ BX, 160(SP)
+ XORQ BX, SI
+ ROLQ $0x29, R13
+ MOVQ R12, CX
+ ORQ R13, CX
+ XORQ R11, CX
+ MOVQ CX, 168(SP)
+ XORQ CX, BP
+ MOVQ R13, DX
+ MOVQ R14, R8
+ ANDQ R14, DX
+ ORQ R10, R8
+ XORQ R12, DX
+ XORQ R13, R8
+ MOVQ DX, 176(SP)
+ MOVQ R8, 184(SP)
+
+ // Prepare round
+ MOVQ BP, BX
+ ROLQ $0x01, BX
+ MOVQ 16(SP), R12
+ XORQ 56(SP), DX
+ XORQ R15, BX
+ XORQ 96(SP), R12
+ XORQ 136(SP), DX
+ XORQ DX, R12
+ MOVQ R12, CX
+ ROLQ $0x01, CX
+ MOVQ 24(SP), R13
+ XORQ 64(SP), R8
+ XORQ SI, CX
+ XORQ 104(SP), R13
+ XORQ 144(SP), R8
+ XORQ R8, R13
+ MOVQ R13, DX
+ ROLQ $0x01, DX
+ MOVQ R15, R8
+ XORQ BP, DX
+ ROLQ $0x01, R8
+ MOVQ SI, R9
+ XORQ R12, R8
+ ROLQ $0x01, R9
+
+ // Result b
+ MOVQ (SP), R10
+ MOVQ 48(SP), R11
+ XORQ R13, R9
+ MOVQ 96(SP), R12
+ MOVQ 144(SP), R13
+ MOVQ 192(SP), R14
+ XORQ CX, R11
+ ROLQ $0x2c, R11
+ XORQ DX, R12
+ XORQ BX, R10
+ ROLQ $0x2b, R12
+ MOVQ R11, SI
+ MOVQ $0x8000000080008000, AX
+ ORQ R12, SI
+ XORQ R10, AX
+ XORQ AX, SI
+ MOVQ SI, (DI)
+ XORQ R9, R14
+ ROLQ $0x0e, R14
+ MOVQ R10, R15
+ ANDQ R11, R15
+ XORQ R14, R15
+ MOVQ R15, 32(DI)
+ XORQ R8, R13
+ ROLQ $0x15, R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 16(DI)
+ NOTQ R12
+ ORQ R10, R14
+ ORQ R13, R12
+ XORQ R13, R14
+ XORQ R11, R12
+ MOVQ R14, 24(DI)
+ MOVQ R12, 8(DI)
+ MOVQ R12, BP
+
+ // Result g
+ MOVQ 72(SP), R11
+ XORQ R9, R11
+ MOVQ 80(SP), R12
+ ROLQ $0x14, R11
+ XORQ BX, R12
+ ROLQ $0x03, R12
+ MOVQ 24(SP), R10
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ R8, R10
+ MOVQ 128(SP), R13
+ MOVQ 176(SP), R14
+ ROLQ $0x1c, R10
+ XORQ R10, AX
+ MOVQ AX, 40(DI)
+ XORQ AX, SI
+ XORQ CX, R13
+ ROLQ $0x2d, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 48(DI)
+ XORQ AX, BP
+ XORQ DX, R14
+ ROLQ $0x3d, R14
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 64(DI)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 72(DI)
+ NOTQ R14
+ XORQ R10, R15
+ ORQ R14, R13
+ XORQ R12, R13
+ MOVQ R13, 56(DI)
+
+ // Result k
+ MOVQ 8(SP), R10
+ MOVQ 56(SP), R11
+ MOVQ 104(SP), R12
+ MOVQ 152(SP), R13
+ MOVQ 160(SP), R14
+ XORQ DX, R11
+ ROLQ $0x06, R11
+ XORQ R8, R12
+ ROLQ $0x19, R12
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ CX, R10
+ ROLQ $0x01, R10
+ XORQ R10, AX
+ MOVQ AX, 80(DI)
+ XORQ AX, SI
+ XORQ R9, R13
+ ROLQ $0x08, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 88(DI)
+ XORQ AX, BP
+ XORQ BX, R14
+ ROLQ $0x12, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 96(DI)
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 104(DI)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 112(DI)
+ XORQ R10, R15
+
+ // Result m
+ MOVQ 40(SP), R11
+ XORQ BX, R11
+ MOVQ 88(SP), R12
+ ROLQ $0x24, R11
+ XORQ CX, R12
+ MOVQ 32(SP), R10
+ ROLQ $0x0a, R12
+ MOVQ R11, AX
+ MOVQ 136(SP), R13
+ ANDQ R12, AX
+ XORQ R9, R10
+ MOVQ 184(SP), R14
+ ROLQ $0x1b, R10
+ XORQ R10, AX
+ MOVQ AX, 120(DI)
+ XORQ AX, SI
+ XORQ DX, R13
+ ROLQ $0x0f, R13
+ MOVQ R12, AX
+ ORQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 128(DI)
+ XORQ AX, BP
+ XORQ R8, R14
+ ROLQ $0x38, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ORQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 136(DI)
+ ORQ R10, R11
+ XORQ R14, R11
+ MOVQ R11, 152(DI)
+ ANDQ R10, R14
+ XORQ R13, R14
+ MOVQ R14, 144(DI)
+ XORQ R11, R15
+
+ // Result s
+ MOVQ 16(SP), R10
+ MOVQ 64(SP), R11
+ MOVQ 112(SP), R12
+ XORQ DX, R10
+ MOVQ 120(SP), R13
+ ROLQ $0x3e, R10
+ XORQ R8, R11
+ MOVQ 168(SP), R14
+ ROLQ $0x37, R11
+ XORQ R9, R12
+ MOVQ R10, R9
+ XORQ CX, R14
+ ROLQ $0x02, R14
+ ANDQ R11, R9
+ XORQ R14, R9
+ MOVQ R9, 192(DI)
+ ROLQ $0x27, R12
+ XORQ R9, R15
+ NOTQ R11
+ XORQ BX, R13
+ MOVQ R11, BX
+ ANDQ R12, BX
+ XORQ R10, BX
+ MOVQ BX, 160(DI)
+ XORQ BX, SI
+ ROLQ $0x29, R13
+ MOVQ R12, CX
+ ORQ R13, CX
+ XORQ R11, CX
+ MOVQ CX, 168(DI)
+ XORQ CX, BP
+ MOVQ R13, DX
+ MOVQ R14, R8
+ ANDQ R14, DX
+ ORQ R10, R8
+ XORQ R12, DX
+ XORQ R13, R8
+ MOVQ DX, 176(DI)
+ MOVQ R8, 184(DI)
+
+ // Prepare round
+ MOVQ BP, BX
+ ROLQ $0x01, BX
+ MOVQ 16(DI), R12
+ XORQ 56(DI), DX
+ XORQ R15, BX
+ XORQ 96(DI), R12
+ XORQ 136(DI), DX
+ XORQ DX, R12
+ MOVQ R12, CX
+ ROLQ $0x01, CX
+ MOVQ 24(DI), R13
+ XORQ 64(DI), R8
+ XORQ SI, CX
+ XORQ 104(DI), R13
+ XORQ 144(DI), R8
+ XORQ R8, R13
+ MOVQ R13, DX
+ ROLQ $0x01, DX
+ MOVQ R15, R8
+ XORQ BP, DX
+ ROLQ $0x01, R8
+ MOVQ SI, R9
+ XORQ R12, R8
+ ROLQ $0x01, R9
+
+ // Result b
+ MOVQ (DI), R10
+ MOVQ 48(DI), R11
+ XORQ R13, R9
+ MOVQ 96(DI), R12
+ MOVQ 144(DI), R13
+ MOVQ 192(DI), R14
+ XORQ CX, R11
+ ROLQ $0x2c, R11
+ XORQ DX, R12
+ XORQ BX, R10
+ ROLQ $0x2b, R12
+ MOVQ R11, SI
+ MOVQ $0x000000000000808b, AX
+ ORQ R12, SI
+ XORQ R10, AX
+ XORQ AX, SI
+ MOVQ SI, (SP)
+ XORQ R9, R14
+ ROLQ $0x0e, R14
+ MOVQ R10, R15
+ ANDQ R11, R15
+ XORQ R14, R15
+ MOVQ R15, 32(SP)
+ XORQ R8, R13
+ ROLQ $0x15, R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 16(SP)
+ NOTQ R12
+ ORQ R10, R14
+ ORQ R13, R12
+ XORQ R13, R14
+ XORQ R11, R12
+ MOVQ R14, 24(SP)
+ MOVQ R12, 8(SP)
+ MOVQ R12, BP
+
+ // Result g
+ MOVQ 72(DI), R11
+ XORQ R9, R11
+ MOVQ 80(DI), R12
+ ROLQ $0x14, R11
+ XORQ BX, R12
+ ROLQ $0x03, R12
+ MOVQ 24(DI), R10
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ R8, R10
+ MOVQ 128(DI), R13
+ MOVQ 176(DI), R14
+ ROLQ $0x1c, R10
+ XORQ R10, AX
+ MOVQ AX, 40(SP)
+ XORQ AX, SI
+ XORQ CX, R13
+ ROLQ $0x2d, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 48(SP)
+ XORQ AX, BP
+ XORQ DX, R14
+ ROLQ $0x3d, R14
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 64(SP)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 72(SP)
+ NOTQ R14
+ XORQ R10, R15
+ ORQ R14, R13
+ XORQ R12, R13
+ MOVQ R13, 56(SP)
+
+ // Result k
+ MOVQ 8(DI), R10
+ MOVQ 56(DI), R11
+ MOVQ 104(DI), R12
+ MOVQ 152(DI), R13
+ MOVQ 160(DI), R14
+ XORQ DX, R11
+ ROLQ $0x06, R11
+ XORQ R8, R12
+ ROLQ $0x19, R12
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ CX, R10
+ ROLQ $0x01, R10
+ XORQ R10, AX
+ MOVQ AX, 80(SP)
+ XORQ AX, SI
+ XORQ R9, R13
+ ROLQ $0x08, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 88(SP)
+ XORQ AX, BP
+ XORQ BX, R14
+ ROLQ $0x12, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 96(SP)
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 104(SP)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 112(SP)
+ XORQ R10, R15
+
+ // Result m
+ MOVQ 40(DI), R11
+ XORQ BX, R11
+ MOVQ 88(DI), R12
+ ROLQ $0x24, R11
+ XORQ CX, R12
+ MOVQ 32(DI), R10
+ ROLQ $0x0a, R12
+ MOVQ R11, AX
+ MOVQ 136(DI), R13
+ ANDQ R12, AX
+ XORQ R9, R10
+ MOVQ 184(DI), R14
+ ROLQ $0x1b, R10
+ XORQ R10, AX
+ MOVQ AX, 120(SP)
+ XORQ AX, SI
+ XORQ DX, R13
+ ROLQ $0x0f, R13
+ MOVQ R12, AX
+ ORQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 128(SP)
+ XORQ AX, BP
+ XORQ R8, R14
+ ROLQ $0x38, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ORQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 136(SP)
+ ORQ R10, R11
+ XORQ R14, R11
+ MOVQ R11, 152(SP)
+ ANDQ R10, R14
+ XORQ R13, R14
+ MOVQ R14, 144(SP)
+ XORQ R11, R15
+
+ // Result s
+ MOVQ 16(DI), R10
+ MOVQ 64(DI), R11
+ MOVQ 112(DI), R12
+ XORQ DX, R10
+ MOVQ 120(DI), R13
+ ROLQ $0x3e, R10
+ XORQ R8, R11
+ MOVQ 168(DI), R14
+ ROLQ $0x37, R11
+ XORQ R9, R12
+ MOVQ R10, R9
+ XORQ CX, R14
+ ROLQ $0x02, R14
+ ANDQ R11, R9
+ XORQ R14, R9
+ MOVQ R9, 192(SP)
+ ROLQ $0x27, R12
+ XORQ R9, R15
+ NOTQ R11
+ XORQ BX, R13
+ MOVQ R11, BX
+ ANDQ R12, BX
+ XORQ R10, BX
+ MOVQ BX, 160(SP)
+ XORQ BX, SI
+ ROLQ $0x29, R13
+ MOVQ R12, CX
+ ORQ R13, CX
+ XORQ R11, CX
+ MOVQ CX, 168(SP)
+ XORQ CX, BP
+ MOVQ R13, DX
+ MOVQ R14, R8
+ ANDQ R14, DX
+ ORQ R10, R8
+ XORQ R12, DX
+ XORQ R13, R8
+ MOVQ DX, 176(SP)
+ MOVQ R8, 184(SP)
+
+ // Prepare round
+ MOVQ BP, BX
+ ROLQ $0x01, BX
+ MOVQ 16(SP), R12
+ XORQ 56(SP), DX
+ XORQ R15, BX
+ XORQ 96(SP), R12
+ XORQ 136(SP), DX
+ XORQ DX, R12
+ MOVQ R12, CX
+ ROLQ $0x01, CX
+ MOVQ 24(SP), R13
+ XORQ 64(SP), R8
+ XORQ SI, CX
+ XORQ 104(SP), R13
+ XORQ 144(SP), R8
+ XORQ R8, R13
+ MOVQ R13, DX
+ ROLQ $0x01, DX
+ MOVQ R15, R8
+ XORQ BP, DX
+ ROLQ $0x01, R8
+ MOVQ SI, R9
+ XORQ R12, R8
+ ROLQ $0x01, R9
+
+ // Result b
+ MOVQ (SP), R10
+ MOVQ 48(SP), R11
+ XORQ R13, R9
+ MOVQ 96(SP), R12
+ MOVQ 144(SP), R13
+ MOVQ 192(SP), R14
+ XORQ CX, R11
+ ROLQ $0x2c, R11
+ XORQ DX, R12
+ XORQ BX, R10
+ ROLQ $0x2b, R12
+ MOVQ R11, SI
+ MOVQ $0x0000000080000001, AX
+ ORQ R12, SI
+ XORQ R10, AX
+ XORQ AX, SI
+ MOVQ SI, (DI)
+ XORQ R9, R14
+ ROLQ $0x0e, R14
+ MOVQ R10, R15
+ ANDQ R11, R15
+ XORQ R14, R15
+ MOVQ R15, 32(DI)
+ XORQ R8, R13
+ ROLQ $0x15, R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 16(DI)
+ NOTQ R12
+ ORQ R10, R14
+ ORQ R13, R12
+ XORQ R13, R14
+ XORQ R11, R12
+ MOVQ R14, 24(DI)
+ MOVQ R12, 8(DI)
+ MOVQ R12, BP
+
+ // Result g
+ MOVQ 72(SP), R11
+ XORQ R9, R11
+ MOVQ 80(SP), R12
+ ROLQ $0x14, R11
+ XORQ BX, R12
+ ROLQ $0x03, R12
+ MOVQ 24(SP), R10
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ R8, R10
+ MOVQ 128(SP), R13
+ MOVQ 176(SP), R14
+ ROLQ $0x1c, R10
+ XORQ R10, AX
+ MOVQ AX, 40(DI)
+ XORQ AX, SI
+ XORQ CX, R13
+ ROLQ $0x2d, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 48(DI)
+ XORQ AX, BP
+ XORQ DX, R14
+ ROLQ $0x3d, R14
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 64(DI)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 72(DI)
+ NOTQ R14
+ XORQ R10, R15
+ ORQ R14, R13
+ XORQ R12, R13
+ MOVQ R13, 56(DI)
+
+ // Result k
+ MOVQ 8(SP), R10
+ MOVQ 56(SP), R11
+ MOVQ 104(SP), R12
+ MOVQ 152(SP), R13
+ MOVQ 160(SP), R14
+ XORQ DX, R11
+ ROLQ $0x06, R11
+ XORQ R8, R12
+ ROLQ $0x19, R12
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ CX, R10
+ ROLQ $0x01, R10
+ XORQ R10, AX
+ MOVQ AX, 80(DI)
+ XORQ AX, SI
+ XORQ R9, R13
+ ROLQ $0x08, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 88(DI)
+ XORQ AX, BP
+ XORQ BX, R14
+ ROLQ $0x12, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 96(DI)
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 104(DI)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 112(DI)
+ XORQ R10, R15
+
+ // Result m
+ MOVQ 40(SP), R11
+ XORQ BX, R11
+ MOVQ 88(SP), R12
+ ROLQ $0x24, R11
+ XORQ CX, R12
+ MOVQ 32(SP), R10
+ ROLQ $0x0a, R12
+ MOVQ R11, AX
+ MOVQ 136(SP), R13
+ ANDQ R12, AX
+ XORQ R9, R10
+ MOVQ 184(SP), R14
+ ROLQ $0x1b, R10
+ XORQ R10, AX
+ MOVQ AX, 120(DI)
+ XORQ AX, SI
+ XORQ DX, R13
+ ROLQ $0x0f, R13
+ MOVQ R12, AX
+ ORQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 128(DI)
+ XORQ AX, BP
+ XORQ R8, R14
+ ROLQ $0x38, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ORQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 136(DI)
+ ORQ R10, R11
+ XORQ R14, R11
+ MOVQ R11, 152(DI)
+ ANDQ R10, R14
+ XORQ R13, R14
+ MOVQ R14, 144(DI)
+ XORQ R11, R15
+
+ // Result s
+ MOVQ 16(SP), R10
+ MOVQ 64(SP), R11
+ MOVQ 112(SP), R12
+ XORQ DX, R10
+ MOVQ 120(SP), R13
+ ROLQ $0x3e, R10
+ XORQ R8, R11
+ MOVQ 168(SP), R14
+ ROLQ $0x37, R11
+ XORQ R9, R12
+ MOVQ R10, R9
+ XORQ CX, R14
+ ROLQ $0x02, R14
+ ANDQ R11, R9
+ XORQ R14, R9
+ MOVQ R9, 192(DI)
+ ROLQ $0x27, R12
+ XORQ R9, R15
+ NOTQ R11
+ XORQ BX, R13
+ MOVQ R11, BX
+ ANDQ R12, BX
+ XORQ R10, BX
+ MOVQ BX, 160(DI)
+ XORQ BX, SI
+ ROLQ $0x29, R13
+ MOVQ R12, CX
+ ORQ R13, CX
+ XORQ R11, CX
+ MOVQ CX, 168(DI)
+ XORQ CX, BP
+ MOVQ R13, DX
+ MOVQ R14, R8
+ ANDQ R14, DX
+ ORQ R10, R8
+ XORQ R12, DX
+ XORQ R13, R8
+ MOVQ DX, 176(DI)
+ MOVQ R8, 184(DI)
+
+ // Prepare round
+ MOVQ BP, BX
+ ROLQ $0x01, BX
+ MOVQ 16(DI), R12
+ XORQ 56(DI), DX
+ XORQ R15, BX
+ XORQ 96(DI), R12
+ XORQ 136(DI), DX
+ XORQ DX, R12
+ MOVQ R12, CX
+ ROLQ $0x01, CX
+ MOVQ 24(DI), R13
+ XORQ 64(DI), R8
+ XORQ SI, CX
+ XORQ 104(DI), R13
+ XORQ 144(DI), R8
+ XORQ R8, R13
+ MOVQ R13, DX
+ ROLQ $0x01, DX
+ MOVQ R15, R8
+ XORQ BP, DX
+ ROLQ $0x01, R8
+ MOVQ SI, R9
+ XORQ R12, R8
+ ROLQ $0x01, R9
+
+ // Result b
+ MOVQ (DI), R10
+ MOVQ 48(DI), R11
+ XORQ R13, R9
+ MOVQ 96(DI), R12
+ MOVQ 144(DI), R13
+ MOVQ 192(DI), R14
+ XORQ CX, R11
+ ROLQ $0x2c, R11
+ XORQ DX, R12
+ XORQ BX, R10
+ ROLQ $0x2b, R12
+ MOVQ R11, SI
+ MOVQ $0x8000000080008081, AX
+ ORQ R12, SI
+ XORQ R10, AX
+ XORQ AX, SI
+ MOVQ SI, (SP)
+ XORQ R9, R14
+ ROLQ $0x0e, R14
+ MOVQ R10, R15
+ ANDQ R11, R15
+ XORQ R14, R15
+ MOVQ R15, 32(SP)
+ XORQ R8, R13
+ ROLQ $0x15, R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 16(SP)
+ NOTQ R12
+ ORQ R10, R14
+ ORQ R13, R12
+ XORQ R13, R14
+ XORQ R11, R12
+ MOVQ R14, 24(SP)
+ MOVQ R12, 8(SP)
+ MOVQ R12, BP
+
+ // Result g
+ MOVQ 72(DI), R11
+ XORQ R9, R11
+ MOVQ 80(DI), R12
+ ROLQ $0x14, R11
+ XORQ BX, R12
+ ROLQ $0x03, R12
+ MOVQ 24(DI), R10
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ R8, R10
+ MOVQ 128(DI), R13
+ MOVQ 176(DI), R14
+ ROLQ $0x1c, R10
+ XORQ R10, AX
+ MOVQ AX, 40(SP)
+ XORQ AX, SI
+ XORQ CX, R13
+ ROLQ $0x2d, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 48(SP)
+ XORQ AX, BP
+ XORQ DX, R14
+ ROLQ $0x3d, R14
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 64(SP)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 72(SP)
+ NOTQ R14
+ XORQ R10, R15
+ ORQ R14, R13
+ XORQ R12, R13
+ MOVQ R13, 56(SP)
+
+ // Result k
+ MOVQ 8(DI), R10
+ MOVQ 56(DI), R11
+ MOVQ 104(DI), R12
+ MOVQ 152(DI), R13
+ MOVQ 160(DI), R14
+ XORQ DX, R11
+ ROLQ $0x06, R11
+ XORQ R8, R12
+ ROLQ $0x19, R12
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ CX, R10
+ ROLQ $0x01, R10
+ XORQ R10, AX
+ MOVQ AX, 80(SP)
+ XORQ AX, SI
+ XORQ R9, R13
+ ROLQ $0x08, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 88(SP)
+ XORQ AX, BP
+ XORQ BX, R14
+ ROLQ $0x12, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 96(SP)
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 104(SP)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 112(SP)
+ XORQ R10, R15
+
+ // Result m
+ MOVQ 40(DI), R11
+ XORQ BX, R11
+ MOVQ 88(DI), R12
+ ROLQ $0x24, R11
+ XORQ CX, R12
+ MOVQ 32(DI), R10
+ ROLQ $0x0a, R12
+ MOVQ R11, AX
+ MOVQ 136(DI), R13
+ ANDQ R12, AX
+ XORQ R9, R10
+ MOVQ 184(DI), R14
+ ROLQ $0x1b, R10
+ XORQ R10, AX
+ MOVQ AX, 120(SP)
+ XORQ AX, SI
+ XORQ DX, R13
+ ROLQ $0x0f, R13
+ MOVQ R12, AX
+ ORQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 128(SP)
+ XORQ AX, BP
+ XORQ R8, R14
+ ROLQ $0x38, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ORQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 136(SP)
+ ORQ R10, R11
+ XORQ R14, R11
+ MOVQ R11, 152(SP)
+ ANDQ R10, R14
+ XORQ R13, R14
+ MOVQ R14, 144(SP)
+ XORQ R11, R15
+
+ // Result s
+ MOVQ 16(DI), R10
+ MOVQ 64(DI), R11
+ MOVQ 112(DI), R12
+ XORQ DX, R10
+ MOVQ 120(DI), R13
+ ROLQ $0x3e, R10
+ XORQ R8, R11
+ MOVQ 168(DI), R14
+ ROLQ $0x37, R11
+ XORQ R9, R12
+ MOVQ R10, R9
+ XORQ CX, R14
+ ROLQ $0x02, R14
+ ANDQ R11, R9
+ XORQ R14, R9
+ MOVQ R9, 192(SP)
+ ROLQ $0x27, R12
+ XORQ R9, R15
+ NOTQ R11
+ XORQ BX, R13
+ MOVQ R11, BX
+ ANDQ R12, BX
+ XORQ R10, BX
+ MOVQ BX, 160(SP)
+ XORQ BX, SI
+ ROLQ $0x29, R13
+ MOVQ R12, CX
+ ORQ R13, CX
+ XORQ R11, CX
+ MOVQ CX, 168(SP)
+ XORQ CX, BP
+ MOVQ R13, DX
+ MOVQ R14, R8
+ ANDQ R14, DX
+ ORQ R10, R8
+ XORQ R12, DX
+ XORQ R13, R8
+ MOVQ DX, 176(SP)
+ MOVQ R8, 184(SP)
+
+ // Prepare round
+ MOVQ BP, BX
+ ROLQ $0x01, BX
+ MOVQ 16(SP), R12
+ XORQ 56(SP), DX
+ XORQ R15, BX
+ XORQ 96(SP), R12
+ XORQ 136(SP), DX
+ XORQ DX, R12
+ MOVQ R12, CX
+ ROLQ $0x01, CX
+ MOVQ 24(SP), R13
+ XORQ 64(SP), R8
+ XORQ SI, CX
+ XORQ 104(SP), R13
+ XORQ 144(SP), R8
+ XORQ R8, R13
+ MOVQ R13, DX
+ ROLQ $0x01, DX
+ MOVQ R15, R8
+ XORQ BP, DX
+ ROLQ $0x01, R8
+ MOVQ SI, R9
+ XORQ R12, R8
+ ROLQ $0x01, R9
+
+ // Result b
+ MOVQ (SP), R10
+ MOVQ 48(SP), R11
+ XORQ R13, R9
+ MOVQ 96(SP), R12
+ MOVQ 144(SP), R13
+ MOVQ 192(SP), R14
+ XORQ CX, R11
+ ROLQ $0x2c, R11
+ XORQ DX, R12
+ XORQ BX, R10
+ ROLQ $0x2b, R12
+ MOVQ R11, SI
+ MOVQ $0x8000000000008009, AX
+ ORQ R12, SI
+ XORQ R10, AX
+ XORQ AX, SI
+ MOVQ SI, (DI)
+ XORQ R9, R14
+ ROLQ $0x0e, R14
+ MOVQ R10, R15
+ ANDQ R11, R15
+ XORQ R14, R15
+ MOVQ R15, 32(DI)
+ XORQ R8, R13
+ ROLQ $0x15, R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 16(DI)
+ NOTQ R12
+ ORQ R10, R14
+ ORQ R13, R12
+ XORQ R13, R14
+ XORQ R11, R12
+ MOVQ R14, 24(DI)
+ MOVQ R12, 8(DI)
+ MOVQ R12, BP
+
+ // Result g
+ MOVQ 72(SP), R11
+ XORQ R9, R11
+ MOVQ 80(SP), R12
+ ROLQ $0x14, R11
+ XORQ BX, R12
+ ROLQ $0x03, R12
+ MOVQ 24(SP), R10
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ R8, R10
+ MOVQ 128(SP), R13
+ MOVQ 176(SP), R14
+ ROLQ $0x1c, R10
+ XORQ R10, AX
+ MOVQ AX, 40(DI)
+ XORQ AX, SI
+ XORQ CX, R13
+ ROLQ $0x2d, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 48(DI)
+ XORQ AX, BP
+ XORQ DX, R14
+ ROLQ $0x3d, R14
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 64(DI)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 72(DI)
+ NOTQ R14
+ XORQ R10, R15
+ ORQ R14, R13
+ XORQ R12, R13
+ MOVQ R13, 56(DI)
+
+ // Result k
+ MOVQ 8(SP), R10
+ MOVQ 56(SP), R11
+ MOVQ 104(SP), R12
+ MOVQ 152(SP), R13
+ MOVQ 160(SP), R14
+ XORQ DX, R11
+ ROLQ $0x06, R11
+ XORQ R8, R12
+ ROLQ $0x19, R12
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ CX, R10
+ ROLQ $0x01, R10
+ XORQ R10, AX
+ MOVQ AX, 80(DI)
+ XORQ AX, SI
+ XORQ R9, R13
+ ROLQ $0x08, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 88(DI)
+ XORQ AX, BP
+ XORQ BX, R14
+ ROLQ $0x12, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 96(DI)
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 104(DI)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 112(DI)
+ XORQ R10, R15
+
+ // Result m
+ MOVQ 40(SP), R11
+ XORQ BX, R11
+ MOVQ 88(SP), R12
+ ROLQ $0x24, R11
+ XORQ CX, R12
+ MOVQ 32(SP), R10
+ ROLQ $0x0a, R12
+ MOVQ R11, AX
+ MOVQ 136(SP), R13
+ ANDQ R12, AX
+ XORQ R9, R10
+ MOVQ 184(SP), R14
+ ROLQ $0x1b, R10
+ XORQ R10, AX
+ MOVQ AX, 120(DI)
+ XORQ AX, SI
+ XORQ DX, R13
+ ROLQ $0x0f, R13
+ MOVQ R12, AX
+ ORQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 128(DI)
+ XORQ AX, BP
+ XORQ R8, R14
+ ROLQ $0x38, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ORQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 136(DI)
+ ORQ R10, R11
+ XORQ R14, R11
+ MOVQ R11, 152(DI)
+ ANDQ R10, R14
+ XORQ R13, R14
+ MOVQ R14, 144(DI)
+ XORQ R11, R15
+
+ // Result s
+ MOVQ 16(SP), R10
+ MOVQ 64(SP), R11
+ MOVQ 112(SP), R12
+ XORQ DX, R10
+ MOVQ 120(SP), R13
+ ROLQ $0x3e, R10
+ XORQ R8, R11
+ MOVQ 168(SP), R14
+ ROLQ $0x37, R11
+ XORQ R9, R12
+ MOVQ R10, R9
+ XORQ CX, R14
+ ROLQ $0x02, R14
+ ANDQ R11, R9
+ XORQ R14, R9
+ MOVQ R9, 192(DI)
+ ROLQ $0x27, R12
+ XORQ R9, R15
+ NOTQ R11
+ XORQ BX, R13
+ MOVQ R11, BX
+ ANDQ R12, BX
+ XORQ R10, BX
+ MOVQ BX, 160(DI)
+ XORQ BX, SI
+ ROLQ $0x29, R13
+ MOVQ R12, CX
+ ORQ R13, CX
+ XORQ R11, CX
+ MOVQ CX, 168(DI)
+ XORQ CX, BP
+ MOVQ R13, DX
+ MOVQ R14, R8
+ ANDQ R14, DX
+ ORQ R10, R8
+ XORQ R12, DX
+ XORQ R13, R8
+ MOVQ DX, 176(DI)
+ MOVQ R8, 184(DI)
+
+ // Prepare round
+ MOVQ BP, BX
+ ROLQ $0x01, BX
+ MOVQ 16(DI), R12
+ XORQ 56(DI), DX
+ XORQ R15, BX
+ XORQ 96(DI), R12
+ XORQ 136(DI), DX
+ XORQ DX, R12
+ MOVQ R12, CX
+ ROLQ $0x01, CX
+ MOVQ 24(DI), R13
+ XORQ 64(DI), R8
+ XORQ SI, CX
+ XORQ 104(DI), R13
+ XORQ 144(DI), R8
+ XORQ R8, R13
+ MOVQ R13, DX
+ ROLQ $0x01, DX
+ MOVQ R15, R8
+ XORQ BP, DX
+ ROLQ $0x01, R8
+ MOVQ SI, R9
+ XORQ R12, R8
+ ROLQ $0x01, R9
+
+ // Result b
+ MOVQ (DI), R10
+ MOVQ 48(DI), R11
+ XORQ R13, R9
+ MOVQ 96(DI), R12
+ MOVQ 144(DI), R13
+ MOVQ 192(DI), R14
+ XORQ CX, R11
+ ROLQ $0x2c, R11
+ XORQ DX, R12
+ XORQ BX, R10
+ ROLQ $0x2b, R12
+ MOVQ R11, SI
+ MOVQ $0x000000000000008a, AX
+ ORQ R12, SI
+ XORQ R10, AX
+ XORQ AX, SI
+ MOVQ SI, (SP)
+ XORQ R9, R14
+ ROLQ $0x0e, R14
+ MOVQ R10, R15
+ ANDQ R11, R15
+ XORQ R14, R15
+ MOVQ R15, 32(SP)
+ XORQ R8, R13
+ ROLQ $0x15, R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 16(SP)
+ NOTQ R12
+ ORQ R10, R14
+ ORQ R13, R12
+ XORQ R13, R14
+ XORQ R11, R12
+ MOVQ R14, 24(SP)
+ MOVQ R12, 8(SP)
+ MOVQ R12, BP
+
+ // Result g
+ MOVQ 72(DI), R11
+ XORQ R9, R11
+ MOVQ 80(DI), R12
+ ROLQ $0x14, R11
+ XORQ BX, R12
+ ROLQ $0x03, R12
+ MOVQ 24(DI), R10
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ R8, R10
+ MOVQ 128(DI), R13
+ MOVQ 176(DI), R14
+ ROLQ $0x1c, R10
+ XORQ R10, AX
+ MOVQ AX, 40(SP)
+ XORQ AX, SI
+ XORQ CX, R13
+ ROLQ $0x2d, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 48(SP)
+ XORQ AX, BP
+ XORQ DX, R14
+ ROLQ $0x3d, R14
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 64(SP)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 72(SP)
+ NOTQ R14
+ XORQ R10, R15
+ ORQ R14, R13
+ XORQ R12, R13
+ MOVQ R13, 56(SP)
+
+ // Result k
+ MOVQ 8(DI), R10
+ MOVQ 56(DI), R11
+ MOVQ 104(DI), R12
+ MOVQ 152(DI), R13
+ MOVQ 160(DI), R14
+ XORQ DX, R11
+ ROLQ $0x06, R11
+ XORQ R8, R12
+ ROLQ $0x19, R12
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ CX, R10
+ ROLQ $0x01, R10
+ XORQ R10, AX
+ MOVQ AX, 80(SP)
+ XORQ AX, SI
+ XORQ R9, R13
+ ROLQ $0x08, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 88(SP)
+ XORQ AX, BP
+ XORQ BX, R14
+ ROLQ $0x12, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 96(SP)
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 104(SP)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 112(SP)
+ XORQ R10, R15
+
+ // Result m
+ MOVQ 40(DI), R11
+ XORQ BX, R11
+ MOVQ 88(DI), R12
+ ROLQ $0x24, R11
+ XORQ CX, R12
+ MOVQ 32(DI), R10
+ ROLQ $0x0a, R12
+ MOVQ R11, AX
+ MOVQ 136(DI), R13
+ ANDQ R12, AX
+ XORQ R9, R10
+ MOVQ 184(DI), R14
+ ROLQ $0x1b, R10
+ XORQ R10, AX
+ MOVQ AX, 120(SP)
+ XORQ AX, SI
+ XORQ DX, R13
+ ROLQ $0x0f, R13
+ MOVQ R12, AX
+ ORQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 128(SP)
+ XORQ AX, BP
+ XORQ R8, R14
+ ROLQ $0x38, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ORQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 136(SP)
+ ORQ R10, R11
+ XORQ R14, R11
+ MOVQ R11, 152(SP)
+ ANDQ R10, R14
+ XORQ R13, R14
+ MOVQ R14, 144(SP)
+ XORQ R11, R15
+
+ // Result s
+ MOVQ 16(DI), R10
+ MOVQ 64(DI), R11
+ MOVQ 112(DI), R12
+ XORQ DX, R10
+ MOVQ 120(DI), R13
+ ROLQ $0x3e, R10
+ XORQ R8, R11
+ MOVQ 168(DI), R14
+ ROLQ $0x37, R11
+ XORQ R9, R12
+ MOVQ R10, R9
+ XORQ CX, R14
+ ROLQ $0x02, R14
+ ANDQ R11, R9
+ XORQ R14, R9
+ MOVQ R9, 192(SP)
+ ROLQ $0x27, R12
+ XORQ R9, R15
+ NOTQ R11
+ XORQ BX, R13
+ MOVQ R11, BX
+ ANDQ R12, BX
+ XORQ R10, BX
+ MOVQ BX, 160(SP)
+ XORQ BX, SI
+ ROLQ $0x29, R13
+ MOVQ R12, CX
+ ORQ R13, CX
+ XORQ R11, CX
+ MOVQ CX, 168(SP)
+ XORQ CX, BP
+ MOVQ R13, DX
+ MOVQ R14, R8
+ ANDQ R14, DX
+ ORQ R10, R8
+ XORQ R12, DX
+ XORQ R13, R8
+ MOVQ DX, 176(SP)
+ MOVQ R8, 184(SP)
+
+ // Prepare round
+ MOVQ BP, BX
+ ROLQ $0x01, BX
+ MOVQ 16(SP), R12
+ XORQ 56(SP), DX
+ XORQ R15, BX
+ XORQ 96(SP), R12
+ XORQ 136(SP), DX
+ XORQ DX, R12
+ MOVQ R12, CX
+ ROLQ $0x01, CX
+ MOVQ 24(SP), R13
+ XORQ 64(SP), R8
+ XORQ SI, CX
+ XORQ 104(SP), R13
+ XORQ 144(SP), R8
+ XORQ R8, R13
+ MOVQ R13, DX
+ ROLQ $0x01, DX
+ MOVQ R15, R8
+ XORQ BP, DX
+ ROLQ $0x01, R8
+ MOVQ SI, R9
+ XORQ R12, R8
+ ROLQ $0x01, R9
+
+ // Result b
+ MOVQ (SP), R10
+ MOVQ 48(SP), R11
+ XORQ R13, R9
+ MOVQ 96(SP), R12
+ MOVQ 144(SP), R13
+ MOVQ 192(SP), R14
+ XORQ CX, R11
+ ROLQ $0x2c, R11
+ XORQ DX, R12
+ XORQ BX, R10
+ ROLQ $0x2b, R12
+ MOVQ R11, SI
+ MOVQ $0x0000000000000088, AX
+ ORQ R12, SI
+ XORQ R10, AX
+ XORQ AX, SI
+ MOVQ SI, (DI)
+ XORQ R9, R14
+ ROLQ $0x0e, R14
+ MOVQ R10, R15
+ ANDQ R11, R15
+ XORQ R14, R15
+ MOVQ R15, 32(DI)
+ XORQ R8, R13
+ ROLQ $0x15, R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 16(DI)
+ NOTQ R12
+ ORQ R10, R14
+ ORQ R13, R12
+ XORQ R13, R14
+ XORQ R11, R12
+ MOVQ R14, 24(DI)
+ MOVQ R12, 8(DI)
+ MOVQ R12, BP
+
+ // Result g
+ MOVQ 72(SP), R11
+ XORQ R9, R11
+ MOVQ 80(SP), R12
+ ROLQ $0x14, R11
+ XORQ BX, R12
+ ROLQ $0x03, R12
+ MOVQ 24(SP), R10
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ R8, R10
+ MOVQ 128(SP), R13
+ MOVQ 176(SP), R14
+ ROLQ $0x1c, R10
+ XORQ R10, AX
+ MOVQ AX, 40(DI)
+ XORQ AX, SI
+ XORQ CX, R13
+ ROLQ $0x2d, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 48(DI)
+ XORQ AX, BP
+ XORQ DX, R14
+ ROLQ $0x3d, R14
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 64(DI)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 72(DI)
+ NOTQ R14
+ XORQ R10, R15
+ ORQ R14, R13
+ XORQ R12, R13
+ MOVQ R13, 56(DI)
+
+ // Result k
+ MOVQ 8(SP), R10
+ MOVQ 56(SP), R11
+ MOVQ 104(SP), R12
+ MOVQ 152(SP), R13
+ MOVQ 160(SP), R14
+ XORQ DX, R11
+ ROLQ $0x06, R11
+ XORQ R8, R12
+ ROLQ $0x19, R12
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ CX, R10
+ ROLQ $0x01, R10
+ XORQ R10, AX
+ MOVQ AX, 80(DI)
+ XORQ AX, SI
+ XORQ R9, R13
+ ROLQ $0x08, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 88(DI)
+ XORQ AX, BP
+ XORQ BX, R14
+ ROLQ $0x12, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 96(DI)
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 104(DI)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 112(DI)
+ XORQ R10, R15
+
+ // Result m
+ MOVQ 40(SP), R11
+ XORQ BX, R11
+ MOVQ 88(SP), R12
+ ROLQ $0x24, R11
+ XORQ CX, R12
+ MOVQ 32(SP), R10
+ ROLQ $0x0a, R12
+ MOVQ R11, AX
+ MOVQ 136(SP), R13
+ ANDQ R12, AX
+ XORQ R9, R10
+ MOVQ 184(SP), R14
+ ROLQ $0x1b, R10
+ XORQ R10, AX
+ MOVQ AX, 120(DI)
+ XORQ AX, SI
+ XORQ DX, R13
+ ROLQ $0x0f, R13
+ MOVQ R12, AX
+ ORQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 128(DI)
+ XORQ AX, BP
+ XORQ R8, R14
+ ROLQ $0x38, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ORQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 136(DI)
+ ORQ R10, R11
+ XORQ R14, R11
+ MOVQ R11, 152(DI)
+ ANDQ R10, R14
+ XORQ R13, R14
+ MOVQ R14, 144(DI)
+ XORQ R11, R15
+
+ // Result s
+ MOVQ 16(SP), R10
+ MOVQ 64(SP), R11
+ MOVQ 112(SP), R12
+ XORQ DX, R10
+ MOVQ 120(SP), R13
+ ROLQ $0x3e, R10
+ XORQ R8, R11
+ MOVQ 168(SP), R14
+ ROLQ $0x37, R11
+ XORQ R9, R12
+ MOVQ R10, R9
+ XORQ CX, R14
+ ROLQ $0x02, R14
+ ANDQ R11, R9
+ XORQ R14, R9
+ MOVQ R9, 192(DI)
+ ROLQ $0x27, R12
+ XORQ R9, R15
+ NOTQ R11
+ XORQ BX, R13
+ MOVQ R11, BX
+ ANDQ R12, BX
+ XORQ R10, BX
+ MOVQ BX, 160(DI)
+ XORQ BX, SI
+ ROLQ $0x29, R13
+ MOVQ R12, CX
+ ORQ R13, CX
+ XORQ R11, CX
+ MOVQ CX, 168(DI)
+ XORQ CX, BP
+ MOVQ R13, DX
+ MOVQ R14, R8
+ ANDQ R14, DX
+ ORQ R10, R8
+ XORQ R12, DX
+ XORQ R13, R8
+ MOVQ DX, 176(DI)
+ MOVQ R8, 184(DI)
+
+ // Prepare round
+ MOVQ BP, BX
+ ROLQ $0x01, BX
+ MOVQ 16(DI), R12
+ XORQ 56(DI), DX
+ XORQ R15, BX
+ XORQ 96(DI), R12
+ XORQ 136(DI), DX
+ XORQ DX, R12
+ MOVQ R12, CX
+ ROLQ $0x01, CX
+ MOVQ 24(DI), R13
+ XORQ 64(DI), R8
+ XORQ SI, CX
+ XORQ 104(DI), R13
+ XORQ 144(DI), R8
+ XORQ R8, R13
+ MOVQ R13, DX
+ ROLQ $0x01, DX
+ MOVQ R15, R8
+ XORQ BP, DX
+ ROLQ $0x01, R8
+ MOVQ SI, R9
+ XORQ R12, R8
+ ROLQ $0x01, R9
+
+ // Result b
+ MOVQ (DI), R10
+ MOVQ 48(DI), R11
+ XORQ R13, R9
+ MOVQ 96(DI), R12
+ MOVQ 144(DI), R13
+ MOVQ 192(DI), R14
+ XORQ CX, R11
+ ROLQ $0x2c, R11
+ XORQ DX, R12
+ XORQ BX, R10
+ ROLQ $0x2b, R12
+ MOVQ R11, SI
+ MOVQ $0x0000000080008009, AX
+ ORQ R12, SI
+ XORQ R10, AX
+ XORQ AX, SI
+ MOVQ SI, (SP)
+ XORQ R9, R14
+ ROLQ $0x0e, R14
+ MOVQ R10, R15
+ ANDQ R11, R15
+ XORQ R14, R15
+ MOVQ R15, 32(SP)
+ XORQ R8, R13
+ ROLQ $0x15, R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 16(SP)
+ NOTQ R12
+ ORQ R10, R14
+ ORQ R13, R12
+ XORQ R13, R14
+ XORQ R11, R12
+ MOVQ R14, 24(SP)
+ MOVQ R12, 8(SP)
+ MOVQ R12, BP
+
+ // Result g
+ MOVQ 72(DI), R11
+ XORQ R9, R11
+ MOVQ 80(DI), R12
+ ROLQ $0x14, R11
+ XORQ BX, R12
+ ROLQ $0x03, R12
+ MOVQ 24(DI), R10
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ R8, R10
+ MOVQ 128(DI), R13
+ MOVQ 176(DI), R14
+ ROLQ $0x1c, R10
+ XORQ R10, AX
+ MOVQ AX, 40(SP)
+ XORQ AX, SI
+ XORQ CX, R13
+ ROLQ $0x2d, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 48(SP)
+ XORQ AX, BP
+ XORQ DX, R14
+ ROLQ $0x3d, R14
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 64(SP)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 72(SP)
+ NOTQ R14
+ XORQ R10, R15
+ ORQ R14, R13
+ XORQ R12, R13
+ MOVQ R13, 56(SP)
+
+ // Result k
+ MOVQ 8(DI), R10
+ MOVQ 56(DI), R11
+ MOVQ 104(DI), R12
+ MOVQ 152(DI), R13
+ MOVQ 160(DI), R14
+ XORQ DX, R11
+ ROLQ $0x06, R11
+ XORQ R8, R12
+ ROLQ $0x19, R12
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ CX, R10
+ ROLQ $0x01, R10
+ XORQ R10, AX
+ MOVQ AX, 80(SP)
+ XORQ AX, SI
+ XORQ R9, R13
+ ROLQ $0x08, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 88(SP)
+ XORQ AX, BP
+ XORQ BX, R14
+ ROLQ $0x12, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 96(SP)
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 104(SP)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 112(SP)
+ XORQ R10, R15
+
+ // Result m
+ MOVQ 40(DI), R11
+ XORQ BX, R11
+ MOVQ 88(DI), R12
+ ROLQ $0x24, R11
+ XORQ CX, R12
+ MOVQ 32(DI), R10
+ ROLQ $0x0a, R12
+ MOVQ R11, AX
+ MOVQ 136(DI), R13
+ ANDQ R12, AX
+ XORQ R9, R10
+ MOVQ 184(DI), R14
+ ROLQ $0x1b, R10
+ XORQ R10, AX
+ MOVQ AX, 120(SP)
+ XORQ AX, SI
+ XORQ DX, R13
+ ROLQ $0x0f, R13
+ MOVQ R12, AX
+ ORQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 128(SP)
+ XORQ AX, BP
+ XORQ R8, R14
+ ROLQ $0x38, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ORQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 136(SP)
+ ORQ R10, R11
+ XORQ R14, R11
+ MOVQ R11, 152(SP)
+ ANDQ R10, R14
+ XORQ R13, R14
+ MOVQ R14, 144(SP)
+ XORQ R11, R15
+
+ // Result s
+ MOVQ 16(DI), R10
+ MOVQ 64(DI), R11
+ MOVQ 112(DI), R12
+ XORQ DX, R10
+ MOVQ 120(DI), R13
+ ROLQ $0x3e, R10
+ XORQ R8, R11
+ MOVQ 168(DI), R14
+ ROLQ $0x37, R11
+ XORQ R9, R12
+ MOVQ R10, R9
+ XORQ CX, R14
+ ROLQ $0x02, R14
+ ANDQ R11, R9
+ XORQ R14, R9
+ MOVQ R9, 192(SP)
+ ROLQ $0x27, R12
+ XORQ R9, R15
+ NOTQ R11
+ XORQ BX, R13
+ MOVQ R11, BX
+ ANDQ R12, BX
+ XORQ R10, BX
+ MOVQ BX, 160(SP)
+ XORQ BX, SI
+ ROLQ $0x29, R13
+ MOVQ R12, CX
+ ORQ R13, CX
+ XORQ R11, CX
+ MOVQ CX, 168(SP)
+ XORQ CX, BP
+ MOVQ R13, DX
+ MOVQ R14, R8
+ ANDQ R14, DX
+ ORQ R10, R8
+ XORQ R12, DX
+ XORQ R13, R8
+ MOVQ DX, 176(SP)
+ MOVQ R8, 184(SP)
+
+ // Prepare round
+ MOVQ BP, BX
+ ROLQ $0x01, BX
+ MOVQ 16(SP), R12
+ XORQ 56(SP), DX
+ XORQ R15, BX
+ XORQ 96(SP), R12
+ XORQ 136(SP), DX
+ XORQ DX, R12
+ MOVQ R12, CX
+ ROLQ $0x01, CX
+ MOVQ 24(SP), R13
+ XORQ 64(SP), R8
+ XORQ SI, CX
+ XORQ 104(SP), R13
+ XORQ 144(SP), R8
+ XORQ R8, R13
+ MOVQ R13, DX
+ ROLQ $0x01, DX
+ MOVQ R15, R8
+ XORQ BP, DX
+ ROLQ $0x01, R8
+ MOVQ SI, R9
+ XORQ R12, R8
+ ROLQ $0x01, R9
+
+ // Result b
+ MOVQ (SP), R10
+ MOVQ 48(SP), R11
+ XORQ R13, R9
+ MOVQ 96(SP), R12
+ MOVQ 144(SP), R13
+ MOVQ 192(SP), R14
+ XORQ CX, R11
+ ROLQ $0x2c, R11
+ XORQ DX, R12
+ XORQ BX, R10
+ ROLQ $0x2b, R12
+ MOVQ R11, SI
+ MOVQ $0x000000008000000a, AX
+ ORQ R12, SI
+ XORQ R10, AX
+ XORQ AX, SI
+ MOVQ SI, (DI)
+ XORQ R9, R14
+ ROLQ $0x0e, R14
+ MOVQ R10, R15
+ ANDQ R11, R15
+ XORQ R14, R15
+ MOVQ R15, 32(DI)
+ XORQ R8, R13
+ ROLQ $0x15, R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 16(DI)
+ NOTQ R12
+ ORQ R10, R14
+ ORQ R13, R12
+ XORQ R13, R14
+ XORQ R11, R12
+ MOVQ R14, 24(DI)
+ MOVQ R12, 8(DI)
+ MOVQ R12, BP
+
+ // Result g
+ MOVQ 72(SP), R11
+ XORQ R9, R11
+ MOVQ 80(SP), R12
+ ROLQ $0x14, R11
+ XORQ BX, R12
+ ROLQ $0x03, R12
+ MOVQ 24(SP), R10
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ R8, R10
+ MOVQ 128(SP), R13
+ MOVQ 176(SP), R14
+ ROLQ $0x1c, R10
+ XORQ R10, AX
+ MOVQ AX, 40(DI)
+ XORQ AX, SI
+ XORQ CX, R13
+ ROLQ $0x2d, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 48(DI)
+ XORQ AX, BP
+ XORQ DX, R14
+ ROLQ $0x3d, R14
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 64(DI)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 72(DI)
+ NOTQ R14
+ XORQ R10, R15
+ ORQ R14, R13
+ XORQ R12, R13
+ MOVQ R13, 56(DI)
+
+ // Result k
+ MOVQ 8(SP), R10
+ MOVQ 56(SP), R11
+ MOVQ 104(SP), R12
+ MOVQ 152(SP), R13
+ MOVQ 160(SP), R14
+ XORQ DX, R11
+ ROLQ $0x06, R11
+ XORQ R8, R12
+ ROLQ $0x19, R12
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ CX, R10
+ ROLQ $0x01, R10
+ XORQ R10, AX
+ MOVQ AX, 80(DI)
+ XORQ AX, SI
+ XORQ R9, R13
+ ROLQ $0x08, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 88(DI)
+ XORQ AX, BP
+ XORQ BX, R14
+ ROLQ $0x12, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 96(DI)
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 104(DI)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 112(DI)
+ XORQ R10, R15
+
+ // Result m
+ MOVQ 40(SP), R11
+ XORQ BX, R11
+ MOVQ 88(SP), R12
+ ROLQ $0x24, R11
+ XORQ CX, R12
+ MOVQ 32(SP), R10
+ ROLQ $0x0a, R12
+ MOVQ R11, AX
+ MOVQ 136(SP), R13
+ ANDQ R12, AX
+ XORQ R9, R10
+ MOVQ 184(SP), R14
+ ROLQ $0x1b, R10
+ XORQ R10, AX
+ MOVQ AX, 120(DI)
+ XORQ AX, SI
+ XORQ DX, R13
+ ROLQ $0x0f, R13
+ MOVQ R12, AX
+ ORQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 128(DI)
+ XORQ AX, BP
+ XORQ R8, R14
+ ROLQ $0x38, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ORQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 136(DI)
+ ORQ R10, R11
+ XORQ R14, R11
+ MOVQ R11, 152(DI)
+ ANDQ R10, R14
+ XORQ R13, R14
+ MOVQ R14, 144(DI)
+ XORQ R11, R15
+
+ // Result s
+ MOVQ 16(SP), R10
+ MOVQ 64(SP), R11
+ MOVQ 112(SP), R12
+ XORQ DX, R10
+ MOVQ 120(SP), R13
+ ROLQ $0x3e, R10
+ XORQ R8, R11
+ MOVQ 168(SP), R14
+ ROLQ $0x37, R11
+ XORQ R9, R12
+ MOVQ R10, R9
+ XORQ CX, R14
+ ROLQ $0x02, R14
+ ANDQ R11, R9
+ XORQ R14, R9
+ MOVQ R9, 192(DI)
+ ROLQ $0x27, R12
+ XORQ R9, R15
+ NOTQ R11
+ XORQ BX, R13
+ MOVQ R11, BX
+ ANDQ R12, BX
+ XORQ R10, BX
+ MOVQ BX, 160(DI)
+ XORQ BX, SI
+ ROLQ $0x29, R13
+ MOVQ R12, CX
+ ORQ R13, CX
+ XORQ R11, CX
+ MOVQ CX, 168(DI)
+ XORQ CX, BP
+ MOVQ R13, DX
+ MOVQ R14, R8
+ ANDQ R14, DX
+ ORQ R10, R8
+ XORQ R12, DX
+ XORQ R13, R8
+ MOVQ DX, 176(DI)
+ MOVQ R8, 184(DI)
+
+ // Prepare round
+ MOVQ BP, BX
+ ROLQ $0x01, BX
+ MOVQ 16(DI), R12
+ XORQ 56(DI), DX
+ XORQ R15, BX
+ XORQ 96(DI), R12
+ XORQ 136(DI), DX
+ XORQ DX, R12
+ MOVQ R12, CX
+ ROLQ $0x01, CX
+ MOVQ 24(DI), R13
+ XORQ 64(DI), R8
+ XORQ SI, CX
+ XORQ 104(DI), R13
+ XORQ 144(DI), R8
+ XORQ R8, R13
+ MOVQ R13, DX
+ ROLQ $0x01, DX
+ MOVQ R15, R8
+ XORQ BP, DX
+ ROLQ $0x01, R8
+ MOVQ SI, R9
+ XORQ R12, R8
+ ROLQ $0x01, R9
+
+ // Result b
+ MOVQ (DI), R10
+ MOVQ 48(DI), R11
+ XORQ R13, R9
+ MOVQ 96(DI), R12
+ MOVQ 144(DI), R13
+ MOVQ 192(DI), R14
+ XORQ CX, R11
+ ROLQ $0x2c, R11
+ XORQ DX, R12
+ XORQ BX, R10
+ ROLQ $0x2b, R12
+ MOVQ R11, SI
+ MOVQ $0x000000008000808b, AX
+ ORQ R12, SI
+ XORQ R10, AX
+ XORQ AX, SI
+ MOVQ SI, (SP)
+ XORQ R9, R14
+ ROLQ $0x0e, R14
+ MOVQ R10, R15
+ ANDQ R11, R15
+ XORQ R14, R15
+ MOVQ R15, 32(SP)
+ XORQ R8, R13
+ ROLQ $0x15, R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 16(SP)
+ NOTQ R12
+ ORQ R10, R14
+ ORQ R13, R12
+ XORQ R13, R14
+ XORQ R11, R12
+ MOVQ R14, 24(SP)
+ MOVQ R12, 8(SP)
+ MOVQ R12, BP
+
+ // Result g
+ MOVQ 72(DI), R11
+ XORQ R9, R11
+ MOVQ 80(DI), R12
+ ROLQ $0x14, R11
+ XORQ BX, R12
+ ROLQ $0x03, R12
+ MOVQ 24(DI), R10
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ R8, R10
+ MOVQ 128(DI), R13
+ MOVQ 176(DI), R14
+ ROLQ $0x1c, R10
+ XORQ R10, AX
+ MOVQ AX, 40(SP)
+ XORQ AX, SI
+ XORQ CX, R13
+ ROLQ $0x2d, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 48(SP)
+ XORQ AX, BP
+ XORQ DX, R14
+ ROLQ $0x3d, R14
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 64(SP)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 72(SP)
+ NOTQ R14
+ XORQ R10, R15
+ ORQ R14, R13
+ XORQ R12, R13
+ MOVQ R13, 56(SP)
+
+ // Result k
+ MOVQ 8(DI), R10
+ MOVQ 56(DI), R11
+ MOVQ 104(DI), R12
+ MOVQ 152(DI), R13
+ MOVQ 160(DI), R14
+ XORQ DX, R11
+ ROLQ $0x06, R11
+ XORQ R8, R12
+ ROLQ $0x19, R12
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ CX, R10
+ ROLQ $0x01, R10
+ XORQ R10, AX
+ MOVQ AX, 80(SP)
+ XORQ AX, SI
+ XORQ R9, R13
+ ROLQ $0x08, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 88(SP)
+ XORQ AX, BP
+ XORQ BX, R14
+ ROLQ $0x12, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 96(SP)
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 104(SP)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 112(SP)
+ XORQ R10, R15
+
+ // Result m
+ MOVQ 40(DI), R11
+ XORQ BX, R11
+ MOVQ 88(DI), R12
+ ROLQ $0x24, R11
+ XORQ CX, R12
+ MOVQ 32(DI), R10
+ ROLQ $0x0a, R12
+ MOVQ R11, AX
+ MOVQ 136(DI), R13
+ ANDQ R12, AX
+ XORQ R9, R10
+ MOVQ 184(DI), R14
+ ROLQ $0x1b, R10
+ XORQ R10, AX
+ MOVQ AX, 120(SP)
+ XORQ AX, SI
+ XORQ DX, R13
+ ROLQ $0x0f, R13
+ MOVQ R12, AX
+ ORQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 128(SP)
+ XORQ AX, BP
+ XORQ R8, R14
+ ROLQ $0x38, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ORQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 136(SP)
+ ORQ R10, R11
+ XORQ R14, R11
+ MOVQ R11, 152(SP)
+ ANDQ R10, R14
+ XORQ R13, R14
+ MOVQ R14, 144(SP)
+ XORQ R11, R15
+
+ // Result s
+ MOVQ 16(DI), R10
+ MOVQ 64(DI), R11
+ MOVQ 112(DI), R12
+ XORQ DX, R10
+ MOVQ 120(DI), R13
+ ROLQ $0x3e, R10
+ XORQ R8, R11
+ MOVQ 168(DI), R14
+ ROLQ $0x37, R11
+ XORQ R9, R12
+ MOVQ R10, R9
+ XORQ CX, R14
+ ROLQ $0x02, R14
+ ANDQ R11, R9
+ XORQ R14, R9
+ MOVQ R9, 192(SP)
+ ROLQ $0x27, R12
+ XORQ R9, R15
+ NOTQ R11
+ XORQ BX, R13
+ MOVQ R11, BX
+ ANDQ R12, BX
+ XORQ R10, BX
+ MOVQ BX, 160(SP)
+ XORQ BX, SI
+ ROLQ $0x29, R13
+ MOVQ R12, CX
+ ORQ R13, CX
+ XORQ R11, CX
+ MOVQ CX, 168(SP)
+ XORQ CX, BP
+ MOVQ R13, DX
+ MOVQ R14, R8
+ ANDQ R14, DX
+ ORQ R10, R8
+ XORQ R12, DX
+ XORQ R13, R8
+ MOVQ DX, 176(SP)
+ MOVQ R8, 184(SP)
+
+ // Prepare round
+ MOVQ BP, BX
+ ROLQ $0x01, BX
+ MOVQ 16(SP), R12
+ XORQ 56(SP), DX
+ XORQ R15, BX
+ XORQ 96(SP), R12
+ XORQ 136(SP), DX
+ XORQ DX, R12
+ MOVQ R12, CX
+ ROLQ $0x01, CX
+ MOVQ 24(SP), R13
+ XORQ 64(SP), R8
+ XORQ SI, CX
+ XORQ 104(SP), R13
+ XORQ 144(SP), R8
+ XORQ R8, R13
+ MOVQ R13, DX
+ ROLQ $0x01, DX
+ MOVQ R15, R8
+ XORQ BP, DX
+ ROLQ $0x01, R8
+ MOVQ SI, R9
+ XORQ R12, R8
+ ROLQ $0x01, R9
+
+ // Result b
+ MOVQ (SP), R10
+ MOVQ 48(SP), R11
+ XORQ R13, R9
+ MOVQ 96(SP), R12
+ MOVQ 144(SP), R13
+ MOVQ 192(SP), R14
+ XORQ CX, R11
+ ROLQ $0x2c, R11
+ XORQ DX, R12
+ XORQ BX, R10
+ ROLQ $0x2b, R12
+ MOVQ R11, SI
+ MOVQ $0x800000000000008b, AX
+ ORQ R12, SI
+ XORQ R10, AX
+ XORQ AX, SI
+ MOVQ SI, (DI)
+ XORQ R9, R14
+ ROLQ $0x0e, R14
+ MOVQ R10, R15
+ ANDQ R11, R15
+ XORQ R14, R15
+ MOVQ R15, 32(DI)
+ XORQ R8, R13
+ ROLQ $0x15, R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 16(DI)
+ NOTQ R12
+ ORQ R10, R14
+ ORQ R13, R12
+ XORQ R13, R14
+ XORQ R11, R12
+ MOVQ R14, 24(DI)
+ MOVQ R12, 8(DI)
+ MOVQ R12, BP
+
+ // Result g
+ MOVQ 72(SP), R11
+ XORQ R9, R11
+ MOVQ 80(SP), R12
+ ROLQ $0x14, R11
+ XORQ BX, R12
+ ROLQ $0x03, R12
+ MOVQ 24(SP), R10
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ R8, R10
+ MOVQ 128(SP), R13
+ MOVQ 176(SP), R14
+ ROLQ $0x1c, R10
+ XORQ R10, AX
+ MOVQ AX, 40(DI)
+ XORQ AX, SI
+ XORQ CX, R13
+ ROLQ $0x2d, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 48(DI)
+ XORQ AX, BP
+ XORQ DX, R14
+ ROLQ $0x3d, R14
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 64(DI)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 72(DI)
+ NOTQ R14
+ XORQ R10, R15
+ ORQ R14, R13
+ XORQ R12, R13
+ MOVQ R13, 56(DI)
+
+ // Result k
+ MOVQ 8(SP), R10
+ MOVQ 56(SP), R11
+ MOVQ 104(SP), R12
+ MOVQ 152(SP), R13
+ MOVQ 160(SP), R14
+ XORQ DX, R11
+ ROLQ $0x06, R11
+ XORQ R8, R12
+ ROLQ $0x19, R12
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ CX, R10
+ ROLQ $0x01, R10
+ XORQ R10, AX
+ MOVQ AX, 80(DI)
+ XORQ AX, SI
+ XORQ R9, R13
+ ROLQ $0x08, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 88(DI)
+ XORQ AX, BP
+ XORQ BX, R14
+ ROLQ $0x12, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 96(DI)
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 104(DI)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 112(DI)
+ XORQ R10, R15
+
+ // Result m
+ MOVQ 40(SP), R11
+ XORQ BX, R11
+ MOVQ 88(SP), R12
+ ROLQ $0x24, R11
+ XORQ CX, R12
+ MOVQ 32(SP), R10
+ ROLQ $0x0a, R12
+ MOVQ R11, AX
+ MOVQ 136(SP), R13
+ ANDQ R12, AX
+ XORQ R9, R10
+ MOVQ 184(SP), R14
+ ROLQ $0x1b, R10
+ XORQ R10, AX
+ MOVQ AX, 120(DI)
+ XORQ AX, SI
+ XORQ DX, R13
+ ROLQ $0x0f, R13
+ MOVQ R12, AX
+ ORQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 128(DI)
+ XORQ AX, BP
+ XORQ R8, R14
+ ROLQ $0x38, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ORQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 136(DI)
+ ORQ R10, R11
+ XORQ R14, R11
+ MOVQ R11, 152(DI)
+ ANDQ R10, R14
+ XORQ R13, R14
+ MOVQ R14, 144(DI)
+ XORQ R11, R15
+
+ // Result s
+ MOVQ 16(SP), R10
+ MOVQ 64(SP), R11
+ MOVQ 112(SP), R12
+ XORQ DX, R10
+ MOVQ 120(SP), R13
+ ROLQ $0x3e, R10
+ XORQ R8, R11
+ MOVQ 168(SP), R14
+ ROLQ $0x37, R11
+ XORQ R9, R12
+ MOVQ R10, R9
+ XORQ CX, R14
+ ROLQ $0x02, R14
+ ANDQ R11, R9
+ XORQ R14, R9
+ MOVQ R9, 192(DI)
+ ROLQ $0x27, R12
+ XORQ R9, R15
+ NOTQ R11
+ XORQ BX, R13
+ MOVQ R11, BX
+ ANDQ R12, BX
+ XORQ R10, BX
+ MOVQ BX, 160(DI)
+ XORQ BX, SI
+ ROLQ $0x29, R13
+ MOVQ R12, CX
+ ORQ R13, CX
+ XORQ R11, CX
+ MOVQ CX, 168(DI)
+ XORQ CX, BP
+ MOVQ R13, DX
+ MOVQ R14, R8
+ ANDQ R14, DX
+ ORQ R10, R8
+ XORQ R12, DX
+ XORQ R13, R8
+ MOVQ DX, 176(DI)
+ MOVQ R8, 184(DI)
+
+ // Prepare round
+ MOVQ BP, BX
+ ROLQ $0x01, BX
+ MOVQ 16(DI), R12
+ XORQ 56(DI), DX
+ XORQ R15, BX
+ XORQ 96(DI), R12
+ XORQ 136(DI), DX
+ XORQ DX, R12
+ MOVQ R12, CX
+ ROLQ $0x01, CX
+ MOVQ 24(DI), R13
+ XORQ 64(DI), R8
+ XORQ SI, CX
+ XORQ 104(DI), R13
+ XORQ 144(DI), R8
+ XORQ R8, R13
+ MOVQ R13, DX
+ ROLQ $0x01, DX
+ MOVQ R15, R8
+ XORQ BP, DX
+ ROLQ $0x01, R8
+ MOVQ SI, R9
+ XORQ R12, R8
+ ROLQ $0x01, R9
+
+ // Result b
+ MOVQ (DI), R10
+ MOVQ 48(DI), R11
+ XORQ R13, R9
+ MOVQ 96(DI), R12
+ MOVQ 144(DI), R13
+ MOVQ 192(DI), R14
+ XORQ CX, R11
+ ROLQ $0x2c, R11
+ XORQ DX, R12
+ XORQ BX, R10
+ ROLQ $0x2b, R12
+ MOVQ R11, SI
+ MOVQ $0x8000000000008089, AX
+ ORQ R12, SI
+ XORQ R10, AX
+ XORQ AX, SI
+ MOVQ SI, (SP)
+ XORQ R9, R14
+ ROLQ $0x0e, R14
+ MOVQ R10, R15
+ ANDQ R11, R15
+ XORQ R14, R15
+ MOVQ R15, 32(SP)
+ XORQ R8, R13
+ ROLQ $0x15, R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 16(SP)
+ NOTQ R12
+ ORQ R10, R14
+ ORQ R13, R12
+ XORQ R13, R14
+ XORQ R11, R12
+ MOVQ R14, 24(SP)
+ MOVQ R12, 8(SP)
+ MOVQ R12, BP
+
+ // Result g
+ MOVQ 72(DI), R11
+ XORQ R9, R11
+ MOVQ 80(DI), R12
+ ROLQ $0x14, R11
+ XORQ BX, R12
+ ROLQ $0x03, R12
+ MOVQ 24(DI), R10
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ R8, R10
+ MOVQ 128(DI), R13
+ MOVQ 176(DI), R14
+ ROLQ $0x1c, R10
+ XORQ R10, AX
+ MOVQ AX, 40(SP)
+ XORQ AX, SI
+ XORQ CX, R13
+ ROLQ $0x2d, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 48(SP)
+ XORQ AX, BP
+ XORQ DX, R14
+ ROLQ $0x3d, R14
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 64(SP)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 72(SP)
+ NOTQ R14
+ XORQ R10, R15
+ ORQ R14, R13
+ XORQ R12, R13
+ MOVQ R13, 56(SP)
+
+ // Result k
+ MOVQ 8(DI), R10
+ MOVQ 56(DI), R11
+ MOVQ 104(DI), R12
+ MOVQ 152(DI), R13
+ MOVQ 160(DI), R14
+ XORQ DX, R11
+ ROLQ $0x06, R11
+ XORQ R8, R12
+ ROLQ $0x19, R12
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ CX, R10
+ ROLQ $0x01, R10
+ XORQ R10, AX
+ MOVQ AX, 80(SP)
+ XORQ AX, SI
+ XORQ R9, R13
+ ROLQ $0x08, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 88(SP)
+ XORQ AX, BP
+ XORQ BX, R14
+ ROLQ $0x12, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 96(SP)
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 104(SP)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 112(SP)
+ XORQ R10, R15
+
+ // Result m
+ MOVQ 40(DI), R11
+ XORQ BX, R11
+ MOVQ 88(DI), R12
+ ROLQ $0x24, R11
+ XORQ CX, R12
+ MOVQ 32(DI), R10
+ ROLQ $0x0a, R12
+ MOVQ R11, AX
+ MOVQ 136(DI), R13
+ ANDQ R12, AX
+ XORQ R9, R10
+ MOVQ 184(DI), R14
+ ROLQ $0x1b, R10
+ XORQ R10, AX
+ MOVQ AX, 120(SP)
+ XORQ AX, SI
+ XORQ DX, R13
+ ROLQ $0x0f, R13
+ MOVQ R12, AX
+ ORQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 128(SP)
+ XORQ AX, BP
+ XORQ R8, R14
+ ROLQ $0x38, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ORQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 136(SP)
+ ORQ R10, R11
+ XORQ R14, R11
+ MOVQ R11, 152(SP)
+ ANDQ R10, R14
+ XORQ R13, R14
+ MOVQ R14, 144(SP)
+ XORQ R11, R15
+
+ // Result s
+ MOVQ 16(DI), R10
+ MOVQ 64(DI), R11
+ MOVQ 112(DI), R12
+ XORQ DX, R10
+ MOVQ 120(DI), R13
+ ROLQ $0x3e, R10
+ XORQ R8, R11
+ MOVQ 168(DI), R14
+ ROLQ $0x37, R11
+ XORQ R9, R12
+ MOVQ R10, R9
+ XORQ CX, R14
+ ROLQ $0x02, R14
+ ANDQ R11, R9
+ XORQ R14, R9
+ MOVQ R9, 192(SP)
+ ROLQ $0x27, R12
+ XORQ R9, R15
+ NOTQ R11
+ XORQ BX, R13
+ MOVQ R11, BX
+ ANDQ R12, BX
+ XORQ R10, BX
+ MOVQ BX, 160(SP)
+ XORQ BX, SI
+ ROLQ $0x29, R13
+ MOVQ R12, CX
+ ORQ R13, CX
+ XORQ R11, CX
+ MOVQ CX, 168(SP)
+ XORQ CX, BP
+ MOVQ R13, DX
+ MOVQ R14, R8
+ ANDQ R14, DX
+ ORQ R10, R8
+ XORQ R12, DX
+ XORQ R13, R8
+ MOVQ DX, 176(SP)
+ MOVQ R8, 184(SP)
+
+ // Prepare round
+ MOVQ BP, BX
+ ROLQ $0x01, BX
+ MOVQ 16(SP), R12
+ XORQ 56(SP), DX
+ XORQ R15, BX
+ XORQ 96(SP), R12
+ XORQ 136(SP), DX
+ XORQ DX, R12
+ MOVQ R12, CX
+ ROLQ $0x01, CX
+ MOVQ 24(SP), R13
+ XORQ 64(SP), R8
+ XORQ SI, CX
+ XORQ 104(SP), R13
+ XORQ 144(SP), R8
+ XORQ R8, R13
+ MOVQ R13, DX
+ ROLQ $0x01, DX
+ MOVQ R15, R8
+ XORQ BP, DX
+ ROLQ $0x01, R8
+ MOVQ SI, R9
+ XORQ R12, R8
+ ROLQ $0x01, R9
+
+ // Result b
+ MOVQ (SP), R10
+ MOVQ 48(SP), R11
+ XORQ R13, R9
+ MOVQ 96(SP), R12
+ MOVQ 144(SP), R13
+ MOVQ 192(SP), R14
+ XORQ CX, R11
+ ROLQ $0x2c, R11
+ XORQ DX, R12
+ XORQ BX, R10
+ ROLQ $0x2b, R12
+ MOVQ R11, SI
+ MOVQ $0x8000000000008003, AX
+ ORQ R12, SI
+ XORQ R10, AX
+ XORQ AX, SI
+ MOVQ SI, (DI)
+ XORQ R9, R14
+ ROLQ $0x0e, R14
+ MOVQ R10, R15
+ ANDQ R11, R15
+ XORQ R14, R15
+ MOVQ R15, 32(DI)
+ XORQ R8, R13
+ ROLQ $0x15, R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 16(DI)
+ NOTQ R12
+ ORQ R10, R14
+ ORQ R13, R12
+ XORQ R13, R14
+ XORQ R11, R12
+ MOVQ R14, 24(DI)
+ MOVQ R12, 8(DI)
+ MOVQ R12, BP
+
+ // Result g
+ MOVQ 72(SP), R11
+ XORQ R9, R11
+ MOVQ 80(SP), R12
+ ROLQ $0x14, R11
+ XORQ BX, R12
+ ROLQ $0x03, R12
+ MOVQ 24(SP), R10
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ R8, R10
+ MOVQ 128(SP), R13
+ MOVQ 176(SP), R14
+ ROLQ $0x1c, R10
+ XORQ R10, AX
+ MOVQ AX, 40(DI)
+ XORQ AX, SI
+ XORQ CX, R13
+ ROLQ $0x2d, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 48(DI)
+ XORQ AX, BP
+ XORQ DX, R14
+ ROLQ $0x3d, R14
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 64(DI)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 72(DI)
+ NOTQ R14
+ XORQ R10, R15
+ ORQ R14, R13
+ XORQ R12, R13
+ MOVQ R13, 56(DI)
+
+ // Result k
+ MOVQ 8(SP), R10
+ MOVQ 56(SP), R11
+ MOVQ 104(SP), R12
+ MOVQ 152(SP), R13
+ MOVQ 160(SP), R14
+ XORQ DX, R11
+ ROLQ $0x06, R11
+ XORQ R8, R12
+ ROLQ $0x19, R12
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ CX, R10
+ ROLQ $0x01, R10
+ XORQ R10, AX
+ MOVQ AX, 80(DI)
+ XORQ AX, SI
+ XORQ R9, R13
+ ROLQ $0x08, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 88(DI)
+ XORQ AX, BP
+ XORQ BX, R14
+ ROLQ $0x12, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 96(DI)
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 104(DI)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 112(DI)
+ XORQ R10, R15
+
+ // Result m
+ MOVQ 40(SP), R11
+ XORQ BX, R11
+ MOVQ 88(SP), R12
+ ROLQ $0x24, R11
+ XORQ CX, R12
+ MOVQ 32(SP), R10
+ ROLQ $0x0a, R12
+ MOVQ R11, AX
+ MOVQ 136(SP), R13
+ ANDQ R12, AX
+ XORQ R9, R10
+ MOVQ 184(SP), R14
+ ROLQ $0x1b, R10
+ XORQ R10, AX
+ MOVQ AX, 120(DI)
+ XORQ AX, SI
+ XORQ DX, R13
+ ROLQ $0x0f, R13
+ MOVQ R12, AX
+ ORQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 128(DI)
+ XORQ AX, BP
+ XORQ R8, R14
+ ROLQ $0x38, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ORQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 136(DI)
+ ORQ R10, R11
+ XORQ R14, R11
+ MOVQ R11, 152(DI)
+ ANDQ R10, R14
+ XORQ R13, R14
+ MOVQ R14, 144(DI)
+ XORQ R11, R15
+
+ // Result s
+ MOVQ 16(SP), R10
+ MOVQ 64(SP), R11
+ MOVQ 112(SP), R12
+ XORQ DX, R10
+ MOVQ 120(SP), R13
+ ROLQ $0x3e, R10
+ XORQ R8, R11
+ MOVQ 168(SP), R14
+ ROLQ $0x37, R11
+ XORQ R9, R12
+ MOVQ R10, R9
+ XORQ CX, R14
+ ROLQ $0x02, R14
+ ANDQ R11, R9
+ XORQ R14, R9
+ MOVQ R9, 192(DI)
+ ROLQ $0x27, R12
+ XORQ R9, R15
+ NOTQ R11
+ XORQ BX, R13
+ MOVQ R11, BX
+ ANDQ R12, BX
+ XORQ R10, BX
+ MOVQ BX, 160(DI)
+ XORQ BX, SI
+ ROLQ $0x29, R13
+ MOVQ R12, CX
+ ORQ R13, CX
+ XORQ R11, CX
+ MOVQ CX, 168(DI)
+ XORQ CX, BP
+ MOVQ R13, DX
+ MOVQ R14, R8
+ ANDQ R14, DX
+ ORQ R10, R8
+ XORQ R12, DX
+ XORQ R13, R8
+ MOVQ DX, 176(DI)
+ MOVQ R8, 184(DI)
+
+ // Prepare round
+ MOVQ BP, BX
+ ROLQ $0x01, BX
+ MOVQ 16(DI), R12
+ XORQ 56(DI), DX
+ XORQ R15, BX
+ XORQ 96(DI), R12
+ XORQ 136(DI), DX
+ XORQ DX, R12
+ MOVQ R12, CX
+ ROLQ $0x01, CX
+ MOVQ 24(DI), R13
+ XORQ 64(DI), R8
+ XORQ SI, CX
+ XORQ 104(DI), R13
+ XORQ 144(DI), R8
+ XORQ R8, R13
+ MOVQ R13, DX
+ ROLQ $0x01, DX
+ MOVQ R15, R8
+ XORQ BP, DX
+ ROLQ $0x01, R8
+ MOVQ SI, R9
+ XORQ R12, R8
+ ROLQ $0x01, R9
+
+ // Result b
+ MOVQ (DI), R10
+ MOVQ 48(DI), R11
+ XORQ R13, R9
+ MOVQ 96(DI), R12
+ MOVQ 144(DI), R13
+ MOVQ 192(DI), R14
+ XORQ CX, R11
+ ROLQ $0x2c, R11
+ XORQ DX, R12
+ XORQ BX, R10
+ ROLQ $0x2b, R12
+ MOVQ R11, SI
+ MOVQ $0x8000000000008002, AX
+ ORQ R12, SI
+ XORQ R10, AX
+ XORQ AX, SI
+ MOVQ SI, (SP)
+ XORQ R9, R14
+ ROLQ $0x0e, R14
+ MOVQ R10, R15
+ ANDQ R11, R15
+ XORQ R14, R15
+ MOVQ R15, 32(SP)
+ XORQ R8, R13
+ ROLQ $0x15, R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 16(SP)
+ NOTQ R12
+ ORQ R10, R14
+ ORQ R13, R12
+ XORQ R13, R14
+ XORQ R11, R12
+ MOVQ R14, 24(SP)
+ MOVQ R12, 8(SP)
+ MOVQ R12, BP
+
+ // Result g
+ MOVQ 72(DI), R11
+ XORQ R9, R11
+ MOVQ 80(DI), R12
+ ROLQ $0x14, R11
+ XORQ BX, R12
+ ROLQ $0x03, R12
+ MOVQ 24(DI), R10
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ R8, R10
+ MOVQ 128(DI), R13
+ MOVQ 176(DI), R14
+ ROLQ $0x1c, R10
+ XORQ R10, AX
+ MOVQ AX, 40(SP)
+ XORQ AX, SI
+ XORQ CX, R13
+ ROLQ $0x2d, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 48(SP)
+ XORQ AX, BP
+ XORQ DX, R14
+ ROLQ $0x3d, R14
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 64(SP)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 72(SP)
+ NOTQ R14
+ XORQ R10, R15
+ ORQ R14, R13
+ XORQ R12, R13
+ MOVQ R13, 56(SP)
+
+ // Result k
+ MOVQ 8(DI), R10
+ MOVQ 56(DI), R11
+ MOVQ 104(DI), R12
+ MOVQ 152(DI), R13
+ MOVQ 160(DI), R14
+ XORQ DX, R11
+ ROLQ $0x06, R11
+ XORQ R8, R12
+ ROLQ $0x19, R12
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ CX, R10
+ ROLQ $0x01, R10
+ XORQ R10, AX
+ MOVQ AX, 80(SP)
+ XORQ AX, SI
+ XORQ R9, R13
+ ROLQ $0x08, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 88(SP)
+ XORQ AX, BP
+ XORQ BX, R14
+ ROLQ $0x12, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 96(SP)
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 104(SP)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 112(SP)
+ XORQ R10, R15
+
+ // Result m
+ MOVQ 40(DI), R11
+ XORQ BX, R11
+ MOVQ 88(DI), R12
+ ROLQ $0x24, R11
+ XORQ CX, R12
+ MOVQ 32(DI), R10
+ ROLQ $0x0a, R12
+ MOVQ R11, AX
+ MOVQ 136(DI), R13
+ ANDQ R12, AX
+ XORQ R9, R10
+ MOVQ 184(DI), R14
+ ROLQ $0x1b, R10
+ XORQ R10, AX
+ MOVQ AX, 120(SP)
+ XORQ AX, SI
+ XORQ DX, R13
+ ROLQ $0x0f, R13
+ MOVQ R12, AX
+ ORQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 128(SP)
+ XORQ AX, BP
+ XORQ R8, R14
+ ROLQ $0x38, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ORQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 136(SP)
+ ORQ R10, R11
+ XORQ R14, R11
+ MOVQ R11, 152(SP)
+ ANDQ R10, R14
+ XORQ R13, R14
+ MOVQ R14, 144(SP)
+ XORQ R11, R15
+
+ // Result s
+ MOVQ 16(DI), R10
+ MOVQ 64(DI), R11
+ MOVQ 112(DI), R12
+ XORQ DX, R10
+ MOVQ 120(DI), R13
+ ROLQ $0x3e, R10
+ XORQ R8, R11
+ MOVQ 168(DI), R14
+ ROLQ $0x37, R11
+ XORQ R9, R12
+ MOVQ R10, R9
+ XORQ CX, R14
+ ROLQ $0x02, R14
+ ANDQ R11, R9
+ XORQ R14, R9
+ MOVQ R9, 192(SP)
+ ROLQ $0x27, R12
+ XORQ R9, R15
+ NOTQ R11
+ XORQ BX, R13
+ MOVQ R11, BX
+ ANDQ R12, BX
+ XORQ R10, BX
+ MOVQ BX, 160(SP)
+ XORQ BX, SI
+ ROLQ $0x29, R13
+ MOVQ R12, CX
+ ORQ R13, CX
+ XORQ R11, CX
+ MOVQ CX, 168(SP)
+ XORQ CX, BP
+ MOVQ R13, DX
+ MOVQ R14, R8
+ ANDQ R14, DX
+ ORQ R10, R8
+ XORQ R12, DX
+ XORQ R13, R8
+ MOVQ DX, 176(SP)
+ MOVQ R8, 184(SP)
+
+ // Prepare round
+ MOVQ BP, BX
+ ROLQ $0x01, BX
+ MOVQ 16(SP), R12
+ XORQ 56(SP), DX
+ XORQ R15, BX
+ XORQ 96(SP), R12
+ XORQ 136(SP), DX
+ XORQ DX, R12
+ MOVQ R12, CX
+ ROLQ $0x01, CX
+ MOVQ 24(SP), R13
+ XORQ 64(SP), R8
+ XORQ SI, CX
+ XORQ 104(SP), R13
+ XORQ 144(SP), R8
+ XORQ R8, R13
+ MOVQ R13, DX
+ ROLQ $0x01, DX
+ MOVQ R15, R8
+ XORQ BP, DX
+ ROLQ $0x01, R8
+ MOVQ SI, R9
+ XORQ R12, R8
+ ROLQ $0x01, R9
+
+ // Result b
+ MOVQ (SP), R10
+ MOVQ 48(SP), R11
+ XORQ R13, R9
+ MOVQ 96(SP), R12
+ MOVQ 144(SP), R13
+ MOVQ 192(SP), R14
+ XORQ CX, R11
+ ROLQ $0x2c, R11
+ XORQ DX, R12
+ XORQ BX, R10
+ ROLQ $0x2b, R12
+ MOVQ R11, SI
+ MOVQ $0x8000000000000080, AX
+ ORQ R12, SI
+ XORQ R10, AX
+ XORQ AX, SI
+ MOVQ SI, (DI)
+ XORQ R9, R14
+ ROLQ $0x0e, R14
+ MOVQ R10, R15
+ ANDQ R11, R15
+ XORQ R14, R15
+ MOVQ R15, 32(DI)
+ XORQ R8, R13
+ ROLQ $0x15, R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 16(DI)
+ NOTQ R12
+ ORQ R10, R14
+ ORQ R13, R12
+ XORQ R13, R14
+ XORQ R11, R12
+ MOVQ R14, 24(DI)
+ MOVQ R12, 8(DI)
+ MOVQ R12, BP
+
+ // Result g
+ MOVQ 72(SP), R11
+ XORQ R9, R11
+ MOVQ 80(SP), R12
+ ROLQ $0x14, R11
+ XORQ BX, R12
+ ROLQ $0x03, R12
+ MOVQ 24(SP), R10
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ R8, R10
+ MOVQ 128(SP), R13
+ MOVQ 176(SP), R14
+ ROLQ $0x1c, R10
+ XORQ R10, AX
+ MOVQ AX, 40(DI)
+ XORQ AX, SI
+ XORQ CX, R13
+ ROLQ $0x2d, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 48(DI)
+ XORQ AX, BP
+ XORQ DX, R14
+ ROLQ $0x3d, R14
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 64(DI)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 72(DI)
+ NOTQ R14
+ XORQ R10, R15
+ ORQ R14, R13
+ XORQ R12, R13
+ MOVQ R13, 56(DI)
+
+ // Result k
+ MOVQ 8(SP), R10
+ MOVQ 56(SP), R11
+ MOVQ 104(SP), R12
+ MOVQ 152(SP), R13
+ MOVQ 160(SP), R14
+ XORQ DX, R11
+ ROLQ $0x06, R11
+ XORQ R8, R12
+ ROLQ $0x19, R12
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ CX, R10
+ ROLQ $0x01, R10
+ XORQ R10, AX
+ MOVQ AX, 80(DI)
+ XORQ AX, SI
+ XORQ R9, R13
+ ROLQ $0x08, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 88(DI)
+ XORQ AX, BP
+ XORQ BX, R14
+ ROLQ $0x12, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 96(DI)
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 104(DI)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 112(DI)
+ XORQ R10, R15
+
+ // Result m
+ MOVQ 40(SP), R11
+ XORQ BX, R11
+ MOVQ 88(SP), R12
+ ROLQ $0x24, R11
+ XORQ CX, R12
+ MOVQ 32(SP), R10
+ ROLQ $0x0a, R12
+ MOVQ R11, AX
+ MOVQ 136(SP), R13
+ ANDQ R12, AX
+ XORQ R9, R10
+ MOVQ 184(SP), R14
+ ROLQ $0x1b, R10
+ XORQ R10, AX
+ MOVQ AX, 120(DI)
+ XORQ AX, SI
+ XORQ DX, R13
+ ROLQ $0x0f, R13
+ MOVQ R12, AX
+ ORQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 128(DI)
+ XORQ AX, BP
+ XORQ R8, R14
+ ROLQ $0x38, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ORQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 136(DI)
+ ORQ R10, R11
+ XORQ R14, R11
+ MOVQ R11, 152(DI)
+ ANDQ R10, R14
+ XORQ R13, R14
+ MOVQ R14, 144(DI)
+ XORQ R11, R15
+
+ // Result s
+ MOVQ 16(SP), R10
+ MOVQ 64(SP), R11
+ MOVQ 112(SP), R12
+ XORQ DX, R10
+ MOVQ 120(SP), R13
+ ROLQ $0x3e, R10
+ XORQ R8, R11
+ MOVQ 168(SP), R14
+ ROLQ $0x37, R11
+ XORQ R9, R12
+ MOVQ R10, R9
+ XORQ CX, R14
+ ROLQ $0x02, R14
+ ANDQ R11, R9
+ XORQ R14, R9
+ MOVQ R9, 192(DI)
+ ROLQ $0x27, R12
+ XORQ R9, R15
+ NOTQ R11
+ XORQ BX, R13
+ MOVQ R11, BX
+ ANDQ R12, BX
+ XORQ R10, BX
+ MOVQ BX, 160(DI)
+ XORQ BX, SI
+ ROLQ $0x29, R13
+ MOVQ R12, CX
+ ORQ R13, CX
+ XORQ R11, CX
+ MOVQ CX, 168(DI)
+ XORQ CX, BP
+ MOVQ R13, DX
+ MOVQ R14, R8
+ ANDQ R14, DX
+ ORQ R10, R8
+ XORQ R12, DX
+ XORQ R13, R8
+ MOVQ DX, 176(DI)
+ MOVQ R8, 184(DI)
+
+ // Prepare round
+ MOVQ BP, BX
+ ROLQ $0x01, BX
+ MOVQ 16(DI), R12
+ XORQ 56(DI), DX
+ XORQ R15, BX
+ XORQ 96(DI), R12
+ XORQ 136(DI), DX
+ XORQ DX, R12
+ MOVQ R12, CX
+ ROLQ $0x01, CX
+ MOVQ 24(DI), R13
+ XORQ 64(DI), R8
+ XORQ SI, CX
+ XORQ 104(DI), R13
+ XORQ 144(DI), R8
+ XORQ R8, R13
+ MOVQ R13, DX
+ ROLQ $0x01, DX
+ MOVQ R15, R8
+ XORQ BP, DX
+ ROLQ $0x01, R8
+ MOVQ SI, R9
+ XORQ R12, R8
+ ROLQ $0x01, R9
+
+ // Result b
+ MOVQ (DI), R10
+ MOVQ 48(DI), R11
+ XORQ R13, R9
+ MOVQ 96(DI), R12
+ MOVQ 144(DI), R13
+ MOVQ 192(DI), R14
+ XORQ CX, R11
+ ROLQ $0x2c, R11
+ XORQ DX, R12
+ XORQ BX, R10
+ ROLQ $0x2b, R12
+ MOVQ R11, SI
+ MOVQ $0x000000000000800a, AX
+ ORQ R12, SI
+ XORQ R10, AX
+ XORQ AX, SI
+ MOVQ SI, (SP)
+ XORQ R9, R14
+ ROLQ $0x0e, R14
+ MOVQ R10, R15
+ ANDQ R11, R15
+ XORQ R14, R15
+ MOVQ R15, 32(SP)
+ XORQ R8, R13
+ ROLQ $0x15, R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 16(SP)
+ NOTQ R12
+ ORQ R10, R14
+ ORQ R13, R12
+ XORQ R13, R14
+ XORQ R11, R12
+ MOVQ R14, 24(SP)
+ MOVQ R12, 8(SP)
+ MOVQ R12, BP
+
+ // Result g
+ MOVQ 72(DI), R11
+ XORQ R9, R11
+ MOVQ 80(DI), R12
+ ROLQ $0x14, R11
+ XORQ BX, R12
+ ROLQ $0x03, R12
+ MOVQ 24(DI), R10
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ R8, R10
+ MOVQ 128(DI), R13
+ MOVQ 176(DI), R14
+ ROLQ $0x1c, R10
+ XORQ R10, AX
+ MOVQ AX, 40(SP)
+ XORQ AX, SI
+ XORQ CX, R13
+ ROLQ $0x2d, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 48(SP)
+ XORQ AX, BP
+ XORQ DX, R14
+ ROLQ $0x3d, R14
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 64(SP)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 72(SP)
+ NOTQ R14
+ XORQ R10, R15
+ ORQ R14, R13
+ XORQ R12, R13
+ MOVQ R13, 56(SP)
+
+ // Result k
+ MOVQ 8(DI), R10
+ MOVQ 56(DI), R11
+ MOVQ 104(DI), R12
+ MOVQ 152(DI), R13
+ MOVQ 160(DI), R14
+ XORQ DX, R11
+ ROLQ $0x06, R11
+ XORQ R8, R12
+ ROLQ $0x19, R12
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ CX, R10
+ ROLQ $0x01, R10
+ XORQ R10, AX
+ MOVQ AX, 80(SP)
+ XORQ AX, SI
+ XORQ R9, R13
+ ROLQ $0x08, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 88(SP)
+ XORQ AX, BP
+ XORQ BX, R14
+ ROLQ $0x12, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 96(SP)
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 104(SP)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 112(SP)
+ XORQ R10, R15
+
+ // Result m
+ MOVQ 40(DI), R11
+ XORQ BX, R11
+ MOVQ 88(DI), R12
+ ROLQ $0x24, R11
+ XORQ CX, R12
+ MOVQ 32(DI), R10
+ ROLQ $0x0a, R12
+ MOVQ R11, AX
+ MOVQ 136(DI), R13
+ ANDQ R12, AX
+ XORQ R9, R10
+ MOVQ 184(DI), R14
+ ROLQ $0x1b, R10
+ XORQ R10, AX
+ MOVQ AX, 120(SP)
+ XORQ AX, SI
+ XORQ DX, R13
+ ROLQ $0x0f, R13
+ MOVQ R12, AX
+ ORQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 128(SP)
+ XORQ AX, BP
+ XORQ R8, R14
+ ROLQ $0x38, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ORQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 136(SP)
+ ORQ R10, R11
+ XORQ R14, R11
+ MOVQ R11, 152(SP)
+ ANDQ R10, R14
+ XORQ R13, R14
+ MOVQ R14, 144(SP)
+ XORQ R11, R15
+
+ // Result s
+ MOVQ 16(DI), R10
+ MOVQ 64(DI), R11
+ MOVQ 112(DI), R12
+ XORQ DX, R10
+ MOVQ 120(DI), R13
+ ROLQ $0x3e, R10
+ XORQ R8, R11
+ MOVQ 168(DI), R14
+ ROLQ $0x37, R11
+ XORQ R9, R12
+ MOVQ R10, R9
+ XORQ CX, R14
+ ROLQ $0x02, R14
+ ANDQ R11, R9
+ XORQ R14, R9
+ MOVQ R9, 192(SP)
+ ROLQ $0x27, R12
+ XORQ R9, R15
+ NOTQ R11
+ XORQ BX, R13
+ MOVQ R11, BX
+ ANDQ R12, BX
+ XORQ R10, BX
+ MOVQ BX, 160(SP)
+ XORQ BX, SI
+ ROLQ $0x29, R13
+ MOVQ R12, CX
+ ORQ R13, CX
+ XORQ R11, CX
+ MOVQ CX, 168(SP)
+ XORQ CX, BP
+ MOVQ R13, DX
+ MOVQ R14, R8
+ ANDQ R14, DX
+ ORQ R10, R8
+ XORQ R12, DX
+ XORQ R13, R8
+ MOVQ DX, 176(SP)
+ MOVQ R8, 184(SP)
+
+ // Prepare round
+ MOVQ BP, BX
+ ROLQ $0x01, BX
+ MOVQ 16(SP), R12
+ XORQ 56(SP), DX
+ XORQ R15, BX
+ XORQ 96(SP), R12
+ XORQ 136(SP), DX
+ XORQ DX, R12
+ MOVQ R12, CX
+ ROLQ $0x01, CX
+ MOVQ 24(SP), R13
+ XORQ 64(SP), R8
+ XORQ SI, CX
+ XORQ 104(SP), R13
+ XORQ 144(SP), R8
+ XORQ R8, R13
+ MOVQ R13, DX
+ ROLQ $0x01, DX
+ MOVQ R15, R8
+ XORQ BP, DX
+ ROLQ $0x01, R8
+ MOVQ SI, R9
+ XORQ R12, R8
+ ROLQ $0x01, R9
+
+ // Result b
+ MOVQ (SP), R10
+ MOVQ 48(SP), R11
+ XORQ R13, R9
+ MOVQ 96(SP), R12
+ MOVQ 144(SP), R13
+ MOVQ 192(SP), R14
+ XORQ CX, R11
+ ROLQ $0x2c, R11
+ XORQ DX, R12
+ XORQ BX, R10
+ ROLQ $0x2b, R12
+ MOVQ R11, SI
+ MOVQ $0x800000008000000a, AX
+ ORQ R12, SI
+ XORQ R10, AX
+ XORQ AX, SI
+ MOVQ SI, (DI)
+ XORQ R9, R14
+ ROLQ $0x0e, R14
+ MOVQ R10, R15
+ ANDQ R11, R15
+ XORQ R14, R15
+ MOVQ R15, 32(DI)
+ XORQ R8, R13
+ ROLQ $0x15, R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 16(DI)
+ NOTQ R12
+ ORQ R10, R14
+ ORQ R13, R12
+ XORQ R13, R14
+ XORQ R11, R12
+ MOVQ R14, 24(DI)
+ MOVQ R12, 8(DI)
+ MOVQ R12, BP
+
+ // Result g
+ MOVQ 72(SP), R11
+ XORQ R9, R11
+ MOVQ 80(SP), R12
+ ROLQ $0x14, R11
+ XORQ BX, R12
+ ROLQ $0x03, R12
+ MOVQ 24(SP), R10
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ R8, R10
+ MOVQ 128(SP), R13
+ MOVQ 176(SP), R14
+ ROLQ $0x1c, R10
+ XORQ R10, AX
+ MOVQ AX, 40(DI)
+ XORQ AX, SI
+ XORQ CX, R13
+ ROLQ $0x2d, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 48(DI)
+ XORQ AX, BP
+ XORQ DX, R14
+ ROLQ $0x3d, R14
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 64(DI)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 72(DI)
+ NOTQ R14
+ XORQ R10, R15
+ ORQ R14, R13
+ XORQ R12, R13
+ MOVQ R13, 56(DI)
+
+ // Result k
+ MOVQ 8(SP), R10
+ MOVQ 56(SP), R11
+ MOVQ 104(SP), R12
+ MOVQ 152(SP), R13
+ MOVQ 160(SP), R14
+ XORQ DX, R11
+ ROLQ $0x06, R11
+ XORQ R8, R12
+ ROLQ $0x19, R12
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ CX, R10
+ ROLQ $0x01, R10
+ XORQ R10, AX
+ MOVQ AX, 80(DI)
+ XORQ AX, SI
+ XORQ R9, R13
+ ROLQ $0x08, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 88(DI)
+ XORQ AX, BP
+ XORQ BX, R14
+ ROLQ $0x12, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 96(DI)
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 104(DI)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 112(DI)
+ XORQ R10, R15
+
+ // Result m
+ MOVQ 40(SP), R11
+ XORQ BX, R11
+ MOVQ 88(SP), R12
+ ROLQ $0x24, R11
+ XORQ CX, R12
+ MOVQ 32(SP), R10
+ ROLQ $0x0a, R12
+ MOVQ R11, AX
+ MOVQ 136(SP), R13
+ ANDQ R12, AX
+ XORQ R9, R10
+ MOVQ 184(SP), R14
+ ROLQ $0x1b, R10
+ XORQ R10, AX
+ MOVQ AX, 120(DI)
+ XORQ AX, SI
+ XORQ DX, R13
+ ROLQ $0x0f, R13
+ MOVQ R12, AX
+ ORQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 128(DI)
+ XORQ AX, BP
+ XORQ R8, R14
+ ROLQ $0x38, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ORQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 136(DI)
+ ORQ R10, R11
+ XORQ R14, R11
+ MOVQ R11, 152(DI)
+ ANDQ R10, R14
+ XORQ R13, R14
+ MOVQ R14, 144(DI)
+ XORQ R11, R15
+
+ // Result s
+ MOVQ 16(SP), R10
+ MOVQ 64(SP), R11
+ MOVQ 112(SP), R12
+ XORQ DX, R10
+ MOVQ 120(SP), R13
+ ROLQ $0x3e, R10
+ XORQ R8, R11
+ MOVQ 168(SP), R14
+ ROLQ $0x37, R11
+ XORQ R9, R12
+ MOVQ R10, R9
+ XORQ CX, R14
+ ROLQ $0x02, R14
+ ANDQ R11, R9
+ XORQ R14, R9
+ MOVQ R9, 192(DI)
+ ROLQ $0x27, R12
+ XORQ R9, R15
+ NOTQ R11
+ XORQ BX, R13
+ MOVQ R11, BX
+ ANDQ R12, BX
+ XORQ R10, BX
+ MOVQ BX, 160(DI)
+ XORQ BX, SI
+ ROLQ $0x29, R13
+ MOVQ R12, CX
+ ORQ R13, CX
+ XORQ R11, CX
+ MOVQ CX, 168(DI)
+ XORQ CX, BP
+ MOVQ R13, DX
+ MOVQ R14, R8
+ ANDQ R14, DX
+ ORQ R10, R8
+ XORQ R12, DX
+ XORQ R13, R8
+ MOVQ DX, 176(DI)
+ MOVQ R8, 184(DI)
+
+ // Prepare round
+ MOVQ BP, BX
+ ROLQ $0x01, BX
+ MOVQ 16(DI), R12
+ XORQ 56(DI), DX
+ XORQ R15, BX
+ XORQ 96(DI), R12
+ XORQ 136(DI), DX
+ XORQ DX, R12
+ MOVQ R12, CX
+ ROLQ $0x01, CX
+ MOVQ 24(DI), R13
+ XORQ 64(DI), R8
+ XORQ SI, CX
+ XORQ 104(DI), R13
+ XORQ 144(DI), R8
+ XORQ R8, R13
+ MOVQ R13, DX
+ ROLQ $0x01, DX
+ MOVQ R15, R8
+ XORQ BP, DX
+ ROLQ $0x01, R8
+ MOVQ SI, R9
+ XORQ R12, R8
+ ROLQ $0x01, R9
+
+ // Result b
+ MOVQ (DI), R10
+ MOVQ 48(DI), R11
+ XORQ R13, R9
+ MOVQ 96(DI), R12
+ MOVQ 144(DI), R13
+ MOVQ 192(DI), R14
+ XORQ CX, R11
+ ROLQ $0x2c, R11
+ XORQ DX, R12
+ XORQ BX, R10
+ ROLQ $0x2b, R12
+ MOVQ R11, SI
+ MOVQ $0x8000000080008081, AX
+ ORQ R12, SI
+ XORQ R10, AX
+ XORQ AX, SI
+ MOVQ SI, (SP)
+ XORQ R9, R14
+ ROLQ $0x0e, R14
+ MOVQ R10, R15
+ ANDQ R11, R15
+ XORQ R14, R15
+ MOVQ R15, 32(SP)
+ XORQ R8, R13
+ ROLQ $0x15, R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 16(SP)
+ NOTQ R12
+ ORQ R10, R14
+ ORQ R13, R12
+ XORQ R13, R14
+ XORQ R11, R12
+ MOVQ R14, 24(SP)
+ MOVQ R12, 8(SP)
+ MOVQ R12, BP
+
+ // Result g
+ MOVQ 72(DI), R11
+ XORQ R9, R11
+ MOVQ 80(DI), R12
+ ROLQ $0x14, R11
+ XORQ BX, R12
+ ROLQ $0x03, R12
+ MOVQ 24(DI), R10
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ R8, R10
+ MOVQ 128(DI), R13
+ MOVQ 176(DI), R14
+ ROLQ $0x1c, R10
+ XORQ R10, AX
+ MOVQ AX, 40(SP)
+ XORQ AX, SI
+ XORQ CX, R13
+ ROLQ $0x2d, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 48(SP)
+ XORQ AX, BP
+ XORQ DX, R14
+ ROLQ $0x3d, R14
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 64(SP)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 72(SP)
+ NOTQ R14
+ XORQ R10, R15
+ ORQ R14, R13
+ XORQ R12, R13
+ MOVQ R13, 56(SP)
+
+ // Result k
+ MOVQ 8(DI), R10
+ MOVQ 56(DI), R11
+ MOVQ 104(DI), R12
+ MOVQ 152(DI), R13
+ MOVQ 160(DI), R14
+ XORQ DX, R11
+ ROLQ $0x06, R11
+ XORQ R8, R12
+ ROLQ $0x19, R12
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ CX, R10
+ ROLQ $0x01, R10
+ XORQ R10, AX
+ MOVQ AX, 80(SP)
+ XORQ AX, SI
+ XORQ R9, R13
+ ROLQ $0x08, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 88(SP)
+ XORQ AX, BP
+ XORQ BX, R14
+ ROLQ $0x12, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 96(SP)
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 104(SP)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 112(SP)
+ XORQ R10, R15
+
+ // Result m
+ MOVQ 40(DI), R11
+ XORQ BX, R11
+ MOVQ 88(DI), R12
+ ROLQ $0x24, R11
+ XORQ CX, R12
+ MOVQ 32(DI), R10
+ ROLQ $0x0a, R12
+ MOVQ R11, AX
+ MOVQ 136(DI), R13
+ ANDQ R12, AX
+ XORQ R9, R10
+ MOVQ 184(DI), R14
+ ROLQ $0x1b, R10
+ XORQ R10, AX
+ MOVQ AX, 120(SP)
+ XORQ AX, SI
+ XORQ DX, R13
+ ROLQ $0x0f, R13
+ MOVQ R12, AX
+ ORQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 128(SP)
+ XORQ AX, BP
+ XORQ R8, R14
+ ROLQ $0x38, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ORQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 136(SP)
+ ORQ R10, R11
+ XORQ R14, R11
+ MOVQ R11, 152(SP)
+ ANDQ R10, R14
+ XORQ R13, R14
+ MOVQ R14, 144(SP)
+ XORQ R11, R15
+
+ // Result s
+ MOVQ 16(DI), R10
+ MOVQ 64(DI), R11
+ MOVQ 112(DI), R12
+ XORQ DX, R10
+ MOVQ 120(DI), R13
+ ROLQ $0x3e, R10
+ XORQ R8, R11
+ MOVQ 168(DI), R14
+ ROLQ $0x37, R11
+ XORQ R9, R12
+ MOVQ R10, R9
+ XORQ CX, R14
+ ROLQ $0x02, R14
+ ANDQ R11, R9
+ XORQ R14, R9
+ MOVQ R9, 192(SP)
+ ROLQ $0x27, R12
+ XORQ R9, R15
+ NOTQ R11
+ XORQ BX, R13
+ MOVQ R11, BX
+ ANDQ R12, BX
+ XORQ R10, BX
+ MOVQ BX, 160(SP)
+ XORQ BX, SI
+ ROLQ $0x29, R13
+ MOVQ R12, CX
+ ORQ R13, CX
+ XORQ R11, CX
+ MOVQ CX, 168(SP)
+ XORQ CX, BP
+ MOVQ R13, DX
+ MOVQ R14, R8
+ ANDQ R14, DX
+ ORQ R10, R8
+ XORQ R12, DX
+ XORQ R13, R8
+ MOVQ DX, 176(SP)
+ MOVQ R8, 184(SP)
+
+ // Prepare round
+ MOVQ BP, BX
+ ROLQ $0x01, BX
+ MOVQ 16(SP), R12
+ XORQ 56(SP), DX
+ XORQ R15, BX
+ XORQ 96(SP), R12
+ XORQ 136(SP), DX
+ XORQ DX, R12
+ MOVQ R12, CX
+ ROLQ $0x01, CX
+ MOVQ 24(SP), R13
+ XORQ 64(SP), R8
+ XORQ SI, CX
+ XORQ 104(SP), R13
+ XORQ 144(SP), R8
+ XORQ R8, R13
+ MOVQ R13, DX
+ ROLQ $0x01, DX
+ MOVQ R15, R8
+ XORQ BP, DX
+ ROLQ $0x01, R8
+ MOVQ SI, R9
+ XORQ R12, R8
+ ROLQ $0x01, R9
+
+ // Result b
+ MOVQ (SP), R10
+ MOVQ 48(SP), R11
+ XORQ R13, R9
+ MOVQ 96(SP), R12
+ MOVQ 144(SP), R13
+ MOVQ 192(SP), R14
+ XORQ CX, R11
+ ROLQ $0x2c, R11
+ XORQ DX, R12
+ XORQ BX, R10
+ ROLQ $0x2b, R12
+ MOVQ R11, SI
+ MOVQ $0x8000000000008080, AX
+ ORQ R12, SI
+ XORQ R10, AX
+ XORQ AX, SI
+ MOVQ SI, (DI)
+ XORQ R9, R14
+ ROLQ $0x0e, R14
+ MOVQ R10, R15
+ ANDQ R11, R15
+ XORQ R14, R15
+ MOVQ R15, 32(DI)
+ XORQ R8, R13
+ ROLQ $0x15, R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 16(DI)
+ NOTQ R12
+ ORQ R10, R14
+ ORQ R13, R12
+ XORQ R13, R14
+ XORQ R11, R12
+ MOVQ R14, 24(DI)
+ MOVQ R12, 8(DI)
+ MOVQ R12, BP
+
+ // Result g
+ MOVQ 72(SP), R11
+ XORQ R9, R11
+ MOVQ 80(SP), R12
+ ROLQ $0x14, R11
+ XORQ BX, R12
+ ROLQ $0x03, R12
+ MOVQ 24(SP), R10
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ R8, R10
+ MOVQ 128(SP), R13
+ MOVQ 176(SP), R14
+ ROLQ $0x1c, R10
+ XORQ R10, AX
+ MOVQ AX, 40(DI)
+ XORQ AX, SI
+ XORQ CX, R13
+ ROLQ $0x2d, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 48(DI)
+ XORQ AX, BP
+ XORQ DX, R14
+ ROLQ $0x3d, R14
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 64(DI)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 72(DI)
+ NOTQ R14
+ XORQ R10, R15
+ ORQ R14, R13
+ XORQ R12, R13
+ MOVQ R13, 56(DI)
+
+ // Result k
+ MOVQ 8(SP), R10
+ MOVQ 56(SP), R11
+ MOVQ 104(SP), R12
+ MOVQ 152(SP), R13
+ MOVQ 160(SP), R14
+ XORQ DX, R11
+ ROLQ $0x06, R11
+ XORQ R8, R12
+ ROLQ $0x19, R12
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ CX, R10
+ ROLQ $0x01, R10
+ XORQ R10, AX
+ MOVQ AX, 80(DI)
+ XORQ AX, SI
+ XORQ R9, R13
+ ROLQ $0x08, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 88(DI)
+ XORQ AX, BP
+ XORQ BX, R14
+ ROLQ $0x12, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 96(DI)
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 104(DI)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 112(DI)
+ XORQ R10, R15
+
+ // Result m
+ MOVQ 40(SP), R11
+ XORQ BX, R11
+ MOVQ 88(SP), R12
+ ROLQ $0x24, R11
+ XORQ CX, R12
+ MOVQ 32(SP), R10
+ ROLQ $0x0a, R12
+ MOVQ R11, AX
+ MOVQ 136(SP), R13
+ ANDQ R12, AX
+ XORQ R9, R10
+ MOVQ 184(SP), R14
+ ROLQ $0x1b, R10
+ XORQ R10, AX
+ MOVQ AX, 120(DI)
+ XORQ AX, SI
+ XORQ DX, R13
+ ROLQ $0x0f, R13
+ MOVQ R12, AX
+ ORQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 128(DI)
+ XORQ AX, BP
+ XORQ R8, R14
+ ROLQ $0x38, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ORQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 136(DI)
+ ORQ R10, R11
+ XORQ R14, R11
+ MOVQ R11, 152(DI)
+ ANDQ R10, R14
+ XORQ R13, R14
+ MOVQ R14, 144(DI)
+ XORQ R11, R15
+
+ // Result s
+ MOVQ 16(SP), R10
+ MOVQ 64(SP), R11
+ MOVQ 112(SP), R12
+ XORQ DX, R10
+ MOVQ 120(SP), R13
+ ROLQ $0x3e, R10
+ XORQ R8, R11
+ MOVQ 168(SP), R14
+ ROLQ $0x37, R11
+ XORQ R9, R12
+ MOVQ R10, R9
+ XORQ CX, R14
+ ROLQ $0x02, R14
+ ANDQ R11, R9
+ XORQ R14, R9
+ MOVQ R9, 192(DI)
+ ROLQ $0x27, R12
+ XORQ R9, R15
+ NOTQ R11
+ XORQ BX, R13
+ MOVQ R11, BX
+ ANDQ R12, BX
+ XORQ R10, BX
+ MOVQ BX, 160(DI)
+ XORQ BX, SI
+ ROLQ $0x29, R13
+ MOVQ R12, CX
+ ORQ R13, CX
+ XORQ R11, CX
+ MOVQ CX, 168(DI)
+ XORQ CX, BP
+ MOVQ R13, DX
+ MOVQ R14, R8
+ ANDQ R14, DX
+ ORQ R10, R8
+ XORQ R12, DX
+ XORQ R13, R8
+ MOVQ DX, 176(DI)
+ MOVQ R8, 184(DI)
+
+ // Prepare round
+ MOVQ BP, BX
+ ROLQ $0x01, BX
+ MOVQ 16(DI), R12
+ XORQ 56(DI), DX
+ XORQ R15, BX
+ XORQ 96(DI), R12
+ XORQ 136(DI), DX
+ XORQ DX, R12
+ MOVQ R12, CX
+ ROLQ $0x01, CX
+ MOVQ 24(DI), R13
+ XORQ 64(DI), R8
+ XORQ SI, CX
+ XORQ 104(DI), R13
+ XORQ 144(DI), R8
+ XORQ R8, R13
+ MOVQ R13, DX
+ ROLQ $0x01, DX
+ MOVQ R15, R8
+ XORQ BP, DX
+ ROLQ $0x01, R8
+ MOVQ SI, R9
+ XORQ R12, R8
+ ROLQ $0x01, R9
+
+ // Result b
+ MOVQ (DI), R10
+ MOVQ 48(DI), R11
+ XORQ R13, R9
+ MOVQ 96(DI), R12
+ MOVQ 144(DI), R13
+ MOVQ 192(DI), R14
+ XORQ CX, R11
+ ROLQ $0x2c, R11
+ XORQ DX, R12
+ XORQ BX, R10
+ ROLQ $0x2b, R12
+ MOVQ R11, SI
+ MOVQ $0x0000000080000001, AX
+ ORQ R12, SI
+ XORQ R10, AX
+ XORQ AX, SI
+ MOVQ SI, (SP)
+ XORQ R9, R14
+ ROLQ $0x0e, R14
+ MOVQ R10, R15
+ ANDQ R11, R15
+ XORQ R14, R15
+ MOVQ R15, 32(SP)
+ XORQ R8, R13
+ ROLQ $0x15, R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 16(SP)
+ NOTQ R12
+ ORQ R10, R14
+ ORQ R13, R12
+ XORQ R13, R14
+ XORQ R11, R12
+ MOVQ R14, 24(SP)
+ MOVQ R12, 8(SP)
+ MOVQ R12, BP
+
+ // Result g
+ MOVQ 72(DI), R11
+ XORQ R9, R11
+ MOVQ 80(DI), R12
+ ROLQ $0x14, R11
+ XORQ BX, R12
+ ROLQ $0x03, R12
+ MOVQ 24(DI), R10
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ R8, R10
+ MOVQ 128(DI), R13
+ MOVQ 176(DI), R14
+ ROLQ $0x1c, R10
+ XORQ R10, AX
+ MOVQ AX, 40(SP)
+ XORQ AX, SI
+ XORQ CX, R13
+ ROLQ $0x2d, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 48(SP)
+ XORQ AX, BP
+ XORQ DX, R14
+ ROLQ $0x3d, R14
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 64(SP)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 72(SP)
+ NOTQ R14
+ XORQ R10, R15
+ ORQ R14, R13
+ XORQ R12, R13
+ MOVQ R13, 56(SP)
+
+ // Result k
+ MOVQ 8(DI), R10
+ MOVQ 56(DI), R11
+ MOVQ 104(DI), R12
+ MOVQ 152(DI), R13
+ MOVQ 160(DI), R14
+ XORQ DX, R11
+ ROLQ $0x06, R11
+ XORQ R8, R12
+ ROLQ $0x19, R12
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ CX, R10
+ ROLQ $0x01, R10
+ XORQ R10, AX
+ MOVQ AX, 80(SP)
+ XORQ AX, SI
+ XORQ R9, R13
+ ROLQ $0x08, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 88(SP)
+ XORQ AX, BP
+ XORQ BX, R14
+ ROLQ $0x12, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 96(SP)
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 104(SP)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 112(SP)
+ XORQ R10, R15
+
+ // Result m
+ MOVQ 40(DI), R11
+ XORQ BX, R11
+ MOVQ 88(DI), R12
+ ROLQ $0x24, R11
+ XORQ CX, R12
+ MOVQ 32(DI), R10
+ ROLQ $0x0a, R12
+ MOVQ R11, AX
+ MOVQ 136(DI), R13
+ ANDQ R12, AX
+ XORQ R9, R10
+ MOVQ 184(DI), R14
+ ROLQ $0x1b, R10
+ XORQ R10, AX
+ MOVQ AX, 120(SP)
+ XORQ AX, SI
+ XORQ DX, R13
+ ROLQ $0x0f, R13
+ MOVQ R12, AX
+ ORQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 128(SP)
+ XORQ AX, BP
+ XORQ R8, R14
+ ROLQ $0x38, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ORQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 136(SP)
+ ORQ R10, R11
+ XORQ R14, R11
+ MOVQ R11, 152(SP)
+ ANDQ R10, R14
+ XORQ R13, R14
+ MOVQ R14, 144(SP)
+ XORQ R11, R15
+
+ // Result s
+ MOVQ 16(DI), R10
+ MOVQ 64(DI), R11
+ MOVQ 112(DI), R12
+ XORQ DX, R10
+ MOVQ 120(DI), R13
+ ROLQ $0x3e, R10
+ XORQ R8, R11
+ MOVQ 168(DI), R14
+ ROLQ $0x37, R11
+ XORQ R9, R12
+ MOVQ R10, R9
+ XORQ CX, R14
+ ROLQ $0x02, R14
+ ANDQ R11, R9
+ XORQ R14, R9
+ MOVQ R9, 192(SP)
+ ROLQ $0x27, R12
+ XORQ R9, R15
+ NOTQ R11
+ XORQ BX, R13
+ MOVQ R11, BX
+ ANDQ R12, BX
+ XORQ R10, BX
+ MOVQ BX, 160(SP)
+ XORQ BX, SI
+ ROLQ $0x29, R13
+ MOVQ R12, CX
+ ORQ R13, CX
+ XORQ R11, CX
+ MOVQ CX, 168(SP)
+ XORQ CX, BP
+ MOVQ R13, DX
+ MOVQ R14, R8
+ ANDQ R14, DX
+ ORQ R10, R8
+ XORQ R12, DX
+ XORQ R13, R8
+ MOVQ DX, 176(SP)
+ MOVQ R8, 184(SP)
+
+ // Prepare round
+ MOVQ BP, BX
+ ROLQ $0x01, BX
+ MOVQ 16(SP), R12
+ XORQ 56(SP), DX
+ XORQ R15, BX
+ XORQ 96(SP), R12
+ XORQ 136(SP), DX
+ XORQ DX, R12
+ MOVQ R12, CX
+ ROLQ $0x01, CX
+ MOVQ 24(SP), R13
+ XORQ 64(SP), R8
+ XORQ SI, CX
+ XORQ 104(SP), R13
+ XORQ 144(SP), R8
+ XORQ R8, R13
+ MOVQ R13, DX
+ ROLQ $0x01, DX
+ MOVQ R15, R8
+ XORQ BP, DX
+ ROLQ $0x01, R8
+ MOVQ SI, R9
+ XORQ R12, R8
+ ROLQ $0x01, R9
+
+ // Result b
+ MOVQ (SP), R10
+ MOVQ 48(SP), R11
+ XORQ R13, R9
+ MOVQ 96(SP), R12
+ MOVQ 144(SP), R13
+ MOVQ 192(SP), R14
+ XORQ CX, R11
+ ROLQ $0x2c, R11
+ XORQ DX, R12
+ XORQ BX, R10
+ ROLQ $0x2b, R12
+ MOVQ R11, SI
+ MOVQ $0x8000000080008008, AX
+ ORQ R12, SI
+ XORQ R10, AX
+ XORQ AX, SI
+ MOVQ SI, (DI)
+ XORQ R9, R14
+ ROLQ $0x0e, R14
+ MOVQ R10, R15
+ ANDQ R11, R15
+ XORQ R14, R15
+ MOVQ R15, 32(DI)
+ XORQ R8, R13
+ ROLQ $0x15, R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 16(DI)
+ NOTQ R12
+ ORQ R10, R14
+ ORQ R13, R12
+ XORQ R13, R14
+ XORQ R11, R12
+ MOVQ R14, 24(DI)
+ MOVQ R12, 8(DI)
+ NOP
+
+ // Result g
+ MOVQ 72(SP), R11
+ XORQ R9, R11
+ MOVQ 80(SP), R12
+ ROLQ $0x14, R11
+ XORQ BX, R12
+ ROLQ $0x03, R12
+ MOVQ 24(SP), R10
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ R8, R10
+ MOVQ 128(SP), R13
+ MOVQ 176(SP), R14
+ ROLQ $0x1c, R10
+ XORQ R10, AX
+ MOVQ AX, 40(DI)
+ NOP
+ XORQ CX, R13
+ ROLQ $0x2d, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 48(DI)
+ NOP
+ XORQ DX, R14
+ ROLQ $0x3d, R14
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 64(DI)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 72(DI)
+ NOTQ R14
+ NOP
+ ORQ R14, R13
+ XORQ R12, R13
+ MOVQ R13, 56(DI)
+
+ // Result k
+ MOVQ 8(SP), R10
+ MOVQ 56(SP), R11
+ MOVQ 104(SP), R12
+ MOVQ 152(SP), R13
+ MOVQ 160(SP), R14
+ XORQ DX, R11
+ ROLQ $0x06, R11
+ XORQ R8, R12
+ ROLQ $0x19, R12
+ MOVQ R11, AX
+ ORQ R12, AX
+ XORQ CX, R10
+ ROLQ $0x01, R10
+ XORQ R10, AX
+ MOVQ AX, 80(DI)
+ NOP
+ XORQ R9, R13
+ ROLQ $0x08, R13
+ MOVQ R12, AX
+ ANDQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 88(DI)
+ NOP
+ XORQ BX, R14
+ ROLQ $0x12, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ANDQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 96(DI)
+ MOVQ R14, AX
+ ORQ R10, AX
+ XORQ R13, AX
+ MOVQ AX, 104(DI)
+ ANDQ R11, R10
+ XORQ R14, R10
+ MOVQ R10, 112(DI)
+ NOP
+
+ // Result m
+ MOVQ 40(SP), R11
+ XORQ BX, R11
+ MOVQ 88(SP), R12
+ ROLQ $0x24, R11
+ XORQ CX, R12
+ MOVQ 32(SP), R10
+ ROLQ $0x0a, R12
+ MOVQ R11, AX
+ MOVQ 136(SP), R13
+ ANDQ R12, AX
+ XORQ R9, R10
+ MOVQ 184(SP), R14
+ ROLQ $0x1b, R10
+ XORQ R10, AX
+ MOVQ AX, 120(DI)
+ NOP
+ XORQ DX, R13
+ ROLQ $0x0f, R13
+ MOVQ R12, AX
+ ORQ R13, AX
+ XORQ R11, AX
+ MOVQ AX, 128(DI)
+ NOP
+ XORQ R8, R14
+ ROLQ $0x38, R14
+ NOTQ R13
+ MOVQ R13, AX
+ ORQ R14, AX
+ XORQ R12, AX
+ MOVQ AX, 136(DI)
+ ORQ R10, R11
+ XORQ R14, R11
+ MOVQ R11, 152(DI)
+ ANDQ R10, R14
+ XORQ R13, R14
+ MOVQ R14, 144(DI)
+ NOP
+
+ // Result s
+ MOVQ 16(SP), R10
+ MOVQ 64(SP), R11
+ MOVQ 112(SP), R12
+ XORQ DX, R10
+ MOVQ 120(SP), R13
+ ROLQ $0x3e, R10
+ XORQ R8, R11
+ MOVQ 168(SP), R14
+ ROLQ $0x37, R11
+ XORQ R9, R12
+ MOVQ R10, R9
+ XORQ CX, R14
+ ROLQ $0x02, R14
+ ANDQ R11, R9
+ XORQ R14, R9
+ MOVQ R9, 192(DI)
+ ROLQ $0x27, R12
+ NOP
+ NOTQ R11
+ XORQ BX, R13
+ MOVQ R11, BX
+ ANDQ R12, BX
+ XORQ R10, BX
+ MOVQ BX, 160(DI)
+ NOP
+ ROLQ $0x29, R13
+ MOVQ R12, CX
+ ORQ R13, CX
+ XORQ R11, CX
+ MOVQ CX, 168(DI)
+ NOP
+ MOVQ R13, DX
+ MOVQ R14, R8
+ ANDQ R14, DX
+ ORQ R10, R8
+ XORQ R12, DX
+ XORQ R13, R8
+ MOVQ DX, 176(DI)
+ MOVQ R8, 184(DI)
+
+ // Revert the internal state to the user state
+ NOTQ 8(DI)
+ NOTQ 16(DI)
+ NOTQ 64(DI)
+ NOTQ 96(DI)
+ NOTQ 136(DI)
+ NOTQ 160(DI)
+ RET
diff --git a/crypto/keccak/sha3.go b/crypto/keccak/sha3.go
new file mode 100644
index 0000000000..a554323244
--- /dev/null
+++ b/crypto/keccak/sha3.go
@@ -0,0 +1,244 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package keccak
+
+import (
+ "crypto/subtle"
+ "encoding/binary"
+ "errors"
+ "unsafe"
+
+ "golang.org/x/sys/cpu"
+)
+
+// spongeDirection indicates the direction bytes are flowing through the sponge.
+type spongeDirection int
+
+const (
+ // spongeAbsorbing indicates that the sponge is absorbing input.
+ spongeAbsorbing spongeDirection = iota
+ // spongeSqueezing indicates that the sponge is being squeezed.
+ spongeSqueezing
+)
+
+type state struct {
+ a [1600 / 8]byte // main state of the hash
+
+ // a[n:rate] is the buffer. If absorbing, it's the remaining space to XOR
+ // into before running the permutation. If squeezing, it's the remaining
+ // output to produce before running the permutation.
+ n, rate int
+
+ // dsbyte contains the "domain separation" bits and the first bit of
+ // the padding. Sections 6.1 and 6.2 of [1] separate the outputs of the
+ // SHA-3 and SHAKE functions by appending bitstrings to the message.
+ // Using a little-endian bit-ordering convention, these are "01" for SHA-3
+ // and "1111" for SHAKE, or 00000010b and 00001111b, respectively. Then the
+ // padding rule from section 5.1 is applied to pad the message to a multiple
+ // of the rate, which involves adding a "1" bit, zero or more "0" bits, and
+ // a final "1" bit. We merge the first "1" bit from the padding into dsbyte,
+ // giving 00000110b (0x06) and 00011111b (0x1f).
+ // [1] http://csrc.nist.gov/publications/drafts/fips-202/fips_202_draft.pdf
+ // "Draft FIPS 202: SHA-3 Standard: Permutation-Based Hash and
+ // Extendable-Output Functions (May 2014)"
+ dsbyte byte
+
+ outputLen int // the default output size in bytes
+ state spongeDirection // whether the sponge is absorbing or squeezing
+}
+
+// BlockSize returns the rate of sponge underlying this hash function.
+func (d *state) BlockSize() int { return d.rate }
+
+// Size returns the output size of the hash function in bytes.
+func (d *state) Size() int { return d.outputLen }
+
+// Reset clears the internal state by zeroing the sponge state and
+// the buffer indexes, and setting Sponge.state to absorbing.
+func (d *state) Reset() {
+ // Zero the permutation's state.
+ for i := range d.a {
+ d.a[i] = 0
+ }
+ d.state = spongeAbsorbing
+ d.n = 0
+}
+
+func (d *state) clone() *state {
+ ret := *d
+ return &ret
+}
+
+// permute applies the KeccakF-1600 permutation.
+func (d *state) permute() {
+ var a *[25]uint64
+ if cpu.IsBigEndian {
+ a = new([25]uint64)
+ for i := range a {
+ a[i] = binary.LittleEndian.Uint64(d.a[i*8:])
+ }
+ } else {
+ a = (*[25]uint64)(unsafe.Pointer(&d.a))
+ }
+
+ keccakF1600(a)
+ d.n = 0
+
+ if cpu.IsBigEndian {
+ for i := range a {
+ binary.LittleEndian.PutUint64(d.a[i*8:], a[i])
+ }
+ }
+}
+
+// pads appends the domain separation bits in dsbyte, applies
+// the multi-bitrate 10..1 padding rule, and permutes the state.
+func (d *state) padAndPermute() {
+ // Pad with this instance's domain-separator bits. We know that there's
+ // at least one byte of space in the sponge because, if it were full,
+ // permute would have been called to empty it. dsbyte also contains the
+ // first one bit for the padding. See the comment in the state struct.
+ d.a[d.n] ^= d.dsbyte
+ // This adds the final one bit for the padding. Because of the way that
+ // bits are numbered from the LSB upwards, the final bit is the MSB of
+ // the last byte.
+ d.a[d.rate-1] ^= 0x80
+ // Apply the permutation
+ d.permute()
+ d.state = spongeSqueezing
+}
+
+// Write absorbs more data into the hash's state. It panics if any
+// output has already been read.
+func (d *state) Write(p []byte) (n int, err error) {
+ if d.state != spongeAbsorbing {
+ panic("sha3: Write after Read")
+ }
+
+ n = len(p)
+
+ for len(p) > 0 {
+ x := subtle.XORBytes(d.a[d.n:d.rate], d.a[d.n:d.rate], p)
+ d.n += x
+ p = p[x:]
+
+ // If the sponge is full, apply the permutation.
+ if d.n == d.rate {
+ d.permute()
+ }
+ }
+
+ return
+}
+
+// Read squeezes an arbitrary number of bytes from the sponge.
+func (d *state) Read(out []byte) (n int, err error) {
+ // If we're still absorbing, pad and apply the permutation.
+ if d.state == spongeAbsorbing {
+ d.padAndPermute()
+ }
+
+ n = len(out)
+
+ // Now, do the squeezing.
+ for len(out) > 0 {
+ // Apply the permutation if we've squeezed the sponge dry.
+ if d.n == d.rate {
+ d.permute()
+ }
+
+ x := copy(out, d.a[d.n:d.rate])
+ d.n += x
+ out = out[x:]
+ }
+
+ return
+}
+
+// Sum applies padding to the hash state and then squeezes out the desired
+// number of output bytes. It panics if any output has already been read.
+func (d *state) Sum(in []byte) []byte {
+ if d.state != spongeAbsorbing {
+ panic("sha3: Sum after Read")
+ }
+
+ // Make a copy of the original hash so that caller can keep writing
+ // and summing.
+ dup := d.clone()
+ hash := make([]byte, dup.outputLen, 64) // explicit cap to allow stack allocation
+ dup.Read(hash)
+ return append(in, hash...)
+}
+
+const (
+ magicSHA3 = "sha\x08"
+ magicShake = "sha\x09"
+ magicCShake = "sha\x0a"
+ magicKeccak = "sha\x0b"
+ // magic || rate || main state || n || sponge direction
+ marshaledSize = len(magicSHA3) + 1 + 200 + 1 + 1
+)
+
+func (d *state) MarshalBinary() ([]byte, error) {
+ return d.AppendBinary(make([]byte, 0, marshaledSize))
+}
+
+func (d *state) AppendBinary(b []byte) ([]byte, error) {
+ switch d.dsbyte {
+ case dsbyteSHA3:
+ b = append(b, magicSHA3...)
+ case dsbyteShake:
+ b = append(b, magicShake...)
+ case dsbyteCShake:
+ b = append(b, magicCShake...)
+ case dsbyteKeccak:
+ b = append(b, magicKeccak...)
+ default:
+ panic("unknown dsbyte")
+ }
+ // rate is at most 168, and n is at most rate.
+ b = append(b, byte(d.rate))
+ b = append(b, d.a[:]...)
+ b = append(b, byte(d.n), byte(d.state))
+ return b, nil
+}
+
+func (d *state) UnmarshalBinary(b []byte) error {
+ if len(b) != marshaledSize {
+ return errors.New("sha3: invalid hash state")
+ }
+
+ magic := string(b[:len(magicSHA3)])
+ b = b[len(magicSHA3):]
+ switch {
+ case magic == magicSHA3 && d.dsbyte == dsbyteSHA3:
+ case magic == magicShake && d.dsbyte == dsbyteShake:
+ case magic == magicCShake && d.dsbyte == dsbyteCShake:
+ case magic == magicKeccak && d.dsbyte == dsbyteKeccak:
+ default:
+ return errors.New("sha3: invalid hash state identifier")
+ }
+
+ rate := int(b[0])
+ b = b[1:]
+ if rate != d.rate {
+ return errors.New("sha3: invalid hash state function")
+ }
+
+ copy(d.a[:], b)
+ b = b[len(d.a):]
+
+ n, state := int(b[0]), spongeDirection(b[1])
+ if n > d.rate {
+ return errors.New("sha3: invalid hash state")
+ }
+ d.n = n
+ if state != spongeAbsorbing && state != spongeSqueezing {
+ return errors.New("sha3: invalid hash state")
+ }
+ d.state = state
+
+ return nil
+}
diff --git a/crypto/keccak/sha3_test.go b/crypto/keccak/sha3_test.go
new file mode 100644
index 0000000000..28a20ec72d
--- /dev/null
+++ b/crypto/keccak/sha3_test.go
@@ -0,0 +1,210 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package keccak
+
+// Tests include all the ShortMsgKATs provided by the Keccak team at
+// https://github.com/gvanas/KeccakCodePackage
+//
+// They only include the zero-bit case of the bitwise testvectors
+// published by NIST in the draft of FIPS-202.
+
+import (
+ "bytes"
+ "compress/flate"
+ "encoding"
+ "encoding/hex"
+ "encoding/json"
+ "hash"
+ "math/rand"
+ "os"
+ "strings"
+ "testing"
+)
+
+const (
+ testString = "brekeccakkeccak koax koax"
+ katFilename = "testdata/keccakKats.json.deflate"
+)
+
+// testDigests contains functions returning hash.Hash instances
+// with output-length equal to the KAT length for SHA-3, Keccak
+// and SHAKE instances.
+var testDigests = map[string]func() hash.Hash{
+ "Keccak-256": NewLegacyKeccak256,
+ "Keccak-512": NewLegacyKeccak512,
+}
+
+// decodeHex converts a hex-encoded string into a raw byte string.
+func decodeHex(s string) []byte {
+ b, err := hex.DecodeString(s)
+ if err != nil {
+ panic(err)
+ }
+ return b
+}
+
+// structs used to marshal JSON test-cases.
+type KeccakKats struct {
+ Kats map[string][]struct {
+ Digest string `json:"digest"`
+ Length int64 `json:"length"`
+ Message string `json:"message"`
+
+ // Defined only for cSHAKE
+ N string `json:"N"`
+ S string `json:"S"`
+ }
+}
+
+// TestKeccakKats tests the SHA-3 and Shake implementations against all the
+// ShortMsgKATs from https://github.com/gvanas/KeccakCodePackage
+// (The testvectors are stored in keccakKats.json.deflate due to their length.)
+func TestKeccakKats(t *testing.T) {
+ // Read the KATs.
+ deflated, err := os.Open(katFilename)
+ if err != nil {
+ t.Errorf("error opening %s: %s", katFilename, err)
+ }
+ file := flate.NewReader(deflated)
+ dec := json.NewDecoder(file)
+ var katSet KeccakKats
+ err = dec.Decode(&katSet)
+ if err != nil {
+ t.Errorf("error decoding KATs: %s", err)
+ }
+
+ for algo, function := range testDigests {
+ d := function()
+ for _, kat := range katSet.Kats[algo] {
+ d.Reset()
+ in, err := hex.DecodeString(kat.Message)
+ if err != nil {
+ t.Errorf("error decoding KAT: %s", err)
+ }
+ d.Write(in[:kat.Length/8])
+ got := strings.ToUpper(hex.EncodeToString(d.Sum(nil)))
+ if got != kat.Digest {
+ t.Errorf("function=%s, length=%d\nmessage:\n %s\ngot:\n %s\nwanted:\n %s",
+ algo, kat.Length, kat.Message, got, kat.Digest)
+ t.Logf("wanted %+v", kat)
+ t.FailNow()
+ }
+ continue
+ }
+ }
+}
+
+// TestKeccak does a basic test of the non-standardized Keccak hash functions.
+func TestKeccak(t *testing.T) {
+ tests := []struct {
+ fn func() hash.Hash
+ data []byte
+ want string
+ }{
+ {
+ NewLegacyKeccak256,
+ []byte("abc"),
+ "4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45",
+ },
+ {
+ NewLegacyKeccak512,
+ []byte("abc"),
+ "18587dc2ea106b9a1563e32b3312421ca164c7f1f07bc922a9c83d77cea3a1e5d0c69910739025372dc14ac9642629379540c17e2a65b19d77aa511a9d00bb96",
+ },
+ }
+
+ for _, u := range tests {
+ h := u.fn()
+ h.Write(u.data)
+ got := h.Sum(nil)
+ want := decodeHex(u.want)
+ if !bytes.Equal(got, want) {
+ t.Errorf("unexpected hash for size %d: got '%x' want '%s'", h.Size()*8, got, u.want)
+ }
+ }
+}
+
+// TestUnalignedWrite tests that writing data in an arbitrary pattern with
+// small input buffers.
+func TestUnalignedWrite(t *testing.T) {
+ buf := sequentialBytes(0x10000)
+ for alg, df := range testDigests {
+ d := df()
+ d.Reset()
+ d.Write(buf)
+ want := d.Sum(nil)
+ d.Reset()
+ for i := 0; i < len(buf); {
+ // Cycle through offsets which make a 137 byte sequence.
+ // Because 137 is prime this sequence should exercise all corner cases.
+ offsets := [17]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1}
+ for _, j := range offsets {
+ if v := len(buf) - i; v < j {
+ j = v
+ }
+ d.Write(buf[i : i+j])
+ i += j
+ }
+ }
+ got := d.Sum(nil)
+ if !bytes.Equal(got, want) {
+ t.Errorf("Unaligned writes, alg=%s\ngot %q, want %q", alg, got, want)
+ }
+ }
+}
+
+// sequentialBytes produces a buffer of size consecutive bytes 0x00, 0x01, ..., used for testing.
+//
+// The alignment of each slice is intentionally randomized to detect alignment
+// issues in the implementation. See https://golang.org/issue/37644.
+// Ideally, the compiler should fuzz the alignment itself.
+// (See https://golang.org/issue/35128.)
+func sequentialBytes(size int) []byte {
+ alignmentOffset := rand.Intn(8)
+ result := make([]byte, size+alignmentOffset)[alignmentOffset:]
+ for i := range result {
+ result[i] = byte(i)
+ }
+ return result
+}
+
+func TestMarshalUnmarshal(t *testing.T) {
+ t.Run("Keccak-256", func(t *testing.T) { testMarshalUnmarshal(t, NewLegacyKeccak256()) })
+ t.Run("Keccak-512", func(t *testing.T) { testMarshalUnmarshal(t, NewLegacyKeccak512()) })
+}
+
+// TODO(filippo): move this to crypto/internal/cryptotest.
+func testMarshalUnmarshal(t *testing.T, h hash.Hash) {
+ buf := make([]byte, 200)
+ rand.Read(buf)
+ n := rand.Intn(200)
+ h.Write(buf)
+ want := h.Sum(nil)
+ h.Reset()
+ h.Write(buf[:n])
+ b, err := h.(encoding.BinaryMarshaler).MarshalBinary()
+ if err != nil {
+ t.Errorf("MarshalBinary: %v", err)
+ }
+ h.Write(bytes.Repeat([]byte{0}, 200))
+ if err := h.(encoding.BinaryUnmarshaler).UnmarshalBinary(b); err != nil {
+ t.Errorf("UnmarshalBinary: %v", err)
+ }
+ h.Write(buf[n:])
+ got := h.Sum(nil)
+ if !bytes.Equal(got, want) {
+ t.Errorf("got %x, want %x", got, want)
+ }
+}
+
+// BenchmarkPermutationFunction measures the speed of the permutation function
+// with no input data.
+func BenchmarkPermutationFunction(b *testing.B) {
+ b.SetBytes(int64(200))
+ var lanes [25]uint64
+ for i := 0; i < b.N; i++ {
+ keccakF1600(&lanes)
+ }
+}
diff --git a/crypto/keccak/testdata/keccakKats.json.deflate b/crypto/keccak/testdata/keccakKats.json.deflate
new file mode 100644
index 0000000000..7a94c2f8bc
Binary files /dev/null and b/crypto/keccak/testdata/keccakKats.json.deflate differ
diff --git a/eth/backend.go b/eth/backend.go
index aed1542aeb..eaa68b501c 100644
--- a/eth/backend.go
+++ b/eth/backend.go
@@ -495,6 +495,9 @@ func (s *Ethereum) updateFilterMapsHeads() {
if head == nil || newHead.Hash() != head.Hash() {
head = newHead
chainView := s.newChainView(head)
+ if chainView == nil {
+ return
+ }
historyCutoff, _ := s.blockchain.HistoryPruningCutoff()
var finalBlock uint64
if fb := s.blockchain.CurrentFinalBlock(); fb != nil {
diff --git a/eth/protocols/snap/sync_test.go b/eth/protocols/snap/sync_test.go
index 713b358ff8..ed83af860a 100644
--- a/eth/protocols/snap/sync_test.go
+++ b/eth/protocols/snap/sync_test.go
@@ -32,6 +32,7 @@ import (
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/crypto/keccak"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/internal/testrand"
"github.com/ethereum/go-ethereum/log"
@@ -41,7 +42,6 @@ import (
"github.com/ethereum/go-ethereum/triedb"
"github.com/ethereum/go-ethereum/triedb/pathdb"
"github.com/holiman/uint256"
- "golang.org/x/crypto/sha3"
)
func TestHashing(t *testing.T) {
@@ -55,7 +55,7 @@ func TestHashing(t *testing.T) {
}
var want, got string
var old = func() {
- hasher := sha3.NewLegacyKeccak256()
+ hasher := keccak.NewLegacyKeccak256()
for i := 0; i < len(bytecodes); i++ {
hasher.Reset()
hasher.Write(bytecodes[i])
@@ -88,7 +88,7 @@ func BenchmarkHashing(b *testing.B) {
bytecodes[i] = buf
}
var old = func() {
- hasher := sha3.NewLegacyKeccak256()
+ hasher := keccak.NewLegacyKeccak256()
for i := 0; i < len(bytecodes); i++ {
hasher.Reset()
hasher.Write(bytecodes[i])
diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go
index 609c3f4d8b..f76c35a1d5 100644
--- a/eth/tracers/api_test.go
+++ b/eth/tracers/api_test.go
@@ -1373,3 +1373,350 @@ func TestStandardTraceBlockToFile(t *testing.T) {
}
}
}
+
+func TestTraceBadBlock(t *testing.T) {
+ t.Parallel()
+
+ var (
+ accounts = newAccounts(2)
+ storageContract = common.HexToAddress("0x00000000000000000000000000000000deadbeef")
+ signer = types.HomesteadSigner{}
+ txHashs = make([]common.Hash, 0, 2)
+ genesis = &core.Genesis{
+ Config: params.TestChainConfig,
+ Alloc: types.GenesisAlloc{
+ accounts[0].addr: {Balance: big.NewInt(params.Ether)},
+ accounts[1].addr: {Balance: big.NewInt(params.Ether)},
+ storageContract: {
+ Nonce: 1,
+ Balance: big.NewInt(0),
+ Code: []byte{
+ byte(vm.PUSH1), 0x2a, // push 42
+ byte(vm.PUSH1), 0x00, // push slot 0
+ byte(vm.SSTORE), // sstore(0, 42)
+ byte(vm.STOP),
+ },
+ },
+ },
+ }
+ )
+ backend := newTestBackend(t, 1, genesis, func(i int, b *core.BlockGen) {
+ // tx 0: plain transfer
+ tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{
+ Nonce: 0,
+ To: &accounts[1].addr,
+ Value: big.NewInt(1000),
+ Gas: params.TxGas,
+ GasPrice: b.BaseFee(),
+ Data: nil}),
+ signer, accounts[0].key)
+ b.AddTx(tx)
+ txHashs = append(txHashs, tx.Hash())
+
+ // tx 1: call storage contract (executes PUSH1, PUSH1, SSTORE, STOP)
+ tx, _ = types.SignTx(types.NewTx(&types.LegacyTx{
+ Nonce: 1,
+ To: &storageContract,
+ Value: big.NewInt(0),
+ Gas: 50000,
+ GasPrice: b.BaseFee(),
+ Data: nil}),
+ signer, accounts[0].key)
+ b.AddTx(tx)
+ txHashs = append(txHashs, tx.Hash())
+ })
+ defer backend.teardown()
+
+ // Write the block as a bad block so parent state is available
+ block := backend.chain.GetBlockByNumber(1)
+ rawdb.WriteBadBlock(backend.chaindb, block)
+
+ api := NewAPI(backend)
+ result, err := api.TraceBadBlock(context.Background(), block.Hash(), nil)
+ if err != nil {
+ t.Fatalf("want no error, have %v", err)
+ }
+ if len(result) != 2 {
+ t.Fatalf("expected 2 tx traces, got %d", len(result))
+ }
+
+ // First tx: plain transfer
+ have, _ := json.Marshal(result)
+ var traces []struct {
+ TxHash common.Hash `json:"txHash"`
+ Result struct {
+ Gas uint64 `json:"gas"`
+ Failed bool `json:"failed"`
+ StructLogs []json.RawMessage `json:"structLogs"`
+ } `json:"result"`
+ }
+ if err := json.Unmarshal(have, &traces); err != nil {
+ t.Fatalf("failed to unmarshal traces: %v", err)
+ }
+ if traces[0].TxHash != txHashs[0] {
+ t.Errorf("tx 0: hash mismatch, have %v, want %v", traces[0].TxHash, txHashs[0])
+ }
+ if traces[0].Result.Gas != params.TxGas {
+ t.Errorf("tx 0: gas mismatch, have %d, want %d", traces[0].Result.Gas, params.TxGas)
+ }
+ if len(traces[0].Result.StructLogs) != 0 {
+ t.Errorf("tx 0: expected empty structLogs for plain transfer, got %d entries", len(traces[0].Result.StructLogs))
+ }
+
+ // Second tx: contract call
+ if traces[1].TxHash != txHashs[1] {
+ t.Errorf("tx 1: hash mismatch, have %v, want %v", traces[1].TxHash, txHashs[1])
+ }
+ if traces[1].Result.Failed {
+ t.Error("tx 1: expected success, got failed")
+ }
+ // Contract has 4 opcodes: PUSH1, PUSH1, SSTORE, STOP
+ if len(traces[1].Result.StructLogs) != 4 {
+ t.Errorf("tx 1: expected 4 structLog entries for contract call, got %d", len(traces[1].Result.StructLogs))
+ }
+
+ // Non-existent bad block
+ _, err = api.TraceBadBlock(context.Background(), common.Hash{42}, nil)
+ if err == nil {
+ t.Fatal("want error for non-existent bad block, have none")
+ }
+ wantErr := fmt.Sprintf("bad block %#x not found", common.Hash{42})
+ if err.Error() != wantErr {
+ t.Errorf("error mismatch, want '%s', have '%v'", wantErr, err)
+ }
+}
+
+func TestIntermediateRoots(t *testing.T) {
+ t.Parallel()
+
+ // Initialize test accounts and a contract that writes to storage.
+ var (
+ accounts = newAccounts(2)
+ storageContract = common.HexToAddress("0x00000000000000000000000000000000deadbeef")
+ signer = types.HomesteadSigner{}
+ genesis = &core.Genesis{
+ Config: params.TestChainConfig,
+ Alloc: types.GenesisAlloc{
+ accounts[0].addr: {Balance: big.NewInt(params.Ether)},
+ accounts[1].addr: {Balance: big.NewInt(params.Ether)},
+ // Contract: SSTORE(CALLVALUE, CALLVALUE)
+ storageContract: {
+ Nonce: 1,
+ Balance: big.NewInt(0),
+ Code: []byte{
+ byte(vm.CALLVALUE),
+ byte(vm.CALLVALUE),
+ byte(vm.SSTORE),
+ byte(vm.STOP),
+ },
+ },
+ },
+ }
+ )
+ backend := newTestBackend(t, 1, genesis, func(i int, b *core.BlockGen) {
+ // tx 0: plain transfer
+ tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{
+ Nonce: 0,
+ To: &accounts[1].addr,
+ Value: big.NewInt(1000),
+ Gas: params.TxGas,
+ GasPrice: b.BaseFee(),
+ Data: nil}),
+ signer, accounts[0].key)
+ b.AddTx(tx)
+
+ // tx 1: sstore(1, 1)
+ tx, _ = types.SignTx(types.NewTx(&types.LegacyTx{
+ Nonce: 1,
+ To: &storageContract,
+ Value: big.NewInt(1),
+ Gas: 50000,
+ GasPrice: b.BaseFee(),
+ Data: nil}),
+ signer, accounts[0].key)
+ b.AddTx(tx)
+
+ // tx 2: sstore(2, 2)
+ tx, _ = types.SignTx(types.NewTx(&types.LegacyTx{
+ Nonce: 2,
+ To: &storageContract,
+ Value: big.NewInt(2),
+ Gas: 50000,
+ GasPrice: b.BaseFee(),
+ Data: nil}),
+ signer, accounts[0].key)
+ b.AddTx(tx)
+ })
+ defer backend.teardown()
+
+ api := NewAPI(backend)
+ block := backend.chain.GetBlockByNumber(1)
+
+ // Should return one root per tx
+ roots, err := api.IntermediateRoots(context.Background(), block.Hash(), nil)
+ if err != nil {
+ t.Fatalf("want no error, have %v", err)
+ }
+ if len(roots) != 3 {
+ t.Fatalf("root count mismatch, have %d, want 3", len(roots))
+ }
+ for i, root := range roots {
+ if root == (common.Hash{}) {
+ t.Errorf("root[%d] should not be zero", i)
+ }
+ }
+ if roots[0] == roots[1] {
+ t.Error("root[0] and root[1] should differ (transfer vs sstore)")
+ }
+ if roots[1] == roots[2] {
+ t.Error("root[1] and root[2] should differ (sstore to different slots)")
+ }
+
+ // Intermediate roots of a bad block
+ rawdb.WriteBadBlock(backend.chaindb, block)
+ badRoots, err := api.IntermediateRoots(context.Background(), block.Hash(), nil)
+ if err != nil {
+ t.Fatalf("want no error for bad block fallback, have %v", err)
+ }
+ if !reflect.DeepEqual(roots, badRoots) {
+ t.Errorf("bad block roots mismatch, have %v, want %v", badRoots, roots)
+ }
+
+ // Genesis block: should return error
+ genesisBlock := backend.chain.GetBlockByNumber(0)
+ _, err = api.IntermediateRoots(context.Background(), genesisBlock.Hash(), nil)
+ if err == nil || err.Error() != "genesis is not traceable" {
+ t.Fatalf("want 'genesis is not traceable' error, have %v", err)
+ }
+
+ // Non-existent block: should return error
+ _, err = api.IntermediateRoots(context.Background(), common.Hash{42}, nil)
+ if err == nil {
+ t.Fatal("want error for non-existent block, have none")
+ }
+ wantErr := fmt.Sprintf("block %#x not found", common.Hash{42})
+ if err.Error() != wantErr {
+ t.Errorf("error mismatch, want '%s', have '%v'", wantErr, err)
+ }
+}
+
+func TestStandardTraceBadBlockToFile(t *testing.T) {
+ var (
+ key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
+ address = crypto.PubkeyToAddress(key.PublicKey)
+ funds = big.NewInt(1000000000000000)
+
+ aa = common.HexToAddress("0x7217d81b76bdd8707601e959454e3d776aee5f43")
+ aaCode = []byte{byte(vm.PUSH1), 0x00, byte(vm.POP)}
+
+ bb = common.HexToAddress("0x7217d81b76bdd8707601e959454e3d776aee5f44")
+ bbCode = []byte{byte(vm.PUSH2), 0x00, 0x01, byte(vm.POP)}
+ )
+
+ genesis := &core.Genesis{
+ Config: params.TestChainConfig,
+ Alloc: types.GenesisAlloc{
+ address: {Balance: funds},
+ aa: {
+ Code: aaCode,
+ Nonce: 1,
+ Balance: big.NewInt(0),
+ },
+ bb: {
+ Code: bbCode,
+ Nonce: 1,
+ Balance: big.NewInt(0),
+ },
+ },
+ }
+ txHashs := make([]common.Hash, 0, 2)
+ backend := newTestBackend(t, 1, genesis, func(i int, b *core.BlockGen) {
+ b.SetCoinbase(common.Address{1})
+ tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{
+ Nonce: 0,
+ To: &aa,
+ Value: big.NewInt(0),
+ Gas: 50000,
+ GasPrice: b.BaseFee(),
+ Data: nil,
+ }), types.HomesteadSigner{}, key)
+ b.AddTx(tx)
+ txHashs = append(txHashs, tx.Hash())
+
+ tx, _ = types.SignTx(types.NewTx(&types.LegacyTx{
+ Nonce: 1,
+ To: &bb,
+ Value: big.NewInt(1),
+ Gas: 100000,
+ GasPrice: b.BaseFee(),
+ Data: nil,
+ }), types.HomesteadSigner{}, key)
+ b.AddTx(tx)
+ txHashs = append(txHashs, tx.Hash())
+ })
+ defer backend.teardown()
+
+ // Write the block as a bad block
+ block := backend.chain.GetBlockByNumber(1)
+ rawdb.WriteBadBlock(backend.chaindb, block)
+
+ var testSuite = []struct {
+ config *StdTraceConfig
+ want []string
+ }{
+ {
+ // All txs traced
+ config: nil,
+ want: []string{
+ `{"pc":0,"op":96,"gas":"0x7148","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"}
+{"pc":2,"op":80,"gas":"0x7145","gasCost":"0x2","memSize":0,"stack":["0x0"],"depth":1,"refund":0,"opName":"POP"}
+{"pc":3,"op":0,"gas":"0x7143","gasCost":"0x0","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"STOP"}
+{"output":"","gasUsed":"0x5"}
+`,
+ `{"pc":0,"op":97,"gas":"0x13498","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH2"}
+{"pc":3,"op":80,"gas":"0x13495","gasCost":"0x2","memSize":0,"stack":["0x1"],"depth":1,"refund":0,"opName":"POP"}
+{"pc":4,"op":0,"gas":"0x13493","gasCost":"0x0","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"STOP"}
+{"output":"","gasUsed":"0x5"}
+`,
+ },
+ },
+ {
+ // Specific tx traced
+ config: &StdTraceConfig{TxHash: txHashs[1]},
+ want: []string{
+ `{"pc":0,"op":97,"gas":"0x13498","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH2"}
+{"pc":3,"op":80,"gas":"0x13495","gasCost":"0x2","memSize":0,"stack":["0x1"],"depth":1,"refund":0,"opName":"POP"}
+{"pc":4,"op":0,"gas":"0x13493","gasCost":"0x0","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"STOP"}
+{"output":"","gasUsed":"0x5"}
+`,
+ },
+ },
+ }
+
+ api := NewAPI(backend)
+ for i, tc := range testSuite {
+ txTraces, err := api.StandardTraceBadBlockToFile(context.Background(), block.Hash(), tc.config)
+ if err != nil {
+ t.Fatalf("test %d: unexpected error %v", i, err)
+ }
+ if len(txTraces) != len(tc.want) {
+ t.Fatalf("test %d: file count mismatch, have %d, want %d", i, len(txTraces), len(tc.want))
+ }
+ for j, traceFileName := range txTraces {
+ defer os.Remove(traceFileName)
+ traceReceived, err := os.ReadFile(traceFileName)
+ if err != nil {
+ t.Fatalf("test %d: could not read trace file: %v", i, err)
+ }
+ if tc.want[j] != string(traceReceived) {
+ t.Fatalf("test %d, trace %d: result mismatch\nhave:\n%s\nwant:\n%s", i, j, string(traceReceived), tc.want[j])
+ }
+ }
+ }
+
+ // Non-existent bad block
+ _, err := api.StandardTraceBadBlockToFile(context.Background(), common.Hash{42}, nil)
+ if err == nil {
+ t.Fatal("want error for non-existent bad block, have none")
+ }
+}
diff --git a/eth/tracers/js/tracer_test.go b/eth/tracers/js/tracer_test.go
index 7f376a27fc..b21e104abc 100644
--- a/eth/tracers/js/tracer_test.go
+++ b/eth/tracers/js/tracer_test.go
@@ -46,7 +46,7 @@ type vmContext struct {
}
func testCtx() *vmContext {
- return &vmContext{blockCtx: vm.BlockContext{BlockNumber: big.NewInt(1), BaseFee: big.NewInt(0)}, txCtx: vm.TxContext{GasPrice: big.NewInt(100000)}}
+ return &vmContext{blockCtx: vm.BlockContext{BlockNumber: big.NewInt(1), BaseFee: big.NewInt(0)}, txCtx: vm.TxContext{GasPrice: uint256.NewInt(100000)}}
}
func runTrace(tracer *tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainConfig, contractCode []byte) (json.RawMessage, error) {
@@ -63,7 +63,7 @@ func runTrace(tracer *tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainCo
contract.Code = contractCode
}
- tracer.OnTxStart(evm.GetVMContext(), types.NewTx(&types.LegacyTx{Gas: gasLimit, GasPrice: vmctx.txCtx.GasPrice}), contract.Caller())
+ tracer.OnTxStart(evm.GetVMContext(), types.NewTx(&types.LegacyTx{Gas: gasLimit, GasPrice: vmctx.txCtx.GasPrice.ToBig()}), contract.Caller())
tracer.OnEnter(0, byte(vm.CALL), contract.Caller(), contract.Address(), []byte{}, startGas, value.ToBig())
ret, err := evm.Run(contract, []byte{}, false)
tracer.OnExit(0, ret, startGas-contract.Gas, err, true)
@@ -186,7 +186,7 @@ func TestHaltBetweenSteps(t *testing.T) {
Contract: vm.NewContract(common.Address{}, common.Address{}, uint256.NewInt(0), 0, nil),
}
evm := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, &dummyStatedb{}, chainConfig, vm.Config{Tracer: tracer.Hooks})
- evm.SetTxContext(vm.TxContext{GasPrice: big.NewInt(1)})
+ evm.SetTxContext(vm.TxContext{GasPrice: uint256.NewInt(1)})
tracer.OnTxStart(evm.GetVMContext(), types.NewTx(&types.LegacyTx{}), common.Address{})
tracer.OnEnter(0, byte(vm.CALL), common.Address{}, common.Address{}, []byte{}, 0, big.NewInt(0))
tracer.OnOpcode(0, 0, 0, 0, scope, nil, 0, nil)
@@ -210,7 +210,7 @@ func TestNoStepExec(t *testing.T) {
t.Fatal(err)
}
evm := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, &dummyStatedb{}, chainConfig, vm.Config{Tracer: tracer.Hooks})
- evm.SetTxContext(vm.TxContext{GasPrice: big.NewInt(100)})
+ evm.SetTxContext(vm.TxContext{GasPrice: uint256.NewInt(100)})
tracer.OnTxStart(evm.GetVMContext(), types.NewTx(&types.LegacyTx{}), common.Address{})
tracer.OnEnter(0, byte(vm.CALL), common.Address{}, common.Address{}, []byte{}, 1000, big.NewInt(0))
tracer.OnExit(0, nil, 0, nil, false)
@@ -240,7 +240,7 @@ func TestIsPrecompile(t *testing.T) {
chaincfg.ByzantiumBlock = big.NewInt(100)
chaincfg.IstanbulBlock = big.NewInt(200)
chaincfg.BerlinBlock = big.NewInt(300)
- txCtx := vm.TxContext{GasPrice: big.NewInt(100000)}
+ txCtx := vm.TxContext{GasPrice: uint256.NewInt(100000)}
tracer, err := newJsTracer("{addr: toAddress('0000000000000000000000000000000000000009'), res: null, step: function() { this.res = isPrecompiled(this.addr); }, fault: function() {}, result: function() { return this.res; }}", nil, nil, chaincfg)
if err != nil {
t.Fatal(err)
diff --git a/ethclient/gethclient/gen_callframe_json.go b/ethclient/gethclient/gen_callframe_json.go
new file mode 100644
index 0000000000..48df2790cc
--- /dev/null
+++ b/ethclient/gethclient/gen_callframe_json.go
@@ -0,0 +1,104 @@
+// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
+
+package gethclient
+
+import (
+ "encoding/json"
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+)
+
+var _ = (*callFrameMarshaling)(nil)
+
+// MarshalJSON marshals as JSON.
+func (c CallFrame) MarshalJSON() ([]byte, error) {
+ type CallFrame0 struct {
+ Type string `json:"type"`
+ From common.Address `json:"from"`
+ Gas hexutil.Uint64 `json:"gas"`
+ GasUsed hexutil.Uint64 `json:"gasUsed"`
+ To *common.Address `json:"to,omitempty"`
+ Input hexutil.Bytes `json:"input"`
+ Output hexutil.Bytes `json:"output,omitempty"`
+ Error string `json:"error,omitempty"`
+ RevertReason string `json:"revertReason,omitempty"`
+ Calls []CallFrame `json:"calls,omitempty"`
+ Logs []CallLog `json:"logs,omitempty"`
+ Value *hexutil.Big `json:"value,omitempty"`
+ }
+ var enc CallFrame0
+ enc.Type = c.Type
+ enc.From = c.From
+ enc.Gas = hexutil.Uint64(c.Gas)
+ enc.GasUsed = hexutil.Uint64(c.GasUsed)
+ enc.To = c.To
+ enc.Input = c.Input
+ enc.Output = c.Output
+ enc.Error = c.Error
+ enc.RevertReason = c.RevertReason
+ enc.Calls = c.Calls
+ enc.Logs = c.Logs
+ enc.Value = (*hexutil.Big)(c.Value)
+ return json.Marshal(&enc)
+}
+
+// UnmarshalJSON unmarshals from JSON.
+func (c *CallFrame) UnmarshalJSON(input []byte) error {
+ type CallFrame0 struct {
+ Type *string `json:"type"`
+ From *common.Address `json:"from"`
+ Gas *hexutil.Uint64 `json:"gas"`
+ GasUsed *hexutil.Uint64 `json:"gasUsed"`
+ To *common.Address `json:"to,omitempty"`
+ Input *hexutil.Bytes `json:"input"`
+ Output *hexutil.Bytes `json:"output,omitempty"`
+ Error *string `json:"error,omitempty"`
+ RevertReason *string `json:"revertReason,omitempty"`
+ Calls []CallFrame `json:"calls,omitempty"`
+ Logs []CallLog `json:"logs,omitempty"`
+ Value *hexutil.Big `json:"value,omitempty"`
+ }
+ var dec CallFrame0
+ if err := json.Unmarshal(input, &dec); err != nil {
+ return err
+ }
+ if dec.Type != nil {
+ c.Type = *dec.Type
+ }
+ if dec.From != nil {
+ c.From = *dec.From
+ }
+ if dec.Gas != nil {
+ c.Gas = uint64(*dec.Gas)
+ }
+ if dec.GasUsed != nil {
+ c.GasUsed = uint64(*dec.GasUsed)
+ }
+ if dec.To != nil {
+ c.To = dec.To
+ }
+ if dec.Input != nil {
+ c.Input = *dec.Input
+ }
+ if dec.Output != nil {
+ c.Output = *dec.Output
+ }
+ if dec.Error != nil {
+ c.Error = *dec.Error
+ }
+ if dec.RevertReason != nil {
+ c.RevertReason = *dec.RevertReason
+ }
+ if dec.Calls != nil {
+ c.Calls = dec.Calls
+ }
+ if dec.Logs != nil {
+ c.Logs = dec.Logs
+ }
+ if dec.Value != nil {
+ c.Value = (*big.Int)(dec.Value)
+ }
+ return nil
+}
diff --git a/ethclient/gethclient/gen_calllog_json.go b/ethclient/gethclient/gen_calllog_json.go
new file mode 100644
index 0000000000..50e25d4bb3
--- /dev/null
+++ b/ethclient/gethclient/gen_calllog_json.go
@@ -0,0 +1,61 @@
+// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
+
+package gethclient
+
+import (
+ "encoding/json"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+)
+
+var _ = (*callLogMarshaling)(nil)
+
+// MarshalJSON marshals as JSON.
+func (c CallLog) MarshalJSON() ([]byte, error) {
+ type CallLog struct {
+ Address common.Address `json:"address"`
+ Topics []common.Hash `json:"topics"`
+ Data hexutil.Bytes `json:"data"`
+ Index hexutil.Uint `json:"index"`
+ Position hexutil.Uint `json:"position"`
+ }
+ var enc CallLog
+ enc.Address = c.Address
+ enc.Topics = c.Topics
+ enc.Data = c.Data
+ enc.Index = hexutil.Uint(c.Index)
+ enc.Position = hexutil.Uint(c.Position)
+ return json.Marshal(&enc)
+}
+
+// UnmarshalJSON unmarshals from JSON.
+func (c *CallLog) UnmarshalJSON(input []byte) error {
+ type CallLog struct {
+ Address *common.Address `json:"address"`
+ Topics []common.Hash `json:"topics"`
+ Data *hexutil.Bytes `json:"data"`
+ Index *hexutil.Uint `json:"index"`
+ Position *hexutil.Uint `json:"position"`
+ }
+ var dec CallLog
+ if err := json.Unmarshal(input, &dec); err != nil {
+ return err
+ }
+ if dec.Address != nil {
+ c.Address = *dec.Address
+ }
+ if dec.Topics != nil {
+ c.Topics = dec.Topics
+ }
+ if dec.Data != nil {
+ c.Data = *dec.Data
+ }
+ if dec.Index != nil {
+ c.Index = uint(*dec.Index)
+ }
+ if dec.Position != nil {
+ c.Position = uint(*dec.Position)
+ }
+ return nil
+}
diff --git a/ethclient/gethclient/gethclient.go b/ethclient/gethclient/gethclient.go
index c2013bca2c..e677e2bb21 100644
--- a/ethclient/gethclient/gethclient.go
+++ b/ethclient/gethclient/gethclient.go
@@ -19,10 +19,12 @@ package gethclient
import (
"context"
+ "encoding/json"
"fmt"
"math/big"
"runtime"
"runtime/debug"
+ "time"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
@@ -229,6 +231,124 @@ func (ec *Client) TraceBlock(ctx context.Context, hash common.Hash, config *trac
return result, nil
}
+// CallTracerConfig configures the call tracer for
+// TraceTransactionWithCallTracer and TraceCallWithCallTracer.
+type CallTracerConfig struct {
+ // OnlyTopCall, when true, limits tracing to the main (top-level) call only.
+ OnlyTopCall bool
+ // WithLog, when true, includes log emissions in the trace output.
+ WithLog bool
+ // Timeout is the maximum duration the tracer may run.
+ // Zero means the server default (5s).
+ Timeout time.Duration
+}
+
+//go:generate go run github.com/fjl/gencodec -type CallLog -field-override callLogMarshaling -out gen_calllog_json.go
+
+// CallLog represents a log emitted during a traced call.
+type CallLog struct {
+ Address common.Address `json:"address"`
+ Topics []common.Hash `json:"topics"`
+ Data []byte `json:"data"`
+ Index uint `json:"index"`
+ Position uint `json:"position"`
+}
+
+type callLogMarshaling struct {
+ Data hexutil.Bytes
+ Index hexutil.Uint
+ Position hexutil.Uint
+}
+
+//go:generate go run github.com/fjl/gencodec -type CallFrame -field-override callFrameMarshaling -out gen_callframe_json.go
+
+// CallFrame contains the result of a call tracer run.
+type CallFrame struct {
+ Type string `json:"type"`
+ From common.Address `json:"from"`
+ Gas uint64 `json:"gas"`
+ GasUsed uint64 `json:"gasUsed"`
+ To *common.Address `json:"to,omitempty"`
+ Input []byte `json:"input"`
+ Output []byte `json:"output,omitempty"`
+ Error string `json:"error,omitempty"`
+ RevertReason string `json:"revertReason,omitempty"`
+ Calls []CallFrame `json:"calls,omitempty"`
+ Logs []CallLog `json:"logs,omitempty"`
+ Value *big.Int `json:"value,omitempty"`
+}
+
+type callFrameMarshaling struct {
+ Gas hexutil.Uint64
+ GasUsed hexutil.Uint64
+ Input hexutil.Bytes
+ Output hexutil.Bytes
+ Value *hexutil.Big
+}
+
+// TraceTransactionWithCallTracer traces a transaction with the call tracer
+// and returns a typed CallFrame. If config is nil, defaults are used.
+func (ec *Client) TraceTransactionWithCallTracer(ctx context.Context, txHash common.Hash, config *CallTracerConfig) (*CallFrame, error) {
+ var result CallFrame
+ err := ec.c.CallContext(ctx, &result, "debug_traceTransaction", txHash, callTracerConfig(config))
+ if err != nil {
+ return nil, err
+ }
+ return &result, nil
+}
+
+// TraceCallWithCallTracer executes a call with the call tracer and returns
+// a typed CallFrame. blockNrOrHash selects the block context for the call.
+// overrides specifies state overrides (nil for none), blockOverrides specifies
+// block header overrides (nil for none), and config configures the tracer
+// (nil for defaults).
+func (ec *Client) TraceCallWithCallTracer(ctx context.Context, msg ethereum.CallMsg, blockNrOrHash rpc.BlockNumberOrHash, overrides map[common.Address]OverrideAccount, blockOverrides *BlockOverrides, config *CallTracerConfig) (*CallFrame, error) {
+ var result CallFrame
+ err := ec.c.CallContext(ctx, &result, "debug_traceCall", toCallArg(msg), blockNrOrHash, callTraceCallConfig(config, overrides, blockOverrides))
+ if err != nil {
+ return nil, err
+ }
+ return &result, nil
+}
+
+// callTracerConfig converts a CallTracerConfig to the wire-format TraceConfig.
+func callTracerConfig(config *CallTracerConfig) *tracers.TraceConfig {
+ tracer := "callTracer"
+ tc := &tracers.TraceConfig{Tracer: &tracer}
+ if config != nil {
+ if config.OnlyTopCall || config.WithLog {
+ cfg, _ := json.Marshal(struct {
+ OnlyTopCall bool `json:"onlyTopCall"`
+ WithLog bool `json:"withLog"`
+ }{config.OnlyTopCall, config.WithLog})
+ tc.TracerConfig = cfg
+ }
+ if config.Timeout != 0 {
+ s := config.Timeout.String()
+ tc.Timeout = &s
+ }
+ }
+ return tc
+}
+
+// callTraceCallConfig builds the wire-format TraceCallConfig for debug_traceCall,
+// bundling tracer settings with optional state and block overrides.
+func callTraceCallConfig(config *CallTracerConfig, overrides map[common.Address]OverrideAccount, blockOverrides *BlockOverrides) interface{} {
+ tc := callTracerConfig(config)
+ // debug_traceCall expects a single config object that includes both
+ // tracer settings and any state/block overrides.
+ type traceCallConfig struct {
+ *tracers.TraceConfig
+ StateOverrides map[common.Address]OverrideAccount `json:"stateOverrides,omitempty"`
+ BlockOverrides *BlockOverrides `json:"blockOverrides,omitempty"`
+ }
+ return &traceCallConfig{
+ TraceConfig: tc,
+ StateOverrides: overrides,
+ BlockOverrides: blockOverrides,
+ }
+}
+
func toBlockNumArg(number *big.Int) string {
if number == nil {
return "latest"
diff --git a/ethclient/gethclient/gethclient_test.go b/ethclient/gethclient/gethclient_test.go
index 0eed63cacf..4d8ccfcb6f 100644
--- a/ethclient/gethclient/gethclient_test.go
+++ b/ethclient/gethclient/gethclient_test.go
@@ -34,6 +34,7 @@ import (
"github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/eth/filters"
"github.com/ethereum/go-ethereum/eth/tracers"
+ _ "github.com/ethereum/go-ethereum/eth/tracers/native"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/params"
@@ -161,6 +162,12 @@ func TestGethClient(t *testing.T) {
}, {
"TestCallContractWithBlockOverrides",
func(t *testing.T) { testCallContractWithBlockOverrides(t, client) },
+ }, {
+ "TestTraceTransactionWithCallTracer",
+ func(t *testing.T) { testTraceTransactionWithCallTracer(t, client, txHashes) },
+ }, {
+ "TestTraceCallWithCallTracer",
+ func(t *testing.T) { testTraceCallWithCallTracer(t, client) },
},
// The testaccesslist is a bit time-sensitive: the newTestBackend imports
// one block. The `testAccessList` fails if the miner has not yet created a
@@ -620,3 +627,60 @@ func testCallContractWithBlockOverrides(t *testing.T, client *rpc.Client) {
t.Fatalf("unexpected result: %x", res)
}
}
+
+func testTraceTransactionWithCallTracer(t *testing.T, client *rpc.Client, txHashes []common.Hash) {
+ ec := New(client)
+ for _, txHash := range txHashes {
+ // With nil config (defaults).
+ result, err := ec.TraceTransactionWithCallTracer(context.Background(), txHash, nil)
+ if err != nil {
+ t.Fatalf("nil config: %v", err)
+ }
+ if result.Type != "CALL" {
+ t.Fatalf("unexpected type: %s", result.Type)
+ }
+ if result.From == (common.Address{}) {
+ t.Fatal("from is zero")
+ }
+ if result.Gas == 0 {
+ t.Fatal("gas is zero")
+ }
+
+ // With explicit config.
+ result, err = ec.TraceTransactionWithCallTracer(context.Background(), txHash,
+ &CallTracerConfig{},
+ )
+ if err != nil {
+ t.Fatalf("explicit config: %v", err)
+ }
+ if result.Type != "CALL" {
+ t.Fatalf("unexpected type: %s", result.Type)
+ }
+ }
+}
+
+func testTraceCallWithCallTracer(t *testing.T, client *rpc.Client) {
+ ec := New(client)
+ msg := ethereum.CallMsg{
+ From: testAddr,
+ To: &common.Address{},
+ Gas: 21000,
+ GasPrice: big.NewInt(1000000000),
+ Value: big.NewInt(1),
+ }
+ result, err := ec.TraceCallWithCallTracer(context.Background(), msg,
+ rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber), nil, nil, nil,
+ )
+ if err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ if result.Type != "CALL" {
+ t.Fatalf("unexpected type: %s", result.Type)
+ }
+ if result.From == (common.Address{}) {
+ t.Fatal("from is zero")
+ }
+ if result.Gas == 0 {
+ t.Fatal("gas is zero")
+ }
+}
diff --git a/ethdb/pebble/pebble.go b/ethdb/pebble/pebble.go
index 395daa6cf4..6b549f40d9 100644
--- a/ethdb/pebble/pebble.go
+++ b/ethdb/pebble/pebble.go
@@ -307,7 +307,7 @@ func New(file string, cache int, handles int, namespace string, readonly bool) (
// These two settings define the conditions under which compaction concurrency
// is increased. Specifically, one additional compaction job will be enabled when:
// - there is one more overlapping sub-level0;
- // - there is an additional 512 MB of compaction debt;
+ // - there is an additional 256 MB of compaction debt;
//
// The maximum concurrency is still capped by MaxConcurrentCompactions, but with
// these settings compactions can scale up more readily.
diff --git a/go.mod b/go.mod
index faff5775ef..e192ac2934 100644
--- a/go.mod
+++ b/go.mod
@@ -44,6 +44,7 @@ require (
github.com/jackpal/go-nat-pmp v1.0.2
github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267
github.com/karalabe/hid v1.0.1-0.20240306101548-573246063e52
+ github.com/klauspost/compress v1.17.8
github.com/kylelemons/godebug v1.1.0
github.com/mattn/go-colorable v0.1.13
github.com/mattn/go-isatty v0.0.20
@@ -134,7 +135,6 @@ require (
github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/kilic/bls12-381 v0.1.0 // indirect
- github.com/klauspost/compress v1.17.8 // indirect
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
diff --git a/internal/blocktest/test_hash.go b/internal/blocktest/test_hash.go
index b3e7098e2b..5e5b1202de 100644
--- a/internal/blocktest/test_hash.go
+++ b/internal/blocktest/test_hash.go
@@ -27,7 +27,7 @@ import (
"hash"
"github.com/ethereum/go-ethereum/common"
- "golang.org/x/crypto/sha3"
+ "github.com/ethereum/go-ethereum/crypto/keccak"
)
// testHasher is the helper tool for transaction/receipt list hashing.
@@ -39,7 +39,7 @@ type testHasher struct {
// NewHasher returns a new testHasher instance.
func NewHasher() *testHasher {
- return &testHasher{hasher: sha3.NewLegacyKeccak256()}
+ return &testHasher{hasher: keccak.NewLegacyKeccak256()}
}
// Reset resets the hash state.
diff --git a/internal/era/accumulator.go b/internal/era/accumulator.go
index 83a761f1fd..72e36fe00f 100644
--- a/internal/era/accumulator.go
+++ b/internal/era/accumulator.go
@@ -20,6 +20,7 @@ import (
"errors"
"fmt"
"math/big"
+ "slices"
"github.com/ethereum/go-ethereum/common"
ssz "github.com/ferranbt/fastssz"
@@ -31,8 +32,8 @@ func ComputeAccumulator(hashes []common.Hash, tds []*big.Int) (common.Hash, erro
if len(hashes) != len(tds) {
return common.Hash{}, errors.New("must have equal number hashes as td values")
}
- if len(hashes) > MaxEra1Size {
- return common.Hash{}, fmt.Errorf("too many records: have %d, max %d", len(hashes), MaxEra1Size)
+ if len(hashes) > MaxSize {
+ return common.Hash{}, fmt.Errorf("too many records: have %d, max %d", len(hashes), MaxSize)
}
hh := ssz.NewHasher()
for i := range hashes {
@@ -43,7 +44,7 @@ func ComputeAccumulator(hashes []common.Hash, tds []*big.Int) (common.Hash, erro
}
hh.Append(root[:])
}
- hh.MerkleizeWithMixin(0, uint64(len(hashes)), uint64(MaxEra1Size))
+ hh.MerkleizeWithMixin(0, uint64(len(hashes)), uint64(MaxSize))
return hh.HashRoot()
}
@@ -69,23 +70,15 @@ func (h *headerRecord) HashTreeRoot() ([32]byte, error) {
// HashTreeRootWith ssz hashes the headerRecord object with a hasher.
func (h *headerRecord) HashTreeRootWith(hh ssz.HashWalker) (err error) {
hh.PutBytes(h.Hash[:])
- td := bigToBytes32(h.TotalDifficulty)
+ td := BigToBytes32(h.TotalDifficulty)
hh.PutBytes(td[:])
hh.Merkleize(0)
return
}
// bigToBytes32 converts a big.Int into a little-endian 32-byte array.
-func bigToBytes32(n *big.Int) (b [32]byte) {
+func BigToBytes32(n *big.Int) (b [32]byte) {
n.FillBytes(b[:])
- reverseOrder(b[:])
+ slices.Reverse(b[:])
return
}
-
-// reverseOrder reverses the byte order of a slice.
-func reverseOrder(b []byte) []byte {
- for i := 0; i < 16; i++ {
- b[i], b[32-i-1] = b[32-i-1], b[i]
- }
- return b
-}
diff --git a/internal/era/era.go b/internal/era/era.go
index 118c67abfd..a3c8465bc4 100644
--- a/internal/era/era.go
+++ b/internal/era/era.go
@@ -1,4 +1,4 @@
-// Copyright 2024 The go-ethereum Authors
+// Copyright 2025 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
@@ -17,7 +17,6 @@
package era
import (
- "encoding/binary"
"fmt"
"io"
"math/big"
@@ -25,293 +24,132 @@ import (
"path"
"strconv"
"strings"
- "sync"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/internal/era/e2store"
- "github.com/ethereum/go-ethereum/rlp"
- "github.com/golang/snappy"
)
+// Type constants for the e2store entries in the Era1 and EraE formats.
var (
- TypeVersion uint16 = 0x3265
- TypeCompressedHeader uint16 = 0x03
- TypeCompressedBody uint16 = 0x04
- TypeCompressedReceipts uint16 = 0x05
- TypeTotalDifficulty uint16 = 0x06
- TypeAccumulator uint16 = 0x07
- TypeBlockIndex uint16 = 0x3266
+ TypeVersion uint16 = 0x3265
+ TypeCompressedHeader uint16 = 0x03
+ TypeCompressedBody uint16 = 0x04
+ TypeCompressedReceipts uint16 = 0x05
+ TypeTotalDifficulty uint16 = 0x06
+ TypeAccumulator uint16 = 0x07
+ TypeCompressedSlimReceipts uint16 = 0x0a // uses eth/69 encoding
+ TypeProof uint16 = 0x0b
+ TypeBlockIndex uint16 = 0x3266
+ TypeComponentIndex uint16 = 0x3267
- MaxEra1Size = 8192
+ MaxSize = 8192
+ // headerSize uint64 = 8
)
-// Filename returns a recognizable Era1-formatted file name for the specified
-// epoch and network.
-func Filename(network string, epoch int, root common.Hash) string {
- return fmt.Sprintf("%s-%05d-%s.era1", network, epoch, root.Hex()[2:10])
-}
-
-// ReadDir reads all the era1 files in a directory for a given network.
-// Format: --.era1
-func ReadDir(dir, network string) ([]string, error) {
- entries, err := os.ReadDir(dir)
- if err != nil {
- return nil, fmt.Errorf("error reading directory %s: %w", dir, err)
- }
- var (
- next = uint64(0)
- eras []string
- )
- for _, entry := range entries {
- if path.Ext(entry.Name()) != ".era1" {
- continue
- }
- parts := strings.Split(entry.Name(), "-")
- if len(parts) != 3 || parts[0] != network {
- // Invalid era1 filename, skip.
- continue
- }
- if epoch, err := strconv.ParseUint(parts[1], 10, 64); err != nil {
- return nil, fmt.Errorf("malformed era1 filename: %s", entry.Name())
- } else if epoch != next {
- return nil, fmt.Errorf("missing epoch %d", next)
- }
- next += 1
- eras = append(eras, entry.Name())
- }
- return eras, nil
-}
-
type ReadAtSeekCloser interface {
io.ReaderAt
io.Seeker
io.Closer
}
-// Era reads and Era1 file.
-type Era struct {
- f ReadAtSeekCloser // backing era1 file
- s *e2store.Reader // e2store reader over f
- m metadata // start, count, length info
- mu *sync.Mutex // lock for buf
- buf [8]byte // buffer reading entry offsets
+// Iterator provides sequential access to blocks in an era file.
+type Iterator interface {
+ // Next advances to the next block. Returns true if a block is available,
+ // false when iteration is complete or an error occurred.
+ Next() bool
+
+ // Number returns the block number of the current block.
+ Number() uint64
+
+ // Block returns the current block.
+ Block() (*types.Block, error)
+
+ // BlockAndReceipts returns the current block and its receipts.
+ BlockAndReceipts() (*types.Block, types.Receipts, error)
+
+ // Receipts returns the receipts for the current block.
+ Receipts() (types.Receipts, error)
+
+ // Error returns any error encountered during iteration.
+ Error() error
}
-// From returns an Era backed by f.
-func From(f ReadAtSeekCloser) (*Era, error) {
- m, err := readMetadata(f)
- if err != nil {
- return nil, err
- }
- return &Era{
- f: f,
- s: e2store.NewReader(f),
- m: m,
- mu: new(sync.Mutex),
- }, nil
+// Builder constructs era files from blocks and receipts.
+//
+// Builders handle three epoch types automatically:
+// - Pre-merge: all blocks have difficulty > 0, TD is stored for each block
+// - Transition: starts pre-merge, ends post-merge; TD stored for all blocks
+// - Post-merge: all blocks have difficulty == 0, no TD stored
+type Builder interface {
+ // Add appends a block and its receipts to the era file.
+ // For pre-merge blocks, td must be provided.
+ // For post-merge blocks, td should be nil.
+ Add(block *types.Block, receipts types.Receipts, td *big.Int) error
+
+ // AddRLP appends RLP-encoded block components to the era file.
+ // For pre-merge blocks, td and difficulty must be provided.
+ // For post-merge blocks, td and difficulty should be nil.
+ AddRLP(header, body, receipts []byte, number uint64, hash common.Hash, td, difficulty *big.Int) error
+
+ // Finalize writes all collected entries and returns the epoch identifier.
+ // For Era1 (onedb): returns the accumulator root.
+ // For EraE (execdb): returns the last block hash.
+ Finalize() (common.Hash, error)
+
+ // Accumulator returns the accumulator root after Finalize has been called.
+ // Returns nil for post-merge epochs where no accumulator exists.
+ Accumulator() *common.Hash
}
-// Open returns an Era backed by the given filename.
-func Open(filename string) (*Era, error) {
- f, err := os.Open(filename)
- if err != nil {
- return nil, err
- }
- return From(f)
+// Era represents the interface for reading era data.
+type Era interface {
+ Close() error
+ Start() uint64
+ Count() uint64
+ Iterator() (Iterator, error)
+ GetBlockByNumber(num uint64) (*types.Block, error)
+ GetRawBodyByNumber(num uint64) ([]byte, error)
+ GetRawReceiptsByNumber(num uint64) ([]byte, error)
+ InitialTD() (*big.Int, error)
+ Accumulator() (common.Hash, error)
}
-func (e *Era) Close() error {
- return e.f.Close()
-}
+// ReadDir reads all the era files in a directory for a given network.
+// Format: --.erae or --.era1
+func ReadDir(dir, network string) ([]string, error) {
+ entries, err := os.ReadDir(dir)
-// GetBlockByNumber returns the block for the given block number.
-func (e *Era) GetBlockByNumber(num uint64) (*types.Block, error) {
- if e.m.start > num || e.m.start+e.m.count <= num {
- return nil, fmt.Errorf("out-of-bounds: %d not in [%d, %d)", num, e.m.start, e.m.start+e.m.count)
- }
- off, err := e.readOffset(num)
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("error reading directory %s: %w", dir, err)
}
- r, n, err := newSnappyReader(e.s, TypeCompressedHeader, off)
- if err != nil {
- return nil, err
- }
- var header types.Header
- if err := rlp.Decode(r, &header); err != nil {
- return nil, err
- }
- off += n
- r, _, err = newSnappyReader(e.s, TypeCompressedBody, off)
- if err != nil {
- return nil, err
- }
- var body types.Body
- if err := rlp.Decode(r, &body); err != nil {
- return nil, err
- }
- return types.NewBlockWithHeader(&header).WithBody(body), nil
-}
-
-// GetRawBodyByNumber returns the RLP-encoded body for the given block number.
-func (e *Era) GetRawBodyByNumber(num uint64) ([]byte, error) {
- if e.m.start > num || e.m.start+e.m.count <= num {
- return nil, fmt.Errorf("out-of-bounds: %d not in [%d, %d)", num, e.m.start, e.m.start+e.m.count)
- }
- off, err := e.readOffset(num)
- if err != nil {
- return nil, err
- }
- off, err = e.s.SkipN(off, 1)
- if err != nil {
- return nil, err
- }
- r, _, err := newSnappyReader(e.s, TypeCompressedBody, off)
- if err != nil {
- return nil, err
- }
- return io.ReadAll(r)
-}
-
-// GetRawReceiptsByNumber returns the RLP-encoded receipts for the given block number.
-func (e *Era) GetRawReceiptsByNumber(num uint64) ([]byte, error) {
- if e.m.start > num || e.m.start+e.m.count <= num {
- return nil, fmt.Errorf("out-of-bounds: %d not in [%d, %d)", num, e.m.start, e.m.start+e.m.count)
- }
- off, err := e.readOffset(num)
- if err != nil {
- return nil, err
- }
-
- // Skip over header and body.
- off, err = e.s.SkipN(off, 2)
- if err != nil {
- return nil, err
- }
-
- r, _, err := newSnappyReader(e.s, TypeCompressedReceipts, off)
- if err != nil {
- return nil, err
- }
- return io.ReadAll(r)
-}
-
-// Accumulator reads the accumulator entry in the Era1 file.
-func (e *Era) Accumulator() (common.Hash, error) {
- entry, err := e.s.Find(TypeAccumulator)
- if err != nil {
- return common.Hash{}, err
- }
- return common.BytesToHash(entry.Value), nil
-}
-
-// InitialTD returns initial total difficulty before the difficulty of the
-// first block of the Era1 is applied.
-func (e *Era) InitialTD() (*big.Int, error) {
var (
- r io.Reader
- header types.Header
- rawTd []byte
- n int64
- off int64
- err error
+ next = uint64(0)
+ eras []string
+ dirType string
)
-
- // Read first header.
- if off, err = e.readOffset(e.m.start); err != nil {
- return nil, err
+ for _, entry := range entries {
+ ext := path.Ext(entry.Name())
+ if ext != ".erae" && ext != ".era1" {
+ continue
+ }
+ if dirType == "" {
+ dirType = ext
+ }
+ parts := strings.Split(entry.Name(), "-")
+ if len(parts) != 3 || parts[0] != network {
+ // Invalid era filename, skip.
+ continue
+ }
+ if epoch, err := strconv.ParseUint(parts[1], 10, 64); err != nil {
+ return nil, fmt.Errorf("malformed era filenames: %s", entry.Name())
+ } else if epoch != next {
+ return nil, fmt.Errorf("missing epoch %d", next)
+ }
+ if dirType != ext {
+ return nil, fmt.Errorf("directory %s contains mixed era file formats: want %s, have %s", dir, dirType, ext)
+ }
+ next += 1
+ eras = append(eras, entry.Name())
}
- if r, n, err = newSnappyReader(e.s, TypeCompressedHeader, off); err != nil {
- return nil, err
- }
- if err := rlp.Decode(r, &header); err != nil {
- return nil, err
- }
- off += n
-
- // Skip over header and body.
- off, err = e.s.SkipN(off, 2)
- if err != nil {
- return nil, err
- }
-
- // Read total difficulty after first block.
- if r, _, err = e.s.ReaderAt(TypeTotalDifficulty, off); err != nil {
- return nil, err
- }
- rawTd, err = io.ReadAll(r)
- if err != nil {
- return nil, err
- }
- td := new(big.Int).SetBytes(reverseOrder(rawTd))
- return td.Sub(td, header.Difficulty), nil
-}
-
-// Start returns the listed start block.
-func (e *Era) Start() uint64 {
- return e.m.start
-}
-
-// Count returns the total number of blocks in the Era1.
-func (e *Era) Count() uint64 {
- return e.m.count
-}
-
-// readOffset reads a specific block's offset from the block index. The value n
-// is the absolute block number desired.
-func (e *Era) readOffset(n uint64) (int64, error) {
- var (
- blockIndexRecordOffset = e.m.length - 24 - int64(e.m.count)*8 // skips start, count, and header
- firstIndex = blockIndexRecordOffset + 16 // first index after header / start-num
- indexOffset = int64(n-e.m.start) * 8 // desired index * size of indexes
- offOffset = firstIndex + indexOffset // offset of block offset
- )
- e.mu.Lock()
- defer e.mu.Unlock()
- clear(e.buf[:])
- if _, err := e.f.ReadAt(e.buf[:], offOffset); err != nil {
- return 0, err
- }
- // Since the block offset is relative from the start of the block index record
- // we need to add the record offset to it's offset to get the block's absolute
- // offset.
- return blockIndexRecordOffset + int64(binary.LittleEndian.Uint64(e.buf[:])), nil
-}
-
-// newSnappyReader returns a snappy.Reader for the e2store entry value at off.
-func newSnappyReader(e *e2store.Reader, expectedType uint16, off int64) (io.Reader, int64, error) {
- r, n, err := e.ReaderAt(expectedType, off)
- if err != nil {
- return nil, 0, err
- }
- return snappy.NewReader(r), int64(n), err
-}
-
-// metadata wraps the metadata in the block index.
-type metadata struct {
- start uint64
- count uint64
- length int64
-}
-
-// readMetadata reads the metadata stored in an Era1 file's block index.
-func readMetadata(f ReadAtSeekCloser) (m metadata, err error) {
- // Determine length of reader.
- if m.length, err = f.Seek(0, io.SeekEnd); err != nil {
- return
- }
- b := make([]byte, 16)
- // Read count. It's the last 8 bytes of the file.
- if _, err = f.ReadAt(b[:8], m.length-8); err != nil {
- return
- }
- m.count = binary.LittleEndian.Uint64(b)
- // Read start. It's at the offset -sizeof(m.count) -
- // count*sizeof(indexEntry) - sizeof(m.start)
- if _, err = f.ReadAt(b[8:], m.length-16-int64(m.count*8)); err != nil {
- return
- }
- m.start = binary.LittleEndian.Uint64(b[8:])
- return
+ return eras, nil
}
diff --git a/internal/era/eradl/eradl.go b/internal/era/eradl/eradl.go
index 30bd2bc0d5..375b9ad15b 100644
--- a/internal/era/eradl/eradl.go
+++ b/internal/era/eradl/eradl.go
@@ -86,8 +86,8 @@ func (l *Loader) DownloadAll(destDir string) error {
// DownloadBlockRange fetches the era1 files for the given block range.
func (l *Loader) DownloadBlockRange(start, end uint64, destDir string) error {
- startEpoch := start / uint64(era.MaxEra1Size)
- endEpoch := end / uint64(era.MaxEra1Size)
+ startEpoch := start / uint64(era.MaxSize)
+ endEpoch := end / uint64(era.MaxSize)
return l.DownloadEpochRange(startEpoch, endEpoch, destDir)
}
diff --git a/internal/era/execdb/builder.go b/internal/era/execdb/builder.go
new file mode 100644
index 0000000000..6246b9caae
--- /dev/null
+++ b/internal/era/execdb/builder.go
@@ -0,0 +1,332 @@
+// Copyright 2025 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package execdb
+
+// EraE file format specification.
+//
+// The format can be summarized with the following expression:
+//
+// eraE := Version | CompressedHeader* | CompressedBody* | CompressedSlimReceipts* | TotalDifficulty* | other-entries* | Accumulator? | ComponentIndex
+//
+// Each basic element is its own e2store entry:
+//
+// Version = { type: 0x3265, data: nil }
+// CompressedHeader = { type: 0x03, data: snappyFramed(rlp(header)) }
+// CompressedBody = { type: 0x04, data: snappyFramed(rlp(body)) }
+// CompressedSlimReceipts = { type: 0x0a, data: snappyFramed(rlp([tx-type, post-state-or-status, cumulative-gas, logs])) }
+// TotalDifficulty = { type: 0x06, data: uint256 (header.total_difficulty) }
+// AccumulatorRoot = { type: 0x07, data: hash_tree_root(List(HeaderRecord, 8192)) }
+// ComponentIndex = { type: 0x3267, data: component-index }
+//
+// Notes:
+// - TotalDifficulty is present for pre-merge and merge transition epochs.
+// For pure post-merge epochs, TotalDifficulty is omitted entirely.
+// - In merge transition epochs, post-merge blocks store the final total
+// difficulty (the TD at which the merge occurred).
+// - AccumulatorRoot is only written for pre-merge epochs.
+// - HeaderRecord is defined in the Portal Network specification.
+// - Proofs (type 0x09) are defined in the spec but not yet supported in this implementation.
+//
+// ComponentIndex stores relative offsets to each block's components:
+//
+// component-index := starting-number | indexes | indexes | ... | component-count | count
+// indexes := header-offset | body-offset | receipts-offset | td-offset?
+//
+// All values are little-endian uint64.
+//
+// Due to the accumulator size limit of 8192, the maximum number of blocks in an
+// EraE file is also 8192.
+
+import (
+ "bytes"
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "io"
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/internal/era"
+ "github.com/ethereum/go-ethereum/internal/era/e2store"
+ "github.com/ethereum/go-ethereum/rlp"
+ "github.com/golang/snappy"
+)
+
+// Builder is used to build an EraE e2store file. It collects block entries and
+// writes them to the underlying e2store.Writer.
+type Builder struct {
+ w *e2store.Writer
+
+ headers [][]byte
+ hashes []common.Hash // only pre-merge block hashes, for accumulator
+ bodies [][]byte
+ receipts [][]byte
+ tds []*big.Int
+
+ startNum *uint64
+ ttd *big.Int // terminal total difficulty
+ last common.Hash // hash of last block added
+ accumulator *common.Hash // accumulator root, set by Finalize (nil for post-merge)
+
+ written uint64
+
+ buf *bytes.Buffer
+ snappy *snappy.Writer
+}
+
+// NewBuilder returns a new Builder instance.
+func NewBuilder(w io.Writer) era.Builder {
+ return &Builder{
+ w: e2store.NewWriter(w),
+ }
+}
+
+// Add writes a block entry and its receipts into the e2store file.
+func (b *Builder) Add(block *types.Block, receipts types.Receipts, td *big.Int) error {
+ eh, err := rlp.EncodeToBytes(block.Header())
+ if err != nil {
+ return fmt.Errorf("encode header: %w", err)
+ }
+ eb, err := rlp.EncodeToBytes(block.Body())
+ if err != nil {
+ return fmt.Errorf("encode body: %w", err)
+ }
+
+ rs := make([]*types.SlimReceipt, len(receipts))
+ for i, receipt := range receipts {
+ rs[i] = (*types.SlimReceipt)(receipt)
+ }
+ er, err := rlp.EncodeToBytes(rs)
+ if err != nil {
+ return fmt.Errorf("encode receipts: %w", err)
+ }
+
+ return b.AddRLP(eh, eb, er, block.Number().Uint64(), block.Hash(), td, block.Difficulty())
+}
+
+// AddRLP takes the RLP encoded block components and writes them to the underlying e2store file.
+// The builder automatically handles transition epochs where both pre and post-merge blocks exist.
+func (b *Builder) AddRLP(header, body, receipts []byte, number uint64, blockHash common.Hash, td, difficulty *big.Int) error {
+ if len(b.headers) >= era.MaxSize {
+ return fmt.Errorf("exceeds max size %d", era.MaxSize)
+ }
+ // Set starting block number on first add.
+ if b.startNum == nil {
+ b.startNum = new(uint64)
+ *b.startNum = number
+ }
+
+ if difficulty == nil {
+ return fmt.Errorf("invalid block: difficulty is nil")
+ }
+ hasDifficulty := difficulty.Sign() > 0
+ // Expect td to be nil for post-merge blocks
+ // and non-nil for pre-merge blocks.
+ if hasDifficulty != (td != nil) {
+ return fmt.Errorf("TD and difficulty mismatch: expected both nil or both non-nil")
+ }
+ // After the merge, difficulty must be nil.
+ post := (b.tds == nil && len(b.headers) > 0) || b.ttd != nil
+ if post && hasDifficulty {
+ return fmt.Errorf("post-merge epoch: cannot accept total difficulty for block %d", number)
+ }
+
+ // If this marks the start of the transition, record final total
+ // difficulty value.
+ if b.ttd == nil && len(b.tds) > 0 && !hasDifficulty {
+ b.ttd = new(big.Int).Set(b.tds[len(b.tds)-1])
+ }
+
+ // Record block data.
+ b.headers = append(b.headers, header)
+ b.bodies = append(b.bodies, body)
+ b.receipts = append(b.receipts, receipts)
+ b.last = blockHash
+
+ // Conditionally write the total difficulty and block hashes.
+ // - Pre-merge: store total difficulty and block hashes.
+ // - Transition: only store total difficulty.
+ // - Post-merge: store neither.
+ if hasDifficulty {
+ b.hashes = append(b.hashes, blockHash)
+ b.tds = append(b.tds, new(big.Int).Set(td))
+ } else if b.ttd != nil {
+ b.tds = append(b.tds, new(big.Int).Set(b.ttd))
+ } else {
+ // Post-merge: no TD or block hashes stored.
+ }
+
+ return nil
+}
+
+// Accumulator returns the accumulator root after Finalize has been called.
+// Returns nil for post-merge epochs where no accumulator exists.
+func (b *Builder) Accumulator() *common.Hash {
+ return b.accumulator
+}
+
+type offsets struct {
+ headers []uint64
+ bodies []uint64
+ receipts []uint64
+ tds []uint64
+}
+
+// Finalize writes all collected block entries to the e2store file.
+// For pre-merge or transition epochs, the accumulator root is computed over
+// pre-merge blocks and written. For pure post-merge epochs, no accumulator
+// is written. Always returns the last block hash as the epoch identifier.
+func (b *Builder) Finalize() (common.Hash, error) {
+ if b.startNum == nil {
+ return common.Hash{}, errors.New("no blocks added, cannot finalize")
+ }
+ // Write version before writing any blocks.
+ if n, err := b.w.Write(era.TypeVersion, nil); err != nil {
+ return common.Hash{}, fmt.Errorf("write version entry: %w", err)
+ } else {
+ b.written += uint64(n)
+ }
+
+ // Convert TD values to byte-level LE representation.
+ var tds [][]byte
+ for _, td := range b.tds {
+ tds = append(tds, uint256LE(td))
+ }
+
+ // Create snappy writer.
+ b.buf = bytes.NewBuffer(nil)
+ b.snappy = snappy.NewBufferedWriter(b.buf)
+
+ var o offsets
+ for _, section := range []struct {
+ typ uint16
+ data [][]byte
+ compressed bool
+ offsets *[]uint64
+ }{
+ {era.TypeCompressedHeader, b.headers, true, &o.headers},
+ {era.TypeCompressedBody, b.bodies, true, &o.bodies},
+ {era.TypeCompressedSlimReceipts, b.receipts, true, &o.receipts},
+ {era.TypeTotalDifficulty, tds, false, &o.tds},
+ } {
+ for _, data := range section.data {
+ *section.offsets = append(*section.offsets, b.written)
+ if section.compressed {
+ // Write snappy compressed data.
+ if err := b.snappyWrite(section.typ, data); err != nil {
+ return common.Hash{}, err
+ }
+ } else {
+ // Directly write uncompressed data.
+ n, err := b.w.Write(section.typ, data)
+ if err != nil {
+ return common.Hash{}, err
+ }
+ b.written += uint64(n)
+ }
+ }
+ }
+
+ // Compute and write accumulator root only for epochs that started pre-merge.
+ // The accumulator is computed over only the pre-merge blocks (b.hashes).
+ // Pure post-merge epochs have no accumulator.
+ if len(b.tds) > 0 {
+ accRoot, err := era.ComputeAccumulator(b.hashes, b.tds[:len(b.hashes)])
+ if err != nil {
+ return common.Hash{}, fmt.Errorf("compute accumulator: %w", err)
+ }
+ if n, err := b.w.Write(era.TypeAccumulator, accRoot[:]); err != nil {
+ return common.Hash{}, fmt.Errorf("write accumulator: %w", err)
+ } else {
+ b.written += uint64(n)
+ }
+ b.accumulator = &accRoot
+ if err := b.writeIndex(&o); err != nil {
+ return common.Hash{}, err
+ }
+ return b.last, nil
+ }
+
+ // Pure post-merge epoch: no accumulator.
+ if err := b.writeIndex(&o); err != nil {
+ return common.Hash{}, err
+ }
+ return b.last, nil
+}
+
+// uin256LE writes 32 byte big integers to little endian.
+func uint256LE(v *big.Int) []byte {
+ b := v.FillBytes(make([]byte, 32))
+ for i := 0; i < 16; i++ {
+ b[i], b[31-i] = b[31-i], b[i]
+ }
+ return b
+}
+
+// SnappyWrite compresses the input data using snappy and writes it to the e2store file.
+func (b *Builder) snappyWrite(typ uint16, in []byte) error {
+ b.buf.Reset()
+ b.snappy.Reset(b.buf)
+ if _, err := b.snappy.Write(in); err != nil {
+ return fmt.Errorf("error snappy encoding: %w", err)
+ }
+ if err := b.snappy.Flush(); err != nil {
+ return fmt.Errorf("error flushing snappy encoding: %w", err)
+ }
+ n, err := b.w.Write(typ, b.buf.Bytes())
+ b.written += uint64(n)
+ if err != nil {
+ return fmt.Errorf("error writing e2store entry: %w", err)
+ }
+ return nil
+}
+
+// writeIndex writes the component index to the file.
+func (b *Builder) writeIndex(o *offsets) error {
+ count := len(o.headers)
+
+ // Post-merge, we only index headers, bodies, and receipts. Pre-merge, we also
+ // need to index the total difficulties.
+ componentCount := 3
+ if len(o.tds) > 0 {
+ componentCount++
+ }
+
+ // Offsets are stored relative to the index position (negative, stored as uint64).
+ base := int64(b.written)
+ rel := func(abs uint64) uint64 { return uint64(int64(abs) - base) }
+
+ var buf bytes.Buffer
+ write := func(v uint64) { binary.Write(&buf, binary.LittleEndian, v) }
+
+ write(*b.startNum)
+ for i := range o.headers {
+ write(rel(o.headers[i]))
+ write(rel(o.bodies[i]))
+ write(rel(o.receipts[i]))
+ if len(o.tds) > 0 {
+ write(rel(o.tds[i]))
+ }
+ }
+ write(uint64(componentCount))
+ write(uint64(count))
+
+ n, err := b.w.Write(era.TypeComponentIndex, buf.Bytes())
+ b.written += uint64(n)
+ return err
+}
diff --git a/internal/era/execdb/era_test.go b/internal/era/execdb/era_test.go
new file mode 100644
index 0000000000..f66931b9ed
--- /dev/null
+++ b/internal/era/execdb/era_test.go
@@ -0,0 +1,348 @@
+// Copyright 2025 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package execdb
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "math/big"
+ "os"
+ "slices"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/rlp"
+)
+
+func TestEraE(t *testing.T) {
+ t.Parallel()
+
+ tests := []struct {
+ name string
+ start uint64
+ preMerge int
+ postMerge int
+ accumulator bool // whether accumulator should exist
+ }{
+ {
+ name: "pre-merge",
+ start: 0,
+ preMerge: 128,
+ postMerge: 0,
+ accumulator: true,
+ },
+ {
+ name: "post-merge",
+ start: 0,
+ preMerge: 0,
+ postMerge: 64,
+ accumulator: false,
+ },
+ {
+ name: "transition",
+ start: 0,
+ preMerge: 32,
+ postMerge: 32,
+ accumulator: true,
+ },
+ {
+ name: "non-zero-start",
+ start: 8192,
+ preMerge: 64,
+ postMerge: 0,
+ accumulator: true,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ t.Parallel()
+
+ f, err := os.CreateTemp(t.TempDir(), "erae-test")
+ if err != nil {
+ t.Fatalf("error creating temp file: %v", err)
+ }
+ defer f.Close()
+
+ // Build test data.
+ type blockData struct {
+ header, body, receipts []byte
+ hash common.Hash
+ td *big.Int
+ difficulty *big.Int
+ }
+ var (
+ builder = NewBuilder(f)
+ blocks []blockData
+ totalBlocks = tt.preMerge + tt.postMerge
+ finalTD = big.NewInt(int64(tt.preMerge))
+ )
+
+ // Add pre-merge blocks.
+ for i := 0; i < tt.preMerge; i++ {
+ num := tt.start + uint64(i)
+ blk := blockData{
+ header: mustEncode(&types.Header{Number: big.NewInt(int64(num)), Difficulty: big.NewInt(1)}),
+ body: mustEncode(&types.Body{Transactions: []*types.Transaction{types.NewTransaction(0, common.Address{byte(i)}, nil, 0, nil, nil)}}),
+ receipts: mustEncode([]types.SlimReceipt{{CumulativeGasUsed: uint64(i)}}),
+ hash: common.Hash{byte(i)},
+ td: big.NewInt(int64(i + 1)),
+ difficulty: big.NewInt(1),
+ }
+ blocks = append(blocks, blk)
+ if err := builder.AddRLP(blk.header, blk.body, blk.receipts, num, blk.hash, blk.td, blk.difficulty); err != nil {
+ t.Fatalf("error adding pre-merge block %d: %v", i, err)
+ }
+ }
+
+ // Add post-merge blocks.
+ for i := 0; i < tt.postMerge; i++ {
+ idx := tt.preMerge + i
+ num := tt.start + uint64(idx)
+ blk := blockData{
+ header: mustEncode(&types.Header{Number: big.NewInt(int64(num)), Difficulty: big.NewInt(0)}),
+ body: mustEncode(&types.Body{}),
+ receipts: mustEncode([]types.SlimReceipt{}),
+ hash: common.Hash{byte(idx)},
+ difficulty: big.NewInt(0),
+ }
+ blocks = append(blocks, blk)
+ if err := builder.AddRLP(blk.header, blk.body, blk.receipts, num, blk.hash, nil, big.NewInt(0)); err != nil {
+ t.Fatalf("error adding post-merge block %d: %v", idx, err)
+ }
+ }
+
+ // Finalize and check return values.
+ epochID, err := builder.Finalize()
+ if err != nil {
+ t.Fatalf("error finalizing: %v", err)
+ }
+
+ // Verify epoch ID is always the last block hash.
+ expectedLastHash := blocks[len(blocks)-1].hash
+ if epochID != expectedLastHash {
+ t.Fatalf("wrong epoch ID: want %s, got %s", expectedLastHash.Hex(), epochID.Hex())
+ }
+
+ // Verify accumulator presence.
+ if tt.accumulator {
+ if builder.Accumulator() == nil {
+ t.Fatal("expected non-nil accumulator")
+ }
+ } else {
+ if builder.Accumulator() != nil {
+ t.Fatalf("expected nil accumulator, got %s", builder.Accumulator().Hex())
+ }
+ }
+
+ // Open and verify the era file.
+ e, err := Open(f.Name())
+ if err != nil {
+ t.Fatalf("failed to open era: %v", err)
+ }
+ defer e.Close()
+
+ // Verify metadata.
+ if e.Start() != tt.start {
+ t.Fatalf("wrong start block: want %d, got %d", tt.start, e.Start())
+ }
+ if e.Count() != uint64(totalBlocks) {
+ t.Fatalf("wrong block count: want %d, got %d", totalBlocks, e.Count())
+ }
+
+ // Verify accumulator in file.
+ if tt.accumulator {
+ accRoot, err := e.Accumulator()
+ if err != nil {
+ t.Fatalf("error getting accumulator: %v", err)
+ }
+ if accRoot != *builder.Accumulator() {
+ t.Fatalf("accumulator mismatch: builder has %s, file contains %s",
+ builder.Accumulator().Hex(), accRoot.Hex())
+ }
+ } else {
+ if _, err := e.Accumulator(); err == nil {
+ t.Fatal("expected error when reading accumulator from post-merge epoch")
+ }
+ }
+
+ // Verify blocks via raw iterator.
+ it, err := NewRawIterator(e)
+ if err != nil {
+ t.Fatalf("failed to make iterator: %v", err)
+ }
+ for i := 0; i < totalBlocks; i++ {
+ if !it.Next() {
+ t.Fatalf("expected more entries at %d", i)
+ }
+ if it.Error() != nil {
+ t.Fatalf("unexpected error: %v", it.Error())
+ }
+
+ // Check header.
+ rawHeader, err := io.ReadAll(it.Header)
+ if err != nil {
+ t.Fatalf("error reading header: %v", err)
+ }
+ if !bytes.Equal(rawHeader, blocks[i].header) {
+ t.Fatalf("mismatched header at %d", i)
+ }
+
+ // Check body.
+ rawBody, err := io.ReadAll(it.Body)
+ if err != nil {
+ t.Fatalf("error reading body: %v", err)
+ }
+ if !bytes.Equal(rawBody, blocks[i].body) {
+ t.Fatalf("mismatched body at %d", i)
+ }
+
+ // Check receipts.
+ rawReceipts, err := io.ReadAll(it.Receipts)
+ if err != nil {
+ t.Fatalf("error reading receipts: %v", err)
+ }
+ if !bytes.Equal(rawReceipts, blocks[i].receipts) {
+ t.Fatalf("mismatched receipts at %d", i)
+ }
+
+ // Check TD (only for epochs that have TD stored).
+ if tt.preMerge > 0 && it.TotalDifficulty != nil {
+ rawTd, err := io.ReadAll(it.TotalDifficulty)
+ if err != nil {
+ t.Fatalf("error reading TD: %v", err)
+ }
+ slices.Reverse(rawTd)
+ td := new(big.Int).SetBytes(rawTd)
+ var expectedTD *big.Int
+ if i < tt.preMerge {
+ expectedTD = blocks[i].td
+ } else {
+ // Post-merge blocks in transition epoch use final TD.
+ expectedTD = finalTD
+ }
+ if td.Cmp(expectedTD) != 0 {
+ t.Fatalf("mismatched TD at %d: want %s, got %s", i, expectedTD, td)
+ }
+ }
+ }
+
+ // Verify random access.
+ for _, blockNum := range []uint64{tt.start, tt.start + uint64(totalBlocks) - 1} {
+ blk, err := e.GetBlockByNumber(blockNum)
+ if err != nil {
+ t.Fatalf("error getting block %d: %v", blockNum, err)
+ }
+ if blk.Number().Uint64() != blockNum {
+ t.Fatalf("wrong block number: want %d, got %d", blockNum, blk.Number().Uint64())
+ }
+ }
+
+ // Verify out-of-range access fails.
+ if _, err := e.GetBlockByNumber(tt.start + uint64(totalBlocks)); err == nil {
+ t.Fatal("expected error for out-of-range block")
+ }
+ if tt.start > 0 {
+ if _, err := e.GetBlockByNumber(tt.start - 1); err == nil {
+ t.Fatal("expected error for block before start")
+ }
+ }
+
+ // Verify high-level iterator.
+ hlIt, err := e.Iterator()
+ if err != nil {
+ t.Fatalf("failed to create iterator: %v", err)
+ }
+ count := 0
+ for hlIt.Next() {
+ blk, err := hlIt.Block()
+ if err != nil {
+ t.Fatalf("error getting block: %v", err)
+ }
+ if blk.Number().Uint64() != tt.start+uint64(count) {
+ t.Fatalf("wrong block number: want %d, got %d", tt.start+uint64(count), blk.Number().Uint64())
+ }
+ count++
+ }
+ if hlIt.Error() != nil {
+ t.Fatalf("iterator error: %v", hlIt.Error())
+ }
+ if count != totalBlocks {
+ t.Fatalf("wrong iteration count: want %d, got %d", totalBlocks, count)
+ }
+ })
+ }
+}
+
+// TestInitialTD tests the InitialTD calculation separately since it requires
+// specific TD/difficulty values.
+func TestInitialTD(t *testing.T) {
+ t.Parallel()
+
+ f, err := os.CreateTemp(t.TempDir(), "erae-initial-td-test")
+ if err != nil {
+ t.Fatalf("error creating temp file: %v", err)
+ }
+ defer f.Close()
+
+ builder := NewBuilder(f)
+
+ // First block: difficulty=5, TD=10, so initial TD = 10-5 = 5.
+ header := mustEncode(&types.Header{Number: big.NewInt(0), Difficulty: big.NewInt(5)})
+ body := mustEncode(&types.Body{})
+ receipts := mustEncode([]types.SlimReceipt{})
+
+ if err := builder.AddRLP(header, body, receipts, 0, common.Hash{0}, big.NewInt(10), big.NewInt(5)); err != nil {
+ t.Fatalf("error adding block: %v", err)
+ }
+
+ // Second block: difficulty=3, TD=13.
+ header2 := mustEncode(&types.Header{Number: big.NewInt(1), Difficulty: big.NewInt(3)})
+ if err := builder.AddRLP(header2, body, receipts, 1, common.Hash{1}, big.NewInt(13), big.NewInt(3)); err != nil {
+ t.Fatalf("error adding block: %v", err)
+ }
+
+ if _, err := builder.Finalize(); err != nil {
+ t.Fatalf("error finalizing: %v", err)
+ }
+
+ e, err := Open(f.Name())
+ if err != nil {
+ t.Fatalf("failed to open era: %v", err)
+ }
+ defer e.Close()
+
+ initialTD, err := e.InitialTD()
+ if err != nil {
+ t.Fatalf("error getting initial TD: %v", err)
+ }
+
+ // Initial TD should be TD[0] - Difficulty[0] = 10 - 5 = 5.
+ if initialTD.Cmp(big.NewInt(5)) != 0 {
+ t.Fatalf("wrong initial TD: want 5, got %s", initialTD)
+ }
+}
+
+func mustEncode(obj any) []byte {
+ b, err := rlp.EncodeToBytes(obj)
+ if err != nil {
+ panic(fmt.Sprintf("failed to encode obj: %v", err))
+ }
+ return b
+}
diff --git a/internal/era/execdb/iterator.go b/internal/era/execdb/iterator.go
new file mode 100644
index 0000000000..8d17ac00a9
--- /dev/null
+++ b/internal/era/execdb/iterator.go
@@ -0,0 +1,240 @@
+// Copyright 2025 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package execdb
+
+import (
+ "errors"
+ "io"
+ "math/big"
+ "slices"
+
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/internal/era"
+ "github.com/ethereum/go-ethereum/internal/era/e2store"
+ "github.com/ethereum/go-ethereum/rlp"
+ "github.com/klauspost/compress/snappy"
+)
+
+type Iterator struct {
+ inner *RawIterator
+ block *types.Block // cache for decoded block
+}
+
+// NewIterator returns a header/body/receipt iterator over the archive.
+// Call Next immediately to position on the first block.
+func NewIterator(e era.Era) (era.Iterator, error) {
+ inner, err := NewRawIterator(e.(*Era))
+ if err != nil {
+ return nil, err
+ }
+ return &Iterator{inner: inner}, nil
+}
+
+// Next advances to the next block entry.
+func (it *Iterator) Next() bool {
+ it.block = nil
+ return it.inner.Next()
+}
+
+// Number is the number of the block currently loaded.
+func (it *Iterator) Number() uint64 { return it.inner.next - 1 }
+
+// Error returns any iteration error (EOF is reported as nil, identical
+// to the Era‑1 iterator behaviour).
+func (it *Iterator) Error() error { return it.inner.Error() }
+
+// Block decodes the current header+body into a *types.Block.
+func (it *Iterator) Block() (*types.Block, error) {
+ if it.block != nil {
+ return it.block, nil
+ }
+ if it.inner.Header == nil || it.inner.Body == nil {
+ return nil, errors.New("header and body must be non‑nil")
+ }
+ var (
+ h types.Header
+ b types.Body
+ )
+ if err := rlp.Decode(it.inner.Header, &h); err != nil {
+ return nil, err
+ }
+ if err := rlp.Decode(it.inner.Body, &b); err != nil {
+ return nil, err
+ }
+ it.block = types.NewBlockWithHeader(&h).WithBody(b)
+ return it.block, nil
+}
+
+// Receipts decodes receipts for the current block.
+func (it *Iterator) Receipts() (types.Receipts, error) {
+ block, err := it.Block()
+ if err != nil {
+ return nil, err
+ }
+ if it.inner.Receipts == nil {
+ return nil, errors.New("receipts must be non‑nil")
+ }
+ var rs []*types.SlimReceipt
+ if err := rlp.Decode(it.inner.Receipts, &rs); err != nil {
+ return nil, err
+ }
+ if len(rs) != len(block.Transactions()) {
+ return nil, errors.New("number of txs does not match receipts")
+ }
+ receipts := make([]*types.Receipt, len(rs))
+ for i, receipt := range rs {
+ receipts[i] = (*types.Receipt)(receipt)
+ receipts[i].Bloom = types.CreateBloom(receipts[i])
+ }
+ return receipts, nil
+}
+
+// BlockAndReceipts is a convenience wrapper.
+func (it *Iterator) BlockAndReceipts() (*types.Block, types.Receipts, error) {
+ b, err := it.Block()
+ if err != nil {
+ return nil, nil, err
+ }
+ r, err := it.Receipts()
+ if err != nil {
+ return nil, nil, err
+ }
+ return b, r, nil
+}
+
+// TotalDifficulty returns the TD at the current position (if present).
+func (it *Iterator) TotalDifficulty() (*big.Int, error) {
+ if it.inner.TotalDifficulty == nil {
+ return nil, errors.New("total‑difficulty stream is nil")
+ }
+ tdBytes, err := io.ReadAll(it.inner.TotalDifficulty)
+ if err != nil {
+ return nil, err
+ }
+ slices.Reverse(tdBytes)
+ return new(big.Int).SetBytes(tdBytes), nil
+}
+
+// -----------------------------------------------------------------------------
+// Low‑level iterator (raw TLV/offset handling, no decoding)
+// -----------------------------------------------------------------------------
+
+type RawIterator struct {
+ e *Era
+ next uint64 // next block to pull
+ err error
+
+ Header io.Reader
+ Body io.Reader
+ Receipts io.Reader
+ TotalDifficulty io.Reader // nil when archive omits TD
+}
+
+// NewRawIterator creates an iterator positioned *before* the first block.
+func NewRawIterator(e *Era) (*RawIterator, error) {
+ return &RawIterator{e: e, next: e.m.start}, nil
+}
+
+// Next loads the next block’s components; returns false on EOF or error.
+func (it *RawIterator) Next() bool {
+ it.err = nil // clear previous error
+
+ if it.next >= it.e.m.start+it.e.m.count {
+ it.clear()
+ return false
+ }
+
+ headerOffset, err := it.e.headerOff(it.next)
+ if err != nil {
+ it.setErr(err)
+ return false
+ }
+ it.Header, _, err = newSnappyReader(it.e.s, era.TypeCompressedHeader, headerOffset)
+ if err != nil {
+ it.setErr(err)
+ return false
+ }
+
+ bodyOffset, err := it.e.bodyOff(it.next)
+ if err != nil {
+ it.setErr(err)
+ return false
+ }
+ it.Body, _, err = newSnappyReader(it.e.s, era.TypeCompressedBody, bodyOffset)
+ if err != nil {
+ it.setErr(err)
+ return false
+ }
+
+ receiptsOffset, err := it.e.receiptOff(it.next)
+ if err != nil {
+ it.setErr(err)
+ return false
+ }
+ it.Receipts, _, err = newSnappyReader(it.e.s, era.TypeCompressedSlimReceipts, receiptsOffset)
+ if err != nil {
+ it.setErr(err)
+ return false
+ }
+
+ // Check if TD component is present in this file (pre-merge or merge-transition epoch).
+ if int(td) < int(it.e.m.components) {
+ tdOffset, err := it.e.tdOff(it.next)
+ if err != nil {
+ it.setErr(err)
+ return false
+ }
+ it.TotalDifficulty, _, err = it.e.s.ReaderAt(era.TypeTotalDifficulty, tdOffset)
+ if err != nil {
+ it.setErr(err)
+ return false
+ }
+ } else {
+ it.TotalDifficulty = nil
+ }
+
+ it.next++
+ return true
+}
+
+func (it *RawIterator) Number() uint64 { return it.next - 1 }
+
+func (it *RawIterator) Error() error {
+ if it.err == io.EOF {
+ return nil
+ }
+ return it.err
+}
+
+func (it *RawIterator) setErr(err error) {
+ it.err = err
+ it.clear()
+}
+
+func (it *RawIterator) clear() {
+ it.Header, it.Body, it.Receipts, it.TotalDifficulty = nil, nil, nil, nil
+}
+
+// newSnappyReader behaves like era.newSnappyReader: returns a snappy.Reader
+// plus the length of the underlying TLV payload so callers can advance offsets.
+func newSnappyReader(r *e2store.Reader, typ uint16, off int64) (io.Reader, int64, error) {
+ raw, n, err := r.ReaderAt(typ, off)
+ if err != nil {
+ return nil, 0, err
+ }
+ return snappy.NewReader(raw), int64(n), nil
+}
diff --git a/internal/era/execdb/reader.go b/internal/era/execdb/reader.go
new file mode 100644
index 0000000000..d0aaad1748
--- /dev/null
+++ b/internal/era/execdb/reader.go
@@ -0,0 +1,296 @@
+// Copyright 2025 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package execdb
+
+import (
+ "encoding/binary"
+ "fmt"
+ "io"
+ "math/big"
+ "os"
+ "slices"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/internal/era"
+ "github.com/ethereum/go-ethereum/internal/era/e2store"
+ "github.com/ethereum/go-ethereum/rlp"
+ "github.com/klauspost/compress/snappy"
+)
+
+// Era object represents an era file that contains blocks and their components.
+type Era struct {
+ f era.ReadAtSeekCloser
+ s *e2store.Reader
+ m metadata // metadata for the Era file
+}
+
+// Filename returns a recognizable filename for an EraE file.
+// The filename uses the last block hash to uniquely identify the epoch's content.
+func Filename(network string, epoch int, lastBlockHash common.Hash) string {
+ return fmt.Sprintf("%s-%05d-%s.erae", network, epoch, lastBlockHash.Hex()[2:10])
+}
+
+// Open accesses the era file.
+func Open(path string) (*Era, error) {
+ f, err := os.Open(path)
+ if err != nil {
+ return nil, err
+ }
+ e := &Era{f: f, s: e2store.NewReader(f)}
+ if err := e.loadIndex(); err != nil {
+ f.Close()
+ return nil, err
+ }
+ return e, nil
+}
+
+// Close closes the era file safely.
+func (e *Era) Close() error {
+ if e.f == nil {
+ return nil
+ }
+ err := e.f.Close()
+ e.f = nil
+ return err
+}
+
+// From returns an Era backed by f.
+func From(f era.ReadAtSeekCloser) (era.Era, error) {
+ e := &Era{f: f, s: e2store.NewReader(f)}
+ if err := e.loadIndex(); err != nil {
+ f.Close()
+ return nil, err
+ }
+ return e, nil
+}
+
+// Start retrieves the starting block number.
+func (e *Era) Start() uint64 {
+ return e.m.start
+}
+
+// Count retrieves the count of blocks present.
+func (e *Era) Count() uint64 {
+ return e.m.count
+}
+
+// Iterator returns an iterator over the era file.
+func (e *Era) Iterator() (era.Iterator, error) {
+ return NewIterator(e)
+}
+
+// GetBlockByNumber retrieves the block if present within the era file.
+func (e *Era) GetBlockByNumber(blockNum uint64) (*types.Block, error) {
+ h, err := e.GetHeader(blockNum)
+ if err != nil {
+ return nil, err
+ }
+ b, err := e.GetBody(blockNum)
+ if err != nil {
+ return nil, err
+ }
+ return types.NewBlockWithHeader(h).WithBody(*b), nil
+}
+
+// GetHeader retrieves the header from the era file through the cached offset table.
+func (e *Era) GetHeader(num uint64) (*types.Header, error) {
+ off, err := e.headerOff(num)
+ if err != nil {
+ return nil, err
+ }
+
+ r, _, err := e.s.ReaderAt(era.TypeCompressedHeader, off)
+ if err != nil {
+ return nil, err
+ }
+
+ r = snappy.NewReader(r)
+ var h types.Header
+ return &h, rlp.Decode(r, &h)
+}
+
+// GetBody retrieves the body from the era file through cached offset table.
+func (e *Era) GetBody(num uint64) (*types.Body, error) {
+ off, err := e.bodyOff(num)
+ if err != nil {
+ return nil, err
+ }
+
+ r, _, err := e.s.ReaderAt(era.TypeCompressedBody, off)
+ if err != nil {
+ return nil, err
+ }
+
+ r = snappy.NewReader(r)
+ var b types.Body
+ return &b, rlp.Decode(r, &b)
+}
+
+// GetTD retrieves the td from the era file through cached offset table.
+func (e *Era) GetTD(blockNum uint64) (*big.Int, error) {
+ off, err := e.tdOff(blockNum)
+ if err != nil {
+ return nil, err
+ }
+ r, _, err := e.s.ReaderAt(era.TypeTotalDifficulty, off)
+ if err != nil {
+ return nil, err
+ }
+ buf, _ := io.ReadAll(r)
+ slices.Reverse(buf)
+ td := new(big.Int).SetBytes(buf)
+ return td, nil
+}
+
+// GetRawBodyByNumber returns the RLP-encoded body for the given block number.
+func (e *Era) GetRawBodyByNumber(blockNum uint64) ([]byte, error) {
+ off, err := e.bodyOff(blockNum)
+ if err != nil {
+ return nil, err
+ }
+ r, _, err := e.s.ReaderAt(era.TypeCompressedBody, off)
+ if err != nil {
+ return nil, err
+ }
+ r = snappy.NewReader(r)
+ return io.ReadAll(r)
+}
+
+// GetRawReceiptsByNumber returns the RLP-encoded receipts for the given block number.
+func (e *Era) GetRawReceiptsByNumber(blockNum uint64) ([]byte, error) {
+ off, err := e.receiptOff(blockNum)
+ if err != nil {
+ return nil, err
+ }
+ r, _, err := e.s.ReaderAt(era.TypeCompressedSlimReceipts, off)
+ if err != nil {
+ return nil, err
+ }
+ r = snappy.NewReader(r)
+ return io.ReadAll(r)
+}
+
+// InitialTD returns initial total difficulty before the difficulty of the
+// first block of the Era is applied. Returns an error if TD is not available
+// (e.g., post-merge epoch).
+func (e *Era) InitialTD() (*big.Int, error) {
+ // Check if TD component exists.
+ if int(td) >= int(e.m.components) {
+ return nil, fmt.Errorf("total difficulty not available in this epoch")
+ }
+
+ // Get first header to read its difficulty.
+ header, err := e.GetHeader(e.m.start)
+ if err != nil {
+ return nil, fmt.Errorf("read first header: %w", err)
+ }
+
+ // Get TD after first block using the index.
+ firstTD, err := e.GetTD(e.m.start)
+ if err != nil {
+ return nil, fmt.Errorf("read first TD: %w", err)
+ }
+
+ // Initial TD = TD[0] - Difficulty[0]
+ return new(big.Int).Sub(firstTD, header.Difficulty), nil
+}
+
+// Accumulator reads the accumulator entry in the EraE file if it exists.
+// Note that one premerge erae files will contain an accumulator entry.
+func (e *Era) Accumulator() (common.Hash, error) {
+ entry, err := e.s.Find(era.TypeAccumulator)
+ if err != nil {
+ return common.Hash{}, err
+ }
+ return common.BytesToHash(entry.Value), nil
+}
+
+// loadIndex loads in the index table containing all offsets and caches it.
+func (e *Era) loadIndex() error {
+ var err error
+ e.m.length, err = e.f.Seek(0, io.SeekEnd)
+ if err != nil {
+ return err
+ }
+
+ b := make([]byte, 16)
+ if _, err = e.f.ReadAt(b, e.m.length-16); err != nil {
+ return err
+ }
+ e.m.components = binary.LittleEndian.Uint64(b[0:8])
+ e.m.count = binary.LittleEndian.Uint64(b[8:16])
+
+ payloadlen := 8 + 8*e.m.count*e.m.components + 16 // 8 for start block, 8 per property per block, 16 for the number of properties and the number of blocks
+ tlvstart := e.m.length - int64(payloadlen) - 8
+ _, err = e.f.ReadAt(b[:8], tlvstart+8)
+ if err != nil {
+ return err
+ }
+
+ e.m.start = binary.LittleEndian.Uint64(b[:8])
+ return nil
+}
+
+// headerOff, bodyOff, receiptOff, and tdOff return the offsets of the respective components for a given block number.
+func (e *Era) headerOff(num uint64) (int64, error) { return e.indexOffset(num, header) }
+func (e *Era) bodyOff(num uint64) (int64, error) { return e.indexOffset(num, body) }
+func (e *Era) receiptOff(num uint64) (int64, error) { return e.indexOffset(num, receipts) }
+func (e *Era) tdOff(num uint64) (int64, error) { return e.indexOffset(num, td) }
+
+// indexOffset calculates offset to a certain component for a block number within a file.
+func (e *Era) indexOffset(n uint64, component componentType) (int64, error) {
+ if n < e.m.start || n >= e.m.start+e.m.count {
+ return 0, fmt.Errorf("block %d out of range [%d,%d)", n, e.m.start, e.m.start+e.m.count)
+ }
+ if int(component) >= int(e.m.components) {
+ return 0, fmt.Errorf("component %d not present", component)
+ }
+
+ payloadlen := 8 + 8*e.m.count*e.m.components + 16 // 8 for start block, 8 per property per block, 16 for the number of properties and the number of blocks
+ indstart := e.m.length - int64(payloadlen) - 8
+
+ rec := (n-e.m.start)*e.m.components + uint64(component)
+ pos := indstart + 8 + 8 + int64(rec*8)
+
+ var buf [8]byte
+ if _, err := e.f.ReadAt(buf[:], pos); err != nil {
+ return 0, err
+ }
+ rel := binary.LittleEndian.Uint64(buf[:])
+ return int64(rel) + indstart, nil
+}
+
+// metadata contains the information about the era file that is written into the file.
+type metadata struct {
+ start uint64 // start block number
+ count uint64 // number of blocks in the era
+ components uint64 // number of properties
+ length int64 // length of the file in bytes
+}
+
+// componentType represents the integer form of a specific type that can be present in the era file.
+type componentType int
+
+// header, body, receipts, td, and proof are the different types of components that can be present in the era file.
+const (
+ header componentType = iota
+ body
+ receipts
+ td
+ proof
+)
diff --git a/internal/era/builder.go b/internal/era/onedb/builder.go
similarity index 84%
rename from internal/era/builder.go
rename to internal/era/onedb/builder.go
index 975561564c..6497bf4f60 100644
--- a/internal/era/builder.go
+++ b/internal/era/onedb/builder.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package era
+package onedb
import (
"bytes"
@@ -26,6 +26,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/internal/era"
"github.com/ethereum/go-ethereum/internal/era/e2store"
"github.com/ethereum/go-ethereum/rlp"
"github.com/golang/snappy"
@@ -72,20 +73,22 @@ import (
// Due to the accumulator size limit of 8192, the maximum number of blocks in
// an Era1 batch is also 8192.
type Builder struct {
- w *e2store.Writer
- startNum *uint64
- startTd *big.Int
- indexes []uint64
- hashes []common.Hash
- tds []*big.Int
- written int
+ w *e2store.Writer
+ startNum *uint64
+ startTd *big.Int
+ indexes []uint64
+ hashes []common.Hash
+ tds []*big.Int
+ accumulator *common.Hash // accumulator root, set by Finalize
+
+ written int
buf *bytes.Buffer
snappy *snappy.Writer
}
// NewBuilder returns a new Builder instance.
-func NewBuilder(w io.Writer) *Builder {
+func NewBuilder(w io.Writer) era.Builder {
buf := bytes.NewBuffer(nil)
return &Builder{
w: e2store.NewWriter(w),
@@ -117,7 +120,7 @@ func (b *Builder) Add(block *types.Block, receipts types.Receipts, td *big.Int)
func (b *Builder) AddRLP(header, body, receipts []byte, number uint64, hash common.Hash, td, difficulty *big.Int) error {
// Write Era1 version entry before first block.
if b.startNum == nil {
- n, err := b.w.Write(TypeVersion, nil)
+ n, err := b.w.Write(era.TypeVersion, nil)
if err != nil {
return err
}
@@ -126,8 +129,8 @@ func (b *Builder) AddRLP(header, body, receipts []byte, number uint64, hash comm
b.startTd = new(big.Int).Sub(td, difficulty)
b.written += n
}
- if len(b.indexes) >= MaxEra1Size {
- return fmt.Errorf("exceeds maximum batch size of %d", MaxEra1Size)
+ if len(b.indexes) >= era.MaxSize {
+ return fmt.Errorf("exceeds maximum batch size of %d", era.MaxSize)
}
b.indexes = append(b.indexes, uint64(b.written))
@@ -135,19 +138,19 @@ func (b *Builder) AddRLP(header, body, receipts []byte, number uint64, hash comm
b.tds = append(b.tds, td)
// Write block data.
- if err := b.snappyWrite(TypeCompressedHeader, header); err != nil {
+ if err := b.snappyWrite(era.TypeCompressedHeader, header); err != nil {
return err
}
- if err := b.snappyWrite(TypeCompressedBody, body); err != nil {
+ if err := b.snappyWrite(era.TypeCompressedBody, body); err != nil {
return err
}
- if err := b.snappyWrite(TypeCompressedReceipts, receipts); err != nil {
+ if err := b.snappyWrite(era.TypeCompressedReceipts, receipts); err != nil {
return err
}
// Also write total difficulty, but don't snappy encode.
- btd := bigToBytes32(td)
- n, err := b.w.Write(TypeTotalDifficulty, btd[:])
+ btd := era.BigToBytes32(td)
+ n, err := b.w.Write(era.TypeTotalDifficulty, btd[:])
b.written += n
if err != nil {
return err
@@ -157,21 +160,24 @@ func (b *Builder) AddRLP(header, body, receipts []byte, number uint64, hash comm
}
// Finalize computes the accumulator and block index values, then writes the
-// corresponding e2store entries.
+// corresponding e2store entries. Era1 always has an accumulator, so this
+// always returns a valid hash.
func (b *Builder) Finalize() (common.Hash, error) {
if b.startNum == nil {
return common.Hash{}, errors.New("finalize called on empty builder")
}
// Compute accumulator root and write entry.
- root, err := ComputeAccumulator(b.hashes, b.tds)
+ root, err := era.ComputeAccumulator(b.hashes, b.tds)
if err != nil {
return common.Hash{}, fmt.Errorf("error calculating accumulator root: %w", err)
}
- n, err := b.w.Write(TypeAccumulator, root[:])
+ n, err := b.w.Write(era.TypeAccumulator, root[:])
b.written += n
if err != nil {
return common.Hash{}, fmt.Errorf("error writing accumulator: %w", err)
}
+ b.accumulator = &root
+
// Get beginning of index entry to calculate block relative offset.
base := int64(b.written)
@@ -196,13 +202,19 @@ func (b *Builder) Finalize() (common.Hash, error) {
binary.LittleEndian.PutUint64(index[8+count*8:], uint64(count))
// Finally, write the block index entry.
- if _, err := b.w.Write(TypeBlockIndex, index); err != nil {
+ if _, err := b.w.Write(era.TypeBlockIndex, index); err != nil {
return common.Hash{}, fmt.Errorf("unable to write block index: %w", err)
}
return root, nil
}
+// Accumulator returns the accumulator root after Finalize has been called.
+// For Era1, this always returns a non-nil value since all blocks are pre-merge.
+func (b *Builder) Accumulator() *common.Hash {
+ return b.accumulator
+}
+
// snappyWrite is a small helper to take care snappy encoding and writing an e2store entry.
func (b *Builder) snappyWrite(typ uint16, in []byte) error {
var (
diff --git a/internal/era/era_test.go b/internal/era/onedb/builder_test.go
similarity index 95%
rename from internal/era/era_test.go
rename to internal/era/onedb/builder_test.go
index 31fa0076a6..bc7a1d9e63 100644
--- a/internal/era/era_test.go
+++ b/internal/era/onedb/builder_test.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package era
+package onedb
import (
"bytes"
@@ -22,6 +22,7 @@ import (
"io"
"math/big"
"os"
+ "slices"
"testing"
"github.com/ethereum/go-ethereum/common"
@@ -82,7 +83,11 @@ func TestEra1Builder(t *testing.T) {
t.Fatalf("failed to open era: %v", err)
}
defer e.Close()
- it, err := NewRawIterator(e)
+ eraPtr, ok := e.(*Era)
+ if !ok {
+ t.Fatalf("failed to assert *Era type")
+ }
+ it, err := NewRawIterator(eraPtr)
if err != nil {
t.Fatalf("failed to make iterator: %s", err)
}
@@ -119,7 +124,7 @@ func TestEra1Builder(t *testing.T) {
if !bytes.Equal(rawReceipts, chain.receipts[i]) {
t.Fatalf("mismatched receipts: want %s, got %s", chain.receipts[i], rawReceipts)
}
- receipts, err := getReceiptsByNumber(e, i)
+ receipts, err := getReceiptsByNumber(eraPtr, i)
if err != nil {
t.Fatalf("error reading receipts: %v", err)
}
@@ -136,7 +141,8 @@ func TestEra1Builder(t *testing.T) {
if err != nil {
t.Fatalf("error reading td: %v", err)
}
- td := new(big.Int).SetBytes(reverseOrder(rawTd))
+ slices.Reverse(rawTd)
+ td := new(big.Int).SetBytes(rawTd)
if td.Cmp(chain.tds[i]) != 0 {
t.Fatalf("mismatched tds: want %s, got %s", chain.tds[i], td)
}
diff --git a/internal/era/iterator.go b/internal/era/onedb/iterator.go
similarity index 89%
rename from internal/era/iterator.go
rename to internal/era/onedb/iterator.go
index 3c4f82d850..b80fbabbc5 100644
--- a/internal/era/iterator.go
+++ b/internal/era/onedb/iterator.go
@@ -14,14 +14,16 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package era
+package onedb
import (
"errors"
"io"
"math/big"
+ "slices"
"github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/internal/era"
"github.com/ethereum/go-ethereum/rlp"
)
@@ -32,8 +34,8 @@ type Iterator struct {
// NewIterator returns a new Iterator instance. Next must be immediately
// called on new iterators to load the first item.
-func NewIterator(e *Era) (*Iterator, error) {
- inner, err := NewRawIterator(e)
+func NewIterator(e era.Era) (era.Iterator, error) {
+ inner, err := NewRawIterator(e.(*Era))
if err != nil {
return nil, err
}
@@ -107,7 +109,8 @@ func (it *Iterator) TotalDifficulty() (*big.Int, error) {
if err != nil {
return nil, err
}
- return new(big.Int).SetBytes(reverseOrder(td)), nil
+ slices.Reverse(td)
+ return new(big.Int).SetBytes(td), nil
}
// RawIterator reads an RLP-encode Era1 entries.
@@ -151,22 +154,22 @@ func (it *RawIterator) Next() bool {
return false
}
var n int64
- if it.Header, n, it.err = newSnappyReader(it.e.s, TypeCompressedHeader, off); it.err != nil {
+ if it.Header, n, it.err = newSnappyReader(it.e.s, era.TypeCompressedHeader, off); it.err != nil {
it.clear()
return true
}
off += n
- if it.Body, n, it.err = newSnappyReader(it.e.s, TypeCompressedBody, off); it.err != nil {
+ if it.Body, n, it.err = newSnappyReader(it.e.s, era.TypeCompressedBody, off); it.err != nil {
it.clear()
return true
}
off += n
- if it.Receipts, n, it.err = newSnappyReader(it.e.s, TypeCompressedReceipts, off); it.err != nil {
+ if it.Receipts, n, it.err = newSnappyReader(it.e.s, era.TypeCompressedReceipts, off); it.err != nil {
it.clear()
return true
}
off += n
- if it.TotalDifficulty, _, it.err = it.e.s.ReaderAt(TypeTotalDifficulty, off); it.err != nil {
+ if it.TotalDifficulty, _, it.err = it.e.s.ReaderAt(era.TypeTotalDifficulty, off); it.err != nil {
it.clear()
return true
}
diff --git a/internal/era/onedb/reader.go b/internal/era/onedb/reader.go
new file mode 100644
index 0000000000..df93ca8211
--- /dev/null
+++ b/internal/era/onedb/reader.go
@@ -0,0 +1,279 @@
+// Copyright 2025 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package onedb
+
+import (
+ "encoding/binary"
+ "fmt"
+ "io"
+ "math/big"
+ "os"
+ "slices"
+ "sync"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/internal/era"
+ "github.com/ethereum/go-ethereum/internal/era/e2store"
+ "github.com/ethereum/go-ethereum/rlp"
+ "github.com/golang/snappy"
+)
+
+// Filename returns a recognizable Era1-formatted file name for the specified
+// epoch and network.
+func Filename(network string, epoch int, root common.Hash) string {
+ return fmt.Sprintf("%s-%05d-%s.era1", network, epoch, root.Hex()[2:10])
+}
+
+type ReadAtSeekCloser interface {
+ io.ReaderAt
+ io.Seeker
+ io.Closer
+}
+
+// Era reads and Era1 file.
+type Era struct {
+ f ReadAtSeekCloser // backing era1 file
+ s *e2store.Reader // e2store reader over f
+ m metadata // start, count, length info
+ mu *sync.Mutex // lock for buf
+ buf [8]byte // buffer reading entry offsets
+}
+
+// From returns an Era backed by f.
+func From(f era.ReadAtSeekCloser) (era.Era, error) {
+ m, err := readMetadata(f)
+ if err != nil {
+ return nil, err
+ }
+ return &Era{
+ f: f,
+ s: e2store.NewReader(f),
+ m: m,
+ mu: new(sync.Mutex),
+ }, nil
+}
+
+// Open returns an Era backed by the given filename.
+func Open(filename string) (era.Era, error) {
+ f, err := os.Open(filename)
+ if err != nil {
+ return nil, err
+ }
+ return From(f)
+}
+
+func (e *Era) Close() error {
+ return e.f.Close()
+}
+
+// Iterator returns an iterator over the era file.
+func (e *Era) Iterator() (era.Iterator, error) {
+ return NewIterator(e)
+}
+
+// GetBlockByNumber returns the block for the given block number.
+func (e *Era) GetBlockByNumber(num uint64) (*types.Block, error) {
+ if e.m.start > num || e.m.start+e.m.count <= num {
+ return nil, fmt.Errorf("out-of-bounds: %d not in [%d, %d)", num, e.m.start, e.m.start+e.m.count)
+ }
+ off, err := e.readOffset(num)
+ if err != nil {
+ return nil, err
+ }
+ r, n, err := newSnappyReader(e.s, era.TypeCompressedHeader, off)
+ if err != nil {
+ return nil, err
+ }
+ var header types.Header
+ if err := rlp.Decode(r, &header); err != nil {
+ return nil, err
+ }
+ off += n
+ r, _, err = newSnappyReader(e.s, era.TypeCompressedBody, off)
+ if err != nil {
+ return nil, err
+ }
+ var body types.Body
+ if err := rlp.Decode(r, &body); err != nil {
+ return nil, err
+ }
+ return types.NewBlockWithHeader(&header).WithBody(body), nil
+}
+
+// GetRawBodyByNumber returns the RLP-encoded body for the given block number.
+func (e *Era) GetRawBodyByNumber(num uint64) ([]byte, error) {
+ if e.m.start > num || e.m.start+e.m.count <= num {
+ return nil, fmt.Errorf("out-of-bounds: %d not in [%d, %d)", num, e.m.start, e.m.start+e.m.count)
+ }
+ off, err := e.readOffset(num)
+ if err != nil {
+ return nil, err
+ }
+ off, err = e.s.SkipN(off, 1)
+ if err != nil {
+ return nil, err
+ }
+ r, _, err := newSnappyReader(e.s, era.TypeCompressedBody, off)
+ if err != nil {
+ return nil, err
+ }
+ return io.ReadAll(r)
+}
+
+// GetRawReceiptsByNumber returns the RLP-encoded receipts for the given block number.
+func (e *Era) GetRawReceiptsByNumber(num uint64) ([]byte, error) {
+ if e.m.start > num || e.m.start+e.m.count <= num {
+ return nil, fmt.Errorf("out-of-bounds: %d not in [%d, %d)", num, e.m.start, e.m.start+e.m.count)
+ }
+ off, err := e.readOffset(num)
+ if err != nil {
+ return nil, err
+ }
+
+ // Skip over header and body.
+ off, err = e.s.SkipN(off, 2)
+ if err != nil {
+ return nil, err
+ }
+
+ r, _, err := newSnappyReader(e.s, era.TypeCompressedReceipts, off)
+ if err != nil {
+ return nil, err
+ }
+ return io.ReadAll(r)
+}
+
+// Accumulator reads the accumulator entry in the Era1 file.
+func (e *Era) Accumulator() (common.Hash, error) {
+ entry, err := e.s.Find(era.TypeAccumulator)
+ if err != nil {
+ return common.Hash{}, err
+ }
+ return common.BytesToHash(entry.Value), nil
+}
+
+// InitialTD returns initial total difficulty before the difficulty of the
+// first block of the Era1 is applied.
+func (e *Era) InitialTD() (*big.Int, error) {
+ var (
+ r io.Reader
+ header types.Header
+ rawTd []byte
+ n int64
+ off int64
+ err error
+ )
+
+ // Read first header.
+ if off, err = e.readOffset(e.m.start); err != nil {
+ return nil, err
+ }
+ if r, n, err = newSnappyReader(e.s, era.TypeCompressedHeader, off); err != nil {
+ return nil, err
+ }
+ if err := rlp.Decode(r, &header); err != nil {
+ return nil, err
+ }
+ off += n
+
+ // Skip over header and body.
+ off, err = e.s.SkipN(off, 2)
+ if err != nil {
+ return nil, err
+ }
+
+ // Read total difficulty after first block.
+ if r, _, err = e.s.ReaderAt(era.TypeTotalDifficulty, off); err != nil {
+ return nil, err
+ }
+ rawTd, err = io.ReadAll(r)
+ if err != nil {
+ return nil, err
+ }
+ slices.Reverse(rawTd)
+ td := new(big.Int).SetBytes(rawTd)
+ return td.Sub(td, header.Difficulty), nil
+}
+
+// Start returns the listed start block.
+func (e *Era) Start() uint64 {
+ return e.m.start
+}
+
+// Count returns the total number of blocks in the Era1.
+func (e *Era) Count() uint64 {
+ return e.m.count
+}
+
+// readOffset reads a specific block's offset from the block index. The value n
+// is the absolute block number desired.
+func (e *Era) readOffset(n uint64) (int64, error) {
+ var (
+ blockIndexRecordOffset = e.m.length - 24 - int64(e.m.count)*8 // skips start, count, and header
+ firstIndex = blockIndexRecordOffset + 16 // first index after header / start-num
+ indexOffset = int64(n-e.m.start) * 8 // desired index * size of indexes
+ offOffset = firstIndex + indexOffset // offset of block offset
+ )
+ e.mu.Lock()
+ defer e.mu.Unlock()
+ clear(e.buf[:])
+ if _, err := e.f.ReadAt(e.buf[:], offOffset); err != nil {
+ return 0, err
+ }
+ // Since the block offset is relative from the start of the block index record
+ // we need to add the record offset to it's offset to get the block's absolute
+ // offset.
+ return blockIndexRecordOffset + int64(binary.LittleEndian.Uint64(e.buf[:])), nil
+}
+
+// newSnappyReader returns a snappy.Reader for the e2store entry value at off.
+func newSnappyReader(e *e2store.Reader, expectedType uint16, off int64) (io.Reader, int64, error) {
+ r, n, err := e.ReaderAt(expectedType, off)
+ if err != nil {
+ return nil, 0, err
+ }
+ return snappy.NewReader(r), int64(n), err
+}
+
+// metadata wraps the metadata in the block index.
+type metadata struct {
+ start uint64
+ count uint64
+ length int64
+}
+
+// readMetadata reads the metadata stored in an Era1 file's block index.
+func readMetadata(f ReadAtSeekCloser) (m metadata, err error) {
+ // Determine length of reader.
+ if m.length, err = f.Seek(0, io.SeekEnd); err != nil {
+ return
+ }
+ b := make([]byte, 16)
+ // Read count. It's the last 8 bytes of the file.
+ if _, err = f.ReadAt(b[:8], m.length-8); err != nil {
+ return
+ }
+ m.count = binary.LittleEndian.Uint64(b)
+ // Read start. It's at the offset -sizeof(m.count) -
+ // count*sizeof(indexEntry) - sizeof(m.start)
+ if _, err = f.ReadAt(b[8:], m.length-16-int64(m.count*8)); err != nil {
+ return
+ }
+ m.start = binary.LittleEndian.Uint64(b[8:])
+ return
+}
diff --git a/internal/era/proof.go b/internal/era/proof.go
new file mode 100644
index 0000000000..464d9e6fe5
--- /dev/null
+++ b/internal/era/proof.go
@@ -0,0 +1,36 @@
+// Copyright 2025 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+package era
+
+import (
+ "io"
+
+ "github.com/ethereum/go-ethereum/rlp"
+)
+
+type ProofVariant uint16
+
+const (
+ ProofNone ProofVariant = iota
+)
+
+// Proof is the interface for all block proof types in the package.
+// It's a stub for later integration into Era.
+type Proof interface {
+ EncodeRLP(w io.Writer) error
+ DecodeRLP(s *rlp.Stream) error
+ Variant() ProofVariant
+}
diff --git a/internal/ethapi/errors.go b/internal/ethapi/errors.go
index 30711a0167..e406c36d6c 100644
--- a/internal/ethapi/errors.go
+++ b/internal/ethapi/errors.go
@@ -112,7 +112,6 @@ const (
errCodeClientLimitExceeded = -38026
errCodeInternalError = -32603
errCodeInvalidParams = -32602
- errCodeReverted = -32000
errCodeVMError = -32015
errCodeTxSyncTimeout = 4
)
diff --git a/internal/ethapi/simulate.go b/internal/ethapi/simulate.go
index 3c08061313..c9396cd327 100644
--- a/internal/ethapi/simulate.go
+++ b/internal/ethapi/simulate.go
@@ -310,7 +310,7 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
if errors.Is(result.Err, vm.ErrExecutionReverted) {
// If the result contains a revert reason, try to unpack it.
revertErr := newRevertError(result.Revert())
- callRes.Error = &callError{Message: revertErr.Error(), Code: errCodeReverted, Data: revertErr.ErrorData().(string)}
+ callRes.Error = &callError{Message: revertErr.Error(), Code: revertErr.ErrorCode(), Data: revertErr.ErrorData().(string)}
} else {
callRes.Error = &callError{Message: result.Err.Error(), Code: errCodeVMError}
}
diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go
index 23aa8e5947..22d1adfa57 100644
--- a/internal/ethapi/transaction_args.go
+++ b/internal/ethapi/transaction_args.go
@@ -329,8 +329,8 @@ func (args *TransactionArgs) setBlobTxSidecar(ctx context.Context, config sideca
commitments = make([]kzg4844.Commitment, n)
proofs = make([]kzg4844.Proof, 0, proofLen)
)
- for i, b := range args.Blobs {
- c, err := kzg4844.BlobToCommitment(&b)
+ for i := range args.Blobs {
+ c, err := kzg4844.BlobToCommitment(&args.Blobs[i])
if err != nil {
return fmt.Errorf("blobs[%d]: error computing commitment: %v", i, err)
}
@@ -338,13 +338,13 @@ func (args *TransactionArgs) setBlobTxSidecar(ctx context.Context, config sideca
switch config.blobSidecarVersion {
case types.BlobSidecarVersion0:
- p, err := kzg4844.ComputeBlobProof(&b, c)
+ p, err := kzg4844.ComputeBlobProof(&args.Blobs[i], c)
if err != nil {
return fmt.Errorf("blobs[%d]: error computing proof: %v", i, err)
}
proofs = append(proofs, p)
case types.BlobSidecarVersion1:
- ps, err := kzg4844.ComputeCellProofs(&b)
+ ps, err := kzg4844.ComputeCellProofs(&args.Blobs[i])
if err != nil {
return fmt.Errorf("blobs[%d]: error computing proof: %v", i, err)
}
@@ -356,8 +356,8 @@ func (args *TransactionArgs) setBlobTxSidecar(ctx context.Context, config sideca
} else {
switch config.blobSidecarVersion {
case types.BlobSidecarVersion0:
- for i, b := range args.Blobs {
- if err := kzg4844.VerifyBlobProof(&b, args.Commitments[i], args.Proofs[i]); err != nil {
+ for i := range args.Blobs {
+ if err := kzg4844.VerifyBlobProof(&args.Blobs[i], args.Commitments[i], args.Proofs[i]); err != nil {
return fmt.Errorf("failed to verify blob proof: %v", err)
}
}
diff --git a/metrics/counter.go b/metrics/counter.go
index c884e9a178..0e4d93bfbc 100644
--- a/metrics/counter.go
+++ b/metrics/counter.go
@@ -7,7 +7,10 @@ import (
// GetOrRegisterCounter returns an existing Counter or constructs and registers
// a new Counter.
func GetOrRegisterCounter(name string, r Registry) *Counter {
- return getOrRegister(name, NewCounter, r)
+ if r == nil {
+ r = DefaultRegistry
+ }
+ return r.GetOrRegister(name, func() any { return NewCounter() }).(*Counter)
}
// NewCounter constructs a new Counter.
diff --git a/metrics/counter_float64.go b/metrics/counter_float64.go
index 6cc73d89a2..caaaeb7be7 100644
--- a/metrics/counter_float64.go
+++ b/metrics/counter_float64.go
@@ -8,7 +8,10 @@ import (
// GetOrRegisterCounterFloat64 returns an existing *CounterFloat64 or constructs and registers
// a new CounterFloat64.
func GetOrRegisterCounterFloat64(name string, r Registry) *CounterFloat64 {
- return getOrRegister(name, NewCounterFloat64, r)
+ if r == nil {
+ r = DefaultRegistry
+ }
+ return r.GetOrRegister(name, func() any { return NewCounterFloat64() }).(*CounterFloat64)
}
// NewCounterFloat64 constructs a new CounterFloat64.
diff --git a/metrics/gauge.go b/metrics/gauge.go
index 20de95255b..39d5ccb3bc 100644
--- a/metrics/gauge.go
+++ b/metrics/gauge.go
@@ -11,7 +11,10 @@ func (g GaugeSnapshot) Value() int64 { return int64(g) }
// GetOrRegisterGauge returns an existing Gauge or constructs and registers a
// new Gauge.
func GetOrRegisterGauge(name string, r Registry) *Gauge {
- return getOrRegister(name, NewGauge, r)
+ if r == nil {
+ r = DefaultRegistry
+ }
+ return r.GetOrRegister(name, func() any { return NewGauge() }).(*Gauge)
}
// NewGauge constructs a new Gauge.
diff --git a/metrics/gauge_float64.go b/metrics/gauge_float64.go
index 48524e4c3f..c7a1df0a23 100644
--- a/metrics/gauge_float64.go
+++ b/metrics/gauge_float64.go
@@ -8,7 +8,10 @@ import (
// GetOrRegisterGaugeFloat64 returns an existing GaugeFloat64 or constructs and registers a
// new GaugeFloat64.
func GetOrRegisterGaugeFloat64(name string, r Registry) *GaugeFloat64 {
- return getOrRegister(name, NewGaugeFloat64, r)
+ if r == nil {
+ r = DefaultRegistry
+ }
+ return r.GetOrRegister(name, func() any { return NewGaugeFloat64() }).(*GaugeFloat64)
}
// GaugeFloat64Snapshot is a read-only copy of a GaugeFloat64.
diff --git a/metrics/gauge_info.go b/metrics/gauge_info.go
index 34ac917919..30c5114085 100644
--- a/metrics/gauge_info.go
+++ b/metrics/gauge_info.go
@@ -16,7 +16,10 @@ func (val GaugeInfoValue) String() string {
// GetOrRegisterGaugeInfo returns an existing GaugeInfo or constructs and registers a
// new GaugeInfo.
func GetOrRegisterGaugeInfo(name string, r Registry) *GaugeInfo {
- return getOrRegister(name, NewGaugeInfo, r)
+ if r == nil {
+ r = DefaultRegistry
+ }
+ return r.GetOrRegister(name, func() any { return NewGaugeInfo() }).(*GaugeInfo)
}
// NewGaugeInfo constructs a new GaugeInfo.
diff --git a/metrics/histogram.go b/metrics/histogram.go
index 18bf6e3d2b..467457abdb 100644
--- a/metrics/histogram.go
+++ b/metrics/histogram.go
@@ -23,13 +23,19 @@ type Histogram interface {
// GetOrRegisterHistogram returns an existing Histogram or constructs and
// registers a new StandardHistogram.
func GetOrRegisterHistogram(name string, r Registry, s Sample) Histogram {
- return getOrRegister(name, func() Histogram { return NewHistogram(s) }, r)
+ if r == nil {
+ r = DefaultRegistry
+ }
+ return r.GetOrRegister(name, func() any { return NewHistogram(s) }).(Histogram)
}
// GetOrRegisterHistogramLazy returns an existing Histogram or constructs and
// registers a new StandardHistogram.
func GetOrRegisterHistogramLazy(name string, r Registry, s func() Sample) Histogram {
- return getOrRegister(name, func() Histogram { return NewHistogram(s()) }, r)
+ if r == nil {
+ r = DefaultRegistry
+ }
+ return r.GetOrRegister(name, func() any { return NewHistogram(s()) }).(Histogram)
}
// NewHistogram constructs a new StandardHistogram from a Sample.
diff --git a/metrics/meter.go b/metrics/meter.go
index ee23af10eb..2829774d8c 100644
--- a/metrics/meter.go
+++ b/metrics/meter.go
@@ -12,7 +12,10 @@ import (
// Be sure to unregister the meter from the registry once it is of no use to
// allow for garbage collection.
func GetOrRegisterMeter(name string, r Registry) *Meter {
- return getOrRegister(name, NewMeter, r)
+ if r == nil {
+ r = DefaultRegistry
+ }
+ return r.GetOrRegister(name, func() any { return NewMeter() }).(*Meter)
}
// NewMeter constructs a new Meter and launches a goroutine.
diff --git a/metrics/registry.go b/metrics/registry.go
index 6070f3d0e9..298598a9aa 100644
--- a/metrics/registry.go
+++ b/metrics/registry.go
@@ -143,6 +143,8 @@ func (r *StandardRegistry) GetAll() map[string]map[string]interface{} {
values["value"] = metric.Snapshot().Value()
case *GaugeFloat64:
values["value"] = metric.Snapshot().Value()
+ case *GaugeInfo:
+ values["value"] = metric.Snapshot().Value()
case *Healthcheck:
values["error"] = nil
metric.Check()
@@ -186,6 +188,18 @@ func (r *StandardRegistry) GetAll() map[string]map[string]interface{} {
values["5m.rate"] = t.Rate5()
values["15m.rate"] = t.Rate15()
values["mean.rate"] = t.RateMean()
+ case *ResettingTimer:
+ t := metric.Snapshot()
+ ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
+ values["count"] = t.Count()
+ values["min"] = t.Min()
+ values["max"] = t.Max()
+ values["mean"] = t.Mean()
+ values["median"] = ps[0]
+ values["75%"] = ps[1]
+ values["95%"] = ps[2]
+ values["99%"] = ps[3]
+ values["99.9%"] = ps[4]
}
data[name] = values
})
@@ -331,13 +345,6 @@ func GetOrRegister(name string, i func() interface{}) interface{} {
return DefaultRegistry.GetOrRegister(name, i)
}
-func getOrRegister[T any](name string, ctor func() T, r Registry) T {
- if r == nil {
- r = DefaultRegistry
- }
- return r.GetOrRegister(name, func() any { return ctor() }).(T)
-}
-
// Register the given metric under the given name. Returns a ErrDuplicateMetric
// if a metric by the given name is already registered.
func Register(name string, i interface{}) error {
diff --git a/metrics/registry_test.go b/metrics/registry_test.go
index 6af0796da9..1aad7a0028 100644
--- a/metrics/registry_test.go
+++ b/metrics/registry_test.go
@@ -14,6 +14,31 @@ func BenchmarkRegistry(b *testing.B) {
}
}
+func BenchmarkRegistryGetOrRegister(b *testing.B) {
+ sample := func() Sample { return nil }
+ tests := []struct {
+ name string
+ ctor func() any
+ }{
+ {name: "counter", ctor: func() any { return GetOrRegisterCounter("counter", DefaultRegistry) }},
+ {name: "gauge", ctor: func() any { return GetOrRegisterGauge("gauge", DefaultRegistry) }},
+ {name: "gaugefloat64", ctor: func() any { return GetOrRegisterGaugeFloat64("gaugefloat64", DefaultRegistry) }},
+ {name: "histogram", ctor: func() any { return GetOrRegisterHistogram("histogram", DefaultRegistry, sample()) }},
+ {name: "meter", ctor: func() any { return GetOrRegisterMeter("meter", DefaultRegistry) }},
+ {name: "timer", ctor: func() any { return GetOrRegisterTimer("timer", DefaultRegistry) }},
+ {name: "gaugeinfo", ctor: func() any { return GetOrRegisterGaugeInfo("gaugeinfo", DefaultRegistry) }},
+ {name: "resettingtimer", ctor: func() any { return GetOrRegisterResettingTimer("resettingtimer", DefaultRegistry) }},
+ {name: "runtimehistogramlazy", ctor: func() any { return GetOrRegisterHistogramLazy("runtimehistogramlazy", DefaultRegistry, sample) }},
+ }
+ for _, test := range tests {
+ b.Run(test.name, func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ test.ctor()
+ }
+ })
+ }
+}
+
func BenchmarkRegistryGetOrRegisterParallel_8(b *testing.B) {
benchmarkRegistryGetOrRegisterParallel(b, 8)
}
@@ -268,7 +293,7 @@ func TestPrefixedChildRegistryGet(t *testing.T) {
}
func TestChildPrefixedRegistryRegister(t *testing.T) {
- r := NewPrefixedChildRegistry(DefaultRegistry, "prefix.")
+ r := NewPrefixedChildRegistry(NewRegistry(), "prefix.")
err := r.Register("foo", NewCounter())
c := NewCounter()
Register("bar", c)
diff --git a/metrics/resetting_timer.go b/metrics/resetting_timer.go
index 8aa7dc1488..a3f46e52e0 100644
--- a/metrics/resetting_timer.go
+++ b/metrics/resetting_timer.go
@@ -8,7 +8,10 @@ import (
// GetOrRegisterResettingTimer returns an existing ResettingTimer or constructs and registers a
// new ResettingTimer.
func GetOrRegisterResettingTimer(name string, r Registry) *ResettingTimer {
- return getOrRegister(name, NewResettingTimer, r)
+ if r == nil {
+ r = DefaultRegistry
+ }
+ return r.GetOrRegister(name, func() any { return NewResettingTimer() }).(*ResettingTimer)
}
// NewRegisteredResettingTimer constructs and registers a new ResettingTimer.
diff --git a/metrics/runtimehistogram.go b/metrics/runtimehistogram.go
index efbed498af..e975a570a4 100644
--- a/metrics/runtimehistogram.go
+++ b/metrics/runtimehistogram.go
@@ -8,8 +8,10 @@ import (
)
func getOrRegisterRuntimeHistogram(name string, scale float64, r Registry) *runtimeHistogram {
- constructor := func() Histogram { return newRuntimeHistogram(scale) }
- return getOrRegister(name, constructor, r).(*runtimeHistogram)
+ if r == nil {
+ r = DefaultRegistry
+ }
+ return r.GetOrRegister(name, func() any { return newRuntimeHistogram(scale) }).(*runtimeHistogram)
}
// runtimeHistogram wraps a runtime/metrics histogram.
diff --git a/metrics/timer.go b/metrics/timer.go
index 894bdfc327..8082b31947 100644
--- a/metrics/timer.go
+++ b/metrics/timer.go
@@ -10,7 +10,10 @@ import (
// Be sure to unregister the meter from the registry once it is of no use to
// allow for garbage collection.
func GetOrRegisterTimer(name string, r Registry) *Timer {
- return getOrRegister(name, NewTimer, r)
+ if r == nil {
+ r = DefaultRegistry
+ }
+ return r.GetOrRegister(name, func() any { return NewTimer() }).(*Timer)
}
// NewCustomTimer constructs a new Timer from a Histogram and a Meter.
diff --git a/node/rpcstack.go b/node/rpcstack.go
index a1cc832f9f..a9ac88e4de 100644
--- a/node/rpcstack.go
+++ b/node/rpcstack.go
@@ -138,6 +138,9 @@ func (h *httpServer) start() error {
// Initialize the server.
h.server = &http.Server{Handler: h}
+ h.server.Protocols = new(http.Protocols)
+ h.server.Protocols.SetHTTP1(true)
+ h.server.Protocols.SetUnencryptedHTTP2(true)
if h.timeouts != (rpc.HTTPTimeouts{}) {
CheckTimeouts(&h.timeouts)
h.server.ReadTimeout = h.timeouts.ReadTimeout
diff --git a/node/rpcstack_test.go b/node/rpcstack_test.go
index 54e58cccb2..bd75dac4eb 100644
--- a/node/rpcstack_test.go
+++ b/node/rpcstack_test.go
@@ -593,6 +593,38 @@ func TestHTTPWriteTimeout(t *testing.T) {
})
}
+func TestHTTP2H2C(t *testing.T) {
+ srv := createAndStartServer(t, &httpConfig{}, false, &wsConfig{}, nil)
+ defer srv.stop()
+
+ // Create an HTTP/2 cleartext client.
+ transport := &http.Transport{}
+ transport.Protocols = new(http.Protocols)
+ transport.Protocols.SetUnencryptedHTTP2(true)
+ client := &http.Client{Transport: transport}
+
+ body := strings.NewReader(`{"jsonrpc":"2.0","id":1,"method":"rpc_modules","params":[]}`)
+ resp, err := client.Post("http://"+srv.listenAddr(), "application/json", body)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode != 200 {
+ t.Fatalf("expected status 200, got %d", resp.StatusCode)
+ }
+ if resp.Proto != "HTTP/2.0" {
+ t.Fatalf("expected HTTP/2.0, got %s", resp.Proto)
+ }
+ result, err := io.ReadAll(resp.Body)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !strings.Contains(string(result), "jsonrpc") {
+ t.Fatalf("unexpected response: %s", result)
+ }
+}
+
func apis() []rpc.API {
return []rpc.API{
{
diff --git a/p2p/dnsdisc/tree.go b/p2p/dnsdisc/tree.go
index a8295ac9eb..a629d6291f 100644
--- a/p2p/dnsdisc/tree.go
+++ b/p2p/dnsdisc/tree.go
@@ -28,10 +28,10 @@ import (
"strings"
"github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/crypto/keccak"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/enr"
"github.com/ethereum/go-ethereum/rlp"
- "golang.org/x/crypto/sha3"
)
// Tree is a merkle tree of node records.
@@ -262,7 +262,7 @@ const (
)
func subdomain(e entry) string {
- h := sha3.NewLegacyKeccak256()
+ h := keccak.NewLegacyKeccak256()
io.WriteString(h, e.String())
return b32format.EncodeToString(h.Sum(nil)[:16])
}
@@ -272,7 +272,7 @@ func (e *rootEntry) String() string {
}
func (e *rootEntry) sigHash() []byte {
- h := sha3.NewLegacyKeccak256()
+ h := keccak.NewLegacyKeccak256()
fmt.Fprintf(h, rootPrefix+" e=%s l=%s seq=%d", e.eroot, e.lroot, e.seq)
return h.Sum(nil)
}
diff --git a/p2p/enode/idscheme.go b/p2p/enode/idscheme.go
index db7841c047..313815c465 100644
--- a/p2p/enode/idscheme.go
+++ b/p2p/enode/idscheme.go
@@ -23,9 +23,9 @@ import (
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/crypto/keccak"
"github.com/ethereum/go-ethereum/p2p/enr"
"github.com/ethereum/go-ethereum/rlp"
- "golang.org/x/crypto/sha3"
)
// ValidSchemes is a List of known secure identity schemes.
@@ -49,7 +49,7 @@ func SignV4(r *enr.Record, privkey *ecdsa.PrivateKey) error {
cpy.Set(enr.ID("v4"))
cpy.Set(Secp256k1(privkey.PublicKey))
- h := sha3.NewLegacyKeccak256()
+ h := keccak.NewLegacyKeccak256()
rlp.Encode(h, cpy.AppendElements(nil))
sig, err := crypto.Sign(h.Sum(nil), privkey)
if err != nil {
@@ -70,7 +70,7 @@ func (V4ID) Verify(r *enr.Record, sig []byte) error {
return errors.New("invalid public key")
}
- h := sha3.NewLegacyKeccak256()
+ h := keccak.NewLegacyKeccak256()
rlp.Encode(h, r.AppendElements(nil))
if !crypto.VerifySignature(entry, h.Sum(nil), sig) {
return enr.ErrInvalidSig
diff --git a/p2p/rlpx/rlpx.go b/p2p/rlpx/rlpx.go
index 0dc4ecbe2d..40a5c38fcb 100644
--- a/p2p/rlpx/rlpx.go
+++ b/p2p/rlpx/rlpx.go
@@ -36,9 +36,9 @@ import (
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/ecies"
+ "github.com/ethereum/go-ethereum/crypto/keccak"
"github.com/ethereum/go-ethereum/rlp"
"github.com/golang/snappy"
- "golang.org/x/crypto/sha3"
)
// Conn is an RLPx network connection. It wraps a low-level network connection. The
@@ -486,10 +486,10 @@ func (h *handshakeState) secrets(auth, authResp []byte) (Secrets, error) {
}
// setup sha3 instances for the MACs
- mac1 := sha3.NewLegacyKeccak256()
+ mac1 := keccak.NewLegacyKeccak256()
mac1.Write(xor(s.MAC, h.respNonce))
mac1.Write(auth)
- mac2 := sha3.NewLegacyKeccak256()
+ mac2 := keccak.NewLegacyKeccak256()
mac2.Write(xor(s.MAC, h.initNonce))
mac2.Write(authResp)
if h.initiator {
diff --git a/params/config.go b/params/config.go
index c5dfad1cd3..197ed56f8a 100644
--- a/params/config.go
+++ b/params/config.go
@@ -1375,7 +1375,6 @@ func (err *ConfigCompatError) Error() string {
// Rules is a one time interface meaning that it shouldn't be used in between transition
// phases.
type Rules struct {
- ChainID *big.Int
IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool
IsEIP2929, IsEIP4762 bool
IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool
@@ -1386,15 +1385,10 @@ type Rules struct {
// Rules ensures c's ChainID is not nil.
func (c *ChainConfig) Rules(num *big.Int, isMerge bool, timestamp uint64) Rules {
- chainID := c.ChainID
- if chainID == nil {
- chainID = new(big.Int)
- }
// disallow setting Merge out of order
isMerge = isMerge && c.IsLondon(num)
isVerkle := isMerge && c.IsVerkle(num, timestamp)
return Rules{
- ChainID: new(big.Int).Set(chainID),
IsHomestead: c.IsHomestead(num),
IsEIP150: c.IsEIP150(num),
IsEIP155: c.IsEIP155(num),
diff --git a/rlp/encode.go b/rlp/encode.go
index ed99275739..ba8959ee6f 100644
--- a/rlp/encode.go
+++ b/rlp/encode.go
@@ -102,6 +102,29 @@ func EncodeToReader(val interface{}) (size int, r io.Reader, err error) {
return buf.size(), &encReader{buf: buf}, nil
}
+// EncodeToRawList encodes val as an RLP list and returns it as a RawList.
+func EncodeToRawList[T any](val []T) (RawList[T], error) {
+ if len(val) == 0 {
+ return RawList[T]{}, nil
+ }
+
+ // Encode the value to an internal buffer.
+ buf := getEncBuffer()
+ defer encBufferPool.Put(buf)
+ if err := buf.encode(val); err != nil {
+ return RawList[T]{}, err
+ }
+
+ // Create the RawList. RawList assumes the initial list header is padded
+ // 9 bytes, so we have to determine the offset where the value should be
+ // placed.
+ contentSize := buf.lheads[0].size
+ bytes := make([]byte, contentSize+9)
+ offset := 9 - headsize(uint64(contentSize))
+ buf.copyTo(bytes[offset:])
+ return RawList[T]{enc: bytes}, nil
+}
+
type listhead struct {
offset int // index of this header in string data
size int // total size of encoded data (including list headers)
diff --git a/rlp/iterator.go b/rlp/iterator.go
index 05567fa05b..f1f214c4bc 100644
--- a/rlp/iterator.go
+++ b/rlp/iterator.go
@@ -16,31 +16,35 @@
package rlp
-type listIterator struct {
- data []byte
- next []byte
- err error
+// Iterator is an iterator over the elements of an encoded container.
+type Iterator struct {
+ data []byte
+ next []byte
+ offset int
+ err error
}
-// NewListIterator creates an iterator for the (list) represented by data
-func NewListIterator(data RawValue) (*listIterator, error) {
+// NewListIterator creates an iterator for the (list) represented by data.
+func NewListIterator(data RawValue) (Iterator, error) {
k, t, c, err := readKind(data)
if err != nil {
- return nil, err
+ return Iterator{}, err
}
if k != List {
- return nil, ErrExpectedList
- }
- it := &listIterator{
- data: data[t : t+c],
+ return Iterator{}, ErrExpectedList
}
+ it := newIterator(data[t:t+c], int(t))
return it, nil
}
+func newIterator(data []byte, initialOffset int) Iterator {
+ return Iterator{data: data, offset: initialOffset}
+}
+
// Next forwards the iterator one step.
// Returns true if there is a next item or an error occurred on this step (check Err()).
// On parse error, the iterator is marked finished and subsequent calls return false.
-func (it *listIterator) Next() bool {
+func (it *Iterator) Next() bool {
if len(it.data) == 0 {
return false
}
@@ -52,17 +56,34 @@ func (it *listIterator) Next() bool {
it.data = nil
return true
}
- it.next = it.data[:t+c]
- it.data = it.data[t+c:]
+ length := t + c
+ it.next = it.data[:length]
+ it.data = it.data[length:]
+ it.offset += int(length)
it.err = nil
return true
}
-// Value returns the current value
-func (it *listIterator) Value() []byte {
+// Count returns the remaining number of items.
+// Note this is O(n) and the result may be incorrect if the list data is invalid.
+// The returned count is always an upper bound on the remaining items
+// that will be visited by the iterator.
+func (it *Iterator) Count() int {
+ count, _ := CountValues(it.data)
+ return count
+}
+
+// Value returns the current value.
+func (it *Iterator) Value() []byte {
return it.next
}
-func (it *listIterator) Err() error {
+// Offset returns the offset of the current value into the list data.
+func (it *Iterator) Offset() int {
+ return it.offset - len(it.next)
+}
+
+// Err returns the error that caused Next to return false, if any.
+func (it *Iterator) Err() error {
return it.err
}
diff --git a/rlp/iterator_test.go b/rlp/iterator_test.go
index a22aaec862..8ea26dad1b 100644
--- a/rlp/iterator_test.go
+++ b/rlp/iterator_test.go
@@ -38,10 +38,18 @@ func TestIterator(t *testing.T) {
t.Fatal("expected two elems, got zero")
}
txs := it.Value()
+ if offset := it.Offset(); offset != 3 {
+ t.Fatal("wrong offset", offset, "want 3")
+ }
+
// Check that uncles exist
if !it.Next() {
t.Fatal("expected two elems, got one")
}
+ if offset := it.Offset(); offset != 219 {
+ t.Fatal("wrong offset", offset, "want 219")
+ }
+
txit, err := NewListIterator(txs)
if err != nil {
t.Fatal(err)
diff --git a/rlp/raw.go b/rlp/raw.go
index f284da3f6d..96ad0427c8 100644
--- a/rlp/raw.go
+++ b/rlp/raw.go
@@ -17,8 +17,10 @@
package rlp
import (
+ "fmt"
"io"
"reflect"
+ "slices"
)
// RawValue represents an encoded RLP value and can be used to delay
@@ -28,6 +30,129 @@ type RawValue []byte
var rawValueType = reflect.TypeFor[RawValue]()
+// RawList represents an encoded RLP list.
+type RawList[T any] struct {
+ // The list is stored in encoded form.
+ // Note this buffer has some special properties:
+ //
+ // - if the buffer is nil, it's the zero value, representing
+ // an empty list.
+ // - if the buffer is non-nil, it must have a length of at least
+ // 9 bytes, which is reserved padding for the encoded list header.
+ // The remaining bytes, enc[9:], store the content bytes of the list.
+ //
+ // The implementation code mostly works with the Content method because it
+ // returns something valid either way.
+ enc []byte
+}
+
+// Content returns the RLP-encoded data of the list.
+// This does not include the list-header.
+// The return value is a direct reference to the internal buffer, not a copy.
+func (r *RawList[T]) Content() []byte {
+ if r.enc == nil {
+ return nil
+ } else {
+ return r.enc[9:]
+ }
+}
+
+// EncodeRLP writes the encoded list to the writer.
+func (r RawList[T]) EncodeRLP(w io.Writer) error {
+ _, err := w.Write(r.Bytes())
+ return err
+}
+
+// Bytes returns the RLP encoding of the list.
+// Note the return value aliases the internal buffer.
+func (r *RawList[T]) Bytes() []byte {
+ if r == nil || r.enc == nil {
+ return []byte{0xC0} // zero value encodes as empty list
+ }
+ n := puthead(r.enc, 0xC0, 0xF7, uint64(len(r.Content())))
+ copy(r.enc[9-n:], r.enc[:n])
+ return r.enc[9-n:]
+}
+
+// DecodeRLP decodes the list. This does not perform validation of the items!
+func (r *RawList[T]) DecodeRLP(s *Stream) error {
+ k, size, err := s.Kind()
+ if err != nil {
+ return err
+ }
+ if k != List {
+ return fmt.Errorf("%w for %T", ErrExpectedList, r)
+ }
+ enc := make([]byte, 9+size)
+ if err := s.readFull(enc[9:]); err != nil {
+ return err
+ }
+ *r = RawList[T]{enc: enc}
+ return nil
+}
+
+// Items decodes and returns all items in the list.
+func (r *RawList[T]) Items() ([]T, error) {
+ items := make([]T, r.Len())
+ it := r.ContentIterator()
+ for i := 0; it.Next(); i++ {
+ if err := DecodeBytes(it.Value(), &items[i]); err != nil {
+ return items[:i], err
+ }
+ }
+ return items, nil
+}
+
+// Len returns the number of items in the list.
+func (r *RawList[T]) Len() int {
+ len, _ := CountValues(r.Content())
+ return len
+}
+
+// Size returns the encoded size of the list.
+func (r *RawList[T]) Size() uint64 {
+ return ListSize(uint64(len(r.Content())))
+}
+
+// Empty returns true if the list contains no items.
+func (r *RawList[T]) Empty() bool {
+ return len(r.Content()) == 0
+}
+
+// ContentIterator returns an iterator over the content of the list.
+// Note the offsets returned by iterator.Offset are relative to the
+// Content bytes of the list.
+func (r *RawList[T]) ContentIterator() Iterator {
+ return newIterator(r.Content(), 0)
+}
+
+// Append adds an item to the end of the list.
+func (r *RawList[T]) Append(item T) error {
+ if r.enc == nil {
+ r.enc = make([]byte, 9)
+ }
+
+ eb := getEncBuffer()
+ defer encBufferPool.Put(eb)
+
+ if err := eb.encode(item); err != nil {
+ return err
+ }
+ prevEnd := len(r.enc)
+ end := prevEnd + eb.size()
+ r.enc = slices.Grow(r.enc, eb.size())[:end]
+ eb.copyTo(r.enc[prevEnd:end])
+ return nil
+}
+
+// AppendRaw adds an encoded item to the list.
+func (r *RawList[T]) AppendRaw(b []byte) {
+ if r.enc == nil {
+ r.enc = make([]byte, 9)
+ }
+ r.enc = append(r.enc, b...)
+}
+
// StringSize returns the encoded size of a string.
func StringSize(s string) uint64 {
switch n := len(s); n {
diff --git a/rlp/raw_test.go b/rlp/raw_test.go
index 2ed77b384c..9a4c68050c 100644
--- a/rlp/raw_test.go
+++ b/rlp/raw_test.go
@@ -19,11 +19,213 @@ package rlp
import (
"bytes"
"errors"
+ "fmt"
"io"
+ "reflect"
"testing"
"testing/quick"
)
+type rawListTest[T any] struct {
+ input string
+ content string
+ items []T
+ length int
+}
+
+func (test rawListTest[T]) name() string {
+ return fmt.Sprintf("%T-%d", *new(T), test.length)
+}
+
+func (test rawListTest[T]) run(t *testing.T) {
+ // check decoding and properties
+ input := unhex(test.input)
+ inputSize := len(input)
+ var rl RawList[T]
+ if err := DecodeBytes(input, &rl); err != nil {
+ t.Fatal("decode failed:", err)
+ }
+ if l := rl.Len(); l != test.length {
+ t.Fatalf("wrong Len %d, want %d", l, test.length)
+ }
+ if sz := rl.Size(); sz != uint64(inputSize) {
+ t.Fatalf("wrong Size %d, want %d", sz, inputSize)
+ }
+ items, err := rl.Items()
+ if err != nil {
+ t.Fatal("Items failed:", err)
+ }
+ if !reflect.DeepEqual(items, test.items) {
+ t.Fatal("wrong items:", items)
+ }
+ if !bytes.Equal(rl.Content(), unhex(test.content)) {
+ t.Fatalf("wrong Content %x, want %s", rl.Content(), test.content)
+ }
+ if !bytes.Equal(rl.Bytes(), unhex(test.input)) {
+ t.Fatalf("wrong Bytes %x, want %s", rl.Bytes(), test.input)
+ }
+
+ // check iterator
+ it := rl.ContentIterator()
+ i := 0
+ if count := it.Count(); count != test.length {
+ t.Fatalf("iterator has wrong Count %d, want %d", count, test.length)
+ }
+ for it.Next() {
+ var item T
+ if err := DecodeBytes(it.Value(), &item); err != nil {
+ t.Fatalf("item %d decode error: %v", i, err)
+ }
+ if !reflect.DeepEqual(item, items[i]) {
+ t.Fatalf("iterator has wrong item %v at %d", item, i)
+ }
+ i++
+ }
+ if i != test.length {
+ t.Fatalf("iterator produced %d values, want %d", i, test.length)
+ }
+ if it.Err() != nil {
+ t.Fatalf("iterator error: %v", it.Err())
+ }
+
+ // check encoding round trip
+ output, err := EncodeToBytes(&rl)
+ if err != nil {
+ t.Fatal("encode error:", err)
+ }
+ if !bytes.Equal(output, unhex(test.input)) {
+ t.Fatalf("encoding does not round trip: %x", output)
+ }
+
+ // check EncodeToRawList on items produces same bytes
+ encRL, err := EncodeToRawList(test.items)
+ if err != nil {
+ t.Fatal("EncodeToRawList error:", err)
+ }
+ encRLOutput, err := EncodeToBytes(&encRL)
+ if err != nil {
+ t.Fatal("EncodeToBytes of encoded list failed:", err)
+ }
+ if !bytes.Equal(encRLOutput, output) {
+ t.Fatalf("wrong encoding of EncodeToRawList result: %x", encRLOutput)
+ }
+}
+
+func TestRawList(t *testing.T) {
+ tests := []interface {
+ name() string
+ run(t *testing.T)
+ }{
+ rawListTest[uint64]{
+ input: "C0",
+ content: "",
+ items: []uint64{},
+ length: 0,
+ },
+ rawListTest[uint64]{
+ input: "C3010203",
+ content: "010203",
+ items: []uint64{1, 2, 3},
+ length: 3,
+ },
+ rawListTest[simplestruct]{
+ input: "C6C20102C20304",
+ content: "C20102C20304",
+ items: []simplestruct{{1, "\x02"}, {3, "\x04"}},
+ length: 2,
+ },
+ rawListTest[string]{
+ input: "F83C836161618362626283636363836464648365656583666666836767678368686883696969836A6A6A836B6B6B836C6C6C836D6D6D836E6E6E836F6F6F",
+ content: "836161618362626283636363836464648365656583666666836767678368686883696969836A6A6A836B6B6B836C6C6C836D6D6D836E6E6E836F6F6F",
+ items: []string{"aaa", "bbb", "ccc", "ddd", "eee", "fff", "ggg", "hhh", "iii", "jjj", "kkk", "lll", "mmm", "nnn", "ooo"},
+ length: 15,
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.name(), test.run)
+ }
+}
+
+func TestRawListEmpty(t *testing.T) {
+ // zero value list
+ var rl RawList[uint64]
+ b, _ := EncodeToBytes(&rl)
+ if !bytes.Equal(b, unhex("C0")) {
+ t.Fatalf("empty RawList has wrong encoding %x", b)
+ }
+ if !rl.Empty() {
+ t.Fatal("list should be Empty")
+ }
+ if rl.Len() != 0 {
+ t.Fatalf("empty list has Len %d", rl.Len())
+ }
+ if rl.Size() != 1 {
+ t.Fatalf("empty list has Size %d", rl.Size())
+ }
+ if len(rl.Content()) > 0 {
+ t.Fatalf("empty list has non-empty Content")
+ }
+ if !bytes.Equal(rl.Bytes(), []byte{0xC0}) {
+ t.Fatalf("empty list has wrong encoding")
+ }
+
+ // nil pointer
+ var nilptr *RawList[uint64]
+ b, _ = EncodeToBytes(nilptr)
+ if !bytes.Equal(b, unhex("C0")) {
+ t.Fatalf("nil pointer to RawList has wrong encoding %x", b)
+ }
+}
+
+// This checks that *RawList works in an 'optional' context.
+func TestRawListOptional(t *testing.T) {
+ type foo struct {
+ L *RawList[uint64] `rlp:"optional"`
+ }
+ // nil pointer encoding
+ var empty foo
+ b, _ := EncodeToBytes(empty)
+ if !bytes.Equal(b, unhex("C0")) {
+ t.Fatalf("nil pointer to RawList has wrong encoding %x", b)
+ }
+ // decoding
+ var dec foo
+ if err := DecodeBytes(unhex("C0"), &dec); err != nil {
+ t.Fatal(err)
+ }
+ if dec.L != nil {
+ t.Fatal("rawlist was decoded as non-nil")
+ }
+}
+
+func TestRawListAppend(t *testing.T) {
+ var rl RawList[simplestruct]
+
+ v1 := simplestruct{1, "one"}
+ v2 := simplestruct{2, "two"}
+ if err := rl.Append(v1); err != nil {
+ t.Fatal("append 1 failed:", err)
+ }
+ if err := rl.Append(v2); err != nil {
+ t.Fatal("append 2 failed:", err)
+ }
+
+ if rl.Len() != 2 {
+ t.Fatalf("wrong Len %d", rl.Len())
+ }
+ if rl.Size() != 13 {
+ t.Fatalf("wrong Size %d", rl.Size())
+ }
+ if !bytes.Equal(rl.Content(), unhex("C501836F6E65 C5028374776F")) {
+ t.Fatalf("wrong Content %x", rl.Content())
+ }
+ encoded, _ := EncodeToBytes(&rl)
+ if !bytes.Equal(encoded, unhex("CC C501836F6E65 C5028374776F")) {
+ t.Fatalf("wrong encoding %x", encoded)
+ }
+}
+
func TestCountValues(t *testing.T) {
tests := []struct {
input string // note: spaces in input are stripped by unhex
diff --git a/signer/core/apitypes/types.go b/signer/core/apitypes/types.go
index 81b7f8c80e..401f4fba07 100644
--- a/signer/core/apitypes/types.go
+++ b/signer/core/apitypes/types.go
@@ -242,8 +242,8 @@ func (args *SendTxArgs) validateTxSidecar() error {
if args.Proofs != nil {
if len(args.Proofs) == n {
// v1 transaction
- for i, b := range args.Blobs {
- if err := kzg4844.VerifyBlobProof(&b, args.Commitments[i], args.Proofs[i]); err != nil {
+ for i := range args.Blobs {
+ if err := kzg4844.VerifyBlobProof(&args.Blobs[i], args.Commitments[i], args.Proofs[i]); err != nil {
return fmt.Errorf("failed to verify blob proof: %v", err)
}
}
@@ -259,8 +259,8 @@ func (args *SendTxArgs) validateTxSidecar() error {
if args.Commitments == nil {
// Generate commitment and proof.
commitments := make([]kzg4844.Commitment, n)
- for i, b := range args.Blobs {
- c, err := kzg4844.BlobToCommitment(&b)
+ for i := range args.Blobs {
+ c, err := kzg4844.BlobToCommitment(&args.Blobs[i])
if err != nil {
return fmt.Errorf("blobs[%d]: error computing commitment: %v", i, err)
}
@@ -269,8 +269,8 @@ func (args *SendTxArgs) validateTxSidecar() error {
var proofs []kzg4844.Proof
if args.BlobVersion == types.BlobSidecarVersion1 {
proofs = make([]kzg4844.Proof, 0, n*kzg4844.CellProofsPerBlob)
- for i, b := range args.Blobs {
- p, err := kzg4844.ComputeCellProofs(&b)
+ for i := range args.Blobs {
+ p, err := kzg4844.ComputeCellProofs(&args.Blobs[i])
if err != nil {
return fmt.Errorf("blobs[%d]: error computing cell proof: %v", i, err)
}
@@ -278,8 +278,8 @@ func (args *SendTxArgs) validateTxSidecar() error {
}
} else {
proofs = make([]kzg4844.Proof, 0, n)
- for i, b := range args.Blobs {
- p, err := kzg4844.ComputeBlobProof(&b, commitments[i])
+ for i := range args.Blobs {
+ p, err := kzg4844.ComputeBlobProof(&args.Blobs[i], commitments[i])
if err != nil {
return fmt.Errorf("blobs[%d]: error computing proof: %v", i, err)
}
diff --git a/tests/state_test_util.go b/tests/state_test_util.go
index 3c1df35157..7525081f84 100644
--- a/tests/state_test_util.go
+++ b/tests/state_test_util.go
@@ -38,6 +38,7 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/crypto/keccak"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
@@ -45,7 +46,6 @@ import (
"github.com/ethereum/go-ethereum/triedb/hashdb"
"github.com/ethereum/go-ethereum/triedb/pathdb"
"github.com/holiman/uint256"
- "golang.org/x/crypto/sha3"
)
// StateTest checks transaction processing without block context.
@@ -496,7 +496,7 @@ func (tx *stTransaction) toMessage(ps stPostState, baseFee *big.Int) (*core.Mess
}
func rlpHash(x interface{}) (h common.Hash) {
- hw := sha3.NewLegacyKeccak256()
+ hw := keccak.NewLegacyKeccak256()
rlp.Encode(hw, x)
hw.Sum(h[:0])
return h
diff --git a/trie/bintrie/trie.go b/trie/bintrie/trie.go
index c082d57bdf..ecdd002331 100644
--- a/trie/bintrie/trie.go
+++ b/trie/bintrie/trie.go
@@ -236,7 +236,7 @@ func (t *BinaryTrie) GetAccount(addr common.Address) (*types.StateAccount, error
// not be modified by the caller. If a node was not found in the database, a
// trie.MissingNodeError is returned.
func (t *BinaryTrie) GetStorage(addr common.Address, key []byte) ([]byte, error) {
- return t.root.Get(GetBinaryTreeKey(addr, key), t.nodeResolver)
+ return t.root.Get(GetBinaryTreeKeyStorageSlot(addr, key), t.nodeResolver)
}
// UpdateAccount updates the account information for the given address.
@@ -302,7 +302,7 @@ func (t *BinaryTrie) DeleteAccount(addr common.Address) error {
// DeleteStorage removes any existing value for key from the trie. If a node was not
// found in the database, a trie.MissingNodeError is returned.
func (t *BinaryTrie) DeleteStorage(addr common.Address, key []byte) error {
- k := GetBinaryTreeKey(addr, key)
+ k := GetBinaryTreeKeyStorageSlot(addr, key)
var zero [HashSize]byte
root, err := t.root.Insert(k, zero[:], t.nodeResolver, 0)
if err != nil {
@@ -424,5 +424,5 @@ func (t *BinaryTrie) PrefetchStorage(addr common.Address, keys [][]byte) error {
// Witness returns a set containing all trie nodes that have been accessed.
func (t *BinaryTrie) Witness() map[string][]byte {
- panic("not implemented")
+ return t.tracer.Values()
}
diff --git a/trie/bintrie/trie_test.go b/trie/bintrie/trie_test.go
index ca02cfaa1f..256fd218e2 100644
--- a/trie/bintrie/trie_test.go
+++ b/trie/bintrie/trie_test.go
@@ -22,6 +22,9 @@ import (
"testing"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/trie"
+ "github.com/holiman/uint256"
)
var (
@@ -195,3 +198,97 @@ func TestMerkleizeMultipleEntries(t *testing.T) {
t.Fatalf("invalid root, expected=%x, got = %x", expected, got)
}
}
+
+// TestStorageRoundTrip verifies that GetStorage and DeleteStorage use the same
+// key mapping as UpdateStorage (GetBinaryTreeKeyStorageSlot). This is a regression
+// test: previously GetStorage and DeleteStorage used GetBinaryTreeKey directly,
+// which produced different tree keys and broke the read/delete path.
+func TestStorageRoundTrip(t *testing.T) {
+ tracer := trie.NewPrevalueTracer()
+ tr := &BinaryTrie{
+ root: NewBinaryNode(),
+ tracer: tracer,
+ }
+ addr := common.HexToAddress("0x1234567890abcdef1234567890abcdef12345678")
+
+ // Create an account first so the root becomes an InternalNode,
+ // which is the realistic state when storage operations happen.
+ acc := &types.StateAccount{
+ Nonce: 1,
+ Balance: uint256.NewInt(1000),
+ CodeHash: common.HexToHash("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470").Bytes(),
+ }
+ if err := tr.UpdateAccount(addr, acc, 0); err != nil {
+ t.Fatalf("UpdateAccount error: %v", err)
+ }
+
+ // Test main storage slots (key[31] >= 64 or key[:31] != 0).
+ // These produce a different stem than the account data, so after
+ // UpdateAccount + UpdateStorage the root is an InternalNode.
+ // Note: header slots (key[31] < 64, key[:31] == 0) share the same
+ // stem as account data and are covered by GetAccount/UpdateAccount path.
+ slots := []common.Hash{
+ common.HexToHash("00000000000000000000000000000000000000000000000000000000000000FF"), // main storage (slot 255)
+ common.HexToHash("0100000000000000000000000000000000000000000000000000000000000001"), // main storage (non-zero prefix)
+ }
+ val := common.TrimLeftZeroes(common.HexToHash("00000000000000000000000000000000000000000000000000000000deadbeef").Bytes())
+
+ for _, slot := range slots {
+ // Write
+ if err := tr.UpdateStorage(addr, slot[:], val); err != nil {
+ t.Fatalf("UpdateStorage(%x) error: %v", slot, err)
+ }
+ // Read back
+ got, err := tr.GetStorage(addr, slot[:])
+ if err != nil {
+ t.Fatalf("GetStorage(%x) error: %v", slot, err)
+ }
+ if len(got) == 0 {
+ t.Fatalf("GetStorage(%x) returned empty, expected value", slot)
+ }
+ // Verify value (right-justified in 32 bytes)
+ var expected [HashSize]byte
+ copy(expected[HashSize-len(val):], val)
+ if !bytes.Equal(got, expected[:]) {
+ t.Fatalf("GetStorage(%x) = %x, want %x", slot, got, expected)
+ }
+ // Delete
+ if err := tr.DeleteStorage(addr, slot[:]); err != nil {
+ t.Fatalf("DeleteStorage(%x) error: %v", slot, err)
+ }
+ // Verify deleted (should read as zero, not the old value)
+ got, err = tr.GetStorage(addr, slot[:])
+ if err != nil {
+ t.Fatalf("GetStorage(%x) after delete error: %v", slot, err)
+ }
+ if len(got) > 0 && !bytes.Equal(got, zero[:]) {
+ t.Fatalf("GetStorage(%x) after delete = %x, expected zero", slot, got)
+ }
+ }
+}
+
+func TestBinaryTrieWitness(t *testing.T) {
+ tracer := trie.NewPrevalueTracer()
+
+ tr := &BinaryTrie{
+ root: NewBinaryNode(),
+ tracer: tracer,
+ }
+ if w := tr.Witness(); len(w) != 0 {
+ t.Fatal("expected empty witness for fresh trie")
+ }
+
+ tracer.Put([]byte("path1"), []byte("blob1"))
+ tracer.Put([]byte("path2"), []byte("blob2"))
+
+ witness := tr.Witness()
+ if len(witness) != 2 {
+ t.Fatalf("expected 2 witness entries, got %d", len(witness))
+ }
+ if !bytes.Equal(witness[string([]byte("path1"))], []byte("blob1")) {
+ t.Fatal("unexpected witness value for path1")
+ }
+ if !bytes.Equal(witness[string([]byte("path2"))], []byte("blob2")) {
+ t.Fatal("unexpected witness value for path2")
+ }
+}
diff --git a/trie/node.go b/trie/node.go
index 3f14f07d63..bff2d0516a 100644
--- a/trie/node.go
+++ b/trie/node.go
@@ -225,7 +225,7 @@ func decodeRef(buf []byte) (node, []byte, error) {
case kind == rlp.List:
// 'embedded' node reference. The encoding must be smaller
// than a hash in order to be valid.
- if size := len(buf) - len(rest); size > hashLen {
+ if size := len(buf) - len(rest); size >= hashLen {
err := fmt.Errorf("oversized embedded node (size is %d bytes, want size < %d)", size, hashLen)
return nil, buf, err
}
diff --git a/trie/trie_test.go b/trie/trie_test.go
index b8b8edb33e..3423cde59c 100644
--- a/trie/trie_test.go
+++ b/trie/trie_test.go
@@ -35,12 +35,12 @@ import (
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/crypto/keccak"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/internal/testrand"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie/trienode"
"github.com/holiman/uint256"
- "golang.org/x/crypto/sha3"
)
func init() {
@@ -968,7 +968,7 @@ func TestCommitSequenceStackTrie(t *testing.T) {
prng := rand.New(rand.NewSource(int64(count)))
// This spongeDb is used to check the sequence of disk-db-writes
s := &spongeDb{
- sponge: sha3.NewLegacyKeccak256(),
+ sponge: keccak.NewLegacyKeccak256(),
id: "a",
values: make(map[string]string),
}
@@ -977,7 +977,7 @@ func TestCommitSequenceStackTrie(t *testing.T) {
// Another sponge is used for the stacktrie commits
stackTrieSponge := &spongeDb{
- sponge: sha3.NewLegacyKeccak256(),
+ sponge: keccak.NewLegacyKeccak256(),
id: "b",
values: make(map[string]string),
}
@@ -1040,7 +1040,7 @@ func TestCommitSequenceStackTrie(t *testing.T) {
// not fit into 32 bytes, rlp-encoded. However, it's still the correct thing to do.
func TestCommitSequenceSmallRoot(t *testing.T) {
s := &spongeDb{
- sponge: sha3.NewLegacyKeccak256(),
+ sponge: keccak.NewLegacyKeccak256(),
id: "a",
values: make(map[string]string),
}
@@ -1049,7 +1049,7 @@ func TestCommitSequenceSmallRoot(t *testing.T) {
// Another sponge is used for the stacktrie commits
stackTrieSponge := &spongeDb{
- sponge: sha3.NewLegacyKeccak256(),
+ sponge: keccak.NewLegacyKeccak256(),
id: "b",
values: make(map[string]string),
}
diff --git a/triedb/pathdb/database.go b/triedb/pathdb/database.go
index f7c0ba1398..5255602a4e 100644
--- a/triedb/pathdb/database.go
+++ b/triedb/pathdb/database.go
@@ -45,7 +45,7 @@ type layer interface {
// Note:
// - the returned node is not a copy, please don't modify it.
// - no error will be returned if the requested node is not found in database.
- node(owner common.Hash, path []byte, depth int) ([]byte, common.Hash, *nodeLoc, error)
+ node(owner common.Hash, path []byte, depth int) ([]byte, common.Hash, nodeLoc, error)
// account directly retrieves the account RLP associated with a particular
// hash in the slim data format. An error will be returned if the read
diff --git a/triedb/pathdb/difflayer.go b/triedb/pathdb/difflayer.go
index ae523c979c..8ca3a39cf3 100644
--- a/triedb/pathdb/difflayer.go
+++ b/triedb/pathdb/difflayer.go
@@ -79,7 +79,7 @@ func (dl *diffLayer) parentLayer() layer {
// node implements the layer interface, retrieving the trie node blob with the
// provided node information. No error will be returned if the node is not found.
-func (dl *diffLayer) node(owner common.Hash, path []byte, depth int) ([]byte, common.Hash, *nodeLoc, error) {
+func (dl *diffLayer) node(owner common.Hash, path []byte, depth int) ([]byte, common.Hash, nodeLoc, error) {
// Hold the lock, ensure the parent won't be changed during the
// state accessing.
dl.lock.RLock()
@@ -91,7 +91,7 @@ func (dl *diffLayer) node(owner common.Hash, path []byte, depth int) ([]byte, co
dirtyNodeHitMeter.Mark(1)
dirtyNodeHitDepthHist.Update(int64(depth))
dirtyNodeReadMeter.Mark(int64(len(n.Blob)))
- return n.Blob, n.Hash, &nodeLoc{loc: locDiffLayer, depth: depth}, nil
+ return n.Blob, n.Hash, nodeLoc{loc: locDiffLayer, depth: depth}, nil
}
// Trie node unknown to this layer, resolve from parent
return dl.parent.node(owner, path, depth+1)
diff --git a/triedb/pathdb/disklayer.go b/triedb/pathdb/disklayer.go
index 911959dfa9..5bad19b4f5 100644
--- a/triedb/pathdb/disklayer.go
+++ b/triedb/pathdb/disklayer.go
@@ -112,12 +112,12 @@ func (dl *diskLayer) markStale() {
// node implements the layer interface, retrieving the trie node with the
// provided node info. No error will be returned if the node is not found.
-func (dl *diskLayer) node(owner common.Hash, path []byte, depth int) ([]byte, common.Hash, *nodeLoc, error) {
+func (dl *diskLayer) node(owner common.Hash, path []byte, depth int) ([]byte, common.Hash, nodeLoc, error) {
dl.lock.RLock()
defer dl.lock.RUnlock()
if dl.stale {
- return nil, common.Hash{}, nil, errSnapshotStale
+ return nil, common.Hash{}, nodeLoc{}, errSnapshotStale
}
// Try to retrieve the trie node from the not-yet-written node buffer first
// (both the live one and the frozen one). Note the buffer is lock free since
@@ -129,7 +129,7 @@ func (dl *diskLayer) node(owner common.Hash, path []byte, depth int) ([]byte, co
dirtyNodeHitMeter.Mark(1)
dirtyNodeReadMeter.Mark(int64(len(n.Blob)))
dirtyNodeHitDepthHist.Update(int64(depth))
- return n.Blob, n.Hash, &nodeLoc{loc: locDirtyCache, depth: depth}, nil
+ return n.Blob, n.Hash, nodeLoc{loc: locDirtyCache, depth: depth}, nil
}
}
}
@@ -141,7 +141,7 @@ func (dl *diskLayer) node(owner common.Hash, path []byte, depth int) ([]byte, co
if blob := dl.nodes.Get(nil, key); len(blob) > 0 {
cleanNodeHitMeter.Mark(1)
cleanNodeReadMeter.Mark(int64(len(blob)))
- return blob, crypto.Keccak256Hash(blob), &nodeLoc{loc: locCleanCache, depth: depth}, nil
+ return blob, crypto.Keccak256Hash(blob), nodeLoc{loc: locCleanCache, depth: depth}, nil
}
cleanNodeMissMeter.Mark(1)
}
@@ -161,7 +161,7 @@ func (dl *diskLayer) node(owner common.Hash, path []byte, depth int) ([]byte, co
dl.nodes.Set(key, blob)
cleanNodeWriteMeter.Mark(int64(len(blob)))
}
- return blob, crypto.Keccak256Hash(blob), &nodeLoc{loc: locDiskLayer, depth: depth}, nil
+ return blob, crypto.Keccak256Hash(blob), nodeLoc{loc: locDiskLayer, depth: depth}, nil
}
// account directly retrieves the account RLP associated with a particular
diff --git a/triedb/pathdb/reader.go b/triedb/pathdb/reader.go
index f55e015ee6..aaa64e902c 100644
--- a/triedb/pathdb/reader.go
+++ b/triedb/pathdb/reader.go
@@ -47,7 +47,7 @@ type nodeLoc struct {
}
// string returns the string representation of node location.
-func (loc *nodeLoc) string() string {
+func (loc nodeLoc) string() string {
return fmt.Sprintf("loc: %s, depth: %d", loc.loc, loc.depth)
}