mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
Delete internal directory
Signed-off-by: Isabel Schöps Thiel @IsabelSchoepd <155141998+IST-Github@users.noreply.github.com>
This commit is contained in:
parent
f974fde3fe
commit
35587a352c
91 changed files with 0 additions and 25380 deletions
|
|
@ -1,59 +0,0 @@
|
|||
// Copyright 2023 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Package utesting provides a standalone replacement for package testing.
|
||||
//
|
||||
// This package exists because package testing cannot easily be embedded into a
|
||||
// standalone go program. It provides an API that mirrors the standard library
|
||||
// testing API.
|
||||
|
||||
package blocktest
|
||||
|
||||
import (
|
||||
"hash"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
// testHasher is the helper tool for transaction/receipt list hashing.
|
||||
// The original hasher is trie, in order to get rid of import cycle,
|
||||
// use the testing hasher instead.
|
||||
type testHasher struct {
|
||||
hasher hash.Hash
|
||||
}
|
||||
|
||||
// NewHasher returns a new testHasher instance.
|
||||
func NewHasher() *testHasher {
|
||||
return &testHasher{hasher: sha3.NewLegacyKeccak256()}
|
||||
}
|
||||
|
||||
// Reset resets the hash state.
|
||||
func (h *testHasher) Reset() {
|
||||
h.hasher.Reset()
|
||||
}
|
||||
|
||||
// Update updates the hash state with the given key and value.
|
||||
func (h *testHasher) Update(key, val []byte) error {
|
||||
h.hasher.Write(key)
|
||||
h.hasher.Write(val)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Hash returns the hash value.
|
||||
func (h *testHasher) Hash() common.Hash {
|
||||
return common.BytesToHash(h.hasher.Sum(nil))
|
||||
}
|
||||
|
|
@ -1,291 +0,0 @@
|
|||
// Copyright 2016 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package build
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"archive/zip"
|
||||
"compress/gzip"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Archive interface {
|
||||
// Directory adds a new directory entry to the archive and sets the
|
||||
// directory for subsequent calls to Header.
|
||||
Directory(name string) error
|
||||
|
||||
// Header adds a new file to the archive. The file is added to the directory
|
||||
// set by Directory. The content of the file must be written to the returned
|
||||
// writer.
|
||||
Header(os.FileInfo) (io.Writer, error)
|
||||
|
||||
// Close flushes the archive and closes the underlying file.
|
||||
Close() error
|
||||
}
|
||||
|
||||
func NewArchive(file *os.File) (Archive, string) {
|
||||
switch {
|
||||
case strings.HasSuffix(file.Name(), ".zip"):
|
||||
return NewZipArchive(file), strings.TrimSuffix(file.Name(), ".zip")
|
||||
case strings.HasSuffix(file.Name(), ".tar.gz"):
|
||||
return NewTarballArchive(file), strings.TrimSuffix(file.Name(), ".tar.gz")
|
||||
default:
|
||||
return nil, ""
|
||||
}
|
||||
}
|
||||
|
||||
// AddFile appends an existing file to an archive.
|
||||
func AddFile(a Archive, file string) error {
|
||||
fd, err := os.Open(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fd.Close()
|
||||
fi, err := fd.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w, err := a.Header(fi)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := io.Copy(w, fd); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteArchive creates an archive containing the given files.
|
||||
func WriteArchive(name string, files []string) (err error) {
|
||||
archfd, err := os.Create(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
archfd.Close()
|
||||
// Remove the half-written archive on failure.
|
||||
if err != nil {
|
||||
os.Remove(name)
|
||||
}
|
||||
}()
|
||||
archive, basename := NewArchive(archfd)
|
||||
if archive == nil {
|
||||
return errors.New("unknown archive extension")
|
||||
}
|
||||
fmt.Println(name)
|
||||
if err := archive.Directory(basename); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, file := range files {
|
||||
fmt.Println(" +", filepath.Base(file))
|
||||
if err := AddFile(archive, file); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return archive.Close()
|
||||
}
|
||||
|
||||
type ZipArchive struct {
|
||||
dir string
|
||||
zipw *zip.Writer
|
||||
file io.Closer
|
||||
}
|
||||
|
||||
func NewZipArchive(w io.WriteCloser) Archive {
|
||||
return &ZipArchive{"", zip.NewWriter(w), w}
|
||||
}
|
||||
|
||||
func (a *ZipArchive) Directory(name string) error {
|
||||
a.dir = name + "/"
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *ZipArchive) Header(fi os.FileInfo) (io.Writer, error) {
|
||||
head, err := zip.FileInfoHeader(fi)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't make zip header: %v", err)
|
||||
}
|
||||
head.Name = a.dir + head.Name
|
||||
head.Method = zip.Deflate
|
||||
w, err := a.zipw.CreateHeader(head)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't add zip header: %v", err)
|
||||
}
|
||||
return w, nil
|
||||
}
|
||||
|
||||
func (a *ZipArchive) Close() error {
|
||||
if err := a.zipw.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
return a.file.Close()
|
||||
}
|
||||
|
||||
type TarballArchive struct {
|
||||
dir string
|
||||
tarw *tar.Writer
|
||||
gzw *gzip.Writer
|
||||
file io.Closer
|
||||
}
|
||||
|
||||
func NewTarballArchive(w io.WriteCloser) Archive {
|
||||
gzw := gzip.NewWriter(w)
|
||||
tarw := tar.NewWriter(gzw)
|
||||
return &TarballArchive{"", tarw, gzw, w}
|
||||
}
|
||||
|
||||
func (a *TarballArchive) Directory(name string) error {
|
||||
a.dir = name + "/"
|
||||
return a.tarw.WriteHeader(&tar.Header{
|
||||
Name: a.dir,
|
||||
Mode: 0755,
|
||||
Typeflag: tar.TypeDir,
|
||||
ModTime: time.Now(),
|
||||
})
|
||||
}
|
||||
|
||||
func (a *TarballArchive) Header(fi os.FileInfo) (io.Writer, error) {
|
||||
head, err := tar.FileInfoHeader(fi, "")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't make tar header: %v", err)
|
||||
}
|
||||
head.Name = a.dir + head.Name
|
||||
if err := a.tarw.WriteHeader(head); err != nil {
|
||||
return nil, fmt.Errorf("can't add tar header: %v", err)
|
||||
}
|
||||
return a.tarw, nil
|
||||
}
|
||||
|
||||
func (a *TarballArchive) Close() error {
|
||||
if err := a.tarw.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := a.gzw.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
return a.file.Close()
|
||||
}
|
||||
|
||||
// ExtractArchive unpacks a .zip or .tar.gz archive to the destination directory.
|
||||
func ExtractArchive(archive string, dest string) error {
|
||||
ar, err := os.Open(archive)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer ar.Close()
|
||||
|
||||
switch {
|
||||
case strings.HasSuffix(archive, ".tar.gz"):
|
||||
return extractTarball(ar, dest)
|
||||
case strings.HasSuffix(archive, ".zip"):
|
||||
return extractZip(ar, dest)
|
||||
default:
|
||||
return fmt.Errorf("unhandled archive type %s", archive)
|
||||
}
|
||||
}
|
||||
|
||||
// extractTarball unpacks a .tar.gz file.
|
||||
func extractTarball(ar io.Reader, dest string) error {
|
||||
gzr, err := gzip.NewReader(ar)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer gzr.Close()
|
||||
|
||||
tr := tar.NewReader(gzr)
|
||||
for {
|
||||
// Move to the next file header.
|
||||
header, err := tr.Next()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
// We only care about regular files, directory modes
|
||||
// and special file types are not supported.
|
||||
if header.Typeflag == tar.TypeReg {
|
||||
armode := header.FileInfo().Mode()
|
||||
err := extractFile(header.Name, armode, tr, dest)
|
||||
if err != nil {
|
||||
return fmt.Errorf("extract %s: %v", header.Name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// extractZip unpacks the given .zip file.
|
||||
func extractZip(ar *os.File, dest string) error {
|
||||
info, err := ar.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
zr, err := zip.NewReader(ar, info.Size())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, zf := range zr.File {
|
||||
if !zf.Mode().IsRegular() {
|
||||
continue
|
||||
}
|
||||
|
||||
data, err := zf.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = extractFile(zf.Name, zf.Mode(), data, dest)
|
||||
data.Close()
|
||||
if err != nil {
|
||||
return fmt.Errorf("extract %s: %v", zf.Name, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// extractFile extracts a single file from an archive.
|
||||
func extractFile(arpath string, armode os.FileMode, data io.Reader, dest string) error {
|
||||
// Check that path is inside destination directory.
|
||||
target := filepath.Join(dest, filepath.FromSlash(arpath))
|
||||
if !strings.HasPrefix(target, filepath.Clean(dest)+string(os.PathSeparator)) {
|
||||
return fmt.Errorf("path %q escapes archive destination", target)
|
||||
}
|
||||
|
||||
// Ensure the destination directory exists.
|
||||
if err := os.MkdirAll(filepath.Dir(target), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Copy file data.
|
||||
file, err := os.OpenFile(target, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, armode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := io.Copy(file, data); err != nil {
|
||||
file.Close()
|
||||
os.Remove(target)
|
||||
return err
|
||||
}
|
||||
return file.Close()
|
||||
}
|
||||
|
|
@ -1,120 +0,0 @@
|
|||
// Copyright 2016 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package build
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/container"
|
||||
)
|
||||
|
||||
// AzureBlobstoreConfig is an authentication and configuration struct containing
|
||||
// the data needed by the Azure SDK to interact with a specific container in the
|
||||
// blobstore.
|
||||
type AzureBlobstoreConfig struct {
|
||||
Account string // Account name to authorize API requests with
|
||||
Token string // Access token for the above account
|
||||
Container string // Blob container to upload files into
|
||||
}
|
||||
|
||||
// AzureBlobstoreUpload uploads a local file to the Azure Blob Storage. Note, this
|
||||
// method assumes a max file size of 64MB (Azure limitation). Larger files will
|
||||
// need a multi API call approach implemented.
|
||||
//
|
||||
// See: https://msdn.microsoft.com/en-us/library/azure/dd179451.aspx#Anchor_3
|
||||
func AzureBlobstoreUpload(path string, name string, config AzureBlobstoreConfig) error {
|
||||
if *DryRunFlag {
|
||||
fmt.Printf("would upload %q to %s/%s/%s\n", path, config.Account, config.Container, name)
|
||||
return nil
|
||||
}
|
||||
// Create an authenticated client against the Azure cloud
|
||||
credential, err := azblob.NewSharedKeyCredential(config.Account, config.Token)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a := fmt.Sprintf("https://%s.blob.core.windows.net/", config.Account)
|
||||
client, err := azblob.NewClientWithSharedKeyCredential(a, credential, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Stream the file to upload into the designated blobstore container
|
||||
in, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer in.Close()
|
||||
|
||||
_, err = client.UploadFile(context.Background(), config.Container, name, in, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
// AzureBlobstoreList lists all the files contained within an azure blobstore.
|
||||
func AzureBlobstoreList(config AzureBlobstoreConfig) ([]*container.BlobItem, error) {
|
||||
// Create an authenticated client against the Azure cloud
|
||||
credential, err := azblob.NewSharedKeyCredential(config.Account, config.Token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
a := fmt.Sprintf("https://%s.blob.core.windows.net/", config.Account)
|
||||
client, err := azblob.NewClientWithSharedKeyCredential(a, credential, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pager := client.NewListBlobsFlatPager(config.Container, nil)
|
||||
|
||||
var blobs []*container.BlobItem
|
||||
for pager.More() {
|
||||
page, err := pager.NextPage(context.TODO())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
blobs = append(blobs, page.Segment.BlobItems...)
|
||||
}
|
||||
return blobs, nil
|
||||
}
|
||||
|
||||
// AzureBlobstoreDelete iterates over a list of files to delete and removes them
|
||||
// from the blobstore.
|
||||
func AzureBlobstoreDelete(config AzureBlobstoreConfig, blobs []*container.BlobItem) error {
|
||||
if *DryRunFlag {
|
||||
for _, blob := range blobs {
|
||||
fmt.Printf("would delete %s (%s) from %s/%s\n", *blob.Name, blob.Properties.LastModified, config.Account, config.Container)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// Create an authenticated client against the Azure cloud
|
||||
credential, err := azblob.NewSharedKeyCredential(config.Account, config.Token)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a := fmt.Sprintf("https://%s.blob.core.windows.net/", config.Account)
|
||||
client, err := azblob.NewClientWithSharedKeyCredential(a, credential, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Iterate over the blobs and delete them
|
||||
for _, blob := range blobs {
|
||||
if _, err := client.DeleteBlob(context.Background(), config.Container, *blob.Name, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("deleted %s (%s)\n", *blob.Name, blob.Properties.LastModified)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1,150 +0,0 @@
|
|||
// Copyright 2019 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package build
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ChecksumDB keeps file checksums.
|
||||
type ChecksumDB struct {
|
||||
allChecksums []string
|
||||
}
|
||||
|
||||
// MustLoadChecksums loads a file containing checksums.
|
||||
func MustLoadChecksums(file string) *ChecksumDB {
|
||||
content, err := os.ReadFile(file)
|
||||
if err != nil {
|
||||
log.Fatal("can't load checksum file: " + err.Error())
|
||||
}
|
||||
return &ChecksumDB{strings.Split(string(content), "\n")}
|
||||
}
|
||||
|
||||
// Verify checks whether the given file is valid according to the checksum database.
|
||||
func (db *ChecksumDB) Verify(path string) error {
|
||||
fd, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fd.Close()
|
||||
|
||||
h := sha256.New()
|
||||
if _, err := io.Copy(h, bufio.NewReader(fd)); err != nil {
|
||||
return err
|
||||
}
|
||||
fileHash := hex.EncodeToString(h.Sum(nil))
|
||||
if !db.findHash(filepath.Base(path), fileHash) {
|
||||
return fmt.Errorf("invalid file hash %s for %s", fileHash, filepath.Base(path))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *ChecksumDB) findHash(basename, hash string) bool {
|
||||
want := hash + " " + basename
|
||||
for _, line := range db.allChecksums {
|
||||
if strings.TrimSpace(line) == want {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// DownloadFile downloads a file and verifies its checksum.
|
||||
func (db *ChecksumDB) DownloadFile(url, dstPath string) error {
|
||||
if err := db.Verify(dstPath); err == nil {
|
||||
fmt.Printf("%s is up-to-date\n", dstPath)
|
||||
return nil
|
||||
}
|
||||
fmt.Printf("%s is stale\n", dstPath)
|
||||
fmt.Printf("downloading from %s\n", url)
|
||||
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return fmt.Errorf("download error: %v", err)
|
||||
} else if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("download error: status %d", resp.StatusCode)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if err := os.MkdirAll(filepath.Dir(dstPath), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
fd, err := os.OpenFile(dstPath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dst := newDownloadWriter(fd, resp.ContentLength)
|
||||
_, err = io.Copy(dst, resp.Body)
|
||||
dst.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return db.Verify(dstPath)
|
||||
}
|
||||
|
||||
type downloadWriter struct {
|
||||
file *os.File
|
||||
dstBuf *bufio.Writer
|
||||
size int64
|
||||
written int64
|
||||
lastpct int64
|
||||
}
|
||||
|
||||
func newDownloadWriter(dst *os.File, size int64) *downloadWriter {
|
||||
return &downloadWriter{
|
||||
file: dst,
|
||||
dstBuf: bufio.NewWriter(dst),
|
||||
size: size,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *downloadWriter) Write(buf []byte) (int, error) {
|
||||
n, err := w.dstBuf.Write(buf)
|
||||
|
||||
// Report progress.
|
||||
w.written += int64(n)
|
||||
pct := w.written * 10 / w.size * 10
|
||||
if pct != w.lastpct {
|
||||
if w.lastpct != 0 {
|
||||
fmt.Print("...")
|
||||
}
|
||||
fmt.Print(pct, "%")
|
||||
w.lastpct = pct
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (w *downloadWriter) Close() error {
|
||||
if w.lastpct > 0 {
|
||||
fmt.Println() // Finish the progress line.
|
||||
}
|
||||
flushErr := w.dstBuf.Flush()
|
||||
closeErr := w.file.Close()
|
||||
if flushErr != nil {
|
||||
return flushErr
|
||||
}
|
||||
return closeErr
|
||||
}
|
||||
|
|
@ -1,177 +0,0 @@
|
|||
// Copyright 2016 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package build
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
// These flags override values in build env.
|
||||
GitCommitFlag = flag.String("git-commit", "", `Overrides git commit hash embedded into executables`)
|
||||
GitBranchFlag = flag.String("git-branch", "", `Overrides git branch being built`)
|
||||
GitTagFlag = flag.String("git-tag", "", `Overrides git tag being built`)
|
||||
BuildnumFlag = flag.String("buildnum", "", `Overrides CI build number`)
|
||||
PullRequestFlag = flag.Bool("pull-request", false, `Overrides pull request status of the build`)
|
||||
CronJobFlag = flag.Bool("cron-job", false, `Overrides cron job status of the build`)
|
||||
UbuntuVersionFlag = flag.String("ubuntu", "", `Sets the ubuntu version being built for`)
|
||||
)
|
||||
|
||||
// Environment contains metadata provided by the build environment.
|
||||
type Environment struct {
|
||||
CI bool
|
||||
Name string // name of the environment
|
||||
Repo string // name of GitHub repo
|
||||
Commit, Date, Branch, Tag string // Git info
|
||||
Buildnum string
|
||||
UbuntuVersion string // Ubuntu version being built for
|
||||
IsPullRequest bool
|
||||
IsCronJob bool
|
||||
}
|
||||
|
||||
func (env Environment) String() string {
|
||||
return fmt.Sprintf("%s env (commit:%s date:%s branch:%s tag:%s buildnum:%s pr:%t)",
|
||||
env.Name, env.Commit, env.Date, env.Branch, env.Tag, env.Buildnum, env.IsPullRequest)
|
||||
}
|
||||
|
||||
// Env returns metadata about the current CI environment, falling back to LocalEnv
|
||||
// if not running on CI.
|
||||
func Env() Environment {
|
||||
switch {
|
||||
case os.Getenv("CI") == "true" && os.Getenv("TRAVIS") == "true":
|
||||
commit := os.Getenv("TRAVIS_PULL_REQUEST_SHA")
|
||||
if commit == "" {
|
||||
commit = os.Getenv("TRAVIS_COMMIT")
|
||||
}
|
||||
return Environment{
|
||||
CI: true,
|
||||
Name: "travis",
|
||||
Repo: os.Getenv("TRAVIS_REPO_SLUG"),
|
||||
Commit: commit,
|
||||
Date: getDate(commit),
|
||||
Branch: os.Getenv("TRAVIS_BRANCH"),
|
||||
Tag: os.Getenv("TRAVIS_TAG"),
|
||||
Buildnum: os.Getenv("TRAVIS_BUILD_NUMBER"),
|
||||
IsPullRequest: os.Getenv("TRAVIS_PULL_REQUEST") != "false",
|
||||
IsCronJob: os.Getenv("TRAVIS_EVENT_TYPE") == "cron",
|
||||
}
|
||||
case os.Getenv("CI") == "True" && os.Getenv("APPVEYOR") == "True":
|
||||
commit := os.Getenv("APPVEYOR_PULL_REQUEST_HEAD_COMMIT")
|
||||
if commit == "" {
|
||||
commit = os.Getenv("APPVEYOR_REPO_COMMIT")
|
||||
}
|
||||
return Environment{
|
||||
CI: true,
|
||||
Name: "appveyor",
|
||||
Repo: os.Getenv("APPVEYOR_REPO_NAME"),
|
||||
Commit: commit,
|
||||
Date: getDate(commit),
|
||||
Branch: os.Getenv("APPVEYOR_REPO_BRANCH"),
|
||||
Tag: os.Getenv("APPVEYOR_REPO_TAG_NAME"),
|
||||
Buildnum: os.Getenv("APPVEYOR_BUILD_NUMBER"),
|
||||
IsPullRequest: os.Getenv("APPVEYOR_PULL_REQUEST_NUMBER") != "",
|
||||
IsCronJob: os.Getenv("APPVEYOR_SCHEDULED_BUILD") == "True",
|
||||
}
|
||||
default:
|
||||
return LocalEnv()
|
||||
}
|
||||
}
|
||||
|
||||
// LocalEnv returns build environment metadata gathered from git.
|
||||
func LocalEnv() Environment {
|
||||
env := applyEnvFlags(Environment{Name: "local", Repo: "ethereum/go-ethereum"})
|
||||
|
||||
head := readGitFile("HEAD")
|
||||
if fields := strings.Fields(head); len(fields) == 2 {
|
||||
head = fields[1]
|
||||
} else {
|
||||
// In this case we are in "detached head" state
|
||||
// see: https://git-scm.com/docs/git-checkout#_detached_head
|
||||
// Additional check required to verify, that file contains commit hash
|
||||
commitRe, _ := regexp.Compile("^([0-9a-f]{40})$")
|
||||
if commit := commitRe.FindString(head); commit != "" && env.Commit == "" {
|
||||
env.Commit = commit
|
||||
}
|
||||
return env
|
||||
}
|
||||
if env.Commit == "" {
|
||||
env.Commit = readGitFile(head)
|
||||
}
|
||||
env.Date = getDate(env.Commit)
|
||||
if env.Branch == "" {
|
||||
if head != "HEAD" {
|
||||
env.Branch = strings.TrimPrefix(head, "refs/heads/")
|
||||
}
|
||||
}
|
||||
if info, err := os.Stat(".git/objects"); err == nil && info.IsDir() && env.Tag == "" {
|
||||
env.Tag = firstLine(RunGit("tag", "-l", "--points-at", "HEAD"))
|
||||
}
|
||||
return env
|
||||
}
|
||||
|
||||
func firstLine(s string) string {
|
||||
return strings.Split(s, "\n")[0]
|
||||
}
|
||||
|
||||
func getDate(commit string) string {
|
||||
if commit == "" {
|
||||
return ""
|
||||
}
|
||||
out := RunGit("show", "-s", "--format=%ct", commit)
|
||||
if out == "" {
|
||||
return ""
|
||||
}
|
||||
date, err := strconv.ParseInt(strings.TrimSpace(out), 10, 64)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to parse git commit date: %v", err))
|
||||
}
|
||||
return time.Unix(date, 0).Format("20060102")
|
||||
}
|
||||
|
||||
func applyEnvFlags(env Environment) Environment {
|
||||
if !flag.Parsed() {
|
||||
panic("you need to call flag.Parse before Env or LocalEnv")
|
||||
}
|
||||
if *GitCommitFlag != "" {
|
||||
env.Commit = *GitCommitFlag
|
||||
}
|
||||
if *GitBranchFlag != "" {
|
||||
env.Branch = *GitBranchFlag
|
||||
}
|
||||
if *GitTagFlag != "" {
|
||||
env.Tag = *GitTagFlag
|
||||
}
|
||||
if *BuildnumFlag != "" {
|
||||
env.Buildnum = *BuildnumFlag
|
||||
}
|
||||
if *PullRequestFlag {
|
||||
env.IsPullRequest = true
|
||||
}
|
||||
if *CronJobFlag {
|
||||
env.IsCronJob = true
|
||||
}
|
||||
if *UbuntuVersionFlag != "" {
|
||||
env.UbuntuVersion = *UbuntuVersionFlag
|
||||
}
|
||||
return env
|
||||
}
|
||||
|
|
@ -1,180 +0,0 @@
|
|||
// Copyright 2021 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package build
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type GoToolchain struct {
|
||||
Root string // GOROOT
|
||||
|
||||
// Cross-compilation variables. These are set when running the go tool.
|
||||
GOARCH string
|
||||
GOOS string
|
||||
CC string
|
||||
}
|
||||
|
||||
// Go creates an invocation of the go command.
|
||||
func (g *GoToolchain) Go(command string, args ...string) *exec.Cmd {
|
||||
tool := g.goTool(command, args...)
|
||||
|
||||
// Configure environment for cross build.
|
||||
if g.GOARCH != "" && g.GOARCH != runtime.GOARCH {
|
||||
tool.Env = append(tool.Env, "CGO_ENABLED=1")
|
||||
tool.Env = append(tool.Env, "GOARCH="+g.GOARCH)
|
||||
}
|
||||
if g.GOOS != "" && g.GOOS != runtime.GOOS {
|
||||
tool.Env = append(tool.Env, "GOOS="+g.GOOS)
|
||||
}
|
||||
// Configure C compiler.
|
||||
if g.CC != "" {
|
||||
tool.Env = append(tool.Env, "CC="+g.CC)
|
||||
} else if os.Getenv("CC") != "" {
|
||||
tool.Env = append(tool.Env, "CC="+os.Getenv("CC"))
|
||||
}
|
||||
// CKZG by default is not portable, append the necessary build flags to make
|
||||
// it not rely on modern CPU instructions and enable linking against.
|
||||
tool.Env = append(tool.Env, "CGO_CFLAGS=-O2 -g -D__BLST_PORTABLE__")
|
||||
|
||||
return tool
|
||||
}
|
||||
|
||||
func (g *GoToolchain) goTool(command string, args ...string) *exec.Cmd {
|
||||
if g.Root == "" {
|
||||
g.Root = runtime.GOROOT()
|
||||
}
|
||||
tool := exec.Command(filepath.Join(g.Root, "bin", "go"), command) // nolint: gosec
|
||||
tool.Args = append(tool.Args, args...)
|
||||
tool.Env = append(tool.Env, "GOROOT="+g.Root)
|
||||
|
||||
// Forward environment variables to the tool, but skip compiler target settings.
|
||||
// TODO: what about GOARM?
|
||||
skip := map[string]struct{}{"GOROOT": {}, "GOARCH": {}, "GOOS": {}, "GOBIN": {}, "CC": {}}
|
||||
for _, e := range os.Environ() {
|
||||
if i := strings.IndexByte(e, '='); i >= 0 {
|
||||
if _, ok := skip[e[:i]]; ok {
|
||||
continue
|
||||
}
|
||||
}
|
||||
tool.Env = append(tool.Env, e)
|
||||
}
|
||||
return tool
|
||||
}
|
||||
|
||||
// DownloadGo downloads the Go binary distribution and unpacks it into a temporary
|
||||
// directory. It returns the GOROOT of the unpacked toolchain.
|
||||
func DownloadGo(csdb *ChecksumDB) string {
|
||||
version, err := Version(csdb, "golang")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// Shortcut: if the Go version that runs this script matches the
|
||||
// requested version exactly, there is no need to download anything.
|
||||
activeGo := strings.TrimPrefix(runtime.Version(), "go")
|
||||
if activeGo == version {
|
||||
log.Printf("-dlgo version matches active Go version %s, skipping download.", activeGo)
|
||||
return runtime.GOROOT()
|
||||
}
|
||||
|
||||
ucache, err := os.UserCacheDir()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// For Arm architecture, GOARCH includes ISA version.
|
||||
os := runtime.GOOS
|
||||
arch := runtime.GOARCH
|
||||
if arch == "arm" {
|
||||
arch = "armv6l"
|
||||
}
|
||||
file := fmt.Sprintf("go%s.%s-%s", version, os, arch)
|
||||
if os == "windows" {
|
||||
file += ".zip"
|
||||
} else {
|
||||
file += ".tar.gz"
|
||||
}
|
||||
url := "https://golang.org/dl/" + file
|
||||
dst := filepath.Join(ucache, file)
|
||||
if err := csdb.DownloadFile(url, dst); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
godir := filepath.Join(ucache, fmt.Sprintf("geth-go-%s-%s-%s", version, os, arch))
|
||||
if err := ExtractArchive(dst, godir); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
goroot, err := filepath.Abs(filepath.Join(godir, "go"))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return goroot
|
||||
}
|
||||
|
||||
// Version returns the versions defined in the checksumdb.
|
||||
func Version(csdb *ChecksumDB, version string) (string, error) {
|
||||
for _, l := range csdb.allChecksums {
|
||||
if !strings.HasPrefix(l, "# version:") {
|
||||
continue
|
||||
}
|
||||
v := strings.Split(l, ":")[1]
|
||||
parts := strings.Split(v, " ")
|
||||
if len(parts) != 2 {
|
||||
log.Print("Erroneous version-string", "v", l)
|
||||
continue
|
||||
}
|
||||
if parts[0] == version {
|
||||
return parts[1], nil
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("no version found for '%v'", version)
|
||||
}
|
||||
|
||||
// DownloadAndVerifyChecksums downloads all files and checks that they match
|
||||
// the checksum given in checksums.txt.
|
||||
// This task can be used to sanity-check new checksums.
|
||||
func DownloadAndVerifyChecksums(csdb *ChecksumDB) {
|
||||
var (
|
||||
base = ""
|
||||
ucache = os.TempDir()
|
||||
)
|
||||
for _, l := range csdb.allChecksums {
|
||||
if strings.HasPrefix(l, "# https://") {
|
||||
base = l[2:]
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(l, "#") {
|
||||
continue
|
||||
}
|
||||
hashFile := strings.Split(l, " ")
|
||||
if len(hashFile) != 2 {
|
||||
continue
|
||||
}
|
||||
file := hashFile[1]
|
||||
url := base + file
|
||||
dst := filepath.Join(ucache, file)
|
||||
if err := csdb.DownloadFile(url, dst); err != nil {
|
||||
log.Print(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
// Copyright 2016 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
// signFile reads the contents of an input file and signs it (in armored format)
|
||||
// with the key provided, placing the signature into the output file.
|
||||
|
||||
package build
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"golang.org/x/crypto/openpgp"
|
||||
)
|
||||
|
||||
// PGPSignFile parses a PGP private key from the specified string and creates a
|
||||
// signature file into the output parameter of the input file.
|
||||
//
|
||||
// Note, this method assumes a single key will be container in the pgpkey arg,
|
||||
// furthermore that it is in armored format.
|
||||
func PGPSignFile(input string, output string, pgpkey string) error {
|
||||
// Parse the keyring and make sure we only have a single private key in it
|
||||
keys, err := openpgp.ReadArmoredKeyRing(bytes.NewBufferString(pgpkey))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(keys) != 1 {
|
||||
return fmt.Errorf("key count mismatch: have %d, want %d", len(keys), 1)
|
||||
}
|
||||
// Create the input and output streams for signing
|
||||
in, err := os.Open(input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer in.Close()
|
||||
|
||||
out, err := os.Create(output)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
// Generate the signature and return
|
||||
return openpgp.ArmoredDetachSign(out, keys[0], in, nil)
|
||||
}
|
||||
|
||||
// PGPKeyID parses an armored key and returns the key ID.
|
||||
func PGPKeyID(pgpkey string) (string, error) {
|
||||
keys, err := openpgp.ReadArmoredKeyRing(bytes.NewBufferString(pgpkey))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(keys) != 1 {
|
||||
return "", fmt.Errorf("key count mismatch: have %d, want %d", len(keys), 1)
|
||||
}
|
||||
return keys[0].PrimaryKey.KeyIdString(), nil
|
||||
}
|
||||
|
|
@ -1,237 +0,0 @@
|
|||
// Copyright 2016 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package build
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
)
|
||||
|
||||
var DryRunFlag = flag.Bool("n", false, "dry run, don't execute commands")
|
||||
|
||||
// MustRun executes the given command and exits the host process for
|
||||
// any error.
|
||||
func MustRun(cmd *exec.Cmd) {
|
||||
fmt.Println(">>>", printArgs(cmd.Args))
|
||||
if !*DryRunFlag {
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Stdout = os.Stdout
|
||||
if err := cmd.Run(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func printArgs(args []string) string {
|
||||
var s strings.Builder
|
||||
for i, arg := range args {
|
||||
if i > 0 {
|
||||
s.WriteByte(' ')
|
||||
}
|
||||
if strings.IndexByte(arg, ' ') >= 0 {
|
||||
arg = strconv.QuoteToASCII(arg)
|
||||
}
|
||||
s.WriteString(arg)
|
||||
}
|
||||
return s.String()
|
||||
}
|
||||
|
||||
func MustRunCommand(cmd string, args ...string) {
|
||||
MustRun(exec.Command(cmd, args...))
|
||||
}
|
||||
|
||||
// MustRunCommandWithOutput runs the given command, and ensures that some output will be
|
||||
// printed while it runs. This is useful for CI builds where the process will be stopped
|
||||
// when there is no output.
|
||||
func MustRunCommandWithOutput(cmd string, args ...string) {
|
||||
interval := time.NewTicker(time.Minute)
|
||||
done := make(chan struct{})
|
||||
defer interval.Stop()
|
||||
defer close(done)
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-interval.C:
|
||||
fmt.Printf("Waiting for command %q\n", cmd)
|
||||
case <-done:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
MustRun(exec.Command(cmd, args...))
|
||||
}
|
||||
|
||||
var warnedAboutGit bool
|
||||
|
||||
// RunGit runs a git subcommand and returns its output.
|
||||
// The command must complete successfully.
|
||||
func RunGit(args ...string) string {
|
||||
cmd := exec.Command("git", args...)
|
||||
var stdout, stderr bytes.Buffer
|
||||
cmd.Stdout, cmd.Stderr = &stdout, &stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
if e, ok := err.(*exec.Error); ok && e.Err == exec.ErrNotFound {
|
||||
if !warnedAboutGit {
|
||||
log.Println("Warning: can't find 'git' in PATH")
|
||||
warnedAboutGit = true
|
||||
}
|
||||
return ""
|
||||
}
|
||||
log.Fatal(strings.Join(cmd.Args, " "), ": ", err, "\n", stderr.String())
|
||||
}
|
||||
return strings.TrimSpace(stdout.String())
|
||||
}
|
||||
|
||||
// readGitFile returns content of file in .git directory.
|
||||
func readGitFile(file string) string {
|
||||
content, err := os.ReadFile(path.Join(".git", file))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSpace(string(content))
|
||||
}
|
||||
|
||||
// Render renders the given template file into outputFile.
|
||||
func Render(templateFile, outputFile string, outputPerm os.FileMode, x interface{}) {
|
||||
tpl := template.Must(template.ParseFiles(templateFile))
|
||||
render(tpl, outputFile, outputPerm, x)
|
||||
}
|
||||
|
||||
// RenderString renders the given template string into outputFile.
|
||||
func RenderString(templateContent, outputFile string, outputPerm os.FileMode, x interface{}) {
|
||||
tpl := template.Must(template.New("").Parse(templateContent))
|
||||
render(tpl, outputFile, outputPerm, x)
|
||||
}
|
||||
|
||||
func render(tpl *template.Template, outputFile string, outputPerm os.FileMode, x interface{}) {
|
||||
if err := os.MkdirAll(filepath.Dir(outputFile), 0755); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
out, err := os.OpenFile(outputFile, os.O_CREATE|os.O_WRONLY|os.O_EXCL, outputPerm)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if err := tpl.Execute(out, x); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if err := out.Close(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// UploadSFTP uploads files to a remote host using the sftp command line tool.
|
||||
// The destination host may be specified either as [user@]host: or as a URI in
|
||||
// the form sftp://[user@]host[:port].
|
||||
func UploadSFTP(identityFile, host, dir string, files []string) error {
|
||||
sftp := exec.Command("sftp")
|
||||
sftp.Stderr = os.Stderr
|
||||
if identityFile != "" {
|
||||
sftp.Args = append(sftp.Args, "-i", identityFile)
|
||||
}
|
||||
sftp.Args = append(sftp.Args, host)
|
||||
fmt.Println(">>>", printArgs(sftp.Args))
|
||||
if *DryRunFlag {
|
||||
return nil
|
||||
}
|
||||
|
||||
stdin, err := sftp.StdinPipe()
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't create stdin pipe for sftp: %v", err)
|
||||
}
|
||||
stdout, err := sftp.StdoutPipe()
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't create stdout pipe for sftp: %v", err)
|
||||
}
|
||||
if err := sftp.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
in := io.MultiWriter(stdin, os.Stdout)
|
||||
for _, f := range files {
|
||||
fmt.Fprintln(in, "put", f, path.Join(dir, filepath.Base(f)))
|
||||
}
|
||||
fmt.Fprintln(in, "exit")
|
||||
// Some issue with the PPA sftp server makes it so the server does not
|
||||
// respond properly to a 'bye', 'exit' or 'quit' from the client.
|
||||
// To work around that, we check the output, and when we see the client
|
||||
// exit command, we do a hard exit.
|
||||
// See
|
||||
// https://github.com/kolban-google/sftp-gcs/issues/23
|
||||
// https://github.com/mscdex/ssh2/pull/1111
|
||||
aborted := false
|
||||
go func() {
|
||||
scanner := bufio.NewScanner(stdout)
|
||||
for scanner.Scan() {
|
||||
txt := scanner.Text()
|
||||
fmt.Println(txt)
|
||||
if txt == "sftp> exit" {
|
||||
// Give it .5 seconds to exit (server might be fixed), then
|
||||
// hard kill it from the outside
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
aborted = true
|
||||
sftp.Process.Kill()
|
||||
}
|
||||
}
|
||||
}()
|
||||
stdin.Close()
|
||||
err = sftp.Wait()
|
||||
if aborted {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// FindMainPackages finds all 'main' packages in the given directory and returns their
|
||||
// package paths.
|
||||
func FindMainPackages(dir string) []string {
|
||||
var commands []string
|
||||
cmds, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
for _, cmd := range cmds {
|
||||
pkgdir := filepath.Join(dir, cmd.Name())
|
||||
if !cmd.IsDir() {
|
||||
continue
|
||||
}
|
||||
pkgs, err := parser.ParseDir(token.NewFileSet(), pkgdir, nil, parser.PackageClauseOnly)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
for name := range pkgs {
|
||||
if name == "main" {
|
||||
path := "./" + filepath.ToSlash(pkgdir)
|
||||
commands = append(commands, path)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return commands
|
||||
}
|
||||
|
|
@ -1,300 +0,0 @@
|
|||
// Copyright 2017 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package cmdtest
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"testing"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/internal/reexec"
|
||||
)
|
||||
|
||||
func NewTestCmd(t *testing.T, data interface{}) *TestCmd {
|
||||
return &TestCmd{T: t, Data: data}
|
||||
}
|
||||
|
||||
type TestCmd struct {
|
||||
// For total convenience, all testing methods are available.
|
||||
*testing.T
|
||||
|
||||
Func template.FuncMap
|
||||
Data interface{}
|
||||
Cleanup func()
|
||||
|
||||
cmd *exec.Cmd
|
||||
stdout *bufio.Reader
|
||||
stdin io.WriteCloser
|
||||
stderr *testlogger
|
||||
// Err will contain the process exit error or interrupt signal error
|
||||
Err error
|
||||
}
|
||||
|
||||
var id atomic.Int32
|
||||
|
||||
// Run exec's the current binary using name as argv[0] which will trigger the
|
||||
// reexec init function for that name (e.g. "geth-test" in cmd/geth/run_test.go)
|
||||
func (tt *TestCmd) Run(name string, args ...string) {
|
||||
id.Add(1)
|
||||
tt.stderr = &testlogger{t: tt.T, name: fmt.Sprintf("%d", id.Load())}
|
||||
tt.cmd = &exec.Cmd{
|
||||
Path: reexec.Self(),
|
||||
Args: append([]string{name}, args...),
|
||||
Stderr: tt.stderr,
|
||||
}
|
||||
stdout, err := tt.cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
tt.Fatal(err)
|
||||
}
|
||||
tt.stdout = bufio.NewReader(stdout)
|
||||
if tt.stdin, err = tt.cmd.StdinPipe(); err != nil {
|
||||
tt.Fatal(err)
|
||||
}
|
||||
if err := tt.cmd.Start(); err != nil {
|
||||
tt.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// InputLine writes the given text to the child's stdin.
|
||||
// This method can also be called from an expect template, e.g.:
|
||||
//
|
||||
// geth.expect(`Passphrase: {{.InputLine "password"}}`)
|
||||
func (tt *TestCmd) InputLine(s string) string {
|
||||
io.WriteString(tt.stdin, s+"\n")
|
||||
return ""
|
||||
}
|
||||
|
||||
func (tt *TestCmd) SetTemplateFunc(name string, fn interface{}) {
|
||||
if tt.Func == nil {
|
||||
tt.Func = make(map[string]interface{})
|
||||
}
|
||||
tt.Func[name] = fn
|
||||
}
|
||||
|
||||
// Expect runs its argument as a template, then expects the
|
||||
// child process to output the result of the template within 5s.
|
||||
//
|
||||
// If the template starts with a newline, the newline is removed
|
||||
// before matching.
|
||||
func (tt *TestCmd) Expect(tplsource string) {
|
||||
// Generate the expected output by running the template.
|
||||
tpl := template.Must(template.New("").Funcs(tt.Func).Parse(tplsource))
|
||||
wantbuf := new(bytes.Buffer)
|
||||
if err := tpl.Execute(wantbuf, tt.Data); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// Trim exactly one newline at the beginning. This makes tests look
|
||||
// much nicer because all expect strings are at column 0.
|
||||
want := bytes.TrimPrefix(wantbuf.Bytes(), []byte("\n"))
|
||||
if err := tt.matchExactOutput(want); err != nil {
|
||||
tt.Fatal(err)
|
||||
}
|
||||
tt.Logf("Matched stdout text:\n%s", want)
|
||||
}
|
||||
|
||||
// Output reads all output from stdout, and returns the data.
|
||||
func (tt *TestCmd) Output() []byte {
|
||||
var buf []byte
|
||||
tt.withKillTimeout(func() { buf, _ = io.ReadAll(tt.stdout) })
|
||||
return buf
|
||||
}
|
||||
|
||||
func (tt *TestCmd) matchExactOutput(want []byte) error {
|
||||
buf := make([]byte, len(want))
|
||||
n := 0
|
||||
tt.withKillTimeout(func() { n, _ = io.ReadFull(tt.stdout, buf) })
|
||||
buf = buf[:n]
|
||||
if n < len(want) || !bytes.Equal(buf, want) {
|
||||
// Grab any additional buffered output in case of mismatch
|
||||
// because it might help with debugging.
|
||||
buf = append(buf, make([]byte, tt.stdout.Buffered())...)
|
||||
tt.stdout.Read(buf[n:])
|
||||
// Find the mismatch position.
|
||||
for i := 0; i < n; i++ {
|
||||
if want[i] != buf[i] {
|
||||
return fmt.Errorf("output mismatch at ◊:\n---------------- (stdout text)\n%s◊%s\n---------------- (expected text)\n%s",
|
||||
buf[:i], buf[i:n], want)
|
||||
}
|
||||
}
|
||||
if n < len(want) {
|
||||
return fmt.Errorf("not enough output, got until ◊:\n---------------- (stdout text)\n%s\n---------------- (expected text)\n%s◊%s",
|
||||
buf, want[:n], want[n:])
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExpectRegexp expects the child process to output text matching the
|
||||
// given regular expression within 5s.
|
||||
//
|
||||
// Note that an arbitrary amount of output may be consumed by the
|
||||
// regular expression. This usually means that expect cannot be used
|
||||
// after ExpectRegexp.
|
||||
func (tt *TestCmd) ExpectRegexp(regex string) (*regexp.Regexp, []string) {
|
||||
regex = strings.TrimPrefix(regex, "\n")
|
||||
var (
|
||||
re = regexp.MustCompile(regex)
|
||||
rtee = &runeTee{in: tt.stdout}
|
||||
matches []int
|
||||
)
|
||||
tt.withKillTimeout(func() { matches = re.FindReaderSubmatchIndex(rtee) })
|
||||
output := rtee.buf.Bytes()
|
||||
if matches == nil {
|
||||
tt.Fatalf("Output did not match:\n---------------- (stdout text)\n%s\n---------------- (regular expression)\n%s",
|
||||
output, regex)
|
||||
return re, nil
|
||||
}
|
||||
tt.Logf("Matched stdout text:\n%s", output)
|
||||
var submatches []string
|
||||
for i := 0; i < len(matches); i += 2 {
|
||||
submatch := string(output[matches[i]:matches[i+1]])
|
||||
submatches = append(submatches, submatch)
|
||||
}
|
||||
return re, submatches
|
||||
}
|
||||
|
||||
// ExpectExit expects the child process to exit within 5s without
|
||||
// printing any additional text on stdout.
|
||||
func (tt *TestCmd) ExpectExit() {
|
||||
var output []byte
|
||||
tt.withKillTimeout(func() {
|
||||
output, _ = io.ReadAll(tt.stdout)
|
||||
})
|
||||
tt.WaitExit()
|
||||
if tt.Cleanup != nil {
|
||||
tt.Cleanup()
|
||||
}
|
||||
if len(output) > 0 {
|
||||
tt.Errorf("Unmatched stdout text:\n%s", output)
|
||||
}
|
||||
}
|
||||
|
||||
func (tt *TestCmd) WaitExit() {
|
||||
tt.Err = tt.cmd.Wait()
|
||||
}
|
||||
|
||||
func (tt *TestCmd) Interrupt() {
|
||||
tt.Err = tt.cmd.Process.Signal(os.Interrupt)
|
||||
}
|
||||
|
||||
// ExitStatus exposes the process' OS exit code
|
||||
// It will only return a valid value after the process has finished.
|
||||
func (tt *TestCmd) ExitStatus() int {
|
||||
if tt.Err != nil {
|
||||
exitErr := tt.Err.(*exec.ExitError)
|
||||
if exitErr != nil {
|
||||
if status, ok := exitErr.Sys().(syscall.WaitStatus); ok {
|
||||
return status.ExitStatus()
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// StderrText returns any stderr output written so far.
|
||||
// The returned text holds all log lines after ExpectExit has
|
||||
// returned.
|
||||
func (tt *TestCmd) StderrText() string {
|
||||
tt.stderr.mu.Lock()
|
||||
defer tt.stderr.mu.Unlock()
|
||||
return tt.stderr.buf.String()
|
||||
}
|
||||
|
||||
func (tt *TestCmd) CloseStdin() {
|
||||
tt.stdin.Close()
|
||||
}
|
||||
|
||||
func (tt *TestCmd) Kill() {
|
||||
tt.cmd.Process.Kill()
|
||||
if tt.Cleanup != nil {
|
||||
tt.Cleanup()
|
||||
}
|
||||
}
|
||||
|
||||
func (tt *TestCmd) withKillTimeout(fn func()) {
|
||||
timeout := time.AfterFunc(30*time.Second, func() {
|
||||
tt.Log("killing the child process (timeout)")
|
||||
tt.Kill()
|
||||
})
|
||||
defer timeout.Stop()
|
||||
fn()
|
||||
}
|
||||
|
||||
// testlogger logs all written lines via t.Log and also
|
||||
// collects them for later inspection.
|
||||
type testlogger struct {
|
||||
t *testing.T
|
||||
mu sync.Mutex
|
||||
buf bytes.Buffer
|
||||
name string
|
||||
}
|
||||
|
||||
func (tl *testlogger) Write(b []byte) (n int, err error) {
|
||||
lines := bytes.Split(b, []byte("\n"))
|
||||
for _, line := range lines {
|
||||
if len(line) > 0 {
|
||||
tl.t.Logf("(stderr:%v) %s", tl.name, line)
|
||||
}
|
||||
}
|
||||
tl.mu.Lock()
|
||||
tl.buf.Write(b)
|
||||
tl.mu.Unlock()
|
||||
return len(b), err
|
||||
}
|
||||
|
||||
// runeTee collects text read through it into buf.
|
||||
type runeTee struct {
|
||||
in interface {
|
||||
io.Reader
|
||||
io.ByteReader
|
||||
io.RuneReader
|
||||
}
|
||||
buf bytes.Buffer
|
||||
}
|
||||
|
||||
func (rtee *runeTee) Read(b []byte) (n int, err error) {
|
||||
n, err = rtee.in.Read(b)
|
||||
rtee.buf.Write(b[:n])
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (rtee *runeTee) ReadRune() (r rune, size int, err error) {
|
||||
r, size, err = rtee.in.ReadRune()
|
||||
if err == nil {
|
||||
rtee.buf.WriteRune(r)
|
||||
}
|
||||
return r, size, err
|
||||
}
|
||||
|
||||
func (rtee *runeTee) ReadByte() (b byte, err error) {
|
||||
b, err = rtee.in.ReadByte()
|
||||
if err == nil {
|
||||
rtee.buf.WriteByte(b)
|
||||
}
|
||||
return b, err
|
||||
}
|
||||
|
|
@ -1,267 +0,0 @@
|
|||
// Copyright 2016 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Package debug interfaces Go runtime debugging facilities.
|
||||
// This package is mostly glue code making these facilities available
|
||||
// through the CLI and RPC subsystem. If you want to use them from Go code,
|
||||
// use package runtime instead.
|
||||
package debug
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"runtime/pprof"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/hashicorp/go-bexpr"
|
||||
"golang.org/x/exp/slog"
|
||||
)
|
||||
|
||||
// Handler is the global debugging handler.
|
||||
var Handler = new(HandlerT)
|
||||
|
||||
// HandlerT implements the debugging API.
|
||||
// Do not create values of this type, use the one
|
||||
// in the Handler variable instead.
|
||||
type HandlerT struct {
|
||||
mu sync.Mutex
|
||||
cpuW io.WriteCloser
|
||||
cpuFile string
|
||||
traceW io.WriteCloser
|
||||
traceFile string
|
||||
}
|
||||
|
||||
// Verbosity sets the log verbosity ceiling. The verbosity of individual packages
|
||||
// and source files can be raised using Vmodule.
|
||||
func (*HandlerT) Verbosity(level int) {
|
||||
glogger.Verbosity(slog.Level(level))
|
||||
}
|
||||
|
||||
// Vmodule sets the log verbosity pattern. See package log for details on the
|
||||
// pattern syntax.
|
||||
func (*HandlerT) Vmodule(pattern string) error {
|
||||
return glogger.Vmodule(pattern)
|
||||
}
|
||||
|
||||
// MemStats returns detailed runtime memory statistics.
|
||||
func (*HandlerT) MemStats() *runtime.MemStats {
|
||||
s := new(runtime.MemStats)
|
||||
runtime.ReadMemStats(s)
|
||||
return s
|
||||
}
|
||||
|
||||
// GcStats returns GC statistics.
|
||||
func (*HandlerT) GcStats() *debug.GCStats {
|
||||
s := new(debug.GCStats)
|
||||
debug.ReadGCStats(s)
|
||||
return s
|
||||
}
|
||||
|
||||
// CpuProfile turns on CPU profiling for nsec seconds and writes
|
||||
// profile data to file.
|
||||
func (h *HandlerT) CpuProfile(file string, nsec uint) error {
|
||||
if err := h.StartCPUProfile(file); err != nil {
|
||||
return err
|
||||
}
|
||||
time.Sleep(time.Duration(nsec) * time.Second)
|
||||
h.StopCPUProfile()
|
||||
return nil
|
||||
}
|
||||
|
||||
// StartCPUProfile turns on CPU profiling, writing to the given file.
|
||||
func (h *HandlerT) StartCPUProfile(file string) error {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
if h.cpuW != nil {
|
||||
return errors.New("CPU profiling already in progress")
|
||||
}
|
||||
f, err := os.Create(expandHome(file))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := pprof.StartCPUProfile(f); err != nil {
|
||||
f.Close()
|
||||
return err
|
||||
}
|
||||
h.cpuW = f
|
||||
h.cpuFile = file
|
||||
log.Info("CPU profiling started", "dump", h.cpuFile)
|
||||
return nil
|
||||
}
|
||||
|
||||
// StopCPUProfile stops an ongoing CPU profile.
|
||||
func (h *HandlerT) StopCPUProfile() error {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
pprof.StopCPUProfile()
|
||||
if h.cpuW == nil {
|
||||
return errors.New("CPU profiling not in progress")
|
||||
}
|
||||
log.Info("Done writing CPU profile", "dump", h.cpuFile)
|
||||
h.cpuW.Close()
|
||||
h.cpuW = nil
|
||||
h.cpuFile = ""
|
||||
return nil
|
||||
}
|
||||
|
||||
// GoTrace turns on tracing for nsec seconds and writes
|
||||
// trace data to file.
|
||||
func (h *HandlerT) GoTrace(file string, nsec uint) error {
|
||||
if err := h.StartGoTrace(file); err != nil {
|
||||
return err
|
||||
}
|
||||
time.Sleep(time.Duration(nsec) * time.Second)
|
||||
h.StopGoTrace()
|
||||
return nil
|
||||
}
|
||||
|
||||
// BlockProfile turns on goroutine profiling for nsec seconds and writes profile data to
|
||||
// file. It uses a profile rate of 1 for most accurate information. If a different rate is
|
||||
// desired, set the rate and write the profile manually.
|
||||
func (*HandlerT) BlockProfile(file string, nsec uint) error {
|
||||
runtime.SetBlockProfileRate(1)
|
||||
time.Sleep(time.Duration(nsec) * time.Second)
|
||||
defer runtime.SetBlockProfileRate(0)
|
||||
return writeProfile("block", file)
|
||||
}
|
||||
|
||||
// SetBlockProfileRate sets the rate of goroutine block profile data collection.
|
||||
// rate 0 disables block profiling.
|
||||
func (*HandlerT) SetBlockProfileRate(rate int) {
|
||||
runtime.SetBlockProfileRate(rate)
|
||||
}
|
||||
|
||||
// WriteBlockProfile writes a goroutine blocking profile to the given file.
|
||||
func (*HandlerT) WriteBlockProfile(file string) error {
|
||||
return writeProfile("block", file)
|
||||
}
|
||||
|
||||
// MutexProfile turns on mutex profiling for nsec seconds and writes profile data to file.
|
||||
// It uses a profile rate of 1 for most accurate information. If a different rate is
|
||||
// desired, set the rate and write the profile manually.
|
||||
func (*HandlerT) MutexProfile(file string, nsec uint) error {
|
||||
runtime.SetMutexProfileFraction(1)
|
||||
time.Sleep(time.Duration(nsec) * time.Second)
|
||||
defer runtime.SetMutexProfileFraction(0)
|
||||
return writeProfile("mutex", file)
|
||||
}
|
||||
|
||||
// SetMutexProfileFraction sets the rate of mutex profiling.
|
||||
func (*HandlerT) SetMutexProfileFraction(rate int) {
|
||||
runtime.SetMutexProfileFraction(rate)
|
||||
}
|
||||
|
||||
// WriteMutexProfile writes a goroutine blocking profile to the given file.
|
||||
func (*HandlerT) WriteMutexProfile(file string) error {
|
||||
return writeProfile("mutex", file)
|
||||
}
|
||||
|
||||
// WriteMemProfile writes an allocation profile to the given file.
|
||||
// Note that the profiling rate cannot be set through the API,
|
||||
// it must be set on the command line.
|
||||
func (*HandlerT) WriteMemProfile(file string) error {
|
||||
return writeProfile("heap", file)
|
||||
}
|
||||
|
||||
// Stacks returns a printed representation of the stacks of all goroutines. It
|
||||
// also permits the following optional filters to be used:
|
||||
// - filter: boolean expression of packages to filter for
|
||||
func (*HandlerT) Stacks(filter *string) string {
|
||||
buf := new(bytes.Buffer)
|
||||
pprof.Lookup("goroutine").WriteTo(buf, 2)
|
||||
|
||||
// If any filtering was requested, execute them now
|
||||
if filter != nil && len(*filter) > 0 {
|
||||
expanded := *filter
|
||||
|
||||
// The input filter is a logical expression of package names. Transform
|
||||
// it into a proper boolean expression that can be fed into a parser and
|
||||
// interpreter:
|
||||
//
|
||||
// E.g. (eth || snap) && !p2p -> (eth in Value || snap in Value) && p2p not in Value
|
||||
expanded = regexp.MustCompile(`[:/\.A-Za-z0-9_-]+`).ReplaceAllString(expanded, "`$0` in Value")
|
||||
expanded = regexp.MustCompile("!(`[:/\\.A-Za-z0-9_-]+`)").ReplaceAllString(expanded, "$1 not")
|
||||
expanded = strings.ReplaceAll(expanded, "||", "or")
|
||||
expanded = strings.ReplaceAll(expanded, "&&", "and")
|
||||
log.Info("Expanded filter expression", "filter", *filter, "expanded", expanded)
|
||||
|
||||
expr, err := bexpr.CreateEvaluator(expanded)
|
||||
if err != nil {
|
||||
log.Error("Failed to parse filter expression", "expanded", expanded, "err", err)
|
||||
return ""
|
||||
}
|
||||
// Split the goroutine dump into segments and filter each
|
||||
dump := buf.String()
|
||||
buf.Reset()
|
||||
|
||||
for _, trace := range strings.Split(dump, "\n\n") {
|
||||
if ok, _ := expr.Evaluate(map[string]string{"Value": trace}); ok {
|
||||
buf.WriteString(trace)
|
||||
buf.WriteString("\n\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// FreeOSMemory forces a garbage collection.
|
||||
func (*HandlerT) FreeOSMemory() {
|
||||
debug.FreeOSMemory()
|
||||
}
|
||||
|
||||
// SetGCPercent sets the garbage collection target percentage. It returns the previous
|
||||
// setting. A negative value disables GC.
|
||||
func (*HandlerT) SetGCPercent(v int) int {
|
||||
return debug.SetGCPercent(v)
|
||||
}
|
||||
|
||||
func writeProfile(name, file string) error {
|
||||
p := pprof.Lookup(name)
|
||||
log.Info("Writing profile records", "count", p.Count(), "type", name, "dump", file)
|
||||
f, err := os.Create(expandHome(file))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
return p.WriteTo(f, 0)
|
||||
}
|
||||
|
||||
// expands home directory in file paths.
|
||||
// ~someuser/tmp will not be expanded.
|
||||
func expandHome(p string) string {
|
||||
if strings.HasPrefix(p, "~/") || strings.HasPrefix(p, "~\\") {
|
||||
home := os.Getenv("HOME")
|
||||
if home == "" {
|
||||
if usr, err := user.Current(); err == nil {
|
||||
home = usr.HomeDir
|
||||
}
|
||||
}
|
||||
if home != "" {
|
||||
p = home + p[1:]
|
||||
}
|
||||
}
|
||||
return filepath.Clean(p)
|
||||
}
|
||||
|
|
@ -1,357 +0,0 @@
|
|||
// Copyright 2016 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package debug
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
"github.com/ethereum/go-ethereum/internal/flags"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
"github.com/ethereum/go-ethereum/metrics/exp"
|
||||
"github.com/fjl/memsize/memsizeui"
|
||||
"github.com/mattn/go-colorable"
|
||||
"github.com/mattn/go-isatty"
|
||||
"github.com/urfave/cli/v2"
|
||||
"golang.org/x/exp/slog"
|
||||
"gopkg.in/natefinch/lumberjack.v2"
|
||||
)
|
||||
|
||||
var Memsize memsizeui.Handler
|
||||
|
||||
var (
|
||||
verbosityFlag = &cli.IntFlag{
|
||||
Name: "verbosity",
|
||||
Usage: "Logging verbosity: 0=silent, 1=error, 2=warn, 3=info, 4=debug, 5=detail",
|
||||
Value: 3,
|
||||
Category: flags.LoggingCategory,
|
||||
}
|
||||
logVmoduleFlag = &cli.StringFlag{
|
||||
Name: "log.vmodule",
|
||||
Usage: "Per-module verbosity: comma-separated list of <pattern>=<level> (e.g. eth/*=5,p2p=4)",
|
||||
Value: "",
|
||||
Category: flags.LoggingCategory,
|
||||
}
|
||||
vmoduleFlag = &cli.StringFlag{
|
||||
Name: "vmodule",
|
||||
Usage: "Per-module verbosity: comma-separated list of <pattern>=<level> (e.g. eth/*=5,p2p=4)",
|
||||
Value: "",
|
||||
Hidden: true,
|
||||
Category: flags.LoggingCategory,
|
||||
}
|
||||
logjsonFlag = &cli.BoolFlag{
|
||||
Name: "log.json",
|
||||
Usage: "Format logs with JSON",
|
||||
Hidden: true,
|
||||
Category: flags.LoggingCategory,
|
||||
}
|
||||
logFormatFlag = &cli.StringFlag{
|
||||
Name: "log.format",
|
||||
Usage: "Log format to use (json|logfmt|terminal)",
|
||||
Category: flags.LoggingCategory,
|
||||
}
|
||||
logFileFlag = &cli.StringFlag{
|
||||
Name: "log.file",
|
||||
Usage: "Write logs to a file",
|
||||
Category: flags.LoggingCategory,
|
||||
}
|
||||
logRotateFlag = &cli.BoolFlag{
|
||||
Name: "log.rotate",
|
||||
Usage: "Enables log file rotation",
|
||||
Category: flags.LoggingCategory,
|
||||
}
|
||||
logMaxSizeMBsFlag = &cli.IntFlag{
|
||||
Name: "log.maxsize",
|
||||
Usage: "Maximum size in MBs of a single log file",
|
||||
Value: 100,
|
||||
Category: flags.LoggingCategory,
|
||||
}
|
||||
logMaxBackupsFlag = &cli.IntFlag{
|
||||
Name: "log.maxbackups",
|
||||
Usage: "Maximum number of log files to retain",
|
||||
Value: 10,
|
||||
Category: flags.LoggingCategory,
|
||||
}
|
||||
logMaxAgeFlag = &cli.IntFlag{
|
||||
Name: "log.maxage",
|
||||
Usage: "Maximum number of days to retain a log file",
|
||||
Value: 30,
|
||||
Category: flags.LoggingCategory,
|
||||
}
|
||||
logCompressFlag = &cli.BoolFlag{
|
||||
Name: "log.compress",
|
||||
Usage: "Compress the log files",
|
||||
Value: false,
|
||||
Category: flags.LoggingCategory,
|
||||
}
|
||||
pprofFlag = &cli.BoolFlag{
|
||||
Name: "pprof",
|
||||
Usage: "Enable the pprof HTTP server",
|
||||
Category: flags.LoggingCategory,
|
||||
}
|
||||
pprofPortFlag = &cli.IntFlag{
|
||||
Name: "pprof.port",
|
||||
Usage: "pprof HTTP server listening port",
|
||||
Value: 6060,
|
||||
Category: flags.LoggingCategory,
|
||||
}
|
||||
pprofAddrFlag = &cli.StringFlag{
|
||||
Name: "pprof.addr",
|
||||
Usage: "pprof HTTP server listening interface",
|
||||
Value: "127.0.0.1",
|
||||
Category: flags.LoggingCategory,
|
||||
}
|
||||
memprofilerateFlag = &cli.IntFlag{
|
||||
Name: "pprof.memprofilerate",
|
||||
Usage: "Turn on memory profiling with the given rate",
|
||||
Value: runtime.MemProfileRate,
|
||||
Category: flags.LoggingCategory,
|
||||
}
|
||||
blockprofilerateFlag = &cli.IntFlag{
|
||||
Name: "pprof.blockprofilerate",
|
||||
Usage: "Turn on block profiling with the given rate",
|
||||
Category: flags.LoggingCategory,
|
||||
}
|
||||
cpuprofileFlag = &cli.StringFlag{
|
||||
Name: "pprof.cpuprofile",
|
||||
Usage: "Write CPU profile to the given file",
|
||||
Category: flags.LoggingCategory,
|
||||
}
|
||||
traceFlag = &cli.StringFlag{
|
||||
Name: "trace",
|
||||
Usage: "Write execution trace to the given file",
|
||||
Category: flags.LoggingCategory,
|
||||
}
|
||||
)
|
||||
|
||||
// Flags holds all command-line flags required for debugging.
|
||||
var Flags = []cli.Flag{
|
||||
verbosityFlag,
|
||||
logVmoduleFlag,
|
||||
vmoduleFlag,
|
||||
logjsonFlag,
|
||||
logFormatFlag,
|
||||
logFileFlag,
|
||||
logRotateFlag,
|
||||
logMaxSizeMBsFlag,
|
||||
logMaxBackupsFlag,
|
||||
logMaxAgeFlag,
|
||||
logCompressFlag,
|
||||
pprofFlag,
|
||||
pprofAddrFlag,
|
||||
pprofPortFlag,
|
||||
memprofilerateFlag,
|
||||
blockprofilerateFlag,
|
||||
cpuprofileFlag,
|
||||
traceFlag,
|
||||
}
|
||||
|
||||
var (
|
||||
glogger *log.GlogHandler
|
||||
logOutputFile io.WriteCloser
|
||||
defaultTerminalHandler *log.TerminalHandler
|
||||
)
|
||||
|
||||
func init() {
|
||||
defaultTerminalHandler = log.NewTerminalHandler(os.Stderr, false)
|
||||
glogger = log.NewGlogHandler(defaultTerminalHandler)
|
||||
glogger.Verbosity(log.LvlInfo)
|
||||
log.SetDefault(log.NewLogger(glogger))
|
||||
}
|
||||
|
||||
func ResetLogging() {
|
||||
if defaultTerminalHandler != nil {
|
||||
defaultTerminalHandler.ResetFieldPadding()
|
||||
}
|
||||
}
|
||||
|
||||
// Setup initializes profiling and logging based on the CLI flags.
|
||||
// It should be called as early as possible in the program.
|
||||
func Setup(ctx *cli.Context) error {
|
||||
var (
|
||||
handler slog.Handler
|
||||
terminalOutput = io.Writer(os.Stderr)
|
||||
output io.Writer
|
||||
logFmtFlag = ctx.String(logFormatFlag.Name)
|
||||
)
|
||||
var (
|
||||
logFile = ctx.String(logFileFlag.Name)
|
||||
rotation = ctx.Bool(logRotateFlag.Name)
|
||||
)
|
||||
if len(logFile) > 0 {
|
||||
if err := validateLogLocation(filepath.Dir(logFile)); err != nil {
|
||||
return fmt.Errorf("failed to initiatilize file logger: %v", err)
|
||||
}
|
||||
}
|
||||
context := []interface{}{"rotate", rotation}
|
||||
if len(logFmtFlag) > 0 {
|
||||
context = append(context, "format", logFmtFlag)
|
||||
} else {
|
||||
context = append(context, "format", "terminal")
|
||||
}
|
||||
if rotation {
|
||||
// Lumberjack uses <processname>-lumberjack.log in is.TempDir() if empty.
|
||||
// so typically /tmp/geth-lumberjack.log on linux
|
||||
if len(logFile) > 0 {
|
||||
context = append(context, "location", logFile)
|
||||
} else {
|
||||
context = append(context, "location", filepath.Join(os.TempDir(), "geth-lumberjack.log"))
|
||||
}
|
||||
logOutputFile = &lumberjack.Logger{
|
||||
Filename: logFile,
|
||||
MaxSize: ctx.Int(logMaxSizeMBsFlag.Name),
|
||||
MaxBackups: ctx.Int(logMaxBackupsFlag.Name),
|
||||
MaxAge: ctx.Int(logMaxAgeFlag.Name),
|
||||
Compress: ctx.Bool(logCompressFlag.Name),
|
||||
}
|
||||
output = io.MultiWriter(terminalOutput, logOutputFile)
|
||||
} else if logFile != "" {
|
||||
var err error
|
||||
if logOutputFile, err = os.OpenFile(logFile, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
output = io.MultiWriter(logOutputFile, terminalOutput)
|
||||
context = append(context, "location", logFile)
|
||||
} else {
|
||||
output = terminalOutput
|
||||
}
|
||||
|
||||
switch {
|
||||
case ctx.Bool(logjsonFlag.Name):
|
||||
// Retain backwards compatibility with `--log.json` flag if `--log.format` not set
|
||||
defer log.Warn("The flag '--log.json' is deprecated, please use '--log.format=json' instead")
|
||||
handler = log.JSONHandler(output)
|
||||
case logFmtFlag == "json":
|
||||
handler = log.JSONHandler(output)
|
||||
case logFmtFlag == "logfmt":
|
||||
handler = log.LogfmtHandler(output)
|
||||
case logFmtFlag == "", logFmtFlag == "terminal":
|
||||
useColor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb"
|
||||
if useColor {
|
||||
terminalOutput = colorable.NewColorableStderr()
|
||||
if logOutputFile != nil {
|
||||
output = io.MultiWriter(logOutputFile, terminalOutput)
|
||||
} else {
|
||||
output = terminalOutput
|
||||
}
|
||||
}
|
||||
handler = log.NewTerminalHandler(output, useColor)
|
||||
default:
|
||||
// Unknown log format specified
|
||||
return fmt.Errorf("unknown log format: %v", ctx.String(logFormatFlag.Name))
|
||||
}
|
||||
|
||||
glogger = log.NewGlogHandler(handler)
|
||||
|
||||
// logging
|
||||
verbosity := log.FromLegacyLevel(ctx.Int(verbosityFlag.Name))
|
||||
glogger.Verbosity(verbosity)
|
||||
vmodule := ctx.String(logVmoduleFlag.Name)
|
||||
if vmodule == "" {
|
||||
// Retain backwards compatibility with `--vmodule` flag if `--log.vmodule` not set
|
||||
vmodule = ctx.String(vmoduleFlag.Name)
|
||||
if vmodule != "" {
|
||||
defer log.Warn("The flag '--vmodule' is deprecated, please use '--log.vmodule' instead")
|
||||
}
|
||||
}
|
||||
glogger.Vmodule(vmodule)
|
||||
|
||||
log.SetDefault(log.NewLogger(glogger))
|
||||
|
||||
// profiling, tracing
|
||||
runtime.MemProfileRate = memprofilerateFlag.Value
|
||||
if ctx.IsSet(memprofilerateFlag.Name) {
|
||||
runtime.MemProfileRate = ctx.Int(memprofilerateFlag.Name)
|
||||
}
|
||||
|
||||
blockProfileRate := ctx.Int(blockprofilerateFlag.Name)
|
||||
Handler.SetBlockProfileRate(blockProfileRate)
|
||||
|
||||
if traceFile := ctx.String(traceFlag.Name); traceFile != "" {
|
||||
if err := Handler.StartGoTrace(traceFile); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if cpuFile := ctx.String(cpuprofileFlag.Name); cpuFile != "" {
|
||||
if err := Handler.StartCPUProfile(cpuFile); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// pprof server
|
||||
if ctx.Bool(pprofFlag.Name) {
|
||||
listenHost := ctx.String(pprofAddrFlag.Name)
|
||||
|
||||
port := ctx.Int(pprofPortFlag.Name)
|
||||
|
||||
address := net.JoinHostPort(listenHost, fmt.Sprintf("%d", port))
|
||||
// This context value ("metrics.addr") represents the utils.MetricsHTTPFlag.Name.
|
||||
// It cannot be imported because it will cause a cyclical dependency.
|
||||
StartPProf(address, !ctx.IsSet("metrics.addr"))
|
||||
}
|
||||
if len(logFile) > 0 || rotation {
|
||||
log.Info("Logging configured", context...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func StartPProf(address string, withMetrics bool) {
|
||||
// Hook go-metrics into expvar on any /debug/metrics request, load all vars
|
||||
// from the registry into expvar, and execute regular expvar handler.
|
||||
if withMetrics {
|
||||
exp.Exp(metrics.DefaultRegistry)
|
||||
}
|
||||
http.Handle("/memsize/", http.StripPrefix("/memsize", &Memsize))
|
||||
log.Info("Starting pprof server", "addr", fmt.Sprintf("http://%s/debug/pprof", address))
|
||||
go func() {
|
||||
if err := http.ListenAndServe(address, nil); err != nil {
|
||||
log.Error("Failure in running pprof server", "err", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Exit stops all running profiles, flushing their output to the
|
||||
// respective file.
|
||||
func Exit() {
|
||||
Handler.StopCPUProfile()
|
||||
Handler.StopGoTrace()
|
||||
if logOutputFile != nil {
|
||||
logOutputFile.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func validateLogLocation(path string) error {
|
||||
if err := os.MkdirAll(path, os.ModePerm); err != nil {
|
||||
return fmt.Errorf("error creating the directory: %w", err)
|
||||
}
|
||||
// Check if the path is writable by trying to create a temporary file
|
||||
tmp := filepath.Join(path, "tmp")
|
||||
if f, err := os.Create(tmp); err != nil {
|
||||
return err
|
||||
} else {
|
||||
f.Close()
|
||||
}
|
||||
return os.Remove(tmp)
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
// Copyright 2016 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package debug
|
||||
|
||||
import "runtime/debug"
|
||||
|
||||
// LoudPanic panics in a way that gets all goroutine stacks printed on stderr.
|
||||
func LoudPanic(x interface{}) {
|
||||
debug.SetTraceback("all")
|
||||
panic(x)
|
||||
}
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
// Copyright 2016 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package debug
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"runtime/trace"
|
||||
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
)
|
||||
|
||||
// StartGoTrace turns on tracing, writing to the given file.
|
||||
func (h *HandlerT) StartGoTrace(file string) error {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
if h.traceW != nil {
|
||||
return errors.New("trace already in progress")
|
||||
}
|
||||
f, err := os.Create(expandHome(file))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := trace.Start(f); err != nil {
|
||||
f.Close()
|
||||
return err
|
||||
}
|
||||
h.traceW = f
|
||||
h.traceFile = file
|
||||
log.Info("Go tracing started", "dump", h.traceFile)
|
||||
return nil
|
||||
}
|
||||
|
||||
// StopGoTrace stops an ongoing trace.
|
||||
func (h *HandlerT) StopGoTrace() error {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
trace.Stop()
|
||||
if h.traceW == nil {
|
||||
return errors.New("trace not in progress")
|
||||
}
|
||||
log.Info("Done writing Go trace", "dump", h.traceFile)
|
||||
h.traceW.Close()
|
||||
h.traceW = nil
|
||||
h.traceFile = ""
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
// Copyright 2017 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package ethapi
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
type AddrLocker struct {
|
||||
mu sync.Mutex
|
||||
locks map[common.Address]*sync.Mutex
|
||||
}
|
||||
|
||||
// lock returns the lock of the given address.
|
||||
func (l *AddrLocker) lock(address common.Address) *sync.Mutex {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.locks == nil {
|
||||
l.locks = make(map[common.Address]*sync.Mutex)
|
||||
}
|
||||
if _, ok := l.locks[address]; !ok {
|
||||
l.locks[address] = new(sync.Mutex)
|
||||
}
|
||||
return l.locks[address]
|
||||
}
|
||||
|
||||
// LockAddr locks an account's mutex. This is used to prevent another tx getting the
|
||||
// same nonce until the lock is released. The mutex prevents the (an identical nonce) from
|
||||
// being read again during the time that the first transaction is being signed.
|
||||
func (l *AddrLocker) LockAddr(address common.Address) {
|
||||
l.lock(address).Lock()
|
||||
}
|
||||
|
||||
// UnlockAddr unlocks the mutex of the given account.
|
||||
func (l *AddrLocker) UnlockAddr(address common.Address) {
|
||||
l.lock(address).Unlock()
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,128 +0,0 @@
|
|||
// Copyright 2015 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Package ethapi implements the general Ethereum API functions.
|
||||
package ethapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/bloombits"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
)
|
||||
|
||||
// Backend interface provides the common API services (that are provided by
|
||||
// both full and light clients) with access to necessary functions.
|
||||
type Backend interface {
|
||||
// General Ethereum API
|
||||
SyncProgress() ethereum.SyncProgress
|
||||
|
||||
SuggestGasTipCap(ctx context.Context) (*big.Int, error)
|
||||
FeeHistory(ctx context.Context, blockCount uint64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, error)
|
||||
ChainDb() ethdb.Database
|
||||
AccountManager() *accounts.Manager
|
||||
ExtRPCEnabled() bool
|
||||
RPCGasCap() uint64 // global gas cap for eth_call over rpc: DoS protection
|
||||
RPCEVMTimeout() time.Duration // global timeout for eth_call over rpc: DoS protection
|
||||
RPCTxFeeCap() float64 // global tx fee cap for all transaction related APIs
|
||||
UnprotectedAllowed() bool // allows only for EIP155 transactions.
|
||||
|
||||
// Blockchain API
|
||||
SetHead(number uint64)
|
||||
HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error)
|
||||
HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error)
|
||||
HeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Header, error)
|
||||
CurrentHeader() *types.Header
|
||||
CurrentBlock() *types.Header
|
||||
BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error)
|
||||
BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error)
|
||||
BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Block, error)
|
||||
StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.Header, error)
|
||||
StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error)
|
||||
PendingBlockAndReceipts() (*types.Block, types.Receipts)
|
||||
GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error)
|
||||
GetTd(ctx context.Context, hash common.Hash) *big.Int
|
||||
GetEVM(ctx context.Context, msg *core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config, blockCtx *vm.BlockContext) *vm.EVM
|
||||
SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription
|
||||
SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription
|
||||
SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription
|
||||
|
||||
// Transaction pool API
|
||||
SendTx(ctx context.Context, signedTx *types.Transaction) error
|
||||
GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error)
|
||||
GetPoolTransactions() (types.Transactions, error)
|
||||
GetPoolTransaction(txHash common.Hash) *types.Transaction
|
||||
GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error)
|
||||
Stats() (pending int, queued int)
|
||||
TxPoolContent() (map[common.Address][]*types.Transaction, map[common.Address][]*types.Transaction)
|
||||
TxPoolContentFrom(addr common.Address) ([]*types.Transaction, []*types.Transaction)
|
||||
SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription
|
||||
|
||||
ChainConfig() *params.ChainConfig
|
||||
Engine() consensus.Engine
|
||||
|
||||
// This is copied from filters.Backend
|
||||
// eth/filters needs to be initialized from this backend type, so methods needed by
|
||||
// it must also be included here.
|
||||
GetBody(ctx context.Context, hash common.Hash, number rpc.BlockNumber) (*types.Body, error)
|
||||
GetLogs(ctx context.Context, blockHash common.Hash, number uint64) ([][]*types.Log, error)
|
||||
SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription
|
||||
SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription
|
||||
SubscribePendingLogsEvent(ch chan<- []*types.Log) event.Subscription
|
||||
BloomStatus() (uint64, uint64)
|
||||
ServiceFilter(ctx context.Context, session *bloombits.MatcherSession)
|
||||
}
|
||||
|
||||
func GetAPIs(apiBackend Backend) []rpc.API {
|
||||
nonceLock := new(AddrLocker)
|
||||
return []rpc.API{
|
||||
{
|
||||
Namespace: "eth",
|
||||
Service: NewEthereumAPI(apiBackend),
|
||||
}, {
|
||||
Namespace: "eth",
|
||||
Service: NewBlockChainAPI(apiBackend),
|
||||
}, {
|
||||
Namespace: "eth",
|
||||
Service: NewTransactionAPI(apiBackend, nonceLock),
|
||||
}, {
|
||||
Namespace: "txpool",
|
||||
Service: NewTxPoolAPI(apiBackend),
|
||||
}, {
|
||||
Namespace: "debug",
|
||||
Service: NewDebugAPI(apiBackend),
|
||||
}, {
|
||||
Namespace: "eth",
|
||||
Service: NewEthereumAccountAPI(apiBackend.AccountManager()),
|
||||
}, {
|
||||
Namespace: "personal",
|
||||
Service: NewPersonalAccountAPI(apiBackend, nonceLock),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
// Copyright 2022 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package ethapi
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
)
|
||||
|
||||
// DbGet returns the raw value of a key stored in the database.
|
||||
func (api *DebugAPI) DbGet(key string) (hexutil.Bytes, error) {
|
||||
blob, err := common.ParseHexOrString(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return api.b.ChainDb().Get(blob)
|
||||
}
|
||||
|
||||
// DbAncient retrieves an ancient binary blob from the append-only immutable files.
|
||||
// It is a mapping to the `AncientReaderOp.Ancient` method
|
||||
func (api *DebugAPI) DbAncient(kind string, number uint64) (hexutil.Bytes, error) {
|
||||
return api.b.ChainDb().Ancient(kind, number)
|
||||
}
|
||||
|
||||
// DbAncients returns the ancient item numbers in the ancient store.
|
||||
// It is a mapping to the `AncientReaderOp.Ancients` method
|
||||
func (api *DebugAPI) DbAncients() (uint64, error) {
|
||||
return api.b.ChainDb().Ancients()
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
{
|
||||
"baseFeePerGas": "0x342770c0",
|
||||
"difficulty": "0x20000",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0x47e7c4",
|
||||
"gasUsed": "0x5208",
|
||||
"hash": "0x0da274b315de8e4d5bf8717218ec43540464ef36378cb896469bb731e1d3f3cb",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"miner": "0x0000000000000000000000000000000000000000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0000000000000000",
|
||||
"number": "0x1",
|
||||
"parentHash": "0xbdc7d83b8f876938810462fe8d053263a482e44201e3883d4ae204ff4de7eff5",
|
||||
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
|
||||
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
"size": "0x26a",
|
||||
"stateRoot": "0x92c5c55a698963f5b06e3aee415630f5c48b0760e537af94917ce9c4f42a2e22",
|
||||
"timestamp": "0xa",
|
||||
"totalDifficulty": "0x1",
|
||||
"transactions": [
|
||||
"0x644a31c354391520d00e95b9affbbb010fc79ac268144ab8e28207f4cf51097e"
|
||||
],
|
||||
"transactionsRoot": "0xca0ebcce920d2cdfbf9e1dbe90ed3441a1a576f344bd80e60508da814916f4e7",
|
||||
"uncles": []
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
null
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
{
|
||||
"baseFeePerGas": "0x3b9aca00",
|
||||
"difficulty": "0x20000",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0x47e7c4",
|
||||
"gasUsed": "0x0",
|
||||
"hash": "0xbdc7d83b8f876938810462fe8d053263a482e44201e3883d4ae204ff4de7eff5",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"miner": "0x0000000000000000000000000000000000000000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0000000000000000",
|
||||
"number": "0x0",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
"size": "0x200",
|
||||
"stateRoot": "0xfe168c5e9584a85927212e5bea5304bb7d0d8a893453b4b2c52176a72f585ae2",
|
||||
"timestamp": "0x0",
|
||||
"totalDifficulty": "0x1",
|
||||
"transactions": [],
|
||||
"transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||
"uncles": []
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
{
|
||||
"baseFeePerGas": "0x121a9cca",
|
||||
"difficulty": "0x20000",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0x47e7c4",
|
||||
"gasUsed": "0x5208",
|
||||
"hash": "0xda97ed946e0d502fb898b0ac881bd44da3c7fee5eaf184431e1ec3d361dad17e",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"miner": "0x0000000000000000000000000000000000000000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0000000000000000",
|
||||
"number": "0x9",
|
||||
"parentHash": "0x5abd19c39d9f1c6e52998e135ea14e1fbc5db3fa2a108f4538e238ca5c2e68d7",
|
||||
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
|
||||
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
"size": "0x26a",
|
||||
"stateRoot": "0xbd4aa2c2873df709151075250a8c01c9a14d2b0e2f715dbdd16e0ef8030c2cf0",
|
||||
"timestamp": "0x5a",
|
||||
"totalDifficulty": "0x1",
|
||||
"transactions": [
|
||||
{
|
||||
"blockHash": "0xda97ed946e0d502fb898b0ac881bd44da3c7fee5eaf184431e1ec3d361dad17e",
|
||||
"blockNumber": "0x9",
|
||||
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
|
||||
"gas": "0x5208",
|
||||
"gasPrice": "0x121a9cca",
|
||||
"hash": "0xecd155a61a5734b3efab75924e3ae34026c7c4133d8c2a46122bd03d7d199725",
|
||||
"input": "0x",
|
||||
"nonce": "0x8",
|
||||
"to": "0x0d3ab14bbad3d99f4203bd7a11acb94882050e7e",
|
||||
"transactionIndex": "0x0",
|
||||
"value": "0x3e8",
|
||||
"type": "0x0",
|
||||
"v": "0x1b",
|
||||
"r": "0xc6028b8e983d62fa8542f8a7633fb23cc941be2c897134352d95a7d9b19feafd",
|
||||
"s": "0xeb6adcaaae3bed489c6cce4435f9db05d23a52820c78bd350e31eec65ed809d"
|
||||
}
|
||||
],
|
||||
"transactionsRoot": "0x0767ed8359337dc6a8fdc77fe52db611bed1be87aac73c4556b1bf1dd3d190a5",
|
||||
"uncles": []
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
{
|
||||
"baseFeePerGas": "0xfdc7303",
|
||||
"difficulty": "0x20000",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0x47e7c4",
|
||||
"gasUsed": "0x5208",
|
||||
"hash": "0x97f540a3577c0f645c5dada5da86f38350e8f847e71f21124f917835003e2607",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"miner": "0x0000000000000000000000000000000000000000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0000000000000000",
|
||||
"number": "0xa",
|
||||
"parentHash": "0xda97ed946e0d502fb898b0ac881bd44da3c7fee5eaf184431e1ec3d361dad17e",
|
||||
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
|
||||
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
"size": "0x26a",
|
||||
"stateRoot": "0xbb62872e4023fa8a8b17b9cc37031f4817d9595779748d01cba408b495707a91",
|
||||
"timestamp": "0x64",
|
||||
"totalDifficulty": "0x1",
|
||||
"transactions": [
|
||||
"0x3ee4094ca1e0b07a66dd616a057e081e53144ca7e9685a126fd4dda9ca042644"
|
||||
],
|
||||
"transactionsRoot": "0xb0893d21a4a44dc26a962a6e91abae66df87fb61ac9c60e936aee89c76331445",
|
||||
"uncles": []
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
null
|
||||
|
|
@ -1 +0,0 @@
|
|||
null
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
{
|
||||
"baseFeePerGas": "0x3b9aca00",
|
||||
"difficulty": "0x20000",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0x47e7c4",
|
||||
"gasUsed": "0x0",
|
||||
"hash": "0xbdc7d83b8f876938810462fe8d053263a482e44201e3883d4ae204ff4de7eff5",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"miner": "0x0000000000000000000000000000000000000000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0000000000000000",
|
||||
"number": "0x0",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
"size": "0x200",
|
||||
"stateRoot": "0xfe168c5e9584a85927212e5bea5304bb7d0d8a893453b4b2c52176a72f585ae2",
|
||||
"timestamp": "0x0",
|
||||
"totalDifficulty": "0x1",
|
||||
"transactions": [],
|
||||
"transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||
"uncles": []
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
{
|
||||
"baseFeePerGas": "0x342770c0",
|
||||
"difficulty": "0x20000",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0x47e7c4",
|
||||
"gasUsed": "0x5208",
|
||||
"hash": "0x0da274b315de8e4d5bf8717218ec43540464ef36378cb896469bb731e1d3f3cb",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"miner": "0x0000000000000000000000000000000000000000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0000000000000000",
|
||||
"number": "0x1",
|
||||
"parentHash": "0xbdc7d83b8f876938810462fe8d053263a482e44201e3883d4ae204ff4de7eff5",
|
||||
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
|
||||
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
"size": "0x26a",
|
||||
"stateRoot": "0x92c5c55a698963f5b06e3aee415630f5c48b0760e537af94917ce9c4f42a2e22",
|
||||
"timestamp": "0xa",
|
||||
"totalDifficulty": "0x1",
|
||||
"transactions": [
|
||||
"0x644a31c354391520d00e95b9affbbb010fc79ac268144ab8e28207f4cf51097e"
|
||||
],
|
||||
"transactionsRoot": "0xca0ebcce920d2cdfbf9e1dbe90ed3441a1a576f344bd80e60508da814916f4e7",
|
||||
"uncles": []
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
null
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
{
|
||||
"baseFeePerGas": "0x121a9cca",
|
||||
"difficulty": "0x20000",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0x47e7c4",
|
||||
"gasUsed": "0x5208",
|
||||
"hash": "0xda97ed946e0d502fb898b0ac881bd44da3c7fee5eaf184431e1ec3d361dad17e",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"miner": "0x0000000000000000000000000000000000000000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0000000000000000",
|
||||
"number": "0x9",
|
||||
"parentHash": "0x5abd19c39d9f1c6e52998e135ea14e1fbc5db3fa2a108f4538e238ca5c2e68d7",
|
||||
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
|
||||
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
"size": "0x26a",
|
||||
"stateRoot": "0xbd4aa2c2873df709151075250a8c01c9a14d2b0e2f715dbdd16e0ef8030c2cf0",
|
||||
"timestamp": "0x5a",
|
||||
"totalDifficulty": "0x1",
|
||||
"transactions": [
|
||||
{
|
||||
"blockHash": "0xda97ed946e0d502fb898b0ac881bd44da3c7fee5eaf184431e1ec3d361dad17e",
|
||||
"blockNumber": "0x9",
|
||||
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
|
||||
"gas": "0x5208",
|
||||
"gasPrice": "0x121a9cca",
|
||||
"hash": "0xecd155a61a5734b3efab75924e3ae34026c7c4133d8c2a46122bd03d7d199725",
|
||||
"input": "0x",
|
||||
"nonce": "0x8",
|
||||
"to": "0x0d3ab14bbad3d99f4203bd7a11acb94882050e7e",
|
||||
"transactionIndex": "0x0",
|
||||
"value": "0x3e8",
|
||||
"type": "0x0",
|
||||
"v": "0x1b",
|
||||
"r": "0xc6028b8e983d62fa8542f8a7633fb23cc941be2c897134352d95a7d9b19feafd",
|
||||
"s": "0xeb6adcaaae3bed489c6cce4435f9db05d23a52820c78bd350e31eec65ed809d"
|
||||
}
|
||||
],
|
||||
"transactionsRoot": "0x0767ed8359337dc6a8fdc77fe52db611bed1be87aac73c4556b1bf1dd3d190a5",
|
||||
"uncles": []
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
{
|
||||
"baseFeePerGas": "0xfdc7303",
|
||||
"difficulty": "0x20000",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0x47e7c4",
|
||||
"gasUsed": "0x5208",
|
||||
"hash": "0x97f540a3577c0f645c5dada5da86f38350e8f847e71f21124f917835003e2607",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"miner": "0x0000000000000000000000000000000000000000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0000000000000000",
|
||||
"number": "0xa",
|
||||
"parentHash": "0xda97ed946e0d502fb898b0ac881bd44da3c7fee5eaf184431e1ec3d361dad17e",
|
||||
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
|
||||
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
"size": "0x26a",
|
||||
"stateRoot": "0xbb62872e4023fa8a8b17b9cc37031f4817d9595779748d01cba408b495707a91",
|
||||
"timestamp": "0x64",
|
||||
"totalDifficulty": "0x1",
|
||||
"transactions": [
|
||||
"0x3ee4094ca1e0b07a66dd616a057e081e53144ca7e9685a126fd4dda9ca042644"
|
||||
],
|
||||
"transactionsRoot": "0xb0893d21a4a44dc26a962a6e91abae66df87fb61ac9c60e936aee89c76331445",
|
||||
"uncles": []
|
||||
}
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
{
|
||||
"difficulty": "0x0",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0x0",
|
||||
"gasUsed": "0x0",
|
||||
"hash": null,
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"miner": null,
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": null,
|
||||
"number": "0xb",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
"size": "0x256",
|
||||
"stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"timestamp": "0x2a",
|
||||
"totalDifficulty": null,
|
||||
"transactions": [
|
||||
{
|
||||
"blockHash": "0x6cebd9f966ea686f44b981685e3f0eacea28591a7a86d7fbbe521a86e9f81165",
|
||||
"blockNumber": "0xb",
|
||||
"from": "0x0000000000000000000000000000000000000000",
|
||||
"gas": "0x457",
|
||||
"gasPrice": "0x2b67",
|
||||
"hash": "0x4afee081df5dff7a025964032871f7d4ba4d21baf5f6376a2f4a9f79fc506298",
|
||||
"input": "0x111111",
|
||||
"nonce": "0xb",
|
||||
"to": "0x0d3ab14bbad3d99f4203bd7a11acb94882050e7e",
|
||||
"transactionIndex": "0x0",
|
||||
"value": "0x6f",
|
||||
"type": "0x0",
|
||||
"chainId": "0x7fffffffffffffee",
|
||||
"v": "0x0",
|
||||
"r": "0x0",
|
||||
"s": "0x0"
|
||||
}
|
||||
],
|
||||
"transactionsRoot": "0x98d9f6dd0aa479c0fb448f2627e9f1964aca699fccab8f6e95861547a4699e37",
|
||||
"uncles": [],
|
||||
"withdrawals": [
|
||||
{
|
||||
"index": "0x0",
|
||||
"validatorIndex": "0x1",
|
||||
"address": "0x1234000000000000000000000000000000000000",
|
||||
"amount": "0xa"
|
||||
}
|
||||
],
|
||||
"withdrawalsRoot": "0x73d756269cdfc22e7e17a3548e36f42f750ca06d7e3cd98d1b6d0eb5add9dc84"
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
{
|
||||
"difficulty": "0x0",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0x0",
|
||||
"gasUsed": "0x0",
|
||||
"hash": null,
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"miner": null,
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": null,
|
||||
"number": "0xb",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
"size": "0x256",
|
||||
"stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"timestamp": "0x2a",
|
||||
"totalDifficulty": null,
|
||||
"transactions": [
|
||||
"0x4afee081df5dff7a025964032871f7d4ba4d21baf5f6376a2f4a9f79fc506298"
|
||||
],
|
||||
"transactionsRoot": "0x98d9f6dd0aa479c0fb448f2627e9f1964aca699fccab8f6e95861547a4699e37",
|
||||
"uncles": [],
|
||||
"withdrawals": [
|
||||
{
|
||||
"index": "0x0",
|
||||
"validatorIndex": "0x1",
|
||||
"address": "0x1234000000000000000000000000000000000000",
|
||||
"amount": "0xa"
|
||||
}
|
||||
],
|
||||
"withdrawalsRoot": "0x73d756269cdfc22e7e17a3548e36f42f750ca06d7e3cd98d1b6d0eb5add9dc84"
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
null
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
[
|
||||
{
|
||||
"blobGasPrice": "0x1",
|
||||
"blobGasUsed": "0x20000",
|
||||
"blockHash": "0xe724dfd4349861f4dceef2bc4df086d0a3d88858214f6bee9fcf1bebd1edc2a6",
|
||||
"blockNumber": "0x6",
|
||||
"contractAddress": null,
|
||||
"cumulativeGasUsed": "0x5208",
|
||||
"effectiveGasPrice": "0x1b09d63b",
|
||||
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
|
||||
"gasUsed": "0x5208",
|
||||
"logs": [],
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"status": "0x1",
|
||||
"to": "0x0d3ab14bbad3d99f4203bd7a11acb94882050e7e",
|
||||
"transactionHash": "0xb51ee3d2a89ba5d5623c73133c8d7a6ba9fb41194c17f4302c21b30994a1180f",
|
||||
"transactionIndex": "0x0",
|
||||
"type": "0x3"
|
||||
}
|
||||
]
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
[
|
||||
{
|
||||
"blockHash": "0x1e7dcf3abe8bf05d32367a5dc387caa32578b15871bf8b3cbeedf2d8d530f844",
|
||||
"blockNumber": "0x2",
|
||||
"contractAddress": "0xae9bea628c4ce503dcfd7e305cab4e29e7476592",
|
||||
"cumulativeGasUsed": "0xcf50",
|
||||
"effectiveGasPrice": "0x2db16291",
|
||||
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
|
||||
"gasUsed": "0xcf50",
|
||||
"logs": [],
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"status": "0x1",
|
||||
"to": null,
|
||||
"transactionHash": "0x340e58cda5086495010b571fe25067fecc9954dc4ee3cedece00691fa3f5904a",
|
||||
"transactionIndex": "0x0",
|
||||
"type": "0x0"
|
||||
}
|
||||
]
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
[
|
||||
{
|
||||
"blockHash": "0xffa737e6ce9a9162ffd411dd06169114b3ed5ee9fc1474a2625c92548e4455e0",
|
||||
"blockNumber": "0x4",
|
||||
"contractAddress": null,
|
||||
"cumulativeGasUsed": "0x538d",
|
||||
"effectiveGasPrice": "0x2325c42f",
|
||||
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
|
||||
"gasUsed": "0x538d",
|
||||
"logs": [],
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"status": "0x0",
|
||||
"to": "0x0000000000000000000000000000000000031ec7",
|
||||
"transactionHash": "0xdcde2574628c9d7dff22b9afa19f235959a924ceec65a9df903a517ae91f5c84",
|
||||
"transactionIndex": "0x0",
|
||||
"type": "0x2"
|
||||
}
|
||||
]
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
[
|
||||
{
|
||||
"blockHash": "0x173dcd9d22ce71929cd17e84ea88702a0f84d6244c6898d2a4f48722e494fe9c",
|
||||
"blockNumber": "0x3",
|
||||
"contractAddress": null,
|
||||
"cumulativeGasUsed": "0x5e28",
|
||||
"effectiveGasPrice": "0x281c2585",
|
||||
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
|
||||
"gasUsed": "0x5e28",
|
||||
"logs": [
|
||||
{
|
||||
"address": "0x0000000000000000000000000000000000031ec7",
|
||||
"topics": [
|
||||
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
|
||||
"0x000000000000000000000000703c4b2bd70c169f5717101caee543299fc946c7",
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000003"
|
||||
],
|
||||
"data": "0x000000000000000000000000000000000000000000000000000000000000000d",
|
||||
"blockNumber": "0x3",
|
||||
"transactionHash": "0xeaf3921cbf03ba45bad4e6ab807b196ce3b2a0b5bacc355b6272fa96b11b4287",
|
||||
"transactionIndex": "0x0",
|
||||
"blockHash": "0x173dcd9d22ce71929cd17e84ea88702a0f84d6244c6898d2a4f48722e494fe9c",
|
||||
"logIndex": "0x0",
|
||||
"removed": false
|
||||
}
|
||||
],
|
||||
"logsBloom": "0x00000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000800000000000000008000000000000000000000000000000000020000000080000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000400000000002000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000",
|
||||
"status": "0x1",
|
||||
"to": "0x0000000000000000000000000000000000031ec7",
|
||||
"transactionHash": "0xeaf3921cbf03ba45bad4e6ab807b196ce3b2a0b5bacc355b6272fa96b11b4287",
|
||||
"transactionIndex": "0x0",
|
||||
"type": "0x0"
|
||||
}
|
||||
]
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
[
|
||||
{
|
||||
"blockHash": "0xa8a067b3cb3b9ddc6cfb8317bfd08b266fcf9994fc870c1f7ed394acecfadf39",
|
||||
"blockNumber": "0x1",
|
||||
"contractAddress": null,
|
||||
"cumulativeGasUsed": "0x5208",
|
||||
"effectiveGasPrice": "0x342770c0",
|
||||
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
|
||||
"gasUsed": "0x5208",
|
||||
"logs": [],
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"status": "0x1",
|
||||
"to": "0x0d3ab14bbad3d99f4203bd7a11acb94882050e7e",
|
||||
"transactionHash": "0x644a31c354391520d00e95b9affbbb010fc79ac268144ab8e28207f4cf51097e",
|
||||
"transactionIndex": "0x0",
|
||||
"type": "0x0"
|
||||
}
|
||||
]
|
||||
|
|
@ -1 +0,0 @@
|
|||
null
|
||||
|
|
@ -1 +0,0 @@
|
|||
null
|
||||
|
|
@ -1 +0,0 @@
|
|||
[]
|
||||
|
|
@ -1 +0,0 @@
|
|||
[]
|
||||
|
|
@ -1 +0,0 @@
|
|||
[]
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
[
|
||||
{
|
||||
"blobGasPrice": "0x1",
|
||||
"blobGasUsed": "0x20000",
|
||||
"blockHash": "0xe724dfd4349861f4dceef2bc4df086d0a3d88858214f6bee9fcf1bebd1edc2a6",
|
||||
"blockNumber": "0x6",
|
||||
"contractAddress": null,
|
||||
"cumulativeGasUsed": "0x5208",
|
||||
"effectiveGasPrice": "0x1b09d63b",
|
||||
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
|
||||
"gasUsed": "0x5208",
|
||||
"logs": [],
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"status": "0x1",
|
||||
"to": "0x0d3ab14bbad3d99f4203bd7a11acb94882050e7e",
|
||||
"transactionHash": "0xb51ee3d2a89ba5d5623c73133c8d7a6ba9fb41194c17f4302c21b30994a1180f",
|
||||
"transactionIndex": "0x0",
|
||||
"type": "0x3"
|
||||
}
|
||||
]
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
{
|
||||
"baseFeePerGas": "0x3b9aca00",
|
||||
"difficulty": "0x20000",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0x47e7c4",
|
||||
"gasUsed": "0x0",
|
||||
"hash": "0xbdc7d83b8f876938810462fe8d053263a482e44201e3883d4ae204ff4de7eff5",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"miner": "0x0000000000000000000000000000000000000000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0000000000000000",
|
||||
"number": "0x0",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
"stateRoot": "0xfe168c5e9584a85927212e5bea5304bb7d0d8a893453b4b2c52176a72f585ae2",
|
||||
"timestamp": "0x0",
|
||||
"totalDifficulty": "0x1",
|
||||
"transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
{
|
||||
"baseFeePerGas": "0x342770c0",
|
||||
"difficulty": "0x20000",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0x47e7c4",
|
||||
"gasUsed": "0x5208",
|
||||
"hash": "0x0da274b315de8e4d5bf8717218ec43540464ef36378cb896469bb731e1d3f3cb",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"miner": "0x0000000000000000000000000000000000000000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0000000000000000",
|
||||
"number": "0x1",
|
||||
"parentHash": "0xbdc7d83b8f876938810462fe8d053263a482e44201e3883d4ae204ff4de7eff5",
|
||||
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
|
||||
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
"stateRoot": "0x92c5c55a698963f5b06e3aee415630f5c48b0760e537af94917ce9c4f42a2e22",
|
||||
"timestamp": "0xa",
|
||||
"totalDifficulty": "0x1",
|
||||
"transactionsRoot": "0xca0ebcce920d2cdfbf9e1dbe90ed3441a1a576f344bd80e60508da814916f4e7"
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
null
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
{
|
||||
"baseFeePerGas": "0x121a9cca",
|
||||
"difficulty": "0x20000",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0x47e7c4",
|
||||
"gasUsed": "0x5208",
|
||||
"hash": "0xda97ed946e0d502fb898b0ac881bd44da3c7fee5eaf184431e1ec3d361dad17e",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"miner": "0x0000000000000000000000000000000000000000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0000000000000000",
|
||||
"number": "0x9",
|
||||
"parentHash": "0x5abd19c39d9f1c6e52998e135ea14e1fbc5db3fa2a108f4538e238ca5c2e68d7",
|
||||
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
|
||||
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
"stateRoot": "0xbd4aa2c2873df709151075250a8c01c9a14d2b0e2f715dbdd16e0ef8030c2cf0",
|
||||
"timestamp": "0x5a",
|
||||
"totalDifficulty": "0x1",
|
||||
"transactionsRoot": "0x0767ed8359337dc6a8fdc77fe52db611bed1be87aac73c4556b1bf1dd3d190a5"
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
{
|
||||
"baseFeePerGas": "0xfdc7303",
|
||||
"difficulty": "0x20000",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0x47e7c4",
|
||||
"gasUsed": "0x5208",
|
||||
"hash": "0x97f540a3577c0f645c5dada5da86f38350e8f847e71f21124f917835003e2607",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"miner": "0x0000000000000000000000000000000000000000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0000000000000000",
|
||||
"number": "0xa",
|
||||
"parentHash": "0xda97ed946e0d502fb898b0ac881bd44da3c7fee5eaf184431e1ec3d361dad17e",
|
||||
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
|
||||
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
"stateRoot": "0xbb62872e4023fa8a8b17b9cc37031f4817d9595779748d01cba408b495707a91",
|
||||
"timestamp": "0x64",
|
||||
"totalDifficulty": "0x1",
|
||||
"transactionsRoot": "0xb0893d21a4a44dc26a962a6e91abae66df87fb61ac9c60e936aee89c76331445"
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
null
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
{
|
||||
"baseFeePerGas": "0x3b9aca00",
|
||||
"difficulty": "0x20000",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0x47e7c4",
|
||||
"gasUsed": "0x0",
|
||||
"hash": "0xbdc7d83b8f876938810462fe8d053263a482e44201e3883d4ae204ff4de7eff5",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"miner": "0x0000000000000000000000000000000000000000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0000000000000000",
|
||||
"number": "0x0",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
"stateRoot": "0xfe168c5e9584a85927212e5bea5304bb7d0d8a893453b4b2c52176a72f585ae2",
|
||||
"timestamp": "0x0",
|
||||
"totalDifficulty": "0x1",
|
||||
"transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
{
|
||||
"baseFeePerGas": "0x342770c0",
|
||||
"difficulty": "0x20000",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0x47e7c4",
|
||||
"gasUsed": "0x5208",
|
||||
"hash": "0x0da274b315de8e4d5bf8717218ec43540464ef36378cb896469bb731e1d3f3cb",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"miner": "0x0000000000000000000000000000000000000000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0000000000000000",
|
||||
"number": "0x1",
|
||||
"parentHash": "0xbdc7d83b8f876938810462fe8d053263a482e44201e3883d4ae204ff4de7eff5",
|
||||
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
|
||||
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
"stateRoot": "0x92c5c55a698963f5b06e3aee415630f5c48b0760e537af94917ce9c4f42a2e22",
|
||||
"timestamp": "0xa",
|
||||
"totalDifficulty": "0x1",
|
||||
"transactionsRoot": "0xca0ebcce920d2cdfbf9e1dbe90ed3441a1a576f344bd80e60508da814916f4e7"
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
null
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
{
|
||||
"baseFeePerGas": "0x121a9cca",
|
||||
"difficulty": "0x20000",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0x47e7c4",
|
||||
"gasUsed": "0x5208",
|
||||
"hash": "0xda97ed946e0d502fb898b0ac881bd44da3c7fee5eaf184431e1ec3d361dad17e",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"miner": "0x0000000000000000000000000000000000000000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0000000000000000",
|
||||
"number": "0x9",
|
||||
"parentHash": "0x5abd19c39d9f1c6e52998e135ea14e1fbc5db3fa2a108f4538e238ca5c2e68d7",
|
||||
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
|
||||
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
"stateRoot": "0xbd4aa2c2873df709151075250a8c01c9a14d2b0e2f715dbdd16e0ef8030c2cf0",
|
||||
"timestamp": "0x5a",
|
||||
"totalDifficulty": "0x1",
|
||||
"transactionsRoot": "0x0767ed8359337dc6a8fdc77fe52db611bed1be87aac73c4556b1bf1dd3d190a5"
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
{
|
||||
"baseFeePerGas": "0xfdc7303",
|
||||
"difficulty": "0x20000",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0x47e7c4",
|
||||
"gasUsed": "0x5208",
|
||||
"hash": "0x97f540a3577c0f645c5dada5da86f38350e8f847e71f21124f917835003e2607",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"miner": "0x0000000000000000000000000000000000000000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0000000000000000",
|
||||
"number": "0xa",
|
||||
"parentHash": "0xda97ed946e0d502fb898b0ac881bd44da3c7fee5eaf184431e1ec3d361dad17e",
|
||||
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
|
||||
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
"stateRoot": "0xbb62872e4023fa8a8b17b9cc37031f4817d9595779748d01cba408b495707a91",
|
||||
"timestamp": "0x64",
|
||||
"totalDifficulty": "0x1",
|
||||
"transactionsRoot": "0xb0893d21a4a44dc26a962a6e91abae66df87fb61ac9c60e936aee89c76331445"
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
{
|
||||
"difficulty": "0x0",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0x0",
|
||||
"gasUsed": "0x0",
|
||||
"hash": null,
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"miner": null,
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": null,
|
||||
"number": "0xb",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
"stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"timestamp": "0x2a",
|
||||
"totalDifficulty": null,
|
||||
"transactionsRoot": "0x98d9f6dd0aa479c0fb448f2627e9f1964aca699fccab8f6e95861547a4699e37",
|
||||
"withdrawalsRoot": "0x73d756269cdfc22e7e17a3548e36f42f750ca06d7e3cd98d1b6d0eb5add9dc84"
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
{
|
||||
"blobGasPrice": "0x1",
|
||||
"blobGasUsed": "0x20000",
|
||||
"blockHash": "0xe724dfd4349861f4dceef2bc4df086d0a3d88858214f6bee9fcf1bebd1edc2a6",
|
||||
"blockNumber": "0x6",
|
||||
"contractAddress": null,
|
||||
"cumulativeGasUsed": "0x5208",
|
||||
"effectiveGasPrice": "0x1b09d63b",
|
||||
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
|
||||
"gasUsed": "0x5208",
|
||||
"logs": [],
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"status": "0x1",
|
||||
"to": "0x0d3ab14bbad3d99f4203bd7a11acb94882050e7e",
|
||||
"transactionHash": "0xb51ee3d2a89ba5d5623c73133c8d7a6ba9fb41194c17f4302c21b30994a1180f",
|
||||
"transactionIndex": "0x0",
|
||||
"type": "0x3"
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
{
|
||||
"blockHash": "0x1e7dcf3abe8bf05d32367a5dc387caa32578b15871bf8b3cbeedf2d8d530f844",
|
||||
"blockNumber": "0x2",
|
||||
"contractAddress": "0xae9bea628c4ce503dcfd7e305cab4e29e7476592",
|
||||
"cumulativeGasUsed": "0xcf50",
|
||||
"effectiveGasPrice": "0x2db16291",
|
||||
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
|
||||
"gasUsed": "0xcf50",
|
||||
"logs": [],
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"status": "0x1",
|
||||
"to": null,
|
||||
"transactionHash": "0x340e58cda5086495010b571fe25067fecc9954dc4ee3cedece00691fa3f5904a",
|
||||
"transactionIndex": "0x0",
|
||||
"type": "0x0"
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
{
|
||||
"blockHash": "0x3fadc5bc916018a326732be829a2565b3acb960a8406f0f151a5e1fa971ea7dd",
|
||||
"blockNumber": "0x5",
|
||||
"contractAddress": "0xfdaa97661a584d977b4d3abb5370766ff5b86a18",
|
||||
"cumulativeGasUsed": "0xe01c",
|
||||
"effectiveGasPrice": "0x1ecb3fb4",
|
||||
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
|
||||
"gasUsed": "0xe01c",
|
||||
"logs": [],
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"status": "0x1",
|
||||
"to": null,
|
||||
"transactionHash": "0xb5a1148819cfdfff9bfe70035524fec940eb735d89b76960b97751d01ae2a9f2",
|
||||
"transactionIndex": "0x0",
|
||||
"type": "0x1"
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
{
|
||||
"blockHash": "0xffa737e6ce9a9162ffd411dd06169114b3ed5ee9fc1474a2625c92548e4455e0",
|
||||
"blockNumber": "0x4",
|
||||
"contractAddress": null,
|
||||
"cumulativeGasUsed": "0x538d",
|
||||
"effectiveGasPrice": "0x2325c42f",
|
||||
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
|
||||
"gasUsed": "0x538d",
|
||||
"logs": [],
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"status": "0x0",
|
||||
"to": "0x0000000000000000000000000000000000031ec7",
|
||||
"transactionHash": "0xdcde2574628c9d7dff22b9afa19f235959a924ceec65a9df903a517ae91f5c84",
|
||||
"transactionIndex": "0x0",
|
||||
"type": "0x2"
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
{
|
||||
"blockHash": "0xa8a067b3cb3b9ddc6cfb8317bfd08b266fcf9994fc870c1f7ed394acecfadf39",
|
||||
"blockNumber": "0x1",
|
||||
"contractAddress": null,
|
||||
"cumulativeGasUsed": "0x5208",
|
||||
"effectiveGasPrice": "0x342770c0",
|
||||
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
|
||||
"gasUsed": "0x5208",
|
||||
"logs": [],
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"status": "0x1",
|
||||
"to": "0x0d3ab14bbad3d99f4203bd7a11acb94882050e7e",
|
||||
"transactionHash": "0x644a31c354391520d00e95b9affbbb010fc79ac268144ab8e28207f4cf51097e",
|
||||
"transactionIndex": "0x0",
|
||||
"type": "0x0"
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
null
|
||||
|
|
@ -1 +0,0 @@
|
|||
null
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
{
|
||||
"blockHash": "0x173dcd9d22ce71929cd17e84ea88702a0f84d6244c6898d2a4f48722e494fe9c",
|
||||
"blockNumber": "0x3",
|
||||
"contractAddress": null,
|
||||
"cumulativeGasUsed": "0x5e28",
|
||||
"effectiveGasPrice": "0x281c2585",
|
||||
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
|
||||
"gasUsed": "0x5e28",
|
||||
"logs": [
|
||||
{
|
||||
"address": "0x0000000000000000000000000000000000031ec7",
|
||||
"topics": [
|
||||
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
|
||||
"0x000000000000000000000000703c4b2bd70c169f5717101caee543299fc946c7",
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000003"
|
||||
],
|
||||
"data": "0x000000000000000000000000000000000000000000000000000000000000000d",
|
||||
"blockNumber": "0x3",
|
||||
"transactionHash": "0xeaf3921cbf03ba45bad4e6ab807b196ce3b2a0b5bacc355b6272fa96b11b4287",
|
||||
"transactionIndex": "0x0",
|
||||
"blockHash": "0x173dcd9d22ce71929cd17e84ea88702a0f84d6244c6898d2a4f48722e494fe9c",
|
||||
"logIndex": "0x0",
|
||||
"removed": false
|
||||
}
|
||||
],
|
||||
"logsBloom": "0x00000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000800000000000000008000000000000000000000000000000000020000000080000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000400000000002000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000",
|
||||
"status": "0x1",
|
||||
"to": "0x0000000000000000000000000000000000031ec7",
|
||||
"transactionHash": "0xeaf3921cbf03ba45bad4e6ab807b196ce3b2a0b5bacc355b6272fa96b11b4287",
|
||||
"transactionIndex": "0x0",
|
||||
"type": "0x0"
|
||||
}
|
||||
|
|
@ -1,346 +0,0 @@
|
|||
// Copyright 2021 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package ethapi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
)
|
||||
|
||||
// TransactionArgs represents the arguments to construct a new transaction
|
||||
// or a message call.
|
||||
type TransactionArgs struct {
|
||||
From *common.Address `json:"from"`
|
||||
To *common.Address `json:"to"`
|
||||
Gas *hexutil.Uint64 `json:"gas"`
|
||||
GasPrice *hexutil.Big `json:"gasPrice"`
|
||||
MaxFeePerGas *hexutil.Big `json:"maxFeePerGas"`
|
||||
MaxPriorityFeePerGas *hexutil.Big `json:"maxPriorityFeePerGas"`
|
||||
Value *hexutil.Big `json:"value"`
|
||||
Nonce *hexutil.Uint64 `json:"nonce"`
|
||||
|
||||
// We accept "data" and "input" for backwards-compatibility reasons.
|
||||
// "input" is the newer name and should be preferred by clients.
|
||||
// Issue detail: https://github.com/ethereum/go-ethereum/issues/15628
|
||||
Data *hexutil.Bytes `json:"data"`
|
||||
Input *hexutil.Bytes `json:"input"`
|
||||
|
||||
// Introduced by AccessListTxType transaction.
|
||||
AccessList *types.AccessList `json:"accessList,omitempty"`
|
||||
ChainID *hexutil.Big `json:"chainId,omitempty"`
|
||||
}
|
||||
|
||||
// from retrieves the transaction sender address.
|
||||
func (args *TransactionArgs) from() common.Address {
|
||||
if args.From == nil {
|
||||
return common.Address{}
|
||||
}
|
||||
return *args.From
|
||||
}
|
||||
|
||||
// data retrieves the transaction calldata. Input field is preferred.
|
||||
func (args *TransactionArgs) data() []byte {
|
||||
if args.Input != nil {
|
||||
return *args.Input
|
||||
}
|
||||
if args.Data != nil {
|
||||
return *args.Data
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// setDefaults fills in default values for unspecified tx fields.
|
||||
func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error {
|
||||
if err := args.setFeeDefaults(ctx, b); err != nil {
|
||||
return err
|
||||
}
|
||||
if args.Value == nil {
|
||||
args.Value = new(hexutil.Big)
|
||||
}
|
||||
if args.Nonce == nil {
|
||||
nonce, err := b.GetPoolNonce(ctx, args.from())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
args.Nonce = (*hexutil.Uint64)(&nonce)
|
||||
}
|
||||
if args.Data != nil && args.Input != nil && !bytes.Equal(*args.Data, *args.Input) {
|
||||
return errors.New(`both "data" and "input" are set and not equal. Please use "input" to pass transaction call data`)
|
||||
}
|
||||
if args.To == nil && len(args.data()) == 0 {
|
||||
return errors.New(`contract creation without any data provided`)
|
||||
}
|
||||
// Estimate the gas usage if necessary.
|
||||
if args.Gas == nil {
|
||||
// These fields are immutable during the estimation, safe to
|
||||
// pass the pointer directly.
|
||||
data := args.data()
|
||||
callArgs := TransactionArgs{
|
||||
From: args.From,
|
||||
To: args.To,
|
||||
GasPrice: args.GasPrice,
|
||||
MaxFeePerGas: args.MaxFeePerGas,
|
||||
MaxPriorityFeePerGas: args.MaxPriorityFeePerGas,
|
||||
Value: args.Value,
|
||||
Data: (*hexutil.Bytes)(&data),
|
||||
AccessList: args.AccessList,
|
||||
}
|
||||
pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber)
|
||||
estimated, err := DoEstimateGas(ctx, b, callArgs, pendingBlockNr, nil, b.RPCGasCap())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
args.Gas = &estimated
|
||||
log.Trace("Estimate gas usage automatically", "gas", args.Gas)
|
||||
}
|
||||
// If chain id is provided, ensure it matches the local chain id. Otherwise, set the local
|
||||
// chain id as the default.
|
||||
want := b.ChainConfig().ChainID
|
||||
if args.ChainID != nil {
|
||||
if have := (*big.Int)(args.ChainID); have.Cmp(want) != 0 {
|
||||
return fmt.Errorf("chainId does not match node's (have=%v, want=%v)", have, want)
|
||||
}
|
||||
} else {
|
||||
args.ChainID = (*hexutil.Big)(want)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// setFeeDefaults fills in default fee values for unspecified tx fields.
|
||||
func (args *TransactionArgs) setFeeDefaults(ctx context.Context, b Backend) error {
|
||||
// If both gasPrice and at least one of the EIP-1559 fee parameters are specified, error.
|
||||
if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) {
|
||||
return errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
|
||||
}
|
||||
// If the tx has completely specified a fee mechanism, no default is needed.
|
||||
// This allows users who are not yet synced past London to get defaults for
|
||||
// other tx values. See https://github.com/ethereum/go-ethereum/pull/23274
|
||||
// for more information.
|
||||
eip1559ParamsSet := args.MaxFeePerGas != nil && args.MaxPriorityFeePerGas != nil
|
||||
|
||||
// Sanity check the EIP-1559 fee parameters if present.
|
||||
if args.GasPrice == nil && eip1559ParamsSet {
|
||||
if args.MaxFeePerGas.ToInt().Sign() == 0 {
|
||||
return errors.New("maxFeePerGas must be non-zero")
|
||||
}
|
||||
if args.MaxFeePerGas.ToInt().Cmp(args.MaxPriorityFeePerGas.ToInt()) < 0 {
|
||||
return fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", args.MaxFeePerGas, args.MaxPriorityFeePerGas)
|
||||
}
|
||||
return nil // No need to set anything, user already set MaxFeePerGas and MaxPriorityFeePerGas
|
||||
}
|
||||
// Sanity check the non-EIP-1559 fee parameters.
|
||||
head := b.CurrentHeader()
|
||||
isLondon := b.ChainConfig().IsLondon(head.Number)
|
||||
if args.GasPrice != nil && !eip1559ParamsSet {
|
||||
// Zero gas-price is not allowed after London fork
|
||||
if args.GasPrice.ToInt().Sign() == 0 && isLondon {
|
||||
return errors.New("gasPrice must be non-zero after london fork")
|
||||
}
|
||||
return nil // No need to set anything, user already set GasPrice
|
||||
}
|
||||
|
||||
// Now attempt to fill in default value depending on whether London is active or not.
|
||||
if isLondon {
|
||||
// London is active, set maxPriorityFeePerGas and maxFeePerGas.
|
||||
if err := args.setLondonFeeDefaults(ctx, head, b); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil {
|
||||
return errors.New("maxFeePerGas and maxPriorityFeePerGas are not valid before London is active")
|
||||
}
|
||||
// London not active, set gas price.
|
||||
price, err := b.SuggestGasTipCap(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
args.GasPrice = (*hexutil.Big)(price)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// setLondonFeeDefaults fills in reasonable default fee values for unspecified fields.
|
||||
func (args *TransactionArgs) setLondonFeeDefaults(ctx context.Context, head *types.Header, b Backend) error {
|
||||
// Set maxPriorityFeePerGas if it is missing.
|
||||
if args.MaxPriorityFeePerGas == nil {
|
||||
tip, err := b.SuggestGasTipCap(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
args.MaxPriorityFeePerGas = (*hexutil.Big)(tip)
|
||||
}
|
||||
// Set maxFeePerGas if it is missing.
|
||||
if args.MaxFeePerGas == nil {
|
||||
// Set the max fee to be 2 times larger than the previous block's base fee.
|
||||
// The additional slack allows the tx to not become invalidated if the base
|
||||
// fee is rising.
|
||||
val := new(big.Int).Add(
|
||||
args.MaxPriorityFeePerGas.ToInt(),
|
||||
new(big.Int).Mul(head.BaseFee, big.NewInt(2)),
|
||||
)
|
||||
args.MaxFeePerGas = (*hexutil.Big)(val)
|
||||
}
|
||||
// Both EIP-1559 fee parameters are now set; sanity check them.
|
||||
if args.MaxFeePerGas.ToInt().Cmp(args.MaxPriorityFeePerGas.ToInt()) < 0 {
|
||||
return fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", args.MaxFeePerGas, args.MaxPriorityFeePerGas)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ToMessage converts the transaction arguments to the Message type used by the
|
||||
// core evm. This method is used in calls and traces that do not require a real
|
||||
// live transaction.
|
||||
func (args *TransactionArgs) ToMessage(globalGasCap uint64, baseFee *big.Int) (*core.Message, error) {
|
||||
// Reject invalid combinations of pre- and post-1559 fee styles
|
||||
if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) {
|
||||
return nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
|
||||
}
|
||||
// Set sender address or use zero address if none specified.
|
||||
addr := args.from()
|
||||
|
||||
// Set default gas & gas price if none were set
|
||||
gas := globalGasCap
|
||||
if gas == 0 {
|
||||
gas = uint64(math.MaxUint64 / 2)
|
||||
}
|
||||
if args.Gas != nil {
|
||||
gas = uint64(*args.Gas)
|
||||
}
|
||||
if globalGasCap != 0 && globalGasCap < gas {
|
||||
log.Warn("Caller gas above allowance, capping", "requested", gas, "cap", globalGasCap)
|
||||
gas = globalGasCap
|
||||
}
|
||||
var (
|
||||
gasPrice *big.Int
|
||||
gasFeeCap *big.Int
|
||||
gasTipCap *big.Int
|
||||
)
|
||||
if baseFee == nil {
|
||||
// If there's no basefee, then it must be a non-1559 execution
|
||||
gasPrice = new(big.Int)
|
||||
if args.GasPrice != nil {
|
||||
gasPrice = args.GasPrice.ToInt()
|
||||
}
|
||||
gasFeeCap, gasTipCap = gasPrice, gasPrice
|
||||
} else {
|
||||
// A basefee is provided, necessitating 1559-type execution
|
||||
if args.GasPrice != nil {
|
||||
// User specified the legacy gas field, convert to 1559 gas typing
|
||||
gasPrice = args.GasPrice.ToInt()
|
||||
gasFeeCap, gasTipCap = gasPrice, gasPrice
|
||||
} else {
|
||||
// User specified 1559 gas fields (or none), use those
|
||||
gasFeeCap = new(big.Int)
|
||||
if args.MaxFeePerGas != nil {
|
||||
gasFeeCap = args.MaxFeePerGas.ToInt()
|
||||
}
|
||||
gasTipCap = new(big.Int)
|
||||
if args.MaxPriorityFeePerGas != nil {
|
||||
gasTipCap = args.MaxPriorityFeePerGas.ToInt()
|
||||
}
|
||||
// Backfill the legacy gasPrice for EVM execution, unless we're all zeroes
|
||||
gasPrice = new(big.Int)
|
||||
if gasFeeCap.BitLen() > 0 || gasTipCap.BitLen() > 0 {
|
||||
gasPrice = math.BigMin(new(big.Int).Add(gasTipCap, baseFee), gasFeeCap)
|
||||
}
|
||||
}
|
||||
}
|
||||
value := new(big.Int)
|
||||
if args.Value != nil {
|
||||
value = args.Value.ToInt()
|
||||
}
|
||||
data := args.data()
|
||||
var accessList types.AccessList
|
||||
if args.AccessList != nil {
|
||||
accessList = *args.AccessList
|
||||
}
|
||||
msg := &core.Message{
|
||||
From: addr,
|
||||
To: args.To,
|
||||
Value: value,
|
||||
GasLimit: gas,
|
||||
GasPrice: gasPrice,
|
||||
GasFeeCap: gasFeeCap,
|
||||
GasTipCap: gasTipCap,
|
||||
Data: data,
|
||||
AccessList: accessList,
|
||||
SkipAccountChecks: true,
|
||||
}
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
// toTransaction converts the arguments to a transaction.
|
||||
// This assumes that setDefaults has been called.
|
||||
func (args *TransactionArgs) toTransaction() *types.Transaction {
|
||||
var data types.TxData
|
||||
switch {
|
||||
case args.MaxFeePerGas != nil:
|
||||
al := types.AccessList{}
|
||||
if args.AccessList != nil {
|
||||
al = *args.AccessList
|
||||
}
|
||||
data = &types.DynamicFeeTx{
|
||||
To: args.To,
|
||||
ChainID: (*big.Int)(args.ChainID),
|
||||
Nonce: uint64(*args.Nonce),
|
||||
Gas: uint64(*args.Gas),
|
||||
GasFeeCap: (*big.Int)(args.MaxFeePerGas),
|
||||
GasTipCap: (*big.Int)(args.MaxPriorityFeePerGas),
|
||||
Value: (*big.Int)(args.Value),
|
||||
Data: args.data(),
|
||||
AccessList: al,
|
||||
}
|
||||
case args.AccessList != nil:
|
||||
data = &types.AccessListTx{
|
||||
To: args.To,
|
||||
ChainID: (*big.Int)(args.ChainID),
|
||||
Nonce: uint64(*args.Nonce),
|
||||
Gas: uint64(*args.Gas),
|
||||
GasPrice: (*big.Int)(args.GasPrice),
|
||||
Value: (*big.Int)(args.Value),
|
||||
Data: args.data(),
|
||||
AccessList: *args.AccessList,
|
||||
}
|
||||
default:
|
||||
data = &types.LegacyTx{
|
||||
To: args.To,
|
||||
Nonce: uint64(*args.Nonce),
|
||||
Gas: uint64(*args.Gas),
|
||||
GasPrice: (*big.Int)(args.GasPrice),
|
||||
Value: (*big.Int)(args.Value),
|
||||
Data: args.data(),
|
||||
}
|
||||
}
|
||||
return types.NewTx(data)
|
||||
}
|
||||
|
||||
// ToTransaction converts the arguments to a transaction.
|
||||
// This assumes that setDefaults has been called.
|
||||
func (args *TransactionArgs) ToTransaction() *types.Transaction {
|
||||
return args.toTransaction()
|
||||
}
|
||||
|
|
@ -1,367 +0,0 @@
|
|||
// Copyright 2022 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package ethapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/bloombits"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
)
|
||||
|
||||
// TestSetFeeDefaults tests the logic for filling in default fee values works as expected.
|
||||
func TestSetFeeDefaults(t *testing.T) {
|
||||
type test struct {
|
||||
name string
|
||||
isLondon bool
|
||||
in *TransactionArgs
|
||||
want *TransactionArgs
|
||||
err error
|
||||
}
|
||||
|
||||
var (
|
||||
b = newBackendMock()
|
||||
zero = (*hexutil.Big)(big.NewInt(0))
|
||||
fortytwo = (*hexutil.Big)(big.NewInt(42))
|
||||
maxFee = (*hexutil.Big)(new(big.Int).Add(new(big.Int).Mul(b.current.BaseFee, big.NewInt(2)), fortytwo.ToInt()))
|
||||
al = &types.AccessList{types.AccessTuple{Address: common.Address{0xaa}, StorageKeys: []common.Hash{{0x01}}}}
|
||||
)
|
||||
|
||||
tests := []test{
|
||||
// Legacy txs
|
||||
{
|
||||
"legacy tx pre-London",
|
||||
false,
|
||||
&TransactionArgs{},
|
||||
&TransactionArgs{GasPrice: fortytwo},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"legacy tx pre-London with zero price",
|
||||
false,
|
||||
&TransactionArgs{GasPrice: zero},
|
||||
&TransactionArgs{GasPrice: zero},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"legacy tx post-London, explicit gas price",
|
||||
true,
|
||||
&TransactionArgs{GasPrice: fortytwo},
|
||||
&TransactionArgs{GasPrice: fortytwo},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"legacy tx post-London with zero price",
|
||||
true,
|
||||
&TransactionArgs{GasPrice: zero},
|
||||
nil,
|
||||
errors.New("gasPrice must be non-zero after london fork"),
|
||||
},
|
||||
|
||||
// Access list txs
|
||||
{
|
||||
"access list tx pre-London",
|
||||
false,
|
||||
&TransactionArgs{AccessList: al},
|
||||
&TransactionArgs{AccessList: al, GasPrice: fortytwo},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"access list tx post-London, explicit gas price",
|
||||
false,
|
||||
&TransactionArgs{AccessList: al, GasPrice: fortytwo},
|
||||
&TransactionArgs{AccessList: al, GasPrice: fortytwo},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"access list tx post-London",
|
||||
true,
|
||||
&TransactionArgs{AccessList: al},
|
||||
&TransactionArgs{AccessList: al, MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"access list tx post-London, only max fee",
|
||||
true,
|
||||
&TransactionArgs{AccessList: al, MaxFeePerGas: maxFee},
|
||||
&TransactionArgs{AccessList: al, MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"access list tx post-London, only priority fee",
|
||||
true,
|
||||
&TransactionArgs{AccessList: al, MaxFeePerGas: maxFee},
|
||||
&TransactionArgs{AccessList: al, MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo},
|
||||
nil,
|
||||
},
|
||||
|
||||
// Dynamic fee txs
|
||||
{
|
||||
"dynamic tx post-London",
|
||||
true,
|
||||
&TransactionArgs{},
|
||||
&TransactionArgs{MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"dynamic tx post-London, only max fee",
|
||||
true,
|
||||
&TransactionArgs{MaxFeePerGas: maxFee},
|
||||
&TransactionArgs{MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"dynamic tx post-London, only priority fee",
|
||||
true,
|
||||
&TransactionArgs{MaxFeePerGas: maxFee},
|
||||
&TransactionArgs{MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"dynamic fee tx pre-London, maxFee set",
|
||||
false,
|
||||
&TransactionArgs{MaxFeePerGas: maxFee},
|
||||
nil,
|
||||
errors.New("maxFeePerGas and maxPriorityFeePerGas are not valid before London is active"),
|
||||
},
|
||||
{
|
||||
"dynamic fee tx pre-London, priorityFee set",
|
||||
false,
|
||||
&TransactionArgs{MaxPriorityFeePerGas: fortytwo},
|
||||
nil,
|
||||
errors.New("maxFeePerGas and maxPriorityFeePerGas are not valid before London is active"),
|
||||
},
|
||||
{
|
||||
"dynamic fee tx, maxFee < priorityFee",
|
||||
true,
|
||||
&TransactionArgs{MaxFeePerGas: maxFee, MaxPriorityFeePerGas: (*hexutil.Big)(big.NewInt(1000))},
|
||||
nil,
|
||||
errors.New("maxFeePerGas (0x3e) < maxPriorityFeePerGas (0x3e8)"),
|
||||
},
|
||||
{
|
||||
"dynamic fee tx, maxFee < priorityFee while setting default",
|
||||
true,
|
||||
&TransactionArgs{MaxFeePerGas: (*hexutil.Big)(big.NewInt(7))},
|
||||
nil,
|
||||
errors.New("maxFeePerGas (0x7) < maxPriorityFeePerGas (0x2a)"),
|
||||
},
|
||||
{
|
||||
"dynamic fee tx post-London, explicit gas price",
|
||||
true,
|
||||
&TransactionArgs{MaxFeePerGas: zero, MaxPriorityFeePerGas: zero},
|
||||
nil,
|
||||
errors.New("maxFeePerGas must be non-zero"),
|
||||
},
|
||||
|
||||
// Misc
|
||||
{
|
||||
"set all fee parameters",
|
||||
false,
|
||||
&TransactionArgs{GasPrice: fortytwo, MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo},
|
||||
nil,
|
||||
errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified"),
|
||||
},
|
||||
{
|
||||
"set gas price and maxPriorityFee",
|
||||
false,
|
||||
&TransactionArgs{GasPrice: fortytwo, MaxPriorityFeePerGas: fortytwo},
|
||||
nil,
|
||||
errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified"),
|
||||
},
|
||||
{
|
||||
"set gas price and maxFee",
|
||||
true,
|
||||
&TransactionArgs{GasPrice: fortytwo, MaxFeePerGas: maxFee},
|
||||
nil,
|
||||
errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified"),
|
||||
},
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
for i, test := range tests {
|
||||
if test.isLondon {
|
||||
b.activateLondon()
|
||||
} else {
|
||||
b.deactivateLondon()
|
||||
}
|
||||
got := test.in
|
||||
err := got.setFeeDefaults(ctx, b)
|
||||
if err != nil && err.Error() == test.err.Error() {
|
||||
// Test threw expected error.
|
||||
continue
|
||||
} else if err != nil {
|
||||
t.Fatalf("test %d (%s): unexpected error: %s", i, test.name, err)
|
||||
}
|
||||
if !reflect.DeepEqual(got, test.want) {
|
||||
t.Fatalf("test %d (%s): did not fill defaults as expected: (got: %v, want: %v)", i, test.name, got, test.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type backendMock struct {
|
||||
current *types.Header
|
||||
config *params.ChainConfig
|
||||
}
|
||||
|
||||
func newBackendMock() *backendMock {
|
||||
config := ¶ms.ChainConfig{
|
||||
ChainID: big.NewInt(42),
|
||||
HomesteadBlock: big.NewInt(0),
|
||||
DAOForkBlock: nil,
|
||||
DAOForkSupport: true,
|
||||
EIP150Block: big.NewInt(0),
|
||||
EIP155Block: big.NewInt(0),
|
||||
EIP158Block: big.NewInt(0),
|
||||
ByzantiumBlock: big.NewInt(0),
|
||||
ConstantinopleBlock: big.NewInt(0),
|
||||
PetersburgBlock: big.NewInt(0),
|
||||
IstanbulBlock: big.NewInt(0),
|
||||
MuirGlacierBlock: big.NewInt(0),
|
||||
BerlinBlock: big.NewInt(0),
|
||||
LondonBlock: big.NewInt(1000),
|
||||
}
|
||||
return &backendMock{
|
||||
current: &types.Header{
|
||||
Difficulty: big.NewInt(10000000000),
|
||||
Number: big.NewInt(1100),
|
||||
GasLimit: 8_000_000,
|
||||
GasUsed: 8_000_000,
|
||||
Time: 555,
|
||||
Extra: make([]byte, 32),
|
||||
BaseFee: big.NewInt(10),
|
||||
},
|
||||
config: config,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *backendMock) activateLondon() {
|
||||
b.current.Number = big.NewInt(1100)
|
||||
}
|
||||
|
||||
func (b *backendMock) deactivateLondon() {
|
||||
b.current.Number = big.NewInt(900)
|
||||
}
|
||||
func (b *backendMock) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
|
||||
return big.NewInt(42), nil
|
||||
}
|
||||
func (b *backendMock) CurrentHeader() *types.Header { return b.current }
|
||||
func (b *backendMock) ChainConfig() *params.ChainConfig { return b.config }
|
||||
|
||||
// Other methods needed to implement Backend interface.
|
||||
func (b *backendMock) SyncProgress() ethereum.SyncProgress { return ethereum.SyncProgress{} }
|
||||
func (b *backendMock) FeeHistory(ctx context.Context, blockCount uint64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, error) {
|
||||
return nil, nil, nil, nil, nil
|
||||
}
|
||||
func (b *backendMock) ChainDb() ethdb.Database { return nil }
|
||||
func (b *backendMock) AccountManager() *accounts.Manager { return nil }
|
||||
func (b *backendMock) ExtRPCEnabled() bool { return false }
|
||||
func (b *backendMock) RPCGasCap() uint64 { return 0 }
|
||||
func (b *backendMock) RPCEVMTimeout() time.Duration { return time.Second }
|
||||
func (b *backendMock) RPCTxFeeCap() float64 { return 0 }
|
||||
func (b *backendMock) UnprotectedAllowed() bool { return false }
|
||||
func (b *backendMock) SetHead(number uint64) {}
|
||||
func (b *backendMock) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (b *backendMock) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (b *backendMock) HeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Header, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (b *backendMock) CurrentBlock() *types.Header { return nil }
|
||||
func (b *backendMock) BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (b *backendMock) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (b *backendMock) BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Block, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (b *backendMock) GetBody(ctx context.Context, hash common.Hash, number rpc.BlockNumber) (*types.Body, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (b *backendMock) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.Header, error) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
func (b *backendMock) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
func (b *backendMock) PendingBlockAndReceipts() (*types.Block, types.Receipts) { return nil, nil }
|
||||
func (b *backendMock) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (b *backendMock) GetLogs(ctx context.Context, blockHash common.Hash, number uint64) ([][]*types.Log, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (b *backendMock) GetTd(ctx context.Context, hash common.Hash) *big.Int { return nil }
|
||||
func (b *backendMock) GetEVM(ctx context.Context, msg *core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config, blockCtx *vm.BlockContext) *vm.EVM {
|
||||
return nil
|
||||
}
|
||||
func (b *backendMock) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription { return nil }
|
||||
func (b *backendMock) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription {
|
||||
return nil
|
||||
}
|
||||
func (b *backendMock) SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription {
|
||||
return nil
|
||||
}
|
||||
func (b *backendMock) SendTx(ctx context.Context, signedTx *types.Transaction) error { return nil }
|
||||
func (b *backendMock) GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) {
|
||||
return nil, [32]byte{}, 0, 0, nil
|
||||
}
|
||||
func (b *backendMock) GetPoolTransactions() (types.Transactions, error) { return nil, nil }
|
||||
func (b *backendMock) GetPoolTransaction(txHash common.Hash) *types.Transaction { return nil }
|
||||
func (b *backendMock) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) {
|
||||
return 0, nil
|
||||
}
|
||||
func (b *backendMock) Stats() (pending int, queued int) { return 0, 0 }
|
||||
func (b *backendMock) TxPoolContent() (map[common.Address][]*types.Transaction, map[common.Address][]*types.Transaction) {
|
||||
return nil, nil
|
||||
}
|
||||
func (b *backendMock) TxPoolContentFrom(addr common.Address) ([]*types.Transaction, []*types.Transaction) {
|
||||
return nil, nil
|
||||
}
|
||||
func (b *backendMock) SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription { return nil }
|
||||
func (b *backendMock) BloomStatus() (uint64, uint64) { return 0, 0 }
|
||||
func (b *backendMock) ServiceFilter(ctx context.Context, session *bloombits.MatcherSession) {}
|
||||
func (b *backendMock) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription { return nil }
|
||||
func (b *backendMock) SubscribePendingLogsEvent(ch chan<- []*types.Log) event.Subscription {
|
||||
return nil
|
||||
}
|
||||
func (b *backendMock) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *backendMock) Engine() consensus.Engine { return nil }
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
// Copyright 2022 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package flags
|
||||
|
||||
import "github.com/urfave/cli/v2"
|
||||
|
||||
const (
|
||||
EthCategory = "ETHEREUM"
|
||||
LightCategory = "LIGHT CLIENT"
|
||||
DevCategory = "DEVELOPER CHAIN"
|
||||
StateCategory = "STATE HISTORY MANAGEMENT"
|
||||
TxPoolCategory = "TRANSACTION POOL (EVM)"
|
||||
BlobPoolCategory = "TRANSACTION POOL (BLOB)"
|
||||
PerfCategory = "PERFORMANCE TUNING"
|
||||
AccountCategory = "ACCOUNT"
|
||||
APICategory = "API AND CONSOLE"
|
||||
NetworkingCategory = "NETWORKING"
|
||||
MinerCategory = "MINER"
|
||||
GasPriceCategory = "GAS PRICE ORACLE"
|
||||
VMCategory = "VIRTUAL MACHINE"
|
||||
LoggingCategory = "LOGGING AND DEBUGGING"
|
||||
MetricsCategory = "METRICS AND STATS"
|
||||
MiscCategory = "MISC"
|
||||
TestingCategory = "TESTING"
|
||||
DeprecatedCategory = "ALIASED (deprecated)"
|
||||
)
|
||||
|
||||
func init() {
|
||||
cli.HelpFlag.(*cli.BoolFlag).Category = MiscCategory
|
||||
cli.VersionFlag.(*cli.BoolFlag).Category = MiscCategory
|
||||
}
|
||||
|
|
@ -1,377 +0,0 @@
|
|||
// Copyright 2015 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package flags
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// DirectoryString is custom type which is registered in the flags library which cli uses for
|
||||
// argument parsing. This allows us to expand Value to an absolute path when
|
||||
// the argument is parsed
|
||||
type DirectoryString string
|
||||
|
||||
func (s *DirectoryString) String() string {
|
||||
return string(*s)
|
||||
}
|
||||
|
||||
func (s *DirectoryString) Set(value string) error {
|
||||
*s = DirectoryString(expandPath(value))
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
_ cli.Flag = (*DirectoryFlag)(nil)
|
||||
_ cli.RequiredFlag = (*DirectoryFlag)(nil)
|
||||
_ cli.VisibleFlag = (*DirectoryFlag)(nil)
|
||||
_ cli.DocGenerationFlag = (*DirectoryFlag)(nil)
|
||||
_ cli.CategorizableFlag = (*DirectoryFlag)(nil)
|
||||
)
|
||||
|
||||
// DirectoryFlag is custom cli.Flag type which expand the received string to an absolute path.
|
||||
// e.g. ~/.ethereum -> /home/username/.ethereum
|
||||
type DirectoryFlag struct {
|
||||
Name string
|
||||
|
||||
Category string
|
||||
DefaultText string
|
||||
Usage string
|
||||
|
||||
Required bool
|
||||
Hidden bool
|
||||
HasBeenSet bool
|
||||
|
||||
Value DirectoryString
|
||||
|
||||
Aliases []string
|
||||
EnvVars []string
|
||||
}
|
||||
|
||||
// For cli.Flag:
|
||||
|
||||
func (f *DirectoryFlag) Names() []string { return append([]string{f.Name}, f.Aliases...) }
|
||||
func (f *DirectoryFlag) IsSet() bool { return f.HasBeenSet }
|
||||
func (f *DirectoryFlag) String() string { return cli.FlagStringer(f) }
|
||||
|
||||
// Apply called by cli library, grabs variable from environment (if in env)
|
||||
// and adds variable to flag set for parsing.
|
||||
func (f *DirectoryFlag) Apply(set *flag.FlagSet) error {
|
||||
for _, envVar := range f.EnvVars {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if value, found := syscall.Getenv(envVar); found {
|
||||
f.Value.Set(value)
|
||||
f.HasBeenSet = true
|
||||
break
|
||||
}
|
||||
}
|
||||
eachName(f, func(name string) {
|
||||
set.Var(&f.Value, f.Name, f.Usage)
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// For cli.RequiredFlag:
|
||||
|
||||
func (f *DirectoryFlag) IsRequired() bool { return f.Required }
|
||||
|
||||
// For cli.VisibleFlag:
|
||||
|
||||
func (f *DirectoryFlag) IsVisible() bool { return !f.Hidden }
|
||||
|
||||
// For cli.CategorizableFlag:
|
||||
|
||||
func (f *DirectoryFlag) GetCategory() string { return f.Category }
|
||||
|
||||
// For cli.DocGenerationFlag:
|
||||
|
||||
func (f *DirectoryFlag) TakesValue() bool { return true }
|
||||
func (f *DirectoryFlag) GetUsage() string { return f.Usage }
|
||||
func (f *DirectoryFlag) GetValue() string { return f.Value.String() }
|
||||
func (f *DirectoryFlag) GetEnvVars() []string { return f.EnvVars }
|
||||
|
||||
func (f *DirectoryFlag) GetDefaultText() string {
|
||||
if f.DefaultText != "" {
|
||||
return f.DefaultText
|
||||
}
|
||||
return f.GetValue()
|
||||
}
|
||||
|
||||
type TextMarshaler interface {
|
||||
encoding.TextMarshaler
|
||||
encoding.TextUnmarshaler
|
||||
}
|
||||
|
||||
// textMarshalerVal turns a TextMarshaler into a flag.Value
|
||||
type textMarshalerVal struct {
|
||||
v TextMarshaler
|
||||
}
|
||||
|
||||
func (v textMarshalerVal) String() string {
|
||||
if v.v == nil {
|
||||
return ""
|
||||
}
|
||||
text, _ := v.v.MarshalText()
|
||||
return string(text)
|
||||
}
|
||||
|
||||
func (v textMarshalerVal) Set(s string) error {
|
||||
return v.v.UnmarshalText([]byte(s))
|
||||
}
|
||||
|
||||
var (
|
||||
_ cli.Flag = (*TextMarshalerFlag)(nil)
|
||||
_ cli.RequiredFlag = (*TextMarshalerFlag)(nil)
|
||||
_ cli.VisibleFlag = (*TextMarshalerFlag)(nil)
|
||||
_ cli.DocGenerationFlag = (*TextMarshalerFlag)(nil)
|
||||
_ cli.CategorizableFlag = (*TextMarshalerFlag)(nil)
|
||||
)
|
||||
|
||||
// TextMarshalerFlag wraps a TextMarshaler value.
|
||||
type TextMarshalerFlag struct {
|
||||
Name string
|
||||
|
||||
Category string
|
||||
DefaultText string
|
||||
Usage string
|
||||
|
||||
Required bool
|
||||
Hidden bool
|
||||
HasBeenSet bool
|
||||
|
||||
Value TextMarshaler
|
||||
|
||||
Aliases []string
|
||||
EnvVars []string
|
||||
}
|
||||
|
||||
// For cli.Flag:
|
||||
|
||||
func (f *TextMarshalerFlag) Names() []string { return append([]string{f.Name}, f.Aliases...) }
|
||||
func (f *TextMarshalerFlag) IsSet() bool { return f.HasBeenSet }
|
||||
func (f *TextMarshalerFlag) String() string { return cli.FlagStringer(f) }
|
||||
|
||||
func (f *TextMarshalerFlag) Apply(set *flag.FlagSet) error {
|
||||
for _, envVar := range f.EnvVars {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if value, found := syscall.Getenv(envVar); found {
|
||||
if err := f.Value.UnmarshalText([]byte(value)); err != nil {
|
||||
return fmt.Errorf("could not parse %q from environment variable %q for flag %s: %s", value, envVar, f.Name, err)
|
||||
}
|
||||
f.HasBeenSet = true
|
||||
break
|
||||
}
|
||||
}
|
||||
eachName(f, func(name string) {
|
||||
set.Var(textMarshalerVal{f.Value}, f.Name, f.Usage)
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// For cli.RequiredFlag:
|
||||
|
||||
func (f *TextMarshalerFlag) IsRequired() bool { return f.Required }
|
||||
|
||||
// For cli.VisibleFlag:
|
||||
|
||||
func (f *TextMarshalerFlag) IsVisible() bool { return !f.Hidden }
|
||||
|
||||
// For cli.CategorizableFlag:
|
||||
|
||||
func (f *TextMarshalerFlag) GetCategory() string { return f.Category }
|
||||
|
||||
// For cli.DocGenerationFlag:
|
||||
|
||||
func (f *TextMarshalerFlag) TakesValue() bool { return true }
|
||||
func (f *TextMarshalerFlag) GetUsage() string { return f.Usage }
|
||||
func (f *TextMarshalerFlag) GetEnvVars() []string { return f.EnvVars }
|
||||
|
||||
func (f *TextMarshalerFlag) GetValue() string {
|
||||
t, err := f.Value.MarshalText()
|
||||
if err != nil {
|
||||
return "(ERR: " + err.Error() + ")"
|
||||
}
|
||||
return string(t)
|
||||
}
|
||||
|
||||
func (f *TextMarshalerFlag) GetDefaultText() string {
|
||||
if f.DefaultText != "" {
|
||||
return f.DefaultText
|
||||
}
|
||||
return f.GetValue()
|
||||
}
|
||||
|
||||
// GlobalTextMarshaler returns the value of a TextMarshalerFlag from the global flag set.
|
||||
func GlobalTextMarshaler(ctx *cli.Context, name string) TextMarshaler {
|
||||
val := ctx.Generic(name)
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
return val.(textMarshalerVal).v
|
||||
}
|
||||
|
||||
var (
|
||||
_ cli.Flag = (*BigFlag)(nil)
|
||||
_ cli.RequiredFlag = (*BigFlag)(nil)
|
||||
_ cli.VisibleFlag = (*BigFlag)(nil)
|
||||
_ cli.DocGenerationFlag = (*BigFlag)(nil)
|
||||
_ cli.CategorizableFlag = (*BigFlag)(nil)
|
||||
)
|
||||
|
||||
// BigFlag is a command line flag that accepts 256 bit big integers in decimal or
|
||||
// hexadecimal syntax.
|
||||
type BigFlag struct {
|
||||
Name string
|
||||
|
||||
Category string
|
||||
DefaultText string
|
||||
Usage string
|
||||
|
||||
Required bool
|
||||
Hidden bool
|
||||
HasBeenSet bool
|
||||
|
||||
Value *big.Int
|
||||
|
||||
Aliases []string
|
||||
EnvVars []string
|
||||
}
|
||||
|
||||
// For cli.Flag:
|
||||
|
||||
func (f *BigFlag) Names() []string { return append([]string{f.Name}, f.Aliases...) }
|
||||
func (f *BigFlag) IsSet() bool { return f.HasBeenSet }
|
||||
func (f *BigFlag) String() string { return cli.FlagStringer(f) }
|
||||
|
||||
func (f *BigFlag) Apply(set *flag.FlagSet) error {
|
||||
for _, envVar := range f.EnvVars {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if value, found := syscall.Getenv(envVar); found {
|
||||
if _, ok := f.Value.SetString(value, 10); !ok {
|
||||
return fmt.Errorf("could not parse %q from environment variable %q for flag %s", value, envVar, f.Name)
|
||||
}
|
||||
f.HasBeenSet = true
|
||||
break
|
||||
}
|
||||
}
|
||||
eachName(f, func(name string) {
|
||||
f.Value = new(big.Int)
|
||||
set.Var((*bigValue)(f.Value), f.Name, f.Usage)
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// For cli.RequiredFlag:
|
||||
|
||||
func (f *BigFlag) IsRequired() bool { return f.Required }
|
||||
|
||||
// For cli.VisibleFlag:
|
||||
|
||||
func (f *BigFlag) IsVisible() bool { return !f.Hidden }
|
||||
|
||||
// For cli.CategorizableFlag:
|
||||
|
||||
func (f *BigFlag) GetCategory() string { return f.Category }
|
||||
|
||||
// For cli.DocGenerationFlag:
|
||||
|
||||
func (f *BigFlag) TakesValue() bool { return true }
|
||||
func (f *BigFlag) GetUsage() string { return f.Usage }
|
||||
func (f *BigFlag) GetValue() string { return f.Value.String() }
|
||||
func (f *BigFlag) GetEnvVars() []string { return f.EnvVars }
|
||||
|
||||
func (f *BigFlag) GetDefaultText() string {
|
||||
if f.DefaultText != "" {
|
||||
return f.DefaultText
|
||||
}
|
||||
return f.GetValue()
|
||||
}
|
||||
|
||||
// bigValue turns *big.Int into a flag.Value
|
||||
type bigValue big.Int
|
||||
|
||||
func (b *bigValue) String() string {
|
||||
if b == nil {
|
||||
return ""
|
||||
}
|
||||
return (*big.Int)(b).String()
|
||||
}
|
||||
|
||||
func (b *bigValue) Set(s string) error {
|
||||
intVal, ok := math.ParseBig256(s)
|
||||
if !ok {
|
||||
return errors.New("invalid integer syntax")
|
||||
}
|
||||
*b = (bigValue)(*intVal)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GlobalBig returns the value of a BigFlag from the global flag set.
|
||||
func GlobalBig(ctx *cli.Context, name string) *big.Int {
|
||||
val := ctx.Generic(name)
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
return (*big.Int)(val.(*bigValue))
|
||||
}
|
||||
|
||||
// Expands a file path
|
||||
// 1. replace tilde with users home dir
|
||||
// 2. expands embedded environment variables
|
||||
// 3. cleans the path, e.g. /a/b/../c -> /a/c
|
||||
// Note, it has limitations, e.g. ~someuser/tmp will not be expanded
|
||||
func expandPath(p string) string {
|
||||
// Named pipes are not file paths on windows, ignore
|
||||
if strings.HasPrefix(p, `\\.\pipe`) {
|
||||
return p
|
||||
}
|
||||
if strings.HasPrefix(p, "~/") || strings.HasPrefix(p, "~\\") {
|
||||
if home := HomeDir(); home != "" {
|
||||
p = home + p[1:]
|
||||
}
|
||||
}
|
||||
return filepath.Clean(os.ExpandEnv(p))
|
||||
}
|
||||
|
||||
func HomeDir() string {
|
||||
if home := os.Getenv("HOME"); home != "" {
|
||||
return home
|
||||
}
|
||||
if usr, err := user.Current(); err == nil {
|
||||
return usr.HomeDir
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func eachName(f cli.Flag, fn func(string)) {
|
||||
for _, name := range f.Names() {
|
||||
name = strings.Trim(name, " ")
|
||||
fn(name)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
// Copyright 2015 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package flags
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/user"
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPathExpansion(t *testing.T) {
|
||||
user, _ := user.Current()
|
||||
var tests map[string]string
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
tests = map[string]string{
|
||||
`/home/someuser/tmp`: `\home\someuser\tmp`,
|
||||
`~/tmp`: user.HomeDir + `\tmp`,
|
||||
`~thisOtherUser/b/`: `~thisOtherUser\b`,
|
||||
`$DDDXXX/a/b`: `\tmp\a\b`,
|
||||
`/a/b/`: `\a\b`,
|
||||
`C:\Documents\Newsletters\`: `C:\Documents\Newsletters`,
|
||||
`C:\`: `C:\`,
|
||||
`\\.\pipe\\pipe\geth621383`: `\\.\pipe\\pipe\geth621383`,
|
||||
}
|
||||
} else {
|
||||
tests = map[string]string{
|
||||
`/home/someuser/tmp`: `/home/someuser/tmp`,
|
||||
`~/tmp`: user.HomeDir + `/tmp`,
|
||||
`~thisOtherUser/b/`: `~thisOtherUser/b`,
|
||||
`$DDDXXX/a/b`: `/tmp/a/b`,
|
||||
`/a/b/`: `/a/b`,
|
||||
`C:\Documents\Newsletters\`: `C:\Documents\Newsletters\`,
|
||||
`C:\`: `C:\`,
|
||||
`\\.\pipe\\pipe\geth621383`: `\\.\pipe\\pipe\geth621383`,
|
||||
}
|
||||
}
|
||||
|
||||
os.Setenv(`DDDXXX`, `/tmp`)
|
||||
for test, expected := range tests {
|
||||
got := expandPath(test)
|
||||
if got != expected {
|
||||
t.Errorf(`test %s, got %s, expected %s\n`, test, got, expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,310 +0,0 @@
|
|||
// Copyright 2020 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package flags
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/internal/version"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/mattn/go-isatty"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// usecolor defines whether the CLI help should use colored output or normal dumb
|
||||
// colorless terminal formatting.
|
||||
var usecolor = (isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd())) && os.Getenv("TERM") != "dumb"
|
||||
|
||||
// NewApp creates an app with sane defaults.
|
||||
func NewApp(usage string) *cli.App {
|
||||
git, _ := version.VCS()
|
||||
app := cli.NewApp()
|
||||
app.EnableBashCompletion = true
|
||||
app.Version = params.VersionWithCommit(git.Commit, git.Date)
|
||||
app.Usage = usage
|
||||
app.Copyright = "Copyright 2013-2023 The go-ethereum Authors"
|
||||
app.Before = func(ctx *cli.Context) error {
|
||||
MigrateGlobalFlags(ctx)
|
||||
return nil
|
||||
}
|
||||
return app
|
||||
}
|
||||
|
||||
// Merge merges the given flag slices.
|
||||
func Merge(groups ...[]cli.Flag) []cli.Flag {
|
||||
var ret []cli.Flag
|
||||
for _, group := range groups {
|
||||
ret = append(ret, group...)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
var migrationApplied = map[*cli.Command]struct{}{}
|
||||
|
||||
// MigrateGlobalFlags makes all global flag values available in the
|
||||
// context. This should be called as early as possible in app.Before.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// geth account new --keystore /tmp/mykeystore --lightkdf
|
||||
//
|
||||
// is equivalent after calling this method with:
|
||||
//
|
||||
// geth --keystore /tmp/mykeystore --lightkdf account new
|
||||
//
|
||||
// i.e. in the subcommand Action function of 'account new', ctx.Bool("lightkdf)
|
||||
// will return true even if --lightkdf is set as a global option.
|
||||
//
|
||||
// This function may become unnecessary when https://github.com/urfave/cli/pull/1245 is merged.
|
||||
func MigrateGlobalFlags(ctx *cli.Context) {
|
||||
var iterate func(cs []*cli.Command, fn func(*cli.Command))
|
||||
iterate = func(cs []*cli.Command, fn func(*cli.Command)) {
|
||||
for _, cmd := range cs {
|
||||
if _, ok := migrationApplied[cmd]; ok {
|
||||
continue
|
||||
}
|
||||
migrationApplied[cmd] = struct{}{}
|
||||
fn(cmd)
|
||||
iterate(cmd.Subcommands, fn)
|
||||
}
|
||||
}
|
||||
|
||||
// This iterates over all commands and wraps their action function.
|
||||
iterate(ctx.App.Commands, func(cmd *cli.Command) {
|
||||
if cmd.Action == nil {
|
||||
return
|
||||
}
|
||||
|
||||
action := cmd.Action
|
||||
cmd.Action = func(ctx *cli.Context) error {
|
||||
doMigrateFlags(ctx)
|
||||
return action(ctx)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func doMigrateFlags(ctx *cli.Context) {
|
||||
// Figure out if there are any aliases of commands. If there are, we want
|
||||
// to ignore them when iterating over the flags.
|
||||
aliases := make(map[string]bool)
|
||||
for _, fl := range ctx.Command.Flags {
|
||||
for _, alias := range fl.Names()[1:] {
|
||||
aliases[alias] = true
|
||||
}
|
||||
}
|
||||
for _, name := range ctx.FlagNames() {
|
||||
for _, parent := range ctx.Lineage()[1:] {
|
||||
if parent.IsSet(name) {
|
||||
// When iterating across the lineage, we will be served both
|
||||
// the 'canon' and alias formats of all commmands. In most cases,
|
||||
// it's fine to set it in the ctx multiple times (one for each
|
||||
// name), however, the Slice-flags are not fine.
|
||||
// The slice-flags accumulate, so if we set it once as
|
||||
// "foo" and once as alias "F", then both will be present in the slice.
|
||||
if _, isAlias := aliases[name]; isAlias {
|
||||
continue
|
||||
}
|
||||
// If it is a string-slice, we need to set it as
|
||||
// "alfa, beta, gamma" instead of "[alfa beta gamma]", in order
|
||||
// for the backing StringSlice to parse it properly.
|
||||
if result := parent.StringSlice(name); len(result) > 0 {
|
||||
ctx.Set(name, strings.Join(result, ","))
|
||||
} else {
|
||||
ctx.Set(name, parent.String(name))
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
if usecolor {
|
||||
// Annotate all help categories with colors
|
||||
cli.AppHelpTemplate = regexp.MustCompile("[A-Z ]+:").ReplaceAllString(cli.AppHelpTemplate, "\u001B[33m$0\u001B[0m")
|
||||
|
||||
// Annotate flag categories with colors (private template, so need to
|
||||
// copy-paste the entire thing here...)
|
||||
cli.AppHelpTemplate = strings.ReplaceAll(cli.AppHelpTemplate, "{{template \"visibleFlagCategoryTemplate\" .}}", "{{range .VisibleFlagCategories}}\n {{if .Name}}\u001B[33m{{.Name}}\u001B[0m\n\n {{end}}{{$flglen := len .Flags}}{{range $i, $e := .Flags}}{{if eq (subtract $flglen $i) 1}}{{$e}}\n{{else}}{{$e}}\n {{end}}{{end}}{{end}}")
|
||||
}
|
||||
cli.FlagStringer = FlagString
|
||||
}
|
||||
|
||||
// FlagString prints a single flag in help.
|
||||
func FlagString(f cli.Flag) string {
|
||||
df, ok := f.(cli.DocGenerationFlag)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
needsPlaceholder := df.TakesValue()
|
||||
placeholder := ""
|
||||
if needsPlaceholder {
|
||||
placeholder = "value"
|
||||
}
|
||||
|
||||
namesText := cli.FlagNamePrefixer(df.Names(), placeholder)
|
||||
|
||||
defaultValueString := ""
|
||||
if s := df.GetDefaultText(); s != "" {
|
||||
defaultValueString = " (default: " + s + ")"
|
||||
}
|
||||
envHint := strings.TrimSpace(cli.FlagEnvHinter(df.GetEnvVars(), ""))
|
||||
if envHint != "" {
|
||||
envHint = " (" + envHint[1:len(envHint)-1] + ")"
|
||||
}
|
||||
usage := strings.TrimSpace(df.GetUsage())
|
||||
usage = wordWrap(usage, 80)
|
||||
usage = indent(usage, 10)
|
||||
|
||||
if usecolor {
|
||||
return fmt.Sprintf("\n \u001B[32m%-35s%-35s\u001B[0m%s\n%s", namesText, defaultValueString, envHint, usage)
|
||||
} else {
|
||||
return fmt.Sprintf("\n %-35s%-35s%s\n%s", namesText, defaultValueString, envHint, usage)
|
||||
}
|
||||
}
|
||||
|
||||
func indent(s string, nspace int) string {
|
||||
ind := strings.Repeat(" ", nspace)
|
||||
return ind + strings.ReplaceAll(s, "\n", "\n"+ind)
|
||||
}
|
||||
|
||||
func wordWrap(s string, width int) string {
|
||||
var (
|
||||
output strings.Builder
|
||||
lineLength = 0
|
||||
)
|
||||
|
||||
for {
|
||||
sp := strings.IndexByte(s, ' ')
|
||||
var word string
|
||||
if sp == -1 {
|
||||
word = s
|
||||
} else {
|
||||
word = s[:sp]
|
||||
}
|
||||
wlen := len(word)
|
||||
over := lineLength+wlen >= width
|
||||
if over {
|
||||
output.WriteByte('\n')
|
||||
lineLength = 0
|
||||
} else {
|
||||
if lineLength != 0 {
|
||||
output.WriteByte(' ')
|
||||
lineLength++
|
||||
}
|
||||
}
|
||||
|
||||
output.WriteString(word)
|
||||
lineLength += wlen
|
||||
|
||||
if sp == -1 {
|
||||
break
|
||||
}
|
||||
s = s[wlen+1:]
|
||||
}
|
||||
|
||||
return output.String()
|
||||
}
|
||||
|
||||
// AutoEnvVars extends all the specific CLI flags with automatically generated
|
||||
// env vars by capitalizing the flag, replacing . with _ and prefixing it with
|
||||
// the specified string.
|
||||
//
|
||||
// Note, the prefix should *not* contain the separator underscore, that will be
|
||||
// added automatically.
|
||||
func AutoEnvVars(flags []cli.Flag, prefix string) {
|
||||
for _, flag := range flags {
|
||||
envvar := strings.ToUpper(prefix + "_" + strings.ReplaceAll(strings.ReplaceAll(flag.Names()[0], ".", "_"), "-", "_"))
|
||||
|
||||
switch flag := flag.(type) {
|
||||
case *cli.StringFlag:
|
||||
flag.EnvVars = append(flag.EnvVars, envvar)
|
||||
|
||||
case *cli.StringSliceFlag:
|
||||
flag.EnvVars = append(flag.EnvVars, envvar)
|
||||
|
||||
case *cli.BoolFlag:
|
||||
flag.EnvVars = append(flag.EnvVars, envvar)
|
||||
|
||||
case *cli.IntFlag:
|
||||
flag.EnvVars = append(flag.EnvVars, envvar)
|
||||
|
||||
case *cli.Int64Flag:
|
||||
flag.EnvVars = append(flag.EnvVars, envvar)
|
||||
|
||||
case *cli.Uint64Flag:
|
||||
flag.EnvVars = append(flag.EnvVars, envvar)
|
||||
|
||||
case *cli.Float64Flag:
|
||||
flag.EnvVars = append(flag.EnvVars, envvar)
|
||||
|
||||
case *cli.DurationFlag:
|
||||
flag.EnvVars = append(flag.EnvVars, envvar)
|
||||
|
||||
case *cli.PathFlag:
|
||||
flag.EnvVars = append(flag.EnvVars, envvar)
|
||||
|
||||
case *BigFlag:
|
||||
flag.EnvVars = append(flag.EnvVars, envvar)
|
||||
|
||||
case *TextMarshalerFlag:
|
||||
flag.EnvVars = append(flag.EnvVars, envvar)
|
||||
|
||||
case *DirectoryFlag:
|
||||
flag.EnvVars = append(flag.EnvVars, envvar)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CheckEnvVars iterates over all the environment variables and checks if any of
|
||||
// them look like a CLI flag but is not consumed. This can be used to detect old
|
||||
// or mistyped names.
|
||||
func CheckEnvVars(ctx *cli.Context, flags []cli.Flag, prefix string) {
|
||||
known := make(map[string]string)
|
||||
for _, flag := range flags {
|
||||
docflag, ok := flag.(cli.DocGenerationFlag)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
for _, envvar := range docflag.GetEnvVars() {
|
||||
known[envvar] = flag.Names()[0]
|
||||
}
|
||||
}
|
||||
keyvals := os.Environ()
|
||||
sort.Strings(keyvals)
|
||||
|
||||
for _, keyval := range keyvals {
|
||||
key := strings.Split(keyval, "=")[0]
|
||||
if !strings.HasPrefix(key, prefix) {
|
||||
continue
|
||||
}
|
||||
if flag, ok := known[key]; ok {
|
||||
if ctx.Count(flag) > 0 {
|
||||
log.Info("Config environment variable found", "envvar", key, "shadowedby", "--"+flag)
|
||||
} else {
|
||||
log.Info("Config environment variable found", "envvar", key)
|
||||
}
|
||||
} else {
|
||||
log.Warn("Unknown config environment variable", "envvar", key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
// Copyright 2014 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Package guide is a small test suite to ensure snippets in the dev guide work.
|
||||
package guide
|
||||
|
|
@ -1,97 +0,0 @@
|
|||
// Copyright 2017 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
// This file contains the code snippets from the developer's guide embedded into
|
||||
// Go tests. This ensures that any code published in out guides will not break
|
||||
// accidentally via some code update. If some API changes nonetheless that needs
|
||||
// modifying this file, please port any modification over into the developer's
|
||||
// guide wiki pages too!
|
||||
|
||||
package guide
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
)
|
||||
|
||||
// Tests that the account management snippets work correctly.
|
||||
func TestAccountManagement(t *testing.T) {
|
||||
// Create a temporary folder to work with
|
||||
workdir := t.TempDir()
|
||||
|
||||
// Create an encrypted keystore (using light scrypt parameters)
|
||||
ks := keystore.NewKeyStore(filepath.Join(workdir, "keystore"), keystore.LightScryptN, keystore.LightScryptP)
|
||||
|
||||
// Create a new account with the specified encryption passphrase
|
||||
newAcc, err := ks.NewAccount("Creation password")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create new account: %v", err)
|
||||
}
|
||||
// Export the newly created account with a different passphrase. The returned
|
||||
// data from this method invocation is a JSON encoded, encrypted key-file
|
||||
jsonAcc, err := ks.Export(newAcc, "Creation password", "Export password")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to export account: %v", err)
|
||||
}
|
||||
// Update the passphrase on the account created above inside the local keystore
|
||||
if err := ks.Update(newAcc, "Creation password", "Update password"); err != nil {
|
||||
t.Fatalf("Failed to update account: %v", err)
|
||||
}
|
||||
// Delete the account updated above from the local keystore
|
||||
if err := ks.Delete(newAcc, "Update password"); err != nil {
|
||||
t.Fatalf("Failed to delete account: %v", err)
|
||||
}
|
||||
// Import back the account we've exported (and then deleted) above with yet
|
||||
// again a fresh passphrase
|
||||
if _, err := ks.Import(jsonAcc, "Export password", "Import password"); err != nil {
|
||||
t.Fatalf("Failed to import account: %v", err)
|
||||
}
|
||||
// Create a new account to sign transactions with
|
||||
signer, err := ks.NewAccount("Signer password")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create signer account: %v", err)
|
||||
}
|
||||
tx := types.NewTransaction(0, common.Address{}, big.NewInt(0), 0, big.NewInt(0), nil)
|
||||
chain := big.NewInt(1)
|
||||
|
||||
// Sign a transaction with a single authorization
|
||||
if _, err := ks.SignTxWithPassphrase(signer, "Signer password", tx, chain); err != nil {
|
||||
t.Fatalf("Failed to sign with passphrase: %v", err)
|
||||
}
|
||||
// Sign a transaction with multiple manually cancelled authorizations
|
||||
if err := ks.Unlock(signer, "Signer password"); err != nil {
|
||||
t.Fatalf("Failed to unlock account: %v", err)
|
||||
}
|
||||
if _, err := ks.SignTx(signer, tx, chain); err != nil {
|
||||
t.Fatalf("Failed to sign with unlocked account: %v", err)
|
||||
}
|
||||
if err := ks.Lock(signer.Address); err != nil {
|
||||
t.Fatalf("Failed to lock account: %v", err)
|
||||
}
|
||||
// Sign a transaction with multiple automatically cancelled authorizations
|
||||
if err := ks.TimedUnlock(signer, "Signer password", time.Second); err != nil {
|
||||
t.Fatalf("Failed to time unlock account: %v", err)
|
||||
}
|
||||
if _, err := ks.SignTx(signer, tx, chain); err != nil {
|
||||
t.Fatalf("Failed to sign with time unlocked account: %v", err)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,93 +0,0 @@
|
|||
// Copyright 2016 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package jsre
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/dop251/goja"
|
||||
)
|
||||
|
||||
// JS numerical token
|
||||
var numerical = regexp.MustCompile(`^(NaN|-?((\d*\.\d+|\d+)([Ee][+-]?\d+)?|Infinity))$`)
|
||||
|
||||
// CompleteKeywords returns potential continuations for the given line. Since line is
|
||||
// evaluated, callers need to make sure that evaluating line does not have side effects.
|
||||
func (jsre *JSRE) CompleteKeywords(line string) []string {
|
||||
var results []string
|
||||
jsre.Do(func(vm *goja.Runtime) {
|
||||
results = getCompletions(vm, line)
|
||||
})
|
||||
return results
|
||||
}
|
||||
|
||||
func getCompletions(vm *goja.Runtime, line string) (results []string) {
|
||||
parts := strings.Split(line, ".")
|
||||
if len(parts) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Find the right-most fully named object in the line. e.g. if line = "x.y.z"
|
||||
// and "x.y" is an object, obj will reference "x.y".
|
||||
obj := vm.GlobalObject()
|
||||
for i := 0; i < len(parts)-1; i++ {
|
||||
if numerical.MatchString(parts[i]) {
|
||||
return nil
|
||||
}
|
||||
v := obj.Get(parts[i])
|
||||
if v == nil || goja.IsNull(v) || goja.IsUndefined(v) {
|
||||
return nil // No object was found
|
||||
}
|
||||
obj = v.ToObject(vm)
|
||||
}
|
||||
|
||||
// Go over the keys of the object and retain the keys matching prefix.
|
||||
// Example: if line = "x.y.z" and "x.y" exists and has keys "zebu", "zebra"
|
||||
// and "platypus", then "x.y.zebu" and "x.y.zebra" will be added to results.
|
||||
prefix := parts[len(parts)-1]
|
||||
iterOwnAndConstructorKeys(vm, obj, func(k string) {
|
||||
if strings.HasPrefix(k, prefix) {
|
||||
if len(parts) == 1 {
|
||||
results = append(results, k)
|
||||
} else {
|
||||
results = append(results, strings.Join(parts[:len(parts)-1], ".")+"."+k)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Append opening parenthesis (for functions) or dot (for objects)
|
||||
// if the line itself is the only completion.
|
||||
if len(results) == 1 && results[0] == line {
|
||||
// Accessing the property will cause it to be evaluated.
|
||||
// This can cause an error, e.g. in case of web3.eth.protocolVersion
|
||||
// which has been dropped from geth. Ignore the error for autocompletion
|
||||
// purposes.
|
||||
obj := SafeGet(obj, parts[len(parts)-1])
|
||||
if obj != nil {
|
||||
if _, isfunc := goja.AssertFunction(obj); isfunc {
|
||||
results[0] += "("
|
||||
} else {
|
||||
results[0] += "."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sort.Strings(results)
|
||||
return results
|
||||
}
|
||||
|
|
@ -1,93 +0,0 @@
|
|||
// Copyright 2016 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package jsre
|
||||
|
||||
import (
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCompleteKeywords(t *testing.T) {
|
||||
re := New("", os.Stdout)
|
||||
re.Run(`
|
||||
function theClass() {
|
||||
this.foo = 3;
|
||||
this.gazonk = {xyz: 4};
|
||||
}
|
||||
theClass.prototype.someMethod = function () {};
|
||||
var x = new theClass();
|
||||
var y = new theClass();
|
||||
y.someMethod = function override() {};
|
||||
`)
|
||||
|
||||
var tests = []struct {
|
||||
input string
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
input: "St",
|
||||
want: []string{"String"},
|
||||
},
|
||||
{
|
||||
input: "x",
|
||||
want: []string{"x."},
|
||||
},
|
||||
{
|
||||
input: "x.someMethod",
|
||||
want: []string{"x.someMethod("},
|
||||
},
|
||||
{
|
||||
input: "x.",
|
||||
want: []string{
|
||||
"x.constructor",
|
||||
"x.foo",
|
||||
"x.gazonk",
|
||||
"x.someMethod",
|
||||
},
|
||||
},
|
||||
{
|
||||
input: "y.",
|
||||
want: []string{
|
||||
"y.constructor",
|
||||
"y.foo",
|
||||
"y.gazonk",
|
||||
"y.someMethod",
|
||||
},
|
||||
},
|
||||
{
|
||||
input: "x.gazonk.",
|
||||
want: []string{
|
||||
"x.gazonk.__proto__",
|
||||
"x.gazonk.constructor",
|
||||
"x.gazonk.hasOwnProperty",
|
||||
"x.gazonk.isPrototypeOf",
|
||||
"x.gazonk.propertyIsEnumerable",
|
||||
"x.gazonk.toLocaleString",
|
||||
"x.gazonk.toString",
|
||||
"x.gazonk.valueOf",
|
||||
"x.gazonk.xyz",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
cs := re.CompleteKeywords(test.input)
|
||||
if !reflect.DeepEqual(cs, test.want) {
|
||||
t.Errorf("wrong completions for %q\ngot %v\nwant %v", test.input, cs, test.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -1,28 +0,0 @@
|
|||
// Copyright 2017 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Package deps contains the console JavaScript dependencies Go embedded.
|
||||
package deps
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
)
|
||||
|
||||
//go:embed web3.js
|
||||
var Web3JS string
|
||||
|
||||
//go:embed bignumber.js
|
||||
var BigNumberJS string
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,340 +0,0 @@
|
|||
// Copyright 2015 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Package jsre provides execution environment for JavaScript.
|
||||
package jsre
|
||||
|
||||
import (
|
||||
crand "crypto/rand"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/dop251/goja"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
// JSRE is a JS runtime environment embedding the goja interpreter.
|
||||
// It provides helper functions to load code from files, run code snippets
|
||||
// and bind native go objects to JS.
|
||||
//
|
||||
// The runtime runs all code on a dedicated event loop and does not expose the underlying
|
||||
// goja runtime directly. To use the runtime, call JSRE.Do. When binding a Go function,
|
||||
// use the Call type to gain access to the runtime.
|
||||
type JSRE struct {
|
||||
assetPath string
|
||||
output io.Writer
|
||||
evalQueue chan *evalReq
|
||||
stopEventLoop chan bool
|
||||
closed chan struct{}
|
||||
vm *goja.Runtime
|
||||
}
|
||||
|
||||
// Call is the argument type of Go functions which are callable from JS.
|
||||
type Call struct {
|
||||
goja.FunctionCall
|
||||
VM *goja.Runtime
|
||||
}
|
||||
|
||||
// jsTimer is a single timer instance with a callback function
|
||||
type jsTimer struct {
|
||||
timer *time.Timer
|
||||
duration time.Duration
|
||||
interval bool
|
||||
call goja.FunctionCall
|
||||
}
|
||||
|
||||
// evalReq is a serialized vm execution request processed by runEventLoop.
|
||||
type evalReq struct {
|
||||
fn func(vm *goja.Runtime)
|
||||
done chan bool
|
||||
}
|
||||
|
||||
// runtime must be stopped with Stop() after use and cannot be used after stopping
|
||||
func New(assetPath string, output io.Writer) *JSRE {
|
||||
re := &JSRE{
|
||||
assetPath: assetPath,
|
||||
output: output,
|
||||
closed: make(chan struct{}),
|
||||
evalQueue: make(chan *evalReq),
|
||||
stopEventLoop: make(chan bool),
|
||||
vm: goja.New(),
|
||||
}
|
||||
go re.runEventLoop()
|
||||
re.Set("loadScript", MakeCallback(re.vm, re.loadScript))
|
||||
re.Set("inspect", re.prettyPrintJS)
|
||||
return re
|
||||
}
|
||||
|
||||
// randomSource returns a pseudo random value generator.
|
||||
func randomSource() *rand.Rand {
|
||||
bytes := make([]byte, 8)
|
||||
seed := time.Now().UnixNano()
|
||||
if _, err := crand.Read(bytes); err == nil {
|
||||
seed = int64(binary.LittleEndian.Uint64(bytes))
|
||||
}
|
||||
|
||||
src := rand.NewSource(seed)
|
||||
return rand.New(src)
|
||||
}
|
||||
|
||||
// This function runs the main event loop from a goroutine that is started
|
||||
// when JSRE is created. Use Stop() before exiting to properly stop it.
|
||||
// The event loop processes vm access requests from the evalQueue in a
|
||||
// serialized way and calls timer callback functions at the appropriate time.
|
||||
|
||||
// Exported functions always access the vm through the event queue. You can
|
||||
// call the functions of the goja vm directly to circumvent the queue. These
|
||||
// functions should be used if and only if running a routine that was already
|
||||
// called from JS through an RPC call.
|
||||
func (re *JSRE) runEventLoop() {
|
||||
defer close(re.closed)
|
||||
|
||||
r := randomSource()
|
||||
re.vm.SetRandSource(r.Float64)
|
||||
|
||||
registry := map[*jsTimer]*jsTimer{}
|
||||
ready := make(chan *jsTimer)
|
||||
|
||||
newTimer := func(call goja.FunctionCall, interval bool) (*jsTimer, goja.Value) {
|
||||
delay := call.Argument(1).ToInteger()
|
||||
if 0 >= delay {
|
||||
delay = 1
|
||||
}
|
||||
timer := &jsTimer{
|
||||
duration: time.Duration(delay) * time.Millisecond,
|
||||
call: call,
|
||||
interval: interval,
|
||||
}
|
||||
registry[timer] = timer
|
||||
|
||||
timer.timer = time.AfterFunc(timer.duration, func() {
|
||||
ready <- timer
|
||||
})
|
||||
|
||||
return timer, re.vm.ToValue(timer)
|
||||
}
|
||||
|
||||
setTimeout := func(call goja.FunctionCall) goja.Value {
|
||||
_, value := newTimer(call, false)
|
||||
return value
|
||||
}
|
||||
|
||||
setInterval := func(call goja.FunctionCall) goja.Value {
|
||||
_, value := newTimer(call, true)
|
||||
return value
|
||||
}
|
||||
|
||||
clearTimeout := func(call goja.FunctionCall) goja.Value {
|
||||
timer := call.Argument(0).Export()
|
||||
if timer, ok := timer.(*jsTimer); ok {
|
||||
timer.timer.Stop()
|
||||
delete(registry, timer)
|
||||
}
|
||||
return goja.Undefined()
|
||||
}
|
||||
re.vm.Set("_setTimeout", setTimeout)
|
||||
re.vm.Set("_setInterval", setInterval)
|
||||
re.vm.RunString(`var setTimeout = function(args) {
|
||||
if (arguments.length < 1) {
|
||||
throw TypeError("Failed to execute 'setTimeout': 1 argument required, but only 0 present.");
|
||||
}
|
||||
return _setTimeout.apply(this, arguments);
|
||||
}`)
|
||||
re.vm.RunString(`var setInterval = function(args) {
|
||||
if (arguments.length < 1) {
|
||||
throw TypeError("Failed to execute 'setInterval': 1 argument required, but only 0 present.");
|
||||
}
|
||||
return _setInterval.apply(this, arguments);
|
||||
}`)
|
||||
re.vm.Set("clearTimeout", clearTimeout)
|
||||
re.vm.Set("clearInterval", clearTimeout)
|
||||
|
||||
var waitForCallbacks bool
|
||||
|
||||
loop:
|
||||
for {
|
||||
select {
|
||||
case timer := <-ready:
|
||||
// execute callback, remove/reschedule the timer
|
||||
var arguments []interface{}
|
||||
if len(timer.call.Arguments) > 2 {
|
||||
tmp := timer.call.Arguments[2:]
|
||||
arguments = make([]interface{}, 2+len(tmp))
|
||||
for i, value := range tmp {
|
||||
arguments[i+2] = value
|
||||
}
|
||||
} else {
|
||||
arguments = make([]interface{}, 1)
|
||||
}
|
||||
arguments[0] = timer.call.Arguments[0]
|
||||
call, isFunc := goja.AssertFunction(timer.call.Arguments[0])
|
||||
if !isFunc {
|
||||
panic(re.vm.ToValue("js error: timer/timeout callback is not a function"))
|
||||
}
|
||||
call(goja.Null(), timer.call.Arguments...)
|
||||
|
||||
_, inreg := registry[timer] // when clearInterval is called from within the callback don't reset it
|
||||
if timer.interval && inreg {
|
||||
timer.timer.Reset(timer.duration)
|
||||
} else {
|
||||
delete(registry, timer)
|
||||
if waitForCallbacks && (len(registry) == 0) {
|
||||
break loop
|
||||
}
|
||||
}
|
||||
case req := <-re.evalQueue:
|
||||
// run the code, send the result back
|
||||
req.fn(re.vm)
|
||||
close(req.done)
|
||||
if waitForCallbacks && (len(registry) == 0) {
|
||||
break loop
|
||||
}
|
||||
case waitForCallbacks = <-re.stopEventLoop:
|
||||
if !waitForCallbacks || (len(registry) == 0) {
|
||||
break loop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, timer := range registry {
|
||||
timer.timer.Stop()
|
||||
delete(registry, timer)
|
||||
}
|
||||
}
|
||||
|
||||
// Do executes the given function on the JS event loop.
|
||||
// When the runtime is stopped, fn will not execute.
|
||||
func (re *JSRE) Do(fn func(*goja.Runtime)) {
|
||||
done := make(chan bool)
|
||||
req := &evalReq{fn, done}
|
||||
select {
|
||||
case re.evalQueue <- req:
|
||||
<-done
|
||||
case <-re.closed:
|
||||
}
|
||||
}
|
||||
|
||||
// Stop terminates the event loop, optionally waiting for all timers to expire.
|
||||
func (re *JSRE) Stop(waitForCallbacks bool) {
|
||||
timeout := time.NewTimer(10 * time.Millisecond)
|
||||
defer timeout.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-re.closed:
|
||||
return
|
||||
case re.stopEventLoop <- waitForCallbacks:
|
||||
<-re.closed
|
||||
return
|
||||
case <-timeout.C:
|
||||
// JS is blocked, interrupt and try again.
|
||||
re.vm.Interrupt(errors.New("JS runtime stopped"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Exec(file) loads and runs the contents of a file
|
||||
// if a relative path is given, the jsre's assetPath is used
|
||||
func (re *JSRE) Exec(file string) error {
|
||||
code, err := os.ReadFile(common.AbsolutePath(re.assetPath, file))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return re.Compile(file, string(code))
|
||||
}
|
||||
|
||||
// Run runs a piece of JS code.
|
||||
func (re *JSRE) Run(code string) (v goja.Value, err error) {
|
||||
re.Do(func(vm *goja.Runtime) { v, err = vm.RunString(code) })
|
||||
return v, err
|
||||
}
|
||||
|
||||
// Set assigns value v to a variable in the JS environment.
|
||||
func (re *JSRE) Set(ns string, v interface{}) (err error) {
|
||||
re.Do(func(vm *goja.Runtime) { vm.Set(ns, v) })
|
||||
return err
|
||||
}
|
||||
|
||||
// MakeCallback turns the given function into a function that's callable by JS.
|
||||
func MakeCallback(vm *goja.Runtime, fn func(Call) (goja.Value, error)) goja.Value {
|
||||
return vm.ToValue(func(call goja.FunctionCall) goja.Value {
|
||||
result, err := fn(Call{call, vm})
|
||||
if err != nil {
|
||||
panic(vm.NewGoError(err))
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
// Evaluate executes code and pretty prints the result to the specified output stream.
|
||||
func (re *JSRE) Evaluate(code string, w io.Writer) {
|
||||
re.Do(func(vm *goja.Runtime) {
|
||||
val, err := vm.RunString(code)
|
||||
if err != nil {
|
||||
prettyError(vm, err, w)
|
||||
} else {
|
||||
prettyPrint(vm, val, w)
|
||||
}
|
||||
fmt.Fprintln(w)
|
||||
})
|
||||
}
|
||||
|
||||
// Interrupt stops the current JS evaluation.
|
||||
func (re *JSRE) Interrupt(v interface{}) {
|
||||
done := make(chan bool)
|
||||
noop := func(*goja.Runtime) {}
|
||||
|
||||
select {
|
||||
case re.evalQueue <- &evalReq{noop, done}:
|
||||
// event loop is not blocked.
|
||||
default:
|
||||
re.vm.Interrupt(v)
|
||||
}
|
||||
}
|
||||
|
||||
// Compile compiles and then runs a piece of JS code.
|
||||
func (re *JSRE) Compile(filename string, src string) (err error) {
|
||||
re.Do(func(vm *goja.Runtime) { _, err = compileAndRun(vm, filename, src) })
|
||||
return err
|
||||
}
|
||||
|
||||
// loadScript loads and executes a JS file.
|
||||
func (re *JSRE) loadScript(call Call) (goja.Value, error) {
|
||||
file := call.Argument(0).ToString().String()
|
||||
file = common.AbsolutePath(re.assetPath, file)
|
||||
source, err := os.ReadFile(file)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read file %s: %v", file, err)
|
||||
}
|
||||
value, err := compileAndRun(re.vm, file, string(source))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error while compiling or running script: %v", err)
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func compileAndRun(vm *goja.Runtime, filename string, src string) (goja.Value, error) {
|
||||
script, err := goja.Compile(filename, src, false)
|
||||
if err != nil {
|
||||
return goja.Null(), err
|
||||
}
|
||||
return vm.RunProgram(script)
|
||||
}
|
||||
|
|
@ -1,130 +0,0 @@
|
|||
// Copyright 2015 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package jsre
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/dop251/goja"
|
||||
)
|
||||
|
||||
type testNativeObjectBinding struct {
|
||||
vm *goja.Runtime
|
||||
}
|
||||
|
||||
type msg struct {
|
||||
Msg string
|
||||
}
|
||||
|
||||
func (no *testNativeObjectBinding) TestMethod(call goja.FunctionCall) goja.Value {
|
||||
m := call.Argument(0).ToString().String()
|
||||
return no.vm.ToValue(&msg{m})
|
||||
}
|
||||
|
||||
func newWithTestJS(t *testing.T, testjs string) *JSRE {
|
||||
dir := t.TempDir()
|
||||
if testjs != "" {
|
||||
if err := os.WriteFile(path.Join(dir, "test.js"), []byte(testjs), os.ModePerm); err != nil {
|
||||
t.Fatal("cannot create test.js:", err)
|
||||
}
|
||||
}
|
||||
jsre := New(dir, os.Stdout)
|
||||
return jsre
|
||||
}
|
||||
|
||||
func TestExec(t *testing.T) {
|
||||
jsre := newWithTestJS(t, `msg = "testMsg"`)
|
||||
|
||||
err := jsre.Exec("test.js")
|
||||
if err != nil {
|
||||
t.Errorf("expected no error, got %v", err)
|
||||
}
|
||||
val, err := jsre.Run("msg")
|
||||
if err != nil {
|
||||
t.Errorf("expected no error, got %v", err)
|
||||
}
|
||||
if val.ExportType().Kind() != reflect.String {
|
||||
t.Errorf("expected string value, got %v", val)
|
||||
}
|
||||
exp := "testMsg"
|
||||
got := val.ToString().String()
|
||||
if exp != got {
|
||||
t.Errorf("expected '%v', got '%v'", exp, got)
|
||||
}
|
||||
jsre.Stop(false)
|
||||
}
|
||||
|
||||
func TestNatto(t *testing.T) {
|
||||
jsre := newWithTestJS(t, `setTimeout(function(){msg = "testMsg"}, 1);`)
|
||||
|
||||
err := jsre.Exec("test.js")
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
val, err := jsre.Run("msg")
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
if val.ExportType().Kind() != reflect.String {
|
||||
t.Fatalf("expected string value, got %v", val)
|
||||
}
|
||||
exp := "testMsg"
|
||||
got := val.ToString().String()
|
||||
if exp != got {
|
||||
t.Fatalf("expected '%v', got '%v'", exp, got)
|
||||
}
|
||||
jsre.Stop(false)
|
||||
}
|
||||
|
||||
func TestBind(t *testing.T) {
|
||||
jsre := New("", os.Stdout)
|
||||
defer jsre.Stop(false)
|
||||
|
||||
jsre.Set("no", &testNativeObjectBinding{vm: jsre.vm})
|
||||
|
||||
_, err := jsre.Run(`no.TestMethod("testMsg")`)
|
||||
if err != nil {
|
||||
t.Errorf("expected no error, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadScript(t *testing.T) {
|
||||
jsre := newWithTestJS(t, `msg = "testMsg"`)
|
||||
|
||||
_, err := jsre.Run(`loadScript("test.js")`)
|
||||
if err != nil {
|
||||
t.Errorf("expected no error, got %v", err)
|
||||
}
|
||||
val, err := jsre.Run("msg")
|
||||
if err != nil {
|
||||
t.Errorf("expected no error, got %v", err)
|
||||
}
|
||||
if val.ExportType().Kind() != reflect.String {
|
||||
t.Errorf("expected string value, got %v", val)
|
||||
}
|
||||
exp := "testMsg"
|
||||
got := val.ToString().String()
|
||||
if exp != got {
|
||||
t.Errorf("expected '%v', got '%v'", exp, got)
|
||||
}
|
||||
jsre.Stop(false)
|
||||
}
|
||||
|
|
@ -1,301 +0,0 @@
|
|||
// Copyright 2016 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package jsre
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/dop251/goja"
|
||||
"github.com/fatih/color"
|
||||
)
|
||||
|
||||
const (
|
||||
maxPrettyPrintLevel = 3
|
||||
indentString = " "
|
||||
)
|
||||
|
||||
var (
|
||||
FunctionColor = color.New(color.FgMagenta).SprintfFunc()
|
||||
SpecialColor = color.New(color.Bold).SprintfFunc()
|
||||
NumberColor = color.New(color.FgRed).SprintfFunc()
|
||||
StringColor = color.New(color.FgGreen).SprintfFunc()
|
||||
ErrorColor = color.New(color.FgHiRed).SprintfFunc()
|
||||
)
|
||||
|
||||
// these fields are hidden when printing objects.
|
||||
var boringKeys = map[string]bool{
|
||||
"valueOf": true,
|
||||
"toString": true,
|
||||
"toLocaleString": true,
|
||||
"hasOwnProperty": true,
|
||||
"isPrototypeOf": true,
|
||||
"propertyIsEnumerable": true,
|
||||
"constructor": true,
|
||||
}
|
||||
|
||||
// prettyPrint writes value to standard output.
|
||||
func prettyPrint(vm *goja.Runtime, value goja.Value, w io.Writer) {
|
||||
ppctx{vm: vm, w: w}.printValue(value, 0, false)
|
||||
}
|
||||
|
||||
// prettyError writes err to standard output.
|
||||
func prettyError(vm *goja.Runtime, err error, w io.Writer) {
|
||||
failure := err.Error()
|
||||
if gojaErr, ok := err.(*goja.Exception); ok {
|
||||
failure = gojaErr.String()
|
||||
}
|
||||
fmt.Fprint(w, ErrorColor("%s", failure))
|
||||
}
|
||||
|
||||
func (re *JSRE) prettyPrintJS(call goja.FunctionCall) goja.Value {
|
||||
for _, v := range call.Arguments {
|
||||
prettyPrint(re.vm, v, re.output)
|
||||
fmt.Fprintln(re.output)
|
||||
}
|
||||
return goja.Undefined()
|
||||
}
|
||||
|
||||
type ppctx struct {
|
||||
vm *goja.Runtime
|
||||
w io.Writer
|
||||
}
|
||||
|
||||
func (ctx ppctx) indent(level int) string {
|
||||
return strings.Repeat(indentString, level)
|
||||
}
|
||||
|
||||
func (ctx ppctx) printValue(v goja.Value, level int, inArray bool) {
|
||||
if goja.IsNull(v) || goja.IsUndefined(v) {
|
||||
fmt.Fprint(ctx.w, SpecialColor(v.String()))
|
||||
return
|
||||
}
|
||||
kind := v.ExportType().Kind()
|
||||
switch {
|
||||
case kind == reflect.Bool:
|
||||
fmt.Fprint(ctx.w, SpecialColor("%t", v.ToBoolean()))
|
||||
case kind == reflect.String:
|
||||
fmt.Fprint(ctx.w, StringColor("%q", v.String()))
|
||||
case kind >= reflect.Int && kind <= reflect.Complex128:
|
||||
fmt.Fprint(ctx.w, NumberColor("%s", v.String()))
|
||||
default:
|
||||
if obj, ok := v.(*goja.Object); ok {
|
||||
ctx.printObject(obj, level, inArray)
|
||||
} else {
|
||||
fmt.Fprintf(ctx.w, "<unprintable %T>", v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SafeGet attempt to get the value associated to `key`, and
|
||||
// catches the panic that goja creates if an error occurs in
|
||||
// key.
|
||||
func SafeGet(obj *goja.Object, key string) (ret goja.Value) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ret = goja.Undefined()
|
||||
}
|
||||
}()
|
||||
ret = obj.Get(key)
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (ctx ppctx) printObject(obj *goja.Object, level int, inArray bool) {
|
||||
switch obj.ClassName() {
|
||||
case "Array", "GoArray":
|
||||
lv := obj.Get("length")
|
||||
len := lv.ToInteger()
|
||||
if len == 0 {
|
||||
fmt.Fprintf(ctx.w, "[]")
|
||||
return
|
||||
}
|
||||
if level > maxPrettyPrintLevel {
|
||||
fmt.Fprint(ctx.w, "[...]")
|
||||
return
|
||||
}
|
||||
fmt.Fprint(ctx.w, "[")
|
||||
for i := int64(0); i < len; i++ {
|
||||
el := obj.Get(strconv.FormatInt(i, 10))
|
||||
if el != nil {
|
||||
ctx.printValue(el, level+1, true)
|
||||
}
|
||||
if i < len-1 {
|
||||
fmt.Fprintf(ctx.w, ", ")
|
||||
}
|
||||
}
|
||||
fmt.Fprint(ctx.w, "]")
|
||||
|
||||
case "Object":
|
||||
// Print values from bignumber.js as regular numbers.
|
||||
if ctx.isBigNumber(obj) {
|
||||
fmt.Fprint(ctx.w, NumberColor("%s", toString(obj)))
|
||||
return
|
||||
}
|
||||
// Otherwise, print all fields indented, but stop if we're too deep.
|
||||
keys := ctx.fields(obj)
|
||||
if len(keys) == 0 {
|
||||
fmt.Fprint(ctx.w, "{}")
|
||||
return
|
||||
}
|
||||
if level > maxPrettyPrintLevel {
|
||||
fmt.Fprint(ctx.w, "{...}")
|
||||
return
|
||||
}
|
||||
fmt.Fprintln(ctx.w, "{")
|
||||
for i, k := range keys {
|
||||
v := SafeGet(obj, k)
|
||||
fmt.Fprintf(ctx.w, "%s%s: ", ctx.indent(level+1), k)
|
||||
ctx.printValue(v, level+1, false)
|
||||
if i < len(keys)-1 {
|
||||
fmt.Fprintf(ctx.w, ",")
|
||||
}
|
||||
fmt.Fprintln(ctx.w)
|
||||
}
|
||||
if inArray {
|
||||
level--
|
||||
}
|
||||
fmt.Fprintf(ctx.w, "%s}", ctx.indent(level))
|
||||
|
||||
case "Function":
|
||||
robj := obj.ToString()
|
||||
desc := strings.Trim(strings.Split(robj.String(), "{")[0], " \t\n")
|
||||
desc = strings.Replace(desc, " (", "(", 1)
|
||||
fmt.Fprint(ctx.w, FunctionColor("%s", desc))
|
||||
|
||||
case "RegExp":
|
||||
fmt.Fprint(ctx.w, StringColor("%s", toString(obj)))
|
||||
|
||||
default:
|
||||
if level <= maxPrettyPrintLevel {
|
||||
s := obj.ToString().String()
|
||||
fmt.Fprintf(ctx.w, "<%s %s>", obj.ClassName(), s)
|
||||
} else {
|
||||
fmt.Fprintf(ctx.w, "<%s>", obj.ClassName())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (ctx ppctx) fields(obj *goja.Object) []string {
|
||||
var (
|
||||
vals, methods []string
|
||||
seen = make(map[string]bool)
|
||||
)
|
||||
add := func(k string) {
|
||||
if seen[k] || boringKeys[k] || strings.HasPrefix(k, "_") {
|
||||
return
|
||||
}
|
||||
seen[k] = true
|
||||
|
||||
key := SafeGet(obj, k)
|
||||
if key == nil {
|
||||
// The value corresponding to that key could not be found
|
||||
// (typically because it is backed by an RPC call that is
|
||||
// not supported by this instance. Add it to the list of
|
||||
// values so that it appears as `undefined` to the user.
|
||||
vals = append(vals, k)
|
||||
} else {
|
||||
if _, callable := goja.AssertFunction(key); callable {
|
||||
methods = append(methods, k)
|
||||
} else {
|
||||
vals = append(vals, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
iterOwnAndConstructorKeys(ctx.vm, obj, add)
|
||||
sort.Strings(vals)
|
||||
sort.Strings(methods)
|
||||
return append(vals, methods...)
|
||||
}
|
||||
|
||||
func iterOwnAndConstructorKeys(vm *goja.Runtime, obj *goja.Object, f func(string)) {
|
||||
seen := make(map[string]bool)
|
||||
iterOwnKeys(vm, obj, func(prop string) {
|
||||
seen[prop] = true
|
||||
f(prop)
|
||||
})
|
||||
if cp := constructorPrototype(vm, obj); cp != nil {
|
||||
iterOwnKeys(vm, cp, func(prop string) {
|
||||
if !seen[prop] {
|
||||
f(prop)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func iterOwnKeys(vm *goja.Runtime, obj *goja.Object, f func(string)) {
|
||||
Object := vm.Get("Object").ToObject(vm)
|
||||
getOwnPropertyNames, isFunc := goja.AssertFunction(Object.Get("getOwnPropertyNames"))
|
||||
if !isFunc {
|
||||
panic(vm.ToValue("Object.getOwnPropertyNames isn't a function"))
|
||||
}
|
||||
rv, err := getOwnPropertyNames(goja.Null(), obj)
|
||||
if err != nil {
|
||||
panic(vm.ToValue(fmt.Sprintf("Error getting object properties: %v", err)))
|
||||
}
|
||||
gv := rv.Export()
|
||||
switch gv := gv.(type) {
|
||||
case []interface{}:
|
||||
for _, v := range gv {
|
||||
f(v.(string))
|
||||
}
|
||||
case []string:
|
||||
for _, v := range gv {
|
||||
f(v)
|
||||
}
|
||||
default:
|
||||
panic(fmt.Errorf("Object.getOwnPropertyNames returned unexpected type %T", gv))
|
||||
}
|
||||
}
|
||||
|
||||
func (ctx ppctx) isBigNumber(v *goja.Object) bool {
|
||||
// Handle numbers with custom constructor.
|
||||
if obj := v.Get("constructor").ToObject(ctx.vm); obj != nil {
|
||||
if strings.HasPrefix(toString(obj), "function BigNumber") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
// Handle default constructor.
|
||||
BigNumber := ctx.vm.Get("BigNumber").ToObject(ctx.vm)
|
||||
if BigNumber == nil {
|
||||
return false
|
||||
}
|
||||
prototype := BigNumber.Get("prototype").ToObject(ctx.vm)
|
||||
isPrototypeOf, callable := goja.AssertFunction(prototype.Get("isPrototypeOf"))
|
||||
if !callable {
|
||||
return false
|
||||
}
|
||||
bv, _ := isPrototypeOf(prototype, v)
|
||||
return bv.ToBoolean()
|
||||
}
|
||||
|
||||
func toString(obj *goja.Object) string {
|
||||
return obj.ToString().String()
|
||||
}
|
||||
|
||||
func constructorPrototype(vm *goja.Runtime, obj *goja.Object) *goja.Object {
|
||||
if v := obj.Get("constructor"); v != nil {
|
||||
if v := v.ToObject(vm).Get("prototype"); v != nil {
|
||||
return v.ToObject(vm)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
// This file originates from Docker/Moby,
|
||||
// https://github.com/moby/moby/blob/master/pkg/reexec/reexec.go
|
||||
// Licensed under Apache License 2.0: https://github.com/moby/moby/blob/master/LICENSE
|
||||
// Copyright 2013-2018 Docker, Inc.
|
||||
//
|
||||
// Package reexec facilitates the busybox style reexec of the docker binary that
|
||||
// we require because of the forking limitations of using Go. Handlers can be
|
||||
// registered with a name and the argv 0 of the exec of the binary will be used
|
||||
// to find and execute custom init paths.
|
||||
package reexec
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
var registeredInitializers = make(map[string]func())
|
||||
|
||||
// Register adds an initialization func under the specified name
|
||||
func Register(name string, initializer func()) {
|
||||
if _, exists := registeredInitializers[name]; exists {
|
||||
panic(fmt.Sprintf("reexec func already registered under name %q", name))
|
||||
}
|
||||
registeredInitializers[name] = initializer
|
||||
}
|
||||
|
||||
// Init is called as the first part of the exec process and returns true if an
|
||||
// initialization function was called.
|
||||
func Init() bool {
|
||||
if initializer, ok := registeredInitializers[os.Args[0]]; ok {
|
||||
initializer()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
// This file originates from Docker/Moby,
|
||||
// https://github.com/moby/moby/blob/master/pkg/reexec/
|
||||
// Licensed under Apache License 2.0: https://github.com/moby/moby/blob/master/LICENSE
|
||||
// Copyright 2013-2018 Docker, Inc.
|
||||
|
||||
//go:build linux
|
||||
|
||||
package reexec
|
||||
|
||||
// Self returns the path to the current process's binary.
|
||||
// Returns "/proc/self/exe".
|
||||
func Self() string {
|
||||
return "/proc/self/exe"
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
// This file originates from Docker/Moby,
|
||||
// https://github.com/moby/moby/blob/master/pkg/reexec/
|
||||
// Licensed under Apache License 2.0: https://github.com/moby/moby/blob/master/LICENSE
|
||||
// Copyright 2013-2018 Docker, Inc.
|
||||
|
||||
//go:build !linux
|
||||
|
||||
package reexec
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// Self returns the path to the current process's binary.
|
||||
// Uses os.Args[0].
|
||||
func Self() string {
|
||||
name := os.Args[0]
|
||||
if filepath.Base(name) == name {
|
||||
if lp, err := exec.LookPath(name); err == nil {
|
||||
return lp
|
||||
}
|
||||
}
|
||||
// handle conversion of relative paths to absolute
|
||||
if absName, err := filepath.Abs(name); err == nil {
|
||||
return absName
|
||||
}
|
||||
// if we couldn't get absolute name, return original
|
||||
// (NOTE: Go only errors on Abs() if os.Getwd fails)
|
||||
return name
|
||||
}
|
||||
|
|
@ -1,85 +0,0 @@
|
|||
// Copyright 2021 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package shutdowncheck
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
)
|
||||
|
||||
// ShutdownTracker is a service that reports previous unclean shutdowns
|
||||
// upon start. It needs to be started after a successful start-up and stopped
|
||||
// after a successful shutdown, just before the db is closed.
|
||||
type ShutdownTracker struct {
|
||||
db ethdb.Database
|
||||
stopCh chan struct{}
|
||||
}
|
||||
|
||||
// NewShutdownTracker creates a new ShutdownTracker instance and has
|
||||
// no other side-effect.
|
||||
func NewShutdownTracker(db ethdb.Database) *ShutdownTracker {
|
||||
return &ShutdownTracker{
|
||||
db: db,
|
||||
stopCh: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
// MarkStartup is to be called in the beginning when the node starts. It will:
|
||||
// - Push a new startup marker to the db
|
||||
// - Report previous unclean shutdowns
|
||||
func (t *ShutdownTracker) MarkStartup() {
|
||||
if uncleanShutdowns, discards, err := rawdb.PushUncleanShutdownMarker(t.db); err != nil {
|
||||
log.Error("Could not update unclean-shutdown-marker list", "error", err)
|
||||
} else {
|
||||
if discards > 0 {
|
||||
log.Warn("Old unclean shutdowns found", "count", discards)
|
||||
}
|
||||
for _, tstamp := range uncleanShutdowns {
|
||||
t := time.Unix(int64(tstamp), 0)
|
||||
log.Warn("Unclean shutdown detected", "booted", t,
|
||||
"age", common.PrettyAge(t))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Start runs an event loop that updates the current marker's timestamp every 5 minutes.
|
||||
func (t *ShutdownTracker) Start() {
|
||||
go func() {
|
||||
ticker := time.NewTicker(5 * time.Minute)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
rawdb.UpdateUncleanShutdownMarker(t.db)
|
||||
case <-t.stopCh:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Stop will stop the update loop and clear the current marker.
|
||||
func (t *ShutdownTracker) Stop() {
|
||||
// Stop update loop.
|
||||
t.stopCh <- struct{}{}
|
||||
// Clear last marker.
|
||||
rawdb.PopUncleanShutdownMarker(t.db)
|
||||
}
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
// Copyright 2021 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Package syncx contains exotic synchronization primitives.
|
||||
package syncx
|
||||
|
||||
// ClosableMutex is a mutex that can also be closed.
|
||||
// Once closed, it can never be taken again.
|
||||
type ClosableMutex struct {
|
||||
ch chan struct{}
|
||||
}
|
||||
|
||||
func NewClosableMutex() *ClosableMutex {
|
||||
ch := make(chan struct{}, 1)
|
||||
ch <- struct{}{}
|
||||
return &ClosableMutex{ch}
|
||||
}
|
||||
|
||||
// TryLock attempts to lock cm.
|
||||
// If the mutex is closed, TryLock returns false.
|
||||
func (cm *ClosableMutex) TryLock() bool {
|
||||
_, ok := <-cm.ch
|
||||
return ok
|
||||
}
|
||||
|
||||
// MustLock locks cm.
|
||||
// If the mutex is closed, MustLock panics.
|
||||
func (cm *ClosableMutex) MustLock() {
|
||||
_, ok := <-cm.ch
|
||||
if !ok {
|
||||
panic("mutex closed")
|
||||
}
|
||||
}
|
||||
|
||||
// Unlock unlocks cm.
|
||||
func (cm *ClosableMutex) Unlock() {
|
||||
select {
|
||||
case cm.ch <- struct{}{}:
|
||||
default:
|
||||
panic("Unlock of already-unlocked ClosableMutex")
|
||||
}
|
||||
}
|
||||
|
||||
// Close locks the mutex, then closes it.
|
||||
func (cm *ClosableMutex) Close() {
|
||||
_, ok := <-cm.ch
|
||||
if !ok {
|
||||
panic("Close of already-closed ClosableMutex")
|
||||
}
|
||||
close(cm.ch)
|
||||
}
|
||||
|
|
@ -1,203 +0,0 @@
|
|||
// Copyright 2019 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Package testlog provides a log handler for unit tests.
|
||||
package testlog
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"golang.org/x/exp/slog"
|
||||
)
|
||||
|
||||
const (
|
||||
termTimeFormat = "01-02|15:04:05.000"
|
||||
)
|
||||
|
||||
// logger implements log.Logger such that all output goes to the unit test log via
|
||||
// t.Logf(). All methods in between logger.Trace, logger.Debug, etc. are marked as test
|
||||
// helpers, so the file and line number in unit test output correspond to the call site
|
||||
// which emitted the log message.
|
||||
type logger struct {
|
||||
t *testing.T
|
||||
l log.Logger
|
||||
mu *sync.Mutex
|
||||
h *bufHandler
|
||||
}
|
||||
|
||||
type bufHandler struct {
|
||||
buf []slog.Record
|
||||
attrs []slog.Attr
|
||||
level slog.Level
|
||||
}
|
||||
|
||||
func (h *bufHandler) Handle(_ context.Context, r slog.Record) error {
|
||||
h.buf = append(h.buf, r)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *bufHandler) Enabled(_ context.Context, lvl slog.Level) bool {
|
||||
return lvl <= h.level
|
||||
}
|
||||
|
||||
func (h *bufHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
|
||||
records := make([]slog.Record, len(h.buf))
|
||||
copy(records[:], h.buf[:])
|
||||
return &bufHandler{
|
||||
records,
|
||||
append(h.attrs, attrs...),
|
||||
h.level,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *bufHandler) WithGroup(_ string) slog.Handler {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
// Logger returns a logger which logs to the unit test log of t.
|
||||
func Logger(t *testing.T, level slog.Level) log.Logger {
|
||||
handler := bufHandler{
|
||||
[]slog.Record{},
|
||||
[]slog.Attr{},
|
||||
level,
|
||||
}
|
||||
return &logger{
|
||||
t: t,
|
||||
l: log.NewLogger(&handler),
|
||||
mu: new(sync.Mutex),
|
||||
h: &handler,
|
||||
}
|
||||
}
|
||||
|
||||
// LoggerWithHandler returns
|
||||
func LoggerWithHandler(t *testing.T, handler slog.Handler) log.Logger {
|
||||
var bh bufHandler
|
||||
return &logger{
|
||||
t: t,
|
||||
l: log.NewLogger(handler),
|
||||
mu: new(sync.Mutex),
|
||||
h: &bh,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *logger) Write(level slog.Level, msg string, ctx ...interface{}) {}
|
||||
|
||||
func (l *logger) Enabled(ctx context.Context, level slog.Level) bool {
|
||||
return l.l.Enabled(ctx, level)
|
||||
}
|
||||
|
||||
func (l *logger) Trace(msg string, ctx ...interface{}) {
|
||||
l.t.Helper()
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
l.l.Trace(msg, ctx...)
|
||||
l.flush()
|
||||
}
|
||||
|
||||
func (l *logger) Log(level slog.Level, msg string, ctx ...interface{}) {
|
||||
l.t.Helper()
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
l.l.Log(level, msg, ctx...)
|
||||
l.flush()
|
||||
}
|
||||
|
||||
func (l *logger) Debug(msg string, ctx ...interface{}) {
|
||||
l.t.Helper()
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
l.l.Debug(msg, ctx...)
|
||||
l.flush()
|
||||
}
|
||||
|
||||
func (l *logger) Info(msg string, ctx ...interface{}) {
|
||||
l.t.Helper()
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
l.l.Info(msg, ctx...)
|
||||
l.flush()
|
||||
}
|
||||
|
||||
func (l *logger) Warn(msg string, ctx ...interface{}) {
|
||||
l.t.Helper()
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
l.l.Warn(msg, ctx...)
|
||||
l.flush()
|
||||
}
|
||||
|
||||
func (l *logger) Error(msg string, ctx ...interface{}) {
|
||||
l.t.Helper()
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
l.l.Error(msg, ctx...)
|
||||
l.flush()
|
||||
}
|
||||
|
||||
func (l *logger) Crit(msg string, ctx ...interface{}) {
|
||||
l.t.Helper()
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
l.l.Crit(msg, ctx...)
|
||||
l.flush()
|
||||
}
|
||||
|
||||
func (l *logger) With(ctx ...interface{}) log.Logger {
|
||||
return &logger{l.t, l.l.With(ctx...), l.mu, l.h}
|
||||
}
|
||||
|
||||
func (l *logger) New(ctx ...interface{}) log.Logger {
|
||||
return l.With(ctx...)
|
||||
}
|
||||
|
||||
// terminalFormat formats a message similarly to the NewTerminalHandler in the log package.
|
||||
// The difference is that terminalFormat does not escape messages/attributes and does not pad attributes.
|
||||
func (h *bufHandler) terminalFormat(r slog.Record) string {
|
||||
buf := &bytes.Buffer{}
|
||||
lvl := log.LevelAlignedString(r.Level)
|
||||
attrs := []slog.Attr{}
|
||||
r.Attrs(func(attr slog.Attr) bool {
|
||||
attrs = append(attrs, attr)
|
||||
return true
|
||||
})
|
||||
|
||||
attrs = append(h.attrs, attrs...)
|
||||
|
||||
fmt.Fprintf(buf, "%s[%s] %s ", lvl, r.Time.Format(termTimeFormat), r.Message)
|
||||
if length := len(r.Message); length < 40 {
|
||||
buf.Write(bytes.Repeat([]byte{' '}, 40-length))
|
||||
}
|
||||
|
||||
for _, attr := range attrs {
|
||||
fmt.Fprintf(buf, " %s=%s", attr.Key, string(log.FormatSlogValue(attr.Value, nil)))
|
||||
}
|
||||
buf.WriteByte('\n')
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// flush writes all buffered messages and clears the buffer.
|
||||
func (l *logger) flush() {
|
||||
l.t.Helper()
|
||||
for _, r := range l.h.buf {
|
||||
l.t.Logf("%s", l.h.terminalFormat(r))
|
||||
}
|
||||
l.h.buf = nil
|
||||
}
|
||||
|
|
@ -1,338 +0,0 @@
|
|||
// Copyright 2020 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Package utesting provides a standalone replacement for package testing.
|
||||
//
|
||||
// This package exists because package testing cannot easily be embedded into a
|
||||
// standalone go program. It provides an API that mirrors the standard library
|
||||
// testing API.
|
||||
package utesting
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Test represents a single test.
|
||||
type Test struct {
|
||||
Name string
|
||||
Fn func(*T)
|
||||
}
|
||||
|
||||
// Result is the result of a test execution.
|
||||
type Result struct {
|
||||
Name string
|
||||
Failed bool
|
||||
Output string
|
||||
Duration time.Duration
|
||||
}
|
||||
|
||||
// MatchTests returns the tests whose name matches a regular expression.
|
||||
func MatchTests(tests []Test, expr string) []Test {
|
||||
var results []Test
|
||||
re, err := regexp.Compile(expr)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
for _, test := range tests {
|
||||
if re.MatchString(test.Name) {
|
||||
results = append(results, test)
|
||||
}
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
// RunTests executes all given tests in order and returns their results.
|
||||
// If the report writer is non-nil, a test report is written to it in real time.
|
||||
func RunTests(tests []Test, report io.Writer) []Result {
|
||||
if report == nil {
|
||||
report = io.Discard
|
||||
}
|
||||
results := run(tests, newConsoleOutput(report))
|
||||
fails := CountFailures(results)
|
||||
fmt.Fprintf(report, "%v/%v tests passed.\n", len(tests)-fails, len(tests))
|
||||
return results
|
||||
}
|
||||
|
||||
// RunTAP runs the given tests and writes Test Anything Protocol output
|
||||
// to the report writer.
|
||||
func RunTAP(tests []Test, report io.Writer) []Result {
|
||||
return run(tests, newTAP(report, len(tests)))
|
||||
}
|
||||
|
||||
func run(tests []Test, output testOutput) []Result {
|
||||
var results = make([]Result, len(tests))
|
||||
for i, test := range tests {
|
||||
buffer := new(bytes.Buffer)
|
||||
logOutput := io.MultiWriter(buffer, output)
|
||||
|
||||
output.testStart(test.Name)
|
||||
start := time.Now()
|
||||
results[i].Name = test.Name
|
||||
results[i].Failed = runTest(test, logOutput)
|
||||
results[i].Duration = time.Since(start)
|
||||
results[i].Output = buffer.String()
|
||||
output.testResult(results[i])
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
// testOutput is implemented by output formats.
|
||||
type testOutput interface {
|
||||
testStart(name string)
|
||||
Write([]byte) (int, error)
|
||||
testResult(Result)
|
||||
}
|
||||
|
||||
// consoleOutput prints test results similarly to go test.
|
||||
type consoleOutput struct {
|
||||
out io.Writer
|
||||
indented *indentWriter
|
||||
curTest string
|
||||
wroteHeader bool
|
||||
}
|
||||
|
||||
func newConsoleOutput(w io.Writer) *consoleOutput {
|
||||
return &consoleOutput{
|
||||
out: w,
|
||||
indented: newIndentWriter(" ", w),
|
||||
}
|
||||
}
|
||||
|
||||
// testStart signals the start of a new test.
|
||||
func (c *consoleOutput) testStart(name string) {
|
||||
c.curTest = name
|
||||
c.wroteHeader = false
|
||||
}
|
||||
|
||||
// Write handles test log output.
|
||||
func (c *consoleOutput) Write(b []byte) (int, error) {
|
||||
if !c.wroteHeader {
|
||||
// This is the first output line from the test. Print a "-- RUN" header.
|
||||
fmt.Fprintln(c.out, "-- RUN", c.curTest)
|
||||
c.wroteHeader = true
|
||||
}
|
||||
return c.indented.Write(b)
|
||||
}
|
||||
|
||||
// testResult prints the final test result line.
|
||||
func (c *consoleOutput) testResult(r Result) {
|
||||
c.indented.flush()
|
||||
pd := r.Duration.Truncate(100 * time.Microsecond)
|
||||
if r.Failed {
|
||||
fmt.Fprintf(c.out, "-- FAIL %s (%v)\n", r.Name, pd)
|
||||
} else {
|
||||
fmt.Fprintf(c.out, "-- OK %s (%v)\n", r.Name, pd)
|
||||
}
|
||||
}
|
||||
|
||||
// tapOutput produces Test Anything Protocol v13 output.
|
||||
type tapOutput struct {
|
||||
out io.Writer
|
||||
indented *indentWriter
|
||||
counter int
|
||||
}
|
||||
|
||||
func newTAP(out io.Writer, numTests int) *tapOutput {
|
||||
fmt.Fprintf(out, "1..%d\n", numTests)
|
||||
return &tapOutput{
|
||||
out: out,
|
||||
indented: newIndentWriter("# ", out),
|
||||
}
|
||||
}
|
||||
|
||||
func (t *tapOutput) testStart(name string) {
|
||||
t.counter++
|
||||
}
|
||||
|
||||
// Write does nothing for TAP because there is no real-time output of test logs.
|
||||
func (t *tapOutput) Write(b []byte) (int, error) {
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
func (t *tapOutput) testResult(r Result) {
|
||||
status := "ok"
|
||||
if r.Failed {
|
||||
status = "not ok"
|
||||
}
|
||||
fmt.Fprintln(t.out, status, t.counter, r.Name)
|
||||
t.indented.Write([]byte(r.Output))
|
||||
t.indented.flush()
|
||||
}
|
||||
|
||||
// indentWriter indents all written text.
|
||||
type indentWriter struct {
|
||||
out io.Writer
|
||||
indent string
|
||||
inLine bool
|
||||
}
|
||||
|
||||
func newIndentWriter(indent string, out io.Writer) *indentWriter {
|
||||
return &indentWriter{out: out, indent: indent}
|
||||
}
|
||||
|
||||
func (w *indentWriter) Write(b []byte) (n int, err error) {
|
||||
for len(b) > 0 {
|
||||
if !w.inLine {
|
||||
if _, err = io.WriteString(w.out, w.indent); err != nil {
|
||||
return n, err
|
||||
}
|
||||
w.inLine = true
|
||||
}
|
||||
|
||||
end := bytes.IndexByte(b, '\n')
|
||||
if end == -1 {
|
||||
nn, err := w.out.Write(b)
|
||||
n += nn
|
||||
return n, err
|
||||
}
|
||||
|
||||
line := b[:end+1]
|
||||
nn, err := w.out.Write(line)
|
||||
n += nn
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
b = b[end+1:]
|
||||
w.inLine = false
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
// flush ensures the current line is terminated.
|
||||
func (w *indentWriter) flush() {
|
||||
if w.inLine {
|
||||
fmt.Println(w.out)
|
||||
w.inLine = false
|
||||
}
|
||||
}
|
||||
|
||||
// CountFailures returns the number of failed tests in the result slice.
|
||||
func CountFailures(rr []Result) int {
|
||||
count := 0
|
||||
for _, r := range rr {
|
||||
if r.Failed {
|
||||
count++
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
// Run executes a single test.
|
||||
func Run(test Test) (bool, string) {
|
||||
output := new(bytes.Buffer)
|
||||
failed := runTest(test, output)
|
||||
return failed, output.String()
|
||||
}
|
||||
|
||||
func runTest(test Test, output io.Writer) bool {
|
||||
t := &T{output: output}
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
defer close(done)
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
buf := make([]byte, 4096)
|
||||
i := runtime.Stack(buf, false)
|
||||
t.Logf("panic: %v\n\n%s", err, buf[:i])
|
||||
t.Fail()
|
||||
}
|
||||
}()
|
||||
test.Fn(t)
|
||||
}()
|
||||
<-done
|
||||
return t.failed
|
||||
}
|
||||
|
||||
// T is the value given to the test function. The test can signal failures
|
||||
// and log output by calling methods on this object.
|
||||
type T struct {
|
||||
mu sync.Mutex
|
||||
failed bool
|
||||
output io.Writer
|
||||
}
|
||||
|
||||
// Helper exists for compatibility with testing.T.
|
||||
func (t *T) Helper() {}
|
||||
|
||||
// FailNow marks the test as having failed and stops its execution by calling
|
||||
// runtime.Goexit (which then runs all deferred calls in the current goroutine).
|
||||
func (t *T) FailNow() {
|
||||
t.Fail()
|
||||
runtime.Goexit()
|
||||
}
|
||||
|
||||
// Fail marks the test as having failed but continues execution.
|
||||
func (t *T) Fail() {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
t.failed = true
|
||||
}
|
||||
|
||||
// Failed reports whether the test has failed.
|
||||
func (t *T) Failed() bool {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
return t.failed
|
||||
}
|
||||
|
||||
// Log formats its arguments using default formatting, analogous to Println, and records
|
||||
// the text in the error log.
|
||||
func (t *T) Log(vs ...interface{}) {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
fmt.Fprintln(t.output, vs...)
|
||||
}
|
||||
|
||||
// Logf formats its arguments according to the format, analogous to Printf, and records
|
||||
// the text in the error log. A final newline is added if not provided.
|
||||
func (t *T) Logf(format string, vs ...interface{}) {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
if len(format) == 0 || format[len(format)-1] != '\n' {
|
||||
format += "\n"
|
||||
}
|
||||
fmt.Fprintf(t.output, format, vs...)
|
||||
}
|
||||
|
||||
// Error is equivalent to Log followed by Fail.
|
||||
func (t *T) Error(vs ...interface{}) {
|
||||
t.Log(vs...)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
// Errorf is equivalent to Logf followed by Fail.
|
||||
func (t *T) Errorf(format string, vs ...interface{}) {
|
||||
t.Logf(format, vs...)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
// Fatal is equivalent to Log followed by FailNow.
|
||||
func (t *T) Fatal(vs ...interface{}) {
|
||||
t.Log(vs...)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// Fatalf is equivalent to Logf followed by FailNow.
|
||||
func (t *T) Fatalf(format string, vs ...interface{}) {
|
||||
t.Logf(format, vs...)
|
||||
t.FailNow()
|
||||
}
|
||||
|
|
@ -1,139 +0,0 @@
|
|||
// Copyright 2020 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package utesting
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTest(t *testing.T) {
|
||||
tests := []Test{
|
||||
{
|
||||
Name: "successful test",
|
||||
Fn: func(t *T) {},
|
||||
},
|
||||
{
|
||||
Name: "failing test",
|
||||
Fn: func(t *T) {
|
||||
t.Log("output")
|
||||
t.Error("failed")
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "panicking test",
|
||||
Fn: func(t *T) {
|
||||
panic("oh no")
|
||||
},
|
||||
},
|
||||
}
|
||||
results := RunTests(tests, nil)
|
||||
|
||||
if results[0].Failed || results[0].Output != "" {
|
||||
t.Fatalf("wrong result for successful test: %#v", results[0])
|
||||
}
|
||||
if !results[1].Failed || results[1].Output != "output\nfailed\n" {
|
||||
t.Fatalf("wrong result for failing test: %#v", results[1])
|
||||
}
|
||||
if !results[2].Failed || !strings.HasPrefix(results[2].Output, "panic: oh no\n") {
|
||||
t.Fatalf("wrong result for panicking test: %#v", results[2])
|
||||
}
|
||||
}
|
||||
|
||||
var outputTests = []Test{
|
||||
{
|
||||
Name: "TestWithLogs",
|
||||
Fn: func(t *T) {
|
||||
t.Log("output line 1")
|
||||
t.Log("output line 2\noutput line 3")
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "TestNoLogs",
|
||||
Fn: func(t *T) {},
|
||||
},
|
||||
{
|
||||
Name: "FailWithLogs",
|
||||
Fn: func(t *T) {
|
||||
t.Log("output line 1")
|
||||
t.Error("failed 1")
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "FailMessage",
|
||||
Fn: func(t *T) {
|
||||
t.Error("failed 2")
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "FailNoOutput",
|
||||
Fn: func(t *T) {
|
||||
t.Fail()
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestOutput(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
RunTests(outputTests, &buf)
|
||||
|
||||
want := regexp.MustCompile(`
|
||||
^-- RUN TestWithLogs
|
||||
output line 1
|
||||
output line 2
|
||||
output line 3
|
||||
-- OK TestWithLogs \([^)]+\)
|
||||
-- OK TestNoLogs \([^)]+\)
|
||||
-- RUN FailWithLogs
|
||||
output line 1
|
||||
failed 1
|
||||
-- FAIL FailWithLogs \([^)]+\)
|
||||
-- RUN FailMessage
|
||||
failed 2
|
||||
-- FAIL FailMessage \([^)]+\)
|
||||
-- FAIL FailNoOutput \([^)]+\)
|
||||
2/5 tests passed.
|
||||
$`[1:])
|
||||
if !want.MatchString(buf.String()) {
|
||||
t.Fatalf("output does not match: %q", buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestOutputTAP(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
RunTAP(outputTests, &buf)
|
||||
|
||||
want := `
|
||||
1..5
|
||||
ok 1 TestWithLogs
|
||||
# output line 1
|
||||
# output line 2
|
||||
# output line 3
|
||||
ok 2 TestNoLogs
|
||||
not ok 3 FailWithLogs
|
||||
# output line 1
|
||||
# failed 1
|
||||
not ok 4 FailMessage
|
||||
# failed 2
|
||||
not ok 5 FailNoOutput
|
||||
`
|
||||
if buf.String() != want[1:] {
|
||||
t.Fatalf("output does not match: %q", buf.String())
|
||||
}
|
||||
}
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
// Copyright 2022 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package version
|
||||
|
||||
import (
|
||||
"runtime/debug"
|
||||
"time"
|
||||
)
|
||||
|
||||
// In go 1.18 and beyond, the go tool embeds VCS information into the build.
|
||||
|
||||
const (
|
||||
govcsTimeLayout = "2006-01-02T15:04:05Z"
|
||||
ourTimeLayout = "20060102"
|
||||
)
|
||||
|
||||
// buildInfoVCS returns VCS information of the build.
|
||||
func buildInfoVCS(info *debug.BuildInfo) (s VCSInfo, ok bool) {
|
||||
for _, v := range info.Settings {
|
||||
switch v.Key {
|
||||
case "vcs.revision":
|
||||
s.Commit = v.Value
|
||||
case "vcs.modified":
|
||||
if v.Value == "true" {
|
||||
s.Dirty = true
|
||||
}
|
||||
case "vcs.time":
|
||||
t, err := time.Parse(govcsTimeLayout, v.Value)
|
||||
if err == nil {
|
||||
s.Date = t.Format(ourTimeLayout)
|
||||
}
|
||||
}
|
||||
}
|
||||
if s.Commit != "" && s.Date != "" {
|
||||
ok = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
@ -1,141 +0,0 @@
|
|||
// Copyright 2022 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Package version implements reading of build version information.
|
||||
package version
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
const ourPath = "github.com/ethereum/go-ethereum" // Path to our module
|
||||
|
||||
// These variables are set at build-time by the linker when the build is
|
||||
// done by build/ci.go.
|
||||
var gitCommit, gitDate string
|
||||
|
||||
// VCSInfo represents the git repository state.
|
||||
type VCSInfo struct {
|
||||
Commit string // head commit hash
|
||||
Date string // commit time in YYYYMMDD format
|
||||
Dirty bool
|
||||
}
|
||||
|
||||
// VCS returns version control information of the current executable.
|
||||
func VCS() (VCSInfo, bool) {
|
||||
if gitCommit != "" {
|
||||
// Use information set by the build script if present.
|
||||
return VCSInfo{Commit: gitCommit, Date: gitDate}, true
|
||||
}
|
||||
if buildInfo, ok := debug.ReadBuildInfo(); ok {
|
||||
if buildInfo.Main.Path == ourPath {
|
||||
return buildInfoVCS(buildInfo)
|
||||
}
|
||||
}
|
||||
return VCSInfo{}, false
|
||||
}
|
||||
|
||||
// ClientName creates a software name/version identifier according to common
|
||||
// conventions in the Ethereum p2p network.
|
||||
func ClientName(clientIdentifier string) string {
|
||||
git, _ := VCS()
|
||||
return fmt.Sprintf("%s/v%v/%v-%v/%v",
|
||||
strings.Title(clientIdentifier),
|
||||
params.VersionWithCommit(git.Commit, git.Date),
|
||||
runtime.GOOS, runtime.GOARCH,
|
||||
runtime.Version(),
|
||||
)
|
||||
}
|
||||
|
||||
// runtimeInfo returns build and platform information about the current binary.
|
||||
//
|
||||
// If the package that is currently executing is a prefixed by our go-ethereum
|
||||
// module path, it will print out commit and date VCS information. Otherwise,
|
||||
// it will assume it's imported by a third-party and will return the imported
|
||||
// version and whether it was replaced by another module.
|
||||
func Info() (version, vcs string) {
|
||||
version = params.VersionWithMeta
|
||||
buildInfo, ok := debug.ReadBuildInfo()
|
||||
if !ok {
|
||||
return version, ""
|
||||
}
|
||||
version = versionInfo(buildInfo)
|
||||
if status, ok := VCS(); ok {
|
||||
modified := ""
|
||||
if status.Dirty {
|
||||
modified = " (dirty)"
|
||||
}
|
||||
commit := status.Commit
|
||||
if len(commit) > 8 {
|
||||
commit = commit[:8]
|
||||
}
|
||||
vcs = commit + "-" + status.Date + modified
|
||||
}
|
||||
return version, vcs
|
||||
}
|
||||
|
||||
// versionInfo returns version information for the currently executing
|
||||
// implementation.
|
||||
//
|
||||
// Depending on how the code is instantiated, it returns different amounts of
|
||||
// information. If it is unable to determine which module is related to our
|
||||
// package it falls back to the hardcoded values in the params package.
|
||||
func versionInfo(info *debug.BuildInfo) string {
|
||||
// If the main package is from our repo, prefix version with "geth".
|
||||
if strings.HasPrefix(info.Path, ourPath) {
|
||||
return fmt.Sprintf("geth %s", info.Main.Version)
|
||||
}
|
||||
// Not our main package, so explicitly print out the module path and
|
||||
// version.
|
||||
var version string
|
||||
if info.Main.Path != "" && info.Main.Version != "" {
|
||||
// These can be empty when invoked with "go run".
|
||||
version = fmt.Sprintf("%s@%s ", info.Main.Path, info.Main.Version)
|
||||
}
|
||||
mod := findModule(info, ourPath)
|
||||
if mod == nil {
|
||||
// If our module path wasn't imported, it's unclear which
|
||||
// version of our code they are running. Fallback to hardcoded
|
||||
// version.
|
||||
return version + fmt.Sprintf("geth %s", params.VersionWithMeta)
|
||||
}
|
||||
// Our package is a dependency for the main module. Return path and
|
||||
// version data for both.
|
||||
version += fmt.Sprintf("%s@%s", mod.Path, mod.Version)
|
||||
if mod.Replace != nil {
|
||||
// If our package was replaced by something else, also note that.
|
||||
version += fmt.Sprintf(" (replaced by %s@%s)", mod.Replace.Path, mod.Replace.Version)
|
||||
}
|
||||
return version
|
||||
}
|
||||
|
||||
// findModule returns the module at path.
|
||||
func findModule(info *debug.BuildInfo, path string) *debug.Module {
|
||||
if info.Path == ourPath {
|
||||
return &info.Main
|
||||
}
|
||||
for _, mod := range info.Deps {
|
||||
if mod.Path == path {
|
||||
return mod
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1,913 +0,0 @@
|
|||
// Copyright 2015 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Package web3ext contains geth specific web3.js extensions.
|
||||
package web3ext
|
||||
|
||||
var Modules = map[string]string{
|
||||
"admin": AdminJs,
|
||||
"clique": CliqueJs,
|
||||
"ethash": EthashJs,
|
||||
"debug": DebugJs,
|
||||
"eth": EthJs,
|
||||
"miner": MinerJs,
|
||||
"net": NetJs,
|
||||
"personal": PersonalJs,
|
||||
"rpc": RpcJs,
|
||||
"txpool": TxpoolJs,
|
||||
"les": LESJs,
|
||||
"vflux": VfluxJs,
|
||||
"dev": DevJs,
|
||||
}
|
||||
|
||||
const CliqueJs = `
|
||||
web3._extend({
|
||||
property: 'clique',
|
||||
methods: [
|
||||
new web3._extend.Method({
|
||||
name: 'getSnapshot',
|
||||
call: 'clique_getSnapshot',
|
||||
params: 1,
|
||||
inputFormatter: [web3._extend.formatters.inputBlockNumberFormatter]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'getSnapshotAtHash',
|
||||
call: 'clique_getSnapshotAtHash',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'getSigners',
|
||||
call: 'clique_getSigners',
|
||||
params: 1,
|
||||
inputFormatter: [web3._extend.formatters.inputBlockNumberFormatter]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'getSignersAtHash',
|
||||
call: 'clique_getSignersAtHash',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'propose',
|
||||
call: 'clique_propose',
|
||||
params: 2
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'discard',
|
||||
call: 'clique_discard',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'status',
|
||||
call: 'clique_status',
|
||||
params: 0
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'getSigner',
|
||||
call: 'clique_getSigner',
|
||||
params: 1,
|
||||
inputFormatter: [null]
|
||||
}),
|
||||
],
|
||||
properties: [
|
||||
new web3._extend.Property({
|
||||
name: 'proposals',
|
||||
getter: 'clique_proposals'
|
||||
}),
|
||||
]
|
||||
});
|
||||
`
|
||||
|
||||
const EthashJs = `
|
||||
web3._extend({
|
||||
property: 'ethash',
|
||||
methods: [
|
||||
new web3._extend.Method({
|
||||
name: 'getWork',
|
||||
call: 'ethash_getWork',
|
||||
params: 0
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'getHashrate',
|
||||
call: 'ethash_getHashrate',
|
||||
params: 0
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'submitWork',
|
||||
call: 'ethash_submitWork',
|
||||
params: 3,
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'submitHashrate',
|
||||
call: 'ethash_submitHashrate',
|
||||
params: 2,
|
||||
}),
|
||||
]
|
||||
});
|
||||
`
|
||||
|
||||
const AdminJs = `
|
||||
web3._extend({
|
||||
property: 'admin',
|
||||
methods: [
|
||||
new web3._extend.Method({
|
||||
name: 'addPeer',
|
||||
call: 'admin_addPeer',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'removePeer',
|
||||
call: 'admin_removePeer',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'addTrustedPeer',
|
||||
call: 'admin_addTrustedPeer',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'removeTrustedPeer',
|
||||
call: 'admin_removeTrustedPeer',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'exportChain',
|
||||
call: 'admin_exportChain',
|
||||
params: 3,
|
||||
inputFormatter: [null, null, null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'importChain',
|
||||
call: 'admin_importChain',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'sleepBlocks',
|
||||
call: 'admin_sleepBlocks',
|
||||
params: 2
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'startHTTP',
|
||||
call: 'admin_startHTTP',
|
||||
params: 5,
|
||||
inputFormatter: [null, null, null, null, null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'stopHTTP',
|
||||
call: 'admin_stopHTTP'
|
||||
}),
|
||||
// This method is deprecated.
|
||||
new web3._extend.Method({
|
||||
name: 'startRPC',
|
||||
call: 'admin_startRPC',
|
||||
params: 5,
|
||||
inputFormatter: [null, null, null, null, null]
|
||||
}),
|
||||
// This method is deprecated.
|
||||
new web3._extend.Method({
|
||||
name: 'stopRPC',
|
||||
call: 'admin_stopRPC'
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'startWS',
|
||||
call: 'admin_startWS',
|
||||
params: 4,
|
||||
inputFormatter: [null, null, null, null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'stopWS',
|
||||
call: 'admin_stopWS'
|
||||
}),
|
||||
],
|
||||
properties: [
|
||||
new web3._extend.Property({
|
||||
name: 'nodeInfo',
|
||||
getter: 'admin_nodeInfo'
|
||||
}),
|
||||
new web3._extend.Property({
|
||||
name: 'peers',
|
||||
getter: 'admin_peers'
|
||||
}),
|
||||
new web3._extend.Property({
|
||||
name: 'datadir',
|
||||
getter: 'admin_datadir'
|
||||
}),
|
||||
]
|
||||
});
|
||||
`
|
||||
|
||||
const DebugJs = `
|
||||
web3._extend({
|
||||
property: 'debug',
|
||||
methods: [
|
||||
new web3._extend.Method({
|
||||
name: 'accountRange',
|
||||
call: 'debug_accountRange',
|
||||
params: 6,
|
||||
inputFormatter: [web3._extend.formatters.inputDefaultBlockNumberFormatter, null, null, null, null, null],
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'printBlock',
|
||||
call: 'debug_printBlock',
|
||||
params: 1,
|
||||
outputFormatter: console.log
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'getRawHeader',
|
||||
call: 'debug_getRawHeader',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'getRawBlock',
|
||||
call: 'debug_getRawBlock',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'getRawReceipts',
|
||||
call: 'debug_getRawReceipts',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'getRawTransaction',
|
||||
call: 'debug_getRawTransaction',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'setHead',
|
||||
call: 'debug_setHead',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'seedHash',
|
||||
call: 'debug_seedHash',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'dumpBlock',
|
||||
call: 'debug_dumpBlock',
|
||||
params: 1,
|
||||
inputFormatter: [web3._extend.formatters.inputBlockNumberFormatter]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'chaindbProperty',
|
||||
call: 'debug_chaindbProperty',
|
||||
params: 1,
|
||||
outputFormatter: console.log
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'chaindbCompact',
|
||||
call: 'debug_chaindbCompact',
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'verbosity',
|
||||
call: 'debug_verbosity',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'vmodule',
|
||||
call: 'debug_vmodule',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'backtraceAt',
|
||||
call: 'debug_backtraceAt',
|
||||
params: 1,
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'stacks',
|
||||
call: 'debug_stacks',
|
||||
params: 1,
|
||||
inputFormatter: [null],
|
||||
outputFormatter: console.log
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'freeOSMemory',
|
||||
call: 'debug_freeOSMemory',
|
||||
params: 0,
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'setGCPercent',
|
||||
call: 'debug_setGCPercent',
|
||||
params: 1,
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'memStats',
|
||||
call: 'debug_memStats',
|
||||
params: 0,
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'gcStats',
|
||||
call: 'debug_gcStats',
|
||||
params: 0,
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'cpuProfile',
|
||||
call: 'debug_cpuProfile',
|
||||
params: 2
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'startCPUProfile',
|
||||
call: 'debug_startCPUProfile',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'stopCPUProfile',
|
||||
call: 'debug_stopCPUProfile',
|
||||
params: 0
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'goTrace',
|
||||
call: 'debug_goTrace',
|
||||
params: 2
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'startGoTrace',
|
||||
call: 'debug_startGoTrace',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'stopGoTrace',
|
||||
call: 'debug_stopGoTrace',
|
||||
params: 0
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'blockProfile',
|
||||
call: 'debug_blockProfile',
|
||||
params: 2
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'setBlockProfileRate',
|
||||
call: 'debug_setBlockProfileRate',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'writeBlockProfile',
|
||||
call: 'debug_writeBlockProfile',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'mutexProfile',
|
||||
call: 'debug_mutexProfile',
|
||||
params: 2
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'setMutexProfileFraction',
|
||||
call: 'debug_setMutexProfileFraction',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'writeMutexProfile',
|
||||
call: 'debug_writeMutexProfile',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'writeMemProfile',
|
||||
call: 'debug_writeMemProfile',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'traceBlock',
|
||||
call: 'debug_traceBlock',
|
||||
params: 2,
|
||||
inputFormatter: [null, null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'traceBlockFromFile',
|
||||
call: 'debug_traceBlockFromFile',
|
||||
params: 2,
|
||||
inputFormatter: [null, null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'traceBadBlock',
|
||||
call: 'debug_traceBadBlock',
|
||||
params: 1,
|
||||
inputFormatter: [null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'standardTraceBadBlockToFile',
|
||||
call: 'debug_standardTraceBadBlockToFile',
|
||||
params: 2,
|
||||
inputFormatter: [null, null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'intermediateRoots',
|
||||
call: 'debug_intermediateRoots',
|
||||
params: 2,
|
||||
inputFormatter: [null, null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'standardTraceBlockToFile',
|
||||
call: 'debug_standardTraceBlockToFile',
|
||||
params: 2,
|
||||
inputFormatter: [null, null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'traceBlockByNumber',
|
||||
call: 'debug_traceBlockByNumber',
|
||||
params: 2,
|
||||
inputFormatter: [web3._extend.formatters.inputBlockNumberFormatter, null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'traceBlockByHash',
|
||||
call: 'debug_traceBlockByHash',
|
||||
params: 2,
|
||||
inputFormatter: [null, null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'traceTransaction',
|
||||
call: 'debug_traceTransaction',
|
||||
params: 2,
|
||||
inputFormatter: [null, null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'traceCall',
|
||||
call: 'debug_traceCall',
|
||||
params: 3,
|
||||
inputFormatter: [null, null, null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'preimage',
|
||||
call: 'debug_preimage',
|
||||
params: 1,
|
||||
inputFormatter: [null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'getBadBlocks',
|
||||
call: 'debug_getBadBlocks',
|
||||
params: 0,
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'storageRangeAt',
|
||||
call: 'debug_storageRangeAt',
|
||||
params: 5,
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'getModifiedAccountsByNumber',
|
||||
call: 'debug_getModifiedAccountsByNumber',
|
||||
params: 2,
|
||||
inputFormatter: [null, null],
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'getModifiedAccountsByHash',
|
||||
call: 'debug_getModifiedAccountsByHash',
|
||||
params: 2,
|
||||
inputFormatter:[null, null],
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'freezeClient',
|
||||
call: 'debug_freezeClient',
|
||||
params: 1,
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'getAccessibleState',
|
||||
call: 'debug_getAccessibleState',
|
||||
params: 2,
|
||||
inputFormatter:[web3._extend.formatters.inputBlockNumberFormatter, web3._extend.formatters.inputBlockNumberFormatter],
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'dbGet',
|
||||
call: 'debug_dbGet',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'dbAncient',
|
||||
call: 'debug_dbAncient',
|
||||
params: 2
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'dbAncients',
|
||||
call: 'debug_dbAncients',
|
||||
params: 0
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'setTrieFlushInterval',
|
||||
call: 'debug_setTrieFlushInterval',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'getTrieFlushInterval',
|
||||
call: 'debug_getTrieFlushInterval',
|
||||
params: 0
|
||||
}),
|
||||
],
|
||||
properties: []
|
||||
});
|
||||
`
|
||||
|
||||
const EthJs = `
|
||||
web3._extend({
|
||||
property: 'eth',
|
||||
methods: [
|
||||
new web3._extend.Method({
|
||||
name: 'chainId',
|
||||
call: 'eth_chainId',
|
||||
params: 0
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'sign',
|
||||
call: 'eth_sign',
|
||||
params: 2,
|
||||
inputFormatter: [web3._extend.formatters.inputAddressFormatter, null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'resend',
|
||||
call: 'eth_resend',
|
||||
params: 3,
|
||||
inputFormatter: [web3._extend.formatters.inputTransactionFormatter, web3._extend.utils.fromDecimal, web3._extend.utils.fromDecimal]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'signTransaction',
|
||||
call: 'eth_signTransaction',
|
||||
params: 1,
|
||||
inputFormatter: [web3._extend.formatters.inputTransactionFormatter]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'estimateGas',
|
||||
call: 'eth_estimateGas',
|
||||
params: 3,
|
||||
inputFormatter: [web3._extend.formatters.inputCallFormatter, web3._extend.formatters.inputBlockNumberFormatter, null],
|
||||
outputFormatter: web3._extend.utils.toDecimal
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'submitTransaction',
|
||||
call: 'eth_submitTransaction',
|
||||
params: 1,
|
||||
inputFormatter: [web3._extend.formatters.inputTransactionFormatter]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'fillTransaction',
|
||||
call: 'eth_fillTransaction',
|
||||
params: 1,
|
||||
inputFormatter: [web3._extend.formatters.inputTransactionFormatter]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'getHeaderByNumber',
|
||||
call: 'eth_getHeaderByNumber',
|
||||
params: 1,
|
||||
inputFormatter: [web3._extend.formatters.inputBlockNumberFormatter]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'getHeaderByHash',
|
||||
call: 'eth_getHeaderByHash',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'getBlockByNumber',
|
||||
call: 'eth_getBlockByNumber',
|
||||
params: 2,
|
||||
inputFormatter: [web3._extend.formatters.inputBlockNumberFormatter, function (val) { return !!val; }]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'getBlockByHash',
|
||||
call: 'eth_getBlockByHash',
|
||||
params: 2,
|
||||
inputFormatter: [null, function (val) { return !!val; }]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'getRawTransaction',
|
||||
call: 'eth_getRawTransactionByHash',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'getRawTransactionFromBlock',
|
||||
call: function(args) {
|
||||
return (web3._extend.utils.isString(args[0]) && args[0].indexOf('0x') === 0) ? 'eth_getRawTransactionByBlockHashAndIndex' : 'eth_getRawTransactionByBlockNumberAndIndex';
|
||||
},
|
||||
params: 2,
|
||||
inputFormatter: [web3._extend.formatters.inputBlockNumberFormatter, web3._extend.utils.toHex]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'getProof',
|
||||
call: 'eth_getProof',
|
||||
params: 3,
|
||||
inputFormatter: [web3._extend.formatters.inputAddressFormatter, null, web3._extend.formatters.inputBlockNumberFormatter]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'createAccessList',
|
||||
call: 'eth_createAccessList',
|
||||
params: 2,
|
||||
inputFormatter: [null, web3._extend.formatters.inputBlockNumberFormatter],
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'feeHistory',
|
||||
call: 'eth_feeHistory',
|
||||
params: 3,
|
||||
inputFormatter: [null, web3._extend.formatters.inputBlockNumberFormatter, null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'getLogs',
|
||||
call: 'eth_getLogs',
|
||||
params: 1,
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'call',
|
||||
call: 'eth_call',
|
||||
params: 4,
|
||||
inputFormatter: [web3._extend.formatters.inputCallFormatter, web3._extend.formatters.inputDefaultBlockNumberFormatter, null, null],
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'getBlockReceipts',
|
||||
call: 'eth_getBlockReceipts',
|
||||
params: 1,
|
||||
}),
|
||||
],
|
||||
properties: [
|
||||
new web3._extend.Property({
|
||||
name: 'pendingTransactions',
|
||||
getter: 'eth_pendingTransactions',
|
||||
outputFormatter: function(txs) {
|
||||
var formatted = [];
|
||||
for (var i = 0; i < txs.length; i++) {
|
||||
formatted.push(web3._extend.formatters.outputTransactionFormatter(txs[i]));
|
||||
formatted[i].blockHash = null;
|
||||
}
|
||||
return formatted;
|
||||
}
|
||||
}),
|
||||
new web3._extend.Property({
|
||||
name: 'maxPriorityFeePerGas',
|
||||
getter: 'eth_maxPriorityFeePerGas',
|
||||
outputFormatter: web3._extend.utils.toBigNumber
|
||||
}),
|
||||
]
|
||||
});
|
||||
`
|
||||
|
||||
const MinerJs = `
|
||||
web3._extend({
|
||||
property: 'miner',
|
||||
methods: [
|
||||
new web3._extend.Method({
|
||||
name: 'start',
|
||||
call: 'miner_start',
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'stop',
|
||||
call: 'miner_stop'
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'setEtherbase',
|
||||
call: 'miner_setEtherbase',
|
||||
params: 1,
|
||||
inputFormatter: [web3._extend.formatters.inputAddressFormatter]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'setExtra',
|
||||
call: 'miner_setExtra',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'setGasPrice',
|
||||
call: 'miner_setGasPrice',
|
||||
params: 1,
|
||||
inputFormatter: [web3._extend.utils.fromDecimal]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'setGasLimit',
|
||||
call: 'miner_setGasLimit',
|
||||
params: 1,
|
||||
inputFormatter: [web3._extend.utils.fromDecimal]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'setRecommitInterval',
|
||||
call: 'miner_setRecommitInterval',
|
||||
params: 1,
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'getHashrate',
|
||||
call: 'miner_getHashrate'
|
||||
}),
|
||||
],
|
||||
properties: []
|
||||
});
|
||||
`
|
||||
|
||||
const NetJs = `
|
||||
web3._extend({
|
||||
property: 'net',
|
||||
methods: [],
|
||||
properties: [
|
||||
new web3._extend.Property({
|
||||
name: 'version',
|
||||
getter: 'net_version'
|
||||
}),
|
||||
]
|
||||
});
|
||||
`
|
||||
|
||||
const PersonalJs = `
|
||||
web3._extend({
|
||||
property: 'personal',
|
||||
methods: [
|
||||
new web3._extend.Method({
|
||||
name: 'importRawKey',
|
||||
call: 'personal_importRawKey',
|
||||
params: 2
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'sign',
|
||||
call: 'personal_sign',
|
||||
params: 3,
|
||||
inputFormatter: [null, web3._extend.formatters.inputAddressFormatter, null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'ecRecover',
|
||||
call: 'personal_ecRecover',
|
||||
params: 2
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'openWallet',
|
||||
call: 'personal_openWallet',
|
||||
params: 2
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'deriveAccount',
|
||||
call: 'personal_deriveAccount',
|
||||
params: 3
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'signTransaction',
|
||||
call: 'personal_signTransaction',
|
||||
params: 2,
|
||||
inputFormatter: [web3._extend.formatters.inputTransactionFormatter, null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'unpair',
|
||||
call: 'personal_unpair',
|
||||
params: 2
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'initializeWallet',
|
||||
call: 'personal_initializeWallet',
|
||||
params: 1
|
||||
})
|
||||
],
|
||||
properties: [
|
||||
new web3._extend.Property({
|
||||
name: 'listWallets',
|
||||
getter: 'personal_listWallets'
|
||||
}),
|
||||
]
|
||||
})
|
||||
`
|
||||
|
||||
const RpcJs = `
|
||||
web3._extend({
|
||||
property: 'rpc',
|
||||
methods: [],
|
||||
properties: [
|
||||
new web3._extend.Property({
|
||||
name: 'modules',
|
||||
getter: 'rpc_modules'
|
||||
}),
|
||||
]
|
||||
});
|
||||
`
|
||||
|
||||
const TxpoolJs = `
|
||||
web3._extend({
|
||||
property: 'txpool',
|
||||
methods: [],
|
||||
properties:
|
||||
[
|
||||
new web3._extend.Property({
|
||||
name: 'content',
|
||||
getter: 'txpool_content'
|
||||
}),
|
||||
new web3._extend.Property({
|
||||
name: 'inspect',
|
||||
getter: 'txpool_inspect'
|
||||
}),
|
||||
new web3._extend.Property({
|
||||
name: 'status',
|
||||
getter: 'txpool_status',
|
||||
outputFormatter: function(status) {
|
||||
status.pending = web3._extend.utils.toDecimal(status.pending);
|
||||
status.queued = web3._extend.utils.toDecimal(status.queued);
|
||||
return status;
|
||||
}
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'contentFrom',
|
||||
call: 'txpool_contentFrom',
|
||||
params: 1,
|
||||
}),
|
||||
]
|
||||
});
|
||||
`
|
||||
|
||||
const LESJs = `
|
||||
web3._extend({
|
||||
property: 'les',
|
||||
methods:
|
||||
[
|
||||
new web3._extend.Method({
|
||||
name: 'getCheckpoint',
|
||||
call: 'les_getCheckpoint',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'clientInfo',
|
||||
call: 'les_clientInfo',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'priorityClientInfo',
|
||||
call: 'les_priorityClientInfo',
|
||||
params: 3
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'setClientParams',
|
||||
call: 'les_setClientParams',
|
||||
params: 2
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'setDefaultParams',
|
||||
call: 'les_setDefaultParams',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'addBalance',
|
||||
call: 'les_addBalance',
|
||||
params: 2
|
||||
}),
|
||||
],
|
||||
properties:
|
||||
[
|
||||
new web3._extend.Property({
|
||||
name: 'latestCheckpoint',
|
||||
getter: 'les_latestCheckpoint'
|
||||
}),
|
||||
new web3._extend.Property({
|
||||
name: 'checkpointContractAddress',
|
||||
getter: 'les_getCheckpointContractAddress'
|
||||
}),
|
||||
new web3._extend.Property({
|
||||
name: 'serverInfo',
|
||||
getter: 'les_serverInfo'
|
||||
}),
|
||||
]
|
||||
});
|
||||
`
|
||||
|
||||
const VfluxJs = `
|
||||
web3._extend({
|
||||
property: 'vflux',
|
||||
methods:
|
||||
[
|
||||
new web3._extend.Method({
|
||||
name: 'distribution',
|
||||
call: 'vflux_distribution',
|
||||
params: 2
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'timeout',
|
||||
call: 'vflux_timeout',
|
||||
params: 2
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'value',
|
||||
call: 'vflux_value',
|
||||
params: 2
|
||||
}),
|
||||
],
|
||||
properties:
|
||||
[
|
||||
new web3._extend.Property({
|
||||
name: 'requestStats',
|
||||
getter: 'vflux_requestStats'
|
||||
}),
|
||||
]
|
||||
});
|
||||
`
|
||||
|
||||
const DevJs = `
|
||||
web3._extend({
|
||||
property: 'dev',
|
||||
methods:
|
||||
[
|
||||
new web3._extend.Method({
|
||||
name: 'addWithdrawal',
|
||||
call: 'dev_addWithdrawal',
|
||||
params: 1
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'setFeeRecipient',
|
||||
call: 'dev_setFeeRecipient',
|
||||
params: 1
|
||||
}),
|
||||
],
|
||||
});
|
||||
`
|
||||
Loading…
Reference in a new issue