diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d1afe2413e..206643e8d7 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -3,13 +3,18 @@ accounts/usbwallet @karalabe accounts/scwallet @gballet accounts/abi @gballet @MariusVanDerWijden +beacon/engine @lightclient cmd/clef @holiman +cmd/evm @holiman @MariusVanDerWijden @lightclient consensus @karalabe core/ @karalabe @holiman @rjl493456442 eth/ @karalabe @holiman @rjl493456442 -eth/catalyst/ @gballet +eth/catalyst/ @gballet @lightclient eth/tracers/ @s1na +core/tracing/ @s1na graphql/ @s1na +internal/ethapi @lightclient +internal/era @lightclient les/ @zsfelfoldi @rjl493456442 light/ @zsfelfoldi @rjl493456442 node/ @fjl diff --git a/.golangci.yml b/.golangci.yml index 90e5c591f6..7c2b44ac42 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -6,8 +6,6 @@ run: # default is true. Enables skipping of directories: # vendor$, third_party$, testdata$, examples$, Godeps$, builtin$ skip-dirs-use-default: true - skip-files: - - core/genesis_alloc.go output: format: colored-line-number @@ -28,7 +26,10 @@ linters: - durationcheck - exportloopref - whitespace + - revive # only certain checks enabled + ### linters we tried and will not be using: + ### # - structcheck # lots of false positives # - errcheck #lot of false positives # - contextcheck @@ -41,8 +42,19 @@ linters: linters-settings: gofmt: simplify: true + revive: + enable-all-rules: false + # here we enable specific useful rules + # see https://golangci-lint.run/usage/linters/#revive for supported rules + rules: + - name: receiver-naming + severity: warning + disabled: false + exclude: [""] issues: + exclude-files: + - core/genesis_alloc.go exclude-rules: - path: core/state/metrics.go linters: @@ -50,6 +62,9 @@ issues: - path: core/txpool/legacypool/list.go linters: - staticcheck + - path: crypto/bn256/ + linters: + - revive - path: internal/build/pgp.go text: 'SA1019: "golang.org/x/crypto/openpgp" is deprecated: this package is unmaintained except for security fixes.' - path: core/vm/contracts.go diff --git a/.travis.yml b/.travis.yml index 8c0af291a3..2dc80f85ed 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,7 @@ jobs: if: type = push os: linux arch: amd64 - dist: bionic + dist: noble go: 1.22.x env: - docker @@ -32,7 +32,7 @@ jobs: if: type = push os: linux arch: arm64 - dist: bionic + dist: noble go: 1.22.x env: - docker @@ -49,21 +49,20 @@ jobs: - stage: build if: type = push os: linux - dist: bionic + dist: noble sudo: required go: 1.22.x env: - azure-linux git: submodules: false # avoid cloning ethereum/tests - addons: - apt: - packages: - - gcc-multilib script: - # Build for the primary platforms that Trusty can manage + # build amd64 - go run build/ci.go install -dlgo - go run build/ci.go archive -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds + + # build 386 + - sudo -E apt-get -yq --no-install-suggests --no-install-recommends install gcc-multilib - go run build/ci.go install -dlgo -arch 386 - go run build/ci.go archive -arch 386 -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds @@ -98,48 +97,34 @@ jobs: # These builders run the tests - stage: build + if: type = push os: linux arch: amd64 - dist: bionic + dist: noble go: 1.22.x script: - - travis_wait 30 go run build/ci.go test $TEST_PACKAGES - - - stage: build - if: type = pull_request - os: linux - arch: arm64 - dist: bionic - go: 1.21.x - script: - - travis_wait 30 go run build/ci.go test $TEST_PACKAGES + - travis_wait 45 go run build/ci.go test $TEST_PACKAGES - stage: build + if: type = push os: linux - dist: bionic + dist: noble go: 1.21.x script: - - travis_wait 30 go run build/ci.go test $TEST_PACKAGES + - travis_wait 45 go run build/ci.go test $TEST_PACKAGES # This builder does the Ubuntu PPA nightly uploads - stage: build if: type = cron || (type = push && tag ~= /^v[0-9]/) os: linux - dist: bionic + dist: noble go: 1.22.x env: - ubuntu-ppa git: submodules: false # avoid cloning ethereum/tests - addons: - apt: - packages: - - devscripts - - debhelper - - dput - - fakeroot - - python-bzrlib - - python-paramiko + before_install: + - sudo -E apt-get -yq --no-install-suggests --no-install-recommends install devscripts debhelper dput fakeroot script: - echo '|1|7SiYPr9xl3uctzovOTj4gMwAC1M=|t6ReES75Bo/PxlOPJ6/GsGbTrM0= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA0aKz5UTUndYgIGG7dQBV+HaeuEZJ2xPHo2DS2iSKvUL4xNMSAY4UguNW+pX56nAQmZKIZZ8MaEvSj6zMEDiq6HFfn5JcTlM80UwlnyKe8B8p7Nk06PPQLrnmQt5fh0HmEcZx+JU9TZsfCHPnX7MNz4ELfZE6cFsclClrKim3BHUIGq//t93DllB+h4O9LHjEUsQ1Sr63irDLSutkLJD6RXchjROXkNirlcNVHH/jwLWR5RcYilNX7S5bIkK8NlWPjsn/8Ua5O7I9/YoE97PpO6i73DTGLh5H9JN/SITwCKBkgSDWUt61uPK3Y11Gty7o2lWsBjhBUm2Y38CBsoGmBw==' >> ~/.ssh/known_hosts - go run build/ci.go debsrc -upload ethereum/ethereum -sftp-user geth-ci -signer "Go Ethereum Linux Builder " @@ -148,7 +133,7 @@ jobs: - stage: build if: type = cron os: linux - dist: bionic + dist: noble go: 1.22.x env: - azure-purge @@ -161,8 +146,9 @@ jobs: - stage: build if: type = cron os: linux - dist: bionic + dist: noble go: 1.22.x + env: + - racetests script: - - travis_wait 30 go run build/ci.go test -race $TEST_PACKAGES - + - travis_wait 60 go run build/ci.go test -race $TEST_PACKAGES diff --git a/Makefile b/Makefile index b5117d936b..4ba7c2eb69 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ # with Go source code. If you know what GOPATH is then you probably # don't need to bother with make. -.PHONY: geth all test truffle-test lint clean devtools help +.PHONY: geth faucet all test truffle-test lint fmt clean devtools help .PHONY: docker GOBIN = ./build/bin @@ -11,7 +11,7 @@ GORUN = go run GIT_COMMIT=$(shell git rev-parse HEAD) GIT_COMMIT_DATE=$(shell git log -n1 --pretty='format:%cd' --date=format:'%Y%m%d') -#? geth: Build geth +#? geth: Build geth. geth: $(GORUN) build/ci.go install ./cmd/geth @echo "Done building." @@ -22,11 +22,11 @@ faucet: $(GORUN) build/ci.go install ./cmd/faucet @echo "Done building faucet" -#? all: Build all packages and executables +#? all: Build all packages and executables. all: $(GORUN) build/ci.go install -#? test: Run the tests +#? test: Run the tests. test: all $(GORUN) build/ci.go test -timeout 1h @@ -40,11 +40,15 @@ truffle-test: docker compose -f ./tests/truffle/docker-compose.yml up --exit-code-from truffle-test truffle-test docker compose -f ./tests/truffle/docker-compose.yml down -#? lint: Run certain pre-selected linters +#? lint: Run certain pre-selected linters. lint: ## Run linters. $(GORUN) build/ci.go lint -#? clean: Clean go cache, built executables, and the auto generated folder +#? fmt: Ensure consistent code formatting. +fmt: + gofmt -s -w $(shell find . -name "*.go") + +#? clean: Clean go cache, built executables, and the auto generated folder. clean: go clean -cache rm -fr build/_workspace/pkg/ $(GOBIN)/* @@ -52,7 +56,7 @@ clean: # The devtools target installs tools required for 'go generate'. # You need to put $GOBIN (or $GOPATH/bin) in your PATH to use 'go generate'. -#? devtools: Install recommended developer tools +#? devtools: Install recommended developer tools. devtools: env GOBIN= go install golang.org/x/tools/cmd/stringer@latest env GOBIN= go install github.com/fjl/gencodec@latest @@ -67,5 +71,9 @@ docker: #? help: Get more info on make commands. help: Makefile - @echo " Choose a command run in go-ethereum:" + @echo '' + @echo 'Usage:' + @echo ' make [target]' + @echo '' + @echo 'Targets:' @sed -n 's/^#?//p' $< | column -t -s ':' | sort | sed -e 's/^/ /' diff --git a/accounts/abi/type.go b/accounts/abi/type.go index 3839826633..d57fa3d4e6 100644 --- a/accounts/abi/type.go +++ b/accounts/abi/type.go @@ -64,6 +64,9 @@ type Type struct { var ( // typeRegex parses the abi sub types typeRegex = regexp.MustCompile("([a-zA-Z]+)(([0-9]+)(x([0-9]+))?)?") + + // sliceSizeRegex grab the slice size + sliceSizeRegex = regexp.MustCompile("[0-9]+") ) // NewType creates a new reflection type of abi type given in t. @@ -91,8 +94,7 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty // grab the last cell and create a type from there sliced := t[i:] // grab the slice size with regexp - re := regexp.MustCompile("[0-9]+") - intz := re.FindAllString(sliced, -1) + intz := sliceSizeRegex.FindAllString(sliced, -1) if len(intz) == 0 { // is a slice diff --git a/accounts/keystore/account_cache_test.go b/accounts/keystore/account_cache_test.go index 19d0e91404..4bf6a88004 100644 --- a/accounts/keystore/account_cache_test.go +++ b/accounts/keystore/account_cache_test.go @@ -325,7 +325,8 @@ func TestUpdatedKeyfileContents(t *testing.T) { t.Parallel() // Create a temporary keystore to test with - dir := filepath.Join(os.TempDir(), fmt.Sprintf("eth-keystore-updatedkeyfilecontents-test-%d-%d", os.Getpid(), rand.Int())) + dir := t.TempDir() + ks := NewKeyStore(dir, LightScryptN, LightScryptP) list := ks.Accounts() @@ -335,9 +336,7 @@ func TestUpdatedKeyfileContents(t *testing.T) { if !waitWatcherStart(ks) { t.Fatal("keystore watcher didn't start in time") } - // Create the directory and copy a key file into it. - os.MkdirAll(dir, 0700) - defer os.RemoveAll(dir) + // Copy a key file into it file := filepath.Join(dir, "aaa") // Place one of our testfiles in there diff --git a/accounts/keystore/keystore.go b/accounts/keystore/keystore.go index 5c978cf0b4..df3dda60b6 100644 --- a/accounts/keystore/keystore.go +++ b/accounts/keystore/keystore.go @@ -312,11 +312,10 @@ func (ks *KeyStore) Unlock(a accounts.Account, passphrase string) error { // Lock removes the private key with the given address from memory. func (ks *KeyStore) Lock(addr common.Address) error { ks.mu.Lock() - if unl, found := ks.unlocked[addr]; found { - ks.mu.Unlock() + unl, found := ks.unlocked[addr] + ks.mu.Unlock() + if found { ks.expire(addr, unl, time.Duration(0)*time.Nanosecond) - } else { - ks.mu.Unlock() } return nil } diff --git a/accounts/scwallet/hub.go b/accounts/scwallet/hub.go index 5f1f369ca2..1b1899dc8e 100644 --- a/accounts/scwallet/hub.go +++ b/accounts/scwallet/hub.go @@ -95,6 +95,7 @@ func (hub *Hub) readPairings() error { } return err } + defer pairingFile.Close() pairingData, err := io.ReadAll(pairingFile) if err != nil { diff --git a/accounts/scwallet/wallet.go b/accounts/scwallet/wallet.go index f0ca9085b6..58cfc88301 100644 --- a/accounts/scwallet/wallet.go +++ b/accounts/scwallet/wallet.go @@ -73,6 +73,14 @@ var ( DerivationSignatureHash = sha256.Sum256(common.Hash{}.Bytes()) ) +var ( + // PinRegexp is the regular expression used to validate PIN codes. + pinRegexp = regexp.MustCompile(`^[0-9]{6,}$`) + + // PukRegexp is the regular expression used to validate PUK codes. + pukRegexp = regexp.MustCompile(`^[0-9]{12,}$`) +) + // List of APDU command-related constants const ( claISO7816 = 0 @@ -380,7 +388,7 @@ func (w *Wallet) Open(passphrase string) error { case passphrase == "": return ErrPINUnblockNeeded case status.PinRetryCount > 0: - if !regexp.MustCompile(`^[0-9]{6,}$`).MatchString(passphrase) { + if !pinRegexp.MatchString(passphrase) { w.log.Error("PIN needs to be at least 6 digits") return ErrPINNeeded } @@ -388,7 +396,7 @@ func (w *Wallet) Open(passphrase string) error { return err } default: - if !regexp.MustCompile(`^[0-9]{12,}$`).MatchString(passphrase) { + if !pukRegexp.MatchString(passphrase) { w.log.Error("PUK needs to be at least 12 digits") return ErrPINUnblockNeeded } diff --git a/accounts/usbwallet/trezor/messages-common.pb.go b/accounts/usbwallet/trezor/messages-common.pb.go index b396c6d8b5..73800802bb 100644 --- a/accounts/usbwallet/trezor/messages-common.pb.go +++ b/accounts/usbwallet/trezor/messages-common.pb.go @@ -1,25 +1,28 @@ +// This file originates from the SatoshiLabs Trezor `common` repository at: +// https://github.com/trezor/trezor-common/blob/master/protob/messages-common.proto +// dated 28.05.2019, commit 893fd219d4a01bcffa0cd9cfa631856371ec5aa9. + // Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.34.2 +// protoc v5.27.1 // source: messages-common.proto package trezor import ( - fmt "fmt" - math "math" - - proto "github.com/golang/protobuf/proto" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" ) -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) type Failure_FailureType int32 @@ -39,37 +42,39 @@ const ( Failure_Failure_FirmwareError Failure_FailureType = 99 ) -var Failure_FailureType_name = map[int32]string{ - 1: "Failure_UnexpectedMessage", - 2: "Failure_ButtonExpected", - 3: "Failure_DataError", - 4: "Failure_ActionCancelled", - 5: "Failure_PinExpected", - 6: "Failure_PinCancelled", - 7: "Failure_PinInvalid", - 8: "Failure_InvalidSignature", - 9: "Failure_ProcessError", - 10: "Failure_NotEnoughFunds", - 11: "Failure_NotInitialized", - 12: "Failure_PinMismatch", - 99: "Failure_FirmwareError", -} - -var Failure_FailureType_value = map[string]int32{ - "Failure_UnexpectedMessage": 1, - "Failure_ButtonExpected": 2, - "Failure_DataError": 3, - "Failure_ActionCancelled": 4, - "Failure_PinExpected": 5, - "Failure_PinCancelled": 6, - "Failure_PinInvalid": 7, - "Failure_InvalidSignature": 8, - "Failure_ProcessError": 9, - "Failure_NotEnoughFunds": 10, - "Failure_NotInitialized": 11, - "Failure_PinMismatch": 12, - "Failure_FirmwareError": 99, -} +// Enum value maps for Failure_FailureType. +var ( + Failure_FailureType_name = map[int32]string{ + 1: "Failure_UnexpectedMessage", + 2: "Failure_ButtonExpected", + 3: "Failure_DataError", + 4: "Failure_ActionCancelled", + 5: "Failure_PinExpected", + 6: "Failure_PinCancelled", + 7: "Failure_PinInvalid", + 8: "Failure_InvalidSignature", + 9: "Failure_ProcessError", + 10: "Failure_NotEnoughFunds", + 11: "Failure_NotInitialized", + 12: "Failure_PinMismatch", + 99: "Failure_FirmwareError", + } + Failure_FailureType_value = map[string]int32{ + "Failure_UnexpectedMessage": 1, + "Failure_ButtonExpected": 2, + "Failure_DataError": 3, + "Failure_ActionCancelled": 4, + "Failure_PinExpected": 5, + "Failure_PinCancelled": 6, + "Failure_PinInvalid": 7, + "Failure_InvalidSignature": 8, + "Failure_ProcessError": 9, + "Failure_NotEnoughFunds": 10, + "Failure_NotInitialized": 11, + "Failure_PinMismatch": 12, + "Failure_FirmwareError": 99, + } +) func (x Failure_FailureType) Enum() *Failure_FailureType { p := new(Failure_FailureType) @@ -78,20 +83,34 @@ func (x Failure_FailureType) Enum() *Failure_FailureType { } func (x Failure_FailureType) String() string { - return proto.EnumName(Failure_FailureType_name, int32(x)) + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Failure_FailureType) Descriptor() protoreflect.EnumDescriptor { + return file_messages_common_proto_enumTypes[0].Descriptor() +} + +func (Failure_FailureType) Type() protoreflect.EnumType { + return &file_messages_common_proto_enumTypes[0] +} + +func (x Failure_FailureType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) } -func (x *Failure_FailureType) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(Failure_FailureType_value, data, "Failure_FailureType") +// Deprecated: Do not use. +func (x *Failure_FailureType) UnmarshalJSON(b []byte) error { + num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b) if err != nil { return err } - *x = Failure_FailureType(value) + *x = Failure_FailureType(num) return nil } +// Deprecated: Use Failure_FailureType.Descriptor instead. func (Failure_FailureType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_aaf30d059fdbc38d, []int{1, 0} + return file_messages_common_proto_rawDescGZIP(), []int{1, 0} } // * @@ -116,41 +135,43 @@ const ( ButtonRequest_ButtonRequest_UnknownDerivationPath ButtonRequest_ButtonRequestType = 15 ) -var ButtonRequest_ButtonRequestType_name = map[int32]string{ - 1: "ButtonRequest_Other", - 2: "ButtonRequest_FeeOverThreshold", - 3: "ButtonRequest_ConfirmOutput", - 4: "ButtonRequest_ResetDevice", - 5: "ButtonRequest_ConfirmWord", - 6: "ButtonRequest_WipeDevice", - 7: "ButtonRequest_ProtectCall", - 8: "ButtonRequest_SignTx", - 9: "ButtonRequest_FirmwareCheck", - 10: "ButtonRequest_Address", - 11: "ButtonRequest_PublicKey", - 12: "ButtonRequest_MnemonicWordCount", - 13: "ButtonRequest_MnemonicInput", - 14: "ButtonRequest_PassphraseType", - 15: "ButtonRequest_UnknownDerivationPath", -} - -var ButtonRequest_ButtonRequestType_value = map[string]int32{ - "ButtonRequest_Other": 1, - "ButtonRequest_FeeOverThreshold": 2, - "ButtonRequest_ConfirmOutput": 3, - "ButtonRequest_ResetDevice": 4, - "ButtonRequest_ConfirmWord": 5, - "ButtonRequest_WipeDevice": 6, - "ButtonRequest_ProtectCall": 7, - "ButtonRequest_SignTx": 8, - "ButtonRequest_FirmwareCheck": 9, - "ButtonRequest_Address": 10, - "ButtonRequest_PublicKey": 11, - "ButtonRequest_MnemonicWordCount": 12, - "ButtonRequest_MnemonicInput": 13, - "ButtonRequest_PassphraseType": 14, - "ButtonRequest_UnknownDerivationPath": 15, -} +// Enum value maps for ButtonRequest_ButtonRequestType. +var ( + ButtonRequest_ButtonRequestType_name = map[int32]string{ + 1: "ButtonRequest_Other", + 2: "ButtonRequest_FeeOverThreshold", + 3: "ButtonRequest_ConfirmOutput", + 4: "ButtonRequest_ResetDevice", + 5: "ButtonRequest_ConfirmWord", + 6: "ButtonRequest_WipeDevice", + 7: "ButtonRequest_ProtectCall", + 8: "ButtonRequest_SignTx", + 9: "ButtonRequest_FirmwareCheck", + 10: "ButtonRequest_Address", + 11: "ButtonRequest_PublicKey", + 12: "ButtonRequest_MnemonicWordCount", + 13: "ButtonRequest_MnemonicInput", + 14: "ButtonRequest_PassphraseType", + 15: "ButtonRequest_UnknownDerivationPath", + } + ButtonRequest_ButtonRequestType_value = map[string]int32{ + "ButtonRequest_Other": 1, + "ButtonRequest_FeeOverThreshold": 2, + "ButtonRequest_ConfirmOutput": 3, + "ButtonRequest_ResetDevice": 4, + "ButtonRequest_ConfirmWord": 5, + "ButtonRequest_WipeDevice": 6, + "ButtonRequest_ProtectCall": 7, + "ButtonRequest_SignTx": 8, + "ButtonRequest_FirmwareCheck": 9, + "ButtonRequest_Address": 10, + "ButtonRequest_PublicKey": 11, + "ButtonRequest_MnemonicWordCount": 12, + "ButtonRequest_MnemonicInput": 13, + "ButtonRequest_PassphraseType": 14, + "ButtonRequest_UnknownDerivationPath": 15, + } +) func (x ButtonRequest_ButtonRequestType) Enum() *ButtonRequest_ButtonRequestType { p := new(ButtonRequest_ButtonRequestType) @@ -159,20 +180,34 @@ func (x ButtonRequest_ButtonRequestType) Enum() *ButtonRequest_ButtonRequestType } func (x ButtonRequest_ButtonRequestType) String() string { - return proto.EnumName(ButtonRequest_ButtonRequestType_name, int32(x)) + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (ButtonRequest_ButtonRequestType) Descriptor() protoreflect.EnumDescriptor { + return file_messages_common_proto_enumTypes[1].Descriptor() +} + +func (ButtonRequest_ButtonRequestType) Type() protoreflect.EnumType { + return &file_messages_common_proto_enumTypes[1] +} + +func (x ButtonRequest_ButtonRequestType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) } -func (x *ButtonRequest_ButtonRequestType) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(ButtonRequest_ButtonRequestType_value, data, "ButtonRequest_ButtonRequestType") +// Deprecated: Do not use. +func (x *ButtonRequest_ButtonRequestType) UnmarshalJSON(b []byte) error { + num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b) if err != nil { return err } - *x = ButtonRequest_ButtonRequestType(value) + *x = ButtonRequest_ButtonRequestType(num) return nil } +// Deprecated: Use ButtonRequest_ButtonRequestType.Descriptor instead. func (ButtonRequest_ButtonRequestType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_aaf30d059fdbc38d, []int{2, 0} + return file_messages_common_proto_rawDescGZIP(), []int{2, 0} } // * @@ -185,17 +220,19 @@ const ( PinMatrixRequest_PinMatrixRequestType_NewSecond PinMatrixRequest_PinMatrixRequestType = 3 ) -var PinMatrixRequest_PinMatrixRequestType_name = map[int32]string{ - 1: "PinMatrixRequestType_Current", - 2: "PinMatrixRequestType_NewFirst", - 3: "PinMatrixRequestType_NewSecond", -} - -var PinMatrixRequest_PinMatrixRequestType_value = map[string]int32{ - "PinMatrixRequestType_Current": 1, - "PinMatrixRequestType_NewFirst": 2, - "PinMatrixRequestType_NewSecond": 3, -} +// Enum value maps for PinMatrixRequest_PinMatrixRequestType. +var ( + PinMatrixRequest_PinMatrixRequestType_name = map[int32]string{ + 1: "PinMatrixRequestType_Current", + 2: "PinMatrixRequestType_NewFirst", + 3: "PinMatrixRequestType_NewSecond", + } + PinMatrixRequest_PinMatrixRequestType_value = map[string]int32{ + "PinMatrixRequestType_Current": 1, + "PinMatrixRequestType_NewFirst": 2, + "PinMatrixRequestType_NewSecond": 3, + } +) func (x PinMatrixRequest_PinMatrixRequestType) Enum() *PinMatrixRequest_PinMatrixRequestType { p := new(PinMatrixRequest_PinMatrixRequestType) @@ -204,60 +241,82 @@ func (x PinMatrixRequest_PinMatrixRequestType) Enum() *PinMatrixRequest_PinMatri } func (x PinMatrixRequest_PinMatrixRequestType) String() string { - return proto.EnumName(PinMatrixRequest_PinMatrixRequestType_name, int32(x)) + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (PinMatrixRequest_PinMatrixRequestType) Descriptor() protoreflect.EnumDescriptor { + return file_messages_common_proto_enumTypes[2].Descriptor() +} + +func (PinMatrixRequest_PinMatrixRequestType) Type() protoreflect.EnumType { + return &file_messages_common_proto_enumTypes[2] } -func (x *PinMatrixRequest_PinMatrixRequestType) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(PinMatrixRequest_PinMatrixRequestType_value, data, "PinMatrixRequest_PinMatrixRequestType") +func (x PinMatrixRequest_PinMatrixRequestType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Do not use. +func (x *PinMatrixRequest_PinMatrixRequestType) UnmarshalJSON(b []byte) error { + num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b) if err != nil { return err } - *x = PinMatrixRequest_PinMatrixRequestType(value) + *x = PinMatrixRequest_PinMatrixRequestType(num) return nil } +// Deprecated: Use PinMatrixRequest_PinMatrixRequestType.Descriptor instead. func (PinMatrixRequest_PinMatrixRequestType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_aaf30d059fdbc38d, []int{4, 0} + return file_messages_common_proto_rawDescGZIP(), []int{4, 0} } // * // Response: Success of the previous request // @end type Success struct { - Message *string `protobuf:"bytes,1,opt,name=message" json:"message,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *Success) Reset() { *m = Success{} } -func (m *Success) String() string { return proto.CompactTextString(m) } -func (*Success) ProtoMessage() {} -func (*Success) Descriptor() ([]byte, []int) { - return fileDescriptor_aaf30d059fdbc38d, []int{0} + Message *string `protobuf:"bytes,1,opt,name=message" json:"message,omitempty"` // human readable description of action or request-specific payload } -func (m *Success) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Success.Unmarshal(m, b) -} -func (m *Success) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Success.Marshal(b, m, deterministic) -} -func (m *Success) XXX_Merge(src proto.Message) { - xxx_messageInfo_Success.Merge(m, src) +func (x *Success) Reset() { + *x = Success{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_common_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *Success) XXX_Size() int { - return xxx_messageInfo_Success.Size(m) + +func (x *Success) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *Success) XXX_DiscardUnknown() { - xxx_messageInfo_Success.DiscardUnknown(m) + +func (*Success) ProtoMessage() {} + +func (x *Success) ProtoReflect() protoreflect.Message { + mi := &file_messages_common_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_Success proto.InternalMessageInfo +// Deprecated: Use Success.ProtoReflect.Descriptor instead. +func (*Success) Descriptor() ([]byte, []int) { + return file_messages_common_proto_rawDescGZIP(), []int{0} +} -func (m *Success) GetMessage() string { - if m != nil && m.Message != nil { - return *m.Message +func (x *Success) GetMessage() string { + if x != nil && x.Message != nil { + return *x.Message } return "" } @@ -266,48 +325,56 @@ func (m *Success) GetMessage() string { // Response: Failure of the previous request // @end type Failure struct { - Code *Failure_FailureType `protobuf:"varint,1,opt,name=code,enum=hw.trezor.messages.common.Failure_FailureType" json:"code,omitempty"` - Message *string `protobuf:"bytes,2,opt,name=message" json:"message,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *Failure) Reset() { *m = Failure{} } -func (m *Failure) String() string { return proto.CompactTextString(m) } -func (*Failure) ProtoMessage() {} -func (*Failure) Descriptor() ([]byte, []int) { - return fileDescriptor_aaf30d059fdbc38d, []int{1} + Code *Failure_FailureType `protobuf:"varint,1,opt,name=code,enum=hw.trezor.messages.common.Failure_FailureType" json:"code,omitempty"` // computer-readable definition of the error state + Message *string `protobuf:"bytes,2,opt,name=message" json:"message,omitempty"` // human-readable message of the error state } -func (m *Failure) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Failure.Unmarshal(m, b) -} -func (m *Failure) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Failure.Marshal(b, m, deterministic) -} -func (m *Failure) XXX_Merge(src proto.Message) { - xxx_messageInfo_Failure.Merge(m, src) +func (x *Failure) Reset() { + *x = Failure{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_common_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *Failure) XXX_Size() int { - return xxx_messageInfo_Failure.Size(m) + +func (x *Failure) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *Failure) XXX_DiscardUnknown() { - xxx_messageInfo_Failure.DiscardUnknown(m) + +func (*Failure) ProtoMessage() {} + +func (x *Failure) ProtoReflect() protoreflect.Message { + mi := &file_messages_common_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_Failure proto.InternalMessageInfo +// Deprecated: Use Failure.ProtoReflect.Descriptor instead. +func (*Failure) Descriptor() ([]byte, []int) { + return file_messages_common_proto_rawDescGZIP(), []int{1} +} -func (m *Failure) GetCode() Failure_FailureType { - if m != nil && m.Code != nil { - return *m.Code +func (x *Failure) GetCode() Failure_FailureType { + if x != nil && x.Code != nil { + return *x.Code } return Failure_Failure_UnexpectedMessage } -func (m *Failure) GetMessage() string { - if m != nil && m.Message != nil { - return *m.Message +func (x *Failure) GetMessage() string { + if x != nil && x.Message != nil { + return *x.Message } return "" } @@ -317,48 +384,56 @@ func (m *Failure) GetMessage() string { // @auxstart // @next ButtonAck type ButtonRequest struct { - Code *ButtonRequest_ButtonRequestType `protobuf:"varint,1,opt,name=code,enum=hw.trezor.messages.common.ButtonRequest_ButtonRequestType" json:"code,omitempty"` - Data *string `protobuf:"bytes,2,opt,name=data" json:"data,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *ButtonRequest) Reset() { *m = ButtonRequest{} } -func (m *ButtonRequest) String() string { return proto.CompactTextString(m) } -func (*ButtonRequest) ProtoMessage() {} -func (*ButtonRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_aaf30d059fdbc38d, []int{2} + Code *ButtonRequest_ButtonRequestType `protobuf:"varint,1,opt,name=code,enum=hw.trezor.messages.common.ButtonRequest_ButtonRequestType" json:"code,omitempty"` + Data *string `protobuf:"bytes,2,opt,name=data" json:"data,omitempty"` } -func (m *ButtonRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ButtonRequest.Unmarshal(m, b) -} -func (m *ButtonRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ButtonRequest.Marshal(b, m, deterministic) -} -func (m *ButtonRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_ButtonRequest.Merge(m, src) +func (x *ButtonRequest) Reset() { + *x = ButtonRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_common_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *ButtonRequest) XXX_Size() int { - return xxx_messageInfo_ButtonRequest.Size(m) + +func (x *ButtonRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *ButtonRequest) XXX_DiscardUnknown() { - xxx_messageInfo_ButtonRequest.DiscardUnknown(m) + +func (*ButtonRequest) ProtoMessage() {} + +func (x *ButtonRequest) ProtoReflect() protoreflect.Message { + mi := &file_messages_common_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_ButtonRequest proto.InternalMessageInfo +// Deprecated: Use ButtonRequest.ProtoReflect.Descriptor instead. +func (*ButtonRequest) Descriptor() ([]byte, []int) { + return file_messages_common_proto_rawDescGZIP(), []int{2} +} -func (m *ButtonRequest) GetCode() ButtonRequest_ButtonRequestType { - if m != nil && m.Code != nil { - return *m.Code +func (x *ButtonRequest) GetCode() ButtonRequest_ButtonRequestType { + if x != nil && x.Code != nil { + return *x.Code } return ButtonRequest_ButtonRequest_Other } -func (m *ButtonRequest) GetData() string { - if m != nil && m.Data != nil { - return *m.Data +func (x *ButtonRequest) GetData() string { + if x != nil && x.Data != nil { + return *x.Data } return "" } @@ -367,75 +442,90 @@ func (m *ButtonRequest) GetData() string { // Request: Computer agrees to wait for HW button press // @auxend type ButtonAck struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields } -func (m *ButtonAck) Reset() { *m = ButtonAck{} } -func (m *ButtonAck) String() string { return proto.CompactTextString(m) } -func (*ButtonAck) ProtoMessage() {} -func (*ButtonAck) Descriptor() ([]byte, []int) { - return fileDescriptor_aaf30d059fdbc38d, []int{3} +func (x *ButtonAck) Reset() { + *x = ButtonAck{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_common_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *ButtonAck) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ButtonAck.Unmarshal(m, b) -} -func (m *ButtonAck) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ButtonAck.Marshal(b, m, deterministic) -} -func (m *ButtonAck) XXX_Merge(src proto.Message) { - xxx_messageInfo_ButtonAck.Merge(m, src) -} -func (m *ButtonAck) XXX_Size() int { - return xxx_messageInfo_ButtonAck.Size(m) +func (x *ButtonAck) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *ButtonAck) XXX_DiscardUnknown() { - xxx_messageInfo_ButtonAck.DiscardUnknown(m) + +func (*ButtonAck) ProtoMessage() {} + +func (x *ButtonAck) ProtoReflect() protoreflect.Message { + mi := &file_messages_common_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_ButtonAck proto.InternalMessageInfo +// Deprecated: Use ButtonAck.ProtoReflect.Descriptor instead. +func (*ButtonAck) Descriptor() ([]byte, []int) { + return file_messages_common_proto_rawDescGZIP(), []int{3} +} // * // Response: Device is asking computer to show PIN matrix and awaits PIN encoded using this matrix scheme // @auxstart // @next PinMatrixAck type PinMatrixRequest struct { - Type *PinMatrixRequest_PinMatrixRequestType `protobuf:"varint,1,opt,name=type,enum=hw.trezor.messages.common.PinMatrixRequest_PinMatrixRequestType" json:"type,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *PinMatrixRequest) Reset() { *m = PinMatrixRequest{} } -func (m *PinMatrixRequest) String() string { return proto.CompactTextString(m) } -func (*PinMatrixRequest) ProtoMessage() {} -func (*PinMatrixRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_aaf30d059fdbc38d, []int{4} + Type *PinMatrixRequest_PinMatrixRequestType `protobuf:"varint,1,opt,name=type,enum=hw.trezor.messages.common.PinMatrixRequest_PinMatrixRequestType" json:"type,omitempty"` } -func (m *PinMatrixRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_PinMatrixRequest.Unmarshal(m, b) -} -func (m *PinMatrixRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_PinMatrixRequest.Marshal(b, m, deterministic) -} -func (m *PinMatrixRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_PinMatrixRequest.Merge(m, src) +func (x *PinMatrixRequest) Reset() { + *x = PinMatrixRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_common_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *PinMatrixRequest) XXX_Size() int { - return xxx_messageInfo_PinMatrixRequest.Size(m) + +func (x *PinMatrixRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *PinMatrixRequest) XXX_DiscardUnknown() { - xxx_messageInfo_PinMatrixRequest.DiscardUnknown(m) + +func (*PinMatrixRequest) ProtoMessage() {} + +func (x *PinMatrixRequest) ProtoReflect() protoreflect.Message { + mi := &file_messages_common_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_PinMatrixRequest proto.InternalMessageInfo +// Deprecated: Use PinMatrixRequest.ProtoReflect.Descriptor instead. +func (*PinMatrixRequest) Descriptor() ([]byte, []int) { + return file_messages_common_proto_rawDescGZIP(), []int{4} +} -func (m *PinMatrixRequest) GetType() PinMatrixRequest_PinMatrixRequestType { - if m != nil && m.Type != nil { - return *m.Type +func (x *PinMatrixRequest) GetType() PinMatrixRequest_PinMatrixRequestType { + if x != nil && x.Type != nil { + return *x.Type } return PinMatrixRequest_PinMatrixRequestType_Current } @@ -444,40 +534,48 @@ func (m *PinMatrixRequest) GetType() PinMatrixRequest_PinMatrixRequestType { // Request: Computer responds with encoded PIN // @auxend type PinMatrixAck struct { - Pin *string `protobuf:"bytes,1,req,name=pin" json:"pin,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *PinMatrixAck) Reset() { *m = PinMatrixAck{} } -func (m *PinMatrixAck) String() string { return proto.CompactTextString(m) } -func (*PinMatrixAck) ProtoMessage() {} -func (*PinMatrixAck) Descriptor() ([]byte, []int) { - return fileDescriptor_aaf30d059fdbc38d, []int{5} + Pin *string `protobuf:"bytes,1,req,name=pin" json:"pin,omitempty"` // matrix encoded PIN entered by user } -func (m *PinMatrixAck) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_PinMatrixAck.Unmarshal(m, b) -} -func (m *PinMatrixAck) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_PinMatrixAck.Marshal(b, m, deterministic) -} -func (m *PinMatrixAck) XXX_Merge(src proto.Message) { - xxx_messageInfo_PinMatrixAck.Merge(m, src) +func (x *PinMatrixAck) Reset() { + *x = PinMatrixAck{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_common_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *PinMatrixAck) XXX_Size() int { - return xxx_messageInfo_PinMatrixAck.Size(m) + +func (x *PinMatrixAck) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *PinMatrixAck) XXX_DiscardUnknown() { - xxx_messageInfo_PinMatrixAck.DiscardUnknown(m) + +func (*PinMatrixAck) ProtoMessage() {} + +func (x *PinMatrixAck) ProtoReflect() protoreflect.Message { + mi := &file_messages_common_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_PinMatrixAck proto.InternalMessageInfo +// Deprecated: Use PinMatrixAck.ProtoReflect.Descriptor instead. +func (*PinMatrixAck) Descriptor() ([]byte, []int) { + return file_messages_common_proto_rawDescGZIP(), []int{5} +} -func (m *PinMatrixAck) GetPin() string { - if m != nil && m.Pin != nil { - return *m.Pin +func (x *PinMatrixAck) GetPin() string { + if x != nil && x.Pin != nil { + return *x.Pin } return "" } @@ -487,40 +585,48 @@ func (m *PinMatrixAck) GetPin() string { // @auxstart // @next PassphraseAck type PassphraseRequest struct { - OnDevice *bool `protobuf:"varint,1,opt,name=on_device,json=onDevice" json:"on_device,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *PassphraseRequest) Reset() { *m = PassphraseRequest{} } -func (m *PassphraseRequest) String() string { return proto.CompactTextString(m) } -func (*PassphraseRequest) ProtoMessage() {} -func (*PassphraseRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_aaf30d059fdbc38d, []int{6} + OnDevice *bool `protobuf:"varint,1,opt,name=on_device,json=onDevice" json:"on_device,omitempty"` // passphrase is being entered on the device } -func (m *PassphraseRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_PassphraseRequest.Unmarshal(m, b) -} -func (m *PassphraseRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_PassphraseRequest.Marshal(b, m, deterministic) -} -func (m *PassphraseRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_PassphraseRequest.Merge(m, src) +func (x *PassphraseRequest) Reset() { + *x = PassphraseRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_common_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *PassphraseRequest) XXX_Size() int { - return xxx_messageInfo_PassphraseRequest.Size(m) + +func (x *PassphraseRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *PassphraseRequest) XXX_DiscardUnknown() { - xxx_messageInfo_PassphraseRequest.DiscardUnknown(m) + +func (*PassphraseRequest) ProtoMessage() {} + +func (x *PassphraseRequest) ProtoReflect() protoreflect.Message { + mi := &file_messages_common_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_PassphraseRequest proto.InternalMessageInfo +// Deprecated: Use PassphraseRequest.ProtoReflect.Descriptor instead. +func (*PassphraseRequest) Descriptor() ([]byte, []int) { + return file_messages_common_proto_rawDescGZIP(), []int{6} +} -func (m *PassphraseRequest) GetOnDevice() bool { - if m != nil && m.OnDevice != nil { - return *m.OnDevice +func (x *PassphraseRequest) GetOnDevice() bool { + if x != nil && x.OnDevice != nil { + return *x.OnDevice } return false } @@ -529,48 +635,56 @@ func (m *PassphraseRequest) GetOnDevice() bool { // Request: Send passphrase back // @next PassphraseStateRequest type PassphraseAck struct { - Passphrase *string `protobuf:"bytes,1,opt,name=passphrase" json:"passphrase,omitempty"` - State []byte `protobuf:"bytes,2,opt,name=state" json:"state,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *PassphraseAck) Reset() { *m = PassphraseAck{} } -func (m *PassphraseAck) String() string { return proto.CompactTextString(m) } -func (*PassphraseAck) ProtoMessage() {} -func (*PassphraseAck) Descriptor() ([]byte, []int) { - return fileDescriptor_aaf30d059fdbc38d, []int{7} + Passphrase *string `protobuf:"bytes,1,opt,name=passphrase" json:"passphrase,omitempty"` + State []byte `protobuf:"bytes,2,opt,name=state" json:"state,omitempty"` // expected device state } -func (m *PassphraseAck) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_PassphraseAck.Unmarshal(m, b) -} -func (m *PassphraseAck) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_PassphraseAck.Marshal(b, m, deterministic) -} -func (m *PassphraseAck) XXX_Merge(src proto.Message) { - xxx_messageInfo_PassphraseAck.Merge(m, src) +func (x *PassphraseAck) Reset() { + *x = PassphraseAck{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_common_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *PassphraseAck) XXX_Size() int { - return xxx_messageInfo_PassphraseAck.Size(m) + +func (x *PassphraseAck) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *PassphraseAck) XXX_DiscardUnknown() { - xxx_messageInfo_PassphraseAck.DiscardUnknown(m) + +func (*PassphraseAck) ProtoMessage() {} + +func (x *PassphraseAck) ProtoReflect() protoreflect.Message { + mi := &file_messages_common_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_PassphraseAck proto.InternalMessageInfo +// Deprecated: Use PassphraseAck.ProtoReflect.Descriptor instead. +func (*PassphraseAck) Descriptor() ([]byte, []int) { + return file_messages_common_proto_rawDescGZIP(), []int{7} +} -func (m *PassphraseAck) GetPassphrase() string { - if m != nil && m.Passphrase != nil { - return *m.Passphrase +func (x *PassphraseAck) GetPassphrase() string { + if x != nil && x.Passphrase != nil { + return *x.Passphrase } return "" } -func (m *PassphraseAck) GetState() []byte { - if m != nil { - return m.State +func (x *PassphraseAck) GetState() []byte { + if x != nil { + return x.State } return nil } @@ -579,40 +693,48 @@ func (m *PassphraseAck) GetState() []byte { // Response: Device awaits passphrase state // @next PassphraseStateAck type PassphraseStateRequest struct { - State []byte `protobuf:"bytes,1,opt,name=state" json:"state,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *PassphraseStateRequest) Reset() { *m = PassphraseStateRequest{} } -func (m *PassphraseStateRequest) String() string { return proto.CompactTextString(m) } -func (*PassphraseStateRequest) ProtoMessage() {} -func (*PassphraseStateRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_aaf30d059fdbc38d, []int{8} + State []byte `protobuf:"bytes,1,opt,name=state" json:"state,omitempty"` // actual device state } -func (m *PassphraseStateRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_PassphraseStateRequest.Unmarshal(m, b) -} -func (m *PassphraseStateRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_PassphraseStateRequest.Marshal(b, m, deterministic) -} -func (m *PassphraseStateRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_PassphraseStateRequest.Merge(m, src) +func (x *PassphraseStateRequest) Reset() { + *x = PassphraseStateRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_common_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *PassphraseStateRequest) XXX_Size() int { - return xxx_messageInfo_PassphraseStateRequest.Size(m) + +func (x *PassphraseStateRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *PassphraseStateRequest) XXX_DiscardUnknown() { - xxx_messageInfo_PassphraseStateRequest.DiscardUnknown(m) + +func (*PassphraseStateRequest) ProtoMessage() {} + +func (x *PassphraseStateRequest) ProtoReflect() protoreflect.Message { + mi := &file_messages_common_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_PassphraseStateRequest proto.InternalMessageInfo +// Deprecated: Use PassphraseStateRequest.ProtoReflect.Descriptor instead. +func (*PassphraseStateRequest) Descriptor() ([]byte, []int) { + return file_messages_common_proto_rawDescGZIP(), []int{8} +} -func (m *PassphraseStateRequest) GetState() []byte { - if m != nil { - return m.State +func (x *PassphraseStateRequest) GetState() []byte { + if x != nil { + return x.State } return nil } @@ -621,191 +743,456 @@ func (m *PassphraseStateRequest) GetState() []byte { // Request: Send passphrase state back // @auxend type PassphraseStateAck struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields } -func (m *PassphraseStateAck) Reset() { *m = PassphraseStateAck{} } -func (m *PassphraseStateAck) String() string { return proto.CompactTextString(m) } -func (*PassphraseStateAck) ProtoMessage() {} -func (*PassphraseStateAck) Descriptor() ([]byte, []int) { - return fileDescriptor_aaf30d059fdbc38d, []int{9} +func (x *PassphraseStateAck) Reset() { + *x = PassphraseStateAck{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_common_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *PassphraseStateAck) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_PassphraseStateAck.Unmarshal(m, b) -} -func (m *PassphraseStateAck) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_PassphraseStateAck.Marshal(b, m, deterministic) -} -func (m *PassphraseStateAck) XXX_Merge(src proto.Message) { - xxx_messageInfo_PassphraseStateAck.Merge(m, src) -} -func (m *PassphraseStateAck) XXX_Size() int { - return xxx_messageInfo_PassphraseStateAck.Size(m) +func (x *PassphraseStateAck) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *PassphraseStateAck) XXX_DiscardUnknown() { - xxx_messageInfo_PassphraseStateAck.DiscardUnknown(m) + +func (*PassphraseStateAck) ProtoMessage() {} + +func (x *PassphraseStateAck) ProtoReflect() protoreflect.Message { + mi := &file_messages_common_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_PassphraseStateAck proto.InternalMessageInfo +// Deprecated: Use PassphraseStateAck.ProtoReflect.Descriptor instead. +func (*PassphraseStateAck) Descriptor() ([]byte, []int) { + return file_messages_common_proto_rawDescGZIP(), []int{9} +} // * // Structure representing BIP32 (hierarchical deterministic) node // Used for imports of private key into the device and exporting public key out of device // @embed type HDNodeType struct { - Depth *uint32 `protobuf:"varint,1,req,name=depth" json:"depth,omitempty"` - Fingerprint *uint32 `protobuf:"varint,2,req,name=fingerprint" json:"fingerprint,omitempty"` - ChildNum *uint32 `protobuf:"varint,3,req,name=child_num,json=childNum" json:"child_num,omitempty"` - ChainCode []byte `protobuf:"bytes,4,req,name=chain_code,json=chainCode" json:"chain_code,omitempty"` - PrivateKey []byte `protobuf:"bytes,5,opt,name=private_key,json=privateKey" json:"private_key,omitempty"` - PublicKey []byte `protobuf:"bytes,6,opt,name=public_key,json=publicKey" json:"public_key,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *HDNodeType) Reset() { *m = HDNodeType{} } -func (m *HDNodeType) String() string { return proto.CompactTextString(m) } -func (*HDNodeType) ProtoMessage() {} -func (*HDNodeType) Descriptor() ([]byte, []int) { - return fileDescriptor_aaf30d059fdbc38d, []int{10} + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Depth *uint32 `protobuf:"varint,1,req,name=depth" json:"depth,omitempty"` + Fingerprint *uint32 `protobuf:"varint,2,req,name=fingerprint" json:"fingerprint,omitempty"` + ChildNum *uint32 `protobuf:"varint,3,req,name=child_num,json=childNum" json:"child_num,omitempty"` + ChainCode []byte `protobuf:"bytes,4,req,name=chain_code,json=chainCode" json:"chain_code,omitempty"` + PrivateKey []byte `protobuf:"bytes,5,opt,name=private_key,json=privateKey" json:"private_key,omitempty"` + PublicKey []byte `protobuf:"bytes,6,opt,name=public_key,json=publicKey" json:"public_key,omitempty"` +} + +func (x *HDNodeType) Reset() { + *x = HDNodeType{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_common_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *HDNodeType) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_HDNodeType.Unmarshal(m, b) -} -func (m *HDNodeType) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_HDNodeType.Marshal(b, m, deterministic) -} -func (m *HDNodeType) XXX_Merge(src proto.Message) { - xxx_messageInfo_HDNodeType.Merge(m, src) -} -func (m *HDNodeType) XXX_Size() int { - return xxx_messageInfo_HDNodeType.Size(m) +func (x *HDNodeType) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *HDNodeType) XXX_DiscardUnknown() { - xxx_messageInfo_HDNodeType.DiscardUnknown(m) + +func (*HDNodeType) ProtoMessage() {} + +func (x *HDNodeType) ProtoReflect() protoreflect.Message { + mi := &file_messages_common_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_HDNodeType proto.InternalMessageInfo +// Deprecated: Use HDNodeType.ProtoReflect.Descriptor instead. +func (*HDNodeType) Descriptor() ([]byte, []int) { + return file_messages_common_proto_rawDescGZIP(), []int{10} +} -func (m *HDNodeType) GetDepth() uint32 { - if m != nil && m.Depth != nil { - return *m.Depth +func (x *HDNodeType) GetDepth() uint32 { + if x != nil && x.Depth != nil { + return *x.Depth } return 0 } -func (m *HDNodeType) GetFingerprint() uint32 { - if m != nil && m.Fingerprint != nil { - return *m.Fingerprint +func (x *HDNodeType) GetFingerprint() uint32 { + if x != nil && x.Fingerprint != nil { + return *x.Fingerprint } return 0 } -func (m *HDNodeType) GetChildNum() uint32 { - if m != nil && m.ChildNum != nil { - return *m.ChildNum +func (x *HDNodeType) GetChildNum() uint32 { + if x != nil && x.ChildNum != nil { + return *x.ChildNum } return 0 } -func (m *HDNodeType) GetChainCode() []byte { - if m != nil { - return m.ChainCode +func (x *HDNodeType) GetChainCode() []byte { + if x != nil { + return x.ChainCode } return nil } -func (m *HDNodeType) GetPrivateKey() []byte { - if m != nil { - return m.PrivateKey +func (x *HDNodeType) GetPrivateKey() []byte { + if x != nil { + return x.PrivateKey } return nil } -func (m *HDNodeType) GetPublicKey() []byte { - if m != nil { - return m.PublicKey +func (x *HDNodeType) GetPublicKey() []byte { + if x != nil { + return x.PublicKey } return nil } -func init() { - proto.RegisterEnum("hw.trezor.messages.common.Failure_FailureType", Failure_FailureType_name, Failure_FailureType_value) - proto.RegisterEnum("hw.trezor.messages.common.ButtonRequest_ButtonRequestType", ButtonRequest_ButtonRequestType_name, ButtonRequest_ButtonRequestType_value) - proto.RegisterEnum("hw.trezor.messages.common.PinMatrixRequest_PinMatrixRequestType", PinMatrixRequest_PinMatrixRequestType_name, PinMatrixRequest_PinMatrixRequestType_value) - proto.RegisterType((*Success)(nil), "hw.trezor.messages.common.Success") - proto.RegisterType((*Failure)(nil), "hw.trezor.messages.common.Failure") - proto.RegisterType((*ButtonRequest)(nil), "hw.trezor.messages.common.ButtonRequest") - proto.RegisterType((*ButtonAck)(nil), "hw.trezor.messages.common.ButtonAck") - proto.RegisterType((*PinMatrixRequest)(nil), "hw.trezor.messages.common.PinMatrixRequest") - proto.RegisterType((*PinMatrixAck)(nil), "hw.trezor.messages.common.PinMatrixAck") - proto.RegisterType((*PassphraseRequest)(nil), "hw.trezor.messages.common.PassphraseRequest") - proto.RegisterType((*PassphraseAck)(nil), "hw.trezor.messages.common.PassphraseAck") - proto.RegisterType((*PassphraseStateRequest)(nil), "hw.trezor.messages.common.PassphraseStateRequest") - proto.RegisterType((*PassphraseStateAck)(nil), "hw.trezor.messages.common.PassphraseStateAck") - proto.RegisterType((*HDNodeType)(nil), "hw.trezor.messages.common.HDNodeType") -} - -func init() { proto.RegisterFile("messages-common.proto", fileDescriptor_aaf30d059fdbc38d) } - -var fileDescriptor_aaf30d059fdbc38d = []byte{ - // 846 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x54, 0xcd, 0x52, 0x23, 0x37, - 0x10, 0x2e, 0xff, 0x80, 0xed, 0xb6, 0xd9, 0x08, 0xc5, 0x80, 0x09, 0xb0, 0x38, 0xc3, 0x21, 0x5c, - 0xe2, 0x4a, 0xe5, 0x98, 0x53, 0x58, 0x83, 0x2b, 0xd4, 0x16, 0x86, 0x1a, 0xd8, 0xda, 0xa3, 0x4b, - 0xd1, 0xf4, 0x32, 0x2a, 0xcf, 0x48, 0x13, 0x8d, 0x06, 0xf0, 0x5e, 0xf2, 0x6a, 0x79, 0x89, 0xbc, - 0x42, 0xaa, 0x52, 0xb9, 0xe4, 0x11, 0xb6, 0x34, 0x3f, 0x78, 0xc6, 0x66, 0x39, 0xcd, 0xe8, 0xfb, - 0xbe, 0xee, 0x96, 0xba, 0x3f, 0x09, 0x76, 0x42, 0x8c, 0x63, 0x76, 0x8f, 0xf1, 0x8f, 0x5c, 0x85, - 0xa1, 0x92, 0xa3, 0x48, 0x2b, 0xa3, 0xe8, 0xbe, 0xff, 0x38, 0x32, 0x1a, 0x3f, 0x2b, 0x3d, 0x2a, - 0x04, 0xa3, 0x4c, 0xe0, 0x9c, 0x40, 0xeb, 0x36, 0xe1, 0x1c, 0xe3, 0x98, 0x0e, 0xa0, 0x95, 0xb3, - 0x83, 0xda, 0xb0, 0x76, 0xda, 0x71, 0x8b, 0xa5, 0xf3, 0x77, 0x03, 0x5a, 0x13, 0x26, 0x82, 0x44, - 0x23, 0x7d, 0x07, 0x4d, 0xae, 0xbc, 0x4c, 0xf2, 0xe6, 0xe7, 0xd1, 0xe8, 0xab, 0xa9, 0x47, 0x79, - 0x44, 0xf1, 0xbd, 0x5b, 0x44, 0xe8, 0xa6, 0xb1, 0xe5, 0x4a, 0xf5, 0x6a, 0xa5, 0xff, 0xea, 0xd0, - 0x2d, 0xe9, 0xe9, 0x11, 0xec, 0xe7, 0xcb, 0xd9, 0x07, 0x89, 0x4f, 0x11, 0x72, 0x83, 0xde, 0x55, - 0x26, 0x26, 0x35, 0xfa, 0x1d, 0xec, 0x16, 0xf4, 0xbb, 0xc4, 0x18, 0x25, 0x2f, 0x72, 0x09, 0xa9, - 0xd3, 0x1d, 0xd8, 0x2e, 0xb8, 0x73, 0x66, 0xd8, 0x85, 0xd6, 0x4a, 0x93, 0x06, 0x3d, 0x80, 0xbd, - 0x02, 0x3e, 0xe3, 0x46, 0x28, 0x39, 0x66, 0x92, 0x63, 0x10, 0xa0, 0x47, 0x9a, 0x74, 0x0f, 0xbe, - 0x2d, 0xc8, 0x1b, 0xb1, 0x4c, 0xb6, 0x41, 0x07, 0xd0, 0x2f, 0x11, 0xcb, 0x90, 0x4d, 0xba, 0x0b, - 0xb4, 0xc4, 0x5c, 0xca, 0x07, 0x16, 0x08, 0x8f, 0xb4, 0xe8, 0x21, 0x0c, 0x0a, 0x3c, 0x07, 0x6f, - 0xc5, 0xbd, 0x64, 0x26, 0xd1, 0x48, 0xda, 0x95, 0x7c, 0x5a, 0xd9, 0xf6, 0x67, 0xfb, 0xeb, 0x94, - 0x8f, 0x34, 0x55, 0xe6, 0x42, 0xaa, 0xe4, 0xde, 0x9f, 0x24, 0xd2, 0x8b, 0x09, 0xac, 0x70, 0x97, - 0x52, 0x18, 0xc1, 0x02, 0xf1, 0x19, 0x3d, 0xd2, 0x5d, 0xd9, 0xfa, 0x95, 0x88, 0x43, 0x66, 0xb8, - 0x4f, 0x7a, 0x74, 0x1f, 0x76, 0x0a, 0x62, 0x22, 0x74, 0xf8, 0xc8, 0x34, 0x66, 0xb5, 0xb8, 0xf3, - 0x4f, 0x13, 0xb6, 0xb2, 0xbe, 0xb9, 0xf8, 0x47, 0x82, 0xb1, 0xa1, 0xd3, 0xca, 0x74, 0x7f, 0x79, - 0x65, 0xba, 0x95, 0xb8, 0xea, 0xaa, 0x34, 0x69, 0x0a, 0x4d, 0x8f, 0x19, 0x96, 0x8f, 0x39, 0xfd, - 0x77, 0xfe, 0x6f, 0xc0, 0xf6, 0x9a, 0xde, 0xee, 0xbf, 0x02, 0xce, 0xae, 0x8d, 0x8f, 0x9a, 0xd4, - 0xa8, 0x03, 0x6f, 0xab, 0xc4, 0x04, 0xf1, 0xfa, 0x01, 0xf5, 0x9d, 0xaf, 0x31, 0xf6, 0x55, 0x60, - 0x67, 0x7d, 0x0c, 0x07, 0x55, 0xcd, 0x58, 0xc9, 0x4f, 0x42, 0x87, 0xd7, 0x89, 0x89, 0x12, 0x43, - 0x1a, 0xd6, 0x47, 0x55, 0x81, 0x8b, 0x31, 0x9a, 0x73, 0x7c, 0x10, 0x1c, 0x49, 0x73, 0x9d, 0xce, - 0xe3, 0x3f, 0x2a, 0x6d, 0xa7, 0x7f, 0x08, 0x83, 0x2a, 0xfd, 0x51, 0x44, 0x98, 0x07, 0x6f, 0xae, - 0x07, 0xdf, 0x68, 0x65, 0x90, 0x9b, 0x31, 0x0b, 0x02, 0xd2, 0xb2, 0xa3, 0xae, 0xd2, 0xd6, 0x07, - 0x77, 0x4f, 0xa4, 0xbd, 0xbe, 0xeb, 0x62, 0x3e, 0x63, 0x1f, 0xf9, 0x9c, 0x74, 0xec, 0xe8, 0xaa, - 0x82, 0x33, 0xcf, 0xd3, 0x18, 0x5b, 0x2b, 0x1c, 0xc0, 0xde, 0x4a, 0xd1, 0xe4, 0xf7, 0x40, 0xf0, - 0xf7, 0xb8, 0x20, 0x5d, 0x7a, 0x02, 0xc7, 0x55, 0xf2, 0x4a, 0x62, 0xa8, 0xa4, 0xe0, 0xf6, 0x3c, - 0x63, 0x95, 0x48, 0x43, 0x7a, 0xeb, 0xd5, 0x0b, 0xd1, 0xa5, 0xb4, 0x3d, 0xdb, 0xa2, 0x43, 0x38, - 0x5c, 0x29, 0xc1, 0xe2, 0x38, 0xf2, 0x35, 0x8b, 0xd3, 0xbb, 0x49, 0xde, 0xd0, 0x1f, 0xe0, 0xa4, - 0xaa, 0xf8, 0x20, 0xe7, 0x52, 0x3d, 0xca, 0x73, 0xd4, 0xe2, 0x81, 0xd9, 0xcb, 0x75, 0xc3, 0x8c, - 0x4f, 0xbe, 0x71, 0xba, 0xd0, 0xc9, 0x84, 0x67, 0x7c, 0xee, 0xfc, 0x5b, 0x03, 0x62, 0x2d, 0xca, - 0x8c, 0x16, 0x4f, 0x85, 0xf1, 0xee, 0xa0, 0x69, 0x16, 0x51, 0x61, 0xbc, 0x5f, 0x5f, 0x31, 0xde, - 0x6a, 0xe8, 0x1a, 0x90, 0xd9, 0xcf, 0x66, 0x73, 0xfe, 0x84, 0xfe, 0x4b, 0xac, 0x3d, 0xda, 0x4b, - 0xf8, 0x6c, 0x9c, 0x68, 0x8d, 0xd2, 0x90, 0x1a, 0xfd, 0x1e, 0x8e, 0x5e, 0x54, 0x4c, 0xf1, 0x71, - 0x22, 0x74, 0x6c, 0x48, 0xdd, 0x1a, 0xf3, 0x6b, 0x92, 0x5b, 0xe4, 0x4a, 0x7a, 0xa4, 0xe1, 0x0c, - 0xa1, 0xf7, 0xac, 0x39, 0xe3, 0x73, 0x4a, 0xa0, 0x11, 0x09, 0x39, 0xa8, 0x0d, 0xeb, 0xa7, 0x1d, - 0xd7, 0xfe, 0x3a, 0x3f, 0xc1, 0xf6, 0xb2, 0xaf, 0x45, 0x37, 0x0e, 0xa0, 0xa3, 0xe4, 0xcc, 0x4b, - 0x1d, 0x96, 0xb6, 0xa4, 0xed, 0xb6, 0x95, 0xcc, 0x1c, 0xe7, 0x5c, 0xc0, 0xd6, 0x32, 0xc2, 0x26, - 0x7d, 0x0b, 0x10, 0x3d, 0x03, 0xf9, 0xdb, 0x5d, 0x42, 0x68, 0x1f, 0x36, 0x62, 0xc3, 0x4c, 0xf6, - 0xd8, 0xf6, 0xdc, 0x6c, 0xe1, 0x8c, 0x60, 0x77, 0x99, 0xe6, 0xd6, 0x42, 0x45, 0xf5, 0x67, 0x7d, - 0xad, 0xac, 0xef, 0x03, 0x5d, 0xd1, 0xdb, 0x61, 0xfe, 0x55, 0x03, 0xf8, 0xed, 0x7c, 0xaa, 0xbc, - 0xec, 0xbd, 0xee, 0xc3, 0x86, 0x87, 0x91, 0xf1, 0xd3, 0x13, 0x6e, 0xb9, 0xd9, 0x82, 0x0e, 0xa1, - 0xfb, 0x49, 0xc8, 0x7b, 0xd4, 0x91, 0x16, 0xd2, 0x0c, 0xea, 0x29, 0x57, 0x86, 0xec, 0x81, 0xb9, - 0x2f, 0x02, 0x6f, 0x26, 0x93, 0x70, 0xd0, 0x48, 0xf9, 0x76, 0x0a, 0x4c, 0x93, 0x90, 0x1e, 0x01, - 0x70, 0x9f, 0x09, 0x39, 0x4b, 0x9f, 0xa6, 0xe6, 0xb0, 0x7e, 0xda, 0x73, 0x3b, 0x29, 0x32, 0xb6, - 0x6f, 0xcc, 0x31, 0x74, 0xa3, 0xd4, 0x6f, 0x38, 0x9b, 0xe3, 0x62, 0xb0, 0x91, 0x6e, 0x1a, 0x72, - 0xe8, 0x3d, 0x2e, 0x6c, 0x7c, 0x94, 0xde, 0x8e, 0x94, 0xdf, 0x4c, 0xf9, 0x4e, 0x54, 0xdc, 0x97, - 0x2f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xb2, 0x7d, 0x20, 0xa6, 0x35, 0x07, 0x00, 0x00, +var File_messages_common_proto protoreflect.FileDescriptor + +var file_messages_common_proto_rawDesc = []byte{ + 0x0a, 0x15, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2d, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, + 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x19, 0x68, 0x77, 0x2e, 0x74, 0x72, 0x65, 0x7a, + 0x6f, 0x72, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, + 0x6f, 0x6e, 0x22, 0x23, 0x0a, 0x07, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x18, 0x0a, + 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0xd5, 0x03, 0x0a, 0x07, 0x46, 0x61, 0x69, 0x6c, + 0x75, 0x72, 0x65, 0x12, 0x42, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x2e, 0x2e, 0x68, 0x77, 0x2e, 0x74, 0x72, 0x65, 0x7a, 0x6f, 0x72, 0x2e, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x46, 0x61, + 0x69, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x54, 0x79, 0x70, + 0x65, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x22, 0xeb, 0x02, 0x0a, 0x0b, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x54, 0x79, 0x70, + 0x65, 0x12, 0x1d, 0x0a, 0x19, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x55, 0x6e, 0x65, + 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x10, 0x01, + 0x12, 0x1a, 0x0a, 0x16, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x42, 0x75, 0x74, 0x74, + 0x6f, 0x6e, 0x45, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x10, 0x02, 0x12, 0x15, 0x0a, 0x11, + 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x44, 0x61, 0x74, 0x61, 0x45, 0x72, 0x72, 0x6f, + 0x72, 0x10, 0x03, 0x12, 0x1b, 0x0a, 0x17, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x41, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x6c, 0x65, 0x64, 0x10, 0x04, + 0x12, 0x17, 0x0a, 0x13, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x50, 0x69, 0x6e, 0x45, + 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x10, 0x05, 0x12, 0x18, 0x0a, 0x14, 0x46, 0x61, 0x69, + 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x50, 0x69, 0x6e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x6c, 0x65, + 0x64, 0x10, 0x06, 0x12, 0x16, 0x0a, 0x12, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x50, + 0x69, 0x6e, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x10, 0x07, 0x12, 0x1c, 0x0a, 0x18, 0x46, + 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x53, 0x69, + 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x10, 0x08, 0x12, 0x18, 0x0a, 0x14, 0x46, 0x61, 0x69, + 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x45, 0x72, 0x72, 0x6f, + 0x72, 0x10, 0x09, 0x12, 0x1a, 0x0a, 0x16, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x4e, + 0x6f, 0x74, 0x45, 0x6e, 0x6f, 0x75, 0x67, 0x68, 0x46, 0x75, 0x6e, 0x64, 0x73, 0x10, 0x0a, 0x12, + 0x1a, 0x0a, 0x16, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x4e, 0x6f, 0x74, 0x49, 0x6e, + 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x10, 0x0b, 0x12, 0x17, 0x0a, 0x13, 0x46, + 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x50, 0x69, 0x6e, 0x4d, 0x69, 0x73, 0x6d, 0x61, 0x74, + 0x63, 0x68, 0x10, 0x0c, 0x12, 0x19, 0x0a, 0x15, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, + 0x46, 0x69, 0x72, 0x6d, 0x77, 0x61, 0x72, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x10, 0x63, 0x22, + 0xe6, 0x04, 0x0a, 0x0d, 0x42, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x4e, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x3a, 0x2e, 0x68, 0x77, 0x2e, 0x74, 0x72, 0x65, 0x7a, 0x6f, 0x72, 0x2e, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x42, 0x75, 0x74, 0x74, + 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x42, 0x75, 0x74, 0x74, 0x6f, 0x6e, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x63, 0x6f, 0x64, + 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0xf0, 0x03, 0x0a, 0x11, 0x42, 0x75, 0x74, 0x74, 0x6f, 0x6e, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x17, 0x0a, 0x13, 0x42, + 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x4f, 0x74, 0x68, + 0x65, 0x72, 0x10, 0x01, 0x12, 0x22, 0x0a, 0x1e, 0x42, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x46, 0x65, 0x65, 0x4f, 0x76, 0x65, 0x72, 0x54, 0x68, 0x72, + 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x10, 0x02, 0x12, 0x1f, 0x0a, 0x1b, 0x42, 0x75, 0x74, 0x74, + 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, + 0x6d, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x10, 0x03, 0x12, 0x1d, 0x0a, 0x19, 0x42, 0x75, 0x74, + 0x74, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x52, 0x65, 0x73, 0x65, 0x74, + 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x10, 0x04, 0x12, 0x1d, 0x0a, 0x19, 0x42, 0x75, 0x74, 0x74, + 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, + 0x6d, 0x57, 0x6f, 0x72, 0x64, 0x10, 0x05, 0x12, 0x1c, 0x0a, 0x18, 0x42, 0x75, 0x74, 0x74, 0x6f, + 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x57, 0x69, 0x70, 0x65, 0x44, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x10, 0x06, 0x12, 0x1d, 0x0a, 0x19, 0x42, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x50, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x43, 0x61, + 0x6c, 0x6c, 0x10, 0x07, 0x12, 0x18, 0x0a, 0x14, 0x42, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x53, 0x69, 0x67, 0x6e, 0x54, 0x78, 0x10, 0x08, 0x12, 0x1f, + 0x0a, 0x1b, 0x42, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, + 0x46, 0x69, 0x72, 0x6d, 0x77, 0x61, 0x72, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x10, 0x09, 0x12, + 0x19, 0x0a, 0x15, 0x42, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x5f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x10, 0x0a, 0x12, 0x1b, 0x0a, 0x17, 0x42, 0x75, + 0x74, 0x74, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x50, 0x75, 0x62, 0x6c, + 0x69, 0x63, 0x4b, 0x65, 0x79, 0x10, 0x0b, 0x12, 0x23, 0x0a, 0x1f, 0x42, 0x75, 0x74, 0x74, 0x6f, + 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x4d, 0x6e, 0x65, 0x6d, 0x6f, 0x6e, 0x69, + 0x63, 0x57, 0x6f, 0x72, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x10, 0x0c, 0x12, 0x1f, 0x0a, 0x1b, + 0x42, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x4d, 0x6e, + 0x65, 0x6d, 0x6f, 0x6e, 0x69, 0x63, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x10, 0x0d, 0x12, 0x20, 0x0a, + 0x1c, 0x42, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x50, + 0x61, 0x73, 0x73, 0x70, 0x68, 0x72, 0x61, 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, 0x10, 0x0e, 0x12, + 0x27, 0x0a, 0x23, 0x42, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x5f, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x44, 0x65, 0x72, 0x69, 0x76, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x50, 0x61, 0x74, 0x68, 0x10, 0x0f, 0x22, 0x0b, 0x0a, 0x09, 0x42, 0x75, 0x74, 0x74, + 0x6f, 0x6e, 0x41, 0x63, 0x6b, 0x22, 0xe9, 0x01, 0x0a, 0x10, 0x50, 0x69, 0x6e, 0x4d, 0x61, 0x74, + 0x72, 0x69, 0x78, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x54, 0x0a, 0x04, 0x74, 0x79, + 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x40, 0x2e, 0x68, 0x77, 0x2e, 0x74, 0x72, + 0x65, 0x7a, 0x6f, 0x72, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x63, 0x6f, + 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x50, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, + 0x22, 0x7f, 0x0a, 0x14, 0x50, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x20, 0x0a, 0x1c, 0x50, 0x69, 0x6e, 0x4d, + 0x61, 0x74, 0x72, 0x69, 0x78, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, + 0x5f, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x10, 0x01, 0x12, 0x21, 0x0a, 0x1d, 0x50, 0x69, + 0x6e, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, + 0x70, 0x65, 0x5f, 0x4e, 0x65, 0x77, 0x46, 0x69, 0x72, 0x73, 0x74, 0x10, 0x02, 0x12, 0x22, 0x0a, + 0x1e, 0x50, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x4e, 0x65, 0x77, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x10, + 0x03, 0x22, 0x20, 0x0a, 0x0c, 0x50, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x41, 0x63, + 0x6b, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x02, 0x28, 0x09, 0x52, 0x03, + 0x70, 0x69, 0x6e, 0x22, 0x30, 0x0a, 0x11, 0x50, 0x61, 0x73, 0x73, 0x70, 0x68, 0x72, 0x61, 0x73, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x6f, 0x6e, 0x5f, 0x64, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6f, 0x6e, 0x44, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x22, 0x45, 0x0a, 0x0d, 0x50, 0x61, 0x73, 0x73, 0x70, 0x68, 0x72, + 0x61, 0x73, 0x65, 0x41, 0x63, 0x6b, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x61, 0x73, 0x73, 0x70, 0x68, + 0x72, 0x61, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x61, 0x73, 0x73, + 0x70, 0x68, 0x72, 0x61, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x2e, 0x0a, 0x16, + 0x50, 0x61, 0x73, 0x73, 0x70, 0x68, 0x72, 0x61, 0x73, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x14, 0x0a, 0x12, + 0x50, 0x61, 0x73, 0x73, 0x70, 0x68, 0x72, 0x61, 0x73, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x41, + 0x63, 0x6b, 0x22, 0xc0, 0x01, 0x0a, 0x0a, 0x48, 0x44, 0x4e, 0x6f, 0x64, 0x65, 0x54, 0x79, 0x70, + 0x65, 0x12, 0x14, 0x0a, 0x05, 0x64, 0x65, 0x70, 0x74, 0x68, 0x18, 0x01, 0x20, 0x02, 0x28, 0x0d, + 0x52, 0x05, 0x64, 0x65, 0x70, 0x74, 0x68, 0x12, 0x20, 0x0a, 0x0b, 0x66, 0x69, 0x6e, 0x67, 0x65, + 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x02, 0x28, 0x0d, 0x52, 0x0b, 0x66, 0x69, + 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x68, 0x69, + 0x6c, 0x64, 0x5f, 0x6e, 0x75, 0x6d, 0x18, 0x03, 0x20, 0x02, 0x28, 0x0d, 0x52, 0x08, 0x63, 0x68, + 0x69, 0x6c, 0x64, 0x4e, 0x75, 0x6d, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, + 0x63, 0x6f, 0x64, 0x65, 0x18, 0x04, 0x20, 0x02, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, + 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x70, 0x72, 0x69, 0x76, + 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, + 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, + 0x69, 0x63, 0x4b, 0x65, 0x79, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2f, 0x67, 0x6f, 0x2d, + 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x73, 0x2f, 0x75, 0x73, 0x62, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2f, 0x74, 0x72, 0x65, 0x7a, + 0x6f, 0x72, +} + +var ( + file_messages_common_proto_rawDescOnce sync.Once + file_messages_common_proto_rawDescData = file_messages_common_proto_rawDesc +) + +func file_messages_common_proto_rawDescGZIP() []byte { + file_messages_common_proto_rawDescOnce.Do(func() { + file_messages_common_proto_rawDescData = protoimpl.X.CompressGZIP(file_messages_common_proto_rawDescData) + }) + return file_messages_common_proto_rawDescData +} + +var file_messages_common_proto_enumTypes = make([]protoimpl.EnumInfo, 3) +var file_messages_common_proto_msgTypes = make([]protoimpl.MessageInfo, 11) +var file_messages_common_proto_goTypes = []any{ + (Failure_FailureType)(0), // 0: hw.trezor.messages.common.Failure.FailureType + (ButtonRequest_ButtonRequestType)(0), // 1: hw.trezor.messages.common.ButtonRequest.ButtonRequestType + (PinMatrixRequest_PinMatrixRequestType)(0), // 2: hw.trezor.messages.common.PinMatrixRequest.PinMatrixRequestType + (*Success)(nil), // 3: hw.trezor.messages.common.Success + (*Failure)(nil), // 4: hw.trezor.messages.common.Failure + (*ButtonRequest)(nil), // 5: hw.trezor.messages.common.ButtonRequest + (*ButtonAck)(nil), // 6: hw.trezor.messages.common.ButtonAck + (*PinMatrixRequest)(nil), // 7: hw.trezor.messages.common.PinMatrixRequest + (*PinMatrixAck)(nil), // 8: hw.trezor.messages.common.PinMatrixAck + (*PassphraseRequest)(nil), // 9: hw.trezor.messages.common.PassphraseRequest + (*PassphraseAck)(nil), // 10: hw.trezor.messages.common.PassphraseAck + (*PassphraseStateRequest)(nil), // 11: hw.trezor.messages.common.PassphraseStateRequest + (*PassphraseStateAck)(nil), // 12: hw.trezor.messages.common.PassphraseStateAck + (*HDNodeType)(nil), // 13: hw.trezor.messages.common.HDNodeType +} +var file_messages_common_proto_depIdxs = []int32{ + 0, // 0: hw.trezor.messages.common.Failure.code:type_name -> hw.trezor.messages.common.Failure.FailureType + 1, // 1: hw.trezor.messages.common.ButtonRequest.code:type_name -> hw.trezor.messages.common.ButtonRequest.ButtonRequestType + 2, // 2: hw.trezor.messages.common.PinMatrixRequest.type:type_name -> hw.trezor.messages.common.PinMatrixRequest.PinMatrixRequestType + 3, // [3:3] is the sub-list for method output_type + 3, // [3:3] is the sub-list for method input_type + 3, // [3:3] is the sub-list for extension type_name + 3, // [3:3] is the sub-list for extension extendee + 0, // [0:3] is the sub-list for field type_name +} + +func init() { file_messages_common_proto_init() } +func file_messages_common_proto_init() { + if File_messages_common_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_messages_common_proto_msgTypes[0].Exporter = func(v any, i int) any { + switch v := v.(*Success); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_common_proto_msgTypes[1].Exporter = func(v any, i int) any { + switch v := v.(*Failure); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_common_proto_msgTypes[2].Exporter = func(v any, i int) any { + switch v := v.(*ButtonRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_common_proto_msgTypes[3].Exporter = func(v any, i int) any { + switch v := v.(*ButtonAck); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_common_proto_msgTypes[4].Exporter = func(v any, i int) any { + switch v := v.(*PinMatrixRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_common_proto_msgTypes[5].Exporter = func(v any, i int) any { + switch v := v.(*PinMatrixAck); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_common_proto_msgTypes[6].Exporter = func(v any, i int) any { + switch v := v.(*PassphraseRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_common_proto_msgTypes[7].Exporter = func(v any, i int) any { + switch v := v.(*PassphraseAck); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_common_proto_msgTypes[8].Exporter = func(v any, i int) any { + switch v := v.(*PassphraseStateRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_common_proto_msgTypes[9].Exporter = func(v any, i int) any { + switch v := v.(*PassphraseStateAck); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_common_proto_msgTypes[10].Exporter = func(v any, i int) any { + switch v := v.(*HDNodeType); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_messages_common_proto_rawDesc, + NumEnums: 3, + NumMessages: 11, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_messages_common_proto_goTypes, + DependencyIndexes: file_messages_common_proto_depIdxs, + EnumInfos: file_messages_common_proto_enumTypes, + MessageInfos: file_messages_common_proto_msgTypes, + }.Build() + File_messages_common_proto = out.File + file_messages_common_proto_rawDesc = nil + file_messages_common_proto_goTypes = nil + file_messages_common_proto_depIdxs = nil } diff --git a/accounts/usbwallet/trezor/messages-common.proto b/accounts/usbwallet/trezor/messages-common.proto index 75a983b0a3..1f524e25d7 100644 --- a/accounts/usbwallet/trezor/messages-common.proto +++ b/accounts/usbwallet/trezor/messages-common.proto @@ -5,6 +5,8 @@ syntax = "proto2"; package hw.trezor.messages.common; +option go_package = "github.com/ethereum/go-ethereum/accounts/usbwallet/trezor"; + /** * Response: Success of the previous request * @end diff --git a/accounts/usbwallet/trezor/messages-ethereum.pb.go b/accounts/usbwallet/trezor/messages-ethereum.pb.go index 230a48279d..a92123efcd 100644 --- a/accounts/usbwallet/trezor/messages-ethereum.pb.go +++ b/accounts/usbwallet/trezor/messages-ethereum.pb.go @@ -1,25 +1,28 @@ +// This file originates from the SatoshiLabs Trezor `common` repository at: +// https://github.com/trezor/trezor-common/blob/master/protob/messages-ethereum.proto +// dated 28.05.2019, commit 893fd219d4a01bcffa0cd9cfa631856371ec5aa9. + // Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.34.2 +// protoc v5.27.1 // source: messages-ethereum.proto package trezor import ( - fmt "fmt" - math "math" - - proto "github.com/golang/protobuf/proto" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" ) -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) // * // Request: Ask device for public key corresponding to address_n path @@ -27,48 +30,56 @@ const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package // @next EthereumPublicKey // @next Failure type EthereumGetPublicKey struct { - AddressN []uint32 `protobuf:"varint,1,rep,name=address_n,json=addressN" json:"address_n,omitempty"` - ShowDisplay *bool `protobuf:"varint,2,opt,name=show_display,json=showDisplay" json:"show_display,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *EthereumGetPublicKey) Reset() { *m = EthereumGetPublicKey{} } -func (m *EthereumGetPublicKey) String() string { return proto.CompactTextString(m) } -func (*EthereumGetPublicKey) ProtoMessage() {} -func (*EthereumGetPublicKey) Descriptor() ([]byte, []int) { - return fileDescriptor_cb33f46ba915f15c, []int{0} + AddressN []uint32 `protobuf:"varint,1,rep,name=address_n,json=addressN" json:"address_n,omitempty"` // BIP-32 path to derive the key from master node + ShowDisplay *bool `protobuf:"varint,2,opt,name=show_display,json=showDisplay" json:"show_display,omitempty"` // optionally show on display before sending the result } -func (m *EthereumGetPublicKey) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_EthereumGetPublicKey.Unmarshal(m, b) -} -func (m *EthereumGetPublicKey) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_EthereumGetPublicKey.Marshal(b, m, deterministic) -} -func (m *EthereumGetPublicKey) XXX_Merge(src proto.Message) { - xxx_messageInfo_EthereumGetPublicKey.Merge(m, src) +func (x *EthereumGetPublicKey) Reset() { + *x = EthereumGetPublicKey{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_ethereum_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *EthereumGetPublicKey) XXX_Size() int { - return xxx_messageInfo_EthereumGetPublicKey.Size(m) + +func (x *EthereumGetPublicKey) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *EthereumGetPublicKey) XXX_DiscardUnknown() { - xxx_messageInfo_EthereumGetPublicKey.DiscardUnknown(m) + +func (*EthereumGetPublicKey) ProtoMessage() {} + +func (x *EthereumGetPublicKey) ProtoReflect() protoreflect.Message { + mi := &file_messages_ethereum_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_EthereumGetPublicKey proto.InternalMessageInfo +// Deprecated: Use EthereumGetPublicKey.ProtoReflect.Descriptor instead. +func (*EthereumGetPublicKey) Descriptor() ([]byte, []int) { + return file_messages_ethereum_proto_rawDescGZIP(), []int{0} +} -func (m *EthereumGetPublicKey) GetAddressN() []uint32 { - if m != nil { - return m.AddressN +func (x *EthereumGetPublicKey) GetAddressN() []uint32 { + if x != nil { + return x.AddressN } return nil } -func (m *EthereumGetPublicKey) GetShowDisplay() bool { - if m != nil && m.ShowDisplay != nil { - return *m.ShowDisplay +func (x *EthereumGetPublicKey) GetShowDisplay() bool { + if x != nil && x.ShowDisplay != nil { + return *x.ShowDisplay } return false } @@ -77,48 +88,56 @@ func (m *EthereumGetPublicKey) GetShowDisplay() bool { // Response: Contains public key derived from device private seed // @end type EthereumPublicKey struct { - Node *HDNodeType `protobuf:"bytes,1,opt,name=node" json:"node,omitempty"` - Xpub *string `protobuf:"bytes,2,opt,name=xpub" json:"xpub,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *EthereumPublicKey) Reset() { *m = EthereumPublicKey{} } -func (m *EthereumPublicKey) String() string { return proto.CompactTextString(m) } -func (*EthereumPublicKey) ProtoMessage() {} -func (*EthereumPublicKey) Descriptor() ([]byte, []int) { - return fileDescriptor_cb33f46ba915f15c, []int{1} + Node *HDNodeType `protobuf:"bytes,1,opt,name=node" json:"node,omitempty"` // BIP32 public node + Xpub *string `protobuf:"bytes,2,opt,name=xpub" json:"xpub,omitempty"` // serialized form of public node } -func (m *EthereumPublicKey) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_EthereumPublicKey.Unmarshal(m, b) -} -func (m *EthereumPublicKey) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_EthereumPublicKey.Marshal(b, m, deterministic) -} -func (m *EthereumPublicKey) XXX_Merge(src proto.Message) { - xxx_messageInfo_EthereumPublicKey.Merge(m, src) +func (x *EthereumPublicKey) Reset() { + *x = EthereumPublicKey{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_ethereum_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *EthereumPublicKey) XXX_Size() int { - return xxx_messageInfo_EthereumPublicKey.Size(m) + +func (x *EthereumPublicKey) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *EthereumPublicKey) XXX_DiscardUnknown() { - xxx_messageInfo_EthereumPublicKey.DiscardUnknown(m) + +func (*EthereumPublicKey) ProtoMessage() {} + +func (x *EthereumPublicKey) ProtoReflect() protoreflect.Message { + mi := &file_messages_ethereum_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_EthereumPublicKey proto.InternalMessageInfo +// Deprecated: Use EthereumPublicKey.ProtoReflect.Descriptor instead. +func (*EthereumPublicKey) Descriptor() ([]byte, []int) { + return file_messages_ethereum_proto_rawDescGZIP(), []int{1} +} -func (m *EthereumPublicKey) GetNode() *HDNodeType { - if m != nil { - return m.Node +func (x *EthereumPublicKey) GetNode() *HDNodeType { + if x != nil { + return x.Node } return nil } -func (m *EthereumPublicKey) GetXpub() string { - if m != nil && m.Xpub != nil { - return *m.Xpub +func (x *EthereumPublicKey) GetXpub() string { + if x != nil && x.Xpub != nil { + return *x.Xpub } return "" } @@ -129,48 +148,56 @@ func (m *EthereumPublicKey) GetXpub() string { // @next EthereumAddress // @next Failure type EthereumGetAddress struct { - AddressN []uint32 `protobuf:"varint,1,rep,name=address_n,json=addressN" json:"address_n,omitempty"` - ShowDisplay *bool `protobuf:"varint,2,opt,name=show_display,json=showDisplay" json:"show_display,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *EthereumGetAddress) Reset() { *m = EthereumGetAddress{} } -func (m *EthereumGetAddress) String() string { return proto.CompactTextString(m) } -func (*EthereumGetAddress) ProtoMessage() {} -func (*EthereumGetAddress) Descriptor() ([]byte, []int) { - return fileDescriptor_cb33f46ba915f15c, []int{2} + AddressN []uint32 `protobuf:"varint,1,rep,name=address_n,json=addressN" json:"address_n,omitempty"` // BIP-32 path to derive the key from master node + ShowDisplay *bool `protobuf:"varint,2,opt,name=show_display,json=showDisplay" json:"show_display,omitempty"` // optionally show on display before sending the result } -func (m *EthereumGetAddress) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_EthereumGetAddress.Unmarshal(m, b) -} -func (m *EthereumGetAddress) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_EthereumGetAddress.Marshal(b, m, deterministic) -} -func (m *EthereumGetAddress) XXX_Merge(src proto.Message) { - xxx_messageInfo_EthereumGetAddress.Merge(m, src) +func (x *EthereumGetAddress) Reset() { + *x = EthereumGetAddress{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_ethereum_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *EthereumGetAddress) XXX_Size() int { - return xxx_messageInfo_EthereumGetAddress.Size(m) + +func (x *EthereumGetAddress) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *EthereumGetAddress) XXX_DiscardUnknown() { - xxx_messageInfo_EthereumGetAddress.DiscardUnknown(m) + +func (*EthereumGetAddress) ProtoMessage() {} + +func (x *EthereumGetAddress) ProtoReflect() protoreflect.Message { + mi := &file_messages_ethereum_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_EthereumGetAddress proto.InternalMessageInfo +// Deprecated: Use EthereumGetAddress.ProtoReflect.Descriptor instead. +func (*EthereumGetAddress) Descriptor() ([]byte, []int) { + return file_messages_ethereum_proto_rawDescGZIP(), []int{2} +} -func (m *EthereumGetAddress) GetAddressN() []uint32 { - if m != nil { - return m.AddressN +func (x *EthereumGetAddress) GetAddressN() []uint32 { + if x != nil { + return x.AddressN } return nil } -func (m *EthereumGetAddress) GetShowDisplay() bool { - if m != nil && m.ShowDisplay != nil { - return *m.ShowDisplay +func (x *EthereumGetAddress) GetShowDisplay() bool { + if x != nil && x.ShowDisplay != nil { + return *x.ShowDisplay } return false } @@ -179,48 +206,56 @@ func (m *EthereumGetAddress) GetShowDisplay() bool { // Response: Contains an Ethereum address derived from device private seed // @end type EthereumAddress struct { - AddressBin []byte `protobuf:"bytes,1,opt,name=addressBin" json:"addressBin,omitempty"` - AddressHex *string `protobuf:"bytes,2,opt,name=addressHex" json:"addressHex,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *EthereumAddress) Reset() { *m = EthereumAddress{} } -func (m *EthereumAddress) String() string { return proto.CompactTextString(m) } -func (*EthereumAddress) ProtoMessage() {} -func (*EthereumAddress) Descriptor() ([]byte, []int) { - return fileDescriptor_cb33f46ba915f15c, []int{3} + AddressBin []byte `protobuf:"bytes,1,opt,name=addressBin" json:"addressBin,omitempty"` // Ethereum address as 20 bytes (legacy firmwares) + AddressHex *string `protobuf:"bytes,2,opt,name=addressHex" json:"addressHex,omitempty"` // Ethereum address as hex string (newer firmwares) } -func (m *EthereumAddress) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_EthereumAddress.Unmarshal(m, b) -} -func (m *EthereumAddress) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_EthereumAddress.Marshal(b, m, deterministic) -} -func (m *EthereumAddress) XXX_Merge(src proto.Message) { - xxx_messageInfo_EthereumAddress.Merge(m, src) +func (x *EthereumAddress) Reset() { + *x = EthereumAddress{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_ethereum_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *EthereumAddress) XXX_Size() int { - return xxx_messageInfo_EthereumAddress.Size(m) + +func (x *EthereumAddress) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *EthereumAddress) XXX_DiscardUnknown() { - xxx_messageInfo_EthereumAddress.DiscardUnknown(m) + +func (*EthereumAddress) ProtoMessage() {} + +func (x *EthereumAddress) ProtoReflect() protoreflect.Message { + mi := &file_messages_ethereum_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_EthereumAddress proto.InternalMessageInfo +// Deprecated: Use EthereumAddress.ProtoReflect.Descriptor instead. +func (*EthereumAddress) Descriptor() ([]byte, []int) { + return file_messages_ethereum_proto_rawDescGZIP(), []int{3} +} -func (m *EthereumAddress) GetAddressBin() []byte { - if m != nil { - return m.AddressBin +func (x *EthereumAddress) GetAddressBin() []byte { + if x != nil { + return x.AddressBin } return nil } -func (m *EthereumAddress) GetAddressHex() string { - if m != nil && m.AddressHex != nil { - return *m.AddressHex +func (x *EthereumAddress) GetAddressHex() string { + if x != nil && x.AddressHex != nil { + return *x.AddressHex } return "" } @@ -233,120 +268,128 @@ func (m *EthereumAddress) GetAddressHex() string { // @next EthereumTxRequest // @next Failure type EthereumSignTx struct { - AddressN []uint32 `protobuf:"varint,1,rep,name=address_n,json=addressN" json:"address_n,omitempty"` - Nonce []byte `protobuf:"bytes,2,opt,name=nonce" json:"nonce,omitempty"` - GasPrice []byte `protobuf:"bytes,3,opt,name=gas_price,json=gasPrice" json:"gas_price,omitempty"` - GasLimit []byte `protobuf:"bytes,4,opt,name=gas_limit,json=gasLimit" json:"gas_limit,omitempty"` - ToBin []byte `protobuf:"bytes,5,opt,name=toBin" json:"toBin,omitempty"` - ToHex *string `protobuf:"bytes,11,opt,name=toHex" json:"toHex,omitempty"` - Value []byte `protobuf:"bytes,6,opt,name=value" json:"value,omitempty"` - DataInitialChunk []byte `protobuf:"bytes,7,opt,name=data_initial_chunk,json=dataInitialChunk" json:"data_initial_chunk,omitempty"` - DataLength *uint32 `protobuf:"varint,8,opt,name=data_length,json=dataLength" json:"data_length,omitempty"` - ChainId *uint32 `protobuf:"varint,9,opt,name=chain_id,json=chainId" json:"chain_id,omitempty"` - TxType *uint32 `protobuf:"varint,10,opt,name=tx_type,json=txType" json:"tx_type,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *EthereumSignTx) Reset() { *m = EthereumSignTx{} } -func (m *EthereumSignTx) String() string { return proto.CompactTextString(m) } -func (*EthereumSignTx) ProtoMessage() {} -func (*EthereumSignTx) Descriptor() ([]byte, []int) { - return fileDescriptor_cb33f46ba915f15c, []int{4} -} + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *EthereumSignTx) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_EthereumSignTx.Unmarshal(m, b) -} -func (m *EthereumSignTx) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_EthereumSignTx.Marshal(b, m, deterministic) + AddressN []uint32 `protobuf:"varint,1,rep,name=address_n,json=addressN" json:"address_n,omitempty"` // BIP-32 path to derive the key from master node + Nonce []byte `protobuf:"bytes,2,opt,name=nonce" json:"nonce,omitempty"` // <=256 bit unsigned big endian + GasPrice []byte `protobuf:"bytes,3,opt,name=gas_price,json=gasPrice" json:"gas_price,omitempty"` // <=256 bit unsigned big endian (in wei) + GasLimit []byte `protobuf:"bytes,4,opt,name=gas_limit,json=gasLimit" json:"gas_limit,omitempty"` // <=256 bit unsigned big endian + ToBin []byte `protobuf:"bytes,5,opt,name=toBin" json:"toBin,omitempty"` // recipient address (20 bytes, legacy firmware) + ToHex *string `protobuf:"bytes,11,opt,name=toHex" json:"toHex,omitempty"` // recipient address (hex string, newer firmware) + Value []byte `protobuf:"bytes,6,opt,name=value" json:"value,omitempty"` // <=256 bit unsigned big endian (in wei) + DataInitialChunk []byte `protobuf:"bytes,7,opt,name=data_initial_chunk,json=dataInitialChunk" json:"data_initial_chunk,omitempty"` // The initial data chunk (<= 1024 bytes) + DataLength *uint32 `protobuf:"varint,8,opt,name=data_length,json=dataLength" json:"data_length,omitempty"` // Length of transaction payload + ChainId *uint32 `protobuf:"varint,9,opt,name=chain_id,json=chainId" json:"chain_id,omitempty"` // Chain Id for EIP 155 + TxType *uint32 `protobuf:"varint,10,opt,name=tx_type,json=txType" json:"tx_type,omitempty"` // (only for Wanchain) } -func (m *EthereumSignTx) XXX_Merge(src proto.Message) { - xxx_messageInfo_EthereumSignTx.Merge(m, src) + +func (x *EthereumSignTx) Reset() { + *x = EthereumSignTx{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_ethereum_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *EthereumSignTx) XXX_Size() int { - return xxx_messageInfo_EthereumSignTx.Size(m) + +func (x *EthereumSignTx) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *EthereumSignTx) XXX_DiscardUnknown() { - xxx_messageInfo_EthereumSignTx.DiscardUnknown(m) + +func (*EthereumSignTx) ProtoMessage() {} + +func (x *EthereumSignTx) ProtoReflect() protoreflect.Message { + mi := &file_messages_ethereum_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_EthereumSignTx proto.InternalMessageInfo +// Deprecated: Use EthereumSignTx.ProtoReflect.Descriptor instead. +func (*EthereumSignTx) Descriptor() ([]byte, []int) { + return file_messages_ethereum_proto_rawDescGZIP(), []int{4} +} -func (m *EthereumSignTx) GetAddressN() []uint32 { - if m != nil { - return m.AddressN +func (x *EthereumSignTx) GetAddressN() []uint32 { + if x != nil { + return x.AddressN } return nil } -func (m *EthereumSignTx) GetNonce() []byte { - if m != nil { - return m.Nonce +func (x *EthereumSignTx) GetNonce() []byte { + if x != nil { + return x.Nonce } return nil } -func (m *EthereumSignTx) GetGasPrice() []byte { - if m != nil { - return m.GasPrice +func (x *EthereumSignTx) GetGasPrice() []byte { + if x != nil { + return x.GasPrice } return nil } -func (m *EthereumSignTx) GetGasLimit() []byte { - if m != nil { - return m.GasLimit +func (x *EthereumSignTx) GetGasLimit() []byte { + if x != nil { + return x.GasLimit } return nil } -func (m *EthereumSignTx) GetToBin() []byte { - if m != nil { - return m.ToBin +func (x *EthereumSignTx) GetToBin() []byte { + if x != nil { + return x.ToBin } return nil } -func (m *EthereumSignTx) GetToHex() string { - if m != nil && m.ToHex != nil { - return *m.ToHex +func (x *EthereumSignTx) GetToHex() string { + if x != nil && x.ToHex != nil { + return *x.ToHex } return "" } -func (m *EthereumSignTx) GetValue() []byte { - if m != nil { - return m.Value +func (x *EthereumSignTx) GetValue() []byte { + if x != nil { + return x.Value } return nil } -func (m *EthereumSignTx) GetDataInitialChunk() []byte { - if m != nil { - return m.DataInitialChunk +func (x *EthereumSignTx) GetDataInitialChunk() []byte { + if x != nil { + return x.DataInitialChunk } return nil } -func (m *EthereumSignTx) GetDataLength() uint32 { - if m != nil && m.DataLength != nil { - return *m.DataLength +func (x *EthereumSignTx) GetDataLength() uint32 { + if x != nil && x.DataLength != nil { + return *x.DataLength } return 0 } -func (m *EthereumSignTx) GetChainId() uint32 { - if m != nil && m.ChainId != nil { - return *m.ChainId +func (x *EthereumSignTx) GetChainId() uint32 { + if x != nil && x.ChainId != nil { + return *x.ChainId } return 0 } -func (m *EthereumSignTx) GetTxType() uint32 { - if m != nil && m.TxType != nil { - return *m.TxType +func (x *EthereumSignTx) GetTxType() uint32 { + if x != nil && x.TxType != nil { + return *x.TxType } return 0 } @@ -358,64 +401,72 @@ func (m *EthereumSignTx) GetTxType() uint32 { // @end // @next EthereumTxAck type EthereumTxRequest struct { - DataLength *uint32 `protobuf:"varint,1,opt,name=data_length,json=dataLength" json:"data_length,omitempty"` - SignatureV *uint32 `protobuf:"varint,2,opt,name=signature_v,json=signatureV" json:"signature_v,omitempty"` - SignatureR []byte `protobuf:"bytes,3,opt,name=signature_r,json=signatureR" json:"signature_r,omitempty"` - SignatureS []byte `protobuf:"bytes,4,opt,name=signature_s,json=signatureS" json:"signature_s,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *EthereumTxRequest) Reset() { *m = EthereumTxRequest{} } -func (m *EthereumTxRequest) String() string { return proto.CompactTextString(m) } -func (*EthereumTxRequest) ProtoMessage() {} -func (*EthereumTxRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_cb33f46ba915f15c, []int{5} -} + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *EthereumTxRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_EthereumTxRequest.Unmarshal(m, b) -} -func (m *EthereumTxRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_EthereumTxRequest.Marshal(b, m, deterministic) + DataLength *uint32 `protobuf:"varint,1,opt,name=data_length,json=dataLength" json:"data_length,omitempty"` // Number of bytes being requested (<= 1024) + SignatureV *uint32 `protobuf:"varint,2,opt,name=signature_v,json=signatureV" json:"signature_v,omitempty"` // Computed signature (recovery parameter, limited to 27 or 28) + SignatureR []byte `protobuf:"bytes,3,opt,name=signature_r,json=signatureR" json:"signature_r,omitempty"` // Computed signature R component (256 bit) + SignatureS []byte `protobuf:"bytes,4,opt,name=signature_s,json=signatureS" json:"signature_s,omitempty"` // Computed signature S component (256 bit) } -func (m *EthereumTxRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_EthereumTxRequest.Merge(m, src) + +func (x *EthereumTxRequest) Reset() { + *x = EthereumTxRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_ethereum_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *EthereumTxRequest) XXX_Size() int { - return xxx_messageInfo_EthereumTxRequest.Size(m) + +func (x *EthereumTxRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *EthereumTxRequest) XXX_DiscardUnknown() { - xxx_messageInfo_EthereumTxRequest.DiscardUnknown(m) + +func (*EthereumTxRequest) ProtoMessage() {} + +func (x *EthereumTxRequest) ProtoReflect() protoreflect.Message { + mi := &file_messages_ethereum_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_EthereumTxRequest proto.InternalMessageInfo +// Deprecated: Use EthereumTxRequest.ProtoReflect.Descriptor instead. +func (*EthereumTxRequest) Descriptor() ([]byte, []int) { + return file_messages_ethereum_proto_rawDescGZIP(), []int{5} +} -func (m *EthereumTxRequest) GetDataLength() uint32 { - if m != nil && m.DataLength != nil { - return *m.DataLength +func (x *EthereumTxRequest) GetDataLength() uint32 { + if x != nil && x.DataLength != nil { + return *x.DataLength } return 0 } -func (m *EthereumTxRequest) GetSignatureV() uint32 { - if m != nil && m.SignatureV != nil { - return *m.SignatureV +func (x *EthereumTxRequest) GetSignatureV() uint32 { + if x != nil && x.SignatureV != nil { + return *x.SignatureV } return 0 } -func (m *EthereumTxRequest) GetSignatureR() []byte { - if m != nil { - return m.SignatureR +func (x *EthereumTxRequest) GetSignatureR() []byte { + if x != nil { + return x.SignatureR } return nil } -func (m *EthereumTxRequest) GetSignatureS() []byte { - if m != nil { - return m.SignatureS +func (x *EthereumTxRequest) GetSignatureS() []byte { + if x != nil { + return x.SignatureS } return nil } @@ -424,40 +475,48 @@ func (m *EthereumTxRequest) GetSignatureS() []byte { // Request: Transaction payload data. // @next EthereumTxRequest type EthereumTxAck struct { - DataChunk []byte `protobuf:"bytes,1,opt,name=data_chunk,json=dataChunk" json:"data_chunk,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *EthereumTxAck) Reset() { *m = EthereumTxAck{} } -func (m *EthereumTxAck) String() string { return proto.CompactTextString(m) } -func (*EthereumTxAck) ProtoMessage() {} -func (*EthereumTxAck) Descriptor() ([]byte, []int) { - return fileDescriptor_cb33f46ba915f15c, []int{6} + DataChunk []byte `protobuf:"bytes,1,opt,name=data_chunk,json=dataChunk" json:"data_chunk,omitempty"` // Bytes from transaction payload (<= 1024 bytes) } -func (m *EthereumTxAck) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_EthereumTxAck.Unmarshal(m, b) -} -func (m *EthereumTxAck) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_EthereumTxAck.Marshal(b, m, deterministic) -} -func (m *EthereumTxAck) XXX_Merge(src proto.Message) { - xxx_messageInfo_EthereumTxAck.Merge(m, src) +func (x *EthereumTxAck) Reset() { + *x = EthereumTxAck{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_ethereum_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *EthereumTxAck) XXX_Size() int { - return xxx_messageInfo_EthereumTxAck.Size(m) + +func (x *EthereumTxAck) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *EthereumTxAck) XXX_DiscardUnknown() { - xxx_messageInfo_EthereumTxAck.DiscardUnknown(m) + +func (*EthereumTxAck) ProtoMessage() {} + +func (x *EthereumTxAck) ProtoReflect() protoreflect.Message { + mi := &file_messages_ethereum_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_EthereumTxAck proto.InternalMessageInfo +// Deprecated: Use EthereumTxAck.ProtoReflect.Descriptor instead. +func (*EthereumTxAck) Descriptor() ([]byte, []int) { + return file_messages_ethereum_proto_rawDescGZIP(), []int{6} +} -func (m *EthereumTxAck) GetDataChunk() []byte { - if m != nil { - return m.DataChunk +func (x *EthereumTxAck) GetDataChunk() []byte { + if x != nil { + return x.DataChunk } return nil } @@ -468,48 +527,56 @@ func (m *EthereumTxAck) GetDataChunk() []byte { // @next EthereumMessageSignature // @next Failure type EthereumSignMessage struct { - AddressN []uint32 `protobuf:"varint,1,rep,name=address_n,json=addressN" json:"address_n,omitempty"` - Message []byte `protobuf:"bytes,2,opt,name=message" json:"message,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *EthereumSignMessage) Reset() { *m = EthereumSignMessage{} } -func (m *EthereumSignMessage) String() string { return proto.CompactTextString(m) } -func (*EthereumSignMessage) ProtoMessage() {} -func (*EthereumSignMessage) Descriptor() ([]byte, []int) { - return fileDescriptor_cb33f46ba915f15c, []int{7} + AddressN []uint32 `protobuf:"varint,1,rep,name=address_n,json=addressN" json:"address_n,omitempty"` // BIP-32 path to derive the key from master node + Message []byte `protobuf:"bytes,2,opt,name=message" json:"message,omitempty"` // message to be signed } -func (m *EthereumSignMessage) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_EthereumSignMessage.Unmarshal(m, b) -} -func (m *EthereumSignMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_EthereumSignMessage.Marshal(b, m, deterministic) -} -func (m *EthereumSignMessage) XXX_Merge(src proto.Message) { - xxx_messageInfo_EthereumSignMessage.Merge(m, src) +func (x *EthereumSignMessage) Reset() { + *x = EthereumSignMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_ethereum_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *EthereumSignMessage) XXX_Size() int { - return xxx_messageInfo_EthereumSignMessage.Size(m) + +func (x *EthereumSignMessage) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *EthereumSignMessage) XXX_DiscardUnknown() { - xxx_messageInfo_EthereumSignMessage.DiscardUnknown(m) + +func (*EthereumSignMessage) ProtoMessage() {} + +func (x *EthereumSignMessage) ProtoReflect() protoreflect.Message { + mi := &file_messages_ethereum_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_EthereumSignMessage proto.InternalMessageInfo +// Deprecated: Use EthereumSignMessage.ProtoReflect.Descriptor instead. +func (*EthereumSignMessage) Descriptor() ([]byte, []int) { + return file_messages_ethereum_proto_rawDescGZIP(), []int{7} +} -func (m *EthereumSignMessage) GetAddressN() []uint32 { - if m != nil { - return m.AddressN +func (x *EthereumSignMessage) GetAddressN() []uint32 { + if x != nil { + return x.AddressN } return nil } -func (m *EthereumSignMessage) GetMessage() []byte { - if m != nil { - return m.Message +func (x *EthereumSignMessage) GetMessage() []byte { + if x != nil { + return x.Message } return nil } @@ -518,56 +585,64 @@ func (m *EthereumSignMessage) GetMessage() []byte { // Response: Signed message // @end type EthereumMessageSignature struct { - AddressBin []byte `protobuf:"bytes,1,opt,name=addressBin" json:"addressBin,omitempty"` - Signature []byte `protobuf:"bytes,2,opt,name=signature" json:"signature,omitempty"` - AddressHex *string `protobuf:"bytes,3,opt,name=addressHex" json:"addressHex,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *EthereumMessageSignature) Reset() { *m = EthereumMessageSignature{} } -func (m *EthereumMessageSignature) String() string { return proto.CompactTextString(m) } -func (*EthereumMessageSignature) ProtoMessage() {} -func (*EthereumMessageSignature) Descriptor() ([]byte, []int) { - return fileDescriptor_cb33f46ba915f15c, []int{8} + AddressBin []byte `protobuf:"bytes,1,opt,name=addressBin" json:"addressBin,omitempty"` // address used to sign the message (20 bytes, legacy firmware) + Signature []byte `protobuf:"bytes,2,opt,name=signature" json:"signature,omitempty"` // signature of the message + AddressHex *string `protobuf:"bytes,3,opt,name=addressHex" json:"addressHex,omitempty"` // address used to sign the message (hex string, newer firmware) } -func (m *EthereumMessageSignature) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_EthereumMessageSignature.Unmarshal(m, b) -} -func (m *EthereumMessageSignature) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_EthereumMessageSignature.Marshal(b, m, deterministic) -} -func (m *EthereumMessageSignature) XXX_Merge(src proto.Message) { - xxx_messageInfo_EthereumMessageSignature.Merge(m, src) +func (x *EthereumMessageSignature) Reset() { + *x = EthereumMessageSignature{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_ethereum_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *EthereumMessageSignature) XXX_Size() int { - return xxx_messageInfo_EthereumMessageSignature.Size(m) + +func (x *EthereumMessageSignature) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *EthereumMessageSignature) XXX_DiscardUnknown() { - xxx_messageInfo_EthereumMessageSignature.DiscardUnknown(m) + +func (*EthereumMessageSignature) ProtoMessage() {} + +func (x *EthereumMessageSignature) ProtoReflect() protoreflect.Message { + mi := &file_messages_ethereum_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_EthereumMessageSignature proto.InternalMessageInfo +// Deprecated: Use EthereumMessageSignature.ProtoReflect.Descriptor instead. +func (*EthereumMessageSignature) Descriptor() ([]byte, []int) { + return file_messages_ethereum_proto_rawDescGZIP(), []int{8} +} -func (m *EthereumMessageSignature) GetAddressBin() []byte { - if m != nil { - return m.AddressBin +func (x *EthereumMessageSignature) GetAddressBin() []byte { + if x != nil { + return x.AddressBin } return nil } -func (m *EthereumMessageSignature) GetSignature() []byte { - if m != nil { - return m.Signature +func (x *EthereumMessageSignature) GetSignature() []byte { + if x != nil { + return x.Signature } return nil } -func (m *EthereumMessageSignature) GetAddressHex() string { - if m != nil && m.AddressHex != nil { - return *m.AddressHex +func (x *EthereumMessageSignature) GetAddressHex() string { + if x != nil && x.AddressHex != nil { + return *x.AddressHex } return "" } @@ -578,121 +653,350 @@ func (m *EthereumMessageSignature) GetAddressHex() string { // @next Success // @next Failure type EthereumVerifyMessage struct { - AddressBin []byte `protobuf:"bytes,1,opt,name=addressBin" json:"addressBin,omitempty"` - Signature []byte `protobuf:"bytes,2,opt,name=signature" json:"signature,omitempty"` - Message []byte `protobuf:"bytes,3,opt,name=message" json:"message,omitempty"` - AddressHex *string `protobuf:"bytes,4,opt,name=addressHex" json:"addressHex,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *EthereumVerifyMessage) Reset() { *m = EthereumVerifyMessage{} } -func (m *EthereumVerifyMessage) String() string { return proto.CompactTextString(m) } -func (*EthereumVerifyMessage) ProtoMessage() {} -func (*EthereumVerifyMessage) Descriptor() ([]byte, []int) { - return fileDescriptor_cb33f46ba915f15c, []int{9} -} + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *EthereumVerifyMessage) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_EthereumVerifyMessage.Unmarshal(m, b) -} -func (m *EthereumVerifyMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_EthereumVerifyMessage.Marshal(b, m, deterministic) + AddressBin []byte `protobuf:"bytes,1,opt,name=addressBin" json:"addressBin,omitempty"` // address to verify (20 bytes, legacy firmware) + Signature []byte `protobuf:"bytes,2,opt,name=signature" json:"signature,omitempty"` // signature to verify + Message []byte `protobuf:"bytes,3,opt,name=message" json:"message,omitempty"` // message to verify + AddressHex *string `protobuf:"bytes,4,opt,name=addressHex" json:"addressHex,omitempty"` // address to verify (hex string, newer firmware) } -func (m *EthereumVerifyMessage) XXX_Merge(src proto.Message) { - xxx_messageInfo_EthereumVerifyMessage.Merge(m, src) + +func (x *EthereumVerifyMessage) Reset() { + *x = EthereumVerifyMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_ethereum_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *EthereumVerifyMessage) XXX_Size() int { - return xxx_messageInfo_EthereumVerifyMessage.Size(m) + +func (x *EthereumVerifyMessage) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *EthereumVerifyMessage) XXX_DiscardUnknown() { - xxx_messageInfo_EthereumVerifyMessage.DiscardUnknown(m) + +func (*EthereumVerifyMessage) ProtoMessage() {} + +func (x *EthereumVerifyMessage) ProtoReflect() protoreflect.Message { + mi := &file_messages_ethereum_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_EthereumVerifyMessage proto.InternalMessageInfo +// Deprecated: Use EthereumVerifyMessage.ProtoReflect.Descriptor instead. +func (*EthereumVerifyMessage) Descriptor() ([]byte, []int) { + return file_messages_ethereum_proto_rawDescGZIP(), []int{9} +} -func (m *EthereumVerifyMessage) GetAddressBin() []byte { - if m != nil { - return m.AddressBin +func (x *EthereumVerifyMessage) GetAddressBin() []byte { + if x != nil { + return x.AddressBin } return nil } -func (m *EthereumVerifyMessage) GetSignature() []byte { - if m != nil { - return m.Signature +func (x *EthereumVerifyMessage) GetSignature() []byte { + if x != nil { + return x.Signature } return nil } -func (m *EthereumVerifyMessage) GetMessage() []byte { - if m != nil { - return m.Message +func (x *EthereumVerifyMessage) GetMessage() []byte { + if x != nil { + return x.Message } return nil } -func (m *EthereumVerifyMessage) GetAddressHex() string { - if m != nil && m.AddressHex != nil { - return *m.AddressHex +func (x *EthereumVerifyMessage) GetAddressHex() string { + if x != nil && x.AddressHex != nil { + return *x.AddressHex } return "" } -func init() { - proto.RegisterType((*EthereumGetPublicKey)(nil), "hw.trezor.messages.ethereum.EthereumGetPublicKey") - proto.RegisterType((*EthereumPublicKey)(nil), "hw.trezor.messages.ethereum.EthereumPublicKey") - proto.RegisterType((*EthereumGetAddress)(nil), "hw.trezor.messages.ethereum.EthereumGetAddress") - proto.RegisterType((*EthereumAddress)(nil), "hw.trezor.messages.ethereum.EthereumAddress") - proto.RegisterType((*EthereumSignTx)(nil), "hw.trezor.messages.ethereum.EthereumSignTx") - proto.RegisterType((*EthereumTxRequest)(nil), "hw.trezor.messages.ethereum.EthereumTxRequest") - proto.RegisterType((*EthereumTxAck)(nil), "hw.trezor.messages.ethereum.EthereumTxAck") - proto.RegisterType((*EthereumSignMessage)(nil), "hw.trezor.messages.ethereum.EthereumSignMessage") - proto.RegisterType((*EthereumMessageSignature)(nil), "hw.trezor.messages.ethereum.EthereumMessageSignature") - proto.RegisterType((*EthereumVerifyMessage)(nil), "hw.trezor.messages.ethereum.EthereumVerifyMessage") -} - -func init() { proto.RegisterFile("messages-ethereum.proto", fileDescriptor_cb33f46ba915f15c) } - -var fileDescriptor_cb33f46ba915f15c = []byte{ - // 593 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x54, 0x4d, 0x6f, 0xd3, 0x40, - 0x10, 0x95, 0x9b, 0xb4, 0x49, 0x26, 0x0d, 0x1f, 0xa6, 0x55, 0x17, 0x0a, 0x34, 0x18, 0x21, 0xe5, - 0x00, 0x3e, 0x70, 0x43, 0xe2, 0xd2, 0x52, 0x44, 0x2b, 0x4a, 0x55, 0xdc, 0xa8, 0x57, 0x6b, 0x63, - 0x6f, 0xe3, 0x55, 0x9d, 0xdd, 0xe0, 0x5d, 0xb7, 0x0e, 0x7f, 0x82, 0x23, 0xff, 0x87, 0x5f, 0x86, - 0xf6, 0x2b, 0x71, 0x52, 0x54, 0x0e, 0xbd, 0x65, 0xde, 0xbc, 0x7d, 0xf3, 0x66, 0xf4, 0x62, 0xd8, - 0x99, 0x10, 0x21, 0xf0, 0x98, 0x88, 0x77, 0x44, 0x66, 0xa4, 0x20, 0xe5, 0x24, 0x9c, 0x16, 0x5c, - 0x72, 0x7f, 0x37, 0xbb, 0x09, 0x65, 0x41, 0x7e, 0xf2, 0x22, 0x74, 0x94, 0xd0, 0x51, 0x9e, 0x6d, - 0xcf, 0x5f, 0x25, 0x7c, 0x32, 0xe1, 0xcc, 0xbc, 0x09, 0x2e, 0x60, 0xeb, 0xb3, 0xa5, 0x7c, 0x21, - 0xf2, 0xac, 0x1c, 0xe5, 0x34, 0xf9, 0x4a, 0x66, 0xfe, 0x2e, 0x74, 0x70, 0x9a, 0x16, 0x44, 0x88, - 0x98, 0x21, 0xaf, 0xdf, 0x18, 0xf4, 0xa2, 0xb6, 0x05, 0x4e, 0xfd, 0x57, 0xb0, 0x29, 0x32, 0x7e, - 0x13, 0xa7, 0x54, 0x4c, 0x73, 0x3c, 0x43, 0x6b, 0x7d, 0x6f, 0xd0, 0x8e, 0xba, 0x0a, 0x3b, 0x34, - 0x50, 0x30, 0x82, 0xc7, 0x4e, 0x77, 0x21, 0xfa, 0x01, 0x9a, 0x8c, 0xa7, 0x04, 0x79, 0x7d, 0x6f, - 0xd0, 0x7d, 0xff, 0x26, 0xfc, 0x87, 0x5f, 0x6b, 0xee, 0xe8, 0xf0, 0x94, 0xa7, 0x64, 0x38, 0x9b, - 0x92, 0x48, 0x3f, 0xf1, 0x7d, 0x68, 0x56, 0xd3, 0x72, 0xa4, 0x47, 0x75, 0x22, 0xfd, 0x3b, 0x18, - 0x82, 0x5f, 0xf3, 0xbe, 0x6f, 0xdc, 0xdd, 0xdb, 0xf9, 0x77, 0x78, 0xe8, 0x54, 0x9d, 0xe4, 0x4b, - 0x00, 0xab, 0x70, 0x40, 0x99, 0x76, 0xbf, 0x19, 0xd5, 0x90, 0x5a, 0xff, 0x88, 0x54, 0xd6, 0x62, - 0x0d, 0x09, 0xfe, 0xac, 0xc1, 0x03, 0xa7, 0x79, 0x4e, 0xc7, 0x6c, 0x58, 0xdd, 0xed, 0x72, 0x0b, - 0xd6, 0x19, 0x67, 0x09, 0xd1, 0x52, 0x9b, 0x91, 0x29, 0xd4, 0x93, 0x31, 0x16, 0xf1, 0xb4, 0xa0, - 0x09, 0x41, 0x0d, 0xdd, 0x69, 0x8f, 0xb1, 0x38, 0x53, 0xb5, 0x6b, 0xe6, 0x74, 0x42, 0x25, 0x6a, - 0xce, 0x9b, 0x27, 0xaa, 0x56, 0x7a, 0x92, 0x2b, 0xeb, 0xeb, 0x46, 0x4f, 0x17, 0x06, 0x55, 0x86, - 0xbb, 0xda, 0xb0, 0x29, 0x14, 0x7a, 0x8d, 0xf3, 0x92, 0xa0, 0x0d, 0xc3, 0xd5, 0x85, 0xff, 0x16, - 0xfc, 0x14, 0x4b, 0x1c, 0x53, 0x46, 0x25, 0xc5, 0x79, 0x9c, 0x64, 0x25, 0xbb, 0x42, 0x2d, 0x4d, - 0x79, 0xa4, 0x3a, 0xc7, 0xa6, 0xf1, 0x49, 0xe1, 0xfe, 0x1e, 0x74, 0x35, 0x3b, 0x27, 0x6c, 0x2c, - 0x33, 0xd4, 0xee, 0x7b, 0x83, 0x5e, 0x04, 0x0a, 0x3a, 0xd1, 0x88, 0xff, 0x14, 0xda, 0x49, 0x86, - 0x29, 0x8b, 0x69, 0x8a, 0x3a, 0xba, 0xdb, 0xd2, 0xf5, 0x71, 0xea, 0xef, 0x40, 0x4b, 0x56, 0xb1, - 0x9c, 0x4d, 0x09, 0x02, 0xdd, 0xd9, 0x90, 0x95, 0xca, 0x41, 0xf0, 0xdb, 0x5b, 0x44, 0x6a, 0x58, - 0x45, 0xe4, 0x47, 0x49, 0x84, 0x5c, 0x1d, 0xe5, 0xdd, 0x1a, 0xb5, 0x07, 0x5d, 0x41, 0xc7, 0x0c, - 0xcb, 0xb2, 0x20, 0xf1, 0xb5, 0xbe, 0x68, 0x2f, 0x82, 0x39, 0x74, 0xb1, 0x4c, 0x28, 0xec, 0x61, - 0x17, 0x84, 0x68, 0x99, 0x20, 0xec, 0x71, 0x17, 0x84, 0xf3, 0x20, 0x84, 0xde, 0xc2, 0xd8, 0x7e, - 0x72, 0xe5, 0xbf, 0x00, 0xed, 0xc0, 0x5e, 0xc9, 0xe4, 0xa5, 0xa3, 0x10, 0x7d, 0x9e, 0xe0, 0x04, - 0x9e, 0xd4, 0xd3, 0xf0, 0xcd, 0x64, 0xff, 0xee, 0x48, 0x20, 0x68, 0xd9, 0xff, 0x88, 0x0d, 0x85, - 0x2b, 0x83, 0x0a, 0x90, 0x53, 0xb3, 0x4a, 0xe7, 0xce, 0xda, 0x7f, 0x83, 0xfb, 0x1c, 0x3a, 0xf3, - 0x3d, 0xac, 0xee, 0x02, 0x58, 0x89, 0x75, 0xe3, 0x56, 0xac, 0x7f, 0x79, 0xb0, 0xed, 0x46, 0x5f, - 0x90, 0x82, 0x5e, 0xce, 0xdc, 0x2a, 0xf7, 0x9b, 0x5b, 0xdb, 0xb5, 0xb1, 0xb4, 0xeb, 0x8a, 0xa3, - 0xe6, 0xaa, 0xa3, 0x83, 0x8f, 0xf0, 0x3a, 0xe1, 0x93, 0x50, 0x60, 0xc9, 0x45, 0x46, 0x73, 0x3c, - 0x12, 0xee, 0x03, 0x93, 0xd3, 0x91, 0xf9, 0xe2, 0x8d, 0xca, 0xcb, 0x83, 0xed, 0xa1, 0x06, 0xad, - 0x5b, 0xb7, 0xc2, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x8a, 0xce, 0x81, 0xc8, 0x59, 0x05, 0x00, - 0x00, +var File_messages_ethereum_proto protoreflect.FileDescriptor + +var file_messages_ethereum_proto_rawDesc = []byte{ + 0x0a, 0x17, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2d, 0x65, 0x74, 0x68, 0x65, 0x72, + 0x65, 0x75, 0x6d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1b, 0x68, 0x77, 0x2e, 0x74, 0x72, + 0x65, 0x7a, 0x6f, 0x72, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x65, 0x74, + 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x1a, 0x15, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, + 0x2d, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x56, 0x0a, + 0x14, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x47, 0x65, 0x74, 0x50, 0x75, 0x62, 0x6c, + 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x5f, 0x6e, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x08, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x4e, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x68, 0x6f, 0x77, 0x5f, 0x64, 0x69, 0x73, 0x70, 0x6c, + 0x61, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x73, 0x68, 0x6f, 0x77, 0x44, 0x69, + 0x73, 0x70, 0x6c, 0x61, 0x79, 0x22, 0x62, 0x0a, 0x11, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, + 0x6d, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x39, 0x0a, 0x04, 0x6e, 0x6f, + 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x68, 0x77, 0x2e, 0x74, 0x72, + 0x65, 0x7a, 0x6f, 0x72, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x63, 0x6f, + 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x48, 0x44, 0x4e, 0x6f, 0x64, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, + 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x78, 0x70, 0x75, 0x62, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x78, 0x70, 0x75, 0x62, 0x22, 0x54, 0x0a, 0x12, 0x45, 0x74, 0x68, + 0x65, 0x72, 0x65, 0x75, 0x6d, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, + 0x1b, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x6e, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0d, 0x52, 0x08, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x4e, 0x12, 0x21, 0x0a, 0x0c, + 0x73, 0x68, 0x6f, 0x77, 0x5f, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0b, 0x73, 0x68, 0x6f, 0x77, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x22, + 0x51, 0x0a, 0x0f, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x41, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x69, 0x6e, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, + 0x69, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x48, 0x65, 0x78, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x48, + 0x65, 0x78, 0x22, 0xc2, 0x02, 0x0a, 0x0e, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x53, + 0x69, 0x67, 0x6e, 0x54, 0x78, 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x5f, 0x6e, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x08, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x4e, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x61, 0x73, 0x5f, + 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x67, 0x61, 0x73, + 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x61, 0x73, 0x5f, 0x6c, 0x69, 0x6d, + 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x67, 0x61, 0x73, 0x4c, 0x69, 0x6d, + 0x69, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x42, 0x69, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x05, 0x74, 0x6f, 0x42, 0x69, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x48, 0x65, + 0x78, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x48, 0x65, 0x78, 0x12, 0x14, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x69, 0x6e, 0x69, + 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x10, 0x64, 0x61, 0x74, 0x61, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x43, 0x68, 0x75, + 0x6e, 0x6b, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, + 0x68, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x64, 0x61, 0x74, 0x61, 0x4c, 0x65, 0x6e, + 0x67, 0x74, 0x68, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, + 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x64, 0x12, 0x17, + 0x0a, 0x07, 0x74, 0x78, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x06, 0x74, 0x78, 0x54, 0x79, 0x70, 0x65, 0x22, 0x97, 0x01, 0x0a, 0x11, 0x45, 0x74, 0x68, 0x65, + 0x72, 0x65, 0x75, 0x6d, 0x54, 0x78, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, + 0x0b, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x0a, 0x64, 0x61, 0x74, 0x61, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, 0x1f, + 0x0a, 0x0b, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x76, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x56, 0x12, + 0x1f, 0x0a, 0x0b, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x72, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, + 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x73, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, + 0x53, 0x22, 0x2e, 0x0a, 0x0d, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x54, 0x78, 0x41, + 0x63, 0x6b, 0x12, 0x1d, 0x0a, 0x0a, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x63, 0x68, 0x75, 0x6e, 0x6b, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x64, 0x61, 0x74, 0x61, 0x43, 0x68, 0x75, 0x6e, + 0x6b, 0x22, 0x4c, 0x0a, 0x13, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x53, 0x69, 0x67, + 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x5f, 0x6e, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x08, 0x61, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x4e, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, + 0x78, 0x0a, 0x18, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x61, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x0a, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x69, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x73, + 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, + 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x48, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x48, 0x65, 0x78, 0x22, 0x8f, 0x01, 0x0a, 0x15, 0x45, 0x74, + 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x69, + 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x42, 0x69, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, + 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x61, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x48, 0x65, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0a, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x48, 0x65, 0x78, 0x42, 0x77, 0x0a, 0x23, 0x63, + 0x6f, 0x6d, 0x2e, 0x73, 0x61, 0x74, 0x6f, 0x73, 0x68, 0x69, 0x6c, 0x61, 0x62, 0x73, 0x2e, 0x74, + 0x72, 0x65, 0x7a, 0x6f, 0x72, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x42, 0x15, 0x54, 0x72, 0x65, 0x7a, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2f, 0x67, + 0x6f, 0x2d, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2f, 0x61, 0x63, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x73, 0x2f, 0x75, 0x73, 0x62, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2f, 0x74, 0x72, + 0x65, 0x7a, 0x6f, 0x72, +} + +var ( + file_messages_ethereum_proto_rawDescOnce sync.Once + file_messages_ethereum_proto_rawDescData = file_messages_ethereum_proto_rawDesc +) + +func file_messages_ethereum_proto_rawDescGZIP() []byte { + file_messages_ethereum_proto_rawDescOnce.Do(func() { + file_messages_ethereum_proto_rawDescData = protoimpl.X.CompressGZIP(file_messages_ethereum_proto_rawDescData) + }) + return file_messages_ethereum_proto_rawDescData +} + +var file_messages_ethereum_proto_msgTypes = make([]protoimpl.MessageInfo, 10) +var file_messages_ethereum_proto_goTypes = []any{ + (*EthereumGetPublicKey)(nil), // 0: hw.trezor.messages.ethereum.EthereumGetPublicKey + (*EthereumPublicKey)(nil), // 1: hw.trezor.messages.ethereum.EthereumPublicKey + (*EthereumGetAddress)(nil), // 2: hw.trezor.messages.ethereum.EthereumGetAddress + (*EthereumAddress)(nil), // 3: hw.trezor.messages.ethereum.EthereumAddress + (*EthereumSignTx)(nil), // 4: hw.trezor.messages.ethereum.EthereumSignTx + (*EthereumTxRequest)(nil), // 5: hw.trezor.messages.ethereum.EthereumTxRequest + (*EthereumTxAck)(nil), // 6: hw.trezor.messages.ethereum.EthereumTxAck + (*EthereumSignMessage)(nil), // 7: hw.trezor.messages.ethereum.EthereumSignMessage + (*EthereumMessageSignature)(nil), // 8: hw.trezor.messages.ethereum.EthereumMessageSignature + (*EthereumVerifyMessage)(nil), // 9: hw.trezor.messages.ethereum.EthereumVerifyMessage + (*HDNodeType)(nil), // 10: hw.trezor.messages.common.HDNodeType +} +var file_messages_ethereum_proto_depIdxs = []int32{ + 10, // 0: hw.trezor.messages.ethereum.EthereumPublicKey.node:type_name -> hw.trezor.messages.common.HDNodeType + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_messages_ethereum_proto_init() } +func file_messages_ethereum_proto_init() { + if File_messages_ethereum_proto != nil { + return + } + file_messages_common_proto_init() + if !protoimpl.UnsafeEnabled { + file_messages_ethereum_proto_msgTypes[0].Exporter = func(v any, i int) any { + switch v := v.(*EthereumGetPublicKey); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_ethereum_proto_msgTypes[1].Exporter = func(v any, i int) any { + switch v := v.(*EthereumPublicKey); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_ethereum_proto_msgTypes[2].Exporter = func(v any, i int) any { + switch v := v.(*EthereumGetAddress); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_ethereum_proto_msgTypes[3].Exporter = func(v any, i int) any { + switch v := v.(*EthereumAddress); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_ethereum_proto_msgTypes[4].Exporter = func(v any, i int) any { + switch v := v.(*EthereumSignTx); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_ethereum_proto_msgTypes[5].Exporter = func(v any, i int) any { + switch v := v.(*EthereumTxRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_ethereum_proto_msgTypes[6].Exporter = func(v any, i int) any { + switch v := v.(*EthereumTxAck); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_ethereum_proto_msgTypes[7].Exporter = func(v any, i int) any { + switch v := v.(*EthereumSignMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_ethereum_proto_msgTypes[8].Exporter = func(v any, i int) any { + switch v := v.(*EthereumMessageSignature); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_ethereum_proto_msgTypes[9].Exporter = func(v any, i int) any { + switch v := v.(*EthereumVerifyMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_messages_ethereum_proto_rawDesc, + NumEnums: 0, + NumMessages: 10, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_messages_ethereum_proto_goTypes, + DependencyIndexes: file_messages_ethereum_proto_depIdxs, + MessageInfos: file_messages_ethereum_proto_msgTypes, + }.Build() + File_messages_ethereum_proto = out.File + file_messages_ethereum_proto_rawDesc = nil + file_messages_ethereum_proto_goTypes = nil + file_messages_ethereum_proto_depIdxs = nil } diff --git a/accounts/usbwallet/trezor/messages-ethereum.proto b/accounts/usbwallet/trezor/messages-ethereum.proto index 096bed2e4a..8e1150abb6 100644 --- a/accounts/usbwallet/trezor/messages-ethereum.proto +++ b/accounts/usbwallet/trezor/messages-ethereum.proto @@ -5,6 +5,8 @@ syntax = "proto2"; package hw.trezor.messages.ethereum; +option go_package = "github.com/ethereum/go-ethereum/accounts/usbwallet/trezor"; + // Sugar for easier handling in Java option java_package = "com.satoshilabs.trezor.lib.protobuf"; option java_outer_classname = "TrezorMessageEthereum"; diff --git a/accounts/usbwallet/trezor/messages-management.pb.go b/accounts/usbwallet/trezor/messages-management.pb.go index 91bfca1e3f..983e2d281d 100644 --- a/accounts/usbwallet/trezor/messages-management.pb.go +++ b/accounts/usbwallet/trezor/messages-management.pb.go @@ -1,25 +1,28 @@ +// This file originates from the SatoshiLabs Trezor `common` repository at: +// https://github.com/trezor/trezor-common/blob/master/protob/messages-management.proto +// dated 28.05.2019, commit 893fd219d4a01bcffa0cd9cfa631856371ec5aa9. + // Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.34.2 +// protoc v5.27.1 // source: messages-management.proto package trezor import ( - fmt "fmt" - math "math" - - proto "github.com/golang/protobuf/proto" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" ) -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) // * // Structure representing passphrase source @@ -31,17 +34,19 @@ const ( ApplySettings_HOST ApplySettings_PassphraseSourceType = 2 ) -var ApplySettings_PassphraseSourceType_name = map[int32]string{ - 0: "ASK", - 1: "DEVICE", - 2: "HOST", -} - -var ApplySettings_PassphraseSourceType_value = map[string]int32{ - "ASK": 0, - "DEVICE": 1, - "HOST": 2, -} +// Enum value maps for ApplySettings_PassphraseSourceType. +var ( + ApplySettings_PassphraseSourceType_name = map[int32]string{ + 0: "ASK", + 1: "DEVICE", + 2: "HOST", + } + ApplySettings_PassphraseSourceType_value = map[string]int32{ + "ASK": 0, + "DEVICE": 1, + "HOST": 2, + } +) func (x ApplySettings_PassphraseSourceType) Enum() *ApplySettings_PassphraseSourceType { p := new(ApplySettings_PassphraseSourceType) @@ -50,20 +55,34 @@ func (x ApplySettings_PassphraseSourceType) Enum() *ApplySettings_PassphraseSour } func (x ApplySettings_PassphraseSourceType) String() string { - return proto.EnumName(ApplySettings_PassphraseSourceType_name, int32(x)) + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } -func (x *ApplySettings_PassphraseSourceType) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(ApplySettings_PassphraseSourceType_value, data, "ApplySettings_PassphraseSourceType") +func (ApplySettings_PassphraseSourceType) Descriptor() protoreflect.EnumDescriptor { + return file_messages_management_proto_enumTypes[0].Descriptor() +} + +func (ApplySettings_PassphraseSourceType) Type() protoreflect.EnumType { + return &file_messages_management_proto_enumTypes[0] +} + +func (x ApplySettings_PassphraseSourceType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Do not use. +func (x *ApplySettings_PassphraseSourceType) UnmarshalJSON(b []byte) error { + num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b) if err != nil { return err } - *x = ApplySettings_PassphraseSourceType(value) + *x = ApplySettings_PassphraseSourceType(num) return nil } +// Deprecated: Use ApplySettings_PassphraseSourceType.Descriptor instead. func (ApplySettings_PassphraseSourceType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_0c720c20d27aa029, []int{4, 0} + return file_messages_management_proto_rawDescGZIP(), []int{4, 0} } // * @@ -77,19 +96,21 @@ type RecoveryDevice_RecoveryDeviceType int32 const ( // use powers of two when extending this field - RecoveryDevice_RecoveryDeviceType_ScrambledWords RecoveryDevice_RecoveryDeviceType = 0 - RecoveryDevice_RecoveryDeviceType_Matrix RecoveryDevice_RecoveryDeviceType = 1 + RecoveryDevice_RecoveryDeviceType_ScrambledWords RecoveryDevice_RecoveryDeviceType = 0 // words in scrambled order + RecoveryDevice_RecoveryDeviceType_Matrix RecoveryDevice_RecoveryDeviceType = 1 // matrix recovery type ) -var RecoveryDevice_RecoveryDeviceType_name = map[int32]string{ - 0: "RecoveryDeviceType_ScrambledWords", - 1: "RecoveryDeviceType_Matrix", -} - -var RecoveryDevice_RecoveryDeviceType_value = map[string]int32{ - "RecoveryDeviceType_ScrambledWords": 0, - "RecoveryDeviceType_Matrix": 1, -} +// Enum value maps for RecoveryDevice_RecoveryDeviceType. +var ( + RecoveryDevice_RecoveryDeviceType_name = map[int32]string{ + 0: "RecoveryDeviceType_ScrambledWords", + 1: "RecoveryDeviceType_Matrix", + } + RecoveryDevice_RecoveryDeviceType_value = map[string]int32{ + "RecoveryDeviceType_ScrambledWords": 0, + "RecoveryDeviceType_Matrix": 1, + } +) func (x RecoveryDevice_RecoveryDeviceType) Enum() *RecoveryDevice_RecoveryDeviceType { p := new(RecoveryDevice_RecoveryDeviceType) @@ -98,20 +119,34 @@ func (x RecoveryDevice_RecoveryDeviceType) Enum() *RecoveryDevice_RecoveryDevice } func (x RecoveryDevice_RecoveryDeviceType) String() string { - return proto.EnumName(RecoveryDevice_RecoveryDeviceType_name, int32(x)) + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (RecoveryDevice_RecoveryDeviceType) Descriptor() protoreflect.EnumDescriptor { + return file_messages_management_proto_enumTypes[1].Descriptor() +} + +func (RecoveryDevice_RecoveryDeviceType) Type() protoreflect.EnumType { + return &file_messages_management_proto_enumTypes[1] } -func (x *RecoveryDevice_RecoveryDeviceType) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(RecoveryDevice_RecoveryDeviceType_value, data, "RecoveryDevice_RecoveryDeviceType") +func (x RecoveryDevice_RecoveryDeviceType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Do not use. +func (x *RecoveryDevice_RecoveryDeviceType) UnmarshalJSON(b []byte) error { + num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b) if err != nil { return err } - *x = RecoveryDevice_RecoveryDeviceType(value) + *x = RecoveryDevice_RecoveryDeviceType(num) return nil } +// Deprecated: Use RecoveryDevice_RecoveryDeviceType.Descriptor instead. func (RecoveryDevice_RecoveryDeviceType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_0c720c20d27aa029, []int{17, 0} + return file_messages_management_proto_rawDescGZIP(), []int{17, 0} } // * @@ -124,17 +159,19 @@ const ( WordRequest_WordRequestType_Matrix6 WordRequest_WordRequestType = 2 ) -var WordRequest_WordRequestType_name = map[int32]string{ - 0: "WordRequestType_Plain", - 1: "WordRequestType_Matrix9", - 2: "WordRequestType_Matrix6", -} - -var WordRequest_WordRequestType_value = map[string]int32{ - "WordRequestType_Plain": 0, - "WordRequestType_Matrix9": 1, - "WordRequestType_Matrix6": 2, -} +// Enum value maps for WordRequest_WordRequestType. +var ( + WordRequest_WordRequestType_name = map[int32]string{ + 0: "WordRequestType_Plain", + 1: "WordRequestType_Matrix9", + 2: "WordRequestType_Matrix6", + } + WordRequest_WordRequestType_value = map[string]int32{ + "WordRequestType_Plain": 0, + "WordRequestType_Matrix9": 1, + "WordRequestType_Matrix6": 2, + } +) func (x WordRequest_WordRequestType) Enum() *WordRequest_WordRequestType { p := new(WordRequest_WordRequestType) @@ -143,20 +180,34 @@ func (x WordRequest_WordRequestType) Enum() *WordRequest_WordRequestType { } func (x WordRequest_WordRequestType) String() string { - return proto.EnumName(WordRequest_WordRequestType_name, int32(x)) + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } -func (x *WordRequest_WordRequestType) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(WordRequest_WordRequestType_value, data, "WordRequest_WordRequestType") +func (WordRequest_WordRequestType) Descriptor() protoreflect.EnumDescriptor { + return file_messages_management_proto_enumTypes[2].Descriptor() +} + +func (WordRequest_WordRequestType) Type() protoreflect.EnumType { + return &file_messages_management_proto_enumTypes[2] +} + +func (x WordRequest_WordRequestType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Do not use. +func (x *WordRequest_WordRequestType) UnmarshalJSON(b []byte) error { + num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b) if err != nil { return err } - *x = WordRequest_WordRequestType(value) + *x = WordRequest_WordRequestType(num) return nil } +// Deprecated: Use WordRequest_WordRequestType.Descriptor instead. func (WordRequest_WordRequestType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_0c720c20d27aa029, []int{18, 0} + return file_messages_management_proto_rawDescGZIP(), []int{18, 0} } // * @@ -164,48 +215,56 @@ func (WordRequest_WordRequestType) EnumDescriptor() ([]byte, []int) { // @start // @next Features type Initialize struct { - State []byte `protobuf:"bytes,1,opt,name=state" json:"state,omitempty"` - SkipPassphrase *bool `protobuf:"varint,2,opt,name=skip_passphrase,json=skipPassphrase" json:"skip_passphrase,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *Initialize) Reset() { *m = Initialize{} } -func (m *Initialize) String() string { return proto.CompactTextString(m) } -func (*Initialize) ProtoMessage() {} -func (*Initialize) Descriptor() ([]byte, []int) { - return fileDescriptor_0c720c20d27aa029, []int{0} + State []byte `protobuf:"bytes,1,opt,name=state" json:"state,omitempty"` // assumed device state, clear session if set and different + SkipPassphrase *bool `protobuf:"varint,2,opt,name=skip_passphrase,json=skipPassphrase" json:"skip_passphrase,omitempty"` // this session should always assume empty passphrase } -func (m *Initialize) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Initialize.Unmarshal(m, b) -} -func (m *Initialize) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Initialize.Marshal(b, m, deterministic) -} -func (m *Initialize) XXX_Merge(src proto.Message) { - xxx_messageInfo_Initialize.Merge(m, src) +func (x *Initialize) Reset() { + *x = Initialize{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_management_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *Initialize) XXX_Size() int { - return xxx_messageInfo_Initialize.Size(m) + +func (x *Initialize) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *Initialize) XXX_DiscardUnknown() { - xxx_messageInfo_Initialize.DiscardUnknown(m) + +func (*Initialize) ProtoMessage() {} + +func (x *Initialize) ProtoReflect() protoreflect.Message { + mi := &file_messages_management_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_Initialize proto.InternalMessageInfo +// Deprecated: Use Initialize.ProtoReflect.Descriptor instead. +func (*Initialize) Descriptor() ([]byte, []int) { + return file_messages_management_proto_rawDescGZIP(), []int{0} +} -func (m *Initialize) GetState() []byte { - if m != nil { - return m.State +func (x *Initialize) GetState() []byte { + if x != nil { + return x.State } return nil } -func (m *Initialize) GetSkipPassphrase() bool { - if m != nil && m.SkipPassphrase != nil { - return *m.SkipPassphrase +func (x *Initialize) GetSkipPassphrase() bool { + if x != nil && x.SkipPassphrase != nil { + return *x.SkipPassphrase } return false } @@ -215,282 +274,297 @@ func (m *Initialize) GetSkipPassphrase() bool { // @start // @next Features type GetFeatures struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields } -func (m *GetFeatures) Reset() { *m = GetFeatures{} } -func (m *GetFeatures) String() string { return proto.CompactTextString(m) } -func (*GetFeatures) ProtoMessage() {} -func (*GetFeatures) Descriptor() ([]byte, []int) { - return fileDescriptor_0c720c20d27aa029, []int{1} +func (x *GetFeatures) Reset() { + *x = GetFeatures{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_management_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *GetFeatures) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_GetFeatures.Unmarshal(m, b) -} -func (m *GetFeatures) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_GetFeatures.Marshal(b, m, deterministic) -} -func (m *GetFeatures) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetFeatures.Merge(m, src) -} -func (m *GetFeatures) XXX_Size() int { - return xxx_messageInfo_GetFeatures.Size(m) +func (x *GetFeatures) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *GetFeatures) XXX_DiscardUnknown() { - xxx_messageInfo_GetFeatures.DiscardUnknown(m) + +func (*GetFeatures) ProtoMessage() {} + +func (x *GetFeatures) ProtoReflect() protoreflect.Message { + mi := &file_messages_management_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_GetFeatures proto.InternalMessageInfo +// Deprecated: Use GetFeatures.ProtoReflect.Descriptor instead. +func (*GetFeatures) Descriptor() ([]byte, []int) { + return file_messages_management_proto_rawDescGZIP(), []int{1} +} // * // Response: Reports various information about the device // @end type Features struct { - Vendor *string `protobuf:"bytes,1,opt,name=vendor" json:"vendor,omitempty"` - MajorVersion *uint32 `protobuf:"varint,2,opt,name=major_version,json=majorVersion" json:"major_version,omitempty"` - MinorVersion *uint32 `protobuf:"varint,3,opt,name=minor_version,json=minorVersion" json:"minor_version,omitempty"` - PatchVersion *uint32 `protobuf:"varint,4,opt,name=patch_version,json=patchVersion" json:"patch_version,omitempty"` - BootloaderMode *bool `protobuf:"varint,5,opt,name=bootloader_mode,json=bootloaderMode" json:"bootloader_mode,omitempty"` - DeviceId *string `protobuf:"bytes,6,opt,name=device_id,json=deviceId" json:"device_id,omitempty"` - PinProtection *bool `protobuf:"varint,7,opt,name=pin_protection,json=pinProtection" json:"pin_protection,omitempty"` - PassphraseProtection *bool `protobuf:"varint,8,opt,name=passphrase_protection,json=passphraseProtection" json:"passphrase_protection,omitempty"` - Language *string `protobuf:"bytes,9,opt,name=language" json:"language,omitempty"` - Label *string `protobuf:"bytes,10,opt,name=label" json:"label,omitempty"` - Initialized *bool `protobuf:"varint,12,opt,name=initialized" json:"initialized,omitempty"` - Revision []byte `protobuf:"bytes,13,opt,name=revision" json:"revision,omitempty"` - BootloaderHash []byte `protobuf:"bytes,14,opt,name=bootloader_hash,json=bootloaderHash" json:"bootloader_hash,omitempty"` - Imported *bool `protobuf:"varint,15,opt,name=imported" json:"imported,omitempty"` - PinCached *bool `protobuf:"varint,16,opt,name=pin_cached,json=pinCached" json:"pin_cached,omitempty"` - PassphraseCached *bool `protobuf:"varint,17,opt,name=passphrase_cached,json=passphraseCached" json:"passphrase_cached,omitempty"` - FirmwarePresent *bool `protobuf:"varint,18,opt,name=firmware_present,json=firmwarePresent" json:"firmware_present,omitempty"` - NeedsBackup *bool `protobuf:"varint,19,opt,name=needs_backup,json=needsBackup" json:"needs_backup,omitempty"` - Flags *uint32 `protobuf:"varint,20,opt,name=flags" json:"flags,omitempty"` - Model *string `protobuf:"bytes,21,opt,name=model" json:"model,omitempty"` - FwMajor *uint32 `protobuf:"varint,22,opt,name=fw_major,json=fwMajor" json:"fw_major,omitempty"` - FwMinor *uint32 `protobuf:"varint,23,opt,name=fw_minor,json=fwMinor" json:"fw_minor,omitempty"` - FwPatch *uint32 `protobuf:"varint,24,opt,name=fw_patch,json=fwPatch" json:"fw_patch,omitempty"` - FwVendor *string `protobuf:"bytes,25,opt,name=fw_vendor,json=fwVendor" json:"fw_vendor,omitempty"` - FwVendorKeys []byte `protobuf:"bytes,26,opt,name=fw_vendor_keys,json=fwVendorKeys" json:"fw_vendor_keys,omitempty"` - UnfinishedBackup *bool `protobuf:"varint,27,opt,name=unfinished_backup,json=unfinishedBackup" json:"unfinished_backup,omitempty"` - NoBackup *bool `protobuf:"varint,28,opt,name=no_backup,json=noBackup" json:"no_backup,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Features) Reset() { *m = Features{} } -func (m *Features) String() string { return proto.CompactTextString(m) } -func (*Features) ProtoMessage() {} + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Vendor *string `protobuf:"bytes,1,opt,name=vendor" json:"vendor,omitempty"` // name of the manufacturer, e.g. "trezor.io" + MajorVersion *uint32 `protobuf:"varint,2,opt,name=major_version,json=majorVersion" json:"major_version,omitempty"` // major version of the firmware/bootloader, e.g. 1 + MinorVersion *uint32 `protobuf:"varint,3,opt,name=minor_version,json=minorVersion" json:"minor_version,omitempty"` // minor version of the firmware/bootloader, e.g. 0 + PatchVersion *uint32 `protobuf:"varint,4,opt,name=patch_version,json=patchVersion" json:"patch_version,omitempty"` // patch version of the firmware/bootloader, e.g. 0 + BootloaderMode *bool `protobuf:"varint,5,opt,name=bootloader_mode,json=bootloaderMode" json:"bootloader_mode,omitempty"` // is device in bootloader mode? + DeviceId *string `protobuf:"bytes,6,opt,name=device_id,json=deviceId" json:"device_id,omitempty"` // device's unique identifier + PinProtection *bool `protobuf:"varint,7,opt,name=pin_protection,json=pinProtection" json:"pin_protection,omitempty"` // is device protected by PIN? + PassphraseProtection *bool `protobuf:"varint,8,opt,name=passphrase_protection,json=passphraseProtection" json:"passphrase_protection,omitempty"` // is node/mnemonic encrypted using passphrase? + Language *string `protobuf:"bytes,9,opt,name=language" json:"language,omitempty"` // device language + Label *string `protobuf:"bytes,10,opt,name=label" json:"label,omitempty"` // device description label + Initialized *bool `protobuf:"varint,12,opt,name=initialized" json:"initialized,omitempty"` // does device contain seed? + Revision []byte `protobuf:"bytes,13,opt,name=revision" json:"revision,omitempty"` // SCM revision of firmware + BootloaderHash []byte `protobuf:"bytes,14,opt,name=bootloader_hash,json=bootloaderHash" json:"bootloader_hash,omitempty"` // hash of the bootloader + Imported *bool `protobuf:"varint,15,opt,name=imported" json:"imported,omitempty"` // was storage imported from an external source? + PinCached *bool `protobuf:"varint,16,opt,name=pin_cached,json=pinCached" json:"pin_cached,omitempty"` // is PIN already cached in session? + PassphraseCached *bool `protobuf:"varint,17,opt,name=passphrase_cached,json=passphraseCached" json:"passphrase_cached,omitempty"` // is passphrase already cached in session? + FirmwarePresent *bool `protobuf:"varint,18,opt,name=firmware_present,json=firmwarePresent" json:"firmware_present,omitempty"` // is valid firmware loaded? + NeedsBackup *bool `protobuf:"varint,19,opt,name=needs_backup,json=needsBackup" json:"needs_backup,omitempty"` // does storage need backup? (equals to Storage.needs_backup) + Flags *uint32 `protobuf:"varint,20,opt,name=flags" json:"flags,omitempty"` // device flags (equals to Storage.flags) + Model *string `protobuf:"bytes,21,opt,name=model" json:"model,omitempty"` // device hardware model + FwMajor *uint32 `protobuf:"varint,22,opt,name=fw_major,json=fwMajor" json:"fw_major,omitempty"` // reported firmware version if in bootloader mode + FwMinor *uint32 `protobuf:"varint,23,opt,name=fw_minor,json=fwMinor" json:"fw_minor,omitempty"` // reported firmware version if in bootloader mode + FwPatch *uint32 `protobuf:"varint,24,opt,name=fw_patch,json=fwPatch" json:"fw_patch,omitempty"` // reported firmware version if in bootloader mode + FwVendor *string `protobuf:"bytes,25,opt,name=fw_vendor,json=fwVendor" json:"fw_vendor,omitempty"` // reported firmware vendor if in bootloader mode + FwVendorKeys []byte `protobuf:"bytes,26,opt,name=fw_vendor_keys,json=fwVendorKeys" json:"fw_vendor_keys,omitempty"` // reported firmware vendor keys (their hash) + UnfinishedBackup *bool `protobuf:"varint,27,opt,name=unfinished_backup,json=unfinishedBackup" json:"unfinished_backup,omitempty"` // report unfinished backup (equals to Storage.unfinished_backup) + NoBackup *bool `protobuf:"varint,28,opt,name=no_backup,json=noBackup" json:"no_backup,omitempty"` // report no backup (equals to Storage.no_backup) +} + +func (x *Features) Reset() { + *x = Features{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_management_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Features) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Features) ProtoMessage() {} + +func (x *Features) ProtoReflect() protoreflect.Message { + mi := &file_messages_management_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Features.ProtoReflect.Descriptor instead. func (*Features) Descriptor() ([]byte, []int) { - return fileDescriptor_0c720c20d27aa029, []int{2} -} - -func (m *Features) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Features.Unmarshal(m, b) -} -func (m *Features) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Features.Marshal(b, m, deterministic) + return file_messages_management_proto_rawDescGZIP(), []int{2} } -func (m *Features) XXX_Merge(src proto.Message) { - xxx_messageInfo_Features.Merge(m, src) -} -func (m *Features) XXX_Size() int { - return xxx_messageInfo_Features.Size(m) -} -func (m *Features) XXX_DiscardUnknown() { - xxx_messageInfo_Features.DiscardUnknown(m) -} - -var xxx_messageInfo_Features proto.InternalMessageInfo -func (m *Features) GetVendor() string { - if m != nil && m.Vendor != nil { - return *m.Vendor +func (x *Features) GetVendor() string { + if x != nil && x.Vendor != nil { + return *x.Vendor } return "" } -func (m *Features) GetMajorVersion() uint32 { - if m != nil && m.MajorVersion != nil { - return *m.MajorVersion +func (x *Features) GetMajorVersion() uint32 { + if x != nil && x.MajorVersion != nil { + return *x.MajorVersion } return 0 } -func (m *Features) GetMinorVersion() uint32 { - if m != nil && m.MinorVersion != nil { - return *m.MinorVersion +func (x *Features) GetMinorVersion() uint32 { + if x != nil && x.MinorVersion != nil { + return *x.MinorVersion } return 0 } -func (m *Features) GetPatchVersion() uint32 { - if m != nil && m.PatchVersion != nil { - return *m.PatchVersion +func (x *Features) GetPatchVersion() uint32 { + if x != nil && x.PatchVersion != nil { + return *x.PatchVersion } return 0 } -func (m *Features) GetBootloaderMode() bool { - if m != nil && m.BootloaderMode != nil { - return *m.BootloaderMode +func (x *Features) GetBootloaderMode() bool { + if x != nil && x.BootloaderMode != nil { + return *x.BootloaderMode } return false } -func (m *Features) GetDeviceId() string { - if m != nil && m.DeviceId != nil { - return *m.DeviceId +func (x *Features) GetDeviceId() string { + if x != nil && x.DeviceId != nil { + return *x.DeviceId } return "" } -func (m *Features) GetPinProtection() bool { - if m != nil && m.PinProtection != nil { - return *m.PinProtection +func (x *Features) GetPinProtection() bool { + if x != nil && x.PinProtection != nil { + return *x.PinProtection } return false } -func (m *Features) GetPassphraseProtection() bool { - if m != nil && m.PassphraseProtection != nil { - return *m.PassphraseProtection +func (x *Features) GetPassphraseProtection() bool { + if x != nil && x.PassphraseProtection != nil { + return *x.PassphraseProtection } return false } -func (m *Features) GetLanguage() string { - if m != nil && m.Language != nil { - return *m.Language +func (x *Features) GetLanguage() string { + if x != nil && x.Language != nil { + return *x.Language } return "" } -func (m *Features) GetLabel() string { - if m != nil && m.Label != nil { - return *m.Label +func (x *Features) GetLabel() string { + if x != nil && x.Label != nil { + return *x.Label } return "" } -func (m *Features) GetInitialized() bool { - if m != nil && m.Initialized != nil { - return *m.Initialized +func (x *Features) GetInitialized() bool { + if x != nil && x.Initialized != nil { + return *x.Initialized } return false } -func (m *Features) GetRevision() []byte { - if m != nil { - return m.Revision +func (x *Features) GetRevision() []byte { + if x != nil { + return x.Revision } return nil } -func (m *Features) GetBootloaderHash() []byte { - if m != nil { - return m.BootloaderHash +func (x *Features) GetBootloaderHash() []byte { + if x != nil { + return x.BootloaderHash } return nil } -func (m *Features) GetImported() bool { - if m != nil && m.Imported != nil { - return *m.Imported +func (x *Features) GetImported() bool { + if x != nil && x.Imported != nil { + return *x.Imported } return false } -func (m *Features) GetPinCached() bool { - if m != nil && m.PinCached != nil { - return *m.PinCached +func (x *Features) GetPinCached() bool { + if x != nil && x.PinCached != nil { + return *x.PinCached } return false } -func (m *Features) GetPassphraseCached() bool { - if m != nil && m.PassphraseCached != nil { - return *m.PassphraseCached +func (x *Features) GetPassphraseCached() bool { + if x != nil && x.PassphraseCached != nil { + return *x.PassphraseCached } return false } -func (m *Features) GetFirmwarePresent() bool { - if m != nil && m.FirmwarePresent != nil { - return *m.FirmwarePresent +func (x *Features) GetFirmwarePresent() bool { + if x != nil && x.FirmwarePresent != nil { + return *x.FirmwarePresent } return false } -func (m *Features) GetNeedsBackup() bool { - if m != nil && m.NeedsBackup != nil { - return *m.NeedsBackup +func (x *Features) GetNeedsBackup() bool { + if x != nil && x.NeedsBackup != nil { + return *x.NeedsBackup } return false } -func (m *Features) GetFlags() uint32 { - if m != nil && m.Flags != nil { - return *m.Flags +func (x *Features) GetFlags() uint32 { + if x != nil && x.Flags != nil { + return *x.Flags } return 0 } -func (m *Features) GetModel() string { - if m != nil && m.Model != nil { - return *m.Model +func (x *Features) GetModel() string { + if x != nil && x.Model != nil { + return *x.Model } return "" } -func (m *Features) GetFwMajor() uint32 { - if m != nil && m.FwMajor != nil { - return *m.FwMajor +func (x *Features) GetFwMajor() uint32 { + if x != nil && x.FwMajor != nil { + return *x.FwMajor } return 0 } -func (m *Features) GetFwMinor() uint32 { - if m != nil && m.FwMinor != nil { - return *m.FwMinor +func (x *Features) GetFwMinor() uint32 { + if x != nil && x.FwMinor != nil { + return *x.FwMinor } return 0 } -func (m *Features) GetFwPatch() uint32 { - if m != nil && m.FwPatch != nil { - return *m.FwPatch +func (x *Features) GetFwPatch() uint32 { + if x != nil && x.FwPatch != nil { + return *x.FwPatch } return 0 } -func (m *Features) GetFwVendor() string { - if m != nil && m.FwVendor != nil { - return *m.FwVendor +func (x *Features) GetFwVendor() string { + if x != nil && x.FwVendor != nil { + return *x.FwVendor } return "" } -func (m *Features) GetFwVendorKeys() []byte { - if m != nil { - return m.FwVendorKeys +func (x *Features) GetFwVendorKeys() []byte { + if x != nil { + return x.FwVendorKeys } return nil } -func (m *Features) GetUnfinishedBackup() bool { - if m != nil && m.UnfinishedBackup != nil { - return *m.UnfinishedBackup +func (x *Features) GetUnfinishedBackup() bool { + if x != nil && x.UnfinishedBackup != nil { + return *x.UnfinishedBackup } return false } -func (m *Features) GetNoBackup() bool { - if m != nil && m.NoBackup != nil { - return *m.NoBackup +func (x *Features) GetNoBackup() bool { + if x != nil && x.NoBackup != nil { + return *x.NoBackup } return false } @@ -500,35 +574,42 @@ func (m *Features) GetNoBackup() bool { // @start // @next Success type ClearSession struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields } -func (m *ClearSession) Reset() { *m = ClearSession{} } -func (m *ClearSession) String() string { return proto.CompactTextString(m) } -func (*ClearSession) ProtoMessage() {} -func (*ClearSession) Descriptor() ([]byte, []int) { - return fileDescriptor_0c720c20d27aa029, []int{3} +func (x *ClearSession) Reset() { + *x = ClearSession{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_management_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *ClearSession) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ClearSession.Unmarshal(m, b) -} -func (m *ClearSession) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ClearSession.Marshal(b, m, deterministic) -} -func (m *ClearSession) XXX_Merge(src proto.Message) { - xxx_messageInfo_ClearSession.Merge(m, src) -} -func (m *ClearSession) XXX_Size() int { - return xxx_messageInfo_ClearSession.Size(m) +func (x *ClearSession) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *ClearSession) XXX_DiscardUnknown() { - xxx_messageInfo_ClearSession.DiscardUnknown(m) + +func (*ClearSession) ProtoMessage() {} + +func (x *ClearSession) ProtoReflect() protoreflect.Message { + mi := &file_messages_management_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_ClearSession proto.InternalMessageInfo +// Deprecated: Use ClearSession.ProtoReflect.Descriptor instead. +func (*ClearSession) Descriptor() ([]byte, []int) { + return file_messages_management_proto_rawDescGZIP(), []int{3} +} // * // Request: change language and/or label of the device @@ -536,88 +617,96 @@ var xxx_messageInfo_ClearSession proto.InternalMessageInfo // @next Success // @next Failure type ApplySettings struct { - Language *string `protobuf:"bytes,1,opt,name=language" json:"language,omitempty"` - Label *string `protobuf:"bytes,2,opt,name=label" json:"label,omitempty"` - UsePassphrase *bool `protobuf:"varint,3,opt,name=use_passphrase,json=usePassphrase" json:"use_passphrase,omitempty"` - Homescreen []byte `protobuf:"bytes,4,opt,name=homescreen" json:"homescreen,omitempty"` - PassphraseSource *ApplySettings_PassphraseSourceType `protobuf:"varint,5,opt,name=passphrase_source,json=passphraseSource,enum=hw.trezor.messages.management.ApplySettings_PassphraseSourceType" json:"passphrase_source,omitempty"` - AutoLockDelayMs *uint32 `protobuf:"varint,6,opt,name=auto_lock_delay_ms,json=autoLockDelayMs" json:"auto_lock_delay_ms,omitempty"` - DisplayRotation *uint32 `protobuf:"varint,7,opt,name=display_rotation,json=displayRotation" json:"display_rotation,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ApplySettings) Reset() { *m = ApplySettings{} } -func (m *ApplySettings) String() string { return proto.CompactTextString(m) } -func (*ApplySettings) ProtoMessage() {} -func (*ApplySettings) Descriptor() ([]byte, []int) { - return fileDescriptor_0c720c20d27aa029, []int{4} -} + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *ApplySettings) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ApplySettings.Unmarshal(m, b) -} -func (m *ApplySettings) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ApplySettings.Marshal(b, m, deterministic) + Language *string `protobuf:"bytes,1,opt,name=language" json:"language,omitempty"` + Label *string `protobuf:"bytes,2,opt,name=label" json:"label,omitempty"` + UsePassphrase *bool `protobuf:"varint,3,opt,name=use_passphrase,json=usePassphrase" json:"use_passphrase,omitempty"` + Homescreen []byte `protobuf:"bytes,4,opt,name=homescreen" json:"homescreen,omitempty"` + PassphraseSource *ApplySettings_PassphraseSourceType `protobuf:"varint,5,opt,name=passphrase_source,json=passphraseSource,enum=hw.trezor.messages.management.ApplySettings_PassphraseSourceType" json:"passphrase_source,omitempty"` + AutoLockDelayMs *uint32 `protobuf:"varint,6,opt,name=auto_lock_delay_ms,json=autoLockDelayMs" json:"auto_lock_delay_ms,omitempty"` + DisplayRotation *uint32 `protobuf:"varint,7,opt,name=display_rotation,json=displayRotation" json:"display_rotation,omitempty"` // in degrees from North } -func (m *ApplySettings) XXX_Merge(src proto.Message) { - xxx_messageInfo_ApplySettings.Merge(m, src) + +func (x *ApplySettings) Reset() { + *x = ApplySettings{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_management_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *ApplySettings) XXX_Size() int { - return xxx_messageInfo_ApplySettings.Size(m) + +func (x *ApplySettings) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *ApplySettings) XXX_DiscardUnknown() { - xxx_messageInfo_ApplySettings.DiscardUnknown(m) + +func (*ApplySettings) ProtoMessage() {} + +func (x *ApplySettings) ProtoReflect() protoreflect.Message { + mi := &file_messages_management_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_ApplySettings proto.InternalMessageInfo +// Deprecated: Use ApplySettings.ProtoReflect.Descriptor instead. +func (*ApplySettings) Descriptor() ([]byte, []int) { + return file_messages_management_proto_rawDescGZIP(), []int{4} +} -func (m *ApplySettings) GetLanguage() string { - if m != nil && m.Language != nil { - return *m.Language +func (x *ApplySettings) GetLanguage() string { + if x != nil && x.Language != nil { + return *x.Language } return "" } -func (m *ApplySettings) GetLabel() string { - if m != nil && m.Label != nil { - return *m.Label +func (x *ApplySettings) GetLabel() string { + if x != nil && x.Label != nil { + return *x.Label } return "" } -func (m *ApplySettings) GetUsePassphrase() bool { - if m != nil && m.UsePassphrase != nil { - return *m.UsePassphrase +func (x *ApplySettings) GetUsePassphrase() bool { + if x != nil && x.UsePassphrase != nil { + return *x.UsePassphrase } return false } -func (m *ApplySettings) GetHomescreen() []byte { - if m != nil { - return m.Homescreen +func (x *ApplySettings) GetHomescreen() []byte { + if x != nil { + return x.Homescreen } return nil } -func (m *ApplySettings) GetPassphraseSource() ApplySettings_PassphraseSourceType { - if m != nil && m.PassphraseSource != nil { - return *m.PassphraseSource +func (x *ApplySettings) GetPassphraseSource() ApplySettings_PassphraseSourceType { + if x != nil && x.PassphraseSource != nil { + return *x.PassphraseSource } return ApplySettings_ASK } -func (m *ApplySettings) GetAutoLockDelayMs() uint32 { - if m != nil && m.AutoLockDelayMs != nil { - return *m.AutoLockDelayMs +func (x *ApplySettings) GetAutoLockDelayMs() uint32 { + if x != nil && x.AutoLockDelayMs != nil { + return *x.AutoLockDelayMs } return 0 } -func (m *ApplySettings) GetDisplayRotation() uint32 { - if m != nil && m.DisplayRotation != nil { - return *m.DisplayRotation +func (x *ApplySettings) GetDisplayRotation() uint32 { + if x != nil && x.DisplayRotation != nil { + return *x.DisplayRotation } return 0 } @@ -628,40 +717,48 @@ func (m *ApplySettings) GetDisplayRotation() uint32 { // @next Success // @next Failure type ApplyFlags struct { - Flags *uint32 `protobuf:"varint,1,opt,name=flags" json:"flags,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *ApplyFlags) Reset() { *m = ApplyFlags{} } -func (m *ApplyFlags) String() string { return proto.CompactTextString(m) } -func (*ApplyFlags) ProtoMessage() {} -func (*ApplyFlags) Descriptor() ([]byte, []int) { - return fileDescriptor_0c720c20d27aa029, []int{5} + Flags *uint32 `protobuf:"varint,1,opt,name=flags" json:"flags,omitempty"` // bitmask, can only set bits, not unset } -func (m *ApplyFlags) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ApplyFlags.Unmarshal(m, b) -} -func (m *ApplyFlags) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ApplyFlags.Marshal(b, m, deterministic) -} -func (m *ApplyFlags) XXX_Merge(src proto.Message) { - xxx_messageInfo_ApplyFlags.Merge(m, src) +func (x *ApplyFlags) Reset() { + *x = ApplyFlags{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_management_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *ApplyFlags) XXX_Size() int { - return xxx_messageInfo_ApplyFlags.Size(m) + +func (x *ApplyFlags) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *ApplyFlags) XXX_DiscardUnknown() { - xxx_messageInfo_ApplyFlags.DiscardUnknown(m) + +func (*ApplyFlags) ProtoMessage() {} + +func (x *ApplyFlags) ProtoReflect() protoreflect.Message { + mi := &file_messages_management_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_ApplyFlags proto.InternalMessageInfo +// Deprecated: Use ApplyFlags.ProtoReflect.Descriptor instead. +func (*ApplyFlags) Descriptor() ([]byte, []int) { + return file_messages_management_proto_rawDescGZIP(), []int{5} +} -func (m *ApplyFlags) GetFlags() uint32 { - if m != nil && m.Flags != nil { - return *m.Flags +func (x *ApplyFlags) GetFlags() uint32 { + if x != nil && x.Flags != nil { + return *x.Flags } return 0 } @@ -672,40 +769,48 @@ func (m *ApplyFlags) GetFlags() uint32 { // @next Success // @next Failure type ChangePin struct { - Remove *bool `protobuf:"varint,1,opt,name=remove" json:"remove,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *ChangePin) Reset() { *m = ChangePin{} } -func (m *ChangePin) String() string { return proto.CompactTextString(m) } -func (*ChangePin) ProtoMessage() {} -func (*ChangePin) Descriptor() ([]byte, []int) { - return fileDescriptor_0c720c20d27aa029, []int{6} + Remove *bool `protobuf:"varint,1,opt,name=remove" json:"remove,omitempty"` // is PIN removal requested? } -func (m *ChangePin) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ChangePin.Unmarshal(m, b) -} -func (m *ChangePin) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ChangePin.Marshal(b, m, deterministic) -} -func (m *ChangePin) XXX_Merge(src proto.Message) { - xxx_messageInfo_ChangePin.Merge(m, src) +func (x *ChangePin) Reset() { + *x = ChangePin{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_management_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *ChangePin) XXX_Size() int { - return xxx_messageInfo_ChangePin.Size(m) + +func (x *ChangePin) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *ChangePin) XXX_DiscardUnknown() { - xxx_messageInfo_ChangePin.DiscardUnknown(m) + +func (*ChangePin) ProtoMessage() {} + +func (x *ChangePin) ProtoReflect() protoreflect.Message { + mi := &file_messages_management_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_ChangePin proto.InternalMessageInfo +// Deprecated: Use ChangePin.ProtoReflect.Descriptor instead. +func (*ChangePin) Descriptor() ([]byte, []int) { + return file_messages_management_proto_rawDescGZIP(), []int{6} +} -func (m *ChangePin) GetRemove() bool { - if m != nil && m.Remove != nil { - return *m.Remove +func (x *ChangePin) GetRemove() bool { + if x != nil && x.Remove != nil { + return *x.Remove } return false } @@ -715,64 +820,72 @@ func (m *ChangePin) GetRemove() bool { // @start // @next Success type Ping struct { - Message *string `protobuf:"bytes,1,opt,name=message" json:"message,omitempty"` - ButtonProtection *bool `protobuf:"varint,2,opt,name=button_protection,json=buttonProtection" json:"button_protection,omitempty"` - PinProtection *bool `protobuf:"varint,3,opt,name=pin_protection,json=pinProtection" json:"pin_protection,omitempty"` - PassphraseProtection *bool `protobuf:"varint,4,opt,name=passphrase_protection,json=passphraseProtection" json:"passphrase_protection,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Ping) Reset() { *m = Ping{} } -func (m *Ping) String() string { return proto.CompactTextString(m) } -func (*Ping) ProtoMessage() {} -func (*Ping) Descriptor() ([]byte, []int) { - return fileDescriptor_0c720c20d27aa029, []int{7} -} + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *Ping) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Ping.Unmarshal(m, b) + Message *string `protobuf:"bytes,1,opt,name=message" json:"message,omitempty"` // message to send back in Success message + ButtonProtection *bool `protobuf:"varint,2,opt,name=button_protection,json=buttonProtection" json:"button_protection,omitempty"` // ask for button press + PinProtection *bool `protobuf:"varint,3,opt,name=pin_protection,json=pinProtection" json:"pin_protection,omitempty"` // ask for PIN if set in device + PassphraseProtection *bool `protobuf:"varint,4,opt,name=passphrase_protection,json=passphraseProtection" json:"passphrase_protection,omitempty"` // ask for passphrase if set in device } -func (m *Ping) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Ping.Marshal(b, m, deterministic) -} -func (m *Ping) XXX_Merge(src proto.Message) { - xxx_messageInfo_Ping.Merge(m, src) + +func (x *Ping) Reset() { + *x = Ping{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_management_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *Ping) XXX_Size() int { - return xxx_messageInfo_Ping.Size(m) + +func (x *Ping) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *Ping) XXX_DiscardUnknown() { - xxx_messageInfo_Ping.DiscardUnknown(m) + +func (*Ping) ProtoMessage() {} + +func (x *Ping) ProtoReflect() protoreflect.Message { + mi := &file_messages_management_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_Ping proto.InternalMessageInfo +// Deprecated: Use Ping.ProtoReflect.Descriptor instead. +func (*Ping) Descriptor() ([]byte, []int) { + return file_messages_management_proto_rawDescGZIP(), []int{7} +} -func (m *Ping) GetMessage() string { - if m != nil && m.Message != nil { - return *m.Message +func (x *Ping) GetMessage() string { + if x != nil && x.Message != nil { + return *x.Message } return "" } -func (m *Ping) GetButtonProtection() bool { - if m != nil && m.ButtonProtection != nil { - return *m.ButtonProtection +func (x *Ping) GetButtonProtection() bool { + if x != nil && x.ButtonProtection != nil { + return *x.ButtonProtection } return false } -func (m *Ping) GetPinProtection() bool { - if m != nil && m.PinProtection != nil { - return *m.PinProtection +func (x *Ping) GetPinProtection() bool { + if x != nil && x.PinProtection != nil { + return *x.PinProtection } return false } -func (m *Ping) GetPassphraseProtection() bool { - if m != nil && m.PassphraseProtection != nil { - return *m.PassphraseProtection +func (x *Ping) GetPassphraseProtection() bool { + if x != nil && x.PassphraseProtection != nil { + return *x.PassphraseProtection } return false } @@ -782,35 +895,42 @@ func (m *Ping) GetPassphraseProtection() bool { // @start // @next Failure type Cancel struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields } -func (m *Cancel) Reset() { *m = Cancel{} } -func (m *Cancel) String() string { return proto.CompactTextString(m) } -func (*Cancel) ProtoMessage() {} -func (*Cancel) Descriptor() ([]byte, []int) { - return fileDescriptor_0c720c20d27aa029, []int{8} +func (x *Cancel) Reset() { + *x = Cancel{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_management_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *Cancel) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Cancel.Unmarshal(m, b) +func (x *Cancel) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *Cancel) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Cancel.Marshal(b, m, deterministic) -} -func (m *Cancel) XXX_Merge(src proto.Message) { - xxx_messageInfo_Cancel.Merge(m, src) -} -func (m *Cancel) XXX_Size() int { - return xxx_messageInfo_Cancel.Size(m) -} -func (m *Cancel) XXX_DiscardUnknown() { - xxx_messageInfo_Cancel.DiscardUnknown(m) + +func (*Cancel) ProtoMessage() {} + +func (x *Cancel) ProtoReflect() protoreflect.Message { + mi := &file_messages_management_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_Cancel proto.InternalMessageInfo +// Deprecated: Use Cancel.ProtoReflect.Descriptor instead. +func (*Cancel) Descriptor() ([]byte, []int) { + return file_messages_management_proto_rawDescGZIP(), []int{8} +} // * // Request: Request a sample of random data generated by hardware RNG. May be used for testing. @@ -818,40 +938,48 @@ var xxx_messageInfo_Cancel proto.InternalMessageInfo // @next Entropy // @next Failure type GetEntropy struct { - Size *uint32 `protobuf:"varint,1,req,name=size" json:"size,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *GetEntropy) Reset() { *m = GetEntropy{} } -func (m *GetEntropy) String() string { return proto.CompactTextString(m) } -func (*GetEntropy) ProtoMessage() {} -func (*GetEntropy) Descriptor() ([]byte, []int) { - return fileDescriptor_0c720c20d27aa029, []int{9} + Size *uint32 `protobuf:"varint,1,req,name=size" json:"size,omitempty"` // size of requested entropy } -func (m *GetEntropy) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_GetEntropy.Unmarshal(m, b) -} -func (m *GetEntropy) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_GetEntropy.Marshal(b, m, deterministic) -} -func (m *GetEntropy) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetEntropy.Merge(m, src) +func (x *GetEntropy) Reset() { + *x = GetEntropy{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_management_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *GetEntropy) XXX_Size() int { - return xxx_messageInfo_GetEntropy.Size(m) + +func (x *GetEntropy) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *GetEntropy) XXX_DiscardUnknown() { - xxx_messageInfo_GetEntropy.DiscardUnknown(m) + +func (*GetEntropy) ProtoMessage() {} + +func (x *GetEntropy) ProtoReflect() protoreflect.Message { + mi := &file_messages_management_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_GetEntropy proto.InternalMessageInfo +// Deprecated: Use GetEntropy.ProtoReflect.Descriptor instead. +func (*GetEntropy) Descriptor() ([]byte, []int) { + return file_messages_management_proto_rawDescGZIP(), []int{9} +} -func (m *GetEntropy) GetSize() uint32 { - if m != nil && m.Size != nil { - return *m.Size +func (x *GetEntropy) GetSize() uint32 { + if x != nil && x.Size != nil { + return *x.Size } return 0 } @@ -860,40 +988,48 @@ func (m *GetEntropy) GetSize() uint32 { // Response: Reply with random data generated by internal RNG // @end type Entropy struct { - Entropy []byte `protobuf:"bytes,1,req,name=entropy" json:"entropy,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *Entropy) Reset() { *m = Entropy{} } -func (m *Entropy) String() string { return proto.CompactTextString(m) } -func (*Entropy) ProtoMessage() {} -func (*Entropy) Descriptor() ([]byte, []int) { - return fileDescriptor_0c720c20d27aa029, []int{10} + Entropy []byte `protobuf:"bytes,1,req,name=entropy" json:"entropy,omitempty"` // chunk of random generated bytes } -func (m *Entropy) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Entropy.Unmarshal(m, b) -} -func (m *Entropy) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Entropy.Marshal(b, m, deterministic) -} -func (m *Entropy) XXX_Merge(src proto.Message) { - xxx_messageInfo_Entropy.Merge(m, src) +func (x *Entropy) Reset() { + *x = Entropy{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_management_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *Entropy) XXX_Size() int { - return xxx_messageInfo_Entropy.Size(m) + +func (x *Entropy) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *Entropy) XXX_DiscardUnknown() { - xxx_messageInfo_Entropy.DiscardUnknown(m) + +func (*Entropy) ProtoMessage() {} + +func (x *Entropy) ProtoReflect() protoreflect.Message { + mi := &file_messages_management_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_Entropy proto.InternalMessageInfo +// Deprecated: Use Entropy.ProtoReflect.Descriptor instead. +func (*Entropy) Descriptor() ([]byte, []int) { + return file_messages_management_proto_rawDescGZIP(), []int{10} +} -func (m *Entropy) GetEntropy() []byte { - if m != nil { - return m.Entropy +func (x *Entropy) GetEntropy() []byte { + if x != nil { + return x.Entropy } return nil } @@ -904,35 +1040,42 @@ func (m *Entropy) GetEntropy() []byte { // @next Success // @next Failure type WipeDevice struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields } -func (m *WipeDevice) Reset() { *m = WipeDevice{} } -func (m *WipeDevice) String() string { return proto.CompactTextString(m) } -func (*WipeDevice) ProtoMessage() {} -func (*WipeDevice) Descriptor() ([]byte, []int) { - return fileDescriptor_0c720c20d27aa029, []int{11} +func (x *WipeDevice) Reset() { + *x = WipeDevice{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_management_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *WipeDevice) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_WipeDevice.Unmarshal(m, b) -} -func (m *WipeDevice) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_WipeDevice.Marshal(b, m, deterministic) -} -func (m *WipeDevice) XXX_Merge(src proto.Message) { - xxx_messageInfo_WipeDevice.Merge(m, src) -} -func (m *WipeDevice) XXX_Size() int { - return xxx_messageInfo_WipeDevice.Size(m) +func (x *WipeDevice) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *WipeDevice) XXX_DiscardUnknown() { - xxx_messageInfo_WipeDevice.DiscardUnknown(m) + +func (*WipeDevice) ProtoMessage() {} + +func (x *WipeDevice) ProtoReflect() protoreflect.Message { + mi := &file_messages_management_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_WipeDevice proto.InternalMessageInfo +// Deprecated: Use WipeDevice.ProtoReflect.Descriptor instead. +func (*WipeDevice) Descriptor() ([]byte, []int) { + return file_messages_management_proto_rawDescGZIP(), []int{11} +} // * // Request: Load seed and related internal settings from the computer @@ -940,98 +1083,109 @@ var xxx_messageInfo_WipeDevice proto.InternalMessageInfo // @next Success // @next Failure type LoadDevice struct { - Mnemonic *string `protobuf:"bytes,1,opt,name=mnemonic" json:"mnemonic,omitempty"` - Node *HDNodeType `protobuf:"bytes,2,opt,name=node" json:"node,omitempty"` - Pin *string `protobuf:"bytes,3,opt,name=pin" json:"pin,omitempty"` - PassphraseProtection *bool `protobuf:"varint,4,opt,name=passphrase_protection,json=passphraseProtection" json:"passphrase_protection,omitempty"` - Language *string `protobuf:"bytes,5,opt,name=language,def=english" json:"language,omitempty"` - Label *string `protobuf:"bytes,6,opt,name=label" json:"label,omitempty"` - SkipChecksum *bool `protobuf:"varint,7,opt,name=skip_checksum,json=skipChecksum" json:"skip_checksum,omitempty"` - U2FCounter *uint32 `protobuf:"varint,8,opt,name=u2f_counter,json=u2fCounter" json:"u2f_counter,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *LoadDevice) Reset() { *m = LoadDevice{} } -func (m *LoadDevice) String() string { return proto.CompactTextString(m) } -func (*LoadDevice) ProtoMessage() {} -func (*LoadDevice) Descriptor() ([]byte, []int) { - return fileDescriptor_0c720c20d27aa029, []int{12} -} + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *LoadDevice) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_LoadDevice.Unmarshal(m, b) -} -func (m *LoadDevice) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_LoadDevice.Marshal(b, m, deterministic) + Mnemonic *string `protobuf:"bytes,1,opt,name=mnemonic" json:"mnemonic,omitempty"` // seed encoded as BIP-39 mnemonic (12, 18 or 24 words) + Node *HDNodeType `protobuf:"bytes,2,opt,name=node" json:"node,omitempty"` // BIP-32 node + Pin *string `protobuf:"bytes,3,opt,name=pin" json:"pin,omitempty"` // set PIN protection + PassphraseProtection *bool `protobuf:"varint,4,opt,name=passphrase_protection,json=passphraseProtection" json:"passphrase_protection,omitempty"` // enable master node encryption using passphrase + Language *string `protobuf:"bytes,5,opt,name=language,def=english" json:"language,omitempty"` // device language + Label *string `protobuf:"bytes,6,opt,name=label" json:"label,omitempty"` // device label + SkipChecksum *bool `protobuf:"varint,7,opt,name=skip_checksum,json=skipChecksum" json:"skip_checksum,omitempty"` // do not test mnemonic for valid BIP-39 checksum + U2FCounter *uint32 `protobuf:"varint,8,opt,name=u2f_counter,json=u2fCounter" json:"u2f_counter,omitempty"` // U2F counter } -func (m *LoadDevice) XXX_Merge(src proto.Message) { - xxx_messageInfo_LoadDevice.Merge(m, src) -} -func (m *LoadDevice) XXX_Size() int { - return xxx_messageInfo_LoadDevice.Size(m) + +// Default values for LoadDevice fields. +const ( + Default_LoadDevice_Language = string("english") +) + +func (x *LoadDevice) Reset() { + *x = LoadDevice{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_management_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *LoadDevice) XXX_DiscardUnknown() { - xxx_messageInfo_LoadDevice.DiscardUnknown(m) + +func (x *LoadDevice) String() string { + return protoimpl.X.MessageStringOf(x) } -var xxx_messageInfo_LoadDevice proto.InternalMessageInfo +func (*LoadDevice) ProtoMessage() {} -const Default_LoadDevice_Language string = "english" +func (x *LoadDevice) ProtoReflect() protoreflect.Message { + mi := &file_messages_management_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} -func (m *LoadDevice) GetMnemonic() string { - if m != nil && m.Mnemonic != nil { - return *m.Mnemonic +// Deprecated: Use LoadDevice.ProtoReflect.Descriptor instead. +func (*LoadDevice) Descriptor() ([]byte, []int) { + return file_messages_management_proto_rawDescGZIP(), []int{12} +} + +func (x *LoadDevice) GetMnemonic() string { + if x != nil && x.Mnemonic != nil { + return *x.Mnemonic } return "" } -func (m *LoadDevice) GetNode() *HDNodeType { - if m != nil { - return m.Node +func (x *LoadDevice) GetNode() *HDNodeType { + if x != nil { + return x.Node } return nil } -func (m *LoadDevice) GetPin() string { - if m != nil && m.Pin != nil { - return *m.Pin +func (x *LoadDevice) GetPin() string { + if x != nil && x.Pin != nil { + return *x.Pin } return "" } -func (m *LoadDevice) GetPassphraseProtection() bool { - if m != nil && m.PassphraseProtection != nil { - return *m.PassphraseProtection +func (x *LoadDevice) GetPassphraseProtection() bool { + if x != nil && x.PassphraseProtection != nil { + return *x.PassphraseProtection } return false } -func (m *LoadDevice) GetLanguage() string { - if m != nil && m.Language != nil { - return *m.Language +func (x *LoadDevice) GetLanguage() string { + if x != nil && x.Language != nil { + return *x.Language } return Default_LoadDevice_Language } -func (m *LoadDevice) GetLabel() string { - if m != nil && m.Label != nil { - return *m.Label +func (x *LoadDevice) GetLabel() string { + if x != nil && x.Label != nil { + return *x.Label } return "" } -func (m *LoadDevice) GetSkipChecksum() bool { - if m != nil && m.SkipChecksum != nil { - return *m.SkipChecksum +func (x *LoadDevice) GetSkipChecksum() bool { + if x != nil && x.SkipChecksum != nil { + return *x.SkipChecksum } return false } -func (m *LoadDevice) GetU2FCounter() uint32 { - if m != nil && m.U2FCounter != nil { - return *m.U2FCounter +func (x *LoadDevice) GetU2FCounter() uint32 { + if x != nil && x.U2FCounter != nil { + return *x.U2FCounter } return 0 } @@ -1042,107 +1196,118 @@ func (m *LoadDevice) GetU2FCounter() uint32 { // @next EntropyRequest // @next Failure type ResetDevice struct { - DisplayRandom *bool `protobuf:"varint,1,opt,name=display_random,json=displayRandom" json:"display_random,omitempty"` - Strength *uint32 `protobuf:"varint,2,opt,name=strength,def=256" json:"strength,omitempty"` - PassphraseProtection *bool `protobuf:"varint,3,opt,name=passphrase_protection,json=passphraseProtection" json:"passphrase_protection,omitempty"` - PinProtection *bool `protobuf:"varint,4,opt,name=pin_protection,json=pinProtection" json:"pin_protection,omitempty"` - Language *string `protobuf:"bytes,5,opt,name=language,def=english" json:"language,omitempty"` - Label *string `protobuf:"bytes,6,opt,name=label" json:"label,omitempty"` - U2FCounter *uint32 `protobuf:"varint,7,opt,name=u2f_counter,json=u2fCounter" json:"u2f_counter,omitempty"` - SkipBackup *bool `protobuf:"varint,8,opt,name=skip_backup,json=skipBackup" json:"skip_backup,omitempty"` - NoBackup *bool `protobuf:"varint,9,opt,name=no_backup,json=noBackup" json:"no_backup,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ResetDevice) Reset() { *m = ResetDevice{} } -func (m *ResetDevice) String() string { return proto.CompactTextString(m) } -func (*ResetDevice) ProtoMessage() {} -func (*ResetDevice) Descriptor() ([]byte, []int) { - return fileDescriptor_0c720c20d27aa029, []int{13} -} + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + DisplayRandom *bool `protobuf:"varint,1,opt,name=display_random,json=displayRandom" json:"display_random,omitempty"` // display entropy generated by the device before asking for additional entropy + Strength *uint32 `protobuf:"varint,2,opt,name=strength,def=256" json:"strength,omitempty"` // strength of seed in bits + PassphraseProtection *bool `protobuf:"varint,3,opt,name=passphrase_protection,json=passphraseProtection" json:"passphrase_protection,omitempty"` // enable master node encryption using passphrase + PinProtection *bool `protobuf:"varint,4,opt,name=pin_protection,json=pinProtection" json:"pin_protection,omitempty"` // enable PIN protection + Language *string `protobuf:"bytes,5,opt,name=language,def=english" json:"language,omitempty"` // device language + Label *string `protobuf:"bytes,6,opt,name=label" json:"label,omitempty"` // device label + U2FCounter *uint32 `protobuf:"varint,7,opt,name=u2f_counter,json=u2fCounter" json:"u2f_counter,omitempty"` // U2F counter + SkipBackup *bool `protobuf:"varint,8,opt,name=skip_backup,json=skipBackup" json:"skip_backup,omitempty"` // postpone seed backup to BackupDevice workflow + NoBackup *bool `protobuf:"varint,9,opt,name=no_backup,json=noBackup" json:"no_backup,omitempty"` // indicate that no backup is going to be made +} + +// Default values for ResetDevice fields. +const ( + Default_ResetDevice_Strength = uint32(256) + Default_ResetDevice_Language = string("english") +) -func (m *ResetDevice) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ResetDevice.Unmarshal(m, b) -} -func (m *ResetDevice) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ResetDevice.Marshal(b, m, deterministic) -} -func (m *ResetDevice) XXX_Merge(src proto.Message) { - xxx_messageInfo_ResetDevice.Merge(m, src) -} -func (m *ResetDevice) XXX_Size() int { - return xxx_messageInfo_ResetDevice.Size(m) +func (x *ResetDevice) Reset() { + *x = ResetDevice{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_management_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *ResetDevice) XXX_DiscardUnknown() { - xxx_messageInfo_ResetDevice.DiscardUnknown(m) + +func (x *ResetDevice) String() string { + return protoimpl.X.MessageStringOf(x) } -var xxx_messageInfo_ResetDevice proto.InternalMessageInfo +func (*ResetDevice) ProtoMessage() {} -const Default_ResetDevice_Strength uint32 = 256 -const Default_ResetDevice_Language string = "english" +func (x *ResetDevice) ProtoReflect() protoreflect.Message { + mi := &file_messages_management_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ResetDevice.ProtoReflect.Descriptor instead. +func (*ResetDevice) Descriptor() ([]byte, []int) { + return file_messages_management_proto_rawDescGZIP(), []int{13} +} -func (m *ResetDevice) GetDisplayRandom() bool { - if m != nil && m.DisplayRandom != nil { - return *m.DisplayRandom +func (x *ResetDevice) GetDisplayRandom() bool { + if x != nil && x.DisplayRandom != nil { + return *x.DisplayRandom } return false } -func (m *ResetDevice) GetStrength() uint32 { - if m != nil && m.Strength != nil { - return *m.Strength +func (x *ResetDevice) GetStrength() uint32 { + if x != nil && x.Strength != nil { + return *x.Strength } return Default_ResetDevice_Strength } -func (m *ResetDevice) GetPassphraseProtection() bool { - if m != nil && m.PassphraseProtection != nil { - return *m.PassphraseProtection +func (x *ResetDevice) GetPassphraseProtection() bool { + if x != nil && x.PassphraseProtection != nil { + return *x.PassphraseProtection } return false } -func (m *ResetDevice) GetPinProtection() bool { - if m != nil && m.PinProtection != nil { - return *m.PinProtection +func (x *ResetDevice) GetPinProtection() bool { + if x != nil && x.PinProtection != nil { + return *x.PinProtection } return false } -func (m *ResetDevice) GetLanguage() string { - if m != nil && m.Language != nil { - return *m.Language +func (x *ResetDevice) GetLanguage() string { + if x != nil && x.Language != nil { + return *x.Language } return Default_ResetDevice_Language } -func (m *ResetDevice) GetLabel() string { - if m != nil && m.Label != nil { - return *m.Label +func (x *ResetDevice) GetLabel() string { + if x != nil && x.Label != nil { + return *x.Label } return "" } -func (m *ResetDevice) GetU2FCounter() uint32 { - if m != nil && m.U2FCounter != nil { - return *m.U2FCounter +func (x *ResetDevice) GetU2FCounter() uint32 { + if x != nil && x.U2FCounter != nil { + return *x.U2FCounter } return 0 } -func (m *ResetDevice) GetSkipBackup() bool { - if m != nil && m.SkipBackup != nil { - return *m.SkipBackup +func (x *ResetDevice) GetSkipBackup() bool { + if x != nil && x.SkipBackup != nil { + return *x.SkipBackup } return false } -func (m *ResetDevice) GetNoBackup() bool { - if m != nil && m.NoBackup != nil { - return *m.NoBackup +func (x *ResetDevice) GetNoBackup() bool { + if x != nil && x.NoBackup != nil { + return *x.NoBackup } return false } @@ -1152,108 +1317,130 @@ func (m *ResetDevice) GetNoBackup() bool { // @start // @next Success type BackupDevice struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields } -func (m *BackupDevice) Reset() { *m = BackupDevice{} } -func (m *BackupDevice) String() string { return proto.CompactTextString(m) } -func (*BackupDevice) ProtoMessage() {} -func (*BackupDevice) Descriptor() ([]byte, []int) { - return fileDescriptor_0c720c20d27aa029, []int{14} +func (x *BackupDevice) Reset() { + *x = BackupDevice{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_management_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *BackupDevice) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_BackupDevice.Unmarshal(m, b) -} -func (m *BackupDevice) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_BackupDevice.Marshal(b, m, deterministic) -} -func (m *BackupDevice) XXX_Merge(src proto.Message) { - xxx_messageInfo_BackupDevice.Merge(m, src) +func (x *BackupDevice) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *BackupDevice) XXX_Size() int { - return xxx_messageInfo_BackupDevice.Size(m) -} -func (m *BackupDevice) XXX_DiscardUnknown() { - xxx_messageInfo_BackupDevice.DiscardUnknown(m) + +func (*BackupDevice) ProtoMessage() {} + +func (x *BackupDevice) ProtoReflect() protoreflect.Message { + mi := &file_messages_management_proto_msgTypes[14] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_BackupDevice proto.InternalMessageInfo +// Deprecated: Use BackupDevice.ProtoReflect.Descriptor instead. +func (*BackupDevice) Descriptor() ([]byte, []int) { + return file_messages_management_proto_rawDescGZIP(), []int{14} +} // * // Response: Ask for additional entropy from host computer // @next EntropyAck type EntropyRequest struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields } -func (m *EntropyRequest) Reset() { *m = EntropyRequest{} } -func (m *EntropyRequest) String() string { return proto.CompactTextString(m) } -func (*EntropyRequest) ProtoMessage() {} -func (*EntropyRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_0c720c20d27aa029, []int{15} +func (x *EntropyRequest) Reset() { + *x = EntropyRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_management_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *EntropyRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_EntropyRequest.Unmarshal(m, b) +func (x *EntropyRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *EntropyRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_EntropyRequest.Marshal(b, m, deterministic) -} -func (m *EntropyRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_EntropyRequest.Merge(m, src) -} -func (m *EntropyRequest) XXX_Size() int { - return xxx_messageInfo_EntropyRequest.Size(m) -} -func (m *EntropyRequest) XXX_DiscardUnknown() { - xxx_messageInfo_EntropyRequest.DiscardUnknown(m) + +func (*EntropyRequest) ProtoMessage() {} + +func (x *EntropyRequest) ProtoReflect() protoreflect.Message { + mi := &file_messages_management_proto_msgTypes[15] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_EntropyRequest proto.InternalMessageInfo +// Deprecated: Use EntropyRequest.ProtoReflect.Descriptor instead. +func (*EntropyRequest) Descriptor() ([]byte, []int) { + return file_messages_management_proto_rawDescGZIP(), []int{15} +} // * // Request: Provide additional entropy for seed generation function // @next Success type EntropyAck struct { - Entropy []byte `protobuf:"bytes,1,opt,name=entropy" json:"entropy,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *EntropyAck) Reset() { *m = EntropyAck{} } -func (m *EntropyAck) String() string { return proto.CompactTextString(m) } -func (*EntropyAck) ProtoMessage() {} -func (*EntropyAck) Descriptor() ([]byte, []int) { - return fileDescriptor_0c720c20d27aa029, []int{16} + Entropy []byte `protobuf:"bytes,1,opt,name=entropy" json:"entropy,omitempty"` // 256 bits (32 bytes) of random data } -func (m *EntropyAck) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_EntropyAck.Unmarshal(m, b) -} -func (m *EntropyAck) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_EntropyAck.Marshal(b, m, deterministic) -} -func (m *EntropyAck) XXX_Merge(src proto.Message) { - xxx_messageInfo_EntropyAck.Merge(m, src) +func (x *EntropyAck) Reset() { + *x = EntropyAck{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_management_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *EntropyAck) XXX_Size() int { - return xxx_messageInfo_EntropyAck.Size(m) + +func (x *EntropyAck) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *EntropyAck) XXX_DiscardUnknown() { - xxx_messageInfo_EntropyAck.DiscardUnknown(m) + +func (*EntropyAck) ProtoMessage() {} + +func (x *EntropyAck) ProtoReflect() protoreflect.Message { + mi := &file_messages_management_proto_msgTypes[16] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_EntropyAck proto.InternalMessageInfo +// Deprecated: Use EntropyAck.ProtoReflect.Descriptor instead. +func (*EntropyAck) Descriptor() ([]byte, []int) { + return file_messages_management_proto_rawDescGZIP(), []int{16} +} -func (m *EntropyAck) GetEntropy() []byte { - if m != nil { - return m.Entropy +func (x *EntropyAck) GetEntropy() []byte { + if x != nil { + return x.Entropy } return nil } @@ -1264,107 +1451,118 @@ func (m *EntropyAck) GetEntropy() []byte { // @start // @next WordRequest type RecoveryDevice struct { - WordCount *uint32 `protobuf:"varint,1,opt,name=word_count,json=wordCount" json:"word_count,omitempty"` - PassphraseProtection *bool `protobuf:"varint,2,opt,name=passphrase_protection,json=passphraseProtection" json:"passphrase_protection,omitempty"` - PinProtection *bool `protobuf:"varint,3,opt,name=pin_protection,json=pinProtection" json:"pin_protection,omitempty"` - Language *string `protobuf:"bytes,4,opt,name=language,def=english" json:"language,omitempty"` - Label *string `protobuf:"bytes,5,opt,name=label" json:"label,omitempty"` - EnforceWordlist *bool `protobuf:"varint,6,opt,name=enforce_wordlist,json=enforceWordlist" json:"enforce_wordlist,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + WordCount *uint32 `protobuf:"varint,1,opt,name=word_count,json=wordCount" json:"word_count,omitempty"` // number of words in BIP-39 mnemonic + PassphraseProtection *bool `protobuf:"varint,2,opt,name=passphrase_protection,json=passphraseProtection" json:"passphrase_protection,omitempty"` // enable master node encryption using passphrase + PinProtection *bool `protobuf:"varint,3,opt,name=pin_protection,json=pinProtection" json:"pin_protection,omitempty"` // enable PIN protection + Language *string `protobuf:"bytes,4,opt,name=language,def=english" json:"language,omitempty"` // device language + Label *string `protobuf:"bytes,5,opt,name=label" json:"label,omitempty"` // device label + EnforceWordlist *bool `protobuf:"varint,6,opt,name=enforce_wordlist,json=enforceWordlist" json:"enforce_wordlist,omitempty"` // enforce BIP-39 wordlist during the process // 7 reserved for unused recovery method - Type *RecoveryDevice_RecoveryDeviceType `protobuf:"varint,8,opt,name=type,enum=hw.trezor.messages.management.RecoveryDevice_RecoveryDeviceType" json:"type,omitempty"` - U2FCounter *uint32 `protobuf:"varint,9,opt,name=u2f_counter,json=u2fCounter" json:"u2f_counter,omitempty"` - DryRun *bool `protobuf:"varint,10,opt,name=dry_run,json=dryRun" json:"dry_run,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Type *RecoveryDevice_RecoveryDeviceType `protobuf:"varint,8,opt,name=type,enum=hw.trezor.messages.management.RecoveryDevice_RecoveryDeviceType" json:"type,omitempty"` // supported recovery type + U2FCounter *uint32 `protobuf:"varint,9,opt,name=u2f_counter,json=u2fCounter" json:"u2f_counter,omitempty"` // U2F counter + DryRun *bool `protobuf:"varint,10,opt,name=dry_run,json=dryRun" json:"dry_run,omitempty"` // perform dry-run recovery workflow (for safe mnemonic validation) } -func (m *RecoveryDevice) Reset() { *m = RecoveryDevice{} } -func (m *RecoveryDevice) String() string { return proto.CompactTextString(m) } -func (*RecoveryDevice) ProtoMessage() {} -func (*RecoveryDevice) Descriptor() ([]byte, []int) { - return fileDescriptor_0c720c20d27aa029, []int{17} -} +// Default values for RecoveryDevice fields. +const ( + Default_RecoveryDevice_Language = string("english") +) -func (m *RecoveryDevice) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_RecoveryDevice.Unmarshal(m, b) -} -func (m *RecoveryDevice) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_RecoveryDevice.Marshal(b, m, deterministic) -} -func (m *RecoveryDevice) XXX_Merge(src proto.Message) { - xxx_messageInfo_RecoveryDevice.Merge(m, src) -} -func (m *RecoveryDevice) XXX_Size() int { - return xxx_messageInfo_RecoveryDevice.Size(m) +func (x *RecoveryDevice) Reset() { + *x = RecoveryDevice{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_management_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *RecoveryDevice) XXX_DiscardUnknown() { - xxx_messageInfo_RecoveryDevice.DiscardUnknown(m) + +func (x *RecoveryDevice) String() string { + return protoimpl.X.MessageStringOf(x) } -var xxx_messageInfo_RecoveryDevice proto.InternalMessageInfo +func (*RecoveryDevice) ProtoMessage() {} + +func (x *RecoveryDevice) ProtoReflect() protoreflect.Message { + mi := &file_messages_management_proto_msgTypes[17] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} -const Default_RecoveryDevice_Language string = "english" +// Deprecated: Use RecoveryDevice.ProtoReflect.Descriptor instead. +func (*RecoveryDevice) Descriptor() ([]byte, []int) { + return file_messages_management_proto_rawDescGZIP(), []int{17} +} -func (m *RecoveryDevice) GetWordCount() uint32 { - if m != nil && m.WordCount != nil { - return *m.WordCount +func (x *RecoveryDevice) GetWordCount() uint32 { + if x != nil && x.WordCount != nil { + return *x.WordCount } return 0 } -func (m *RecoveryDevice) GetPassphraseProtection() bool { - if m != nil && m.PassphraseProtection != nil { - return *m.PassphraseProtection +func (x *RecoveryDevice) GetPassphraseProtection() bool { + if x != nil && x.PassphraseProtection != nil { + return *x.PassphraseProtection } return false } -func (m *RecoveryDevice) GetPinProtection() bool { - if m != nil && m.PinProtection != nil { - return *m.PinProtection +func (x *RecoveryDevice) GetPinProtection() bool { + if x != nil && x.PinProtection != nil { + return *x.PinProtection } return false } -func (m *RecoveryDevice) GetLanguage() string { - if m != nil && m.Language != nil { - return *m.Language +func (x *RecoveryDevice) GetLanguage() string { + if x != nil && x.Language != nil { + return *x.Language } return Default_RecoveryDevice_Language } -func (m *RecoveryDevice) GetLabel() string { - if m != nil && m.Label != nil { - return *m.Label +func (x *RecoveryDevice) GetLabel() string { + if x != nil && x.Label != nil { + return *x.Label } return "" } -func (m *RecoveryDevice) GetEnforceWordlist() bool { - if m != nil && m.EnforceWordlist != nil { - return *m.EnforceWordlist +func (x *RecoveryDevice) GetEnforceWordlist() bool { + if x != nil && x.EnforceWordlist != nil { + return *x.EnforceWordlist } return false } -func (m *RecoveryDevice) GetType() RecoveryDevice_RecoveryDeviceType { - if m != nil && m.Type != nil { - return *m.Type +func (x *RecoveryDevice) GetType() RecoveryDevice_RecoveryDeviceType { + if x != nil && x.Type != nil { + return *x.Type } return RecoveryDevice_RecoveryDeviceType_ScrambledWords } -func (m *RecoveryDevice) GetU2FCounter() uint32 { - if m != nil && m.U2FCounter != nil { - return *m.U2FCounter +func (x *RecoveryDevice) GetU2FCounter() uint32 { + if x != nil && x.U2FCounter != nil { + return *x.U2FCounter } return 0 } -func (m *RecoveryDevice) GetDryRun() bool { - if m != nil && m.DryRun != nil { - return *m.DryRun +func (x *RecoveryDevice) GetDryRun() bool { + if x != nil && x.DryRun != nil { + return *x.DryRun } return false } @@ -1374,40 +1572,48 @@ func (m *RecoveryDevice) GetDryRun() bool { // Its position is shown only on device's internal display. // @next WordAck type WordRequest struct { - Type *WordRequest_WordRequestType `protobuf:"varint,1,opt,name=type,enum=hw.trezor.messages.management.WordRequest_WordRequestType" json:"type,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *WordRequest) Reset() { *m = WordRequest{} } -func (m *WordRequest) String() string { return proto.CompactTextString(m) } -func (*WordRequest) ProtoMessage() {} -func (*WordRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_0c720c20d27aa029, []int{18} + Type *WordRequest_WordRequestType `protobuf:"varint,1,opt,name=type,enum=hw.trezor.messages.management.WordRequest_WordRequestType" json:"type,omitempty"` } -func (m *WordRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_WordRequest.Unmarshal(m, b) -} -func (m *WordRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_WordRequest.Marshal(b, m, deterministic) -} -func (m *WordRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_WordRequest.Merge(m, src) +func (x *WordRequest) Reset() { + *x = WordRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_management_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *WordRequest) XXX_Size() int { - return xxx_messageInfo_WordRequest.Size(m) + +func (x *WordRequest) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *WordRequest) XXX_DiscardUnknown() { - xxx_messageInfo_WordRequest.DiscardUnknown(m) + +func (*WordRequest) ProtoMessage() {} + +func (x *WordRequest) ProtoReflect() protoreflect.Message { + mi := &file_messages_management_proto_msgTypes[18] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_WordRequest proto.InternalMessageInfo +// Deprecated: Use WordRequest.ProtoReflect.Descriptor instead. +func (*WordRequest) Descriptor() ([]byte, []int) { + return file_messages_management_proto_rawDescGZIP(), []int{18} +} -func (m *WordRequest) GetType() WordRequest_WordRequestType { - if m != nil && m.Type != nil { - return *m.Type +func (x *WordRequest) GetType() WordRequest_WordRequestType { + if x != nil && x.Type != nil { + return *x.Type } return WordRequest_WordRequestType_Plain } @@ -1418,40 +1624,48 @@ func (m *WordRequest) GetType() WordRequest_WordRequestType { // @next Success // @next Failure type WordAck struct { - Word *string `protobuf:"bytes,1,req,name=word" json:"word,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *WordAck) Reset() { *m = WordAck{} } -func (m *WordAck) String() string { return proto.CompactTextString(m) } -func (*WordAck) ProtoMessage() {} -func (*WordAck) Descriptor() ([]byte, []int) { - return fileDescriptor_0c720c20d27aa029, []int{19} + Word *string `protobuf:"bytes,1,req,name=word" json:"word,omitempty"` // one word of mnemonic on asked position } -func (m *WordAck) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_WordAck.Unmarshal(m, b) -} -func (m *WordAck) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_WordAck.Marshal(b, m, deterministic) -} -func (m *WordAck) XXX_Merge(src proto.Message) { - xxx_messageInfo_WordAck.Merge(m, src) +func (x *WordAck) Reset() { + *x = WordAck{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_management_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *WordAck) XXX_Size() int { - return xxx_messageInfo_WordAck.Size(m) + +func (x *WordAck) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *WordAck) XXX_DiscardUnknown() { - xxx_messageInfo_WordAck.DiscardUnknown(m) + +func (*WordAck) ProtoMessage() {} + +func (x *WordAck) ProtoReflect() protoreflect.Message { + mi := &file_messages_management_proto_msgTypes[19] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_WordAck proto.InternalMessageInfo +// Deprecated: Use WordAck.ProtoReflect.Descriptor instead. +func (*WordAck) Descriptor() ([]byte, []int) { + return file_messages_management_proto_rawDescGZIP(), []int{19} +} -func (m *WordAck) GetWord() string { - if m != nil && m.Word != nil { - return *m.Word +func (x *WordAck) GetWord() string { + if x != nil && x.Word != nil { + return *x.Word } return "" } @@ -1461,161 +1675,602 @@ func (m *WordAck) GetWord() string { // @start // @next Success type SetU2FCounter struct { - U2FCounter *uint32 `protobuf:"varint,1,opt,name=u2f_counter,json=u2fCounter" json:"u2f_counter,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *SetU2FCounter) Reset() { *m = SetU2FCounter{} } -func (m *SetU2FCounter) String() string { return proto.CompactTextString(m) } -func (*SetU2FCounter) ProtoMessage() {} -func (*SetU2FCounter) Descriptor() ([]byte, []int) { - return fileDescriptor_0c720c20d27aa029, []int{20} + U2FCounter *uint32 `protobuf:"varint,1,opt,name=u2f_counter,json=u2fCounter" json:"u2f_counter,omitempty"` // counter } -func (m *SetU2FCounter) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_SetU2FCounter.Unmarshal(m, b) -} -func (m *SetU2FCounter) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_SetU2FCounter.Marshal(b, m, deterministic) -} -func (m *SetU2FCounter) XXX_Merge(src proto.Message) { - xxx_messageInfo_SetU2FCounter.Merge(m, src) +func (x *SetU2FCounter) Reset() { + *x = SetU2FCounter{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_management_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *SetU2FCounter) XXX_Size() int { - return xxx_messageInfo_SetU2FCounter.Size(m) + +func (x *SetU2FCounter) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *SetU2FCounter) XXX_DiscardUnknown() { - xxx_messageInfo_SetU2FCounter.DiscardUnknown(m) + +func (*SetU2FCounter) ProtoMessage() {} + +func (x *SetU2FCounter) ProtoReflect() protoreflect.Message { + mi := &file_messages_management_proto_msgTypes[20] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_SetU2FCounter proto.InternalMessageInfo +// Deprecated: Use SetU2FCounter.ProtoReflect.Descriptor instead. +func (*SetU2FCounter) Descriptor() ([]byte, []int) { + return file_messages_management_proto_rawDescGZIP(), []int{20} +} -func (m *SetU2FCounter) GetU2FCounter() uint32 { - if m != nil && m.U2FCounter != nil { - return *m.U2FCounter +func (x *SetU2FCounter) GetU2FCounter() uint32 { + if x != nil && x.U2FCounter != nil { + return *x.U2FCounter } return 0 } -func init() { - proto.RegisterEnum("hw.trezor.messages.management.ApplySettings_PassphraseSourceType", ApplySettings_PassphraseSourceType_name, ApplySettings_PassphraseSourceType_value) - proto.RegisterEnum("hw.trezor.messages.management.RecoveryDevice_RecoveryDeviceType", RecoveryDevice_RecoveryDeviceType_name, RecoveryDevice_RecoveryDeviceType_value) - proto.RegisterEnum("hw.trezor.messages.management.WordRequest_WordRequestType", WordRequest_WordRequestType_name, WordRequest_WordRequestType_value) - proto.RegisterType((*Initialize)(nil), "hw.trezor.messages.management.Initialize") - proto.RegisterType((*GetFeatures)(nil), "hw.trezor.messages.management.GetFeatures") - proto.RegisterType((*Features)(nil), "hw.trezor.messages.management.Features") - proto.RegisterType((*ClearSession)(nil), "hw.trezor.messages.management.ClearSession") - proto.RegisterType((*ApplySettings)(nil), "hw.trezor.messages.management.ApplySettings") - proto.RegisterType((*ApplyFlags)(nil), "hw.trezor.messages.management.ApplyFlags") - proto.RegisterType((*ChangePin)(nil), "hw.trezor.messages.management.ChangePin") - proto.RegisterType((*Ping)(nil), "hw.trezor.messages.management.Ping") - proto.RegisterType((*Cancel)(nil), "hw.trezor.messages.management.Cancel") - proto.RegisterType((*GetEntropy)(nil), "hw.trezor.messages.management.GetEntropy") - proto.RegisterType((*Entropy)(nil), "hw.trezor.messages.management.Entropy") - proto.RegisterType((*WipeDevice)(nil), "hw.trezor.messages.management.WipeDevice") - proto.RegisterType((*LoadDevice)(nil), "hw.trezor.messages.management.LoadDevice") - proto.RegisterType((*ResetDevice)(nil), "hw.trezor.messages.management.ResetDevice") - proto.RegisterType((*BackupDevice)(nil), "hw.trezor.messages.management.BackupDevice") - proto.RegisterType((*EntropyRequest)(nil), "hw.trezor.messages.management.EntropyRequest") - proto.RegisterType((*EntropyAck)(nil), "hw.trezor.messages.management.EntropyAck") - proto.RegisterType((*RecoveryDevice)(nil), "hw.trezor.messages.management.RecoveryDevice") - proto.RegisterType((*WordRequest)(nil), "hw.trezor.messages.management.WordRequest") - proto.RegisterType((*WordAck)(nil), "hw.trezor.messages.management.WordAck") - proto.RegisterType((*SetU2FCounter)(nil), "hw.trezor.messages.management.SetU2FCounter") -} - -func init() { proto.RegisterFile("messages-management.proto", fileDescriptor_0c720c20d27aa029) } - -var fileDescriptor_0c720c20d27aa029 = []byte{ - // 1393 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x57, 0xdd, 0x6e, 0xdb, 0xc8, - 0x15, 0x8e, 0x7e, 0x62, 0x49, 0xc7, 0xfa, 0xcb, 0xd4, 0x8e, 0xe9, 0xb8, 0x6e, 0x1c, 0xba, 0x6e, - 0x12, 0x04, 0x15, 0x0a, 0x17, 0x09, 0x90, 0x5c, 0x14, 0x75, 0xec, 0xfc, 0x21, 0x71, 0x6a, 0xd0, - 0x6e, 0x02, 0xf4, 0x86, 0x18, 0x91, 0x47, 0xd2, 0xd4, 0xe4, 0x0c, 0xcb, 0x19, 0xda, 0x55, 0x5e, - 0x60, 0x6f, 0xf6, 0x45, 0x16, 0xfb, 0x1c, 0x7b, 0xb5, 0xcf, 0xb0, 0xef, 0xb2, 0x98, 0x19, 0x52, - 0xa2, 0x65, 0x3b, 0x46, 0x76, 0xef, 0xe6, 0x7c, 0xe7, 0xe3, 0x68, 0xce, 0x77, 0xbe, 0x39, 0x63, - 0xc3, 0x7a, 0x8c, 0x52, 0xd2, 0x31, 0xca, 0xbf, 0xc6, 0x94, 0xd3, 0x31, 0xc6, 0xc8, 0xd5, 0x20, - 0x49, 0x85, 0x12, 0x64, 0x73, 0x72, 0x3e, 0x50, 0x29, 0x7e, 0x11, 0xe9, 0xa0, 0x20, 0x0d, 0xe6, - 0xa4, 0x7b, 0xab, 0xb3, 0x2f, 0x03, 0x11, 0xc7, 0x82, 0xdb, 0xaf, 0xdc, 0xf7, 0x00, 0xef, 0x38, - 0x53, 0x8c, 0x46, 0xec, 0x0b, 0x92, 0x15, 0xb8, 0x2d, 0x15, 0x55, 0xe8, 0x54, 0xb6, 0x2a, 0x8f, - 0xda, 0x9e, 0x0d, 0xc8, 0x43, 0xe8, 0xc9, 0x53, 0x96, 0xf8, 0x09, 0x95, 0x32, 0x99, 0xa4, 0x54, - 0xa2, 0x53, 0xdd, 0xaa, 0x3c, 0x6a, 0x7a, 0x5d, 0x0d, 0x1f, 0xcd, 0x50, 0xb7, 0x03, 0xcb, 0x6f, - 0x50, 0xbd, 0x46, 0xaa, 0xb2, 0x14, 0xa5, 0xfb, 0x7d, 0x03, 0x9a, 0x45, 0x40, 0xee, 0xc2, 0xd2, - 0x19, 0xf2, 0x50, 0xa4, 0x66, 0xef, 0x96, 0x97, 0x47, 0x64, 0x1b, 0x3a, 0x31, 0xfd, 0xaf, 0x48, - 0xfd, 0x33, 0x4c, 0x25, 0x13, 0xdc, 0x6c, 0xdd, 0xf1, 0xda, 0x06, 0xfc, 0x64, 0x31, 0x43, 0x62, - 0xbc, 0x44, 0xaa, 0xe5, 0x24, 0x0d, 0x96, 0x48, 0x09, 0x55, 0xc1, 0x64, 0x46, 0xaa, 0x5b, 0x92, - 0x01, 0x0b, 0xd2, 0x43, 0xe8, 0x0d, 0x85, 0x50, 0x91, 0xa0, 0x21, 0xa6, 0x7e, 0x2c, 0x42, 0x74, - 0x6e, 0xdb, 0x5a, 0xe6, 0xf0, 0xa1, 0x08, 0x91, 0x6c, 0x40, 0x2b, 0xc4, 0x33, 0x16, 0xa0, 0xcf, - 0x42, 0x67, 0xc9, 0x1c, 0xb9, 0x69, 0x81, 0x77, 0x21, 0xd9, 0x81, 0x6e, 0xc2, 0xb8, 0xaf, 0x25, - 0xc4, 0x40, 0xe9, 0xdf, 0x6a, 0x98, 0x4d, 0x3a, 0x09, 0xe3, 0x47, 0x33, 0x90, 0xfc, 0x1d, 0x56, - 0xe7, 0x9a, 0x95, 0xd9, 0x4d, 0xc3, 0x5e, 0x99, 0x27, 0x4b, 0x1f, 0xdd, 0x83, 0x66, 0x44, 0xf9, - 0x38, 0xa3, 0x63, 0x74, 0x5a, 0xf6, 0x77, 0x8b, 0x58, 0xf7, 0x27, 0xa2, 0x43, 0x8c, 0x1c, 0x30, - 0x09, 0x1b, 0x90, 0x2d, 0x58, 0x66, 0xb3, 0x1e, 0x86, 0x4e, 0xdb, 0x6c, 0x5e, 0x86, 0xf4, 0x9e, - 0x29, 0x9e, 0x31, 0xa3, 0x4a, 0xc7, 0xb4, 0x76, 0x16, 0x2f, 0x28, 0x32, 0xa1, 0x72, 0xe2, 0x74, - 0x0d, 0xa5, 0xa4, 0xc8, 0x5b, 0x2a, 0x27, 0x7a, 0x13, 0x16, 0x27, 0x22, 0x55, 0x18, 0x3a, 0x3d, - 0xf3, 0x1b, 0xb3, 0x98, 0x6c, 0x02, 0x68, 0x41, 0x02, 0x1a, 0x4c, 0x30, 0x74, 0xfa, 0x26, 0xdb, - 0x4a, 0x18, 0xdf, 0x37, 0x00, 0x79, 0x02, 0x77, 0x4a, 0x42, 0xe4, 0xac, 0x3b, 0x86, 0xd5, 0x9f, - 0x27, 0x72, 0xf2, 0x63, 0xe8, 0x8f, 0x58, 0x1a, 0x9f, 0xd3, 0x54, 0x6b, 0x86, 0x12, 0xb9, 0x72, - 0x88, 0xe1, 0xf6, 0x0a, 0xfc, 0xc8, 0xc2, 0xe4, 0x01, 0xb4, 0x39, 0x62, 0x28, 0xfd, 0x21, 0x0d, - 0x4e, 0xb3, 0xc4, 0xf9, 0x83, 0x2d, 0xdd, 0x60, 0x2f, 0x0d, 0xa4, 0x25, 0x1b, 0x45, 0x74, 0x2c, - 0x9d, 0x15, 0xe3, 0x06, 0x1b, 0x68, 0x54, 0xf7, 0x3e, 0x72, 0x56, 0xad, 0x90, 0x26, 0x20, 0xeb, - 0xd0, 0x1c, 0x9d, 0xfb, 0xc6, 0x79, 0xce, 0x5d, 0x43, 0x6f, 0x8c, 0xce, 0x0f, 0x75, 0x58, 0xa4, - 0xb4, 0xdf, 0x9c, 0xb5, 0x59, 0x4a, 0x87, 0x79, 0xca, 0xb8, 0xcc, 0x71, 0x8a, 0xd4, 0x91, 0x0e, - 0xb5, 0x89, 0x46, 0xe7, 0x7e, 0xee, 0xfb, 0x75, 0xdb, 0xcc, 0xd1, 0xf9, 0x27, 0xeb, 0xfc, 0x3f, - 0x43, 0x77, 0x96, 0xf4, 0x4f, 0x71, 0x2a, 0x9d, 0x7b, 0x46, 0xf7, 0x76, 0xc1, 0x78, 0x8f, 0x53, - 0xa9, 0xa5, 0xcb, 0xf8, 0x88, 0x71, 0x26, 0x27, 0x18, 0x16, 0x75, 0x6e, 0x58, 0xe9, 0xe6, 0x89, - 0xbc, 0xd8, 0x0d, 0x68, 0x71, 0x51, 0x90, 0xfe, 0x68, 0x7b, 0xc4, 0x85, 0x4d, 0xba, 0x5d, 0x68, - 0xef, 0x47, 0x48, 0xd3, 0x63, 0x94, 0xba, 0xf1, 0xee, 0x77, 0x35, 0xe8, 0xec, 0x25, 0x49, 0x34, - 0x3d, 0x46, 0xa5, 0x18, 0x1f, 0xcb, 0x0b, 0xd6, 0xab, 0x5c, 0x67, 0xbd, 0x6a, 0xd9, 0x7a, 0x3b, - 0xd0, 0xcd, 0xb4, 0xb5, 0xe7, 0x93, 0xa1, 0x66, 0x2f, 0x42, 0x26, 0x71, 0x3e, 0x18, 0xc8, 0x9f, - 0x00, 0x26, 0x22, 0x46, 0x19, 0xa4, 0x88, 0xf6, 0x5e, 0xb6, 0xbd, 0x12, 0x42, 0xf8, 0x05, 0x7f, - 0x48, 0x91, 0xa5, 0x81, 0xbd, 0x97, 0xdd, 0xdd, 0xbd, 0xc1, 0x57, 0xe7, 0xda, 0xe0, 0x42, 0x05, - 0x83, 0xf9, 0x6f, 0x1e, 0x9b, 0x4d, 0x4e, 0xa6, 0x09, 0x96, 0x2d, 0x66, 0x51, 0xf2, 0x04, 0x08, - 0xcd, 0x94, 0xf0, 0x23, 0x11, 0x9c, 0xfa, 0x21, 0x46, 0x74, 0xea, 0xc7, 0xd2, 0xdc, 0xf2, 0x8e, - 0xd7, 0xd3, 0x99, 0x0f, 0x22, 0x38, 0x3d, 0xd0, 0xf8, 0xa1, 0xd4, 0x7e, 0x0c, 0x99, 0x4c, 0x34, - 0x29, 0x15, 0x8a, 0xce, 0xae, 0x7b, 0xc7, 0xeb, 0xe5, 0xb8, 0x97, 0xc3, 0xee, 0x53, 0x58, 0xb9, - 0xea, 0x04, 0xa4, 0x01, 0xb5, 0xbd, 0xe3, 0xf7, 0xfd, 0x5b, 0x04, 0x60, 0xe9, 0xe0, 0xd5, 0xa7, - 0x77, 0xfb, 0xaf, 0xfa, 0x15, 0xd2, 0x84, 0xfa, 0xdb, 0x7f, 0x1d, 0x9f, 0xf4, 0xab, 0xae, 0x0b, - 0x60, 0xca, 0x78, 0x5d, 0x78, 0xd3, 0x3a, 0xb6, 0x52, 0x72, 0xac, 0xbb, 0x0d, 0xad, 0xfd, 0x09, - 0xe5, 0x63, 0x3c, 0x62, 0x5c, 0x0f, 0xd3, 0x14, 0x63, 0x71, 0x66, 0xdb, 0xd4, 0xf4, 0xf2, 0xc8, - 0xfd, 0xa1, 0x02, 0xf5, 0x23, 0xc6, 0xc7, 0xc4, 0x81, 0x46, 0x2e, 0x56, 0xde, 0xc8, 0x22, 0xd4, - 0x7e, 0x1a, 0x66, 0x4a, 0x89, 0x0b, 0xd3, 0xcb, 0x8e, 0xf3, 0xbe, 0x4d, 0x94, 0x66, 0xd1, 0xe5, - 0x39, 0x57, 0xfb, 0xa6, 0x39, 0x57, 0xbf, 0x7e, 0xce, 0xb9, 0x4d, 0x58, 0xda, 0xa7, 0x3c, 0xc0, - 0xc8, 0xdd, 0x02, 0x78, 0x83, 0xea, 0x15, 0x57, 0xa9, 0x48, 0xa6, 0x84, 0x40, 0x5d, 0xb2, 0x2f, - 0xfa, 0xdc, 0xd5, 0x47, 0x1d, 0xcf, 0xac, 0xdd, 0x6d, 0x68, 0x14, 0x69, 0x07, 0x1a, 0x68, 0x97, - 0x86, 0xd1, 0xf6, 0x8a, 0xd0, 0x6d, 0x03, 0x7c, 0x66, 0x09, 0x1e, 0x98, 0x21, 0xed, 0xfe, 0x58, - 0x05, 0xf8, 0x20, 0x68, 0x68, 0x43, 0x6d, 0xed, 0x98, 0x63, 0x2c, 0x38, 0x0b, 0x0a, 0x6b, 0x17, - 0x31, 0x79, 0x0e, 0x75, 0xae, 0x1f, 0x02, 0xad, 0xc2, 0xf2, 0xee, 0xce, 0x55, 0x86, 0xcb, 0xdf, - 0xcc, 0xb7, 0x07, 0x1f, 0x45, 0x68, 0x4d, 0x65, 0x3e, 0x21, 0x7d, 0xa8, 0x25, 0xcc, 0xaa, 0xd2, - 0xf2, 0xf4, 0xf2, 0x37, 0x69, 0x41, 0xb6, 0x4b, 0x17, 0x4f, 0xdb, 0xbe, 0xf5, 0xa2, 0x81, 0x7c, - 0x1c, 0x31, 0x39, 0xb9, 0xea, 0x06, 0x2e, 0x95, 0x6f, 0xe0, 0x36, 0x74, 0xcc, 0xe3, 0x1c, 0x4c, - 0x30, 0x38, 0x95, 0x59, 0x9c, 0xbf, 0x44, 0x6d, 0x0d, 0xee, 0xe7, 0x18, 0xb9, 0x0f, 0xcb, 0xd9, - 0xee, 0xc8, 0x0f, 0x44, 0xc6, 0x15, 0xa6, 0xe6, 0xf9, 0xe9, 0x78, 0x90, 0xed, 0x8e, 0xf6, 0x2d, - 0xe2, 0xfe, 0x5c, 0x85, 0x65, 0x0f, 0x25, 0xaa, 0x5c, 0xae, 0x1d, 0xe8, 0xce, 0x3c, 0x4f, 0x79, - 0x28, 0xe2, 0xdc, 0x68, 0x9d, 0xc2, 0xf1, 0x06, 0x24, 0xf7, 0xa1, 0x29, 0x55, 0x8a, 0x7c, 0xac, - 0x26, 0xf6, 0xdd, 0x7e, 0x51, 0xdb, 0x7d, 0xfa, 0xcc, 0x9b, 0x81, 0xd7, 0xab, 0x51, 0xfb, 0x8a, - 0x1a, 0x97, 0x5d, 0x57, 0xbf, 0xca, 0x75, 0xbf, 0x43, 0xb4, 0x05, 0x3d, 0x1a, 0x8b, 0x7a, 0x68, - 0x82, 0x51, 0x35, 0x1f, 0xa5, 0xf6, 0xbd, 0x06, 0x0d, 0x5d, 0x35, 0x69, 0x5b, 0x97, 0x27, 0xad, - 0x5d, 0xe5, 0x5e, 0xec, 0x43, 0x37, 0xb7, 0xaf, 0x87, 0xff, 0xcb, 0x50, 0x2a, 0xf7, 0x2f, 0x00, - 0x39, 0xb2, 0x17, 0x9c, 0x5e, 0xf4, 0x74, 0xa5, 0xec, 0xe9, 0x5f, 0x6a, 0xd0, 0xf5, 0x30, 0x10, - 0x67, 0x98, 0x4e, 0xf3, 0xd6, 0x6c, 0x02, 0x9c, 0x8b, 0x34, 0xb4, 0x87, 0xcf, 0x67, 0x44, 0x4b, - 0x23, 0xe6, 0xec, 0xd7, 0x2b, 0x5e, 0xfd, 0x26, 0xc5, 0x6b, 0x37, 0x29, 0x5e, 0xbf, 0x51, 0xf1, - 0xdb, 0x65, 0xc5, 0x1f, 0x43, 0x1f, 0xf9, 0x48, 0xa4, 0x01, 0xfa, 0xfa, 0xac, 0x11, 0x93, 0xca, - 0xb4, 0xa4, 0xe9, 0xf5, 0x72, 0xfc, 0x73, 0x0e, 0x93, 0x13, 0xa8, 0xab, 0x69, 0x82, 0x46, 0xf4, - 0xee, 0xee, 0x3f, 0x6f, 0x98, 0xff, 0x17, 0xd5, 0x59, 0x08, 0xed, 0x4d, 0xd5, 0xbb, 0x2d, 0xb6, - 0xbc, 0x75, 0xa9, 0xe5, 0x6b, 0xd0, 0x08, 0xd3, 0xa9, 0x9f, 0x66, 0xdc, 0xfc, 0x75, 0xd5, 0xf4, - 0x96, 0xc2, 0x74, 0xea, 0x65, 0xdc, 0xfd, 0x0f, 0x90, 0xcb, 0xbb, 0x92, 0x1d, 0x78, 0x70, 0x19, - 0xf5, 0x8f, 0x83, 0x94, 0xc6, 0xc3, 0x08, 0x43, 0x5d, 0x8d, 0xec, 0xdf, 0x22, 0x9b, 0xb0, 0x7e, - 0x05, 0xed, 0x90, 0xaa, 0x94, 0xfd, 0xbf, 0x5f, 0x71, 0x7f, 0xaa, 0xc0, 0xb2, 0xa6, 0xe6, 0xbe, - 0x20, 0x1f, 0xf3, 0xda, 0x2b, 0xa6, 0xf6, 0x17, 0x37, 0xd4, 0x5e, 0xfa, 0xb2, 0xbc, 0x9e, 0x57, - 0xed, 0x8e, 0xa0, 0xb7, 0x90, 0x20, 0xeb, 0xb0, 0xba, 0x00, 0xf9, 0x47, 0x11, 0x65, 0xbc, 0x7f, - 0x8b, 0x6c, 0xc0, 0xda, 0x62, 0xca, 0x9e, 0xf4, 0x79, 0xbf, 0x72, 0x7d, 0xf2, 0x59, 0xbf, 0xea, - 0x6e, 0x42, 0x43, 0x27, 0xb5, 0x99, 0x09, 0xd4, 0x75, 0x87, 0xcd, 0x74, 0x6e, 0x79, 0x66, 0xed, - 0xfe, 0x0d, 0x3a, 0xc7, 0xa8, 0xfe, 0xbd, 0xfb, 0xba, 0x74, 0xbf, 0xca, 0xdd, 0xa8, 0x2c, 0x76, - 0xe3, 0xe5, 0x3f, 0x60, 0x3b, 0x10, 0xf1, 0x40, 0x52, 0x25, 0xe4, 0x84, 0x45, 0x74, 0x28, 0x0b, - 0x21, 0x22, 0x36, 0xb4, 0xff, 0xbb, 0x0c, 0xb3, 0xd1, 0xcb, 0xb5, 0x13, 0x03, 0x1e, 0x5a, 0x71, - 0x0e, 0x67, 0xd2, 0xfc, 0x1a, 0x00, 0x00, 0xff, 0xff, 0xd7, 0x6e, 0xfc, 0x59, 0x29, 0x0d, 0x00, - 0x00, +var File_messages_management_proto protoreflect.FileDescriptor + +var file_messages_management_proto_rawDesc = []byte{ + 0x0a, 0x19, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2d, 0x6d, 0x61, 0x6e, 0x61, 0x67, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1d, 0x68, 0x77, 0x2e, + 0x74, 0x72, 0x65, 0x7a, 0x6f, 0x72, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, + 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x1a, 0x15, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x73, 0x2d, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x22, 0x4b, 0x0a, 0x0a, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x12, + 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x70, 0x61, + 0x73, 0x73, 0x70, 0x68, 0x72, 0x61, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, + 0x73, 0x6b, 0x69, 0x70, 0x50, 0x61, 0x73, 0x73, 0x70, 0x68, 0x72, 0x61, 0x73, 0x65, 0x22, 0x0d, + 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x22, 0x8c, 0x07, + 0x0a, 0x08, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x65, + 0x6e, 0x64, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x76, 0x65, 0x6e, 0x64, + 0x6f, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x5f, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x6d, 0x61, 0x6a, 0x6f, 0x72, + 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x69, 0x6e, 0x6f, 0x72, + 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, + 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, + 0x70, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x70, 0x61, 0x74, 0x63, 0x68, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x12, 0x27, 0x0a, 0x0f, 0x62, 0x6f, 0x6f, 0x74, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x72, 0x5f, + 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x62, 0x6f, 0x6f, 0x74, + 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, + 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x69, 0x6e, 0x5f, 0x70, + 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0d, 0x70, 0x69, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x33, + 0x0a, 0x15, 0x70, 0x61, 0x73, 0x73, 0x70, 0x68, 0x72, 0x61, 0x73, 0x65, 0x5f, 0x70, 0x72, 0x6f, + 0x74, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x70, + 0x61, 0x73, 0x73, 0x70, 0x68, 0x72, 0x61, 0x73, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x18, + 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x12, + 0x14, 0x0a, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x20, 0x0a, 0x0b, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, + 0x69, 0x7a, 0x65, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x69, 0x6e, 0x69, 0x74, + 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x76, 0x69, 0x73, + 0x69, 0x6f, 0x6e, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x72, 0x65, 0x76, 0x69, 0x73, + 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x0f, 0x62, 0x6f, 0x6f, 0x74, 0x6c, 0x6f, 0x61, 0x64, 0x65, + 0x72, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x62, 0x6f, + 0x6f, 0x74, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x72, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1a, 0x0a, 0x08, + 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, + 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x69, 0x6e, 0x5f, + 0x63, 0x61, 0x63, 0x68, 0x65, 0x64, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x70, 0x69, + 0x6e, 0x43, 0x61, 0x63, 0x68, 0x65, 0x64, 0x12, 0x2b, 0x0a, 0x11, 0x70, 0x61, 0x73, 0x73, 0x70, + 0x68, 0x72, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x61, 0x63, 0x68, 0x65, 0x64, 0x18, 0x11, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x10, 0x70, 0x61, 0x73, 0x73, 0x70, 0x68, 0x72, 0x61, 0x73, 0x65, 0x43, 0x61, + 0x63, 0x68, 0x65, 0x64, 0x12, 0x29, 0x0a, 0x10, 0x66, 0x69, 0x72, 0x6d, 0x77, 0x61, 0x72, 0x65, + 0x5f, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x18, 0x12, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, + 0x66, 0x69, 0x72, 0x6d, 0x77, 0x61, 0x72, 0x65, 0x50, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x12, + 0x21, 0x0a, 0x0c, 0x6e, 0x65, 0x65, 0x64, 0x73, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x18, + 0x13, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x6e, 0x65, 0x65, 0x64, 0x73, 0x42, 0x61, 0x63, 0x6b, + 0x75, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x14, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x6f, 0x64, 0x65, + 0x6c, 0x18, 0x15, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x12, 0x19, + 0x0a, 0x08, 0x66, 0x77, 0x5f, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x07, 0x66, 0x77, 0x4d, 0x61, 0x6a, 0x6f, 0x72, 0x12, 0x19, 0x0a, 0x08, 0x66, 0x77, 0x5f, + 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x66, 0x77, 0x4d, + 0x69, 0x6e, 0x6f, 0x72, 0x12, 0x19, 0x0a, 0x08, 0x66, 0x77, 0x5f, 0x70, 0x61, 0x74, 0x63, 0x68, + 0x18, 0x18, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x66, 0x77, 0x50, 0x61, 0x74, 0x63, 0x68, 0x12, + 0x1b, 0x0a, 0x09, 0x66, 0x77, 0x5f, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x18, 0x19, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x66, 0x77, 0x56, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x12, 0x24, 0x0a, 0x0e, + 0x66, 0x77, 0x5f, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x1a, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x66, 0x77, 0x56, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x4b, 0x65, + 0x79, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x75, 0x6e, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, + 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x75, + 0x6e, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, + 0x1b, 0x0a, 0x09, 0x6e, 0x6f, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x18, 0x1c, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x08, 0x6e, 0x6f, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x22, 0x0e, 0x0a, 0x0c, + 0x43, 0x6c, 0x65, 0x61, 0x72, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x87, 0x03, 0x0a, + 0x0d, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x1a, + 0x0a, 0x08, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x61, + 0x62, 0x65, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, + 0x12, 0x25, 0x0a, 0x0e, 0x75, 0x73, 0x65, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x70, 0x68, 0x72, 0x61, + 0x73, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x75, 0x73, 0x65, 0x50, 0x61, 0x73, + 0x73, 0x70, 0x68, 0x72, 0x61, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x68, 0x6f, 0x6d, 0x65, 0x73, + 0x63, 0x72, 0x65, 0x65, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x68, 0x6f, 0x6d, + 0x65, 0x73, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x12, 0x6e, 0x0a, 0x11, 0x70, 0x61, 0x73, 0x73, 0x70, + 0x68, 0x72, 0x61, 0x73, 0x65, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x41, 0x2e, 0x68, 0x77, 0x2e, 0x74, 0x72, 0x65, 0x7a, 0x6f, 0x72, 0x2e, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, + 0x2e, 0x50, 0x61, 0x73, 0x73, 0x70, 0x68, 0x72, 0x61, 0x73, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x10, 0x70, 0x61, 0x73, 0x73, 0x70, 0x68, 0x72, 0x61, 0x73, + 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x2b, 0x0a, 0x12, 0x61, 0x75, 0x74, 0x6f, 0x5f, + 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x5f, 0x6d, 0x73, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x61, 0x75, 0x74, 0x6f, 0x4c, 0x6f, 0x63, 0x6b, 0x44, 0x65, 0x6c, + 0x61, 0x79, 0x4d, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, + 0x72, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, + 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, + 0x35, 0x0a, 0x14, 0x50, 0x61, 0x73, 0x73, 0x70, 0x68, 0x72, 0x61, 0x73, 0x65, 0x53, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x53, 0x4b, 0x10, 0x00, + 0x12, 0x0a, 0x0a, 0x06, 0x44, 0x45, 0x56, 0x49, 0x43, 0x45, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, + 0x48, 0x4f, 0x53, 0x54, 0x10, 0x02, 0x22, 0x22, 0x0a, 0x0a, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x46, + 0x6c, 0x61, 0x67, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x22, 0x23, 0x0a, 0x09, 0x43, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x50, 0x69, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x6d, 0x6f, 0x76, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x22, + 0xa9, 0x01, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x5f, 0x70, 0x72, 0x6f, + 0x74, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x62, + 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x25, 0x0a, 0x0e, 0x70, 0x69, 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x70, 0x69, 0x6e, 0x50, 0x72, 0x6f, 0x74, + 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x33, 0x0a, 0x15, 0x70, 0x61, 0x73, 0x73, 0x70, 0x68, + 0x72, 0x61, 0x73, 0x65, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x70, 0x61, 0x73, 0x73, 0x70, 0x68, 0x72, 0x61, 0x73, + 0x65, 0x50, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x08, 0x0a, 0x06, 0x43, + 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x22, 0x20, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, + 0x6f, 0x70, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x01, 0x20, 0x02, 0x28, + 0x0d, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x22, 0x23, 0x0a, 0x07, 0x45, 0x6e, 0x74, 0x72, 0x6f, + 0x70, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x6f, 0x70, 0x79, 0x18, 0x01, 0x20, + 0x02, 0x28, 0x0c, 0x52, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x6f, 0x70, 0x79, 0x22, 0x0c, 0x0a, 0x0a, + 0x57, 0x69, 0x70, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x22, 0xab, 0x02, 0x0a, 0x0a, 0x4c, + 0x6f, 0x61, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x6e, 0x65, + 0x6d, 0x6f, 0x6e, 0x69, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x6e, 0x65, + 0x6d, 0x6f, 0x6e, 0x69, 0x63, 0x12, 0x39, 0x0a, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x68, 0x77, 0x2e, 0x74, 0x72, 0x65, 0x7a, 0x6f, 0x72, 0x2e, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, + 0x48, 0x44, 0x4e, 0x6f, 0x64, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x6e, 0x6f, 0x64, 0x65, + 0x12, 0x10, 0x0a, 0x03, 0x70, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x70, + 0x69, 0x6e, 0x12, 0x33, 0x0a, 0x15, 0x70, 0x61, 0x73, 0x73, 0x70, 0x68, 0x72, 0x61, 0x73, 0x65, + 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x14, 0x70, 0x61, 0x73, 0x73, 0x70, 0x68, 0x72, 0x61, 0x73, 0x65, 0x50, 0x72, 0x6f, + 0x74, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x08, 0x6c, 0x61, 0x6e, 0x67, 0x75, + 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x3a, 0x07, 0x65, 0x6e, 0x67, 0x6c, 0x69, + 0x73, 0x68, 0x52, 0x08, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, + 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x61, 0x62, + 0x65, 0x6c, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, + 0x73, 0x75, 0x6d, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x73, 0x6b, 0x69, 0x70, 0x43, + 0x68, 0x65, 0x63, 0x6b, 0x73, 0x75, 0x6d, 0x12, 0x1f, 0x0a, 0x0b, 0x75, 0x32, 0x66, 0x5f, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x75, 0x32, + 0x66, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x22, 0xcb, 0x02, 0x0a, 0x0b, 0x52, 0x65, 0x73, + 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x69, 0x73, 0x70, + 0x6c, 0x61, 0x79, 0x5f, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x0d, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x52, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x12, + 0x1f, 0x0a, 0x08, 0x73, 0x74, 0x72, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0d, 0x3a, 0x03, 0x32, 0x35, 0x36, 0x52, 0x08, 0x73, 0x74, 0x72, 0x65, 0x6e, 0x67, 0x74, 0x68, + 0x12, 0x33, 0x0a, 0x15, 0x70, 0x61, 0x73, 0x73, 0x70, 0x68, 0x72, 0x61, 0x73, 0x65, 0x5f, 0x70, + 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x14, 0x70, 0x61, 0x73, 0x73, 0x70, 0x68, 0x72, 0x61, 0x73, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x65, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x69, 0x6e, 0x5f, 0x70, 0x72, 0x6f, + 0x74, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x70, + 0x69, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x08, + 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x3a, 0x07, + 0x65, 0x6e, 0x67, 0x6c, 0x69, 0x73, 0x68, 0x52, 0x08, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, + 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x1f, 0x0a, 0x0b, 0x75, 0x32, 0x66, 0x5f, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x75, 0x32, + 0x66, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x6b, 0x69, 0x70, + 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x73, + 0x6b, 0x69, 0x70, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x6f, 0x5f, + 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6e, 0x6f, + 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x22, 0x0e, 0x0a, 0x0c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, + 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x22, 0x10, 0x0a, 0x0e, 0x45, 0x6e, 0x74, 0x72, 0x6f, 0x70, + 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x26, 0x0a, 0x0a, 0x45, 0x6e, 0x74, 0x72, + 0x6f, 0x70, 0x79, 0x41, 0x63, 0x6b, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x6f, 0x70, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x6f, 0x70, 0x79, + 0x22, 0xdd, 0x03, 0x0a, 0x0e, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x44, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x77, 0x6f, 0x72, 0x64, 0x43, 0x6f, 0x75, + 0x6e, 0x74, 0x12, 0x33, 0x0a, 0x15, 0x70, 0x61, 0x73, 0x73, 0x70, 0x68, 0x72, 0x61, 0x73, 0x65, + 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x14, 0x70, 0x61, 0x73, 0x73, 0x70, 0x68, 0x72, 0x61, 0x73, 0x65, 0x50, 0x72, 0x6f, + 0x74, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x69, 0x6e, 0x5f, 0x70, + 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0d, 0x70, 0x69, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x23, + 0x0a, 0x08, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x3a, 0x07, 0x65, 0x6e, 0x67, 0x6c, 0x69, 0x73, 0x68, 0x52, 0x08, 0x6c, 0x61, 0x6e, 0x67, 0x75, + 0x61, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x29, 0x0a, 0x10, 0x65, 0x6e, 0x66, + 0x6f, 0x72, 0x63, 0x65, 0x5f, 0x77, 0x6f, 0x72, 0x64, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x0f, 0x65, 0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x57, 0x6f, 0x72, 0x64, + 0x6c, 0x69, 0x73, 0x74, 0x12, 0x54, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x08, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x40, 0x2e, 0x68, 0x77, 0x2e, 0x74, 0x72, 0x65, 0x7a, 0x6f, 0x72, 0x2e, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x44, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x75, 0x32, + 0x66, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x0a, 0x75, 0x32, 0x66, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x12, 0x17, 0x0a, 0x07, 0x64, + 0x72, 0x79, 0x5f, 0x72, 0x75, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x64, 0x72, + 0x79, 0x52, 0x75, 0x6e, 0x22, 0x5a, 0x0a, 0x12, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, + 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x25, 0x0a, 0x21, 0x52, 0x65, + 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, + 0x5f, 0x53, 0x63, 0x72, 0x61, 0x6d, 0x62, 0x6c, 0x65, 0x64, 0x57, 0x6f, 0x72, 0x64, 0x73, 0x10, + 0x00, 0x12, 0x1d, 0x0a, 0x19, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x44, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x10, 0x01, + 0x22, 0xc5, 0x01, 0x0a, 0x0b, 0x57, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x4e, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x3a, + 0x2e, 0x68, 0x77, 0x2e, 0x74, 0x72, 0x65, 0x7a, 0x6f, 0x72, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x73, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x57, + 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x57, 0x6f, 0x72, 0x64, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, + 0x22, 0x66, 0x0a, 0x0f, 0x57, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, + 0x79, 0x70, 0x65, 0x12, 0x19, 0x0a, 0x15, 0x57, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x50, 0x6c, 0x61, 0x69, 0x6e, 0x10, 0x00, 0x12, 0x1b, + 0x0a, 0x17, 0x57, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, + 0x65, 0x5f, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x39, 0x10, 0x01, 0x12, 0x1b, 0x0a, 0x17, 0x57, + 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x4d, + 0x61, 0x74, 0x72, 0x69, 0x78, 0x36, 0x10, 0x02, 0x22, 0x1d, 0x0a, 0x07, 0x57, 0x6f, 0x72, 0x64, + 0x41, 0x63, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x01, 0x20, 0x02, 0x28, + 0x09, 0x52, 0x04, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x30, 0x0a, 0x0d, 0x53, 0x65, 0x74, 0x55, 0x32, + 0x46, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x75, 0x32, 0x66, 0x5f, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x75, + 0x32, 0x66, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x42, 0x79, 0x0a, 0x23, 0x63, 0x6f, 0x6d, + 0x2e, 0x73, 0x61, 0x74, 0x6f, 0x73, 0x68, 0x69, 0x6c, 0x61, 0x62, 0x73, 0x2e, 0x74, 0x72, 0x65, + 0x7a, 0x6f, 0x72, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x42, 0x17, 0x54, 0x72, 0x65, 0x7a, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4d, + 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2f, 0x67, + 0x6f, 0x2d, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2f, 0x61, 0x63, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x73, 0x2f, 0x75, 0x73, 0x62, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2f, 0x74, 0x72, + 0x65, 0x7a, 0x6f, 0x72, +} + +var ( + file_messages_management_proto_rawDescOnce sync.Once + file_messages_management_proto_rawDescData = file_messages_management_proto_rawDesc +) + +func file_messages_management_proto_rawDescGZIP() []byte { + file_messages_management_proto_rawDescOnce.Do(func() { + file_messages_management_proto_rawDescData = protoimpl.X.CompressGZIP(file_messages_management_proto_rawDescData) + }) + return file_messages_management_proto_rawDescData +} + +var file_messages_management_proto_enumTypes = make([]protoimpl.EnumInfo, 3) +var file_messages_management_proto_msgTypes = make([]protoimpl.MessageInfo, 21) +var file_messages_management_proto_goTypes = []any{ + (ApplySettings_PassphraseSourceType)(0), // 0: hw.trezor.messages.management.ApplySettings.PassphraseSourceType + (RecoveryDevice_RecoveryDeviceType)(0), // 1: hw.trezor.messages.management.RecoveryDevice.RecoveryDeviceType + (WordRequest_WordRequestType)(0), // 2: hw.trezor.messages.management.WordRequest.WordRequestType + (*Initialize)(nil), // 3: hw.trezor.messages.management.Initialize + (*GetFeatures)(nil), // 4: hw.trezor.messages.management.GetFeatures + (*Features)(nil), // 5: hw.trezor.messages.management.Features + (*ClearSession)(nil), // 6: hw.trezor.messages.management.ClearSession + (*ApplySettings)(nil), // 7: hw.trezor.messages.management.ApplySettings + (*ApplyFlags)(nil), // 8: hw.trezor.messages.management.ApplyFlags + (*ChangePin)(nil), // 9: hw.trezor.messages.management.ChangePin + (*Ping)(nil), // 10: hw.trezor.messages.management.Ping + (*Cancel)(nil), // 11: hw.trezor.messages.management.Cancel + (*GetEntropy)(nil), // 12: hw.trezor.messages.management.GetEntropy + (*Entropy)(nil), // 13: hw.trezor.messages.management.Entropy + (*WipeDevice)(nil), // 14: hw.trezor.messages.management.WipeDevice + (*LoadDevice)(nil), // 15: hw.trezor.messages.management.LoadDevice + (*ResetDevice)(nil), // 16: hw.trezor.messages.management.ResetDevice + (*BackupDevice)(nil), // 17: hw.trezor.messages.management.BackupDevice + (*EntropyRequest)(nil), // 18: hw.trezor.messages.management.EntropyRequest + (*EntropyAck)(nil), // 19: hw.trezor.messages.management.EntropyAck + (*RecoveryDevice)(nil), // 20: hw.trezor.messages.management.RecoveryDevice + (*WordRequest)(nil), // 21: hw.trezor.messages.management.WordRequest + (*WordAck)(nil), // 22: hw.trezor.messages.management.WordAck + (*SetU2FCounter)(nil), // 23: hw.trezor.messages.management.SetU2FCounter + (*HDNodeType)(nil), // 24: hw.trezor.messages.common.HDNodeType +} +var file_messages_management_proto_depIdxs = []int32{ + 0, // 0: hw.trezor.messages.management.ApplySettings.passphrase_source:type_name -> hw.trezor.messages.management.ApplySettings.PassphraseSourceType + 24, // 1: hw.trezor.messages.management.LoadDevice.node:type_name -> hw.trezor.messages.common.HDNodeType + 1, // 2: hw.trezor.messages.management.RecoveryDevice.type:type_name -> hw.trezor.messages.management.RecoveryDevice.RecoveryDeviceType + 2, // 3: hw.trezor.messages.management.WordRequest.type:type_name -> hw.trezor.messages.management.WordRequest.WordRequestType + 4, // [4:4] is the sub-list for method output_type + 4, // [4:4] is the sub-list for method input_type + 4, // [4:4] is the sub-list for extension type_name + 4, // [4:4] is the sub-list for extension extendee + 0, // [0:4] is the sub-list for field type_name +} + +func init() { file_messages_management_proto_init() } +func file_messages_management_proto_init() { + if File_messages_management_proto != nil { + return + } + file_messages_common_proto_init() + if !protoimpl.UnsafeEnabled { + file_messages_management_proto_msgTypes[0].Exporter = func(v any, i int) any { + switch v := v.(*Initialize); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_management_proto_msgTypes[1].Exporter = func(v any, i int) any { + switch v := v.(*GetFeatures); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_management_proto_msgTypes[2].Exporter = func(v any, i int) any { + switch v := v.(*Features); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_management_proto_msgTypes[3].Exporter = func(v any, i int) any { + switch v := v.(*ClearSession); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_management_proto_msgTypes[4].Exporter = func(v any, i int) any { + switch v := v.(*ApplySettings); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_management_proto_msgTypes[5].Exporter = func(v any, i int) any { + switch v := v.(*ApplyFlags); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_management_proto_msgTypes[6].Exporter = func(v any, i int) any { + switch v := v.(*ChangePin); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_management_proto_msgTypes[7].Exporter = func(v any, i int) any { + switch v := v.(*Ping); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_management_proto_msgTypes[8].Exporter = func(v any, i int) any { + switch v := v.(*Cancel); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_management_proto_msgTypes[9].Exporter = func(v any, i int) any { + switch v := v.(*GetEntropy); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_management_proto_msgTypes[10].Exporter = func(v any, i int) any { + switch v := v.(*Entropy); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_management_proto_msgTypes[11].Exporter = func(v any, i int) any { + switch v := v.(*WipeDevice); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_management_proto_msgTypes[12].Exporter = func(v any, i int) any { + switch v := v.(*LoadDevice); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_management_proto_msgTypes[13].Exporter = func(v any, i int) any { + switch v := v.(*ResetDevice); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_management_proto_msgTypes[14].Exporter = func(v any, i int) any { + switch v := v.(*BackupDevice); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_management_proto_msgTypes[15].Exporter = func(v any, i int) any { + switch v := v.(*EntropyRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_management_proto_msgTypes[16].Exporter = func(v any, i int) any { + switch v := v.(*EntropyAck); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_management_proto_msgTypes[17].Exporter = func(v any, i int) any { + switch v := v.(*RecoveryDevice); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_management_proto_msgTypes[18].Exporter = func(v any, i int) any { + switch v := v.(*WordRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_management_proto_msgTypes[19].Exporter = func(v any, i int) any { + switch v := v.(*WordAck); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_management_proto_msgTypes[20].Exporter = func(v any, i int) any { + switch v := v.(*SetU2FCounter); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_messages_management_proto_rawDesc, + NumEnums: 3, + NumMessages: 21, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_messages_management_proto_goTypes, + DependencyIndexes: file_messages_management_proto_depIdxs, + EnumInfos: file_messages_management_proto_enumTypes, + MessageInfos: file_messages_management_proto_msgTypes, + }.Build() + File_messages_management_proto = out.File + file_messages_management_proto_rawDesc = nil + file_messages_management_proto_goTypes = nil + file_messages_management_proto_depIdxs = nil } diff --git a/accounts/usbwallet/trezor/messages-management.proto b/accounts/usbwallet/trezor/messages-management.proto index 0ab825a1bc..55eb58983e 100644 --- a/accounts/usbwallet/trezor/messages-management.proto +++ b/accounts/usbwallet/trezor/messages-management.proto @@ -5,6 +5,8 @@ syntax = "proto2"; package hw.trezor.messages.management; +option go_package = "github.com/ethereum/go-ethereum/accounts/usbwallet/trezor"; + // Sugar for easier handling in Java option java_package = "com.satoshilabs.trezor.lib.protobuf"; option java_outer_classname = "TrezorMessageManagement"; diff --git a/accounts/usbwallet/trezor/messages.pb.go b/accounts/usbwallet/trezor/messages.pb.go index af0c957144..4518db679e 100644 --- a/accounts/usbwallet/trezor/messages.pb.go +++ b/accounts/usbwallet/trezor/messages.pb.go @@ -1,26 +1,29 @@ +// This file originates from the SatoshiLabs Trezor `common` repository at: +// https://github.com/trezor/trezor-common/blob/master/protob/messages.proto +// dated 28.05.2019, commit 893fd219d4a01bcffa0cd9cfa631856371ec5aa9. + // Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.34.2 +// protoc v5.27.1 // source: messages.proto package trezor import ( - fmt "fmt" - math "math" - - proto "github.com/golang/protobuf/proto" - descriptor "github.com/golang/protobuf/protoc-gen-go/descriptor" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + descriptorpb "google.golang.org/protobuf/types/descriptorpb" + reflect "reflect" + sync "sync" ) -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) // * // Mapping between TREZOR wire identifier (uint) and a protobuf message @@ -241,397 +244,399 @@ const ( MessageType_MessageType_BinanceSignedTx MessageType = 709 ) -var MessageType_name = map[int32]string{ - 0: "MessageType_Initialize", - 1: "MessageType_Ping", - 2: "MessageType_Success", - 3: "MessageType_Failure", - 4: "MessageType_ChangePin", - 5: "MessageType_WipeDevice", - 9: "MessageType_GetEntropy", - 10: "MessageType_Entropy", - 13: "MessageType_LoadDevice", - 14: "MessageType_ResetDevice", - 17: "MessageType_Features", - 18: "MessageType_PinMatrixRequest", - 19: "MessageType_PinMatrixAck", - 20: "MessageType_Cancel", - 24: "MessageType_ClearSession", - 25: "MessageType_ApplySettings", - 26: "MessageType_ButtonRequest", - 27: "MessageType_ButtonAck", - 28: "MessageType_ApplyFlags", - 34: "MessageType_BackupDevice", - 35: "MessageType_EntropyRequest", - 36: "MessageType_EntropyAck", - 41: "MessageType_PassphraseRequest", - 42: "MessageType_PassphraseAck", - 77: "MessageType_PassphraseStateRequest", - 78: "MessageType_PassphraseStateAck", - 45: "MessageType_RecoveryDevice", - 46: "MessageType_WordRequest", - 47: "MessageType_WordAck", - 55: "MessageType_GetFeatures", - 63: "MessageType_SetU2FCounter", - 6: "MessageType_FirmwareErase", - 7: "MessageType_FirmwareUpload", - 8: "MessageType_FirmwareRequest", - 32: "MessageType_SelfTest", - 11: "MessageType_GetPublicKey", - 12: "MessageType_PublicKey", - 15: "MessageType_SignTx", - 21: "MessageType_TxRequest", - 22: "MessageType_TxAck", - 29: "MessageType_GetAddress", - 30: "MessageType_Address", - 38: "MessageType_SignMessage", - 39: "MessageType_VerifyMessage", - 40: "MessageType_MessageSignature", - 23: "MessageType_CipherKeyValue", - 48: "MessageType_CipheredKeyValue", - 53: "MessageType_SignIdentity", - 54: "MessageType_SignedIdentity", - 61: "MessageType_GetECDHSessionKey", - 62: "MessageType_ECDHSessionKey", - 71: "MessageType_CosiCommit", - 72: "MessageType_CosiCommitment", - 73: "MessageType_CosiSign", - 74: "MessageType_CosiSignature", - 100: "MessageType_DebugLinkDecision", - 101: "MessageType_DebugLinkGetState", - 102: "MessageType_DebugLinkState", - 103: "MessageType_DebugLinkStop", - 104: "MessageType_DebugLinkLog", - 110: "MessageType_DebugLinkMemoryRead", - 111: "MessageType_DebugLinkMemory", - 112: "MessageType_DebugLinkMemoryWrite", - 113: "MessageType_DebugLinkFlashErase", - 450: "MessageType_EthereumGetPublicKey", - 451: "MessageType_EthereumPublicKey", - 56: "MessageType_EthereumGetAddress", - 57: "MessageType_EthereumAddress", - 58: "MessageType_EthereumSignTx", - 59: "MessageType_EthereumTxRequest", - 60: "MessageType_EthereumTxAck", - 64: "MessageType_EthereumSignMessage", - 65: "MessageType_EthereumVerifyMessage", - 66: "MessageType_EthereumMessageSignature", - 67: "MessageType_NEMGetAddress", - 68: "MessageType_NEMAddress", - 69: "MessageType_NEMSignTx", - 70: "MessageType_NEMSignedTx", - 75: "MessageType_NEMDecryptMessage", - 76: "MessageType_NEMDecryptedMessage", - 114: "MessageType_LiskGetAddress", - 115: "MessageType_LiskAddress", - 116: "MessageType_LiskSignTx", - 117: "MessageType_LiskSignedTx", - 118: "MessageType_LiskSignMessage", - 119: "MessageType_LiskMessageSignature", - 120: "MessageType_LiskVerifyMessage", - 121: "MessageType_LiskGetPublicKey", - 122: "MessageType_LiskPublicKey", - 150: "MessageType_TezosGetAddress", - 151: "MessageType_TezosAddress", - 152: "MessageType_TezosSignTx", - 153: "MessageType_TezosSignedTx", - 154: "MessageType_TezosGetPublicKey", - 155: "MessageType_TezosPublicKey", - 202: "MessageType_StellarSignTx", - 203: "MessageType_StellarTxOpRequest", - 207: "MessageType_StellarGetAddress", - 208: "MessageType_StellarAddress", - 210: "MessageType_StellarCreateAccountOp", - 211: "MessageType_StellarPaymentOp", - 212: "MessageType_StellarPathPaymentOp", - 213: "MessageType_StellarManageOfferOp", - 214: "MessageType_StellarCreatePassiveOfferOp", - 215: "MessageType_StellarSetOptionsOp", - 216: "MessageType_StellarChangeTrustOp", - 217: "MessageType_StellarAllowTrustOp", - 218: "MessageType_StellarAccountMergeOp", - 220: "MessageType_StellarManageDataOp", - 221: "MessageType_StellarBumpSequenceOp", - 230: "MessageType_StellarSignedTx", - 250: "MessageType_TronGetAddress", - 251: "MessageType_TronAddress", - 252: "MessageType_TronSignTx", - 253: "MessageType_TronSignedTx", - 303: "MessageType_CardanoSignTx", - 304: "MessageType_CardanoTxRequest", - 305: "MessageType_CardanoGetPublicKey", - 306: "MessageType_CardanoPublicKey", - 307: "MessageType_CardanoGetAddress", - 308: "MessageType_CardanoAddress", - 309: "MessageType_CardanoTxAck", - 310: "MessageType_CardanoSignedTx", - 350: "MessageType_OntologyGetAddress", - 351: "MessageType_OntologyAddress", - 352: "MessageType_OntologyGetPublicKey", - 353: "MessageType_OntologyPublicKey", - 354: "MessageType_OntologySignTransfer", - 355: "MessageType_OntologySignedTransfer", - 356: "MessageType_OntologySignWithdrawOng", - 357: "MessageType_OntologySignedWithdrawOng", - 358: "MessageType_OntologySignOntIdRegister", - 359: "MessageType_OntologySignedOntIdRegister", - 360: "MessageType_OntologySignOntIdAddAttributes", - 361: "MessageType_OntologySignedOntIdAddAttributes", - 400: "MessageType_RippleGetAddress", - 401: "MessageType_RippleAddress", - 402: "MessageType_RippleSignTx", - 403: "MessageType_RippleSignedTx", - 501: "MessageType_MoneroTransactionInitRequest", - 502: "MessageType_MoneroTransactionInitAck", - 503: "MessageType_MoneroTransactionSetInputRequest", - 504: "MessageType_MoneroTransactionSetInputAck", - 505: "MessageType_MoneroTransactionInputsPermutationRequest", - 506: "MessageType_MoneroTransactionInputsPermutationAck", - 507: "MessageType_MoneroTransactionInputViniRequest", - 508: "MessageType_MoneroTransactionInputViniAck", - 509: "MessageType_MoneroTransactionAllInputsSetRequest", - 510: "MessageType_MoneroTransactionAllInputsSetAck", - 511: "MessageType_MoneroTransactionSetOutputRequest", - 512: "MessageType_MoneroTransactionSetOutputAck", - 513: "MessageType_MoneroTransactionAllOutSetRequest", - 514: "MessageType_MoneroTransactionAllOutSetAck", - 515: "MessageType_MoneroTransactionSignInputRequest", - 516: "MessageType_MoneroTransactionSignInputAck", - 517: "MessageType_MoneroTransactionFinalRequest", - 518: "MessageType_MoneroTransactionFinalAck", - 530: "MessageType_MoneroKeyImageExportInitRequest", - 531: "MessageType_MoneroKeyImageExportInitAck", - 532: "MessageType_MoneroKeyImageSyncStepRequest", - 533: "MessageType_MoneroKeyImageSyncStepAck", - 534: "MessageType_MoneroKeyImageSyncFinalRequest", - 535: "MessageType_MoneroKeyImageSyncFinalAck", - 540: "MessageType_MoneroGetAddress", - 541: "MessageType_MoneroAddress", - 542: "MessageType_MoneroGetWatchKey", - 543: "MessageType_MoneroWatchKey", - 546: "MessageType_DebugMoneroDiagRequest", - 547: "MessageType_DebugMoneroDiagAck", - 550: "MessageType_MoneroGetTxKeyRequest", - 551: "MessageType_MoneroGetTxKeyAck", - 552: "MessageType_MoneroLiveRefreshStartRequest", - 553: "MessageType_MoneroLiveRefreshStartAck", - 554: "MessageType_MoneroLiveRefreshStepRequest", - 555: "MessageType_MoneroLiveRefreshStepAck", - 556: "MessageType_MoneroLiveRefreshFinalRequest", - 557: "MessageType_MoneroLiveRefreshFinalAck", - 600: "MessageType_EosGetPublicKey", - 601: "MessageType_EosPublicKey", - 602: "MessageType_EosSignTx", - 603: "MessageType_EosTxActionRequest", - 604: "MessageType_EosTxActionAck", - 605: "MessageType_EosSignedTx", - 700: "MessageType_BinanceGetAddress", - 701: "MessageType_BinanceAddress", - 702: "MessageType_BinanceGetPublicKey", - 703: "MessageType_BinancePublicKey", - 704: "MessageType_BinanceSignTx", - 705: "MessageType_BinanceTxRequest", - 706: "MessageType_BinanceTransferMsg", - 707: "MessageType_BinanceOrderMsg", - 708: "MessageType_BinanceCancelMsg", - 709: "MessageType_BinanceSignedTx", -} - -var MessageType_value = map[string]int32{ - "MessageType_Initialize": 0, - "MessageType_Ping": 1, - "MessageType_Success": 2, - "MessageType_Failure": 3, - "MessageType_ChangePin": 4, - "MessageType_WipeDevice": 5, - "MessageType_GetEntropy": 9, - "MessageType_Entropy": 10, - "MessageType_LoadDevice": 13, - "MessageType_ResetDevice": 14, - "MessageType_Features": 17, - "MessageType_PinMatrixRequest": 18, - "MessageType_PinMatrixAck": 19, - "MessageType_Cancel": 20, - "MessageType_ClearSession": 24, - "MessageType_ApplySettings": 25, - "MessageType_ButtonRequest": 26, - "MessageType_ButtonAck": 27, - "MessageType_ApplyFlags": 28, - "MessageType_BackupDevice": 34, - "MessageType_EntropyRequest": 35, - "MessageType_EntropyAck": 36, - "MessageType_PassphraseRequest": 41, - "MessageType_PassphraseAck": 42, - "MessageType_PassphraseStateRequest": 77, - "MessageType_PassphraseStateAck": 78, - "MessageType_RecoveryDevice": 45, - "MessageType_WordRequest": 46, - "MessageType_WordAck": 47, - "MessageType_GetFeatures": 55, - "MessageType_SetU2FCounter": 63, - "MessageType_FirmwareErase": 6, - "MessageType_FirmwareUpload": 7, - "MessageType_FirmwareRequest": 8, - "MessageType_SelfTest": 32, - "MessageType_GetPublicKey": 11, - "MessageType_PublicKey": 12, - "MessageType_SignTx": 15, - "MessageType_TxRequest": 21, - "MessageType_TxAck": 22, - "MessageType_GetAddress": 29, - "MessageType_Address": 30, - "MessageType_SignMessage": 38, - "MessageType_VerifyMessage": 39, - "MessageType_MessageSignature": 40, - "MessageType_CipherKeyValue": 23, - "MessageType_CipheredKeyValue": 48, - "MessageType_SignIdentity": 53, - "MessageType_SignedIdentity": 54, - "MessageType_GetECDHSessionKey": 61, - "MessageType_ECDHSessionKey": 62, - "MessageType_CosiCommit": 71, - "MessageType_CosiCommitment": 72, - "MessageType_CosiSign": 73, - "MessageType_CosiSignature": 74, - "MessageType_DebugLinkDecision": 100, - "MessageType_DebugLinkGetState": 101, - "MessageType_DebugLinkState": 102, - "MessageType_DebugLinkStop": 103, - "MessageType_DebugLinkLog": 104, - "MessageType_DebugLinkMemoryRead": 110, - "MessageType_DebugLinkMemory": 111, - "MessageType_DebugLinkMemoryWrite": 112, - "MessageType_DebugLinkFlashErase": 113, - "MessageType_EthereumGetPublicKey": 450, - "MessageType_EthereumPublicKey": 451, - "MessageType_EthereumGetAddress": 56, - "MessageType_EthereumAddress": 57, - "MessageType_EthereumSignTx": 58, - "MessageType_EthereumTxRequest": 59, - "MessageType_EthereumTxAck": 60, - "MessageType_EthereumSignMessage": 64, - "MessageType_EthereumVerifyMessage": 65, - "MessageType_EthereumMessageSignature": 66, - "MessageType_NEMGetAddress": 67, - "MessageType_NEMAddress": 68, - "MessageType_NEMSignTx": 69, - "MessageType_NEMSignedTx": 70, - "MessageType_NEMDecryptMessage": 75, - "MessageType_NEMDecryptedMessage": 76, - "MessageType_LiskGetAddress": 114, - "MessageType_LiskAddress": 115, - "MessageType_LiskSignTx": 116, - "MessageType_LiskSignedTx": 117, - "MessageType_LiskSignMessage": 118, - "MessageType_LiskMessageSignature": 119, - "MessageType_LiskVerifyMessage": 120, - "MessageType_LiskGetPublicKey": 121, - "MessageType_LiskPublicKey": 122, - "MessageType_TezosGetAddress": 150, - "MessageType_TezosAddress": 151, - "MessageType_TezosSignTx": 152, - "MessageType_TezosSignedTx": 153, - "MessageType_TezosGetPublicKey": 154, - "MessageType_TezosPublicKey": 155, - "MessageType_StellarSignTx": 202, - "MessageType_StellarTxOpRequest": 203, - "MessageType_StellarGetAddress": 207, - "MessageType_StellarAddress": 208, - "MessageType_StellarCreateAccountOp": 210, - "MessageType_StellarPaymentOp": 211, - "MessageType_StellarPathPaymentOp": 212, - "MessageType_StellarManageOfferOp": 213, - "MessageType_StellarCreatePassiveOfferOp": 214, - "MessageType_StellarSetOptionsOp": 215, - "MessageType_StellarChangeTrustOp": 216, - "MessageType_StellarAllowTrustOp": 217, - "MessageType_StellarAccountMergeOp": 218, - "MessageType_StellarManageDataOp": 220, - "MessageType_StellarBumpSequenceOp": 221, - "MessageType_StellarSignedTx": 230, - "MessageType_TronGetAddress": 250, - "MessageType_TronAddress": 251, - "MessageType_TronSignTx": 252, - "MessageType_TronSignedTx": 253, - "MessageType_CardanoSignTx": 303, - "MessageType_CardanoTxRequest": 304, - "MessageType_CardanoGetPublicKey": 305, - "MessageType_CardanoPublicKey": 306, - "MessageType_CardanoGetAddress": 307, - "MessageType_CardanoAddress": 308, - "MessageType_CardanoTxAck": 309, - "MessageType_CardanoSignedTx": 310, - "MessageType_OntologyGetAddress": 350, - "MessageType_OntologyAddress": 351, - "MessageType_OntologyGetPublicKey": 352, - "MessageType_OntologyPublicKey": 353, - "MessageType_OntologySignTransfer": 354, - "MessageType_OntologySignedTransfer": 355, - "MessageType_OntologySignWithdrawOng": 356, - "MessageType_OntologySignedWithdrawOng": 357, - "MessageType_OntologySignOntIdRegister": 358, - "MessageType_OntologySignedOntIdRegister": 359, - "MessageType_OntologySignOntIdAddAttributes": 360, - "MessageType_OntologySignedOntIdAddAttributes": 361, - "MessageType_RippleGetAddress": 400, - "MessageType_RippleAddress": 401, - "MessageType_RippleSignTx": 402, - "MessageType_RippleSignedTx": 403, - "MessageType_MoneroTransactionInitRequest": 501, - "MessageType_MoneroTransactionInitAck": 502, - "MessageType_MoneroTransactionSetInputRequest": 503, - "MessageType_MoneroTransactionSetInputAck": 504, - "MessageType_MoneroTransactionInputsPermutationRequest": 505, - "MessageType_MoneroTransactionInputsPermutationAck": 506, - "MessageType_MoneroTransactionInputViniRequest": 507, - "MessageType_MoneroTransactionInputViniAck": 508, - "MessageType_MoneroTransactionAllInputsSetRequest": 509, - "MessageType_MoneroTransactionAllInputsSetAck": 510, - "MessageType_MoneroTransactionSetOutputRequest": 511, - "MessageType_MoneroTransactionSetOutputAck": 512, - "MessageType_MoneroTransactionAllOutSetRequest": 513, - "MessageType_MoneroTransactionAllOutSetAck": 514, - "MessageType_MoneroTransactionSignInputRequest": 515, - "MessageType_MoneroTransactionSignInputAck": 516, - "MessageType_MoneroTransactionFinalRequest": 517, - "MessageType_MoneroTransactionFinalAck": 518, - "MessageType_MoneroKeyImageExportInitRequest": 530, - "MessageType_MoneroKeyImageExportInitAck": 531, - "MessageType_MoneroKeyImageSyncStepRequest": 532, - "MessageType_MoneroKeyImageSyncStepAck": 533, - "MessageType_MoneroKeyImageSyncFinalRequest": 534, - "MessageType_MoneroKeyImageSyncFinalAck": 535, - "MessageType_MoneroGetAddress": 540, - "MessageType_MoneroAddress": 541, - "MessageType_MoneroGetWatchKey": 542, - "MessageType_MoneroWatchKey": 543, - "MessageType_DebugMoneroDiagRequest": 546, - "MessageType_DebugMoneroDiagAck": 547, - "MessageType_MoneroGetTxKeyRequest": 550, - "MessageType_MoneroGetTxKeyAck": 551, - "MessageType_MoneroLiveRefreshStartRequest": 552, - "MessageType_MoneroLiveRefreshStartAck": 553, - "MessageType_MoneroLiveRefreshStepRequest": 554, - "MessageType_MoneroLiveRefreshStepAck": 555, - "MessageType_MoneroLiveRefreshFinalRequest": 556, - "MessageType_MoneroLiveRefreshFinalAck": 557, - "MessageType_EosGetPublicKey": 600, - "MessageType_EosPublicKey": 601, - "MessageType_EosSignTx": 602, - "MessageType_EosTxActionRequest": 603, - "MessageType_EosTxActionAck": 604, - "MessageType_EosSignedTx": 605, - "MessageType_BinanceGetAddress": 700, - "MessageType_BinanceAddress": 701, - "MessageType_BinanceGetPublicKey": 702, - "MessageType_BinancePublicKey": 703, - "MessageType_BinanceSignTx": 704, - "MessageType_BinanceTxRequest": 705, - "MessageType_BinanceTransferMsg": 706, - "MessageType_BinanceOrderMsg": 707, - "MessageType_BinanceCancelMsg": 708, - "MessageType_BinanceSignedTx": 709, -} +// Enum value maps for MessageType. +var ( + MessageType_name = map[int32]string{ + 0: "MessageType_Initialize", + 1: "MessageType_Ping", + 2: "MessageType_Success", + 3: "MessageType_Failure", + 4: "MessageType_ChangePin", + 5: "MessageType_WipeDevice", + 9: "MessageType_GetEntropy", + 10: "MessageType_Entropy", + 13: "MessageType_LoadDevice", + 14: "MessageType_ResetDevice", + 17: "MessageType_Features", + 18: "MessageType_PinMatrixRequest", + 19: "MessageType_PinMatrixAck", + 20: "MessageType_Cancel", + 24: "MessageType_ClearSession", + 25: "MessageType_ApplySettings", + 26: "MessageType_ButtonRequest", + 27: "MessageType_ButtonAck", + 28: "MessageType_ApplyFlags", + 34: "MessageType_BackupDevice", + 35: "MessageType_EntropyRequest", + 36: "MessageType_EntropyAck", + 41: "MessageType_PassphraseRequest", + 42: "MessageType_PassphraseAck", + 77: "MessageType_PassphraseStateRequest", + 78: "MessageType_PassphraseStateAck", + 45: "MessageType_RecoveryDevice", + 46: "MessageType_WordRequest", + 47: "MessageType_WordAck", + 55: "MessageType_GetFeatures", + 63: "MessageType_SetU2FCounter", + 6: "MessageType_FirmwareErase", + 7: "MessageType_FirmwareUpload", + 8: "MessageType_FirmwareRequest", + 32: "MessageType_SelfTest", + 11: "MessageType_GetPublicKey", + 12: "MessageType_PublicKey", + 15: "MessageType_SignTx", + 21: "MessageType_TxRequest", + 22: "MessageType_TxAck", + 29: "MessageType_GetAddress", + 30: "MessageType_Address", + 38: "MessageType_SignMessage", + 39: "MessageType_VerifyMessage", + 40: "MessageType_MessageSignature", + 23: "MessageType_CipherKeyValue", + 48: "MessageType_CipheredKeyValue", + 53: "MessageType_SignIdentity", + 54: "MessageType_SignedIdentity", + 61: "MessageType_GetECDHSessionKey", + 62: "MessageType_ECDHSessionKey", + 71: "MessageType_CosiCommit", + 72: "MessageType_CosiCommitment", + 73: "MessageType_CosiSign", + 74: "MessageType_CosiSignature", + 100: "MessageType_DebugLinkDecision", + 101: "MessageType_DebugLinkGetState", + 102: "MessageType_DebugLinkState", + 103: "MessageType_DebugLinkStop", + 104: "MessageType_DebugLinkLog", + 110: "MessageType_DebugLinkMemoryRead", + 111: "MessageType_DebugLinkMemory", + 112: "MessageType_DebugLinkMemoryWrite", + 113: "MessageType_DebugLinkFlashErase", + 450: "MessageType_EthereumGetPublicKey", + 451: "MessageType_EthereumPublicKey", + 56: "MessageType_EthereumGetAddress", + 57: "MessageType_EthereumAddress", + 58: "MessageType_EthereumSignTx", + 59: "MessageType_EthereumTxRequest", + 60: "MessageType_EthereumTxAck", + 64: "MessageType_EthereumSignMessage", + 65: "MessageType_EthereumVerifyMessage", + 66: "MessageType_EthereumMessageSignature", + 67: "MessageType_NEMGetAddress", + 68: "MessageType_NEMAddress", + 69: "MessageType_NEMSignTx", + 70: "MessageType_NEMSignedTx", + 75: "MessageType_NEMDecryptMessage", + 76: "MessageType_NEMDecryptedMessage", + 114: "MessageType_LiskGetAddress", + 115: "MessageType_LiskAddress", + 116: "MessageType_LiskSignTx", + 117: "MessageType_LiskSignedTx", + 118: "MessageType_LiskSignMessage", + 119: "MessageType_LiskMessageSignature", + 120: "MessageType_LiskVerifyMessage", + 121: "MessageType_LiskGetPublicKey", + 122: "MessageType_LiskPublicKey", + 150: "MessageType_TezosGetAddress", + 151: "MessageType_TezosAddress", + 152: "MessageType_TezosSignTx", + 153: "MessageType_TezosSignedTx", + 154: "MessageType_TezosGetPublicKey", + 155: "MessageType_TezosPublicKey", + 202: "MessageType_StellarSignTx", + 203: "MessageType_StellarTxOpRequest", + 207: "MessageType_StellarGetAddress", + 208: "MessageType_StellarAddress", + 210: "MessageType_StellarCreateAccountOp", + 211: "MessageType_StellarPaymentOp", + 212: "MessageType_StellarPathPaymentOp", + 213: "MessageType_StellarManageOfferOp", + 214: "MessageType_StellarCreatePassiveOfferOp", + 215: "MessageType_StellarSetOptionsOp", + 216: "MessageType_StellarChangeTrustOp", + 217: "MessageType_StellarAllowTrustOp", + 218: "MessageType_StellarAccountMergeOp", + 220: "MessageType_StellarManageDataOp", + 221: "MessageType_StellarBumpSequenceOp", + 230: "MessageType_StellarSignedTx", + 250: "MessageType_TronGetAddress", + 251: "MessageType_TronAddress", + 252: "MessageType_TronSignTx", + 253: "MessageType_TronSignedTx", + 303: "MessageType_CardanoSignTx", + 304: "MessageType_CardanoTxRequest", + 305: "MessageType_CardanoGetPublicKey", + 306: "MessageType_CardanoPublicKey", + 307: "MessageType_CardanoGetAddress", + 308: "MessageType_CardanoAddress", + 309: "MessageType_CardanoTxAck", + 310: "MessageType_CardanoSignedTx", + 350: "MessageType_OntologyGetAddress", + 351: "MessageType_OntologyAddress", + 352: "MessageType_OntologyGetPublicKey", + 353: "MessageType_OntologyPublicKey", + 354: "MessageType_OntologySignTransfer", + 355: "MessageType_OntologySignedTransfer", + 356: "MessageType_OntologySignWithdrawOng", + 357: "MessageType_OntologySignedWithdrawOng", + 358: "MessageType_OntologySignOntIdRegister", + 359: "MessageType_OntologySignedOntIdRegister", + 360: "MessageType_OntologySignOntIdAddAttributes", + 361: "MessageType_OntologySignedOntIdAddAttributes", + 400: "MessageType_RippleGetAddress", + 401: "MessageType_RippleAddress", + 402: "MessageType_RippleSignTx", + 403: "MessageType_RippleSignedTx", + 501: "MessageType_MoneroTransactionInitRequest", + 502: "MessageType_MoneroTransactionInitAck", + 503: "MessageType_MoneroTransactionSetInputRequest", + 504: "MessageType_MoneroTransactionSetInputAck", + 505: "MessageType_MoneroTransactionInputsPermutationRequest", + 506: "MessageType_MoneroTransactionInputsPermutationAck", + 507: "MessageType_MoneroTransactionInputViniRequest", + 508: "MessageType_MoneroTransactionInputViniAck", + 509: "MessageType_MoneroTransactionAllInputsSetRequest", + 510: "MessageType_MoneroTransactionAllInputsSetAck", + 511: "MessageType_MoneroTransactionSetOutputRequest", + 512: "MessageType_MoneroTransactionSetOutputAck", + 513: "MessageType_MoneroTransactionAllOutSetRequest", + 514: "MessageType_MoneroTransactionAllOutSetAck", + 515: "MessageType_MoneroTransactionSignInputRequest", + 516: "MessageType_MoneroTransactionSignInputAck", + 517: "MessageType_MoneroTransactionFinalRequest", + 518: "MessageType_MoneroTransactionFinalAck", + 530: "MessageType_MoneroKeyImageExportInitRequest", + 531: "MessageType_MoneroKeyImageExportInitAck", + 532: "MessageType_MoneroKeyImageSyncStepRequest", + 533: "MessageType_MoneroKeyImageSyncStepAck", + 534: "MessageType_MoneroKeyImageSyncFinalRequest", + 535: "MessageType_MoneroKeyImageSyncFinalAck", + 540: "MessageType_MoneroGetAddress", + 541: "MessageType_MoneroAddress", + 542: "MessageType_MoneroGetWatchKey", + 543: "MessageType_MoneroWatchKey", + 546: "MessageType_DebugMoneroDiagRequest", + 547: "MessageType_DebugMoneroDiagAck", + 550: "MessageType_MoneroGetTxKeyRequest", + 551: "MessageType_MoneroGetTxKeyAck", + 552: "MessageType_MoneroLiveRefreshStartRequest", + 553: "MessageType_MoneroLiveRefreshStartAck", + 554: "MessageType_MoneroLiveRefreshStepRequest", + 555: "MessageType_MoneroLiveRefreshStepAck", + 556: "MessageType_MoneroLiveRefreshFinalRequest", + 557: "MessageType_MoneroLiveRefreshFinalAck", + 600: "MessageType_EosGetPublicKey", + 601: "MessageType_EosPublicKey", + 602: "MessageType_EosSignTx", + 603: "MessageType_EosTxActionRequest", + 604: "MessageType_EosTxActionAck", + 605: "MessageType_EosSignedTx", + 700: "MessageType_BinanceGetAddress", + 701: "MessageType_BinanceAddress", + 702: "MessageType_BinanceGetPublicKey", + 703: "MessageType_BinancePublicKey", + 704: "MessageType_BinanceSignTx", + 705: "MessageType_BinanceTxRequest", + 706: "MessageType_BinanceTransferMsg", + 707: "MessageType_BinanceOrderMsg", + 708: "MessageType_BinanceCancelMsg", + 709: "MessageType_BinanceSignedTx", + } + MessageType_value = map[string]int32{ + "MessageType_Initialize": 0, + "MessageType_Ping": 1, + "MessageType_Success": 2, + "MessageType_Failure": 3, + "MessageType_ChangePin": 4, + "MessageType_WipeDevice": 5, + "MessageType_GetEntropy": 9, + "MessageType_Entropy": 10, + "MessageType_LoadDevice": 13, + "MessageType_ResetDevice": 14, + "MessageType_Features": 17, + "MessageType_PinMatrixRequest": 18, + "MessageType_PinMatrixAck": 19, + "MessageType_Cancel": 20, + "MessageType_ClearSession": 24, + "MessageType_ApplySettings": 25, + "MessageType_ButtonRequest": 26, + "MessageType_ButtonAck": 27, + "MessageType_ApplyFlags": 28, + "MessageType_BackupDevice": 34, + "MessageType_EntropyRequest": 35, + "MessageType_EntropyAck": 36, + "MessageType_PassphraseRequest": 41, + "MessageType_PassphraseAck": 42, + "MessageType_PassphraseStateRequest": 77, + "MessageType_PassphraseStateAck": 78, + "MessageType_RecoveryDevice": 45, + "MessageType_WordRequest": 46, + "MessageType_WordAck": 47, + "MessageType_GetFeatures": 55, + "MessageType_SetU2FCounter": 63, + "MessageType_FirmwareErase": 6, + "MessageType_FirmwareUpload": 7, + "MessageType_FirmwareRequest": 8, + "MessageType_SelfTest": 32, + "MessageType_GetPublicKey": 11, + "MessageType_PublicKey": 12, + "MessageType_SignTx": 15, + "MessageType_TxRequest": 21, + "MessageType_TxAck": 22, + "MessageType_GetAddress": 29, + "MessageType_Address": 30, + "MessageType_SignMessage": 38, + "MessageType_VerifyMessage": 39, + "MessageType_MessageSignature": 40, + "MessageType_CipherKeyValue": 23, + "MessageType_CipheredKeyValue": 48, + "MessageType_SignIdentity": 53, + "MessageType_SignedIdentity": 54, + "MessageType_GetECDHSessionKey": 61, + "MessageType_ECDHSessionKey": 62, + "MessageType_CosiCommit": 71, + "MessageType_CosiCommitment": 72, + "MessageType_CosiSign": 73, + "MessageType_CosiSignature": 74, + "MessageType_DebugLinkDecision": 100, + "MessageType_DebugLinkGetState": 101, + "MessageType_DebugLinkState": 102, + "MessageType_DebugLinkStop": 103, + "MessageType_DebugLinkLog": 104, + "MessageType_DebugLinkMemoryRead": 110, + "MessageType_DebugLinkMemory": 111, + "MessageType_DebugLinkMemoryWrite": 112, + "MessageType_DebugLinkFlashErase": 113, + "MessageType_EthereumGetPublicKey": 450, + "MessageType_EthereumPublicKey": 451, + "MessageType_EthereumGetAddress": 56, + "MessageType_EthereumAddress": 57, + "MessageType_EthereumSignTx": 58, + "MessageType_EthereumTxRequest": 59, + "MessageType_EthereumTxAck": 60, + "MessageType_EthereumSignMessage": 64, + "MessageType_EthereumVerifyMessage": 65, + "MessageType_EthereumMessageSignature": 66, + "MessageType_NEMGetAddress": 67, + "MessageType_NEMAddress": 68, + "MessageType_NEMSignTx": 69, + "MessageType_NEMSignedTx": 70, + "MessageType_NEMDecryptMessage": 75, + "MessageType_NEMDecryptedMessage": 76, + "MessageType_LiskGetAddress": 114, + "MessageType_LiskAddress": 115, + "MessageType_LiskSignTx": 116, + "MessageType_LiskSignedTx": 117, + "MessageType_LiskSignMessage": 118, + "MessageType_LiskMessageSignature": 119, + "MessageType_LiskVerifyMessage": 120, + "MessageType_LiskGetPublicKey": 121, + "MessageType_LiskPublicKey": 122, + "MessageType_TezosGetAddress": 150, + "MessageType_TezosAddress": 151, + "MessageType_TezosSignTx": 152, + "MessageType_TezosSignedTx": 153, + "MessageType_TezosGetPublicKey": 154, + "MessageType_TezosPublicKey": 155, + "MessageType_StellarSignTx": 202, + "MessageType_StellarTxOpRequest": 203, + "MessageType_StellarGetAddress": 207, + "MessageType_StellarAddress": 208, + "MessageType_StellarCreateAccountOp": 210, + "MessageType_StellarPaymentOp": 211, + "MessageType_StellarPathPaymentOp": 212, + "MessageType_StellarManageOfferOp": 213, + "MessageType_StellarCreatePassiveOfferOp": 214, + "MessageType_StellarSetOptionsOp": 215, + "MessageType_StellarChangeTrustOp": 216, + "MessageType_StellarAllowTrustOp": 217, + "MessageType_StellarAccountMergeOp": 218, + "MessageType_StellarManageDataOp": 220, + "MessageType_StellarBumpSequenceOp": 221, + "MessageType_StellarSignedTx": 230, + "MessageType_TronGetAddress": 250, + "MessageType_TronAddress": 251, + "MessageType_TronSignTx": 252, + "MessageType_TronSignedTx": 253, + "MessageType_CardanoSignTx": 303, + "MessageType_CardanoTxRequest": 304, + "MessageType_CardanoGetPublicKey": 305, + "MessageType_CardanoPublicKey": 306, + "MessageType_CardanoGetAddress": 307, + "MessageType_CardanoAddress": 308, + "MessageType_CardanoTxAck": 309, + "MessageType_CardanoSignedTx": 310, + "MessageType_OntologyGetAddress": 350, + "MessageType_OntologyAddress": 351, + "MessageType_OntologyGetPublicKey": 352, + "MessageType_OntologyPublicKey": 353, + "MessageType_OntologySignTransfer": 354, + "MessageType_OntologySignedTransfer": 355, + "MessageType_OntologySignWithdrawOng": 356, + "MessageType_OntologySignedWithdrawOng": 357, + "MessageType_OntologySignOntIdRegister": 358, + "MessageType_OntologySignedOntIdRegister": 359, + "MessageType_OntologySignOntIdAddAttributes": 360, + "MessageType_OntologySignedOntIdAddAttributes": 361, + "MessageType_RippleGetAddress": 400, + "MessageType_RippleAddress": 401, + "MessageType_RippleSignTx": 402, + "MessageType_RippleSignedTx": 403, + "MessageType_MoneroTransactionInitRequest": 501, + "MessageType_MoneroTransactionInitAck": 502, + "MessageType_MoneroTransactionSetInputRequest": 503, + "MessageType_MoneroTransactionSetInputAck": 504, + "MessageType_MoneroTransactionInputsPermutationRequest": 505, + "MessageType_MoneroTransactionInputsPermutationAck": 506, + "MessageType_MoneroTransactionInputViniRequest": 507, + "MessageType_MoneroTransactionInputViniAck": 508, + "MessageType_MoneroTransactionAllInputsSetRequest": 509, + "MessageType_MoneroTransactionAllInputsSetAck": 510, + "MessageType_MoneroTransactionSetOutputRequest": 511, + "MessageType_MoneroTransactionSetOutputAck": 512, + "MessageType_MoneroTransactionAllOutSetRequest": 513, + "MessageType_MoneroTransactionAllOutSetAck": 514, + "MessageType_MoneroTransactionSignInputRequest": 515, + "MessageType_MoneroTransactionSignInputAck": 516, + "MessageType_MoneroTransactionFinalRequest": 517, + "MessageType_MoneroTransactionFinalAck": 518, + "MessageType_MoneroKeyImageExportInitRequest": 530, + "MessageType_MoneroKeyImageExportInitAck": 531, + "MessageType_MoneroKeyImageSyncStepRequest": 532, + "MessageType_MoneroKeyImageSyncStepAck": 533, + "MessageType_MoneroKeyImageSyncFinalRequest": 534, + "MessageType_MoneroKeyImageSyncFinalAck": 535, + "MessageType_MoneroGetAddress": 540, + "MessageType_MoneroAddress": 541, + "MessageType_MoneroGetWatchKey": 542, + "MessageType_MoneroWatchKey": 543, + "MessageType_DebugMoneroDiagRequest": 546, + "MessageType_DebugMoneroDiagAck": 547, + "MessageType_MoneroGetTxKeyRequest": 550, + "MessageType_MoneroGetTxKeyAck": 551, + "MessageType_MoneroLiveRefreshStartRequest": 552, + "MessageType_MoneroLiveRefreshStartAck": 553, + "MessageType_MoneroLiveRefreshStepRequest": 554, + "MessageType_MoneroLiveRefreshStepAck": 555, + "MessageType_MoneroLiveRefreshFinalRequest": 556, + "MessageType_MoneroLiveRefreshFinalAck": 557, + "MessageType_EosGetPublicKey": 600, + "MessageType_EosPublicKey": 601, + "MessageType_EosSignTx": 602, + "MessageType_EosTxActionRequest": 603, + "MessageType_EosTxActionAck": 604, + "MessageType_EosSignedTx": 605, + "MessageType_BinanceGetAddress": 700, + "MessageType_BinanceAddress": 701, + "MessageType_BinanceGetPublicKey": 702, + "MessageType_BinancePublicKey": 703, + "MessageType_BinanceSignTx": 704, + "MessageType_BinanceTxRequest": 705, + "MessageType_BinanceTransferMsg": 706, + "MessageType_BinanceOrderMsg": 707, + "MessageType_BinanceCancelMsg": 708, + "MessageType_BinanceSignedTx": 709, + } +) func (x MessageType) Enum() *MessageType { p := new(MessageType) @@ -640,250 +645,722 @@ func (x MessageType) Enum() *MessageType { } func (x MessageType) String() string { - return proto.EnumName(MessageType_name, int32(x)) + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (MessageType) Descriptor() protoreflect.EnumDescriptor { + return file_messages_proto_enumTypes[0].Descriptor() } -func (x *MessageType) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(MessageType_value, data, "MessageType") +func (MessageType) Type() protoreflect.EnumType { + return &file_messages_proto_enumTypes[0] +} + +func (x MessageType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Do not use. +func (x *MessageType) UnmarshalJSON(b []byte) error { + num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b) if err != nil { return err } - *x = MessageType(value) + *x = MessageType(num) return nil } +// Deprecated: Use MessageType.Descriptor instead. func (MessageType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_4dc296cbfe5ffcd5, []int{0} + return file_messages_proto_rawDescGZIP(), []int{0} } -var E_WireIn = &proto.ExtensionDesc{ - ExtendedType: (*descriptor.EnumValueOptions)(nil), - ExtensionType: (*bool)(nil), - Field: 50002, - Name: "hw.trezor.messages.wire_in", - Tag: "varint,50002,opt,name=wire_in", - Filename: "messages.proto", +var file_messages_proto_extTypes = []protoimpl.ExtensionInfo{ + { + ExtendedType: (*descriptorpb.EnumValueOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 50002, + Name: "hw.trezor.messages.wire_in", + Tag: "varint,50002,opt,name=wire_in", + Filename: "messages.proto", + }, + { + ExtendedType: (*descriptorpb.EnumValueOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 50003, + Name: "hw.trezor.messages.wire_out", + Tag: "varint,50003,opt,name=wire_out", + Filename: "messages.proto", + }, + { + ExtendedType: (*descriptorpb.EnumValueOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 50004, + Name: "hw.trezor.messages.wire_debug_in", + Tag: "varint,50004,opt,name=wire_debug_in", + Filename: "messages.proto", + }, + { + ExtendedType: (*descriptorpb.EnumValueOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 50005, + Name: "hw.trezor.messages.wire_debug_out", + Tag: "varint,50005,opt,name=wire_debug_out", + Filename: "messages.proto", + }, + { + ExtendedType: (*descriptorpb.EnumValueOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 50006, + Name: "hw.trezor.messages.wire_tiny", + Tag: "varint,50006,opt,name=wire_tiny", + Filename: "messages.proto", + }, + { + ExtendedType: (*descriptorpb.EnumValueOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 50007, + Name: "hw.trezor.messages.wire_bootloader", + Tag: "varint,50007,opt,name=wire_bootloader", + Filename: "messages.proto", + }, + { + ExtendedType: (*descriptorpb.EnumValueOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 50008, + Name: "hw.trezor.messages.wire_no_fsm", + Tag: "varint,50008,opt,name=wire_no_fsm", + Filename: "messages.proto", + }, } -var E_WireOut = &proto.ExtensionDesc{ - ExtendedType: (*descriptor.EnumValueOptions)(nil), - ExtensionType: (*bool)(nil), - Field: 50003, - Name: "hw.trezor.messages.wire_out", - Tag: "varint,50003,opt,name=wire_out", - Filename: "messages.proto", -} +// Extension fields to descriptorpb.EnumValueOptions. +var ( + // optional bool wire_in = 50002; + E_WireIn = &file_messages_proto_extTypes[0] // message can be transmitted via wire from PC to TREZOR + // optional bool wire_out = 50003; + E_WireOut = &file_messages_proto_extTypes[1] // message can be transmitted via wire from TREZOR to PC + // optional bool wire_debug_in = 50004; + E_WireDebugIn = &file_messages_proto_extTypes[2] // message can be transmitted via debug wire from PC to TREZOR + // optional bool wire_debug_out = 50005; + E_WireDebugOut = &file_messages_proto_extTypes[3] // message can be transmitted via debug wire from TREZOR to PC + // optional bool wire_tiny = 50006; + E_WireTiny = &file_messages_proto_extTypes[4] // message is handled by TREZOR when the USB stack is in tiny mode + // optional bool wire_bootloader = 50007; + E_WireBootloader = &file_messages_proto_extTypes[5] // message is only handled by TREZOR Bootloader + // optional bool wire_no_fsm = 50008; + E_WireNoFsm = &file_messages_proto_extTypes[6] // message is not handled by TREZOR unless the USB stack is in tiny mode +) -var E_WireDebugIn = &proto.ExtensionDesc{ - ExtendedType: (*descriptor.EnumValueOptions)(nil), - ExtensionType: (*bool)(nil), - Field: 50004, - Name: "hw.trezor.messages.wire_debug_in", - Tag: "varint,50004,opt,name=wire_debug_in", - Filename: "messages.proto", -} +var File_messages_proto protoreflect.FileDescriptor -var E_WireDebugOut = &proto.ExtensionDesc{ - ExtendedType: (*descriptor.EnumValueOptions)(nil), - ExtensionType: (*bool)(nil), - Field: 50005, - Name: "hw.trezor.messages.wire_debug_out", - Tag: "varint,50005,opt,name=wire_debug_out", - Filename: "messages.proto", +var file_messages_proto_rawDesc = []byte{ + 0x0a, 0x0e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x12, 0x12, 0x68, 0x77, 0x2e, 0x74, 0x72, 0x65, 0x7a, 0x6f, 0x72, 0x2e, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x73, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2a, 0xb9, 0x3f, 0x0a, 0x0b, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x24, 0x0a, 0x16, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, + 0x10, 0x00, 0x1a, 0x08, 0x90, 0xb5, 0x18, 0x01, 0xb0, 0xb5, 0x18, 0x01, 0x12, 0x1a, 0x0a, 0x10, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x50, 0x69, 0x6e, 0x67, + 0x10, 0x01, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x1d, 0x0a, 0x13, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x10, + 0x02, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x1d, 0x0a, 0x13, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x10, 0x03, + 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x1f, 0x0a, 0x15, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x69, 0x6e, 0x10, + 0x04, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x20, 0x0a, 0x16, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x57, 0x69, 0x70, 0x65, 0x44, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x10, 0x05, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x20, 0x0a, 0x16, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, + 0x6f, 0x70, 0x79, 0x10, 0x09, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x1d, 0x0a, 0x13, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x45, 0x6e, 0x74, 0x72, 0x6f, + 0x70, 0x79, 0x10, 0x0a, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x20, 0x0a, 0x16, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x4c, 0x6f, 0x61, 0x64, 0x44, 0x65, + 0x76, 0x69, 0x63, 0x65, 0x10, 0x0d, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x21, 0x0a, 0x17, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x52, 0x65, 0x73, 0x65, + 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x10, 0x0e, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, + 0x1e, 0x0a, 0x14, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x46, + 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x10, 0x11, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, + 0x26, 0x0a, 0x1c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x50, + 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x10, + 0x12, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x2a, 0x0a, 0x18, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x50, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, + 0x41, 0x63, 0x6b, 0x10, 0x13, 0x1a, 0x0c, 0x90, 0xb5, 0x18, 0x01, 0xb0, 0xb5, 0x18, 0x01, 0xc0, + 0xb5, 0x18, 0x01, 0x12, 0x20, 0x0a, 0x12, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, + 0x70, 0x65, 0x5f, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x10, 0x14, 0x1a, 0x08, 0x90, 0xb5, 0x18, + 0x01, 0xb0, 0xb5, 0x18, 0x01, 0x12, 0x22, 0x0a, 0x18, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x54, 0x79, 0x70, 0x65, 0x5f, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x10, 0x18, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x23, 0x0a, 0x19, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x53, 0x65, + 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x10, 0x19, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x23, + 0x0a, 0x19, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x42, 0x75, + 0x74, 0x74, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x10, 0x1a, 0x1a, 0x04, 0x98, + 0xb5, 0x18, 0x01, 0x12, 0x27, 0x0a, 0x15, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, + 0x70, 0x65, 0x5f, 0x42, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x41, 0x63, 0x6b, 0x10, 0x1b, 0x1a, 0x0c, + 0x90, 0xb5, 0x18, 0x01, 0xb0, 0xb5, 0x18, 0x01, 0xc0, 0xb5, 0x18, 0x01, 0x12, 0x20, 0x0a, 0x16, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x41, 0x70, 0x70, 0x6c, + 0x79, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x10, 0x1c, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x22, + 0x0a, 0x18, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x42, 0x61, + 0x63, 0x6b, 0x75, 0x70, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x10, 0x22, 0x1a, 0x04, 0x90, 0xb5, + 0x18, 0x01, 0x12, 0x24, 0x0a, 0x1a, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, + 0x65, 0x5f, 0x45, 0x6e, 0x74, 0x72, 0x6f, 0x70, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x10, 0x23, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x20, 0x0a, 0x16, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x45, 0x6e, 0x74, 0x72, 0x6f, 0x70, 0x79, 0x41, + 0x63, 0x6b, 0x10, 0x24, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x27, 0x0a, 0x1d, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x50, 0x61, 0x73, 0x73, 0x70, 0x68, + 0x72, 0x61, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x10, 0x29, 0x1a, 0x04, 0x98, + 0xb5, 0x18, 0x01, 0x12, 0x2b, 0x0a, 0x19, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, + 0x70, 0x65, 0x5f, 0x50, 0x61, 0x73, 0x73, 0x70, 0x68, 0x72, 0x61, 0x73, 0x65, 0x41, 0x63, 0x6b, + 0x10, 0x2a, 0x1a, 0x0c, 0x90, 0xb5, 0x18, 0x01, 0xb0, 0xb5, 0x18, 0x01, 0xc0, 0xb5, 0x18, 0x01, + 0x12, 0x2c, 0x0a, 0x22, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, + 0x50, 0x61, 0x73, 0x73, 0x70, 0x68, 0x72, 0x61, 0x73, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x10, 0x4d, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x30, + 0x0a, 0x1e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x50, 0x61, + 0x73, 0x73, 0x70, 0x68, 0x72, 0x61, 0x73, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x41, 0x63, 0x6b, + 0x10, 0x4e, 0x1a, 0x0c, 0x90, 0xb5, 0x18, 0x01, 0xb0, 0xb5, 0x18, 0x01, 0xc0, 0xb5, 0x18, 0x01, + 0x12, 0x24, 0x0a, 0x1a, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, + 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x10, 0x2d, + 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x21, 0x0a, 0x17, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x57, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x10, 0x2e, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x1d, 0x0a, 0x13, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x57, 0x6f, 0x72, 0x64, 0x41, 0x63, 0x6b, + 0x10, 0x2f, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x21, 0x0a, 0x17, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x47, 0x65, 0x74, 0x46, 0x65, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x73, 0x10, 0x37, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x23, 0x0a, 0x19, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x53, 0x65, 0x74, 0x55, 0x32, + 0x46, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x10, 0x3f, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, + 0x12, 0x27, 0x0a, 0x19, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, + 0x46, 0x69, 0x72, 0x6d, 0x77, 0x61, 0x72, 0x65, 0x45, 0x72, 0x61, 0x73, 0x65, 0x10, 0x06, 0x1a, + 0x08, 0x90, 0xb5, 0x18, 0x01, 0xb8, 0xb5, 0x18, 0x01, 0x12, 0x28, 0x0a, 0x1a, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x46, 0x69, 0x72, 0x6d, 0x77, 0x61, 0x72, + 0x65, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x10, 0x07, 0x1a, 0x08, 0x90, 0xb5, 0x18, 0x01, 0xb8, + 0xb5, 0x18, 0x01, 0x12, 0x29, 0x0a, 0x1b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, + 0x70, 0x65, 0x5f, 0x46, 0x69, 0x72, 0x6d, 0x77, 0x61, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x10, 0x08, 0x1a, 0x08, 0x98, 0xb5, 0x18, 0x01, 0xb8, 0xb5, 0x18, 0x01, 0x12, 0x22, + 0x0a, 0x14, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x53, 0x65, + 0x6c, 0x66, 0x54, 0x65, 0x73, 0x74, 0x10, 0x20, 0x1a, 0x08, 0x90, 0xb5, 0x18, 0x01, 0xb8, 0xb5, + 0x18, 0x01, 0x12, 0x22, 0x0a, 0x18, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, + 0x65, 0x5f, 0x47, 0x65, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x10, 0x0b, + 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x1f, 0x0a, 0x15, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x10, + 0x0c, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x1c, 0x0a, 0x12, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x53, 0x69, 0x67, 0x6e, 0x54, 0x78, 0x10, 0x0f, 0x1a, + 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x1f, 0x0a, 0x15, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x54, 0x79, 0x70, 0x65, 0x5f, 0x54, 0x78, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x10, 0x15, + 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x1b, 0x0a, 0x11, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x54, 0x78, 0x41, 0x63, 0x6b, 0x10, 0x16, 0x1a, 0x04, 0x90, + 0xb5, 0x18, 0x01, 0x12, 0x20, 0x0a, 0x16, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, + 0x70, 0x65, 0x5f, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x10, 0x1d, 0x1a, + 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x1d, 0x0a, 0x13, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x54, 0x79, 0x70, 0x65, 0x5f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x10, 0x1e, 0x1a, 0x04, + 0x98, 0xb5, 0x18, 0x01, 0x12, 0x21, 0x0a, 0x17, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, + 0x79, 0x70, 0x65, 0x5f, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x10, + 0x26, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x23, 0x0a, 0x19, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x10, 0x27, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x26, 0x0a, 0x1c, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x10, 0x28, 0x1a, 0x04, + 0x98, 0xb5, 0x18, 0x01, 0x12, 0x24, 0x0a, 0x1a, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, + 0x79, 0x70, 0x65, 0x5f, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x10, 0x17, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x26, 0x0a, 0x1c, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, + 0x65, 0x64, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x10, 0x30, 0x1a, 0x04, 0x98, 0xb5, + 0x18, 0x01, 0x12, 0x22, 0x0a, 0x18, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, + 0x65, 0x5f, 0x53, 0x69, 0x67, 0x6e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x10, 0x35, + 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x24, 0x0a, 0x1a, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x49, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x74, 0x79, 0x10, 0x36, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x27, 0x0a, 0x1d, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x47, 0x65, 0x74, 0x45, + 0x43, 0x44, 0x48, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x10, 0x3d, 0x1a, + 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x24, 0x0a, 0x1a, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x54, 0x79, 0x70, 0x65, 0x5f, 0x45, 0x43, 0x44, 0x48, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x4b, 0x65, 0x79, 0x10, 0x3e, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x20, 0x0a, 0x16, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x43, 0x6f, 0x73, 0x69, 0x43, + 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x10, 0x47, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x24, 0x0a, + 0x1a, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x43, 0x6f, 0x73, + 0x69, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x10, 0x48, 0x1a, 0x04, 0x98, + 0xb5, 0x18, 0x01, 0x12, 0x1e, 0x0a, 0x14, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, + 0x70, 0x65, 0x5f, 0x43, 0x6f, 0x73, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x10, 0x49, 0x1a, 0x04, 0x90, + 0xb5, 0x18, 0x01, 0x12, 0x23, 0x0a, 0x19, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, + 0x70, 0x65, 0x5f, 0x43, 0x6f, 0x73, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, + 0x10, 0x4a, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x2f, 0x0a, 0x1d, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x69, 0x6e, + 0x6b, 0x44, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x10, 0x64, 0x1a, 0x0c, 0xa0, 0xb5, 0x18, + 0x01, 0xb0, 0xb5, 0x18, 0x01, 0xc0, 0xb5, 0x18, 0x01, 0x12, 0x2b, 0x0a, 0x1d, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x69, + 0x6e, 0x6b, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x10, 0x65, 0x1a, 0x08, 0xa0, 0xb5, + 0x18, 0x01, 0xb0, 0xb5, 0x18, 0x01, 0x12, 0x24, 0x0a, 0x1a, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x69, 0x6e, 0x6b, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x10, 0x66, 0x1a, 0x04, 0xa8, 0xb5, 0x18, 0x01, 0x12, 0x23, 0x0a, 0x19, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x44, 0x65, 0x62, 0x75, + 0x67, 0x4c, 0x69, 0x6e, 0x6b, 0x53, 0x74, 0x6f, 0x70, 0x10, 0x67, 0x1a, 0x04, 0xa0, 0xb5, 0x18, + 0x01, 0x12, 0x22, 0x0a, 0x18, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, + 0x5f, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x69, 0x6e, 0x6b, 0x4c, 0x6f, 0x67, 0x10, 0x68, 0x1a, + 0x04, 0xa8, 0xb5, 0x18, 0x01, 0x12, 0x29, 0x0a, 0x1f, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x54, 0x79, 0x70, 0x65, 0x5f, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x69, 0x6e, 0x6b, 0x4d, 0x65, + 0x6d, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x61, 0x64, 0x10, 0x6e, 0x1a, 0x04, 0xa0, 0xb5, 0x18, 0x01, + 0x12, 0x25, 0x0a, 0x1b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, + 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x69, 0x6e, 0x6b, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x10, + 0x6f, 0x1a, 0x04, 0xa8, 0xb5, 0x18, 0x01, 0x12, 0x2a, 0x0a, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x69, 0x6e, 0x6b, + 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x57, 0x72, 0x69, 0x74, 0x65, 0x10, 0x70, 0x1a, 0x04, 0xa0, + 0xb5, 0x18, 0x01, 0x12, 0x29, 0x0a, 0x1f, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, + 0x70, 0x65, 0x5f, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x69, 0x6e, 0x6b, 0x46, 0x6c, 0x61, 0x73, + 0x68, 0x45, 0x72, 0x61, 0x73, 0x65, 0x10, 0x71, 0x1a, 0x04, 0xa0, 0xb5, 0x18, 0x01, 0x12, 0x2b, + 0x0a, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x45, 0x74, + 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x47, 0x65, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, + 0x65, 0x79, 0x10, 0xc2, 0x03, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x28, 0x0a, 0x1d, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x45, 0x74, 0x68, 0x65, 0x72, + 0x65, 0x75, 0x6d, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x10, 0xc3, 0x03, 0x1a, + 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x28, 0x0a, 0x1e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x54, 0x79, 0x70, 0x65, 0x5f, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x47, 0x65, 0x74, + 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x10, 0x38, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, + 0x25, 0x0a, 0x1b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x45, + 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x10, 0x39, + 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x24, 0x0a, 0x1a, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x53, 0x69, + 0x67, 0x6e, 0x54, 0x78, 0x10, 0x3a, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x27, 0x0a, 0x1d, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x45, 0x74, 0x68, 0x65, + 0x72, 0x65, 0x75, 0x6d, 0x54, 0x78, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x10, 0x3b, 0x1a, + 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x23, 0x0a, 0x19, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x54, 0x79, 0x70, 0x65, 0x5f, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x54, 0x78, 0x41, + 0x63, 0x6b, 0x10, 0x3c, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x29, 0x0a, 0x1f, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, + 0x75, 0x6d, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x10, 0x40, 0x1a, + 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x2b, 0x0a, 0x21, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x54, 0x79, 0x70, 0x65, 0x5f, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x56, 0x65, 0x72, + 0x69, 0x66, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x10, 0x41, 0x1a, 0x04, 0x90, 0xb5, + 0x18, 0x01, 0x12, 0x2e, 0x0a, 0x24, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, + 0x65, 0x5f, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x10, 0x42, 0x1a, 0x04, 0x98, 0xb5, + 0x18, 0x01, 0x12, 0x23, 0x0a, 0x19, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, + 0x65, 0x5f, 0x4e, 0x45, 0x4d, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x10, + 0x43, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x20, 0x0a, 0x16, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x4e, 0x45, 0x4d, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x10, 0x44, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x1f, 0x0a, 0x15, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x4e, 0x45, 0x4d, 0x53, 0x69, 0x67, 0x6e, + 0x54, 0x78, 0x10, 0x45, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x21, 0x0a, 0x17, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x4e, 0x45, 0x4d, 0x53, 0x69, 0x67, + 0x6e, 0x65, 0x64, 0x54, 0x78, 0x10, 0x46, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x27, 0x0a, + 0x1d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x4e, 0x45, 0x4d, + 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x10, 0x4b, + 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x29, 0x0a, 0x1f, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x4e, 0x45, 0x4d, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, + 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x10, 0x4c, 0x1a, 0x04, 0x98, 0xb5, 0x18, + 0x01, 0x12, 0x24, 0x0a, 0x1a, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, + 0x5f, 0x4c, 0x69, 0x73, 0x6b, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x10, + 0x72, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x21, 0x0a, 0x17, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x4c, 0x69, 0x73, 0x6b, 0x41, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x10, 0x73, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x20, 0x0a, 0x16, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x4c, 0x69, 0x73, 0x6b, 0x53, 0x69, + 0x67, 0x6e, 0x54, 0x78, 0x10, 0x74, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x22, 0x0a, 0x18, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x4c, 0x69, 0x73, 0x6b, + 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x78, 0x10, 0x75, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, + 0x12, 0x25, 0x0a, 0x1b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, + 0x4c, 0x69, 0x73, 0x6b, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x10, + 0x76, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x2a, 0x0a, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x4c, 0x69, 0x73, 0x6b, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x10, 0x77, 0x1a, 0x04, 0x98, + 0xb5, 0x18, 0x01, 0x12, 0x27, 0x0a, 0x1d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, + 0x70, 0x65, 0x5f, 0x4c, 0x69, 0x73, 0x6b, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x10, 0x78, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x26, 0x0a, 0x1c, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x4c, 0x69, 0x73, 0x6b, + 0x47, 0x65, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x10, 0x79, 0x1a, 0x04, + 0x90, 0xb5, 0x18, 0x01, 0x12, 0x23, 0x0a, 0x19, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, + 0x79, 0x70, 0x65, 0x5f, 0x4c, 0x69, 0x73, 0x6b, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, + 0x79, 0x10, 0x7a, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x26, 0x0a, 0x1b, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x54, 0x65, 0x7a, 0x6f, 0x73, 0x47, 0x65, + 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x10, 0x96, 0x01, 0x1a, 0x04, 0x90, 0xb5, 0x18, + 0x01, 0x12, 0x23, 0x0a, 0x18, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, + 0x5f, 0x54, 0x65, 0x7a, 0x6f, 0x73, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x10, 0x97, 0x01, + 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x22, 0x0a, 0x17, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x54, 0x65, 0x7a, 0x6f, 0x73, 0x53, 0x69, 0x67, 0x6e, 0x54, + 0x78, 0x10, 0x98, 0x01, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x24, 0x0a, 0x19, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x54, 0x65, 0x7a, 0x6f, 0x73, 0x53, + 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x78, 0x10, 0x99, 0x01, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, + 0x12, 0x28, 0x0a, 0x1d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, + 0x54, 0x65, 0x7a, 0x6f, 0x73, 0x47, 0x65, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, + 0x79, 0x10, 0x9a, 0x01, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x25, 0x0a, 0x1a, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x54, 0x65, 0x7a, 0x6f, 0x73, 0x50, + 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x10, 0x9b, 0x01, 0x1a, 0x04, 0x98, 0xb5, 0x18, + 0x01, 0x12, 0x24, 0x0a, 0x19, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, + 0x5f, 0x53, 0x74, 0x65, 0x6c, 0x6c, 0x61, 0x72, 0x53, 0x69, 0x67, 0x6e, 0x54, 0x78, 0x10, 0xca, + 0x01, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x29, 0x0a, 0x1e, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x53, 0x74, 0x65, 0x6c, 0x6c, 0x61, 0x72, 0x54, 0x78, + 0x4f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x10, 0xcb, 0x01, 0x1a, 0x04, 0x98, 0xb5, + 0x18, 0x01, 0x12, 0x28, 0x0a, 0x1d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, + 0x65, 0x5f, 0x53, 0x74, 0x65, 0x6c, 0x6c, 0x61, 0x72, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x10, 0xcf, 0x01, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x25, 0x0a, 0x1a, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x53, 0x74, 0x65, 0x6c, + 0x6c, 0x61, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x10, 0xd0, 0x01, 0x1a, 0x04, 0x98, + 0xb5, 0x18, 0x01, 0x12, 0x2d, 0x0a, 0x22, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, + 0x70, 0x65, 0x5f, 0x53, 0x74, 0x65, 0x6c, 0x6c, 0x61, 0x72, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4f, 0x70, 0x10, 0xd2, 0x01, 0x1a, 0x04, 0x90, 0xb5, + 0x18, 0x01, 0x12, 0x27, 0x0a, 0x1c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, + 0x65, 0x5f, 0x53, 0x74, 0x65, 0x6c, 0x6c, 0x61, 0x72, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, + 0x4f, 0x70, 0x10, 0xd3, 0x01, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x2b, 0x0a, 0x20, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x53, 0x74, 0x65, 0x6c, 0x6c, + 0x61, 0x72, 0x50, 0x61, 0x74, 0x68, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4f, 0x70, 0x10, + 0xd4, 0x01, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x2b, 0x0a, 0x20, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x53, 0x74, 0x65, 0x6c, 0x6c, 0x61, 0x72, 0x4d, + 0x61, 0x6e, 0x61, 0x67, 0x65, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x4f, 0x70, 0x10, 0xd5, 0x01, 0x1a, + 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x32, 0x0a, 0x27, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x54, 0x79, 0x70, 0x65, 0x5f, 0x53, 0x74, 0x65, 0x6c, 0x6c, 0x61, 0x72, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x69, 0x76, 0x65, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x4f, 0x70, + 0x10, 0xd6, 0x01, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x2a, 0x0a, 0x1f, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x53, 0x74, 0x65, 0x6c, 0x6c, 0x61, 0x72, + 0x53, 0x65, 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x4f, 0x70, 0x10, 0xd7, 0x01, 0x1a, + 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x2b, 0x0a, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x54, 0x79, 0x70, 0x65, 0x5f, 0x53, 0x74, 0x65, 0x6c, 0x6c, 0x61, 0x72, 0x43, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x54, 0x72, 0x75, 0x73, 0x74, 0x4f, 0x70, 0x10, 0xd8, 0x01, 0x1a, 0x04, 0x90, 0xb5, + 0x18, 0x01, 0x12, 0x2a, 0x0a, 0x1f, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, + 0x65, 0x5f, 0x53, 0x74, 0x65, 0x6c, 0x6c, 0x61, 0x72, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x54, 0x72, + 0x75, 0x73, 0x74, 0x4f, 0x70, 0x10, 0xd9, 0x01, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x2c, + 0x0a, 0x21, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x53, 0x74, + 0x65, 0x6c, 0x6c, 0x61, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4d, 0x65, 0x72, 0x67, + 0x65, 0x4f, 0x70, 0x10, 0xda, 0x01, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x2a, 0x0a, 0x1f, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x53, 0x74, 0x65, 0x6c, + 0x6c, 0x61, 0x72, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x44, 0x61, 0x74, 0x61, 0x4f, 0x70, 0x10, + 0xdc, 0x01, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x2c, 0x0a, 0x21, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x53, 0x74, 0x65, 0x6c, 0x6c, 0x61, 0x72, 0x42, + 0x75, 0x6d, 0x70, 0x53, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x4f, 0x70, 0x10, 0xdd, 0x01, + 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x26, 0x0a, 0x1b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x53, 0x74, 0x65, 0x6c, 0x6c, 0x61, 0x72, 0x53, 0x69, 0x67, + 0x6e, 0x65, 0x64, 0x54, 0x78, 0x10, 0xe6, 0x01, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x25, + 0x0a, 0x1a, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x54, 0x72, + 0x6f, 0x6e, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x10, 0xfa, 0x01, 0x1a, + 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x22, 0x0a, 0x17, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x54, 0x79, 0x70, 0x65, 0x5f, 0x54, 0x72, 0x6f, 0x6e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x10, 0xfb, 0x01, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x21, 0x0a, 0x16, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x54, 0x72, 0x6f, 0x6e, 0x53, 0x69, 0x67, + 0x6e, 0x54, 0x78, 0x10, 0xfc, 0x01, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x23, 0x0a, 0x18, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x54, 0x72, 0x6f, 0x6e, + 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x78, 0x10, 0xfd, 0x01, 0x1a, 0x04, 0x98, 0xb5, 0x18, + 0x01, 0x12, 0x24, 0x0a, 0x19, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, + 0x5f, 0x43, 0x61, 0x72, 0x64, 0x61, 0x6e, 0x6f, 0x53, 0x69, 0x67, 0x6e, 0x54, 0x78, 0x10, 0xaf, + 0x02, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x27, 0x0a, 0x1c, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x43, 0x61, 0x72, 0x64, 0x61, 0x6e, 0x6f, 0x54, 0x78, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x10, 0xb0, 0x02, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, + 0x12, 0x2a, 0x0a, 0x1f, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, + 0x43, 0x61, 0x72, 0x64, 0x61, 0x6e, 0x6f, 0x47, 0x65, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, + 0x4b, 0x65, 0x79, 0x10, 0xb1, 0x02, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x27, 0x0a, 0x1c, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x43, 0x61, 0x72, 0x64, + 0x61, 0x6e, 0x6f, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x10, 0xb2, 0x02, 0x1a, + 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x28, 0x0a, 0x1d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x54, 0x79, 0x70, 0x65, 0x5f, 0x43, 0x61, 0x72, 0x64, 0x61, 0x6e, 0x6f, 0x47, 0x65, 0x74, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x10, 0xb3, 0x02, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, + 0x25, 0x0a, 0x1a, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x43, + 0x61, 0x72, 0x64, 0x61, 0x6e, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x10, 0xb4, 0x02, + 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x23, 0x0a, 0x18, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x43, 0x61, 0x72, 0x64, 0x61, 0x6e, 0x6f, 0x54, 0x78, 0x41, + 0x63, 0x6b, 0x10, 0xb5, 0x02, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x26, 0x0a, 0x1b, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x43, 0x61, 0x72, 0x64, 0x61, + 0x6e, 0x6f, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x78, 0x10, 0xb6, 0x02, 0x1a, 0x04, 0x98, + 0xb5, 0x18, 0x01, 0x12, 0x29, 0x0a, 0x1e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, + 0x70, 0x65, 0x5f, 0x4f, 0x6e, 0x74, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x47, 0x65, 0x74, 0x41, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x10, 0xde, 0x02, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x26, + 0x0a, 0x1b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x4f, 0x6e, + 0x74, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x10, 0xdf, 0x02, + 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x2b, 0x0a, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x4f, 0x6e, 0x74, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x47, 0x65, + 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x10, 0xe0, 0x02, 0x1a, 0x04, 0x90, + 0xb5, 0x18, 0x01, 0x12, 0x28, 0x0a, 0x1d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, + 0x70, 0x65, 0x5f, 0x4f, 0x6e, 0x74, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x50, 0x75, 0x62, 0x6c, 0x69, + 0x63, 0x4b, 0x65, 0x79, 0x10, 0xe1, 0x02, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x2b, 0x0a, + 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x4f, 0x6e, 0x74, + 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x53, 0x69, 0x67, 0x6e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, + 0x72, 0x10, 0xe2, 0x02, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x2d, 0x0a, 0x22, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x4f, 0x6e, 0x74, 0x6f, 0x6c, 0x6f, + 0x67, 0x79, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, + 0x10, 0xe3, 0x02, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x2e, 0x0a, 0x23, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x4f, 0x6e, 0x74, 0x6f, 0x6c, 0x6f, 0x67, + 0x79, 0x53, 0x69, 0x67, 0x6e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x4f, 0x6e, 0x67, + 0x10, 0xe4, 0x02, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x30, 0x0a, 0x25, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x4f, 0x6e, 0x74, 0x6f, 0x6c, 0x6f, 0x67, + 0x79, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x4f, + 0x6e, 0x67, 0x10, 0xe5, 0x02, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x30, 0x0a, 0x25, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x4f, 0x6e, 0x74, 0x6f, 0x6c, + 0x6f, 0x67, 0x79, 0x53, 0x69, 0x67, 0x6e, 0x4f, 0x6e, 0x74, 0x49, 0x64, 0x52, 0x65, 0x67, 0x69, + 0x73, 0x74, 0x65, 0x72, 0x10, 0xe6, 0x02, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x32, 0x0a, + 0x27, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x4f, 0x6e, 0x74, + 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x4f, 0x6e, 0x74, 0x49, 0x64, + 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x10, 0xe7, 0x02, 0x1a, 0x04, 0x98, 0xb5, 0x18, + 0x01, 0x12, 0x35, 0x0a, 0x2a, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, + 0x5f, 0x4f, 0x6e, 0x74, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x53, 0x69, 0x67, 0x6e, 0x4f, 0x6e, 0x74, + 0x49, 0x64, 0x41, 0x64, 0x64, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x10, + 0xe8, 0x02, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x37, 0x0a, 0x2c, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x4f, 0x6e, 0x74, 0x6f, 0x6c, 0x6f, 0x67, 0x79, + 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x4f, 0x6e, 0x74, 0x49, 0x64, 0x41, 0x64, 0x64, 0x41, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x10, 0xe9, 0x02, 0x1a, 0x04, 0x98, 0xb5, 0x18, + 0x01, 0x12, 0x27, 0x0a, 0x1c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, + 0x5f, 0x52, 0x69, 0x70, 0x70, 0x6c, 0x65, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x10, 0x90, 0x03, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x24, 0x0a, 0x19, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x52, 0x69, 0x70, 0x70, 0x6c, 0x65, + 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x10, 0x91, 0x03, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, + 0x12, 0x23, 0x0a, 0x18, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, + 0x52, 0x69, 0x70, 0x70, 0x6c, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x54, 0x78, 0x10, 0x92, 0x03, 0x1a, + 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x25, 0x0a, 0x1a, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x54, 0x79, 0x70, 0x65, 0x5f, 0x52, 0x69, 0x70, 0x70, 0x6c, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x65, + 0x64, 0x54, 0x78, 0x10, 0x93, 0x03, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x33, 0x0a, 0x28, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x4d, 0x6f, 0x6e, 0x65, + 0x72, 0x6f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x69, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x10, 0xf5, 0x03, 0x1a, 0x04, 0x98, 0xb5, 0x18, + 0x01, 0x12, 0x2f, 0x0a, 0x24, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, + 0x5f, 0x4d, 0x6f, 0x6e, 0x65, 0x72, 0x6f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x49, 0x6e, 0x69, 0x74, 0x41, 0x63, 0x6b, 0x10, 0xf6, 0x03, 0x1a, 0x04, 0x98, 0xb5, + 0x18, 0x01, 0x12, 0x37, 0x0a, 0x2c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, + 0x65, 0x5f, 0x4d, 0x6f, 0x6e, 0x65, 0x72, 0x6f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x74, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x10, 0xf7, 0x03, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x33, 0x0a, 0x28, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x4d, 0x6f, 0x6e, 0x65, 0x72, + 0x6f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x74, 0x49, + 0x6e, 0x70, 0x75, 0x74, 0x41, 0x63, 0x6b, 0x10, 0xf8, 0x03, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, + 0x12, 0x40, 0x0a, 0x35, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, + 0x4d, 0x6f, 0x6e, 0x65, 0x72, 0x6f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x50, 0x65, 0x72, 0x6d, 0x75, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x10, 0xf9, 0x03, 0x1a, 0x04, 0x98, 0xb5, + 0x18, 0x01, 0x12, 0x3c, 0x0a, 0x31, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, + 0x65, 0x5f, 0x4d, 0x6f, 0x6e, 0x65, 0x72, 0x6f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x50, 0x65, 0x72, 0x6d, 0x75, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x63, 0x6b, 0x10, 0xfa, 0x03, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, + 0x12, 0x38, 0x0a, 0x2d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, + 0x4d, 0x6f, 0x6e, 0x65, 0x72, 0x6f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x56, 0x69, 0x6e, 0x69, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x10, 0xfb, 0x03, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x34, 0x0a, 0x29, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x4d, 0x6f, 0x6e, 0x65, 0x72, 0x6f, + 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x70, 0x75, 0x74, + 0x56, 0x69, 0x6e, 0x69, 0x41, 0x63, 0x6b, 0x10, 0xfc, 0x03, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, + 0x12, 0x3b, 0x0a, 0x30, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, + 0x4d, 0x6f, 0x6e, 0x65, 0x72, 0x6f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x41, 0x6c, 0x6c, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x53, 0x65, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x10, 0xfd, 0x03, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x37, 0x0a, + 0x2c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x4d, 0x6f, 0x6e, + 0x65, 0x72, 0x6f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x6c, + 0x6c, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x53, 0x65, 0x74, 0x41, 0x63, 0x6b, 0x10, 0xfe, 0x03, + 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x38, 0x0a, 0x2d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x4d, 0x6f, 0x6e, 0x65, 0x72, 0x6f, 0x54, 0x72, 0x61, 0x6e, + 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x74, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x10, 0xff, 0x03, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, + 0x12, 0x34, 0x0a, 0x29, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, + 0x4d, 0x6f, 0x6e, 0x65, 0x72, 0x6f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x53, 0x65, 0x74, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x41, 0x63, 0x6b, 0x10, 0x80, 0x04, + 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x38, 0x0a, 0x2d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x4d, 0x6f, 0x6e, 0x65, 0x72, 0x6f, 0x54, 0x72, 0x61, 0x6e, + 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x6c, 0x6c, 0x4f, 0x75, 0x74, 0x53, 0x65, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x10, 0x81, 0x04, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, + 0x12, 0x34, 0x0a, 0x29, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, + 0x4d, 0x6f, 0x6e, 0x65, 0x72, 0x6f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x41, 0x6c, 0x6c, 0x4f, 0x75, 0x74, 0x53, 0x65, 0x74, 0x41, 0x63, 0x6b, 0x10, 0x82, 0x04, + 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x38, 0x0a, 0x2d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x4d, 0x6f, 0x6e, 0x65, 0x72, 0x6f, 0x54, 0x72, 0x61, 0x6e, + 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x69, 0x67, 0x6e, 0x49, 0x6e, 0x70, 0x75, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x10, 0x83, 0x04, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, + 0x12, 0x34, 0x0a, 0x29, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, + 0x4d, 0x6f, 0x6e, 0x65, 0x72, 0x6f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x53, 0x69, 0x67, 0x6e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x41, 0x63, 0x6b, 0x10, 0x84, 0x04, + 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x34, 0x0a, 0x29, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x4d, 0x6f, 0x6e, 0x65, 0x72, 0x6f, 0x54, 0x72, 0x61, 0x6e, + 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x10, 0x85, 0x04, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x30, 0x0a, 0x25, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x4d, 0x6f, 0x6e, 0x65, + 0x72, 0x6f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x69, 0x6e, + 0x61, 0x6c, 0x41, 0x63, 0x6b, 0x10, 0x86, 0x04, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x36, + 0x0a, 0x2b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x4d, 0x6f, + 0x6e, 0x65, 0x72, 0x6f, 0x4b, 0x65, 0x79, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x45, 0x78, 0x70, 0x6f, + 0x72, 0x74, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x10, 0x92, 0x04, + 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x32, 0x0a, 0x27, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x4d, 0x6f, 0x6e, 0x65, 0x72, 0x6f, 0x4b, 0x65, 0x79, 0x49, + 0x6d, 0x61, 0x67, 0x65, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x69, 0x74, 0x41, 0x63, + 0x6b, 0x10, 0x93, 0x04, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x34, 0x0a, 0x29, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x4d, 0x6f, 0x6e, 0x65, 0x72, 0x6f, + 0x4b, 0x65, 0x79, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x53, 0x79, 0x6e, 0x63, 0x53, 0x74, 0x65, 0x70, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x10, 0x94, 0x04, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, + 0x12, 0x30, 0x0a, 0x25, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, + 0x4d, 0x6f, 0x6e, 0x65, 0x72, 0x6f, 0x4b, 0x65, 0x79, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x53, 0x79, + 0x6e, 0x63, 0x53, 0x74, 0x65, 0x70, 0x41, 0x63, 0x6b, 0x10, 0x95, 0x04, 0x1a, 0x04, 0x98, 0xb5, + 0x18, 0x01, 0x12, 0x35, 0x0a, 0x2a, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, + 0x65, 0x5f, 0x4d, 0x6f, 0x6e, 0x65, 0x72, 0x6f, 0x4b, 0x65, 0x79, 0x49, 0x6d, 0x61, 0x67, 0x65, + 0x53, 0x79, 0x6e, 0x63, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x10, 0x96, 0x04, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x31, 0x0a, 0x26, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x4d, 0x6f, 0x6e, 0x65, 0x72, 0x6f, 0x4b, + 0x65, 0x79, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x53, 0x79, 0x6e, 0x63, 0x46, 0x69, 0x6e, 0x61, 0x6c, + 0x41, 0x63, 0x6b, 0x10, 0x97, 0x04, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x27, 0x0a, 0x1c, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x4d, 0x6f, 0x6e, 0x65, + 0x72, 0x6f, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x10, 0x9c, 0x04, 0x1a, + 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x24, 0x0a, 0x19, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x54, 0x79, 0x70, 0x65, 0x5f, 0x4d, 0x6f, 0x6e, 0x65, 0x72, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x10, 0x9d, 0x04, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x28, 0x0a, 0x1d, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x4d, 0x6f, 0x6e, 0x65, 0x72, + 0x6f, 0x47, 0x65, 0x74, 0x57, 0x61, 0x74, 0x63, 0x68, 0x4b, 0x65, 0x79, 0x10, 0x9e, 0x04, 0x1a, + 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x25, 0x0a, 0x1a, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x54, 0x79, 0x70, 0x65, 0x5f, 0x4d, 0x6f, 0x6e, 0x65, 0x72, 0x6f, 0x57, 0x61, 0x74, 0x63, 0x68, + 0x4b, 0x65, 0x79, 0x10, 0x9f, 0x04, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x2d, 0x0a, 0x22, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x44, 0x65, 0x62, 0x75, + 0x67, 0x4d, 0x6f, 0x6e, 0x65, 0x72, 0x6f, 0x44, 0x69, 0x61, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x10, 0xa2, 0x04, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x29, 0x0a, 0x1e, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x44, 0x65, 0x62, 0x75, 0x67, + 0x4d, 0x6f, 0x6e, 0x65, 0x72, 0x6f, 0x44, 0x69, 0x61, 0x67, 0x41, 0x63, 0x6b, 0x10, 0xa3, 0x04, + 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x2c, 0x0a, 0x21, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x4d, 0x6f, 0x6e, 0x65, 0x72, 0x6f, 0x47, 0x65, 0x74, 0x54, + 0x78, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x10, 0xa6, 0x04, 0x1a, 0x04, + 0x90, 0xb5, 0x18, 0x01, 0x12, 0x28, 0x0a, 0x1d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, + 0x79, 0x70, 0x65, 0x5f, 0x4d, 0x6f, 0x6e, 0x65, 0x72, 0x6f, 0x47, 0x65, 0x74, 0x54, 0x78, 0x4b, + 0x65, 0x79, 0x41, 0x63, 0x6b, 0x10, 0xa7, 0x04, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x34, + 0x0a, 0x29, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x4d, 0x6f, + 0x6e, 0x65, 0x72, 0x6f, 0x4c, 0x69, 0x76, 0x65, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x53, + 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x10, 0xa8, 0x04, 0x1a, 0x04, + 0x90, 0xb5, 0x18, 0x01, 0x12, 0x30, 0x0a, 0x25, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, + 0x79, 0x70, 0x65, 0x5f, 0x4d, 0x6f, 0x6e, 0x65, 0x72, 0x6f, 0x4c, 0x69, 0x76, 0x65, 0x52, 0x65, + 0x66, 0x72, 0x65, 0x73, 0x68, 0x53, 0x74, 0x61, 0x72, 0x74, 0x41, 0x63, 0x6b, 0x10, 0xa9, 0x04, + 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x33, 0x0a, 0x28, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x4d, 0x6f, 0x6e, 0x65, 0x72, 0x6f, 0x4c, 0x69, 0x76, 0x65, + 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x53, 0x74, 0x65, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x10, 0xaa, 0x04, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x2f, 0x0a, 0x24, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x4d, 0x6f, 0x6e, 0x65, 0x72, + 0x6f, 0x4c, 0x69, 0x76, 0x65, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x53, 0x74, 0x65, 0x70, + 0x41, 0x63, 0x6b, 0x10, 0xab, 0x04, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x34, 0x0a, 0x29, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x4d, 0x6f, 0x6e, 0x65, + 0x72, 0x6f, 0x4c, 0x69, 0x76, 0x65, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x46, 0x69, 0x6e, + 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x10, 0xac, 0x04, 0x1a, 0x04, 0x90, 0xb5, + 0x18, 0x01, 0x12, 0x30, 0x0a, 0x25, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, + 0x65, 0x5f, 0x4d, 0x6f, 0x6e, 0x65, 0x72, 0x6f, 0x4c, 0x69, 0x76, 0x65, 0x52, 0x65, 0x66, 0x72, + 0x65, 0x73, 0x68, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x41, 0x63, 0x6b, 0x10, 0xad, 0x04, 0x1a, 0x04, + 0x98, 0xb5, 0x18, 0x01, 0x12, 0x26, 0x0a, 0x1b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, + 0x79, 0x70, 0x65, 0x5f, 0x45, 0x6f, 0x73, 0x47, 0x65, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, + 0x4b, 0x65, 0x79, 0x10, 0xd8, 0x04, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x23, 0x0a, 0x18, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x45, 0x6f, 0x73, 0x50, + 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x10, 0xd9, 0x04, 0x1a, 0x04, 0x98, 0xb5, 0x18, + 0x01, 0x12, 0x20, 0x0a, 0x15, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, + 0x5f, 0x45, 0x6f, 0x73, 0x53, 0x69, 0x67, 0x6e, 0x54, 0x78, 0x10, 0xda, 0x04, 0x1a, 0x04, 0x90, + 0xb5, 0x18, 0x01, 0x12, 0x29, 0x0a, 0x1e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, + 0x70, 0x65, 0x5f, 0x45, 0x6f, 0x73, 0x54, 0x78, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x10, 0xdb, 0x04, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x25, + 0x0a, 0x1a, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x45, 0x6f, + 0x73, 0x54, 0x78, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x63, 0x6b, 0x10, 0xdc, 0x04, 0x1a, + 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x22, 0x0a, 0x17, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x54, 0x79, 0x70, 0x65, 0x5f, 0x45, 0x6f, 0x73, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x78, + 0x10, 0xdd, 0x04, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x28, 0x0a, 0x1d, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, + 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x10, 0xbc, 0x05, 0x1a, 0x04, 0x90, + 0xb5, 0x18, 0x01, 0x12, 0x25, 0x0a, 0x1a, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, + 0x70, 0x65, 0x5f, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x10, 0xbd, 0x05, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x2a, 0x0a, 0x1f, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, + 0x65, 0x47, 0x65, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x10, 0xbe, 0x05, + 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x27, 0x0a, 0x1c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x50, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x10, 0xbf, 0x05, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, + 0x24, 0x0a, 0x19, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x42, + 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x54, 0x78, 0x10, 0xc0, 0x05, 0x1a, + 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x27, 0x0a, 0x1c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x54, 0x79, 0x70, 0x65, 0x5f, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x78, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x10, 0xc1, 0x05, 0x1a, 0x04, 0x98, 0xb5, 0x18, 0x01, 0x12, 0x29, + 0x0a, 0x1e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x42, 0x69, + 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x4d, 0x73, 0x67, + 0x10, 0xc2, 0x05, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x26, 0x0a, 0x1b, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, + 0x4f, 0x72, 0x64, 0x65, 0x72, 0x4d, 0x73, 0x67, 0x10, 0xc3, 0x05, 0x1a, 0x04, 0x90, 0xb5, 0x18, + 0x01, 0x12, 0x27, 0x0a, 0x1c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, + 0x5f, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4d, 0x73, + 0x67, 0x10, 0xc4, 0x05, 0x1a, 0x04, 0x90, 0xb5, 0x18, 0x01, 0x12, 0x26, 0x0a, 0x1b, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x5f, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, + 0x65, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x78, 0x10, 0xc5, 0x05, 0x1a, 0x04, 0x98, 0xb5, + 0x18, 0x01, 0x3a, 0x3c, 0x0a, 0x07, 0x77, 0x69, 0x72, 0x65, 0x5f, 0x69, 0x6e, 0x12, 0x21, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x45, 0x6e, 0x75, 0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x18, 0xd2, 0x86, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x77, 0x69, 0x72, 0x65, 0x49, 0x6e, + 0x3a, 0x3e, 0x0a, 0x08, 0x77, 0x69, 0x72, 0x65, 0x5f, 0x6f, 0x75, 0x74, 0x12, 0x21, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, + 0x6e, 0x75, 0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, + 0xd3, 0x86, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x77, 0x69, 0x72, 0x65, 0x4f, 0x75, 0x74, + 0x3a, 0x47, 0x0a, 0x0d, 0x77, 0x69, 0x72, 0x65, 0x5f, 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x69, + 0x6e, 0x12, 0x21, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4f, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xd4, 0x86, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x77, 0x69, + 0x72, 0x65, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x3a, 0x49, 0x0a, 0x0e, 0x77, 0x69, 0x72, + 0x65, 0x5f, 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x6f, 0x75, 0x74, 0x12, 0x21, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6e, + 0x75, 0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xd5, + 0x86, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x77, 0x69, 0x72, 0x65, 0x44, 0x65, 0x62, 0x75, + 0x67, 0x4f, 0x75, 0x74, 0x3a, 0x40, 0x0a, 0x09, 0x77, 0x69, 0x72, 0x65, 0x5f, 0x74, 0x69, 0x6e, + 0x79, 0x12, 0x21, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4f, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xd6, 0x86, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x77, 0x69, + 0x72, 0x65, 0x54, 0x69, 0x6e, 0x79, 0x3a, 0x4c, 0x0a, 0x0f, 0x77, 0x69, 0x72, 0x65, 0x5f, 0x62, + 0x6f, 0x6f, 0x74, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x72, 0x12, 0x21, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6e, 0x75, 0x6d, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xd7, 0x86, 0x03, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x77, 0x69, 0x72, 0x65, 0x42, 0x6f, 0x6f, 0x74, 0x6c, 0x6f, + 0x61, 0x64, 0x65, 0x72, 0x3a, 0x43, 0x0a, 0x0b, 0x77, 0x69, 0x72, 0x65, 0x5f, 0x6e, 0x6f, 0x5f, + 0x66, 0x73, 0x6d, 0x12, 0x21, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4f, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xd8, 0x86, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, + 0x77, 0x69, 0x72, 0x65, 0x4e, 0x6f, 0x46, 0x73, 0x6d, 0x42, 0x6f, 0x0a, 0x23, 0x63, 0x6f, 0x6d, + 0x2e, 0x73, 0x61, 0x74, 0x6f, 0x73, 0x68, 0x69, 0x6c, 0x61, 0x62, 0x73, 0x2e, 0x74, 0x72, 0x65, + 0x7a, 0x6f, 0x72, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x42, 0x0d, 0x54, 0x72, 0x65, 0x7a, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5a, + 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x65, 0x74, 0x68, 0x65, + 0x72, 0x65, 0x75, 0x6d, 0x2f, 0x67, 0x6f, 0x2d, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, + 0x2f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2f, 0x75, 0x73, 0x62, 0x77, 0x61, 0x6c, + 0x6c, 0x65, 0x74, 0x2f, 0x74, 0x72, 0x65, 0x7a, 0x6f, 0x72, } -var E_WireTiny = &proto.ExtensionDesc{ - ExtendedType: (*descriptor.EnumValueOptions)(nil), - ExtensionType: (*bool)(nil), - Field: 50006, - Name: "hw.trezor.messages.wire_tiny", - Tag: "varint,50006,opt,name=wire_tiny", - Filename: "messages.proto", -} +var ( + file_messages_proto_rawDescOnce sync.Once + file_messages_proto_rawDescData = file_messages_proto_rawDesc +) -var E_WireBootloader = &proto.ExtensionDesc{ - ExtendedType: (*descriptor.EnumValueOptions)(nil), - ExtensionType: (*bool)(nil), - Field: 50007, - Name: "hw.trezor.messages.wire_bootloader", - Tag: "varint,50007,opt,name=wire_bootloader", - Filename: "messages.proto", +func file_messages_proto_rawDescGZIP() []byte { + file_messages_proto_rawDescOnce.Do(func() { + file_messages_proto_rawDescData = protoimpl.X.CompressGZIP(file_messages_proto_rawDescData) + }) + return file_messages_proto_rawDescData } -var E_WireNoFsm = &proto.ExtensionDesc{ - ExtendedType: (*descriptor.EnumValueOptions)(nil), - ExtensionType: (*bool)(nil), - Field: 50008, - Name: "hw.trezor.messages.wire_no_fsm", - Tag: "varint,50008,opt,name=wire_no_fsm", - Filename: "messages.proto", +var file_messages_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_messages_proto_goTypes = []any{ + (MessageType)(0), // 0: hw.trezor.messages.MessageType + (*descriptorpb.EnumValueOptions)(nil), // 1: google.protobuf.EnumValueOptions } - -func init() { - proto.RegisterEnum("hw.trezor.messages.MessageType", MessageType_name, MessageType_value) - proto.RegisterExtension(E_WireIn) - proto.RegisterExtension(E_WireOut) - proto.RegisterExtension(E_WireDebugIn) - proto.RegisterExtension(E_WireDebugOut) - proto.RegisterExtension(E_WireTiny) - proto.RegisterExtension(E_WireBootloader) - proto.RegisterExtension(E_WireNoFsm) +var file_messages_proto_depIdxs = []int32{ + 1, // 0: hw.trezor.messages.wire_in:extendee -> google.protobuf.EnumValueOptions + 1, // 1: hw.trezor.messages.wire_out:extendee -> google.protobuf.EnumValueOptions + 1, // 2: hw.trezor.messages.wire_debug_in:extendee -> google.protobuf.EnumValueOptions + 1, // 3: hw.trezor.messages.wire_debug_out:extendee -> google.protobuf.EnumValueOptions + 1, // 4: hw.trezor.messages.wire_tiny:extendee -> google.protobuf.EnumValueOptions + 1, // 5: hw.trezor.messages.wire_bootloader:extendee -> google.protobuf.EnumValueOptions + 1, // 6: hw.trezor.messages.wire_no_fsm:extendee -> google.protobuf.EnumValueOptions + 7, // [7:7] is the sub-list for method output_type + 7, // [7:7] is the sub-list for method input_type + 7, // [7:7] is the sub-list for extension type_name + 0, // [0:7] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name } -func init() { proto.RegisterFile("messages.proto", fileDescriptor_4dc296cbfe5ffcd5) } - -var fileDescriptor_4dc296cbfe5ffcd5 = []byte{ - // 2430 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x9a, 0xd9, 0x73, 0x1c, 0xc5, - 0x1d, 0xc7, 0xb3, 0xab, 0x11, 0x88, 0xf6, 0x41, 0x23, 0xb0, 0x2d, 0xaf, 0x2f, 0xf9, 0xc0, 0x96, - 0x2f, 0xd9, 0x10, 0x0c, 0x44, 0x38, 0x60, 0x69, 0xb5, 0x12, 0x8a, 0xb5, 0x5a, 0x97, 0x76, 0xb1, - 0x1f, 0x5d, 0xa3, 0x9d, 0xd6, 0x6e, 0x97, 0x67, 0x67, 0x86, 0x9e, 0x1e, 0x49, 0xeb, 0xa7, 0x9c, - 0x3c, 0x13, 0x48, 0xc0, 0xb9, 0xa9, 0xa4, 0x2a, 0x21, 0x57, 0x85, 0x1c, 0x4e, 0x25, 0x55, 0x39, - 0x08, 0x24, 0x2f, 0xc9, 0x43, 0x52, 0x9c, 0x86, 0x40, 0xee, 0x90, 0xe4, 0x0f, 0xc8, 0xc5, 0x91, - 0xa4, 0x7a, 0xa6, 0xbb, 0xe7, 0xd8, 0xdf, 0xae, 0x36, 0x6f, 0x58, 0xf3, 0xf9, 0x7d, 0x7f, 0x47, - 0xff, 0xfa, 0x37, 0xdd, 0xb3, 0xa0, 0xcd, 0x2d, 0xe2, 0xfb, 0x66, 0x83, 0xf8, 0xe3, 0x1e, 0x73, - 0xb9, 0x3b, 0x3c, 0xdc, 0x5c, 0x1d, 0xe7, 0x8c, 0x5c, 0x76, 0xd9, 0xb8, 0x7a, 0x52, 0x18, 0x6d, - 0xb8, 0x6e, 0xc3, 0x26, 0x27, 0x42, 0x62, 0x29, 0x58, 0x3e, 0x61, 0x11, 0xbf, 0xce, 0xa8, 0xc7, - 0x5d, 0x16, 0x59, 0x1d, 0xf9, 0xfe, 0x7d, 0x68, 0x43, 0x39, 0xc2, 0x6b, 0x6d, 0x8f, 0x0c, 0x1f, - 0x40, 0x5b, 0x13, 0xff, 0xbc, 0x38, 0xe7, 0x50, 0x4e, 0x4d, 0x9b, 0x5e, 0x26, 0xf8, 0x5d, 0x85, - 0xa1, 0x87, 0xaf, 0x8e, 0xe4, 0x9e, 0xba, 0x3a, 0x92, 0x1b, 0x2e, 0x20, 0x9c, 0xa4, 0xce, 0x51, - 0xa7, 0x81, 0x73, 0x05, 0x43, 0x3c, 0x1f, 0xde, 0x85, 0x6e, 0x4e, 0x3e, 0xab, 0x06, 0xf5, 0x3a, - 0xf1, 0x7d, 0x9c, 0x2f, 0x18, 0x57, 0x80, 0xc7, 0x33, 0x26, 0xb5, 0x03, 0x46, 0xf0, 0x80, 0x7c, - 0xbc, 0x07, 0x6d, 0x49, 0x3e, 0x2e, 0x36, 0x4d, 0xa7, 0x41, 0xce, 0x51, 0x07, 0x1b, 0x52, 0x7e, - 0x34, 0x1d, 0xe0, 0x05, 0xea, 0x91, 0x69, 0xb2, 0x42, 0xeb, 0x04, 0x0f, 0xc2, 0xc4, 0x2c, 0xe1, - 0x25, 0x87, 0x33, 0xd7, 0x6b, 0xe3, 0x1b, 0xe0, 0x10, 0xd5, 0x63, 0x24, 0x63, 0xc8, 0x08, 0xcc, - 0xbb, 0xa6, 0x25, 0x5d, 0x6c, 0x92, 0x02, 0x7b, 0xd1, 0xb6, 0x24, 0xb1, 0x48, 0x7c, 0xc2, 0x25, - 0xb2, 0x59, 0x22, 0xbb, 0xd1, 0x2d, 0xa9, 0x3c, 0x89, 0xc9, 0x03, 0x46, 0x7c, 0x7c, 0x93, 0x74, - 0x72, 0x10, 0xed, 0xcc, 0x94, 0xb0, 0x6c, 0x72, 0x46, 0xd7, 0x16, 0xc9, 0x83, 0x01, 0xf1, 0x39, - 0x1e, 0x96, 0xdc, 0x11, 0x34, 0x02, 0x72, 0x93, 0xf5, 0x4b, 0xf8, 0xe6, 0xc2, 0x46, 0xb5, 0x24, - 0x4f, 0x47, 0x81, 0x0f, 0xa7, 0x8a, 0x67, 0x3a, 0x75, 0x62, 0xe3, 0x5b, 0x12, 0x0b, 0xb7, 0x2f, - 0xad, 0x56, 0xb4, 0x89, 0xc9, 0xaa, 0xc4, 0xf7, 0xa9, 0xeb, 0xe0, 0x11, 0x19, 0xf9, 0x7e, 0xb4, - 0x3d, 0xc9, 0x4c, 0x7a, 0x9e, 0xdd, 0xae, 0x12, 0xce, 0xa9, 0xd3, 0xf0, 0xf1, 0x76, 0x18, 0x9a, - 0x0a, 0x38, 0x77, 0x1d, 0x15, 0x7b, 0x41, 0xc6, 0x7e, 0x28, 0xbd, 0x98, 0x11, 0x24, 0x02, 0xdf, - 0xd1, 0x11, 0xf8, 0xd6, 0x0e, 0x97, 0x33, 0xb6, 0xd9, 0xf0, 0xf1, 0x4e, 0xe9, 0x2f, 0x13, 0xf8, - 0x94, 0x59, 0xbf, 0x14, 0x78, 0xb2, 0xe4, 0xfb, 0x24, 0x73, 0x00, 0x15, 0x80, 0x65, 0x55, 0x41, - 0xed, 0x87, 0x57, 0x57, 0x52, 0x22, 0xaa, 0x03, 0x52, 0xe7, 0x10, 0xda, 0x95, 0x2a, 0xb9, 0xe9, - 0xfb, 0x5e, 0x93, 0x99, 0x3e, 0x51, 0x52, 0x87, 0xa5, 0xd4, 0xd1, 0x74, 0x11, 0x62, 0x50, 0xa8, - 0x1d, 0xc9, 0xe4, 0x78, 0x0c, 0xed, 0x83, 0xe1, 0x2a, 0x37, 0xb9, 0x96, 0x2e, 0x4b, 0xe9, 0x93, - 0x68, 0x77, 0x0f, 0x5a, 0xe8, 0x2f, 0x64, 0xf4, 0x33, 0xd9, 0x2f, 0x92, 0xba, 0xbb, 0x42, 0x58, - 0x5b, 0xd6, 0xe8, 0x38, 0xdc, 0xb9, 0x17, 0x5c, 0x66, 0x29, 0xd7, 0xe3, 0xf0, 0x0e, 0x15, 0x88, - 0xf0, 0x77, 0x02, 0x56, 0x98, 0x25, 0x5c, 0xf7, 0xf6, 0x5d, 0x70, 0x73, 0x54, 0x09, 0x7f, 0xe0, - 0xf6, 0x99, 0xa2, 0x1b, 0x38, 0x9c, 0x30, 0x7c, 0x9f, 0xae, 0x72, 0x0a, 0x9a, 0xa1, 0xac, 0xb5, - 0x6a, 0x32, 0x52, 0x12, 0x49, 0xe2, 0xeb, 0xa2, 0x9e, 0xfd, 0x9e, 0x00, 0xc7, 0xd2, 0x89, 0x29, - 0xf0, 0x01, 0xcf, 0x76, 0x4d, 0x0b, 0x5f, 0x9f, 0x20, 0x0f, 0xa3, 0x1d, 0x10, 0xa9, 0x12, 0x1c, - 0x2a, 0x0c, 0x5d, 0x51, 0xe8, 0xbe, 0xf4, 0xf6, 0xac, 0x12, 0x7b, 0xb9, 0x26, 0x98, 0xd1, 0x84, - 0x5c, 0xa6, 0xe7, 0x66, 0x09, 0x3f, 0x17, 0x2c, 0xd9, 0xb4, 0x7e, 0x96, 0xb4, 0xf1, 0x06, 0x99, - 0x45, 0x66, 0x5e, 0xc5, 0xc0, 0x46, 0x59, 0xcd, 0x9d, 0xe9, 0x3d, 0x59, 0xa5, 0x0d, 0xa7, 0xb6, - 0x86, 0x6f, 0x84, 0xcd, 0x6b, 0x7a, 0xfb, 0x6f, 0x91, 0xe6, 0x3b, 0xd0, 0x4d, 0x69, 0x40, 0x2c, - 0xc5, 0xd6, 0xae, 0x93, 0x6e, 0xd2, 0xb2, 0x98, 0x98, 0xb6, 0xbb, 0xe0, 0x49, 0xa7, 0x1e, 0xef, - 0x96, 0xea, 0x99, 0xb5, 0x14, 0xc1, 0xc9, 0x7f, 0xe3, 0x83, 0xf0, 0x5a, 0x9e, 0x27, 0x8c, 0x2e, - 0xb7, 0x15, 0x74, 0x48, 0x42, 0x99, 0x61, 0x26, 0xff, 0x5b, 0xc8, 0x85, 0x9d, 0x81, 0xc7, 0xa4, - 0xbf, 0x4c, 0x8f, 0x16, 0xa9, 0xd7, 0x24, 0xec, 0x2c, 0x69, 0x9f, 0x37, 0xed, 0x80, 0xe0, 0x6d, - 0xb0, 0x5a, 0x44, 0x11, 0x4b, 0x73, 0x27, 0xa5, 0x5a, 0x66, 0x7d, 0x84, 0xbb, 0x39, 0x8b, 0x38, - 0x9c, 0xf2, 0x36, 0x3e, 0x05, 0xcf, 0x04, 0xc1, 0x10, 0x4b, 0x53, 0x77, 0xea, 0x41, 0xb5, 0x2b, - 0xfb, 0xca, 0x28, 0x4e, 0xdf, 0x2f, 0x07, 0xa3, 0x58, 0xcd, 0xf7, 0x76, 0x19, 0x31, 0x69, 0xea, - 0x5e, 0x78, 0xc4, 0x14, 0x5d, 0x9f, 0x16, 0xdd, 0x56, 0x8b, 0x72, 0x3c, 0x0b, 0xeb, 0xc4, 0x44, - 0x8b, 0x38, 0x1c, 0xdf, 0x2f, 0x75, 0x32, 0xef, 0x10, 0x41, 0x89, 0x04, 0xf0, 0x1c, 0xbc, 0x36, - 0xea, 0x79, 0x54, 0xf3, 0xf7, 0x49, 0x91, 0x13, 0xe9, 0xdc, 0xa6, 0xc9, 0x52, 0xd0, 0x98, 0xa7, - 0xce, 0xa5, 0x69, 0x52, 0xa7, 0xe1, 0xdc, 0xb7, 0x0a, 0x1b, 0x9f, 0x48, 0x0e, 0x92, 0xa3, 0x5d, - 0x0c, 0x66, 0x09, 0x0f, 0x87, 0x0f, 0x26, 0x85, 0x21, 0x65, 0x90, 0x4d, 0x44, 0xc3, 0x11, 0xb9, - 0x5c, 0x30, 0x9e, 0x04, 0x02, 0x4d, 0x50, 0xae, 0x87, 0x1b, 0x05, 0xe3, 0x09, 0x60, 0x39, 0x35, - 0x34, 0xef, 0x36, 0x70, 0x53, 0x0a, 0x1d, 0x46, 0x7b, 0x40, 0xa6, 0x4c, 0x5a, 0x2e, 0x6b, 0x2f, - 0x12, 0xd3, 0xc2, 0x8e, 0x94, 0xbb, 0x35, 0x3d, 0x0c, 0x32, 0x28, 0x76, 0xa5, 0xe2, 0x11, 0x34, - 0xda, 0x03, 0xbb, 0xc0, 0x28, 0x27, 0xd8, 0x93, 0x92, 0xdd, 0xbc, 0xcf, 0xd8, 0xa6, 0xdf, 0x8c, - 0x06, 0xd7, 0x83, 0x12, 0x3d, 0x9a, 0x96, 0x2d, 0x71, 0xd1, 0xc2, 0x41, 0x2b, 0x35, 0x43, 0x9e, - 0x19, 0x90, 0xeb, 0x38, 0x96, 0xae, 0xb8, 0x82, 0x63, 0xf2, 0x59, 0x75, 0x3c, 0x1a, 0x4b, 0xbf, - 0x16, 0x12, 0xb2, 0x6a, 0x6b, 0xdf, 0x2d, 0x35, 0x33, 0xe9, 0x2b, 0x52, 0x61, 0xef, 0x81, 0x77, - 0xa4, 0xc2, 0xe4, 0x98, 0x9a, 0x80, 0xdf, 0x88, 0x8a, 0x8a, 0xc7, 0xd5, 0x3d, 0x52, 0x2e, 0xb3, - 0xd0, 0x31, 0x28, 0xc6, 0xd6, 0x69, 0xa9, 0x96, 0x29, 0x63, 0xd2, 0xa7, 0x1a, 0x2c, 0x67, 0x24, - 0x7a, 0x14, 0xed, 0x85, 0xd0, 0xf4, 0x14, 0x9a, 0x94, 0xf0, 0x38, 0x3a, 0x00, 0xc1, 0x1d, 0xd3, - 0x68, 0x0a, 0x0e, 0x76, 0xa1, 0x54, 0x4e, 0xd4, 0xb1, 0x08, 0xcf, 0xd8, 0x85, 0x52, 0x59, 0x11, - 0xd3, 0xf0, 0x91, 0x75, 0xa1, 0x54, 0x96, 0xd5, 0x2b, 0xc1, 0x6f, 0x4c, 0x09, 0x10, 0xab, 0xb6, - 0x86, 0x67, 0xe0, 0x01, 0xb4, 0x50, 0x2a, 0x4f, 0x93, 0x3a, 0x6b, 0x7b, 0x5c, 0xe5, 0x78, 0x16, - 0xae, 0x5d, 0x0c, 0x12, 0x4b, 0xa1, 0xf3, 0xf0, 0xd2, 0xce, 0x53, 0xff, 0x52, 0x22, 0x3f, 0x06, - 0x07, 0x27, 0x28, 0x85, 0xf8, 0x5d, 0xce, 0xc3, 0xd4, 0xbf, 0x24, 0x33, 0xe4, 0xf0, 0xe9, 0x4c, - 0x11, 0x61, 0x8a, 0x81, 0x54, 0xc9, 0x34, 0xa4, 0x62, 0x54, 0xd4, 0x2b, 0x52, 0x2a, 0xb3, 0x1f, - 0x05, 0xd6, 0xb1, 0x80, 0xab, 0x70, 0xd5, 0x04, 0x9b, 0xee, 0x8c, 0x35, 0xf8, 0x8d, 0x22, 0x4b, - 0x11, 0xef, 0xaf, 0x36, 0x3c, 0x50, 0x05, 0x17, 0x43, 0x97, 0xf5, 0xc9, 0x3d, 0x95, 0x48, 0x8d, - 0x5c, 0x76, 0xfd, 0x44, 0x61, 0x1f, 0xcb, 0x69, 0xb1, 0x91, 0x0e, 0x4e, 0x41, 0x8f, 0xe7, 0xf4, - 0x3b, 0x6c, 0x5b, 0x07, 0x24, 0x8b, 0x7b, 0x25, 0xa7, 0x5f, 0x16, 0xdb, 0x41, 0x26, 0x2c, 0xef, - 0x27, 0x72, 0x7a, 0x34, 0xec, 0x82, 0xc2, 0x8a, 0xe3, 0xff, 0x64, 0x4e, 0x8f, 0x86, 0x42, 0x07, - 0x19, 0x63, 0x9f, 0xca, 0xe9, 0xfe, 0x49, 0x9f, 0xe2, 0x38, 0xb1, 0x6d, 0x93, 0xc9, 0xe0, 0x7e, - 0x9e, 0xd3, 0x0d, 0xb9, 0x1b, 0xa0, 0x6a, 0x6b, 0x15, 0x4f, 0xcd, 0x86, 0x5f, 0x74, 0x89, 0x50, - 0xa2, 0x89, 0xd2, 0xfd, 0xb2, 0x4b, 0x84, 0x92, 0x54, 0xd8, 0xaf, 0x94, 0xe0, 0xf1, 0xf4, 0x91, - 0x5a, 0x62, 0x45, 0x46, 0xc2, 0x23, 0x72, 0x5d, 0x1c, 0x38, 0x2b, 0x1e, 0x7e, 0x2e, 0xa7, 0xa7, - 0xd8, 0x4e, 0x00, 0x3f, 0x67, 0xb6, 0xc5, 0x4b, 0xb7, 0xe2, 0xe1, 0xe7, 0x73, 0x7a, 0xea, 0x8c, - 0x82, 0x20, 0x6f, 0xc6, 0xf0, 0x0b, 0xbd, 0xe1, 0xb2, 0xe9, 0x98, 0x0d, 0x52, 0x59, 0x5e, 0x26, - 0xac, 0xe2, 0xe1, 0x17, 0x15, 0x7c, 0x3b, 0x3a, 0xd4, 0x35, 0x62, 0x71, 0xc6, 0xa7, 0x2b, 0xda, - 0xe6, 0xa5, 0x9c, 0xde, 0x11, 0x7b, 0xa0, 0x75, 0x20, 0xbc, 0xe2, 0x71, 0xea, 0x3a, 0x7e, 0xc5, - 0xc3, 0x2f, 0xf7, 0x0e, 0x26, 0xba, 0x45, 0xd7, 0x58, 0xe0, 0x8b, 0xc8, 0xaf, 0xf5, 0x16, 0x9e, - 0xb4, 0x6d, 0x77, 0x55, 0xb1, 0xaf, 0x28, 0xf6, 0x58, 0x7a, 0x10, 0x2b, 0x36, 0x2a, 0x72, 0x99, - 0xb0, 0x06, 0xa9, 0x78, 0xf8, 0xd5, 0xde, 0xca, 0x51, 0x4d, 0xa6, 0x4d, 0x6e, 0x56, 0x3c, 0xfc, - 0x5a, 0x6f, 0xe5, 0xa9, 0xa0, 0xe5, 0x55, 0x45, 0x03, 0x39, 0x75, 0xa1, 0xfc, 0x7a, 0x4e, 0xef, - 0xe4, 0x1d, 0x5d, 0x9a, 0x32, 0xdc, 0x0d, 0x6f, 0xe4, 0xf4, 0xb4, 0x49, 0xf7, 0x38, 0x73, 0x9d, - 0x44, 0xa3, 0xbd, 0x99, 0xd3, 0x83, 0x6b, 0x5b, 0x16, 0x53, 0xcc, 0x5b, 0x39, 0x7d, 0x48, 0xde, - 0x9a, 0x65, 0xe4, 0x26, 0x78, 0xbb, 0xdb, 0x56, 0x97, 0x48, 0x18, 0xd2, 0x3b, 0x5d, 0xf6, 0x53, - 0xd1, 0x64, 0x96, 0xe9, 0xb8, 0x52, 0xea, 0x1b, 0x79, 0xb8, 0x49, 0x25, 0x15, 0xbf, 0x69, 0x9f, - 0xca, 0xeb, 0x0f, 0x03, 0x7b, 0x00, 0x30, 0xb5, 0xe3, 0xbf, 0xd9, 0x5b, 0x34, 0x06, 0xbf, 0x95, - 0x87, 0xb7, 0x68, 0x2c, 0xaa, 0xaa, 0xf2, 0xed, 0x3c, 0xbc, 0x45, 0x25, 0xa9, 0xb0, 0xef, 0xe4, - 0xf5, 0x3b, 0x76, 0x04, 0x4c, 0x47, 0x9c, 0x07, 0xae, 0xe6, 0xe1, 0x45, 0x4d, 0x54, 0x26, 0xac, - 0xe0, 0x77, 0x95, 0x58, 0x66, 0xd6, 0x54, 0x1c, 0xee, 0xda, 0x6e, 0xa3, 0x9d, 0x08, 0xef, 0x37, - 0x5d, 0x24, 0x15, 0xaa, 0xb8, 0xdf, 0xe6, 0xf5, 0x15, 0x7e, 0xb4, 0x8b, 0x64, 0x5c, 0x9d, 0xdf, - 0xe5, 0xe1, 0x73, 0x9a, 0x82, 0x63, 0xf2, 0xf7, 0xeb, 0xc8, 0x86, 0x8b, 0xcd, 0x4c, 0xc7, 0x5f, - 0x26, 0x0c, 0xff, 0x41, 0xc9, 0x66, 0xc6, 0x58, 0x12, 0x26, 0x96, 0xc6, 0xff, 0xa8, 0xb4, 0xc7, - 0xd1, 0xfe, 0x6e, 0xf8, 0x05, 0xca, 0x9b, 0x16, 0x33, 0x57, 0x2b, 0x4e, 0x03, 0xff, 0x49, 0xc9, - 0x9f, 0x44, 0xb7, 0x76, 0x97, 0x4f, 0x5a, 0xfc, 0x39, 0xaf, 0x3f, 0x3e, 0x74, 0xb5, 0xa8, 0x38, - 0x7c, 0xce, 0x5a, 0x24, 0x0d, 0xea, 0x8b, 0xbb, 0xfc, 0x1b, 0x79, 0x78, 0xae, 0xa5, 0x7d, 0xa4, - 0x6d, 0xfe, 0xa2, 0xbc, 0x9c, 0x42, 0x47, 0x7a, 0x7a, 0x99, 0xb4, 0xac, 0x49, 0xce, 0x19, 0x5d, - 0x0a, 0x38, 0xf1, 0xf1, 0x5f, 0x95, 0xab, 0xbb, 0xd0, 0xb1, 0x75, 0x5c, 0xa5, 0x0d, 0xff, 0x96, - 0xd7, 0xa7, 0x85, 0xd4, 0x26, 0x58, 0xa4, 0x9e, 0x67, 0x93, 0x44, 0xef, 0x3c, 0x3c, 0x00, 0xbf, - 0x6f, 0x23, 0x50, 0x51, 0x1f, 0x1d, 0x80, 0x3b, 0x3b, 0xa2, 0xe4, 0x6e, 0x7e, 0x64, 0x00, 0xde, - 0x25, 0x31, 0x14, 0x36, 0xf6, 0xa3, 0x0a, 0x7b, 0x37, 0x1a, 0x4b, 0xdd, 0x9f, 0x5d, 0x87, 0x30, - 0x37, 0x5c, 0x79, 0xb3, 0x2e, 0x66, 0xfc, 0x9c, 0x43, 0xb9, 0x1a, 0x00, 0x7f, 0x1f, 0xd0, 0x17, - 0xbb, 0x03, 0xeb, 0x1a, 0x89, 0x6d, 0xf6, 0x0f, 0x65, 0x90, 0xa9, 0x5c, 0x87, 0x41, 0x95, 0xf0, - 0x39, 0xc7, 0x0b, 0xb4, 0xa7, 0x7f, 0x2a, 0xc3, 0xf5, 0xc2, 0x53, 0x86, 0xc2, 0xdb, 0xbf, 0x94, - 0xd1, 0x19, 0x74, 0x6a, 0x9d, 0xf0, 0xbc, 0x80, 0xfb, 0xe7, 0x08, 0x6b, 0x05, 0xdc, 0x14, 0x7f, - 0x50, 0x6e, 0xff, 0xad, 0x14, 0x4e, 0xa3, 0xdb, 0xfe, 0x3f, 0x05, 0xe1, 0xff, 0x4d, 0x65, 0x7d, - 0x37, 0x3a, 0xbe, 0xbe, 0xf5, 0x79, 0xea, 0x50, 0xe5, 0xf7, 0x2d, 0x65, 0x79, 0x07, 0x3a, 0xdc, - 0x9f, 0xa5, 0xf0, 0xf7, 0xb6, 0xb2, 0xba, 0x07, 0x9d, 0xec, 0x69, 0x35, 0x69, 0xdb, 0x51, 0xc0, - 0x55, 0xa2, 0x2b, 0xfc, 0x4e, 0xbf, 0x4b, 0x93, 0x34, 0x16, 0x5e, 0xff, 0xd3, 0x6f, 0x96, 0xe2, - 0x98, 0x10, 0xf0, 0xc4, 0xa2, 0xfe, 0xb7, 0xdf, 0x2c, 0xb5, 0xa5, 0xf0, 0xf7, 0x7e, 0xa3, 0x4f, - 0x7f, 0x93, 0xb6, 0x5d, 0x09, 0x78, 0x22, 0xc5, 0x0f, 0x18, 0x7d, 0xfa, 0xd3, 0x96, 0xc2, 0xdf, - 0x07, 0xfb, 0xf5, 0x17, 0x7e, 0xf4, 0x49, 0x36, 0xed, 0x87, 0xfa, 0xf5, 0xa7, 0x2d, 0x85, 0xbf, - 0x0f, 0xf7, 0x6b, 0x35, 0x43, 0x1d, 0xd3, 0x56, 0xbe, 0x3e, 0x62, 0xc0, 0x03, 0x13, 0xb6, 0x12, - 0x7e, 0x1e, 0x52, 0x16, 0x77, 0xa2, 0xa3, 0x9d, 0x16, 0x67, 0x49, 0x7b, 0xae, 0x65, 0x36, 0x48, - 0x69, 0xcd, 0x73, 0x19, 0x4f, 0x6e, 0xfa, 0x47, 0x94, 0x5d, 0x66, 0xd0, 0x76, 0xb3, 0x13, 0xbe, - 0x1e, 0xed, 0x99, 0x93, 0xb2, 0xa9, 0xb6, 0x9d, 0x7a, 0x95, 0x13, 0x7d, 0x5a, 0xff, 0x58, 0xcf, - 0x9c, 0xb2, 0x56, 0xc2, 0xcf, 0xc7, 0x0d, 0x78, 0xa0, 0x77, 0x5a, 0xa4, 0x8a, 0xf7, 0x98, 0x32, - 0xbb, 0x0d, 0x1d, 0xec, 0xc3, 0x4c, 0x78, 0x7a, 0xdc, 0x80, 0x47, 0x79, 0x64, 0x92, 0x18, 0xe5, - 0x9f, 0x36, 0xe0, 0x51, 0x1e, 0x81, 0x8a, 0xfa, 0x8c, 0x01, 0x9f, 0x7a, 0xb4, 0xdc, 0x05, 0x93, - 0xd7, 0x9b, 0xe2, 0xbd, 0xfe, 0x59, 0x03, 0x9e, 0xe7, 0x11, 0xa9, 0xb1, 0xcf, 0x19, 0xf0, 0xc5, - 0x24, 0xfc, 0x50, 0x14, 0xb1, 0xd3, 0xd4, 0x6c, 0xa8, 0x0a, 0x7c, 0xde, 0x80, 0xef, 0x50, 0x19, - 0x5c, 0x64, 0xfe, 0x05, 0xa5, 0x9c, 0x39, 0x2d, 0xeb, 0x50, 0x6b, 0x6b, 0x67, 0x89, 0xfe, 0xa9, - 0xe3, 0x8b, 0x06, 0x7c, 0x60, 0x49, 0xd3, 0x42, 0xf7, 0x4b, 0x3d, 0x7b, 0x64, 0x9e, 0xae, 0x90, - 0x45, 0xb2, 0xcc, 0x88, 0xdf, 0xac, 0x72, 0x93, 0xe9, 0x6e, 0x7c, 0xd2, 0x80, 0x8f, 0x16, 0xb0, - 0x95, 0xf0, 0xf3, 0x65, 0xa3, 0xd7, 0xab, 0x24, 0x65, 0x11, 0xb7, 0xe2, 0x57, 0x94, 0x1b, 0xf0, - 0x4d, 0x97, 0x31, 0x12, 0x5e, 0xbe, 0xda, 0x6f, 0x36, 0xa9, 0x46, 0xfc, 0x5a, 0xbf, 0xd9, 0xe8, - 0x3e, 0xfc, 0xba, 0x01, 0x7f, 0x0a, 0x28, 0x65, 0x6e, 0xdc, 0xd7, 0x0c, 0xf8, 0x7e, 0x50, 0x4a, - 0xde, 0xb7, 0x5f, 0x31, 0xf4, 0x67, 0x96, 0x2d, 0x19, 0x48, 0x9e, 0x26, 0x5e, 0xed, 0xd2, 0x27, - 0x25, 0xd7, 0x17, 0x07, 0xe9, 0xe4, 0xbb, 0xf3, 0xd7, 0x06, 0x7c, 0xff, 0x49, 0xa0, 0x22, 0x81, - 0xd7, 0x0c, 0xf8, 0xfe, 0x53, 0x4a, 0x7c, 0x58, 0x78, 0xbd, 0xcb, 0xee, 0x98, 0xa2, 0x8e, 0xe9, - 0xd4, 0x93, 0x07, 0xa7, 0x1f, 0x0c, 0xc2, 0xbb, 0x43, 0x92, 0x0a, 0xfb, 0xe1, 0x20, 0x7c, 0x73, - 0x89, 0x05, 0xe3, 0xa2, 0xfc, 0x68, 0x10, 0xbe, 0xb9, 0x48, 0x36, 0x06, 0x7f, 0x3c, 0x08, 0xdf, - 0xae, 0x24, 0x28, 0x2b, 0xf8, 0x74, 0x6f, 0xb9, 0xf8, 0x76, 0xf5, 0x93, 0x41, 0xf8, 0xaa, 0xa1, - 0x40, 0x79, 0x18, 0x2f, 0xfb, 0x0d, 0xfc, 0xcc, 0x20, 0x7c, 0xd5, 0x90, 0x68, 0x85, 0x59, 0x11, - 0xf7, 0x6c, 0x6f, 0xdf, 0xd1, 0x8f, 0xb4, 0x02, 0xfc, 0x69, 0x6f, 0x41, 0xbd, 0x30, 0x3f, 0x93, - 0x31, 0x4e, 0x9c, 0x46, 0xd7, 0xaf, 0x52, 0x46, 0x2e, 0x52, 0x67, 0x78, 0xef, 0x78, 0xf4, 0x4b, - 0xff, 0xb8, 0xfa, 0xa5, 0x7f, 0xbc, 0xe4, 0x04, 0xad, 0xf0, 0xe7, 0x12, 0xf9, 0x95, 0x60, 0xe4, - 0xb9, 0x87, 0x06, 0x46, 0x73, 0x63, 0x43, 0x8b, 0xd7, 0x09, 0x9b, 0x39, 0x67, 0xe2, 0x5e, 0x34, - 0x14, 0x5a, 0xbb, 0x01, 0xef, 0xc7, 0xfc, 0x79, 0x69, 0x1e, 0xba, 0xac, 0x04, 0x7c, 0x62, 0x16, - 0x6d, 0x0a, 0xed, 0x2d, 0x31, 0xad, 0xfa, 0x8c, 0xe1, 0x05, 0x29, 0xb2, 0x41, 0x58, 0x86, 0x63, - 0x6e, 0xce, 0x99, 0x98, 0x43, 0x9b, 0x13, 0x42, 0x7d, 0x86, 0xf3, 0xa2, 0x54, 0xda, 0xa8, 0x95, - 0x44, 0x4c, 0x67, 0xd0, 0x0d, 0xa1, 0x14, 0xa7, 0x4e, 0xbb, 0x1f, 0x95, 0x97, 0xa4, 0x4a, 0x58, - 0x89, 0x1a, 0x75, 0xda, 0x13, 0xf3, 0xe8, 0xc6, 0x50, 0x61, 0xc9, 0x75, 0xb9, 0xed, 0x9a, 0x16, - 0x61, 0xfd, 0xe8, 0xbc, 0x2c, 0x75, 0xc2, 0x44, 0xa6, 0xb4, 0xe9, 0x44, 0x11, 0x85, 0x99, 0x5e, - 0x74, 0xdc, 0x8b, 0xcb, 0x7e, 0xab, 0x1f, 0xa5, 0x6b, 0x52, 0x29, 0xcc, 0x63, 0xc1, 0x9d, 0xf1, - 0x5b, 0x53, 0x77, 0xa0, 0xfd, 0x75, 0xb7, 0x35, 0xee, 0x9b, 0xdc, 0xf5, 0x9b, 0xd4, 0x36, 0x97, - 0x7c, 0xf5, 0xff, 0x79, 0xd8, 0x74, 0x49, 0x4b, 0x4d, 0x6d, 0xaa, 0x85, 0x7f, 0x94, 0x9d, 0xf3, - 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xa3, 0x69, 0x67, 0x5d, 0x1f, 0x22, 0x00, 0x00, +func init() { file_messages_proto_init() } +func file_messages_proto_init() { + if File_messages_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_messages_proto_rawDesc, + NumEnums: 1, + NumMessages: 0, + NumExtensions: 7, + NumServices: 0, + }, + GoTypes: file_messages_proto_goTypes, + DependencyIndexes: file_messages_proto_depIdxs, + EnumInfos: file_messages_proto_enumTypes, + ExtensionInfos: file_messages_proto_extTypes, + }.Build() + File_messages_proto = out.File + file_messages_proto_rawDesc = nil + file_messages_proto_goTypes = nil + file_messages_proto_depIdxs = nil } diff --git a/accounts/usbwallet/trezor/messages.proto b/accounts/usbwallet/trezor/messages.proto index 3e0482e344..c232bef60d 100644 --- a/accounts/usbwallet/trezor/messages.proto +++ b/accounts/usbwallet/trezor/messages.proto @@ -9,10 +9,13 @@ package hw.trezor.messages; * Messages for TREZOR communication */ +option go_package = "github.com/ethereum/go-ethereum/accounts/usbwallet/trezor"; + // Sugar for easier handling in Java option java_package = "com.satoshilabs.trezor.lib.protobuf"; option java_outer_classname = "TrezorMessage"; + import "google/protobuf/descriptor.proto"; /** diff --git a/accounts/usbwallet/trezor/trezor.go b/accounts/usbwallet/trezor/trezor.go index cdca6b4e0b..4324258314 100644 --- a/accounts/usbwallet/trezor/trezor.go +++ b/accounts/usbwallet/trezor/trezor.go @@ -42,7 +42,7 @@ // - Grab the latest Go plugin `go get -u github.com/golang/protobuf/protoc-gen-go` // - Vendor in the latest Go plugin `govendor fetch github.com/golang/protobuf/...` -//go:generate protoc -I/usr/local/include:. --go_out=import_path=trezor:. messages.proto messages-common.proto messages-management.proto messages-ethereum.proto +//go:generate protoc -I/usr/local/include:. --go_out=paths=source_relative:. messages.proto messages-common.proto messages-management.proto messages-ethereum.proto // Package trezor contains the wire protocol. package trezor diff --git a/appveyor.yml b/appveyor.yml index 41c70491b4..92369537cd 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -24,6 +24,7 @@ for: - image: Ubuntu build_script: - go run build/ci.go lint + - go run build/ci.go generate -verify - go run build/ci.go install -dlgo test_script: - go run build/ci.go test -dlgo -short diff --git a/beacon/engine/types.go b/beacon/engine/types.go index a80c1162b6..2d98cbd968 100644 --- a/beacon/engine/types.go +++ b/beacon/engine/types.go @@ -209,7 +209,7 @@ func ExecutableDataToBlock(params ExecutableData, versionedHashes []common.Hash, if params.BaseFeePerGas != nil && (params.BaseFeePerGas.Sign() == -1 || params.BaseFeePerGas.BitLen() > 256) { return nil, fmt.Errorf("invalid baseFeePerGas: %v", params.BaseFeePerGas) } - var blobHashes []common.Hash + var blobHashes = make([]common.Hash, 0, len(txs)) for _, tx := range txs { blobHashes = append(blobHashes, tx.BlobHashes()...) } @@ -250,7 +250,7 @@ func ExecutableDataToBlock(params ExecutableData, versionedHashes []common.Hash, BlobGasUsed: params.BlobGasUsed, ParentBeaconRoot: beaconRoot, } - block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */).WithWithdrawals(params.Withdrawals) + block := types.NewBlockWithHeader(header).WithBody(types.Body{Transactions: txs, Uncles: nil, Withdrawals: params.Withdrawals}) if block.Hash() != params.BlockHash { return nil, fmt.Errorf("blockhash mismatch, want %x, got %x", params.BlockHash, block.Hash()) } diff --git a/beacon/light/api/light_api.go b/beacon/light/api/light_api.go index 903db57344..6f60fc0cc6 100755 --- a/beacon/light/api/light_api.go +++ b/beacon/light/api/light_api.go @@ -494,9 +494,6 @@ func (api *BeaconLightApi) StartHeadListener(listener HeadEventListener) func() for { select { - case <-ctx.Done(): - stream.Close() - case event, ok := <-stream.Events: if !ok { log.Trace("Event stream closed") diff --git a/beacon/light/request/server.go b/beacon/light/request/server.go index 9f3b09b81e..a06dec99ae 100644 --- a/beacon/light/request/server.go +++ b/beacon/light/request/server.go @@ -186,10 +186,14 @@ func (s *serverWithTimeout) eventCallback(event Event) { // call will just do nothing timer.Stop() delete(s.timeouts, id) - s.childEventCb(event) + if s.childEventCb != nil { + s.childEventCb(event) + } } default: - s.childEventCb(event) + if s.childEventCb != nil { + s.childEventCb(event) + } } } @@ -211,25 +215,27 @@ func (s *serverWithTimeout) startTimeout(reqData RequestResponse) { delete(s.timeouts, id) childEventCb := s.childEventCb s.lock.Unlock() - childEventCb(Event{Type: EvFail, Data: reqData}) + if childEventCb != nil { + childEventCb(Event{Type: EvFail, Data: reqData}) + } }) childEventCb := s.childEventCb s.lock.Unlock() - childEventCb(Event{Type: EvTimeout, Data: reqData}) + if childEventCb != nil { + childEventCb(Event{Type: EvTimeout, Data: reqData}) + } }) } // unsubscribe stops all goroutines associated with the server. func (s *serverWithTimeout) unsubscribe() { s.lock.Lock() - defer s.lock.Unlock() - for _, timer := range s.timeouts { if timer != nil { timer.Stop() } } - s.childEventCb = nil + s.lock.Unlock() s.parent.Unsubscribe() } @@ -328,10 +334,10 @@ func (s *serverWithLimits) eventCallback(event Event) { } childEventCb := s.childEventCb s.lock.Unlock() - if passEvent { + if passEvent && childEventCb != nil { childEventCb(event) } - if sendCanRequestAgain { + if sendCanRequestAgain && childEventCb != nil { childEventCb(Event{Type: EvCanRequestAgain}) } } @@ -347,13 +353,12 @@ func (s *serverWithLimits) sendRequest(request Request) (reqId ID) { // unsubscribe stops all goroutines associated with the server. func (s *serverWithLimits) unsubscribe() { s.lock.Lock() - defer s.lock.Unlock() - if s.delayTimer != nil { s.delayTimer.Stop() s.delayTimer = nil } s.childEventCb = nil + s.lock.Unlock() s.serverWithTimeout.unsubscribe() } @@ -383,7 +388,7 @@ func (s *serverWithLimits) canRequestNow() bool { } childEventCb := s.childEventCb s.lock.Unlock() - if sendCanRequestAgain { + if sendCanRequestAgain && childEventCb != nil { childEventCb(Event{Type: EvCanRequestAgain}) } return canRequest @@ -415,7 +420,7 @@ func (s *serverWithLimits) delay(delay time.Duration) { } childEventCb := s.childEventCb s.lock.Unlock() - if sendCanRequestAgain { + if sendCanRequestAgain && childEventCb != nil { childEventCb(Event{Type: EvCanRequestAgain}) } }) diff --git a/beacon/light/request/server_test.go b/beacon/light/request/server_test.go index 38629cb8c4..fef5d062ea 100644 --- a/beacon/light/request/server_test.go +++ b/beacon/light/request/server_test.go @@ -51,6 +51,7 @@ func TestServerEvents(t *testing.T) { expEvent(EvFail) rs.eventCb(Event{Type: EvResponse, Data: RequestResponse{ID: 1, Request: testRequest, Response: testResponse}}) expEvent(nil) + srv.unsubscribe() } func TestServerParallel(t *testing.T) { @@ -129,9 +130,7 @@ func TestServerEventRateLimit(t *testing.T) { srv := NewServer(rs, clock) var eventCount int srv.subscribe(func(event Event) { - if !event.IsRequestEvent() { - eventCount++ - } + eventCount++ }) expEvents := func(send, expAllowed int) { eventCount = 0 @@ -147,6 +146,30 @@ func TestServerEventRateLimit(t *testing.T) { expEvents(5, 1) clock.Run(maxServerEventRate * maxServerEventBuffer * 2) expEvents(maxServerEventBuffer+5, maxServerEventBuffer) + srv.unsubscribe() +} + +func TestServerUnsubscribe(t *testing.T) { + rs := &testRequestServer{} + clock := &mclock.Simulated{} + srv := NewServer(rs, clock) + var eventCount int + srv.subscribe(func(event Event) { + eventCount++ + }) + eventCb := rs.eventCb + eventCb(Event{Type: testEventType}) + if eventCount != 1 { + t.Errorf("Server event callback not called before unsubscribe") + } + srv.unsubscribe() + if rs.eventCb != nil { + t.Errorf("Server event callback not removed after unsubscribe") + } + eventCb(Event{Type: testEventType}) + if eventCount != 1 { + t.Errorf("Server event callback called after unsubscribe") + } } type testRequestServer struct { @@ -156,4 +179,4 @@ type testRequestServer struct { func (rs *testRequestServer) Name() string { return "" } func (rs *testRequestServer) Subscribe(eventCb func(Event)) { rs.eventCb = eventCb } func (rs *testRequestServer) SendRequest(ID, Request) {} -func (rs *testRequestServer) Unsubscribe() {} +func (rs *testRequestServer) Unsubscribe() { rs.eventCb = nil } diff --git a/beacon/light/sync/head_sync_test.go b/beacon/light/sync/head_sync_test.go index cd7dacf7fe..d095d6a446 100644 --- a/beacon/light/sync/head_sync_test.go +++ b/beacon/light/sync/head_sync_test.go @@ -91,7 +91,7 @@ func TestValidatedHead(t *testing.T) { ts.ServerEvent(EvNewOptimisticUpdate, testServer3, testOptUpdate4) // finality should be requested from both servers ts.Run(4, testServer1, ReqFinality{}, testServer3, ReqFinality{}) - // future period annonced heads should be queued + // future period announced heads should be queued ht.ExpValidated(t, 4, nil) chain.SetNextSyncPeriod(2) diff --git a/beacon/light/sync/test_helpers.go b/beacon/light/sync/test_helpers.go index cfca8ad8a4..b331bf7110 100644 --- a/beacon/light/sync/test_helpers.go +++ b/beacon/light/sync/test_helpers.go @@ -173,24 +173,24 @@ type TestCommitteeChain struct { init bool } -func (t *TestCommitteeChain) CheckpointInit(bootstrap types.BootstrapData) error { - t.fsp, t.nsp, t.init = bootstrap.Header.SyncPeriod(), bootstrap.Header.SyncPeriod()+2, true +func (tc *TestCommitteeChain) CheckpointInit(bootstrap types.BootstrapData) error { + tc.fsp, tc.nsp, tc.init = bootstrap.Header.SyncPeriod(), bootstrap.Header.SyncPeriod()+2, true return nil } -func (t *TestCommitteeChain) InsertUpdate(update *types.LightClientUpdate, nextCommittee *types.SerializedSyncCommittee) error { +func (tc *TestCommitteeChain) InsertUpdate(update *types.LightClientUpdate, nextCommittee *types.SerializedSyncCommittee) error { period := update.AttestedHeader.Header.SyncPeriod() - if period < t.fsp || period > t.nsp || !t.init { + if period < tc.fsp || period > tc.nsp || !tc.init { return light.ErrInvalidPeriod } - if period == t.nsp { - t.nsp++ + if period == tc.nsp { + tc.nsp++ } return nil } -func (t *TestCommitteeChain) NextSyncPeriod() (uint64, bool) { - return t.nsp, t.init +func (tc *TestCommitteeChain) NextSyncPeriod() (uint64, bool) { + return tc.nsp, tc.init } func (tc *TestCommitteeChain) ExpInit(t *testing.T, ExpInit bool) { @@ -199,8 +199,8 @@ func (tc *TestCommitteeChain) ExpInit(t *testing.T, ExpInit bool) { } } -func (t *TestCommitteeChain) SetNextSyncPeriod(nsp uint64) { - t.init, t.nsp = true, nsp +func (tc *TestCommitteeChain) SetNextSyncPeriod(nsp uint64) { + tc.init, tc.nsp = true, nsp } func (tc *TestCommitteeChain) ExpNextSyncPeriod(t *testing.T, expNsp uint64) { diff --git a/beacon/types/exec_payload.go b/beacon/types/exec_payload.go index 718f98f529..b159687dfc 100644 --- a/beacon/types/exec_payload.go +++ b/beacon/types/exec_payload.go @@ -63,11 +63,9 @@ func convertPayload[T payloadType](payload T, parentRoot *zrntcommon.Root) (*typ panic("unsupported block type") } - block := types.NewBlockWithHeader(&header) - block = block.WithBody(transactions, nil) - block = block.WithWithdrawals(withdrawals) + block := types.NewBlockWithHeader(&header).WithBody(types.Body{Transactions: transactions, Withdrawals: withdrawals}) if hash := block.Hash(); hash != expectedHash { - return nil, fmt.Errorf("Sanity check failed, payload hash does not match (expected %x, got %x)", expectedHash, hash) + return nil, fmt.Errorf("sanity check failed, payload hash does not match (expected %x, got %x)", expectedHash, hash) } return block, nil } diff --git a/build/checksums.txt b/build/checksums.txt index 27577285b8..c6c9606865 100644 --- a/build/checksums.txt +++ b/build/checksums.txt @@ -5,61 +5,122 @@ # https://github.com/ethereum/execution-spec-tests/releases/download/v2.1.0/ ca89c76851b0900bfcc3cbb9a26cbece1f3d7c64a3bed38723e914713290df6c fixtures_develop.tar.gz -# version:golang 1.22.2 +# version:golang 1.22.4 # https://go.dev/dl/ -374ea82b289ec738e968267cac59c7d5ff180f9492250254784b2044e90df5a9 go1.22.2.src.tar.gz -33e7f63077b1c5bce4f1ecadd4d990cf229667c40bfb00686990c950911b7ab7 go1.22.2.darwin-amd64.tar.gz -660298be38648723e783ba0398e90431de1cb288c637880cdb124f39bd977f0d go1.22.2.darwin-arm64.tar.gz -efc7162b0cad2f918ac566a923d4701feb29dc9c0ab625157d49b1cbcbba39da go1.22.2.freebsd-386.tar.gz -d753428296e6709527e291fd204700a587ffef2c0a472b21aebea11618245929 go1.22.2.freebsd-amd64.tar.gz -586d9eb7fe0489ab297ad80dd06414997df487c5cf536c490ffeaa8d8f1807a7 go1.22.2.linux-386.tar.gz -5901c52b7a78002aeff14a21f93e0f064f74ce1360fce51c6ee68cd471216a17 go1.22.2.linux-amd64.tar.gz -36e720b2d564980c162a48c7e97da2e407dfcc4239e1e58d98082dfa2486a0c1 go1.22.2.linux-arm64.tar.gz -9243dfafde06e1efe24d59df6701818e6786b4adfdf1191098050d6d023c5369 go1.22.2.linux-armv6l.tar.gz -251a8886c5113be6490bdbb955ddee98763b49c9b1bf4c8364c02d3b482dab00 go1.22.2.linux-ppc64le.tar.gz -2b39019481c28c560d65e9811a478ae10e3ef765e0f59af362031d386a71bfef go1.22.2.linux-s390x.tar.gz -651753c06df037020ef4d162c5b273452e9ba976ed17ae39e66ef7ee89d8147e go1.22.2.windows-386.zip -8e581cf330f49d3266e936521a2d8263679ef7e2fc2cbbceb85659122d883596 go1.22.2.windows-amd64.zip -ddfca5beb9a0c62254266c3090c2555d899bf3e7aa26243e7de3621108f06875 go1.22.2.windows-arm64.zip +fed720678e728a7ca30ba8d1ded1caafe27d16028fab0232b8ba8e22008fb784 go1.22.4.src.tar.gz +b9647fa9fc83a0cc5d4f092a19eaeaecf45f063a5aa7d4962fde65aeb7ae6ce1 go1.22.4.aix-ppc64.tar.gz +7788f40f3a46f201df1dc46ca640403eb535d5513fc33449164a90dbd229b761 go1.22.4.darwin-amd64.pkg +c95967f50aa4ace34af0c236cbdb49a9a3e80ee2ad09d85775cb4462a5c19ed3 go1.22.4.darwin-amd64.tar.gz +4036c88faf57a6b096916f1827edcdbf5290a47cc5f59956e88cdd9b1b71088c go1.22.4.darwin-arm64.pkg +242b78dc4c8f3d5435d28a0d2cec9b4c1aa999b601fb8aa59fb4e5a1364bf827 go1.22.4.darwin-arm64.tar.gz +f2fbb51af4719d3616efb482d6ed2b96579b474156f85a7ddc6f126764feec4b go1.22.4.dragonfly-amd64.tar.gz +7c54884bb9f274884651d41e61d1bc12738863ad1497e97ea19ad0e9aa6bf7b5 go1.22.4.freebsd-386.tar.gz +88d44500e1701dd35797619774d6dd51bf60f45a8338b0a82ddc018e4e63fb78 go1.22.4.freebsd-amd64.tar.gz +3d9efe47db142a22679aba46b1772e3900b0d87ae13bd2b3bc80dbf2ac0b2cd6 go1.22.4.freebsd-arm.tar.gz +726dc093cf020277be45debf03c3b02b43c2efb3e2a5d4fba8f52579d65327dc go1.22.4.freebsd-arm64.tar.gz +5f6b67e5e32f1d6ccb2d4dcb44934a5e2e870a877ba7443d86ec43cfc28afa71 go1.22.4.freebsd-riscv64.tar.gz +d56ecc2f85b6418a21ef83879594d0c42ab4f65391a676bb12254870e6690d63 go1.22.4.illumos-amd64.tar.gz +47a2a8d249a91eb8605c33bceec63aedda0441a43eac47b4721e3975ff916cec go1.22.4.linux-386.tar.gz +ba79d4526102575196273416239cca418a651e049c2b099f3159db85e7bade7d go1.22.4.linux-amd64.tar.gz +a8e177c354d2e4a1b61020aca3562e27ea3e8f8247eca3170e3fa1e0c2f9e771 go1.22.4.linux-arm64.tar.gz +e2b143fbacbc9cbd448e9ef41ac3981f0488ce849af1cf37e2341d09670661de go1.22.4.linux-armv6l.tar.gz +e2ff9436e4b34bf6926b06d97916e26d67a909a2effec17967245900f0816f1d go1.22.4.linux-loong64.tar.gz +73f0dcc60458c4770593b05a7bc01cc0d31fc98f948c0c2334812c7a1f2fc3f1 go1.22.4.linux-mips.tar.gz +417af97fc2630a647052375768be4c38adcc5af946352ea5b28613ea81ca5d45 go1.22.4.linux-mips64.tar.gz +7486e2d7dd8c98eb44df815ace35a7fe7f30b7c02326e3741bd934077508139b go1.22.4.linux-mips64le.tar.gz +69479c8aad301e459a8365b40cad1074a0dbba5defb9291669f94809c4c4be6e go1.22.4.linux-mipsle.tar.gz +dd238847e65bc3e2745caca475a5db6522a2fcf85cf6c38fc36a06642b19efd7 go1.22.4.linux-ppc64.tar.gz +a3e5834657ef92523f570f798fed42f1f87bc18222a16815ec76b84169649ec4 go1.22.4.linux-ppc64le.tar.gz +56a827ff7dc6245bcd7a1e9288dffaa1d8b0fd7468562264c1523daf3b4f1b4a go1.22.4.linux-riscv64.tar.gz +7590c3e278e2dc6040aae0a39da3ca1eb2e3921673a7304cc34d588c45889eec go1.22.4.linux-s390x.tar.gz +ddd2eebe34471a2502de6c5dad04ab27c9fc80cbde7a9ad5b3c66ecec4504e1d go1.22.4.netbsd-386.tar.gz +33af79f6f935f6fbacc5d23876450b3567b79348fc065beef8e64081127dd234 go1.22.4.netbsd-amd64.tar.gz +fa3550ebd5375a70b3bcd342b5a71f4bd271dcbbfaf4eabefa2144ab5d8924b6 go1.22.4.netbsd-arm.tar.gz +c9a2971dec9f6d320c6f2b049b2353c6d0a2d35e87b8a4b2d78a2f0d62545f8e go1.22.4.netbsd-arm64.tar.gz +d21af022331bfdc2b5b161d616c3a1a4573d33cf7a30416ee509a8f3641deb47 go1.22.4.openbsd-386.tar.gz +72c0094c43f7e5722ec49c2a3e9dfa7a1123ac43a5f3a63eecf3e3795d3ff0ae go1.22.4.openbsd-amd64.tar.gz +1096831ea3c5ea3ca57d14251d9eda3786889531eb40d7d6775dcaa324d4b065 go1.22.4.openbsd-arm.tar.gz +a7ab8d4e0b02bf06ed144ba42c61c0e93ee00f2b433415dfd4ad4b6e79f31650 go1.22.4.openbsd-arm64.tar.gz +9716327c8a628358798898dc5148c49dbbeb5196bf2cbf088e550721a6e4f60b go1.22.4.openbsd-ppc64.tar.gz +a8dd4503c95c32a502a616ab78870a19889c9325fe9bd31eb16dd69346e4bfa8 go1.22.4.plan9-386.tar.gz +5423a25808d76fe5aca8607a2e5ac5673abf45446b168cb5e9d8519ee9fe39a1 go1.22.4.plan9-amd64.tar.gz +6af939ad583f5c85c09c53728ab7d38c3cc2b39167562d6c18a07c5c6608b370 go1.22.4.plan9-arm.tar.gz +e8cabe69c03085725afdb32a6f9998191a3e55a747b270d835fd05000d56abba go1.22.4.solaris-amd64.tar.gz +5c6446e2ea80bc6a971d2b34446f16e6517e638b0ff8d3ea229228d1931790b0 go1.22.4.windows-386.msi +aca4e2c37278a10f1c70dd0df142f7d66b50334fcee48978d409202d308d6d25 go1.22.4.windows-386.zip +3c21105d7b584759b6e266383b777caf6e87142d304a10b539dbc66ab482bb5f go1.22.4.windows-amd64.msi +26321c4d945a0035d8a5bc4a1965b0df401ff8ceac66ce2daadabf9030419a98 go1.22.4.windows-amd64.zip +c4303f02b864304eb83dd1db0b4ebf9d2ec9d216e7ef44a7657b166a52889c7f go1.22.4.windows-arm.msi +5fcd0671a49cecf39b41021621ee1b6e7aa1370f37122b72e80d4fd4185833b6 go1.22.4.windows-arm.zip +553cc6c460f4e3eb4fad5b897c0bb22cd8bbeb20929f0e3eeb939420320292ce go1.22.4.windows-arm64.msi +8a2daa9ea28cbdafddc6171aefed384f4e5b6e714fb52116fe9ed25a132f37ed go1.22.4.windows-arm64.zip -# version:golangci 1.55.2 +# version:golangci 1.59.0 # https://github.com/golangci/golangci-lint/releases/ -# https://github.com/golangci/golangci-lint/releases/download/v1.55.2/ -632e96e6d5294fbbe7b2c410a49c8fa01c60712a0af85a567de85bcc1623ea21 golangci-lint-1.55.2-darwin-amd64.tar.gz -234463f059249f82045824afdcdd5db5682d0593052f58f6a3039a0a1c3899f6 golangci-lint-1.55.2-darwin-arm64.tar.gz -2bdd105e2d4e003a9058c33a22bb191a1e0f30fa0790acca0d8fbffac1d6247c golangci-lint-1.55.2-freebsd-386.tar.gz -e75056e8b082386676ce23eba455cf893931a792c0d87e1e3743c0aec33c7fb5 golangci-lint-1.55.2-freebsd-amd64.tar.gz -5789b933facaf6136bd23f1d50add67b79bbcf8dfdfc9069a37f729395940a66 golangci-lint-1.55.2-freebsd-armv6.tar.gz -7f21ab1008d05f32c954f99470fc86a83a059e530fe2add1d0b7d8ed4d8992a7 golangci-lint-1.55.2-freebsd-armv7.tar.gz -33ab06139b9219a28251f10821da94423db30285cc2af97494cbb2a281927de9 golangci-lint-1.55.2-illumos-amd64.tar.gz -57ce6f8ce3ad6ee45d7cc3d9a047545a851c2547637834a3fcb086c7b40b1e6b golangci-lint-1.55.2-linux-386.tar.gz -ca21c961a33be3bc15e4292dc40c98c8dcc5463a7b6768a3afc123761630c09c golangci-lint-1.55.2-linux-amd64.tar.gz -8eb0cee9b1dbf0eaa49871798c7f8a5b35f2960c52d776a5f31eb7d886b92746 golangci-lint-1.55.2-linux-arm64.tar.gz -3195f3e0f37d353fd5bd415cabcd4e263f5c29d3d0ffb176c26ff3d2c75eb3bb golangci-lint-1.55.2-linux-armv6.tar.gz -c823ee36eb1a719e171de1f2f5ca3068033dce8d9817232fd10ed71fd6650406 golangci-lint-1.55.2-linux-armv7.tar.gz -758a5d2a356dc494bd13ed4c0d4bf5a54a4dc91267ea5ecdd87b86c7ca0624e7 golangci-lint-1.55.2-linux-loong64.tar.gz -2c7b9abdce7cae802a67d583cd7c6dca520bff6d0e17c8535a918e2f2b437aa0 golangci-lint-1.55.2-linux-mips64.tar.gz -024e0a15b85352cc27271285526e16a4ab66d3e67afbbe446c9808c06cb8dbed golangci-lint-1.55.2-linux-mips64le.tar.gz -6b00f89ba5506c1de1efdd9fa17c54093013a294fefd8b9b31534db626a672ee golangci-lint-1.55.2-linux-ppc64le.tar.gz -0faa0d047d9bf7b703ed3ea65b6117043c93504f9ca1de25ae929d3901c73d4a golangci-lint-1.55.2-linux-riscv64.tar.gz -30dec9b22e7d5bb4e9d5ccea96da20f71cd7db3c8cf30b8ddc7cb9174c4d742a golangci-lint-1.55.2-linux-s390x.tar.gz -5a0ede48f79ad707902fdb29be8cd2abd8302dc122b65ebae3fdfc86751c7698 golangci-lint-1.55.2-netbsd-386.tar.gz -95af20a2e617126dd5b08122ece7819101070e1582a961067ce8c41172f901ad golangci-lint-1.55.2-netbsd-amd64.tar.gz -94fb7dacb7527847cc95d7120904e19a2a0a81a0d50d61766c9e0251da72ab9d golangci-lint-1.55.2-netbsd-armv6.tar.gz -ca906bce5fee9619400e4a321c56476fe4a4efb6ac4fc989d340eb5563348873 golangci-lint-1.55.2-netbsd-armv7.tar.gz -45b442f69fc8915c4500201c0247b7f3f69544dbc9165403a61f9095f2c57355 golangci-lint-1.55.2-windows-386.zip -f57d434d231d43417dfa631587522f8c1991220b43c8ffadb9c7bd279508bf81 golangci-lint-1.55.2-windows-amd64.zip -fd7dc8f4c6829ee6fafb252a4d81d2155cd35da7833665cbb25d53ce7cecd990 golangci-lint-1.55.2-windows-arm64.zip -1892c3c24f9e7ef44b02f6750c703864b6dc350129f3ec39510300007b2376f1 golangci-lint-1.55.2-windows-armv6.zip -a5e68ae73d38748b5269fad36ac7575e3c162a5dc63ef58abdea03cc5da4522a golangci-lint-1.55.2-windows-armv7.zip +# https://github.com/golangci/golangci-lint/releases/download/v1.59.0/ +418acf7e255ddc0783e97129c9b03d9311b77826a5311d425a01c708a86417e7 golangci-lint-1.59.0-darwin-amd64.tar.gz +5f6a1d95a6dd69f6e328eb56dd311a38e04cfab79a1305fbf4957f4e203f47b6 golangci-lint-1.59.0-darwin-arm64.tar.gz +8899bf589185d49f747f3e5db9f0bde8a47245a100c64a3dd4d65e8e92cfc4f2 golangci-lint-1.59.0-freebsd-386.tar.gz +658212f138d9df2ac89427e22115af34bf387c0871d70f2a25101718946a014f golangci-lint-1.59.0-freebsd-amd64.tar.gz +4c6395ea40f314d3b6fa17d8997baab93464d5d1deeaab513155e625473bd03a golangci-lint-1.59.0-freebsd-armv6.tar.gz +ff37da4fbaacdb6bbae70fdbdbb1ba932a859956f788c82822fa06bef5b7c6b3 golangci-lint-1.59.0-freebsd-armv7.tar.gz +439739469ed2bda182b1ec276d40c40e02f195537f78e3672996741ad223d6b6 golangci-lint-1.59.0-illumos-amd64.tar.gz +940801d46790e40d0a097d8fee34e2606f0ef148cd039654029b0b8750a15ed6 golangci-lint-1.59.0-linux-386.tar.gz +3b14a439f33c4fff83dbe0349950d984042b9a1feb6c62f82787b598fc3ab5f4 golangci-lint-1.59.0-linux-amd64.tar.gz +c57e6c0b0fa03089a2611dceddd5bc5d206716cccdff8b149da8baac598719a1 golangci-lint-1.59.0-linux-arm64.tar.gz +93149e2d3b25ac754df9a23172403d8aa6d021a7e0d9c090a12f51897f68c9a0 golangci-lint-1.59.0-linux-armv6.tar.gz +d10ac38239d9efee3ee87b55c96cdf3fa09e1a525babe3ffdaaf65ccc48cf3dc golangci-lint-1.59.0-linux-armv7.tar.gz +047338114b4f0d5f08f0fb9a397b03cc171916ed0960be7dfb355c2320cd5e9c golangci-lint-1.59.0-linux-loong64.tar.gz +5632df0f7f8fc03a80a266130faef0b5902d280cf60621f1b2bdc1aef6d97ee9 golangci-lint-1.59.0-linux-mips64.tar.gz +71dd638c82fa4439171e7126d2c7a32b5d103bfdef282cea40c83632cb3d1f4b golangci-lint-1.59.0-linux-mips64le.tar.gz +6cf9ea0d34e91669948483f9ae7f07da319a879344373a1981099fbd890cde00 golangci-lint-1.59.0-linux-ppc64le.tar.gz +af0205fa6fbab197cee613c359947711231739095d21b5c837086233b36ad971 golangci-lint-1.59.0-linux-riscv64.tar.gz +a9d2fb93f3c688ebccef94f5dc96c0b07c4d20bf6556cddebd8442159b0c80f6 golangci-lint-1.59.0-linux-s390x.tar.gz +68ab4c57a847b8ace9679887f2f8b2b6760e57ee29dcde8c3f40dd8bb2654fa2 golangci-lint-1.59.0-netbsd-386.tar.gz +d277b8b435c19406d00de4d509eadf5a024a5782878332e9a1b7c02bb76e87a7 golangci-lint-1.59.0-netbsd-amd64.tar.gz +83211656be8dcfa1545af4f92894409f412d1f37566798cb9460a526593ad62c golangci-lint-1.59.0-netbsd-arm64.tar.gz +6c6866d28bf79fa9817a0f7d2b050890ed109cae80bdb4dfa39536a7226da237 golangci-lint-1.59.0-netbsd-armv6.tar.gz +11587566363bd03ca586b7df9776ccaed569fcd1f3489930ac02f9375b307503 golangci-lint-1.59.0-netbsd-armv7.tar.gz +466181a8967bafa495e41494f93a0bec829c2cf715de874583b0460b3b8ae2b8 golangci-lint-1.59.0-windows-386.zip +3317d8a87a99a49a0a1321d295c010790e6dbf43ee96b318f4b8bb23eae7a565 golangci-lint-1.59.0-windows-amd64.zip +b3af955c7fceac8220a36fc799e1b3f19d3b247d32f422caac5f9845df8f7316 golangci-lint-1.59.0-windows-arm64.zip +6f083c7d0c764e5a0e5bde46ee3e91ae357d80c194190fe1d9754392e9064c7e golangci-lint-1.59.0-windows-armv6.zip +3709b4dd425deadab27748778d08e03c0f804d7748f7dd5b6bb488d98aa031c7 golangci-lint-1.59.0-windows-armv7.zip # This is the builder on PPA that will build Go itself (inception-y), don't modify! # # This version is fine to be old and full of security holes, we just use it -# to build the latest Go. Don't change it. If it ever becomes insufficient, -# we need to switch over to a recursive builder to jump across supported -# versions. +# to build the latest Go. Don't change it. # -# version:ppa-builder 1.19.6 +# version:ppa-builder-1 1.19.6 # https://go.dev/dl/ d7f0013f82e6d7f862cc6cb5c8cdb48eef5f2e239b35baa97e2f1a7466043767 go1.19.6.src.tar.gz + +# version:ppa-builder-2 1.21.9 +# https://go.dev/dl/ +58f0c5ced45a0012bce2ff7a9df03e128abcc8818ebabe5027bb92bafe20e421 go1.21.9.src.tar.gz + +# version:protoc 27.1 +# https://github.com/protocolbuffers/protobuf/releases/ +# https://github.com/protocolbuffers/protobuf/releases/download/v27.1/ +8809c2ec85368c6b6e9af161b6771a153aa92670a24adbe46dd34fa02a04df2f protoc-27.1-linux-aarch_64.zip +5d21979a6d27475e810b76b88863d1e784fa01ffb15e511a3ec5bd1924d89426 protoc-27.1-linux-ppcle_64.zip +84d8852750ed186dc4a057a1a86bcac409be5362d6af04770f42367fee6b7bc1 protoc-27.1-linux-s390_64.zip +2f028796ff5741691650e0eea290e61ff2f1c0d87f8d31fe45ef47fd967cef0c protoc-27.1-linux-x86_32.zip +8970e3d8bbd67d53768fe8c2e3971bdd71e51cfe2001ca06dacad17258a7dae3 protoc-27.1-linux-x86_64.zip +03b7af1bf469e7285dc51976ee5fa99412704dbd1c017105114852a37b165c12 protoc-27.1-osx-aarch_64.zip +f14d3973cf13283d07c520ed6f4c12405ad41b9efd18089a1c74897037d742b5 protoc-27.1-osx-universal_binary.zip +8520d944f3a3890fa296a3b3b0d4bb18337337e2526bbbf1b507eeea3c2a1ec4 protoc-27.1-osx-x86_64.zip +6263718ff96547b8392a079f6fdf02a4156f2e8d13cd51649a0d03fb7afa2de8 protoc-27.1-win32.zip +da531c51ccd1290d8d34821f0ce4e219c7fbaa6f9825f5a3fb092a9d03fe6206 protoc-27.1-win64.zip + +# version:protoc-gen-go 1.34.2 +# https://github.com/protocolbuffers/protobuf-go/releases/ +# https://github.com/protocolbuffers/protobuf-go/releases/download/v1.34.2/ +9b48d8f90add02e8e94e14962fed74e7ce2b2d6bda4dd42f1f4fbccf0f766f1a protoc-gen-go.v1.34.2.darwin.amd64.tar.gz +17aca7f948dbb624049030cf841e35895cf34183ba006e721247fdeb95ff2780 protoc-gen-go.v1.34.2.darwin.arm64.tar.gz +a191849433fd489f1d44f37788d762658f3f5fb225f3a85d4ce6ba32666703ed protoc-gen-go.v1.34.2.linux.386.tar.gz +b87bc134dee55576a842141bf0ed27761c635d746780fce5dee038c6dd16554f protoc-gen-go.v1.34.2.linux.amd64.tar.gz +63d400167e75ab9f6690688f6fdc6a9455aa20bc1faa71e32149dbd322f7f198 protoc-gen-go.v1.34.2.linux.arm64.tar.gz +56e7675816db6e62be4f833a51544d5716b8420c462515579e05ca8444ab06ed protoc-gen-go.v1.34.2.windows.386.zip +abafd39612177dd4e9a65207cadd5374a9352d8611e8e040f8462fcfa3010daf protoc-gen-go.v1.34.2.windows.amd64.zip diff --git a/build/ci.go b/build/ci.go index 31ffa4a7b6..c86a16d91d 100644 --- a/build/ci.go +++ b/build/ci.go @@ -39,9 +39,11 @@ package main import ( "bytes" + "crypto/sha256" "encoding/base64" "flag" "fmt" + "io" "log" "os" "os/exec" @@ -117,23 +119,15 @@ var ( debEthereum, } - // Distros for which packages are created. - // Note: vivid is unsupported because there is no golang-1.6 package for it. - // Note: the following Ubuntu releases have been officially deprecated on Launchpad: - // wily, yakkety, zesty, artful, cosmic, disco, eoan, groovy, hirsuite, impish, - // kinetic, lunar - debDistroGoBoots = map[string]string{ - "trusty": "golang-1.11", // 14.04, EOL: 04/2024 - "xenial": "golang-go", // 16.04, EOL: 04/2026 - "bionic": "golang-go", // 18.04, EOL: 04/2028 - "focal": "golang-go", // 20.04, EOL: 04/2030 - "jammy": "golang-go", // 22.04, EOL: 04/2032 - "mantic": "golang-go", // 23.10, EOL: 07/2024 - } + // Distros for which packages are created + debDistros = []string{ + "xenial", // 16.04, EOL: 04/2026 + "bionic", // 18.04, EOL: 04/2028 + "focal", // 20.04, EOL: 04/2030 + "jammy", // 22.04, EOL: 04/2032 + "noble", // 24.04, EOL: 04/2034 - debGoBootPaths = map[string]string{ - "golang-1.11": "/usr/lib/go-1.11", - "golang-go": "/usr/lib/go", + "mantic", // 23.10, EOL: 07/2024 } // This is where the tests should be unpacked. @@ -177,6 +171,8 @@ func main() { doPurge(os.Args[2:]) case "sanitycheck": doSanityCheck() + case "generate": + doGenerate() default: log.Fatal("unknown command ", os.Args[1]) } @@ -362,6 +358,86 @@ func downloadSpecTestFixtures(csdb *build.ChecksumDB, cachedir string) string { return filepath.Join(cachedir, base) } +// hashSourceFiles iterates all files under the top-level project directory +// computing the hash of each file (excluding files within the tests +// subrepo) +func hashSourceFiles() (map[string]common.Hash, error) { + res := make(map[string]common.Hash) + err := filepath.WalkDir(".", func(path string, d os.DirEntry, err error) error { + if strings.HasPrefix(path, filepath.FromSlash("tests/testdata")) { + return filepath.SkipDir + } + if !d.Type().IsRegular() { + return nil + } + // open the file and hash it + f, err := os.OpenFile(path, os.O_RDONLY, 0666) + if err != nil { + return err + } + hasher := sha256.New() + if _, err := io.Copy(hasher, f); err != nil { + return err + } + res[path] = common.Hash(hasher.Sum(nil)) + return nil + }) + if err != nil { + return nil, err + } + return res, nil +} + +// doGenerate ensures that re-generating generated files does not cause +// any mutations in the source file tree: i.e. all generated files were +// updated and committed. Any stale generated files are updated. +func doGenerate() { + var ( + tc = new(build.GoToolchain) + cachedir = flag.String("cachedir", "./build/cache", "directory for caching binaries.") + verify = flag.Bool("verify", false, "check whether any files are changed by go generate") + ) + + protocPath := downloadProtoc(*cachedir) + protocGenGoPath := downloadProtocGenGo(*cachedir) + + var preHashes map[string]common.Hash + if *verify { + var err error + preHashes, err = hashSourceFiles() + if err != nil { + log.Fatal("failed to compute map of source hashes", "err", err) + } + } + + c := tc.Go("generate", "./...") + pathList := []string{filepath.Join(protocPath, "bin"), protocGenGoPath, os.Getenv("PATH")} + c.Env = append(c.Env, "PATH="+strings.Join(pathList, string(os.PathListSeparator))) + build.MustRun(c) + + if !*verify { + return + } + // Check if files were changed. + postHashes, err := hashSourceFiles() + if err != nil { + log.Fatal("error computing source tree file hashes", "err", err) + } + updates := []string{} + for path, postHash := range postHashes { + preHash, ok := preHashes[path] + if !ok || preHash != postHash { + updates = append(updates, path) + } + } + for _, updatedFile := range updates { + fmt.Fprintf(os.Stderr, "changed file %s\n", updatedFile) + } + if len(updates) != 0 { + log.Fatal("One or more generated files were updated by running 'go generate ./...'") + } +} + // doLint runs golangci-lint on requested packages. func doLint(cmdline []string) { var ( @@ -407,6 +483,96 @@ func downloadLinter(cachedir string) string { return filepath.Join(cachedir, base, "golangci-lint") } +// protocArchiveBaseName returns the name of the protoc archive file for +// the current system, stripped of version and file suffix. +func protocArchiveBaseName() (string, error) { + switch runtime.GOOS + "-" + runtime.GOARCH { + case "windows-amd64": + return "win64", nil + case "windows-386": + return "win32", nil + case "linux-arm64": + return "linux-aarch_64", nil + case "linux-386": + return "linux-x86_32", nil + case "linux-amd64": + return "linux-x86_64", nil + case "darwin-arm64": + return "osx-aarch_64", nil + case "darwin-amd64": + return "osx-x86_64", nil + default: + return "", fmt.Errorf("no prebuilt release of protoc available for this system (os: %s, arch: %s)", runtime.GOOS, runtime.GOARCH) + } +} + +// downloadProtocGenGo downloads protoc-gen-go, which is used by protoc +// in the generate command. It returns the full path of the directory +// containing the 'protoc-gen-go' executable. +func downloadProtocGenGo(cachedir string) string { + csdb := build.MustLoadChecksums("build/checksums.txt") + version, err := build.Version(csdb, "protoc-gen-go") + if err != nil { + log.Fatal(err) + } + baseName := fmt.Sprintf("protoc-gen-go.v%s.%s.%s", version, runtime.GOOS, runtime.GOARCH) + archiveName := baseName + if runtime.GOOS == "windows" { + archiveName += ".zip" + } else { + archiveName += ".tar.gz" + } + + url := fmt.Sprintf("https://github.com/protocolbuffers/protobuf-go/releases/download/v%s/%s", version, archiveName) + + archivePath := path.Join(cachedir, archiveName) + if err := csdb.DownloadFile(url, archivePath); err != nil { + log.Fatal(err) + } + extractDest := filepath.Join(cachedir, baseName) + if err := build.ExtractArchive(archivePath, extractDest); err != nil { + log.Fatal(err) + } + extractDest, err = filepath.Abs(extractDest) + if err != nil { + log.Fatal("error resolving absolute path for protoc", "err", err) + } + return extractDest +} + +// downloadProtoc downloads the prebuilt protoc binary used to lint generated +// files as a CI step. It returns the full path to the directory containing +// the protoc executable. +func downloadProtoc(cachedir string) string { + csdb := build.MustLoadChecksums("build/checksums.txt") + version, err := build.Version(csdb, "protoc") + if err != nil { + log.Fatal(err) + } + baseName, err := protocArchiveBaseName() + if err != nil { + log.Fatal(err) + } + + fileName := fmt.Sprintf("protoc-%s-%s", version, baseName) + archiveFileName := fileName + ".zip" + url := fmt.Sprintf("https://github.com/protocolbuffers/protobuf/releases/download/v%s/%s", version, archiveFileName) + archivePath := filepath.Join(cachedir, archiveFileName) + + if err := csdb.DownloadFile(url, archivePath); err != nil { + log.Fatal(err) + } + extractDest := filepath.Join(cachedir, fileName) + if err := build.ExtractArchive(archivePath, extractDest); err != nil { + log.Fatal(err) + } + extractDest, err = filepath.Abs(extractDest) + if err != nil { + log.Fatal("error resolving absolute path for protoc", "err", err) + } + return extractDest +} + // Release Packaging func doArchive(cmdline []string) { var ( @@ -703,8 +869,8 @@ func doDebianSource(cmdline []string) { } // Download and verify the Go source packages. var ( - gobootbundle = downloadGoBootstrapSources(*cachedir) - gobundle = downloadGoSources(*cachedir) + gobootbundles = downloadGoBootstrapSources(*cachedir) + gobundle = downloadGoSources(*cachedir) ) // Download all the dependencies needed to build the sources and run the ci script srcdepfetch := tc.Go("mod", "download") @@ -717,17 +883,19 @@ func doDebianSource(cmdline []string) { // Create Debian packages and upload them. for _, pkg := range debPackages { - for distro, goboot := range debDistroGoBoots { + for _, distro := range debDistros { // Prepare the debian package with the go-ethereum sources. - meta := newDebMetadata(distro, goboot, *signer, env, now, pkg.Name, pkg.Version, pkg.Executables) + meta := newDebMetadata(distro, *signer, env, now, pkg.Name, pkg.Version, pkg.Executables) pkgdir := stageDebianSource(*workdir, meta) // Add bootstrapper Go source code - if err := build.ExtractArchive(gobootbundle, pkgdir); err != nil { - log.Fatalf("Failed to extract bootstrapper Go sources: %v", err) - } - if err := os.Rename(filepath.Join(pkgdir, "go"), filepath.Join(pkgdir, ".goboot")); err != nil { - log.Fatalf("Failed to rename bootstrapper Go source folder: %v", err) + for i, gobootbundle := range gobootbundles { + if err := build.ExtractArchive(gobootbundle, pkgdir); err != nil { + log.Fatalf("Failed to extract bootstrapper Go sources: %v", err) + } + if err := os.Rename(filepath.Join(pkgdir, "go"), filepath.Join(pkgdir, fmt.Sprintf(".goboot-%d", i+1))); err != nil { + log.Fatalf("Failed to rename bootstrapper Go source folder: %v", err) + } } // Add builder Go source code if err := build.ExtractArchive(gobundle, pkgdir); err != nil { @@ -763,21 +931,26 @@ func doDebianSource(cmdline []string) { } } -// downloadGoBootstrapSources downloads the Go source tarball that will be used +// downloadGoBootstrapSources downloads the Go source tarball(s) that will be used // to bootstrap the builder Go. -func downloadGoBootstrapSources(cachedir string) string { +func downloadGoBootstrapSources(cachedir string) []string { csdb := build.MustLoadChecksums("build/checksums.txt") - gobootVersion, err := build.Version(csdb, "ppa-builder") - if err != nil { - log.Fatal(err) - } - file := fmt.Sprintf("go%s.src.tar.gz", gobootVersion) - url := "https://dl.google.com/go/" + file - dst := filepath.Join(cachedir, file) - if err := csdb.DownloadFile(url, dst); err != nil { - log.Fatal(err) + + var bundles []string + for _, booter := range []string{"ppa-builder-1", "ppa-builder-2"} { + gobootVersion, err := build.Version(csdb, booter) + if err != nil { + log.Fatal(err) + } + file := fmt.Sprintf("go%s.src.tar.gz", gobootVersion) + url := "https://dl.google.com/go/" + file + dst := filepath.Join(cachedir, file) + if err := csdb.DownloadFile(url, dst); err != nil { + log.Fatal(err) + } + bundles = append(bundles, dst) } - return dst + return bundles } // downloadGoSources downloads the Go source tarball. @@ -855,10 +1028,7 @@ type debPackage struct { } type debMetadata struct { - Env build.Environment - GoBootPackage string - GoBootPath string - + Env build.Environment PackageName string // go-ethereum version being built. Note that this @@ -886,21 +1056,19 @@ func (d debExecutable) Package() string { return d.BinaryName } -func newDebMetadata(distro, goboot, author string, env build.Environment, t time.Time, name string, version string, exes []debExecutable) debMetadata { +func newDebMetadata(distro, author string, env build.Environment, t time.Time, name string, version string, exes []debExecutable) debMetadata { if author == "" { // No signing key, use default author. author = "Ethereum Builds " } return debMetadata{ - GoBootPackage: goboot, - GoBootPath: debGoBootPaths[goboot], - PackageName: name, - Env: env, - Author: author, - Distro: distro, - Version: version, - Time: t.Format(time.RFC1123Z), - Executables: exes, + PackageName: name, + Env: env, + Author: author, + Distro: distro, + Version: version, + Time: t.Format(time.RFC1123Z), + Executables: exes, } } diff --git a/build/deb/ethereum/deb.control b/build/deb/ethereum/deb.control index 3b759f2d04..333e954c17 100644 --- a/build/deb/ethereum/deb.control +++ b/build/deb/ethereum/deb.control @@ -2,7 +2,7 @@ Source: {{.Name}} Section: science Priority: extra Maintainer: {{.Author}} -Build-Depends: debhelper (>= 8.0.0), {{.GoBootPackage}} +Build-Depends: debhelper (>= 8.0.0), golang-go Standards-Version: 3.9.5 Homepage: https://ethereum.org Vcs-Git: https://github.com/ethereum/go-ethereum.git diff --git a/build/deb/ethereum/deb.rules b/build/deb/ethereum/deb.rules index daca793e55..3287e15ff0 100644 --- a/build/deb/ethereum/deb.rules +++ b/build/deb/ethereum/deb.rules @@ -7,7 +7,7 @@ # Launchpad rejects Go's access to $HOME, use custom folders export GOCACHE=/tmp/go-build export GOPATH=/tmp/gopath -export GOROOT_BOOTSTRAP={{.GoBootPath}} +export GOROOT_BOOTSTRAP=/usr/lib/go override_dh_auto_clean: # Don't try to be smart Launchpad, we know our build rules better than you @@ -19,8 +19,9 @@ override_dh_auto_build: # # We're also shipping the bootstrapper as of Go 1.20 as it had minimum version # requirements opposed to older versions of Go. - (mv .goboot ../ && cd ../.goboot/src && ./make.bash) - (mv .go ../ && cd ../.go/src && GOROOT_BOOTSTRAP=`pwd`/../../.goboot ./make.bash) + (mv .goboot-1 ../ && cd ../.goboot-1/src && ./make.bash) + (mv .goboot-2 ../ && cd ../.goboot-2/src && GOROOT_BOOTSTRAP=`pwd`/../../.goboot-1 ./make.bash) + (mv .go ../ && cd ../.go/src && GOROOT_BOOTSTRAP=`pwd`/../../.goboot-2 ./make.bash) # We can't download external go modules within Launchpad, so we're shipping the # entire dependency source cache with go-ethereum. diff --git a/cmd/blsync/main.go b/cmd/blsync/main.go index 2aa3d9a24e..854c99703c 100644 --- a/cmd/blsync/main.go +++ b/cmd/blsync/main.go @@ -19,39 +19,21 @@ package main import ( "context" "fmt" - "io" "os" "github.com/ethereum/go-ethereum/beacon/blsync" "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/internal/debug" "github.com/ethereum/go-ethereum/internal/flags" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/rpc" - "github.com/mattn/go-colorable" - "github.com/mattn/go-isatty" "github.com/urfave/cli/v2" ) -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, - } - vmoduleFlag = &cli.StringFlag{ - Name: "vmodule", - Usage: "Per-module verbosity: comma-separated list of = (e.g. eth/*=5,p2p=4)", - Value: "", - Hidden: true, - Category: flags.LoggingCategory, - } -) - func main() { app := flags.NewApp("beacon light syncer tool") - app.Flags = []cli.Flag{ + app.Flags = flags.Merge([]cli.Flag{ utils.BeaconApiFlag, utils.BeaconApiHeaderFlag, utils.BeaconThresholdFlag, @@ -66,8 +48,16 @@ func main() { utils.GoerliFlag, utils.BlsyncApiFlag, utils.BlsyncJWTSecretFlag, - verbosityFlag, - vmoduleFlag, + }, + debug.Flags, + ) + app.Before = func(ctx *cli.Context) error { + flags.MigrateGlobalFlags(ctx) + return debug.Setup(ctx) + } + app.After = func(ctx *cli.Context) error { + debug.Exit() + return nil } app.Action = sync @@ -78,14 +68,6 @@ func main() { } func sync(ctx *cli.Context) error { - usecolor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb" - output := io.Writer(os.Stderr) - if usecolor { - output = colorable.NewColorable(os.Stderr) - } - verbosity := log.FromLegacyLevel(ctx.Int(verbosityFlag.Name)) - log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(output, verbosity, usecolor))) - // set up blsync client := blsync.NewClient(ctx) client.SetEngineRPC(makeRPCClient(ctx)) diff --git a/cmd/clef/README.md b/cmd/clef/README.md index cf09265136..b7018a5f41 100644 --- a/cmd/clef/README.md +++ b/cmd/clef/README.md @@ -225,8 +225,8 @@ Response - `value` [number:optional]: amount of Wei to send with the transaction - `data` [data:optional]: input data - `nonce` [number]: account nonce - 1. method signature [string:optional] - - The method signature, if present, is to aid decoding the calldata. Should consist of `methodname(paramtype,...)`, e.g. `transfer(uint256,address)`. The signer may use this data to parse the supplied calldata, and show the user. The data, however, is considered totally untrusted, and reliability is not expected. + 2. method signature [string:optional] + - The method signature, if present, is to aid decoding the calldata. Should consist of `methodname(paramtype,...)`, e.g. `transfer(uint256,address)`. The signer may use this data to parse the supplied calldata, and show the user. The data, however, is considered totally untrusted, and reliability is not expected. #### Result diff --git a/cmd/clef/main.go b/cmd/clef/main.go index f9b00e4a12..88d4c99e78 100644 --- a/cmd/clef/main.go +++ b/cmd/clef/main.go @@ -552,7 +552,7 @@ func listWallets(c *cli.Context) error { // accountImport imports a raw hexadecimal private key via CLI. func accountImport(c *cli.Context) error { if c.Args().Len() != 1 { - return errors.New(" must be given as first argument.") + return errors.New(" must be given as first argument") } internalApi, ui, err := initInternalApi(c) if err != nil { diff --git a/cmd/devp2p/discv4cmd.go b/cmd/devp2p/discv4cmd.go index 45bcdcd367..3b5400ca3a 100644 --- a/cmd/devp2p/discv4cmd.go +++ b/cmd/devp2p/discv4cmd.go @@ -20,6 +20,7 @@ import ( "errors" "fmt" "net" + "net/http" "strconv" "strings" "time" @@ -28,9 +29,11 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/internal/flags" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rpc" "github.com/urfave/cli/v2" ) @@ -45,6 +48,7 @@ var ( discv4ResolveJSONCommand, discv4CrawlCommand, discv4TestCommand, + discv4ListenCommand, }, } discv4PingCommand = &cli.Command{ @@ -75,6 +79,14 @@ var ( Flags: discoveryNodeFlags, ArgsUsage: "", } + discv4ListenCommand = &cli.Command{ + Name: "listen", + Usage: "Runs a discovery node", + Action: discv4Listen, + Flags: flags.Merge(discoveryNodeFlags, []cli.Flag{ + httpAddrFlag, + }), + } discv4CrawlCommand = &cli.Command{ Name: "crawl", Usage: "Updates a nodes.json file with random nodes found in the DHT", @@ -131,6 +143,10 @@ var ( Usage: "Enode of the remote node under test", EnvVars: []string{"REMOTE_ENODE"}, } + httpAddrFlag = &cli.StringFlag{ + Name: "rpc", + Usage: "HTTP server listening address", + } ) var discoveryNodeFlags = []cli.Flag{ @@ -154,6 +170,27 @@ func discv4Ping(ctx *cli.Context) error { return nil } +func discv4Listen(ctx *cli.Context) error { + disc, _ := startV4(ctx) + defer disc.Close() + + fmt.Println(disc.Self()) + + httpAddr := ctx.String(httpAddrFlag.Name) + if httpAddr == "" { + // Non-HTTP mode. + select {} + } + + api := &discv4API{disc} + log.Info("Starting RPC API server", "addr", httpAddr) + srv := rpc.NewServer() + srv.RegisterName("discv4", api) + http.DefaultServeMux.Handle("/", srv) + httpsrv := http.Server{Addr: httpAddr, Handler: http.DefaultServeMux} + return httpsrv.ListenAndServe() +} + func discv4RequestRecord(ctx *cli.Context) error { n := getNodeArg(ctx) disc, _ := startV4(ctx) @@ -362,3 +399,23 @@ func parseBootnodes(ctx *cli.Context) ([]*enode.Node, error) { } return nodes, nil } + +type discv4API struct { + host *discover.UDPv4 +} + +func (api *discv4API) LookupRandom(n int) (ns []*enode.Node) { + it := api.host.RandomNodes() + for len(ns) < n && it.Next() { + ns = append(ns, it.Node()) + } + return ns +} + +func (api *discv4API) Buckets() [][]discover.BucketNode { + return api.host.TableBuckets() +} + +func (api *discv4API) Self() *enode.Node { + return api.host.Self() +} diff --git a/cmd/devp2p/internal/ethtest/conn.go b/cmd/devp2p/internal/ethtest/conn.go index c4f1bb1ff4..991c1a6c15 100644 --- a/cmd/devp2p/internal/ethtest/conn.go +++ b/cmd/devp2p/internal/ethtest/conn.go @@ -53,7 +53,8 @@ func (s *Suite) dial() (*Conn, error) { // dialAs attempts to dial a given node and perform a handshake using the given // private key. func (s *Suite) dialAs(key *ecdsa.PrivateKey) (*Conn, error) { - fd, err := net.Dial("tcp", fmt.Sprintf("%v:%d", s.Dest.IP(), s.Dest.TCP())) + tcpEndpoint, _ := s.Dest.TCPEndpoint() + fd, err := net.Dial("tcp", tcpEndpoint.String()) if err != nil { return nil, err } diff --git a/cmd/devp2p/internal/ethtest/snap.go b/cmd/devp2p/internal/ethtest/snap.go index 8ff3f1f71a..4f1b6f8656 100644 --- a/cmd/devp2p/internal/ethtest/snap.go +++ b/cmd/devp2p/internal/ethtest/snap.go @@ -32,7 +32,6 @@ import ( "github.com/ethereum/go-ethereum/internal/utesting" "github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie/trienode" - "golang.org/x/crypto/sha3" ) func (c *Conn) snapRequest(code uint64, msg any) (any, error) { @@ -905,7 +904,7 @@ func (s *Suite) snapGetByteCodes(t *utesting.T, tc *byteCodesTest) error { // that the serving node is missing var ( bytecodes = res.Codes - hasher = sha3.NewLegacyKeccak256().(crypto.KeccakState) + hasher = crypto.NewKeccakState() hash = make([]byte, 32) codes = make([][]byte, len(req.Hashes)) ) @@ -964,7 +963,7 @@ func (s *Suite) snapGetTrieNodes(t *utesting.T, tc *trieNodesTest) error { // Cross reference the requested trienodes with the response to find gaps // that the serving node is missing - hasher := sha3.NewLegacyKeccak256().(crypto.KeccakState) + hasher := crypto.NewKeccakState() hash := make([]byte, 32) trienodes := res.Nodes if got, want := len(trienodes), len(tc.expHashes); got != want { diff --git a/cmd/devp2p/internal/v4test/framework.go b/cmd/devp2p/internal/v4test/framework.go index 9286594181..958fb71179 100644 --- a/cmd/devp2p/internal/v4test/framework.go +++ b/cmd/devp2p/internal/v4test/framework.go @@ -53,16 +53,18 @@ func newTestEnv(remote string, listen1, listen2 string) *testenv { if err != nil { panic(err) } - if node.IP() == nil || node.UDP() == 0 { + if !node.IPAddr().IsValid() || node.UDP() == 0 { var ip net.IP var tcpPort, udpPort int - if ip = node.IP(); ip == nil { + if node.IPAddr().IsValid() { + ip = node.IPAddr().AsSlice() + } else { ip = net.ParseIP("127.0.0.1") } if tcpPort = node.TCP(); tcpPort == 0 { tcpPort = 30303 } - if udpPort = node.TCP(); udpPort == 0 { + if udpPort = node.UDP(); udpPort == 0 { udpPort = 30303 } node = enode.NewV4(node.Pubkey(), ip, tcpPort, udpPort) @@ -110,7 +112,7 @@ func (te *testenv) localEndpoint(c net.PacketConn) v4wire.Endpoint { } func (te *testenv) remoteEndpoint() v4wire.Endpoint { - return v4wire.NewEndpoint(te.remoteAddr, 0) + return v4wire.NewEndpoint(te.remoteAddr.AddrPort(), 0) } func contains(ns []v4wire.Node, key v4wire.Pubkey) bool { diff --git a/cmd/devp2p/nodesetcmd.go b/cmd/devp2p/nodesetcmd.go index d0ba0517ba..e1f8cc053e 100644 --- a/cmd/devp2p/nodesetcmd.go +++ b/cmd/devp2p/nodesetcmd.go @@ -19,7 +19,7 @@ package main import ( "errors" "fmt" - "net" + "net/netip" "sort" "strconv" "strings" @@ -205,11 +205,11 @@ func trueFilter(args []string) (nodeFilter, error) { } func ipFilter(args []string) (nodeFilter, error) { - _, cidr, err := net.ParseCIDR(args[0]) + prefix, err := netip.ParsePrefix(args[0]) if err != nil { return nil, err } - f := func(n nodeJSON) bool { return cidr.Contains(n.N.IP()) } + f := func(n nodeJSON) bool { return prefix.Contains(n.N.IPAddr()) } return f, nil } diff --git a/cmd/devp2p/rlpxcmd.go b/cmd/devp2p/rlpxcmd.go index aa7d065818..77f09e6b85 100644 --- a/cmd/devp2p/rlpxcmd.go +++ b/cmd/devp2p/rlpxcmd.go @@ -77,7 +77,11 @@ var ( func rlpxPing(ctx *cli.Context) error { n := getNodeArg(ctx) - fd, err := net.Dial("tcp", fmt.Sprintf("%v:%d", n.IP(), n.TCP())) + tcpEndpoint, ok := n.TCPEndpoint() + if !ok { + return fmt.Errorf("node has no TCP endpoint") + } + fd, err := net.Dial("tcp", tcpEndpoint.String()) if err != nil { return err } @@ -105,7 +109,7 @@ func rlpxPing(ctx *cli.Context) error { } return fmt.Errorf("received disconnect message: %v", msg[0]) default: - return fmt.Errorf("invalid message code %d, expected handshake (code zero)", code) + return fmt.Errorf("invalid message code %d, expected handshake (code zero) or disconnect (code one)", code) } return nil } diff --git a/cmd/evm/README.md b/cmd/evm/README.md index 25647c18a9..f95b6b4d7b 100644 --- a/cmd/evm/README.md +++ b/cmd/evm/README.md @@ -14,15 +14,15 @@ The `evm t8n` tool is a stateless state transition utility. It is a utility which can 1. Take a prestate, including - - Accounts, - - Block context information, - - Previous blockshashes (*optional) + - Accounts, + - Block context information, + - Previous blockshashes (*optional) 2. Apply a set of transactions, 3. Apply a mining-reward (*optional), 4. And generate a post-state, including - - State root, transaction root, receipt root, - - Information about rejected transactions, - - Optionally: a full or partial post-state dump + - State root, transaction root, receipt root, + - Information about rejected transactions, + - Optionally: a full or partial post-state dump ### Specification diff --git a/cmd/evm/blockrunner.go b/cmd/evm/blockrunner.go index 0275c019bc..d5cd8d8e3d 100644 --- a/cmd/evm/blockrunner.go +++ b/cmd/evm/blockrunner.go @@ -86,7 +86,7 @@ func blockTestCmd(ctx *cli.Context) error { continue } test := tests[name] - if err := test.Run(false, rawdb.HashScheme, tracer, func(res error, chain *core.BlockChain) { + if err := test.Run(false, rawdb.HashScheme, false, tracer, func(res error, chain *core.BlockChain) { if ctx.Bool(DumpFlag.Name) { if state, _ := chain.State(); state != nil { fmt.Println(string(state.Dump(nil))) diff --git a/cmd/evm/internal/t8ntool/block.go b/cmd/evm/internal/t8ntool/block.go index 62c8593a1d..37a6db9ffc 100644 --- a/cmd/evm/internal/t8ntool/block.go +++ b/cmd/evm/internal/t8ntool/block.go @@ -160,7 +160,7 @@ func (i *bbInput) ToBlock() *types.Block { if i.Header.Difficulty != nil { header.Difficulty = i.Header.Difficulty } - return types.NewBlockWithHeader(header).WithBody(i.Txs, i.Ommers).WithWithdrawals(i.Withdrawals) + return types.NewBlockWithHeader(header).WithBody(types.Body{Transactions: i.Txs, Uncles: i.Ommers, Withdrawals: i.Withdrawals}) } // SealBlock seals the given block using the configured engine. diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 2a79daae54..0223560d58 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -306,7 +306,9 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, if tracer.Hooks.OnTxEnd != nil { tracer.Hooks.OnTxEnd(receipt, nil) } - writeTraceResult(tracer, traceOutput) + if err = writeTraceResult(tracer, traceOutput); err != nil { + log.Warn("Error writing tracer output", "err", err) + } } } @@ -323,7 +325,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, var ( blockReward = big.NewInt(miningReward) minerReward = new(big.Int).Set(blockReward) - perOmmer = new(big.Int).Div(blockReward, big.NewInt(32)) + perOmmer = new(big.Int).Rsh(blockReward, 5) ) for _, ommer := range pre.Env.Ommers { // Add 1/32th for each ommer included @@ -332,7 +334,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, reward := big.NewInt(8) reward.Sub(reward, new(big.Int).SetUint64(ommer.Delta)) reward.Mul(reward, blockReward) - reward.Div(reward, big.NewInt(8)) + reward.Rsh(reward, 3) statedb.AddBalance(ommer.Address, uint256.MustFromBig(reward), tracing.BalanceIncreaseRewardMineUncle) } statedb.AddBalance(pre.Env.Coinbase, uint256.MustFromBig(minerReward), tracing.BalanceIncreaseRewardMineBlock) diff --git a/cmd/evm/internal/t8ntool/transition.go b/cmd/evm/internal/t8ntool/transition.go index d7c677061d..d5d0890d98 100644 --- a/cmd/evm/internal/t8ntool/transition.go +++ b/cmd/evm/internal/t8ntool/transition.go @@ -181,7 +181,7 @@ func Transition(ctx *cli.Context) error { // Set the chain id chainConfig.ChainID = big.NewInt(ctx.Int64(ChainIDFlag.Name)) - if txIt, err = loadTransactions(txStr, inputData, prestate.Env, chainConfig); err != nil { + if txIt, err = loadTransactions(txStr, inputData, chainConfig); err != nil { return err } if err := applyLondonChecks(&prestate.Env, chainConfig); err != nil { @@ -217,7 +217,7 @@ func applyLondonChecks(env *stEnv, chainConfig *params.ChainConfig) error { return nil } if env.ParentBaseFee == nil || env.Number == 0 { - return NewError(ErrorConfig, errors.New("EIP-1559 config but missing 'currentBaseFee' in env section")) + return NewError(ErrorConfig, errors.New("EIP-1559 config but missing 'parentBaseFee' in env section")) } env.BaseFee = eip1559.CalcBaseFee(chainConfig, &types.Header{ Number: new(big.Int).SetUint64(env.Number - 1), @@ -296,7 +296,7 @@ func (g Alloc) OnAccount(addr *common.Address, dumpAccount state.DumpAccount) { balance, _ := new(big.Int).SetString(dumpAccount.Balance, 0) var storage map[common.Hash]common.Hash if dumpAccount.Storage != nil { - storage = make(map[common.Hash]common.Hash) + storage = make(map[common.Hash]common.Hash, len(dumpAccount.Storage)) for k, v := range dumpAccount.Storage { storage[k] = common.HexToHash(v) } diff --git a/cmd/evm/internal/t8ntool/tx_iterator.go b/cmd/evm/internal/t8ntool/tx_iterator.go index 046f62314d..d4ebb4b399 100644 --- a/cmd/evm/internal/t8ntool/tx_iterator.go +++ b/cmd/evm/internal/t8ntool/tx_iterator.go @@ -112,7 +112,7 @@ func signUnsignedTransactions(txs []*txWithKey, signer types.Signer) (types.Tran return signedTxs, nil } -func loadTransactions(txStr string, inputData *input, env stEnv, chainConfig *params.ChainConfig) (txIterator, error) { +func loadTransactions(txStr string, inputData *input, chainConfig *params.ChainConfig) (txIterator, error) { var txsWithKeys []*txWithKey if txStr != stdinSelector { data, err := os.ReadFile(txStr) diff --git a/cmd/evm/t8n_test.go b/cmd/evm/t8n_test.go index 92d54f9835..3f3da8be63 100644 --- a/cmd/evm/t8n_test.go +++ b/cmd/evm/t8n_test.go @@ -234,7 +234,7 @@ func TestT8n(t *testing.T) { { // Test post-merge transition base: "./testdata/24", input: t8nInput{ - "alloc.json", "txs.json", "env.json", "Merge", "", + "alloc.json", "txs.json", "env.json", "Paris", "", }, output: t8nOutput{alloc: true, result: true}, expOut: "exp.json", @@ -242,7 +242,7 @@ func TestT8n(t *testing.T) { { // Test post-merge transition where input is missing random base: "./testdata/24", input: t8nInput{ - "alloc.json", "txs.json", "env-missingrandom.json", "Merge", "", + "alloc.json", "txs.json", "env-missingrandom.json", "Paris", "", }, output: t8nOutput{alloc: false, result: false}, expExitCode: 3, @@ -252,7 +252,7 @@ func TestT8n(t *testing.T) { { // Test base fee calculation base: "./testdata/25", input: t8nInput{ - "alloc.json", "txs.json", "env.json", "Merge", "", + "alloc.json", "txs.json", "env.json", "Paris", "", }, output: t8nOutput{alloc: true, result: true}, expOut: "exp.json", @@ -381,7 +381,7 @@ func TestT8nTracing(t *testing.T) { { base: "./testdata/32", input: t8nInput{ - "alloc.json", "txs.json", "env.json", "Merge", "", + "alloc.json", "txs.json", "env.json", "Paris", "", }, extraArgs: []string{"--trace", "--trace.callframes"}, expectedTraces: []string{"trace-0-0x47806361c0fa084be3caa18afe8c48156747c01dbdfc1ee11b5aecdbe4fcf23e.jsonl"}, diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go index 7aa1a734ff..f856d0500d 100644 --- a/cmd/geth/chaincmd.go +++ b/cmd/geth/chaincmd.go @@ -576,7 +576,7 @@ func importChain(ctx *cli.Context) error { fmt.Printf("Import done in %v.\n\n", time.Since(start)) // Output pre-compaction stats mostly to see the import trashing - showLeveldbStats(db) + showDBStats(db) // Print the memory statistics used by the importing mem := new(runtime.MemStats) @@ -599,7 +599,7 @@ func importChain(ctx *cli.Context) error { } fmt.Printf("Compaction done in %v.\n\n", time.Since(start)) - showLeveldbStats(db) + showDBStats(db) return importErr } diff --git a/cmd/geth/consolecmd_test.go b/cmd/geth/consolecmd_test.go index 59c0e0015e..a365b1018a 100644 --- a/cmd/geth/consolecmd_test.go +++ b/cmd/geth/consolecmd_test.go @@ -103,17 +103,17 @@ func TestAttachWelcome(t *testing.T) { "--http", "--http.port", httpPort, "--ws", "--ws.port", wsPort) t.Run("ipc", func(t *testing.T) { - waitForEndpoint(t, ipc, 3*time.Second) + waitForEndpoint(t, ipc, 4*time.Second) testAttachWelcome(t, geth, "ipc:"+ipc, ipcAPIs) }) t.Run("http", func(t *testing.T) { endpoint := "http://127.0.0.1:" + httpPort - waitForEndpoint(t, endpoint, 3*time.Second) + waitForEndpoint(t, endpoint, 4*time.Second) testAttachWelcome(t, geth, endpoint, httpAPIs) }) t.Run("ws", func(t *testing.T) { endpoint := "ws://127.0.0.1:" + wsPort - waitForEndpoint(t, endpoint, 3*time.Second) + waitForEndpoint(t, endpoint, 4*time.Second) testAttachWelcome(t, geth, endpoint, httpAPIs) }) geth.Kill() diff --git a/cmd/geth/dbcmd.go b/cmd/geth/dbcmd.go index 3d2ca5323b..d0b28b5a49 100644 --- a/cmd/geth/dbcmd.go +++ b/cmd/geth/dbcmd.go @@ -328,11 +328,17 @@ func removeDB(ctx *cli.Context) error { ancientDir = config.Node.ResolvePath(ancientDir) } // Delete state data - statePaths := []string{rootDir, filepath.Join(ancientDir, rawdb.StateFreezerName)} + statePaths := []string{ + rootDir, + filepath.Join(ancientDir, rawdb.StateFreezerName), + } confirmAndRemoveDB(statePaths, "state data", ctx, removeStateDataFlag.Name) // Delete ancient chain - chainPaths := []string{filepath.Join(ancientDir, rawdb.ChainFreezerName)} + chainPaths := []string{filepath.Join( + ancientDir, + rawdb.ChainFreezerName, + )} confirmAndRemoveDB(chainPaths, "ancient chain", ctx, removeChainDataFlag.Name) return nil } @@ -597,17 +603,13 @@ func checkStateContent(ctx *cli.Context) error { return nil } -func showLeveldbStats(db ethdb.KeyValueStater) { - if stats, err := db.Stat("leveldb.stats"); err != nil { +func showDBStats(db ethdb.KeyValueStater) { + stats, err := db.Stat() + if err != nil { log.Warn("Failed to read database stats", "error", err) - } else { - fmt.Println(stats) - } - if ioStats, err := db.Stat("leveldb.iostats"); err != nil { - log.Warn("Failed to read database iostats", "error", err) - } else { - fmt.Println(ioStats) + return } + fmt.Println(stats) } func dbStats(ctx *cli.Context) error { @@ -617,12 +619,12 @@ func dbStats(ctx *cli.Context) error { db := utils.MakeChainDatabase(ctx, stack, true, false) defer db.Close() - showLeveldbStats(db) + showDBStats(db) if stack.CheckIfMultiDataBase() { fmt.Println("show stats of state store") - showLeveldbStats(db.StateStore()) + showDBStats(db.StateStore()) fmt.Println("show stats of block store") - showLeveldbStats(db.BlockStore()) + showDBStats(db.BlockStore()) } return nil @@ -636,13 +638,12 @@ func dbCompact(ctx *cli.Context) error { defer db.Close() log.Info("Stats before compaction") - showLeveldbStats(db) - + showDBStats(db) if stack.CheckIfMultiDataBase() { fmt.Println("show stats of state store") - showLeveldbStats(db.StateStore()) + showDBStats(db.StateStore()) fmt.Println("show stats of block store") - showLeveldbStats(db.BlockStore()) + showDBStats(db.BlockStore()) } log.Info("Triggering compaction") @@ -663,12 +664,12 @@ func dbCompact(ctx *cli.Context) error { } log.Info("Stats after compaction") - showLeveldbStats(db) + showDBStats(db) if stack.CheckIfMultiDataBase() { fmt.Println("show stats of state store after compaction") - showLeveldbStats(db.StateStore()) + showDBStats(db.StateStore()) fmt.Println("show stats of block store after compaction") - showLeveldbStats(db.BlockStore()) + showDBStats(db.BlockStore()) } return nil } @@ -742,7 +743,7 @@ func dbTrieGet(ctx *cli.Context) error { log.Info("Could not decode the value", "error", err) return err } - nodeVal, hash := rawdb.ReadAccountTrieNode(db, pathKey) + nodeVal, hash := rawdb.ReadAccountTrieNodeAndHash(db, pathKey) log.Info("TrieGet result ", "PathKey", common.Bytes2Hex(pathKey), "Hash: ", hash, "node: ", trie.NodeString(hash.Bytes(), nodeVal)) } else if ctx.NArg() == 2 { owner, err = hexutil.Decode(ctx.Args().Get(0)) @@ -756,7 +757,7 @@ func dbTrieGet(ctx *cli.Context) error { return err } - nodeVal, hash := rawdb.ReadStorageTrieNode(db, common.BytesToHash(owner), pathKey) + nodeVal, hash := rawdb.ReadStorageTrieNodeAndHash(db, common.BytesToHash(owner), pathKey) log.Info("TrieGet result ", "PathKey: ", common.Bytes2Hex(pathKey), "Owner: ", common.BytesToHash(owner), "Hash: ", hash, "node: ", trie.NodeString(hash.Bytes(), nodeVal)) } } else if scheme == rawdb.HashScheme { diff --git a/cmd/geth/logging_test.go b/cmd/geth/logging_test.go index b5ce03f4b8..f426b138bb 100644 --- a/cmd/geth/logging_test.go +++ b/cmd/geth/logging_test.go @@ -73,6 +73,7 @@ func testConsoleLogging(t *testing.T, format string, tStart, tEnd int) { if err != nil { t.Fatal(err) } + defer readFile.Close() wantLines := split(readFile) haveLines := split(bytes.NewBuffer(haveB)) for i, want := range wantLines { @@ -109,6 +110,7 @@ func TestJsonLogging(t *testing.T) { if err != nil { t.Fatal(err) } + defer readFile.Close() wantLines := split(readFile) haveLines := split(bytes.NewBuffer(haveB)) for i, wantLine := range wantLines { diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 4ecd3b2076..24345bfe84 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -195,6 +195,7 @@ var ( utils.BeaconGenesisRootFlag, utils.BeaconGenesisTimeFlag, utils.BeaconCheckpointFlag, + utils.CollectWitnessFlag, }, utils.NetworkFlags, utils.DatabaseFlags) rpcFlags = []cli.Flag{ diff --git a/cmd/geth/snapshot.go b/cmd/geth/snapshot.go index 12de0b10d8..a2b6ee66b4 100644 --- a/cmd/geth/snapshot.go +++ b/cmd/geth/snapshot.go @@ -151,7 +151,7 @@ data, and verifies that all snapshot storage data has a corresponding account. }, { Name: "inspect-account", - Usage: "Check all snapshot layers for the a specific account", + Usage: "Check all snapshot layers for the specific account", ArgsUsage: "
", Action: checkAccount, Flags: flags.Merge(utils.NetworkFlags, utils.DatabaseFlags), diff --git a/cmd/geth/testdata/vcheck/vulnerabilities.json b/cmd/geth/testdata/vcheck/vulnerabilities.json index bee0e66dd8..31a34de6be 100644 --- a/cmd/geth/testdata/vcheck/vulnerabilities.json +++ b/cmd/geth/testdata/vcheck/vulnerabilities.json @@ -166,5 +166,37 @@ "severity": "Low", "CVE": "CVE-2022-29177", "check": "(Geth\\/v1\\.10\\.(0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16)-.*)$" + }, + { + "name": "DoS via malicious p2p message", + "uid": "GETH-2023-01", + "summary": "A vulnerable node can be made to consume unbounded amounts of memory when handling specially crafted p2p messages sent from an attacker node.", + "description": "The p2p handler spawned a new goroutine to respond to ping requests. By flooding a node with ping requests, an unbounded number of goroutines can be created, leading to resource exhaustion and potentially crash due to OOM.", + "links": [ + "https://github.com/ethereum/go-ethereum/security/advisories/GHSA-ppjg-v974-84cm", + "https://geth.ethereum.org/docs/vulnerabilities/vulnerabilities" + ], + "introduced": "v1.10.0", + "fixed": "v1.12.1", + "published": "2023-09-06", + "severity": "High", + "CVE": "CVE-2023-40591", + "check": "(Geth\\/v1\\.(10|11)\\..*)|(Geth\\/v1\\.12\\.0-.*)$" + }, + { + "name": "DoS via malicious p2p message", + "uid": "GETH-2024-01", + "summary": "A vulnerable node can be made to consume very large amounts of memory when handling specially crafted p2p messages sent from an attacker node.", + "description": "A vulnerable node can be made to consume very large amounts of memory when handling specially crafted p2p messages sent from an attacker node. Full details will be available at the Github security [advisory](https://github.com/ethereum/go-ethereum/security/advisories/GHSA-4xc9-8hmq-j652)", + "links": [ + "https://github.com/ethereum/go-ethereum/security/advisories/GHSA-4xc9-8hmq-j652", + "https://geth.ethereum.org/docs/vulnerabilities/vulnerabilities" + ], + "introduced": "v1.10.0", + "fixed": "v1.13.15", + "published": "2024-05-06", + "severity": "High", + "CVE": "CVE-2024-32972", + "check": "(Geth\\/v1\\.(10|11|12)\\..*)|(Geth\\/v1\\.13\\.\\d-.*)|(Geth\\/v1\\.13\\.1(0|1|2|3|4)-.*)$" } ] diff --git a/cmd/geth/verkle.go b/cmd/geth/verkle.go index 9c056ff50c..132f5eb722 100644 --- a/cmd/geth/verkle.go +++ b/cmd/geth/verkle.go @@ -28,7 +28,7 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/internal/flags" "github.com/ethereum/go-ethereum/log" - "github.com/gballet/go-verkle" + "github.com/ethereum/go-verkle" "github.com/urfave/cli/v2" ) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index c1ee80a52f..41d19116a7 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -756,6 +756,11 @@ var ( Usage: "Disables db compaction after import", Category: flags.LoggingCategory, } + CollectWitnessFlag = &cli.BoolFlag{ + Name: "collectwitness", + Usage: "Enable state witness generation during block execution. Work in progress flag, don't use.", + Category: flags.MiscCategory, + } // MISC settings SyncTargetFlag = &cli.StringFlag{ @@ -2130,6 +2135,9 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { // TODO(fjl): force-enable this in --dev mode cfg.EnablePreimageRecording = ctx.Bool(VMEnableDebugFlag.Name) } + if ctx.IsSet(CollectWitnessFlag.Name) { + cfg.EnableWitnessCollection = ctx.Bool(CollectWitnessFlag.Name) + } if ctx.IsSet(RPCGlobalGasCapFlag.Name) { cfg.RPCGasCap = ctx.Uint64(RPCGlobalGasCapFlag.Name) @@ -2238,13 +2246,15 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { Fatalf("Could not read genesis from database: %v", err) } if !genesis.Config.TerminalTotalDifficultyPassed { - Fatalf("Bad developer-mode genesis configuration: terminalTotalDifficultyPassed must be true in developer mode") + Fatalf("Bad developer-mode genesis configuration: terminalTotalDifficultyPassed must be true") } if genesis.Config.TerminalTotalDifficulty == nil { - Fatalf("Bad developer-mode genesis configuration: terminalTotalDifficulty must be specified.") + Fatalf("Bad developer-mode genesis configuration: terminalTotalDifficulty must be specified") + } else if genesis.Config.TerminalTotalDifficulty.Cmp(big.NewInt(0)) != 0 { + Fatalf("Bad developer-mode genesis configuration: terminalTotalDifficulty must be 0") } - if genesis.Difficulty.Cmp(genesis.Config.TerminalTotalDifficulty) != 1 { - Fatalf("Bad developer-mode genesis configuration: genesis block difficulty must be > terminalTotalDifficulty") + if genesis.Difficulty.Cmp(big.NewInt(0)) != 0 { + Fatalf("Bad developer-mode genesis configuration: difficulty must be 0") } } chaindb.Close() @@ -2713,7 +2723,10 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh if ctx.IsSet(TriesInMemoryFlag.Name) { cache.TriesInMemory = ctx.Uint64(TriesInMemoryFlag.Name) } - vmcfg := vm.Config{EnablePreimageRecording: ctx.Bool(VMEnableDebugFlag.Name)} + vmcfg := vm.Config{ + EnablePreimageRecording: ctx.Bool(VMEnableDebugFlag.Name), + EnableWitnessCollection: ctx.Bool(CollectWitnessFlag.Name), + } if ctx.IsSet(VMTraceFlag.Name) { if name := ctx.String(VMTraceFlag.Name); name != "" { var config json.RawMessage diff --git a/cmd/utils/history_test.go b/cmd/utils/history_test.go index aafc512e7c..6ec2459d29 100644 --- a/cmd/utils/history_test.go +++ b/cmd/utils/history_test.go @@ -162,8 +162,7 @@ func TestHistoryImportAndExport(t *testing.T) { } // Now import Era. - freezer := t.TempDir() - db2, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), freezer, "", false, false, false, false, false) + db2, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "",, "", false, false, false, false, false) if err != nil { panic(err) } diff --git a/common/math/big.go b/common/math/big.go index 721b297c9c..d9748d01a3 100644 --- a/common/math/big.go +++ b/common/math/big.go @@ -54,7 +54,7 @@ func NewHexOrDecimal256(x int64) *HexOrDecimal256 { // It is similar to UnmarshalText, but allows parsing real decimals too, not just // quoted decimal strings. func (i *HexOrDecimal256) UnmarshalJSON(input []byte) error { - if len(input) > 0 && input[0] == '"' { + if len(input) > 1 && input[0] == '"' { input = input[1 : len(input)-1] } return i.UnmarshalText(input) diff --git a/common/math/big_test.go b/common/math/big_test.go index 803b5e1cc6..ee8f09e7b4 100644 --- a/common/math/big_test.go +++ b/common/math/big_test.go @@ -180,9 +180,9 @@ func BenchmarkByteAtOld(b *testing.B) { func TestReadBits(t *testing.T) { check := func(input string) { want, _ := hex.DecodeString(input) - int, _ := new(big.Int).SetString(input, 16) + n, _ := new(big.Int).SetString(input, 16) buf := make([]byte, len(want)) - ReadBits(int, buf) + ReadBits(n, buf) if !bytes.Equal(buf, want) { t.Errorf("have: %x\nwant: %x", buf, want) } diff --git a/common/math/integer.go b/common/math/integer.go index db17426ffd..5defa75f28 100644 --- a/common/math/integer.go +++ b/common/math/integer.go @@ -46,7 +46,7 @@ type HexOrDecimal64 uint64 // It is similar to UnmarshalText, but allows parsing real decimals too, not just // quoted decimal strings. func (i *HexOrDecimal64) UnmarshalJSON(input []byte) error { - if len(input) > 0 && input[0] == '"' { + if len(input) > 1 && input[0] == '"' { input = input[1 : len(input)-1] } return i.UnmarshalText(input) @@ -54,11 +54,11 @@ func (i *HexOrDecimal64) UnmarshalJSON(input []byte) error { // UnmarshalText implements encoding.TextUnmarshaler. func (i *HexOrDecimal64) UnmarshalText(input []byte) error { - int, ok := ParseUint64(string(input)) + n, ok := ParseUint64(string(input)) if !ok { return fmt.Errorf("invalid hex or decimal integer %q", input) } - *i = HexOrDecimal64(int) + *i = HexOrDecimal64(n) return nil } diff --git a/common/types.go b/common/types.go index b914787d13..fdb25f1b34 100644 --- a/common/types.go +++ b/common/types.go @@ -468,7 +468,7 @@ func (d *Decimal) UnmarshalJSON(input []byte) error { if !isString(input) { return &json.UnmarshalTypeError{Value: "non-string", Type: reflect.TypeOf(uint64(0))} } - if i, err := strconv.ParseInt(string(input[1:len(input)-1]), 10, 64); err == nil { + if i, err := strconv.ParseUint(string(input[1:len(input)-1]), 10, 64); err == nil { *d = Decimal(i) return nil } else { diff --git a/common/types_test.go b/common/types_test.go index cec689ea39..11247b117b 100644 --- a/common/types_test.go +++ b/common/types_test.go @@ -21,6 +21,7 @@ import ( "database/sql/driver" "encoding/json" "fmt" + "math" "math/big" "reflect" "strings" @@ -595,3 +596,29 @@ func BenchmarkPrettyDuration(b *testing.B) { } b.Logf("Post %s", a) } + +func TestDecimalUnmarshalJSON(t *testing.T) { + // These should error + for _, tc := range []string{``, `"`, `""`, `"-1"`} { + if err := new(Decimal).UnmarshalJSON([]byte(tc)); err == nil { + t.Errorf("input %s should cause error", tc) + } + } + // These should succeed + for _, tc := range []struct { + input string + want uint64 + }{ + {`"0"`, 0}, + {`"9223372036854775807"`, math.MaxInt64}, + {`"18446744073709551615"`, math.MaxUint64}, + } { + have := new(Decimal) + if err := have.UnmarshalJSON([]byte(tc.input)); err != nil { + t.Errorf("input %q triggered error: %v", tc.input, err) + } + if uint64(*have) != tc.want { + t.Errorf("input %q, have %d want %d", tc.input, *have, tc.want) + } + } +} diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go index 77addf6662..a3bf12ea2f 100644 --- a/consensus/beacon/consensus.go +++ b/consensus/beacon/consensus.go @@ -400,7 +400,7 @@ func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea // Assign the final state root to header. header.Root = state.IntermediateRoot(true) - return types.NewBlockWithWithdrawals(header, txs, uncles, receipts, withdrawals, trie.NewStackTrie(nil)), receipts, nil + return types.NewBlock(header, &types.Body{Transactions: txs, Uncles: uncles, Withdrawals: withdrawals}, receipts, trie.NewStackTrie(nil)), receipts, nil } // Seal generates a new sealing request for the given input block and pushes diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index b2c2a99fe4..a8174e9c5d 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -604,8 +604,8 @@ func (c *Clique) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header * // Assign the final state root to header. header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) - // Assemble and return the final block for sealing - return types.NewBlock(header, txs, nil, receipts, trie.NewStackTrie(nil)), receipts, nil + // Assemble and return the final block for sealing. + return types.NewBlock(header, &types.Body{Transactions: txs, Uncles: uncles}, receipts, trie.NewStackTrie(nil)), receipts, nil } // Authorize injects a private key into the consensus engine to mint new blocks diff --git a/consensus/clique/snapshot.go b/consensus/clique/snapshot.go index 8ff0b3a70f..d0b15e9489 100644 --- a/consensus/clique/snapshot.go +++ b/consensus/clique/snapshot.go @@ -19,6 +19,7 @@ package clique import ( "bytes" "encoding/json" + "maps" "slices" "time" @@ -108,28 +109,16 @@ func (s *Snapshot) store(db ethdb.Database) error { // copy creates a deep copy of the snapshot, though not the individual votes. func (s *Snapshot) copy() *Snapshot { - cpy := &Snapshot{ + return &Snapshot{ config: s.config, sigcache: s.sigcache, Number: s.Number, Hash: s.Hash, - Signers: make(map[common.Address]struct{}), - Recents: make(map[uint64]common.Address), - Votes: make([]*Vote, len(s.Votes)), - Tally: make(map[common.Address]Tally), - } - for signer := range s.Signers { - cpy.Signers[signer] = struct{}{} + Signers: maps.Clone(s.Signers), + Recents: maps.Clone(s.Recents), + Votes: slices.Clone(s.Votes), + Tally: maps.Clone(s.Tally), } - for block, signer := range s.Recents { - cpy.Recents[block] = signer - } - for address, tally := range s.Tally { - cpy.Tally[address] = tally - } - copy(cpy.Votes, s.Votes) - - return cpy } // validVote returns whether it makes sense to cast the specified vote in the diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index e967965ff5..0f3c537fb2 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -528,7 +528,7 @@ func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) // Header seems complete, assemble into a block and return - return types.NewBlock(header, txs, uncles, receipts, trie.NewStackTrie(nil)), receipts, nil + return types.NewBlock(header, &types.Body{Transactions: txs, Uncles: uncles}, receipts, trie.NewStackTrie(nil)), receipts, nil } func (ethash *Ethash) Delay(_ consensus.ChainReader, _ *types.Header, _ *time.Duration) *time.Duration { @@ -574,12 +574,6 @@ func (ethash *Ethash) SealHash(header *types.Header) (hash common.Hash) { return hash } -// Some weird constants to avoid constant memory allocs for them. -var ( - u256_8 = uint256.NewInt(8) - u256_32 = uint256.NewInt(32) -) - // accumulateRewards credits the coinbase of the given block with the mining // reward. The total reward consists of the static block reward and rewards for // included uncles. The coinbase of each uncle block is also rewarded. @@ -601,10 +595,10 @@ func accumulateRewards(config *params.ChainConfig, stateDB *state.StateDB, heade r.AddUint64(uNum, 8) r.Sub(r, hNum) r.Mul(r, blockReward) - r.Div(r, u256_8) + r.Rsh(r, 3) stateDB.AddBalance(uncle.Coinbase, r, tracing.BalanceIncreaseRewardMineUncle) - r.Div(blockReward, u256_32) + r.Rsh(blockReward, 5) reward.Add(reward, r) } stateDB.AddBalance(header.Coinbase, reward, tracing.BalanceIncreaseRewardMineBlock) diff --git a/consensus/parlia/parlia.go b/consensus/parlia/parlia.go index c7eba0c2de..9da22e5f78 100644 --- a/consensus/parlia/parlia.go +++ b/consensus/parlia/parlia.go @@ -1442,7 +1442,7 @@ func (p *Parlia) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header * wg.Done() }() go func() { - blk = types.NewBlock(header, txs, nil, receipts, trie.NewStackTrie(nil)) + blk = types.NewBlock(header, &types.Body{Transactions: txs}, receipts, trie.NewStackTrie(nil)) wg.Done() }() wg.Wait() diff --git a/core/block_validator.go b/core/block_validator.go index 6b292ddbe4..80aa1d71e3 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -20,8 +20,10 @@ import ( "errors" "fmt" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/stateless" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie" @@ -43,15 +45,13 @@ func EnableRemoteVerifyManager(remoteValidator *remoteVerifyManager) BlockValida type BlockValidator struct { config *params.ChainConfig // Chain configuration options bc *BlockChain // Canonical block chain - engine consensus.Engine // Consensus engine used for validating remoteValidator *remoteVerifyManager } // NewBlockValidator returns a new block validator which is safe for re-use -func NewBlockValidator(config *params.ChainConfig, blockchain *BlockChain, engine consensus.Engine, opts ...BlockValidatorOption) *BlockValidator { +func NewBlockValidator(config *params.ChainConfig, blockchain *BlockChain, opts ...BlockValidatorOption) *BlockValidator { validator := &BlockValidator{ config: config, - engine: engine, bc: blockchain, } @@ -73,7 +73,7 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error { // Header validity is known at this point. Here we verify that uncles, transactions // and withdrawals given in the block body match the header. header := block.Header() - if err := v.engine.VerifyUncles(v.bc, block); err != nil { + if err := v.bc.engine.VerifyUncles(v.bc, block); err != nil { return err } if hash := types.CalcUncleHash(block.Uncles()); hash != header.UncleHash { @@ -162,7 +162,7 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error { // ValidateState validates the various changes that happen after a state transition, // such as amount of used gas, the receipt roots and the state root itself. -func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateDB, receipts types.Receipts, usedGas uint64) error { +func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateDB, receipts types.Receipts, usedGas uint64, stateless bool) error { header := block.Header() if block.GasUsed() != usedGas { return fmt.Errorf("invalid gas used (remote: %d local: %d)", block.GasUsed(), usedGas) @@ -177,20 +177,28 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD } return nil }, - func() error { + } + // In stateless mode, return early because the receipt and state root are not + // provided through the witness, rather the cross validator needs to return it. + if !stateless { + validateFuns = append(validateFuns, func() error { + // The receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, Rn]])) receiptSha := types.DeriveSha(receipts, trie.NewStackTrie(nil)) if receiptSha != header.ReceiptHash { return fmt.Errorf("invalid receipt root hash (remote: %x local: %x)", header.ReceiptHash, receiptSha) } return nil - }, + }) + validateFuns = append(validateFuns, func() error { + // Validate the state root against the received state root and throw + // an error if they don't match. + if root := statedb.IntermediateRoot(v.config.IsEIP158(header.Number)); header.Root != root { + return fmt.Errorf("invalid merkle root (remote: %x local: %x) dberr: %w", header.Root, root, statedb.Error()) + } + return nil + }) } - validateFuns = append(validateFuns, func() error { - if root := statedb.IntermediateRoot(v.config.IsEIP158(header.Number)); header.Root != root { - return fmt.Errorf("invalid merkle root (remote: %x local: %x) dberr: %w", header.Root, root, statedb.Error()) - } - return nil - }) + validateRes := make(chan error, len(validateFuns)) for _, f := range validateFuns { tmpFunc := f @@ -213,6 +221,28 @@ func (v *BlockValidator) RemoteVerifyManager() *remoteVerifyManager { return v.remoteValidator } +// ValidateWitness cross validates a block execution with stateless remote clients. +// +// Normally we'd distribute the block witness to remote cross validators, wait +// for them to respond and then merge the results. For now, however, it's only +// Geth, so do an internal stateless run. +func (v *BlockValidator) ValidateWitness(witness *stateless.Witness, receiptRoot common.Hash, stateRoot common.Hash) error { + // Run the cross client stateless execution + // TODO(karalabe): Self-stateless for now, swap with other clients + crossReceiptRoot, crossStateRoot, err := ExecuteStateless(v.config, witness) + if err != nil { + return fmt.Errorf("stateless execution failed: %v", err) + } + // Stateless cross execution suceeeded, validate the withheld computed fields + if crossReceiptRoot != receiptRoot { + return fmt.Errorf("cross validator receipt root mismatch (cross: %x local: %x)", crossReceiptRoot, receiptRoot) + } + if crossStateRoot != stateRoot { + return fmt.Errorf("cross validator state root mismatch (cross: %x local: %x)", crossStateRoot, stateRoot) + } + return nil +} + // CalcGasLimit computes the gas limit of the next block after parent. It aims // to keep the baseline gas close to the provided target, and increase it towards // the target if the baseline gas is lower. diff --git a/core/block_validator_test.go b/core/block_validator_test.go index 6cdf7a2bef..60697dcc0e 100644 --- a/core/block_validator_test.go +++ b/core/block_validator_test.go @@ -154,12 +154,10 @@ func testHeaderVerificationForMerging(t *testing.T, isClique bool) { preHeaders := make([]*types.Header, len(preBlocks)) for i, block := range preBlocks { preHeaders[i] = block.Header() - t.Logf("Pre-merge header: %d", block.NumberU64()) } postHeaders := make([]*types.Header, len(postBlocks)) for i, block := range postBlocks { postHeaders[i] = block.Header() - t.Logf("Post-merge header: %d", block.NumberU64()) } // Run the header checker for blocks one-by-one, checking for both valid and invalid nonces chain, _ := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{}, nil, nil) diff --git a/core/blockchain.go b/core/blockchain.go index 015b12502b..1ade728567 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -42,6 +42,7 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state/snapshot" + "github.com/ethereum/go-ethereum/core/stateless" "github.com/ethereum/go-ethereum/core/systemcontracts" "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" @@ -82,7 +83,6 @@ var ( accountCommitTimer = metrics.NewRegisteredResettingTimer("chain/account/commits", nil) storageReadTimer = metrics.NewRegisteredResettingTimer("chain/storage/reads", nil) - storageHashTimer = metrics.NewRegisteredResettingTimer("chain/storage/hashes", nil) storageUpdateTimer = metrics.NewRegisteredResettingTimer("chain/storage/updates", nil) storageCommitTimer = metrics.NewRegisteredResettingTimer("chain/storage/commits", nil) @@ -118,7 +118,6 @@ const ( txLookupCacheLimit = 1024 maxFutureBlocks = 256 maxTimeFutureBlocks = 30 - TriesInMemory = 128 maxBeyondBlocks = 2048 prefetchTxNumber = 100 @@ -384,18 +383,18 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis diffQueueBuffer: make(chan *types.DiffLayer), logger: vmConfig.Tracer, } - bc.flushInterval.Store(int64(cacheConfig.TrieTimeLimit)) - bc.forker = NewForkChoice(bc, shouldPreserve) - bc.stateCache = state.NewDatabaseWithNodeDB(bc.db, bc.triedb) - bc.validator = NewBlockValidator(chainConfig, bc, engine) - bc.prefetcher = NewStatePrefetcher(chainConfig, bc, engine) - bc.processor = NewStateProcessor(chainConfig, bc, engine) - var err error bc.hc, err = NewHeaderChain(db, chainConfig, engine, bc.insertStopped) if err != nil { return nil, err } + bc.flushInterval.Store(int64(cacheConfig.TrieTimeLimit)) + bc.forker = NewForkChoice(bc, shouldPreserve) + bc.stateCache = state.NewDatabaseWithNodeDB(bc.db, bc.triedb) + bc.validator = NewBlockValidator(chainConfig, bc) + bc.prefetcher = NewStatePrefetcher(chainConfig, bc.hc) + bc.processor = NewStateProcessor(chainConfig, bc.hc) + bc.genesisBlock = bc.GetBlockByNumber(0) if bc.genesisBlock == nil { return nil, ErrNoGenesis @@ -1369,7 +1368,7 @@ func (bc *BlockChain) Stop() { if !bc.cacheConfig.TrieDirtyDisabled { triedb := bc.triedb var once sync.Once - for _, offset := range []uint64{0, 1, bc.TriesInMemory() - 1} { + for _, offset := range []uint64{0, 1, state.TriesInMemory - 1} { if number := bc.CurrentBlock().Number.Uint64(); number > offset { recent := bc.GetBlockByNumber(number - offset) log.Info("Writing cached state to disk", "block", recent.Number(), "hash", recent.Hash(), "root", recent.Root()) @@ -1583,8 +1582,8 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [ // Delete block data from the main database. var ( - canonHashes = make(map[common.Hash]struct{}) blockBatch = bc.db.BlockStore().NewBatch() + canonHashes = make(map[common.Hash]struct{}, len(blockChain)) ) for _, block := range blockChain { canonHashes[block.Hash()] = struct{}{} @@ -1748,11 +1747,11 @@ func (bc *BlockChain) writeKnownBlock(block *types.Block) error { // writeBlockWithState writes block, metadata and corresponding state data to the // database. -func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.Receipt, state *state.StateDB) error { +func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.Receipt, statedb *state.StateDB) error { // Calculate the total difficulty of the block ptd := bc.GetTd(block.ParentHash(), block.NumberU64()-1) if ptd == nil { - state.StopPrefetcher() + statedb.StopPrefetcher() return consensus.ErrUnknownAncestor } // Make sure no inconsistent state is leaked during insertion @@ -1774,9 +1773,9 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. rawdb.WriteBlobSidecars(blockBatch, block.Hash(), block.NumberU64(), block.Sidecars()) } if bc.db.StateStore() != nil { - rawdb.WritePreimages(bc.db.StateStore(), state.Preimages()) + rawdb.WritePreimages(bc.db.StateStore(), statedb.Preimages()) } else { - rawdb.WritePreimages(blockBatch, state.Preimages()) + rawdb.WritePreimages(blockBatch, statedb.Preimages()) } if err := blockBatch.Write(); err != nil { log.Crit("Failed to write block into disk", "err", err) @@ -1811,7 +1810,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. // Flush limits are not considered for the first TriesInMemory blocks. current := block.NumberU64() - if current <= bc.TriesInMemory() { + if current <= state.TriesInMemory { return nil } // If we exceeded our memory allowance, flush matured singleton nodes to disk @@ -1823,7 +1822,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. triedb.Cap(limit - ethdb.IdealBatchSize) } // Find the next state trie we need to commit - chosen := current - bc.triesInMemory + chosen := current - state.TriesInMemory flushInterval := time.Duration(bc.flushInterval.Load()) // If we exceeded out time allowance, flush an entire trie to disk if bc.gcproc > flushInterval { @@ -1842,8 +1841,8 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. } else { // If we're exceeding limits but haven't reached a large enough memory gap, // warn the user that the system is becoming unstable. - if chosen < bc.lastWrite+bc.triesInMemory && bc.gcproc >= 2*flushInterval { - log.Info("State in memory for too long, committing", "time", bc.gcproc, "allowance", flushInterval, "optimum", float64(chosen-bc.lastWrite)/float64(bc.triesInMemory)) + if chosen < bc.lastWrite+state.TriesInMemory && bc.gcproc >= 2*flushInterval { + log.Info("State in memory for too long, committing", "time", bc.gcproc, "allowance", flushInterval, "optimum", float64(chosen-bc.lastWrite)/float64(state.TriesInMemory)) } // Flush an entire trie and restart the counters triedb.Commit(header.Root, true) @@ -1871,7 +1870,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. return nil } // Commit all cached state changes into underlying memory database. - _, diffLayer, err := state.Commit(block.NumberU64(), tryCommitTrieDB) + _, diffLayer, err := statedb.Commit(block.NumberU64(), bc.chainConfig.IsEIP158(block.Number()), tryCommitTrieDB) if err != nil { return err } @@ -2233,13 +2232,24 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error) bc.updateHighestVerifiedHeader(block.Header()) statedb.SetLogger(bc.logger) - // Enable prefetching to pull in trie node paths while processing transactions - statedb.StartPrefetcher("chain") + // If we are past Byzantium, enable prefetching to pull in trie node paths + // while processing transactions. Before Byzantium the prefetcher is mostly + // useless due to the intermediate root hashing after each transaction. + if bc.chainConfig.IsByzantium(block.Number()) { + var witness *stateless.Witness + if bc.vmConfig.EnableWitnessCollection { + witness, err = stateless.NewWitness(bc, block) + if err != nil { + return it.index, err + } + } + statedb.StartPrefetcher("chain", witness) + } + interruptCh := make(chan struct{}) // For diff sync, it may fallback to full sync, so we still do prefetch if len(block.Transactions()) >= prefetchTxNumber { // do Prefetch in a separate goroutine to avoid blocking the critical path - // 1.do state prefetch for snapshot cache throwaway := statedb.CopyDoPrefetch() // Disable tracing for prefetcher executions. @@ -2380,12 +2390,19 @@ func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, s // Validate the state using the default validator vstart := time.Now() - if err := bc.validator.ValidateState(block, statedb, receipts, usedGas); err != nil { + if err := bc.validator.ValidateState(block, statedb, receipts, usedGas, false); err != nil { bc.reportBlock(block, receipts, err) statedb.StopPrefetcher() return nil, err } vtime := time.Since(vstart) + + if witness := statedb.Witness(); witness != nil { + if err = bc.validator.ValidateWitness(witness, block.ReceiptHash(), block.Root()); err != nil { + bc.reportBlock(block, receipts, err) + return nil, fmt.Errorf("cross verification failed: %v", err) + } + } proctime := time.Since(start) // processing + validation // Update the metrics touched during block processing and validation @@ -2396,8 +2413,7 @@ func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, s accountUpdateTimer.Update(statedb.AccountUpdates) // Account updates are complete(in validation) storageUpdateTimer.Update(statedb.StorageUpdates) // Storage updates are complete(in validation) accountHashTimer.Update(statedb.AccountHashes) // Account hashes are complete(in validation) - storageHashTimer.Update(statedb.StorageHashes) // Storage hashes are complete(in validation) - triehash := statedb.AccountHashes + statedb.StorageHashes // The time spent on tries hashing + triehash := statedb.AccountHashes // The time spent on tries hashing trieUpdate := statedb.AccountUpdates + statedb.StorageUpdates // The time spent on tries update trieRead := statedb.SnapshotAccountReads + statedb.AccountReads // The time spent on account read trieRead += statedb.SnapshotStorageReads + statedb.StorageReads // The time spent on storage read @@ -2424,7 +2440,7 @@ func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, s snapshotCommitTimer.Update(statedb.SnapshotCommits) // Snapshot commits are complete, we can mark them triedbCommitTimer.Update(statedb.TrieDBCommits) // Trie database commits are complete, we can mark them - blockWriteTimer.Update(time.Since(wstart) - statedb.AccountCommits - statedb.StorageCommits - statedb.SnapshotCommits - statedb.TrieDBCommits) + blockWriteTimer.Update(time.Since(wstart) - max(statedb.AccountCommits, statedb.StorageCommits) /* concurrent */ - statedb.SnapshotCommits - statedb.TrieDBCommits) blockInsertTimer.UpdateSince(start) return &blockProcessingResult{usedGas: usedGas, procTime: proctime, status: status}, nil @@ -3104,7 +3120,10 @@ func (bc *BlockChain) InsertHeaderChain(chain []*types.Header) (int, error) { return 0, err } -func (bc *BlockChain) TriesInMemory() uint64 { return bc.triesInMemory } +func (bc *BlockChain) HeadChain() *HeaderChain { + return bc.hc +} +func (bc *BlockChain) TriesInMemory() uint64 { return state.TriesInMemory } func EnablePersistDiff(limit uint64) BlockChainOption { return func(chain *BlockChain) (*BlockChain, error) { @@ -3113,7 +3132,7 @@ func EnablePersistDiff(limit uint64) BlockChainOption { } } -func EnableBlockValidator(chainConfig *params.ChainConfig, engine consensus.Engine, mode VerifyMode, peers verifyPeers) BlockChainOption { +func EnableBlockValidator(chainConfig *params.ChainConfig, _ consensus.Engine, mode VerifyMode, peers verifyPeers) BlockChainOption { return func(bc *BlockChain) (*BlockChain, error) { if mode.NeedRemoteVerify() { vm, err := NewVerifyManager(bc, peers, mode == InsecureVerify) @@ -3121,7 +3140,7 @@ func EnableBlockValidator(chainConfig *params.ChainConfig, engine consensus.Engi return nil, err } go vm.mainLoop() - bc.validator = NewBlockValidator(chainConfig, bc, engine, EnableRemoteVerifyManager(vm)) + bc.validator = NewBlockValidator(chainConfig, bc, EnableRemoteVerifyManager(vm)) } return bc, nil } diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 371741bfd6..dc50756a1f 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -52,8 +52,6 @@ import ( var ( canonicalSeed = 1 forkSeed = 2 - - TestTriesInMemory = 128 ) // newCanonical creates a chain database, and injects a deterministic canonical @@ -176,8 +174,7 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error { blockchain.reportBlock(block, receipts, err) return err } - err = blockchain.validator.ValidateState(block, statedb, receipts, usedGas) - if err != nil { + if err = blockchain.validator.ValidateState(block, statedb, receipts, usedGas, false); err != nil { blockchain.reportBlock(block, receipts, err) return err } @@ -797,7 +794,7 @@ func testFastVsFullChains(t *testing.T, scheme string) { t.Fatalf("failed to insert receipt %d: %v", n, err) } // Freezer style fast import the chain. - ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false, false) + ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false, false, false, false, false) if err != nil { t.Fatalf("failed to create temp freezer db: %v", err) } @@ -887,12 +884,12 @@ func testLightVsFastVsFullChainHeads(t *testing.T, scheme string) { BaseFee: big.NewInt(params.InitialBaseFee), } ) - height := uint64(1024) + height := uint64(64) _, blocks, receipts := GenerateChainWithGenesis(gspec, ethash.NewFaker(), int(height), nil) // makeDb creates a db instance for testing. makeDb := func() ethdb.Database { - db, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false, false) + db, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(),"", "", false, false, false, false, false) if err != nil { t.Fatalf("failed to create temp freezer db: %v", err) } @@ -1724,7 +1721,7 @@ func TestTrieForkGC(t *testing.T) { Config: params.TestChainConfig, BaseFee: big.NewInt(params.InitialBaseFee), } - genDb, blocks, _ := GenerateChainWithGenesis(genesis, engine, 2*TriesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) }) + genDb, blocks, _ := GenerateChainWithGenesis(genesis, engine, 2*state.TriesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) }) // Generate a bunch of fork blocks, each side forking from the canonical chain forks := make([]*types.Block, len(blocks)) @@ -1752,7 +1749,7 @@ func TestTrieForkGC(t *testing.T) { } } // Dereference all the recent tries and ensure no past trie is left in - for i := 0; i < TriesInMemory; i++ { + for i := 0; i < state.TriesInMemory; i++ { chain.TrieDB().Dereference(blocks[len(blocks)-1-i].Root()) chain.TrieDB().Dereference(forks[len(blocks)-1-i].Root()) } @@ -1776,11 +1773,11 @@ func testLargeReorgTrieGC(t *testing.T, scheme string) { BaseFee: big.NewInt(params.InitialBaseFee), } genDb, shared, _ := GenerateChainWithGenesis(genesis, engine, 64, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) }) - original, _ := GenerateChain(genesis.Config, shared[len(shared)-1], engine, genDb, 2*TriesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{2}) }) - competitor, _ := GenerateChain(genesis.Config, shared[len(shared)-1], engine, genDb, 2*TriesInMemory+1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{3}) }) + original, _ := GenerateChain(genesis.Config, shared[len(shared)-1], engine, genDb, 2*state.TriesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{2}) }) + competitor, _ := GenerateChain(genesis.Config, shared[len(shared)-1], engine, genDb, 2*state.TriesInMemory+1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{3}) }) // Import the shared chain and the original canonical one - db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false, false) + db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false, false, false, false, false) defer db.Close() chain, err := NewBlockChain(db, DefaultCacheConfigWithScheme(scheme), genesis, nil, engine, vm.Config{}, nil, nil) @@ -1816,7 +1813,7 @@ func testLargeReorgTrieGC(t *testing.T, scheme string) { } // In path-based trie database implementation, it will keep 128 diff + 1 disk // layers, totally 129 latest states available. In hash-based it's 128. - states := TestTriesInMemory + states := state.TriesInMemory if scheme == rawdb.PathScheme { states = states + 1 } @@ -1845,7 +1842,7 @@ func testBlockchainRecovery(t *testing.T, scheme string) { funds = big.NewInt(1000000000) gspec = &Genesis{Config: params.TestChainConfig, Alloc: types.GenesisAlloc{address: {Balance: funds}}} ) - height := uint64(1024) + height := uint64(64) _, blocks, receipts := GenerateChainWithGenesis(gspec, ethash.NewFaker(), int(height), nil) // Import the chain as a ancient-first node and ensure all pointers are updated @@ -1920,7 +1917,7 @@ func testInsertReceiptChainRollback(t *testing.T, scheme string) { } // Set up a BlockChain that uses the ancient store. - ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false, false) + ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "",, "", false, false, false, false, false) if err != nil { t.Fatalf("failed to create temp freezer db: %v", err) } @@ -1984,13 +1981,13 @@ func testLowDiffLongChain(t *testing.T, scheme string) { } // We must use a pretty long chain to ensure that the fork doesn't overtake us // until after at least 128 blocks post tip - genDb, blocks, _ := GenerateChainWithGenesis(genesis, engine, 6*TriesInMemory, func(i int, b *BlockGen) { + genDb, blocks, _ := GenerateChainWithGenesis(genesis, engine, 6*state.TriesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) b.OffsetTime(-9) }) // Import the canonical chain - diskdb, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false, false) + diskdb, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false, false, false, false, false) defer diskdb.Close() chain, err := NewBlockChain(diskdb, DefaultCacheConfigWithScheme(scheme), genesis, nil, engine, vm.Config{}, nil, nil) @@ -2004,7 +2001,7 @@ func testLowDiffLongChain(t *testing.T, scheme string) { } // Generate fork chain, starting from an early block parent := blocks[10] - fork, _ := GenerateChain(genesis.Config, parent, engine, genDb, 8*TriesInMemory, func(i int, b *BlockGen) { + fork, _ := GenerateChain(genesis.Config, parent, engine, genDb, 8*state.TriesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{2}) }) @@ -2067,7 +2064,7 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon // Set the terminal total difficulty in the config gspec.Config.TerminalTotalDifficulty = big.NewInt(0) } - genDb, blocks, _ := GenerateChainWithGenesis(gspec, engine, 2*TriesInMemory, func(i int, gen *BlockGen) { + genDb, blocks, _ := GenerateChainWithGenesis(gspec, engine, 2*state.TriesInMemory, func(i int, gen *BlockGen) { tx, err := types.SignTx(types.NewTransaction(nonce, common.HexToAddress("deadbeef"), big.NewInt(100), 21000, big.NewInt(int64(i+1)*params.GWei), nil), signer, key) if err != nil { t.Fatalf("failed to create tx: %v", err) @@ -2082,9 +2079,9 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon t.Fatalf("block %d: failed to insert into chain: %v", n, err) } - lastPrunedIndex := len(blocks) - TestTriesInMemory - 1 + lastPrunedIndex := len(blocks) - state.TriesInMemory - 1 lastPrunedBlock := blocks[lastPrunedIndex-1] - firstNonPrunedBlock := blocks[len(blocks)-TestTriesInMemory] + firstNonPrunedBlock := blocks[len(blocks)-state.TriesInMemory] // Verify pruning of lastPrunedBlock if chain.HasBlockAndState(lastPrunedBlock.Hash(), lastPrunedBlock.NumberU64()) { @@ -2111,7 +2108,7 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon // Generate fork chain, make it longer than canon parentIndex := lastPrunedIndex + blocksBetweenCommonAncestorAndPruneblock parent := blocks[parentIndex] - fork, _ := GenerateChain(gspec.Config, parent, engine, genDb, 2*TriesInMemory, func(i int, b *BlockGen) { + fork, _ := GenerateChain(gspec.Config, parent, engine, genDb, 2*state.TriesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{2}) if int(b.header.Number.Uint64()) >= mergeBlock { b.SetPoS() @@ -2202,7 +2199,7 @@ func testInsertKnownChainData(t *testing.T, typ string, scheme string) { b.OffsetTime(-9) // A higher difficulty }) // Import the shared chain and the original canonical one - chaindb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false, false) + chaindb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false, false, false, false, false) if err != nil { t.Fatalf("failed to create temp freezer db: %v", err) } @@ -2373,7 +2370,7 @@ func testInsertKnownChainDataWithMerging(t *testing.T, typ string, mergeHeight i } }) // Import the shared chain and the original canonical one - chaindb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false, false) + chaindb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false, false, false, false, false) if err != nil { t.Fatalf("failed to create temp freezer db: %v", err) } @@ -2754,7 +2751,7 @@ func testSideImportPrunedBlocks(t *testing.T, scheme string) { BaseFee: big.NewInt(params.InitialBaseFee), } // Generate and import the canonical chain - _, blocks, _ := GenerateChainWithGenesis(genesis, engine, 2*TriesInMemory, nil) + _, blocks, _ := GenerateChainWithGenesis(genesis, engine, 2*state.TriesInMemory, nil) chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), genesis, nil, engine, vm.Config{}, nil, nil) if err != nil { @@ -2767,9 +2764,9 @@ func testSideImportPrunedBlocks(t *testing.T, scheme string) { } // In path-based trie database implementation, it will keep 128 diff + 1 disk // layers, totally 129 latest states available. In hash-based it's 128. - states := TestTriesInMemory + states := state.TriesInMemory if scheme == rawdb.PathScheme { - states = TestTriesInMemory + 1 + states = state.TriesInMemory + 1 } lastPrunedIndex := len(blocks) - states - 1 lastPrunedBlock := blocks[lastPrunedIndex] @@ -3665,18 +3662,19 @@ func testSetCanonical(t *testing.T, scheme string) { Alloc: types.GenesisAlloc{address: {Balance: funds}}, BaseFee: big.NewInt(params.InitialBaseFee), } - signer = types.LatestSigner(gspec.Config) - engine = ethash.NewFaker() + signer = types.LatestSigner(gspec.Config) + engine = ethash.NewFaker() + chainLength = 10 ) // Generate and import the canonical chain - _, canon, _ := GenerateChainWithGenesis(gspec, engine, 2*TriesInMemory, func(i int, gen *BlockGen) { + _, canon, _ := GenerateChainWithGenesis(gspec, engine, chainLength, func(i int, gen *BlockGen) { tx, err := types.SignTx(types.NewTransaction(gen.TxNonce(address), common.Address{0x00}, big.NewInt(1000), params.TxGas, gen.header.BaseFee, nil), signer, key) if err != nil { panic(err) } gen.AddTx(tx) }) - diskdb, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false, false) + diskdb, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false, false, false, false, false) defer diskdb.Close() chain, err := NewBlockChain(diskdb, DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{}, nil, nil) @@ -3690,7 +3688,7 @@ func testSetCanonical(t *testing.T, scheme string) { } // Generate the side chain and import them - _, side, _ := GenerateChainWithGenesis(gspec, engine, 2*TriesInMemory, func(i int, gen *BlockGen) { + _, side, _ := GenerateChainWithGenesis(gspec, engine, chainLength, func(i int, gen *BlockGen) { tx, err := types.SignTx(types.NewTransaction(gen.TxNonce(address), common.Address{0x00}, big.NewInt(1), params.TxGas, gen.header.BaseFee, nil), signer, key) if err != nil { panic(err) @@ -3729,8 +3727,8 @@ func testSetCanonical(t *testing.T, scheme string) { verify(side[len(side)-1]) // Reset the chain head to original chain - chain.SetCanonical(canon[TriesInMemory-1]) - verify(canon[TriesInMemory-1]) + chain.SetCanonical(canon[chainLength-1]) + verify(canon[chainLength-1]) } // TestCanonicalHashMarker tests all the canonical hash markers are updated/deleted diff --git a/core/bloombits/scheduler.go b/core/bloombits/scheduler.go index 5fa6248110..e4503be3f1 100644 --- a/core/bloombits/scheduler.go +++ b/core/bloombits/scheduler.go @@ -25,7 +25,7 @@ import ( // request represents a bloom retrieval task to prioritize and pull from the local // database or remotely from the network. type request struct { - section uint64 // Section index to retrieve the a bit-vector from + section uint64 // Section index to retrieve the bit-vector from bit uint // Bit index within the section to retrieve the vector of } diff --git a/core/chain_indexer_test.go b/core/chain_indexer_test.go index f099609015..bf3bde756c 100644 --- a/core/chain_indexer_test.go +++ b/core/chain_indexer_test.go @@ -228,7 +228,7 @@ func (b *testChainIndexBackend) Process(ctx context.Context, header *types.Heade b.t.Error("Unexpected call to Process") // Can't use Fatal since this is not the test's goroutine. // Returning error stops the chainIndexer's updateLoop - return errors.New("Unexpected call to Process") + return errors.New("unexpected call to Process") case b.processCh <- header.Number.Uint64(): } return nil diff --git a/core/chain_makers.go b/core/chain_makers.go index 8d1a9841d9..4f3f572f57 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -33,7 +33,7 @@ import ( "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/triedb" - "github.com/gballet/go-verkle" + "github.com/ethereum/go-verkle" "github.com/holiman/uint256" ) @@ -385,7 +385,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse } // Write state changes to db - root, _, err := statedb.Commit(b.header.Number.Uint64(), nil) + root, _, err := statedb.Commit(b.header.Number.Uint64(), config.IsEIP158(b.header.Number)) if err != nil { panic(fmt.Sprintf("state write error: %v", err)) } @@ -493,9 +493,7 @@ func GenerateVerkleChain(config *params.ChainConfig, parent *types.Block, engine } // Write state changes to db - statedb.Finalise(config.IsEIP158(b.header.Number)) - statedb.AccountsIntermediateRoot() - root, _, err := statedb.Commit(b.header.Number.Uint64(), nil) + root, _, err := statedb.Commit(b.header.Number.Uint64(), config.IsEIP158(b.header.Number)) if err != nil { panic(fmt.Sprintf("state write error: %v", err)) } diff --git a/core/chain_makers_test.go b/core/chain_makers_test.go index a2ec9e6507..6241f3fb69 100644 --- a/core/chain_makers_test.go +++ b/core/chain_makers_test.go @@ -43,12 +43,11 @@ func TestGeneratePOSChain(t *testing.T) { bb = common.Address{0xbb} funds = big.NewInt(0).Mul(big.NewInt(1337), big.NewInt(params.Ether)) config = *params.AllEthashProtocolChanges - asm4788 = common.Hex2Bytes("3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500") gspec = &Genesis{ Config: &config, Alloc: types.GenesisAlloc{ address: {Balance: funds}, - params.BeaconRootsAddress: {Balance: common.Big0, Code: asm4788}, + params.BeaconRootsAddress: {Code: params.BeaconRootsCode}, }, BaseFee: big.NewInt(params.InitialBaseFee), Difficulty: common.Big1, diff --git a/core/error.go b/core/error.go index f33880bde5..088ce0f620 100644 --- a/core/error.go +++ b/core/error.go @@ -70,6 +70,11 @@ var ( // than init code size limit. ErrMaxInitCodeSizeExceeded = errors.New("max initcode size exceeded") + // ErrInsufficientBalanceWitness is returned if the transaction sender has enough + // funds to cover the transfer, but not enough to pay for witness access/modification + // costs for the transaction + ErrInsufficientBalanceWitness = errors.New("insufficient funds to cover witness access costs for transaction") + // ErrInsufficientFunds is returned if the total cost of executing a transaction // is higher than the balance of the user's account. ErrInsufficientFunds = errors.New("insufficient funds for gas * price + value") diff --git a/core/genesis.go b/core/genesis.go index 24e62c81a7..1e9ba6fc54 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -142,8 +142,7 @@ func hashAlloc(ga *types.GenesisAlloc, isVerkle bool) (common.Hash, error) { statedb.SetState(addr, key, value) } } - statedb.IntermediateRoot(false) - root, _, err := statedb.Commit(0, nil) + root, _, err := statedb.Commit(0, false) return root, err } @@ -171,8 +170,7 @@ func flushAlloc(ga *types.GenesisAlloc, db ethdb.Database, triedb *triedb.Databa statedb.SetState(addr, key, value) } } - statedb.IntermediateRoot(false) - root, _, err := statedb.Commit(0, nil) + root, _, err := statedb.Commit(0, false) if err != nil { return err } @@ -505,7 +503,7 @@ func (g *Genesis) ToBlock() *types.Block { } } } - return types.NewBlock(head, nil, nil, nil, trie.NewStackTrie(nil)).WithWithdrawals(withdrawals) + return types.NewBlock(head, &types.Body{Withdrawals: withdrawals}, nil, trie.NewStackTrie(nil)) } // Commit writes the block and state of a genesis specification to the database. @@ -617,6 +615,8 @@ func DeveloperGenesisBlock(gasLimit uint64, faucet *common.Address) *Genesis { common.BytesToAddress([]byte{7}): {Balance: big.NewInt(1)}, // ECScalarMul common.BytesToAddress([]byte{8}): {Balance: big.NewInt(1)}, // ECPairing common.BytesToAddress([]byte{9}): {Balance: big.NewInt(1)}, // BLAKE2b + // Pre-deploy EIP-4788 system contract + params.BeaconRootsAddress: {Nonce: 1, Code: params.BeaconRootsCode, Balance: common.Big0}, }, } if faucet != nil { diff --git a/core/genesis_test.go b/core/genesis_test.go index 4b280bcf13..576cdf71fe 100644 --- a/core/genesis_test.go +++ b/core/genesis_test.go @@ -318,7 +318,7 @@ func TestVerkleGenesisCommit(t *testing.T) { }, } - expected := common.Hex2Bytes("14398d42be3394ff8d50681816a4b7bf8d8283306f577faba2d5bc57498de23b") + expected := common.FromHex("14398d42be3394ff8d50681816a4b7bf8d8283306f577faba2d5bc57498de23b") got := genesis.ToBlock().Root().Bytes() if !bytes.Equal(got, expected) { t.Fatalf("invalid genesis state root, expected %x, got %x", expected, got) @@ -328,7 +328,7 @@ func TestVerkleGenesisCommit(t *testing.T) { triedb := triedb.NewDatabase(db, &triedb.Config{IsVerkle: true, PathDB: pathdb.Defaults}) block := genesis.MustCommit(db, triedb) if !bytes.Equal(block.Root().Bytes(), expected) { - t.Fatalf("invalid genesis state root, expected %x, got %x", expected, got) + t.Fatalf("invalid genesis state root, expected %x, got %x", expected, block.Root()) } // Test that the trie is verkle @@ -336,7 +336,7 @@ func TestVerkleGenesisCommit(t *testing.T) { t.Fatalf("expected trie to be verkle") } - if !rawdb.ExistsAccountTrieNode(db, nil) { + if !rawdb.HasAccountTrieNode(db, nil) { t.Fatal("could not find node") } } diff --git a/core/mkalloc.go b/core/mkalloc.go index 201c2fe7de..cc4955f038 100644 --- a/core/mkalloc.go +++ b/core/mkalloc.go @@ -101,6 +101,7 @@ func main() { if err != nil { panic(err) } + defer file.Close() if err := json.NewDecoder(file).Decode(g); err != nil { panic(err) } diff --git a/core/rawdb/accessors_chain.go b/core/rawdb/accessors_chain.go index ba657d62d3..d11c57546e 100644 --- a/core/rawdb/accessors_chain.go +++ b/core/rawdb/accessors_chain.go @@ -19,7 +19,6 @@ package rawdb import ( "bytes" "encoding/binary" - "errors" "fmt" "math/big" "slices" @@ -756,27 +755,6 @@ func (r *receiptLogs) DecodeRLP(s *rlp.Stream) error { return nil } -// deriveLogFields fills the logs in receiptLogs with information such as block number, txhash, etc. -func deriveLogFields(receipts []*receiptLogs, hash common.Hash, number uint64, txs types.Transactions) error { - logIndex := uint(0) - if len(txs) != len(receipts) { - return errors.New("transaction and receipt count mismatch") - } - for i := 0; i < len(receipts); i++ { - txHash := txs[i].Hash() - // The derived log fields can simply be set from the block and transaction - for j := 0; j < len(receipts[i].Logs); j++ { - receipts[i].Logs[j].BlockNumber = number - receipts[i].Logs[j].BlockHash = hash - receipts[i].Logs[j].TxHash = txHash - receipts[i].Logs[j].TxIndex = uint(i) - receipts[i].Logs[j].Index = logIndex - logIndex++ - } - } - return nil -} - // ReadLogs retrieves the logs for all transactions in a block. In case // receipts is not found, a nil is returned. // Note: ReadLogs does not derive unstored log fields. @@ -814,7 +792,7 @@ func ReadBlock(db ethdb.Reader, hash common.Hash, number uint64) *types.Block { if body == nil { return nil } - return types.NewBlockWithHeader(header).WithBody(body.Transactions, body.Uncles).WithWithdrawals(body.Withdrawals) + return types.NewBlockWithHeader(header).WithBody(*body) } // WriteBlock serializes a block into the database, header and body separately. @@ -1003,7 +981,11 @@ func ReadBadBlock(db ethdb.Reader, hash common.Hash) *types.Block { } for _, bad := range badBlocks { if bad.Header.Hash() == hash { - return types.NewBlockWithHeader(bad.Header).WithBody(bad.Body.Transactions, bad.Body.Uncles).WithWithdrawals(bad.Body.Withdrawals) + block := types.NewBlockWithHeader(bad.Header) + if bad.Body != nil { + block = block.WithBody(*bad.Body) + } + return block } } return nil @@ -1022,7 +1004,11 @@ func ReadAllBadBlocks(db ethdb.Reader) []*types.Block { } var blocks []*types.Block for _, bad := range badBlocks { - blocks = append(blocks, types.NewBlockWithHeader(bad.Header).WithBody(bad.Body.Transactions, bad.Body.Uncles).WithWithdrawals(bad.Body.Withdrawals)) + block := types.NewBlockWithHeader(bad.Header) + if bad.Body != nil { + block = block.WithBody(*bad.Body) + } + blocks = append(blocks, block) } return blocks } diff --git a/core/rawdb/accessors_chain_test.go b/core/rawdb/accessors_chain_test.go index 8c76141369..869958b520 100644 --- a/core/rawdb/accessors_chain_test.go +++ b/core/rawdb/accessors_chain_test.go @@ -728,7 +728,7 @@ func makeTestBlocks(nblock int, txsPerBlock int) []*types.Block { Number: big.NewInt(int64(i)), Extra: []byte("test block"), } - blocks[i] = types.NewBlockWithHeader(header).WithBody(txs, nil) + blocks[i] = types.NewBlockWithHeader(header).WithBody(types.Body{Transactions: txs}) blocks[i].Hash() // pre-cache the block hash } return blocks @@ -919,7 +919,7 @@ func TestDeriveLogFields(t *testing.T) { }), } // Create the corresponding receipts - receipts := []*receiptLogs{ + receipts := []*types.Receipt{ { Logs: []*types.Log{ {Address: common.BytesToAddress([]byte{0x11})}, @@ -943,9 +943,7 @@ func TestDeriveLogFields(t *testing.T) { // Derive log metadata fields number := big.NewInt(1) hash := common.BytesToHash([]byte{0x03, 0x14}) - if err := deriveLogFields(receipts, hash, number.Uint64(), txs); err != nil { - t.Fatal(err) - } + types.Receipts(receipts).DeriveFields(params.TestChainConfig, hash, number.Uint64(), 0, big.NewInt(0), big.NewInt(0), txs) // Iterate over all the computed fields and check that they're correct logIndex := uint(0) diff --git a/core/rawdb/accessors_indexes_test.go b/core/rawdb/accessors_indexes_test.go index a209026fa9..016880d5e4 100644 --- a/core/rawdb/accessors_indexes_test.go +++ b/core/rawdb/accessors_indexes_test.go @@ -76,7 +76,7 @@ func TestLookupStorage(t *testing.T) { tx3 := types.NewTransaction(3, common.BytesToAddress([]byte{0x33}), big.NewInt(333), 3333, big.NewInt(33333), []byte{0x33, 0x33, 0x33}) txs := []*types.Transaction{tx1, tx2, tx3} - block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, txs, nil, nil, newTestHasher()) + block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, &types.Body{Transactions: txs}, nil, newTestHasher()) // Check that no transactions entries are in a pristine database for i, tx := range txs { diff --git a/core/rawdb/accessors_trie.go b/core/rawdb/accessors_trie.go index 886c1c469b..8cd8d60f6e 100644 --- a/core/rawdb/accessors_trie.go +++ b/core/rawdb/accessors_trie.go @@ -24,7 +24,6 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" - "golang.org/x/crypto/sha3" ) // HashScheme is the legacy hash-based state scheme with which trie nodes are @@ -50,7 +49,7 @@ const PathScheme = "path" type hasher struct{ sha crypto.KeccakState } var hasherPool = sync.Pool{ - New: func() interface{} { return &hasher{sha: sha3.NewLegacyKeccak256().(crypto.KeccakState)} }, + New: func() interface{} { return &hasher{sha: crypto.NewKeccakState()} }, } func newHasher() *hasher { @@ -65,9 +64,9 @@ func (h *hasher) release() { hasherPool.Put(h) } -// ReadAccountTrieNode retrieves the account trie node and the associated node +// ReadAccountTrieNodeAndHash retrieves the account trie node and the associated node // hash with the specified node path. -func ReadAccountTrieNode(db ethdb.KeyValueReader, path []byte) ([]byte, common.Hash) { +func ReadAccountTrieNodeAndHash(db ethdb.KeyValueReader, path []byte) ([]byte, common.Hash) { data, err := db.Get(accountTrieNodeKey(path)) if err != nil { return nil, common.Hash{} @@ -77,21 +76,15 @@ func ReadAccountTrieNode(db ethdb.KeyValueReader, path []byte) ([]byte, common.H return data, h.hash(data) } -// HasAccountTrieNode checks the account trie node presence with the specified -// node path and the associated node hash. -func HasAccountTrieNode(db ethdb.KeyValueReader, path []byte, hash common.Hash) bool { - data, err := db.Get(accountTrieNodeKey(path)) - if err != nil { - return false - } - h := newHasher() - defer h.release() - return h.hash(data) == hash +// ReadAccountTrieNode retrieves the account trie node with the specified node path. +func ReadAccountTrieNode(db ethdb.KeyValueReader, path []byte) []byte { + data, _ := db.Get(accountTrieNodeKey(path)) + return data } -// ExistsAccountTrieNode checks the presence of the account trie node with the +// HasAccountTrieNode checks the presence of the account trie node with the // specified node path, regardless of the node hash. -func ExistsAccountTrieNode(db ethdb.KeyValueReader, path []byte) bool { +func HasAccountTrieNode(db ethdb.KeyValueReader, path []byte) bool { has, err := db.Has(accountTrieNodeKey(path)) if err != nil { return false @@ -113,9 +106,9 @@ func DeleteAccountTrieNode(db ethdb.KeyValueWriter, path []byte) { } } -// ReadStorageTrieNode retrieves the storage trie node and the associated node +// ReadStorageTrieNodeAndHash retrieves the storage trie node and the associated node // hash with the specified node path. -func ReadStorageTrieNode(db ethdb.KeyValueReader, accountHash common.Hash, path []byte) ([]byte, common.Hash) { +func ReadStorageTrieNodeAndHash(db ethdb.KeyValueReader, accountHash common.Hash, path []byte) ([]byte, common.Hash) { data, err := db.Get(storageTrieNodeKey(accountHash, path)) if err != nil { return nil, common.Hash{} @@ -125,21 +118,15 @@ func ReadStorageTrieNode(db ethdb.KeyValueReader, accountHash common.Hash, path return data, h.hash(data) } -// HasStorageTrieNode checks the storage trie node presence with the provided -// node path and the associated node hash. -func HasStorageTrieNode(db ethdb.KeyValueReader, accountHash common.Hash, path []byte, hash common.Hash) bool { - data, err := db.Get(storageTrieNodeKey(accountHash, path)) - if err != nil { - return false - } - h := newHasher() - defer h.release() - return h.hash(data) == hash +// ReadStorageTrieNode retrieves the storage trie node with the specified node path. +func ReadStorageTrieNode(db ethdb.KeyValueReader, accountHash common.Hash, path []byte) []byte { + data, _ := db.Get(storageTrieNodeKey(accountHash, path)) + return data } -// ExistsStorageTrieNode checks the presence of the storage trie node with the +// HasStorageTrieNode checks the presence of the storage trie node with the // specified account hash and node path, regardless of the node hash. -func ExistsStorageTrieNode(db ethdb.KeyValueReader, accountHash common.Hash, path []byte) bool { +func HasStorageTrieNode(db ethdb.KeyValueReader, accountHash common.Hash, path []byte) bool { has, err := db.Has(storageTrieNodeKey(accountHash, path)) if err != nil { return false @@ -198,10 +185,18 @@ func HasTrieNode(db ethdb.KeyValueReader, owner common.Hash, path []byte, hash c case HashScheme: return HasLegacyTrieNode(db, hash) case PathScheme: + var blob []byte if owner == (common.Hash{}) { - return HasAccountTrieNode(db, path, hash) + blob = ReadAccountTrieNode(db, path) + } else { + blob = ReadStorageTrieNode(db, owner, path) } - return HasStorageTrieNode(db, owner, path, hash) + if len(blob) == 0 { + return false + } + h := newHasher() + defer h.release() + return h.hash(blob) == hash // exists but not match default: panic(fmt.Sprintf("Unknown scheme %v", scheme)) } @@ -209,43 +204,35 @@ func HasTrieNode(db ethdb.KeyValueReader, owner common.Hash, path []byte, hash c // ReadTrieNode retrieves the trie node from database with the provided node info // and associated node hash. -// hashScheme-based lookup requires the following: -// - hash -// -// pathScheme-based lookup requires the following: -// - owner -// - path func ReadTrieNode(db ethdb.KeyValueReader, owner common.Hash, path []byte, hash common.Hash, scheme string) []byte { switch scheme { case HashScheme: return ReadLegacyTrieNode(db, hash) case PathScheme: - var ( - blob []byte - nHash common.Hash - ) + var blob []byte if owner == (common.Hash{}) { - blob, nHash = ReadAccountTrieNode(db, path) + blob = ReadAccountTrieNode(db, path) } else { - blob, nHash = ReadStorageTrieNode(db, owner, path) + blob = ReadStorageTrieNode(db, owner, path) } - if nHash != hash { + if len(blob) == 0 { return nil } + h := newHasher() + defer h.release() + if h.hash(blob) != hash { + return nil // exists but not match + } return blob default: panic(fmt.Sprintf("Unknown scheme %v", scheme)) } } -// WriteTrieNode writes the trie node into database with the provided node info -// and associated node hash. -// hashScheme-based lookup requires the following: -// - hash +// WriteTrieNode writes the trie node into database with the provided node info. // -// pathScheme-based lookup requires the following: -// - owner -// - path +// hash-scheme requires the node hash as the identifier. +// path-scheme requires the node owner and path as the identifier. func WriteTrieNode(db ethdb.KeyValueWriter, owner common.Hash, path []byte, hash common.Hash, node []byte, scheme string) { switch scheme { case HashScheme: @@ -261,14 +248,10 @@ func WriteTrieNode(db ethdb.KeyValueWriter, owner common.Hash, path []byte, hash } } -// DeleteTrieNode deletes the trie node from database with the provided node info -// and associated node hash. -// hashScheme-based lookup requires the following: -// - hash +// DeleteTrieNode deletes the trie node from database with the provided node info. // -// pathScheme-based lookup requires the following: -// - owner -// - path +// hash-scheme requires the node hash as the identifier. +// path-scheme requires the node owner and path as the identifier. func DeleteTrieNode(db ethdb.KeyValueWriter, owner common.Hash, path []byte, hash common.Hash, scheme string) { switch scheme { case HashScheme: @@ -287,9 +270,8 @@ func DeleteTrieNode(db ethdb.KeyValueWriter, owner common.Hash, path []byte, has // ReadStateScheme reads the state scheme of persistent state, or none // if the state is not present in database. func ReadStateScheme(db ethdb.Reader) string { - // Check if state in path-based scheme is present - blob, _ := ReadAccountTrieNode(db.StateStoreReader(), nil) - if len(blob) != 0 { + // Check if state in path-based scheme is present. + if HasAccountTrieNode(db.StateStoreReader(), nil) { return PathScheme } // The root node might be deleted during the initial snap sync, check @@ -304,8 +286,7 @@ func ReadStateScheme(db ethdb.Reader) string { if header == nil { return "" // empty datadir } - blob = ReadLegacyTrieNode(db.StateStoreReader(), header.Root) - if len(blob) == 0 { + if !HasLegacyTrieNode(db.StateStoreReader(), header.Root) { return "" // no state in disk } return HashScheme diff --git a/core/rawdb/ancient_scheme.go b/core/rawdb/ancient_scheme.go index ee71dc7cd6..3a371d59ac 100644 --- a/core/rawdb/ancient_scheme.go +++ b/core/rawdb/ancient_scheme.go @@ -16,7 +16,11 @@ package rawdb -import "path/filepath" +import ( + "path/filepath" + + "github.com/ethereum/go-ethereum/ethdb" +) // The list of table names of chain freezer. const ( @@ -81,7 +85,15 @@ var ( // freezers the collections of all builtin freezers. var freezers = []string{ChainFreezerName, StateFreezerName} -// NewStateFreezer initializes the freezer for state history. -func NewStateFreezer(ancientDir string, readOnly bool, offset uint64) (*ResettableFreezer, error) { - return NewResettableFreezer(filepath.Join(ancientDir, StateFreezerName), "eth/db/state", readOnly, offset, stateHistoryTableSize, stateFreezerNoSnappy) +// NewStateFreezer initializes the ancient store for state history. +// +// - if the empty directory is given, initializes the pure in-memory +// state freezer (e.g. dev mode). +// - if non-empty directory is given, initializes the regular file-based +// state freezer. +func NewStateFreezer(ancientDir string, readOnly bool, offset uint64) (ethdb.ResettableAncientStore, error) { + if ancientDir == "" { + return NewMemoryFreezer(readOnly, stateFreezerNoSnappy), nil + } + return newResettableFreezer(filepath.Join(ancientDir, StateFreezerName), "eth/db/state", readOnly, offset, stateHistoryTableSize, stateFreezerNoSnappy) } diff --git a/core/rawdb/ancient_utils.go b/core/rawdb/ancient_utils.go index 33e1e02bfc..b66a4000a6 100644 --- a/core/rawdb/ancient_utils.go +++ b/core/rawdb/ancient_utils.go @@ -93,7 +93,7 @@ func inspectFreezers(db ethdb.Database) ([]freezerInfo, error) { infos = append(infos, info) case StateFreezerName: - if ReadStateScheme(db) != PathScheme || db.StateStore() != nil { + if db.StateStore() != nil { continue } datadir, err := db.AncientDatadir() @@ -114,11 +114,11 @@ func inspectFreezers(db ethdb.Database) ([]freezerInfo, error) { f, err := NewStateFreezer(datadir, true, 0) if err != nil { - return nil, err + continue // might be possible the state freezer is not existent } defer f.Close() - info, err := inspect(StateFreezerName, stateFreezerNoSnappy, f) + info, err := inspect(freezer, stateFreezerNoSnappy, f) if err != nil { return nil, err } diff --git a/core/rawdb/ancienttest/testsuite.go b/core/rawdb/ancienttest/testsuite.go new file mode 100644 index 0000000000..70de263c04 --- /dev/null +++ b/core/rawdb/ancienttest/testsuite.go @@ -0,0 +1,325 @@ +// Copyright 2024 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package ancienttest + +import ( + "bytes" + "reflect" + "testing" + + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/internal/testrand" +) + +// TestAncientSuite runs a suite of tests against an ancient database +// implementation. +func TestAncientSuite(t *testing.T, newFn func(kinds []string) ethdb.AncientStore) { + // Test basic read methods + t.Run("BasicRead", func(t *testing.T) { basicRead(t, newFn) }) + + // Test batch read method + t.Run("BatchRead", func(t *testing.T) { batchRead(t, newFn) }) + + // Test basic write methods + t.Run("BasicWrite", func(t *testing.T) { basicWrite(t, newFn) }) + + // Test if data mutation is allowed after db write + t.Run("nonMutable", func(t *testing.T) { nonMutable(t, newFn) }) +} + +func basicRead(t *testing.T, newFn func(kinds []string) ethdb.AncientStore) { + var ( + db = newFn([]string{"a"}) + data = makeDataset(100, 32) + ) + defer db.Close() + + db.ModifyAncients(func(op ethdb.AncientWriteOp) error { + for i := 0; i < len(data); i++ { + op.AppendRaw("a", uint64(i), data[i]) + } + return nil + }) + db.TruncateTail(10) + db.TruncateHead(90) + + // Test basic tail and head retrievals + tail, err := db.Tail() + if err != nil || tail != 10 { + t.Fatal("Failed to retrieve tail") + } + ancient, err := db.Ancients() + if err != nil || ancient != 90 { + t.Fatal("Failed to retrieve ancient") + } + + // Test the deleted items shouldn't be reachable + var cases = []struct { + start int + limit int + }{ + {0, 10}, + {90, 100}, + } + for _, c := range cases { + for i := c.start; i < c.limit; i++ { + exist, err := db.HasAncient("a", uint64(i)) + if err != nil { + t.Fatalf("Failed to check presence, %v", err) + } + if exist { + t.Fatalf("Item %d is already truncated", uint64(i)) + } + _, err = db.Ancient("a", uint64(i)) + if err == nil { + t.Fatal("Error is expected for non-existent item") + } + } + } + + // Test the items in range should be reachable + for i := 10; i < 90; i++ { + exist, err := db.HasAncient("a", uint64(i)) + if err != nil { + t.Fatalf("Failed to check presence, %v", err) + } + if !exist { + t.Fatalf("Item %d is missing", uint64(i)) + } + blob, err := db.Ancient("a", uint64(i)) + if err != nil { + t.Fatalf("Failed to retrieve item, %v", err) + } + if !bytes.Equal(blob, data[i]) { + t.Fatalf("Unexpected item content, want: %v, got: %v", data[i], blob) + } + } + + // Test the items in unknown table shouldn't be reachable + exist, err := db.HasAncient("b", uint64(0)) + if err != nil { + t.Fatalf("Failed to check presence, %v", err) + } + if exist { + t.Fatal("Item in unknown table shouldn't be found") + } + _, err = db.Ancient("b", uint64(0)) + if err == nil { + t.Fatal("Error is expected for unknown table") + } +} + +func batchRead(t *testing.T, newFn func(kinds []string) ethdb.AncientStore) { + var ( + db = newFn([]string{"a"}) + data = makeDataset(100, 32) + ) + defer db.Close() + + db.ModifyAncients(func(op ethdb.AncientWriteOp) error { + for i := 0; i < 100; i++ { + op.AppendRaw("a", uint64(i), data[i]) + } + return nil + }) + db.TruncateTail(10) + db.TruncateHead(90) + + // Test the items in range should be reachable + var cases = []struct { + start uint64 + count uint64 + maxSize uint64 + expStart int + expLimit int + }{ + // Items in range [10, 90) with no size limitation + { + 10, 80, 0, 10, 90, + }, + // Items in range [10, 90) with 32 size cap, single item is expected + { + 10, 80, 32, 10, 11, + }, + // Items in range [10, 90) with 31 size cap, single item is expected + { + 10, 80, 31, 10, 11, + }, + // Items in range [10, 90) with 32*80 size cap, all items are expected + { + 10, 80, 32 * 80, 10, 90, + }, + // Extra items above the last item are not returned + { + 10, 90, 0, 10, 90, + }, + } + for i, c := range cases { + batch, err := db.AncientRange("a", c.start, c.count, c.maxSize) + if err != nil { + t.Fatalf("Failed to retrieve item in range, %v", err) + } + if !reflect.DeepEqual(batch, data[c.expStart:c.expLimit]) { + t.Fatalf("Case %d, Batch content is not matched", i) + } + } + + // Test out-of-range / zero-size retrieval should be rejected + _, err := db.AncientRange("a", 0, 1, 0) + if err == nil { + t.Fatal("Out-of-range retrieval should be rejected") + } + _, err = db.AncientRange("a", 90, 1, 0) + if err == nil { + t.Fatal("Out-of-range retrieval should be rejected") + } + _, err = db.AncientRange("a", 10, 0, 0) + if err == nil { + t.Fatal("Zero-size retrieval should be rejected") + } + + // Test item in unknown table shouldn't be reachable + _, err = db.AncientRange("b", 10, 1, 0) + if err == nil { + t.Fatal("Item in unknown table shouldn't be found") + } +} + +func basicWrite(t *testing.T, newFn func(kinds []string) ethdb.AncientStore) { + var ( + db = newFn([]string{"a", "b"}) + dataA = makeDataset(100, 32) + dataB = makeDataset(100, 32) + ) + defer db.Close() + + // The ancient write to tables should be aligned + _, err := db.ModifyAncients(func(op ethdb.AncientWriteOp) error { + for i := 0; i < 100; i++ { + op.AppendRaw("a", uint64(i), dataA[i]) + } + return nil + }) + if err == nil { + t.Fatal("Unaligned ancient write should be rejected") + } + + // Test normal ancient write + size, err := db.ModifyAncients(func(op ethdb.AncientWriteOp) error { + for i := 0; i < 100; i++ { + op.AppendRaw("a", uint64(i), dataA[i]) + op.AppendRaw("b", uint64(i), dataB[i]) + } + return nil + }) + if err != nil { + t.Fatalf("Failed to write ancient data %v", err) + } + wantSize := int64(6400) + if size != wantSize { + t.Fatalf("Ancient write size is not expected, want: %d, got: %d", wantSize, size) + } + + // Write should work after head truncating + db.TruncateHead(90) + _, err = db.ModifyAncients(func(op ethdb.AncientWriteOp) error { + for i := 90; i < 100; i++ { + op.AppendRaw("a", uint64(i), dataA[i]) + op.AppendRaw("b", uint64(i), dataB[i]) + } + return nil + }) + if err != nil { + t.Fatalf("Failed to write ancient data %v", err) + } + + // Write should work after truncating everything + db.TruncateTail(0) + _, err = db.ModifyAncients(func(op ethdb.AncientWriteOp) error { + for i := 0; i < 100; i++ { + op.AppendRaw("a", uint64(i), dataA[i]) + op.AppendRaw("b", uint64(i), dataB[i]) + } + return nil + }) + if err != nil { + t.Fatalf("Failed to write ancient data %v", err) + } +} + +func nonMutable(t *testing.T, newFn func(kinds []string) ethdb.AncientStore) { + db := newFn([]string{"a"}) + defer db.Close() + + // We write 100 zero-bytes to the freezer and immediately mutate the slice + db.ModifyAncients(func(op ethdb.AncientWriteOp) error { + data := make([]byte, 100) + op.AppendRaw("a", uint64(0), data) + for i := range data { + data[i] = 0xff + } + return nil + }) + // Now read it. + data, err := db.Ancient("a", uint64(0)) + if err != nil { + t.Fatal(err) + } + for k, v := range data { + if v != 0 { + t.Fatalf("byte %d != 0: %x", k, v) + } + } +} + +// TestResettableAncientSuite runs a suite of tests against a resettable ancient +// database implementation. +func TestResettableAncientSuite(t *testing.T, newFn func(kinds []string) ethdb.ResettableAncientStore) { + t.Run("Reset", func(t *testing.T) { + var ( + db = newFn([]string{"a"}) + data = makeDataset(100, 32) + ) + defer db.Close() + + db.ModifyAncients(func(op ethdb.AncientWriteOp) error { + for i := 0; i < 100; i++ { + op.AppendRaw("a", uint64(i), data[i]) + } + return nil + }) + db.TruncateTail(10) + db.TruncateHead(90) + + // Ancient write should work after resetting + db.Reset() + db.ModifyAncients(func(op ethdb.AncientWriteOp) error { + for i := 0; i < 100; i++ { + op.AppendRaw("a", uint64(i), data[i]) + } + return nil + }) + }) +} + +func makeDataset(size, value int) [][]byte { + var vals [][]byte + for i := 0; i < size; i += 1 { + vals = append(vals, testrand.Bytes(value)) + } + return vals +} diff --git a/core/rawdb/chain_freezer.go b/core/rawdb/chain_freezer.go index 0cc639f6bc..0e66516036 100644 --- a/core/rawdb/chain_freezer.go +++ b/core/rawdb/chain_freezer.go @@ -47,32 +47,47 @@ var ( missFreezerEnvErr = errors.New("missing freezer env error") ) -// chainFreezer is a wrapper of freezer with additional chain freezing feature. -// The background thread will keep moving ancient chain segments from key-value -// database to flat files for saving space on live database. +// chainFreezer is a wrapper of chain ancient store with additional chain freezing +// feature. The background thread will keep moving ancient chain segments from +// key-value database to flat files for saving space on live database. type chainFreezer struct { - threshold atomic.Uint64 // Number of recent blocks not to freeze (params.FullImmutabilityThreshold apart from tests) - *Freezer + ethdb.AncientStore // Ancient store for storing cold chain segment + quit chan struct{} wg sync.WaitGroup trigger chan chan struct{} // Manual blocking freeze trigger, test determinism + threshold atomic.Uint64 // Number of recent blocks not to freeze (params.FullImmutabilityThreshold apart from tests) + freezeEnv atomic.Value waitEnvTimes int multiDatabase bool } -// newChainFreezer initializes the freezer for ancient chain data. +// newChainFreezer initializes the freezer for ancient chain segment. +// +// - if the empty directory is given, initializes the pure in-memory +// state freezer (e.g. dev mode). +// - if non-empty directory is given, initializes the regular file-based +// state freezer. func newChainFreezer(datadir string, namespace string, readonly bool, offset uint64, multiDatabase bool) (*chainFreezer, error) { - freezer, err := NewChainFreezer(datadir, namespace, readonly, offset) + var ( + err error + freezer ethdb.AncientStore + ) + if datadir == "" { + freezer = NewMemoryFreezer(readonly, chainFreezerNoSnappy) + } else { + freezer, err = NewFreezer(datadir, namespace, readonly, offset, freezerTableSize, chainFreezerNoSnappy) + } if err != nil { return nil, err } cf := chainFreezer{ - Freezer: freezer, - quit: make(chan struct{}), - trigger: make(chan chan struct{}), + AncientStore: freezer, + quit: make(chan struct{}), + trigger: make(chan chan struct{}), } cf.threshold.Store(params.FullImmutabilityThreshold) return &cf, nil @@ -86,7 +101,7 @@ func (f *chainFreezer) Close() error { close(f.quit) } f.wg.Wait() - return f.Freezer.Close() + return f.AncientStore.Close() } // readHeadNumber returns the number of chain head block. 0 is returned if the @@ -198,7 +213,7 @@ func (f *chainFreezer) freeze(db ethdb.KeyValueStore) { log.Debug("Current full block not old enough to freeze", "err", err) continue } - frozen = f.frozen.Load() + frozen, _ := f.Ancients() // no error will occur, safe to ignore // Short circuit if the blocks below threshold are already frozen. if frozen != 0 && frozen-1 >= threshold { @@ -241,7 +256,7 @@ func (f *chainFreezer) freeze(db ethdb.KeyValueStore) { } number = ReadHeaderNumber(nfdb, hash) threshold = f.threshold.Load() - frozen = f.frozen.Load() + frozen, _ := f.Ancients() // no error will occur, safe to ignore switch { case number == nil: log.Error("Current full block number unavailable", "hash", hash) @@ -293,7 +308,7 @@ func (f *chainFreezer) freeze(db ethdb.KeyValueStore) { backoff = true continue } - // Batch of blocks have been frozen, flush them before wiping from leveldb + // Batch of blocks have been frozen, flush them before wiping from key-value store if err := f.Sync(); err != nil { log.Crit("Failed to flush frozen tables", "err", err) } @@ -313,7 +328,7 @@ func (f *chainFreezer) freeze(db ethdb.KeyValueStore) { // Wipe out side chains also and track dangling side chains var dangling []common.Hash - frozen = f.frozen.Load() // Needs reload after during freezeRange + frozen, _ = f.Ancients() // Needs reload after during freezeRange for number := first; number < frozen; number++ { // Always keep the genesis block in active database if number != 0 { diff --git a/core/rawdb/chain_iterator_test.go b/core/rawdb/chain_iterator_test.go index 78b0a82e10..390424f673 100644 --- a/core/rawdb/chain_iterator_test.go +++ b/core/rawdb/chain_iterator_test.go @@ -34,7 +34,7 @@ func TestChainIterator(t *testing.T) { var block *types.Block var txs []*types.Transaction to := common.BytesToAddress([]byte{0x11}) - block = types.NewBlock(&types.Header{Number: big.NewInt(int64(0))}, nil, nil, nil, newTestHasher()) // Empty genesis block + block = types.NewBlock(&types.Header{Number: big.NewInt(int64(0))}, nil, nil, newTestHasher()) // Empty genesis block WriteBlock(chainDb, block) WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64()) for i := uint64(1); i <= 10; i++ { @@ -60,7 +60,7 @@ func TestChainIterator(t *testing.T) { }) } txs = append(txs, tx) - block = types.NewBlock(&types.Header{Number: big.NewInt(int64(i))}, []*types.Transaction{tx}, nil, nil, newTestHasher()) + block = types.NewBlock(&types.Header{Number: big.NewInt(int64(i))}, &types.Body{Transactions: types.Transactions{tx}}, nil, newTestHasher()) WriteBlock(chainDb, block) WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64()) } @@ -111,7 +111,7 @@ func TestIndexTransactions(t *testing.T) { to := common.BytesToAddress([]byte{0x11}) // Write empty genesis block - block = types.NewBlock(&types.Header{Number: big.NewInt(int64(0))}, nil, nil, nil, newTestHasher()) + block = types.NewBlock(&types.Header{Number: big.NewInt(int64(0))}, nil, nil, newTestHasher()) WriteBlock(chainDb, block) WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64()) @@ -138,7 +138,7 @@ func TestIndexTransactions(t *testing.T) { }) } txs = append(txs, tx) - block = types.NewBlock(&types.Header{Number: big.NewInt(int64(i))}, []*types.Transaction{tx}, nil, nil, newTestHasher()) + block = types.NewBlock(&types.Header{Number: big.NewInt(int64(i))}, &types.Body{Transactions: types.Transactions{tx}}, nil, newTestHasher()) WriteBlock(chainDb, block) WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64()) } diff --git a/core/rawdb/database.go b/core/rawdb/database.go index 2e3cf41123..5a9d90a4b1 100644 --- a/core/rawdb/database.go +++ b/core/rawdb/database.go @@ -34,11 +34,14 @@ import ( "github.com/olekukonko/tablewriter" ) -// freezerdb is a database wrapper that enables freezer data retrievals. +// freezerdb is a database wrapper that enables ancient chain segment freezing. type freezerdb struct { - ancientRoot string ethdb.KeyValueStore ethdb.AncientStore + + readOnly bool + ancientRoot string + ethdb.AncientFreezer diffStore ethdb.KeyValueStore stateStore ethdb.Database @@ -154,7 +157,7 @@ func (frdb *freezerdb) HasSeparateBlockStore() bool { // a freeze cycle completes, without having to sleep for a minute to trigger the // automatic background run. func (frdb *freezerdb) Freeze(threshold uint64) error { - if frdb.AncientStore.(*chainFreezer).readonly { + if frdb.readOnly { return errReadOnly } // Set the freezer threshold to a temporary value @@ -493,6 +496,14 @@ func resolveChainFreezerDir(ancient string) string { // storage. The passed ancient indicates the path of root ancient directory // where the chain freezer can be opened. func NewDatabaseWithFreezer(db ethdb.KeyValueStore, ancient string, namespace string, readonly, disableFreeze, isLastOffset, pruneAncientData, multiDatabase bool) (ethdb.Database, error) { + // Create the idle freezer instance. If the given ancient directory is empty, + // in-memory chain freezer is used (e.g. dev mode); otherwise the regular + // file-based freezer is created. + chainFreezerDir := ancient + if chainFreezerDir != "" { + chainFreezerDir = resolveChainFreezerDir(chainFreezerDir) + } + var offset uint64 // The offset of ancientDB should be handled differently in different scenarios. if isLastOffset { @@ -636,7 +647,7 @@ func NewDatabaseWithFreezer(db ethdb.KeyValueStore, ancient string, namespace st WriteAncientType(db, EntireFreezerType) } // Freezer is consistent with the key-value database, permit combining the two - if !disableFreeze && !frdb.readonly { + if !disableFreeze && readonly { frdb.wg.Add(1) go func() { frdb.freeze(db) diff --git a/core/rawdb/freezer.go b/core/rawdb/freezer.go index dbdb7c937e..f1e9f5e179 100644 --- a/core/rawdb/freezer.go +++ b/core/rawdb/freezer.go @@ -64,7 +64,7 @@ const freezerTableSize = 2 * 1000 * 1000 * 1000 // reserving it for go-ethereum. This would also reduce the memory requirements // of Geth, and thus also GC overhead. type Freezer struct { - frozen atomic.Uint64 // Number of blocks already frozen + frozen atomic.Uint64 // Number of items already frozen tail atomic.Uint64 // Number of the first stored item in the freezer // This lock synchronizes writers and the truncate operation, as well as @@ -79,12 +79,6 @@ type Freezer struct { offset uint64 // Starting BlockNumber in current freezer } -// NewChainFreezer is a small utility method around NewFreezer that sets the -// default parameters for the chain storage. -func NewChainFreezer(datadir string, namespace string, readonly bool, offset uint64) (*Freezer, error) { - return NewFreezer(datadir, namespace, readonly, offset, freezerTableSize, chainFreezerNoSnappy) -} - // NewFreezer creates a freezer instance for maintaining immutable ordered // data according to the given parameters. // diff --git a/core/rawdb/freezer_memory.go b/core/rawdb/freezer_memory.go new file mode 100644 index 0000000000..c6634979c4 --- /dev/null +++ b/core/rawdb/freezer_memory.go @@ -0,0 +1,454 @@ +// Copyright 2024 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rawdb + +import ( + "errors" + "fmt" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/rlp" +) + +// memoryTable is used to store a list of sequential items in memory. +type memoryTable struct { + name string // Table name + items uint64 // Number of stored items in the table, including the deleted ones + offset uint64 // Number of deleted items from the table + data [][]byte // List of rlp-encoded items, sort in order + size uint64 // Total memory size occupied by the table + lock sync.RWMutex +} + +// newMemoryTable initializes the memory table. +func newMemoryTable(name string) *memoryTable { + return &memoryTable{name: name} +} + +// has returns an indicator whether the specified data exists. +func (t *memoryTable) has(number uint64) bool { + t.lock.RLock() + defer t.lock.RUnlock() + + return number >= t.offset && number < t.items +} + +// retrieve retrieves multiple items in sequence, starting from the index 'start'. +// It will return: +// - at most 'count' items, +// - if maxBytes is specified: at least 1 item (even if exceeding the maxByteSize), +// but will otherwise return as many items as fit into maxByteSize. +// - if maxBytes is not specified, 'count' items will be returned if they are present +func (t *memoryTable) retrieve(start uint64, count, maxBytes uint64) ([][]byte, error) { + t.lock.RLock() + defer t.lock.RUnlock() + + var ( + size uint64 + batch [][]byte + ) + // Ensure the start is written, not deleted from the tail, and that the + // caller actually wants something. + if t.items <= start || t.offset > start || count == 0 { + return nil, errOutOfBounds + } + // Cap the item count if the retrieval is out of bound. + if start+count > t.items { + count = t.items - start + } + for n := start; n < start+count; n++ { + index := n - t.offset + if len(batch) != 0 && maxBytes != 0 && size+uint64(len(t.data[index])) > maxBytes { + return batch, nil + } + batch = append(batch, t.data[index]) + size += uint64(len(t.data[index])) + } + return batch, nil +} + +// truncateHead discards any recent data above the provided threshold number. +func (t *memoryTable) truncateHead(items uint64) error { + t.lock.Lock() + defer t.lock.Unlock() + + // Short circuit if nothing to delete. + if t.items <= items { + return nil + } + if items < t.offset { + return errors.New("truncation below tail") + } + t.data = t.data[:items-t.offset] + t.items = items + return nil +} + +// truncateTail discards any recent data before the provided threshold number. +func (t *memoryTable) truncateTail(items uint64) error { + t.lock.Lock() + defer t.lock.Unlock() + + // Short circuit if nothing to delete. + if t.offset >= items { + return nil + } + if t.items < items { + return errors.New("truncation above head") + } + t.data = t.data[items-t.offset:] + t.offset = items + return nil +} + +// commit merges the given item batch into table. It's presumed that the +// batch is ordered and continuous with table. +func (t *memoryTable) commit(batch [][]byte) error { + t.lock.Lock() + defer t.lock.Unlock() + + for _, item := range batch { + t.size += uint64(len(item)) + } + t.data = append(t.data, batch...) + t.items += uint64(len(batch)) + return nil +} + +// memoryBatch is the singleton batch used for ancient write. +type memoryBatch struct { + data map[string][][]byte + next map[string]uint64 + size map[string]int64 +} + +func newMemoryBatch() *memoryBatch { + return &memoryBatch{ + data: make(map[string][][]byte), + next: make(map[string]uint64), + size: make(map[string]int64), + } +} + +func (b *memoryBatch) reset(freezer *MemoryFreezer) { + b.data = make(map[string][][]byte) + b.next = make(map[string]uint64) + b.size = make(map[string]int64) + + for name, table := range freezer.tables { + b.next[name] = table.items + } +} + +// Append adds an RLP-encoded item. +func (b *memoryBatch) Append(kind string, number uint64, item interface{}) error { + if b.next[kind] != number { + return errOutOrderInsertion + } + blob, err := rlp.EncodeToBytes(item) + if err != nil { + return err + } + b.data[kind] = append(b.data[kind], blob) + b.next[kind]++ + b.size[kind] += int64(len(blob)) + return nil +} + +// AppendRaw adds an item without RLP-encoding it. +func (b *memoryBatch) AppendRaw(kind string, number uint64, blob []byte) error { + if b.next[kind] != number { + return errOutOrderInsertion + } + b.data[kind] = append(b.data[kind], common.CopyBytes(blob)) + b.next[kind]++ + b.size[kind] += int64(len(blob)) + return nil +} + +// commit is called at the end of a write operation and writes all remaining +// data to tables. +func (b *memoryBatch) commit(freezer *MemoryFreezer) (items uint64, writeSize int64, err error) { + // Check that count agrees on all batches. + items = math.MaxUint64 + for name, next := range b.next { + if items < math.MaxUint64 && next != items { + return 0, 0, fmt.Errorf("table %s is at item %d, want %d", name, next, items) + } + items = next + } + // Commit all table batches. + for name, batch := range b.data { + table := freezer.tables[name] + if err := table.commit(batch); err != nil { + return 0, 0, err + } + writeSize += b.size[name] + } + return items, writeSize, nil +} + +// MemoryFreezer is an ephemeral ancient store. It implements the ethdb.AncientStore +// interface and can be used along with ephemeral key-value store. +type MemoryFreezer struct { + items uint64 // Number of items stored + tail uint64 // Number of the first stored item in the freezer + readonly bool // Flag if the freezer is only for reading + lock sync.RWMutex // Lock to protect fields + tables map[string]*memoryTable // Tables for storing everything + writeBatch *memoryBatch // Pre-allocated write batch +} + +// NewMemoryFreezer initializes an in-memory freezer instance. +func NewMemoryFreezer(readonly bool, tableName map[string]bool) *MemoryFreezer { + tables := make(map[string]*memoryTable) + for name := range tableName { + tables[name] = newMemoryTable(name) + } + return &MemoryFreezer{ + writeBatch: newMemoryBatch(), + readonly: readonly, + tables: tables, + } +} + +// HasAncient returns an indicator whether the specified data exists. +func (f *MemoryFreezer) HasAncient(kind string, number uint64) (bool, error) { + f.lock.RLock() + defer f.lock.RUnlock() + + if table := f.tables[kind]; table != nil { + return table.has(number), nil + } + return false, nil +} + +// Ancient retrieves an ancient binary blob from the in-memory freezer. +func (f *MemoryFreezer) Ancient(kind string, number uint64) ([]byte, error) { + f.lock.RLock() + defer f.lock.RUnlock() + + t := f.tables[kind] + if t == nil { + return nil, errUnknownTable + } + data, err := t.retrieve(number, 1, 0) + if err != nil { + return nil, err + } + return data[0], nil +} + +// AncientRange retrieves multiple items in sequence, starting from the index 'start'. +// It will return +// - at most 'count' items, +// - if maxBytes is specified: at least 1 item (even if exceeding the maxByteSize), +// but will otherwise return as many items as fit into maxByteSize. +// - if maxBytes is not specified, 'count' items will be returned if they are present +func (f *MemoryFreezer) AncientRange(kind string, start, count, maxBytes uint64) ([][]byte, error) { + f.lock.RLock() + defer f.lock.RUnlock() + + t := f.tables[kind] + if t == nil { + return nil, errUnknownTable + } + return t.retrieve(start, count, maxBytes) +} + +// Ancients returns the ancient item numbers in the freezer. +func (f *MemoryFreezer) Ancients() (uint64, error) { + f.lock.RLock() + defer f.lock.RUnlock() + + return f.items, nil +} + +// ItemAmountInAncient returns the actual length of current freezer. +func (f *MemoryFreezer) ItemAmountInAncient() (uint64, error) { + f.lock.RLock() + defer f.lock.RUnlock() + + return f.items, nil +} + +// Ancients returns the ancient item numbers in the freezer. +func (f *MemoryFreezer) AncientOffSet() uint64 { + f.lock.RLock() + defer f.lock.RUnlock() + + return f.tail +} + +// Tail returns the number of first stored item in the freezer. +// This number can also be interpreted as the total deleted item numbers. +func (f *MemoryFreezer) Tail() (uint64, error) { + f.lock.RLock() + defer f.lock.RUnlock() + + return f.tail, nil +} + +// AncientSize returns the ancient size of the specified category. +func (f *MemoryFreezer) AncientSize(kind string) (uint64, error) { + f.lock.RLock() + defer f.lock.RUnlock() + + if table := f.tables[kind]; table != nil { + return table.size, nil + } + return 0, errUnknownTable +} + +// ReadAncients runs the given read operation while ensuring that no writes take place +// on the underlying freezer. +func (f *MemoryFreezer) ReadAncients(fn func(ethdb.AncientReaderOp) error) (err error) { + f.lock.RLock() + defer f.lock.RUnlock() + + return fn(f) +} + +// ModifyAncients runs the given write operation. +func (f *MemoryFreezer) ModifyAncients(fn func(ethdb.AncientWriteOp) error) (writeSize int64, err error) { + f.lock.Lock() + defer f.lock.Unlock() + + if f.readonly { + return 0, errReadOnly + } + // Roll back all tables to the starting position in case of error. + defer func(old uint64) { + if err == nil { + return + } + // The write operation has failed. Go back to the previous item position. + for name, table := range f.tables { + err := table.truncateHead(old) + if err != nil { + log.Error("Freezer table roll-back failed", "table", name, "index", old, "err", err) + } + } + }(f.items) + + // Modify the ancients in batch. + f.writeBatch.reset(f) + if err := fn(f.writeBatch); err != nil { + return 0, err + } + item, writeSize, err := f.writeBatch.commit(f) + if err != nil { + return 0, err + } + f.items = item + return writeSize, nil +} + +// TruncateHead discards any recent data above the provided threshold number. +// It returns the previous head number. +func (f *MemoryFreezer) TruncateHead(items uint64) (uint64, error) { + f.lock.Lock() + defer f.lock.Unlock() + + if f.readonly { + return 0, errReadOnly + } + old := f.items + if old <= items { + return old, nil + } + for _, table := range f.tables { + if err := table.truncateHead(items); err != nil { + return 0, err + } + } + f.items = items + return old, nil +} + +// TruncateTail discards any recent data below the provided threshold number. +func (f *MemoryFreezer) TruncateTail(tail uint64) (uint64, error) { + f.lock.Lock() + defer f.lock.Unlock() + + if f.readonly { + return 0, errReadOnly + } + old := f.tail + if old >= tail { + return old, nil + } + for _, table := range f.tables { + if err := table.truncateTail(tail); err != nil { + return 0, err + } + } + f.tail = tail + return old, nil +} + +// Sync flushes all data tables to disk. +func (f *MemoryFreezer) Sync() error { + return nil +} + +// MigrateTable processes and migrates entries of a given table to a new format. +// The second argument is a function that takes a raw entry and returns it +// in the newest format. +func (f *MemoryFreezer) MigrateTable(string, func([]byte) ([]byte, error)) error { + return errors.New("not implemented") +} + +// Close releases all the sources held by the memory freezer. It will panic if +// any following invocation is made to a closed freezer. +func (f *MemoryFreezer) Close() error { + f.lock.Lock() + defer f.lock.Unlock() + + f.tables = nil + f.writeBatch = nil + return nil +} + +// Reset drops all the data cached in the memory freezer and reset itself +// back to default state. +func (f *MemoryFreezer) Reset() error { + f.lock.Lock() + defer f.lock.Unlock() + + tables := make(map[string]*memoryTable) + for name := range f.tables { + tables[name] = newMemoryTable(name) + } + f.tables = tables + f.items, f.tail = 0, 0 + return nil +} + +func (f *MemoryFreezer) TruncateTableTail(kind string, tail uint64) (uint64, error) { + //TODO implement me + panic("implement me") +} + +func (f *MemoryFreezer) ResetTable(kind string, startAt uint64, onlyEmpty bool) error { + //TODO implement me + panic("not supported") +} diff --git a/core/rawdb/freezer_memory_test.go b/core/rawdb/freezer_memory_test.go new file mode 100644 index 0000000000..e71de0f629 --- /dev/null +++ b/core/rawdb/freezer_memory_test.go @@ -0,0 +1,41 @@ +// Copyright 2024 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rawdb + +import ( + "testing" + + "github.com/ethereum/go-ethereum/core/rawdb/ancienttest" + "github.com/ethereum/go-ethereum/ethdb" +) + +func TestMemoryFreezer(t *testing.T) { + ancienttest.TestAncientSuite(t, func(kinds []string) ethdb.AncientStore { + tables := make(map[string]bool) + for _, kind := range kinds { + tables[kind] = true + } + return NewMemoryFreezer(false, tables) + }) + ancienttest.TestResettableAncientSuite(t, func(kinds []string) ethdb.ResettableAncientStore { + tables := make(map[string]bool) + for _, kind := range kinds { + tables[kind] = true + } + return NewMemoryFreezer(false, tables) + }) +} diff --git a/core/rawdb/freezer_resettable.go b/core/rawdb/freezer_resettable.go index dfefc6a45e..ca8c296c83 100644 --- a/core/rawdb/freezer_resettable.go +++ b/core/rawdb/freezer_resettable.go @@ -30,16 +30,17 @@ const tmpSuffix = ".tmp" // freezerOpenFunc is the function used to open/create a freezer. type freezerOpenFunc = func() (*Freezer, error) -// ResettableFreezer is a wrapper of the freezer which makes the +// resettableFreezer is a wrapper of the freezer which makes the // freezer resettable. -type ResettableFreezer struct { - freezer *Freezer - opener freezerOpenFunc - datadir string - lock sync.RWMutex +type resettableFreezer struct { + readOnly bool + freezer *Freezer + opener freezerOpenFunc + datadir string + lock sync.RWMutex } -// NewResettableFreezer creates a resettable freezer, note freezer is +// newResettableFreezer creates a resettable freezer, note freezer is // only resettable if the passed file directory is exclusively occupied // by the freezer. And also the user-configurable ancient root directory // is **not** supported for reset since it might be a mount and rename @@ -48,7 +49,7 @@ type ResettableFreezer struct { // // The reset function will delete directory atomically and re-create the // freezer from scratch. -func NewResettableFreezer(datadir string, namespace string, readonly bool, offset uint64, maxTableSize uint32, tables map[string]bool) (*ResettableFreezer, error) { +func newResettableFreezer(datadir string, namespace string, readonly bool, offset uint64, maxTableSize uint32, tables map[string]bool) (*resettableFreezer, error) { if err := cleanup(datadir); err != nil { return nil, err } @@ -59,10 +60,11 @@ func NewResettableFreezer(datadir string, namespace string, readonly bool, offse if err != nil { return nil, err } - return &ResettableFreezer{ - freezer: freezer, - opener: opener, - datadir: datadir, + return &resettableFreezer{ + readOnly: readonly, + freezer: freezer, + opener: opener, + datadir: datadir, }, nil } @@ -70,10 +72,13 @@ func NewResettableFreezer(datadir string, namespace string, readonly bool, offse // recreate the freezer from scratch. The atomicity of directory deletion // is guaranteed by the rename operation, the leftover directory will be // cleaned up in next startup in case crash happens after rename. -func (f *ResettableFreezer) Reset() error { +func (f *resettableFreezer) Reset() error { f.lock.Lock() defer f.lock.Unlock() + if f.readOnly { + return errReadOnly + } if err := f.freezer.Close(); err != nil { return err } @@ -93,7 +98,7 @@ func (f *ResettableFreezer) Reset() error { } // Close terminates the chain freezer, unmapping all the data files. -func (f *ResettableFreezer) Close() error { +func (f *resettableFreezer) Close() error { f.lock.RLock() defer f.lock.RUnlock() @@ -102,7 +107,7 @@ func (f *ResettableFreezer) Close() error { // HasAncient returns an indicator whether the specified ancient data exists // in the freezer -func (f *ResettableFreezer) HasAncient(kind string, number uint64) (bool, error) { +func (f *resettableFreezer) HasAncient(kind string, number uint64) (bool, error) { f.lock.RLock() defer f.lock.RUnlock() @@ -110,7 +115,7 @@ func (f *ResettableFreezer) HasAncient(kind string, number uint64) (bool, error) } // Ancient retrieves an ancient binary blob from the append-only immutable files. -func (f *ResettableFreezer) Ancient(kind string, number uint64) ([]byte, error) { +func (f *resettableFreezer) Ancient(kind string, number uint64) ([]byte, error) { f.lock.RLock() defer f.lock.RUnlock() @@ -123,7 +128,7 @@ func (f *ResettableFreezer) Ancient(kind string, number uint64) ([]byte, error) // - if maxBytes is specified: at least 1 item (even if exceeding the maxByteSize), // but will otherwise return as many items as fit into maxByteSize. // - if maxBytes is not specified, 'count' items will be returned if they are present. -func (f *ResettableFreezer) AncientRange(kind string, start, count, maxBytes uint64) ([][]byte, error) { +func (f *resettableFreezer) AncientRange(kind string, start, count, maxBytes uint64) ([][]byte, error) { f.lock.RLock() defer f.lock.RUnlock() @@ -131,7 +136,7 @@ func (f *ResettableFreezer) AncientRange(kind string, start, count, maxBytes uin } // Ancients returns the length of the frozen items. -func (f *ResettableFreezer) Ancients() (uint64, error) { +func (f *resettableFreezer) Ancients() (uint64, error) { f.lock.RLock() defer f.lock.RUnlock() @@ -139,7 +144,7 @@ func (f *ResettableFreezer) Ancients() (uint64, error) { } // Tail returns the number of first stored item in the freezer. -func (f *ResettableFreezer) Tail() (uint64, error) { +func (f *resettableFreezer) Tail() (uint64, error) { f.lock.RLock() defer f.lock.RUnlock() @@ -147,7 +152,7 @@ func (f *ResettableFreezer) Tail() (uint64, error) { } // AncientSize returns the ancient size of the specified category. -func (f *ResettableFreezer) AncientSize(kind string) (uint64, error) { +func (f *resettableFreezer) AncientSize(kind string) (uint64, error) { f.lock.RLock() defer f.lock.RUnlock() @@ -156,7 +161,7 @@ func (f *ResettableFreezer) AncientSize(kind string) (uint64, error) { // ReadAncients runs the given read operation while ensuring that no writes take place // on the underlying freezer. -func (f *ResettableFreezer) ReadAncients(fn func(ethdb.AncientReaderOp) error) (err error) { +func (f *resettableFreezer) ReadAncients(fn func(ethdb.AncientReaderOp) error) (err error) { f.lock.RLock() defer f.lock.RUnlock() @@ -164,7 +169,7 @@ func (f *ResettableFreezer) ReadAncients(fn func(ethdb.AncientReaderOp) error) ( } // ItemAmountInAncient returns the actual length of current ancientDB. -func (f *ResettableFreezer) ItemAmountInAncient() (uint64, error) { +func (f *resettableFreezer) ItemAmountInAncient() (uint64, error) { f.lock.RLock() defer f.lock.RUnlock() @@ -172,7 +177,7 @@ func (f *ResettableFreezer) ItemAmountInAncient() (uint64, error) { } // AncientOffSet returns the offset of current ancientDB. -func (f *ResettableFreezer) AncientOffSet() uint64 { +func (f *resettableFreezer) AncientOffSet() uint64 { f.lock.RLock() defer f.lock.RUnlock() @@ -180,7 +185,7 @@ func (f *ResettableFreezer) AncientOffSet() uint64 { } // ModifyAncients runs the given write operation. -func (f *ResettableFreezer) ModifyAncients(fn func(ethdb.AncientWriteOp) error) (writeSize int64, err error) { +func (f *resettableFreezer) ModifyAncients(fn func(ethdb.AncientWriteOp) error) (writeSize int64, err error) { f.lock.RLock() defer f.lock.RUnlock() @@ -189,7 +194,7 @@ func (f *ResettableFreezer) ModifyAncients(fn func(ethdb.AncientWriteOp) error) // TruncateHead discards any recent data above the provided threshold number. // It returns the previous head number. -func (f *ResettableFreezer) TruncateHead(items uint64) (uint64, error) { +func (f *resettableFreezer) TruncateHead(items uint64) (uint64, error) { f.lock.RLock() defer f.lock.RUnlock() @@ -198,7 +203,7 @@ func (f *ResettableFreezer) TruncateHead(items uint64) (uint64, error) { // TruncateTail discards any recent data below the provided threshold number. // It returns the previous value -func (f *ResettableFreezer) TruncateTail(tail uint64) (uint64, error) { +func (f *resettableFreezer) TruncateTail(tail uint64) (uint64, error) { f.lock.RLock() defer f.lock.RUnlock() @@ -206,7 +211,7 @@ func (f *ResettableFreezer) TruncateTail(tail uint64) (uint64, error) { } // TruncateTableTail will truncate certain table to new tail -func (f *ResettableFreezer) TruncateTableTail(kind string, tail uint64) (uint64, error) { +func (f *resettableFreezer) TruncateTableTail(kind string, tail uint64) (uint64, error) { f.lock.RLock() defer f.lock.RUnlock() @@ -214,7 +219,7 @@ func (f *ResettableFreezer) TruncateTableTail(kind string, tail uint64) (uint64, } // ResetTable will reset certain table with new start point -func (f *ResettableFreezer) ResetTable(kind string, startAt uint64, onlyEmpty bool) error { +func (f *resettableFreezer) ResetTable(kind string, startAt uint64, onlyEmpty bool) error { f.lock.RLock() defer f.lock.RUnlock() @@ -222,7 +227,7 @@ func (f *ResettableFreezer) ResetTable(kind string, startAt uint64, onlyEmpty bo } // Sync flushes all data tables to disk. -func (f *ResettableFreezer) Sync() error { +func (f *resettableFreezer) Sync() error { f.lock.RLock() defer f.lock.RUnlock() @@ -231,7 +236,7 @@ func (f *ResettableFreezer) Sync() error { // MigrateTable processes the entries in a given table in sequence // converting them to a new format if they're of an old format. -func (f *ResettableFreezer) MigrateTable(kind string, convert convertLegacyFn) error { +func (f *resettableFreezer) MigrateTable(kind string, convert convertLegacyFn) error { f.lock.RLock() defer f.lock.RUnlock() diff --git a/core/rawdb/freezer_resettable_test.go b/core/rawdb/freezer_resettable_test.go index 57eff54fcc..cfd9efb832 100644 --- a/core/rawdb/freezer_resettable_test.go +++ b/core/rawdb/freezer_resettable_test.go @@ -33,7 +33,7 @@ func TestResetFreezer(t *testing.T) { {1, bytes.Repeat([]byte{1}, 2048)}, {2, bytes.Repeat([]byte{2}, 2048)}, } - f, _ := NewResettableFreezer(t.TempDir(), "", false, 0, 2048, freezerTestTableDef) + f, _ := newResettableFreezer(t.TempDir(), "", false, 0, 2048, freezerTestTableDef) defer f.Close() f.ModifyAncients(func(op ethdb.AncientWriteOp) error { @@ -87,7 +87,7 @@ func TestFreezerCleanup(t *testing.T) { {2, bytes.Repeat([]byte{2}, 2048)}, } datadir := t.TempDir() - f, _ := NewResettableFreezer(datadir, "", false, 2048, 0, freezerTestTableDef) + f, _ := newResettableFreezer(datadir, "", false, 2048, 0, freezerTestTableDef) f.ModifyAncients(func(op ethdb.AncientWriteOp) error { for _, item := range items { op.AppendRaw("test", item.id, item.blob) @@ -98,7 +98,7 @@ func TestFreezerCleanup(t *testing.T) { os.Rename(datadir, tmpName(datadir)) // Open the freezer again, trigger cleanup operation - f, _ = NewResettableFreezer(datadir, "", false, 2048, 0, freezerTestTableDef) + f, _ = newResettableFreezer(datadir, "", false, 2048, 0, freezerTestTableDef) f.Close() if _, err := os.Lstat(tmpName(datadir)); !os.IsNotExist(err) { diff --git a/core/rawdb/freezer_test.go b/core/rawdb/freezer_test.go index 9c6bc8a655..da1a10eede 100644 --- a/core/rawdb/freezer_test.go +++ b/core/rawdb/freezer_test.go @@ -27,6 +27,7 @@ import ( "sync" "testing" + "github.com/ethereum/go-ethereum/core/rawdb/ancienttest" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/rlp" "github.com/stretchr/testify/require" @@ -581,3 +582,22 @@ func TestFreezerCloseSync(t *testing.T) { t.Fatalf("want %v, have %v", have, want) } } + +func TestFreezerSuite(t *testing.T) { + ancienttest.TestAncientSuite(t, func(kinds []string) ethdb.AncientStore { + tables := make(map[string]bool) + for _, kind := range kinds { + tables[kind] = true + } + f, _ := newFreezerForTesting(t, tables) + return f + }) + ancienttest.TestResettableAncientSuite(t, func(kinds []string) ethdb.ResettableAncientStore { + tables := make(map[string]bool) + for _, kind := range kinds { + tables[kind] = true + } + f, _ := newResettableFreezer(t.TempDir(), "", false, 2048, tables) + return f + }) +} diff --git a/core/rawdb/table.go b/core/rawdb/table.go index 7fc182b7c5..ab9bab009a 100644 --- a/core/rawdb/table.go +++ b/core/rawdb/table.go @@ -187,9 +187,9 @@ func (t *table) NewIterator(prefix []byte, start []byte) ethdb.Iterator { } } -// Stat returns a particular internal stat of the database. -func (t *table) Stat(property string) (string, error) { - return t.db.Stat(property) +// Stat returns the statistic data of the database. +func (t *table) Stat() (string, error) { + return t.db.Stat() } // Compact flattens the underlying data store for the given key range. In essence, diff --git a/core/state/access_events.go b/core/state/access_events.go new file mode 100644 index 0000000000..4b6c7c7e69 --- /dev/null +++ b/core/state/access_events.go @@ -0,0 +1,320 @@ +// 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 . + +package state + +import ( + "maps" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/trie/utils" + "github.com/holiman/uint256" +) + +// mode specifies how a tree location has been accessed +// for the byte value: +// * the first bit is set if the branch has been edited +// * the second bit is set if the branch has been read +type mode byte + +const ( + AccessWitnessReadFlag = mode(1) + AccessWitnessWriteFlag = mode(2) +) + +var zeroTreeIndex uint256.Int + +// AccessEvents lists the locations of the state that are being accessed +// during the production of a block. +type AccessEvents struct { + branches map[branchAccessKey]mode + chunks map[chunkAccessKey]mode + + pointCache *utils.PointCache +} + +func NewAccessEvents(pointCache *utils.PointCache) *AccessEvents { + return &AccessEvents{ + branches: make(map[branchAccessKey]mode), + chunks: make(map[chunkAccessKey]mode), + pointCache: pointCache, + } +} + +// Merge is used to merge the access events that were generated during the +// execution of a tx, with the accumulation of all access events that were +// generated during the execution of all txs preceding this one in a block. +func (ae *AccessEvents) Merge(other *AccessEvents) { + for k := range other.branches { + ae.branches[k] |= other.branches[k] + } + for k, chunk := range other.chunks { + ae.chunks[k] |= chunk + } +} + +// Keys returns, predictably, the list of keys that were touched during the +// buildup of the access witness. +func (ae *AccessEvents) Keys() [][]byte { + // TODO: consider if parallelizing this is worth it, probably depending on len(ae.chunks). + keys := make([][]byte, 0, len(ae.chunks)) + for chunk := range ae.chunks { + basePoint := ae.pointCache.Get(chunk.addr[:]) + key := utils.GetTreeKeyWithEvaluatedAddress(basePoint, &chunk.treeIndex, chunk.leafKey) + keys = append(keys, key) + } + return keys +} + +func (ae *AccessEvents) Copy() *AccessEvents { + cpy := &AccessEvents{ + branches: maps.Clone(ae.branches), + chunks: maps.Clone(ae.chunks), + pointCache: ae.pointCache, + } + return cpy +} + +// AddAccount returns the gas to be charged for each of the currently cold +// member fields of an account. +func (ae *AccessEvents) AddAccount(addr common.Address, isWrite bool) uint64 { + var gas uint64 + gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.VersionLeafKey, isWrite) + gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BalanceLeafKey, isWrite) + gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.NonceLeafKey, isWrite) + gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeKeccakLeafKey, isWrite) + gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeSizeLeafKey, isWrite) + return gas +} + +// MessageCallGas returns the gas to be charged for each of the currently +// cold member fields of an account, that need to be touched when making a message +// call to that account. +func (ae *AccessEvents) MessageCallGas(destination common.Address) uint64 { + var gas uint64 + gas += ae.touchAddressAndChargeGas(destination, zeroTreeIndex, utils.VersionLeafKey, false) + gas += ae.touchAddressAndChargeGas(destination, zeroTreeIndex, utils.CodeSizeLeafKey, false) + return gas +} + +// ValueTransferGas returns the gas to be charged for each of the currently +// cold balance member fields of the caller and the callee accounts. +func (ae *AccessEvents) ValueTransferGas(callerAddr, targetAddr common.Address) uint64 { + var gas uint64 + gas += ae.touchAddressAndChargeGas(callerAddr, zeroTreeIndex, utils.BalanceLeafKey, true) + gas += ae.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BalanceLeafKey, true) + return gas +} + +// ContractCreateInitGas returns the access gas costs for the initialization of +// a contract creation. +func (ae *AccessEvents) ContractCreateInitGas(addr common.Address, createSendsValue bool) uint64 { + var gas uint64 + gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.VersionLeafKey, true) + gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.NonceLeafKey, true) + if createSendsValue { + gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BalanceLeafKey, true) + } + return gas +} + +// AddTxOrigin adds the member fields of the sender account to the access event list, +// so that cold accesses are not charged, since they are covered by the 21000 gas. +func (ae *AccessEvents) AddTxOrigin(originAddr common.Address) { + ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.VersionLeafKey, false) + ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.BalanceLeafKey, true) + ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.NonceLeafKey, true) + ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.CodeKeccakLeafKey, false) + ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.CodeSizeLeafKey, false) +} + +// AddTxDestination adds the member fields of the sender account to the access event list, +// so that cold accesses are not charged, since they are covered by the 21000 gas. +func (ae *AccessEvents) AddTxDestination(addr common.Address, sendsValue bool) { + ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.VersionLeafKey, false) + ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BalanceLeafKey, sendsValue) + ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.NonceLeafKey, false) + ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeKeccakLeafKey, false) + ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeSizeLeafKey, false) +} + +// SlotGas returns the amount of gas to be charged for a cold storage access. +func (ae *AccessEvents) SlotGas(addr common.Address, slot common.Hash, isWrite bool) uint64 { + treeIndex, subIndex := utils.StorageIndex(slot.Bytes()) + return ae.touchAddressAndChargeGas(addr, *treeIndex, subIndex, isWrite) +} + +// touchAddressAndChargeGas adds any missing access event to the access event list, and returns the cold +// access cost to be charged, if need be. +func (ae *AccessEvents) touchAddressAndChargeGas(addr common.Address, treeIndex uint256.Int, subIndex byte, isWrite bool) uint64 { + stemRead, selectorRead, stemWrite, selectorWrite, selectorFill := ae.touchAddress(addr, treeIndex, subIndex, isWrite) + + var gas uint64 + if stemRead { + gas += params.WitnessBranchReadCost + } + if selectorRead { + gas += params.WitnessChunkReadCost + } + if stemWrite { + gas += params.WitnessBranchWriteCost + } + if selectorWrite { + gas += params.WitnessChunkWriteCost + } + if selectorFill { + gas += params.WitnessChunkFillCost + } + return gas +} + +// touchAddress adds any missing access event to the access event list. +func (ae *AccessEvents) touchAddress(addr common.Address, treeIndex uint256.Int, subIndex byte, isWrite bool) (bool, bool, bool, bool, bool) { + branchKey := newBranchAccessKey(addr, treeIndex) + chunkKey := newChunkAccessKey(branchKey, subIndex) + + // Read access. + var branchRead, chunkRead bool + if _, hasStem := ae.branches[branchKey]; !hasStem { + branchRead = true + ae.branches[branchKey] = AccessWitnessReadFlag + } + if _, hasSelector := ae.chunks[chunkKey]; !hasSelector { + chunkRead = true + ae.chunks[chunkKey] = AccessWitnessReadFlag + } + + // Write access. + var branchWrite, chunkWrite, chunkFill bool + if isWrite { + if (ae.branches[branchKey] & AccessWitnessWriteFlag) == 0 { + branchWrite = true + ae.branches[branchKey] |= AccessWitnessWriteFlag + } + + chunkValue := ae.chunks[chunkKey] + if (chunkValue & AccessWitnessWriteFlag) == 0 { + chunkWrite = true + ae.chunks[chunkKey] |= AccessWitnessWriteFlag + } + // TODO: charge chunk filling costs if the leaf was previously empty in the state + } + return branchRead, chunkRead, branchWrite, chunkWrite, chunkFill +} + +type branchAccessKey struct { + addr common.Address + treeIndex uint256.Int +} + +func newBranchAccessKey(addr common.Address, treeIndex uint256.Int) branchAccessKey { + var sk branchAccessKey + sk.addr = addr + sk.treeIndex = treeIndex + return sk +} + +type chunkAccessKey struct { + branchAccessKey + leafKey byte +} + +func newChunkAccessKey(branchKey branchAccessKey, leafKey byte) chunkAccessKey { + var lk chunkAccessKey + lk.branchAccessKey = branchKey + lk.leafKey = leafKey + return lk +} + +// CodeChunksRangeGas is a helper function to touch every chunk in a code range and charge witness gas costs +func (ae *AccessEvents) CodeChunksRangeGas(contractAddr common.Address, startPC, size uint64, codeLen uint64, isWrite bool) uint64 { + // note that in the case where the copied code is outside the range of the + // contract code but touches the last leaf with contract code in it, + // we don't include the last leaf of code in the AccessWitness. The + // reason that we do not need the last leaf is the account's code size + // is already in the AccessWitness so a stateless verifier can see that + // the code from the last leaf is not needed. + if (codeLen == 0 && size == 0) || startPC > codeLen { + return 0 + } + + endPC := startPC + size + if endPC > codeLen { + endPC = codeLen + } + if endPC > 0 { + endPC -= 1 // endPC is the last bytecode that will be touched. + } + + var statelessGasCharged uint64 + for chunkNumber := startPC / 31; chunkNumber <= endPC/31; chunkNumber++ { + treeIndex := *uint256.NewInt((chunkNumber + 128) / 256) + subIndex := byte((chunkNumber + 128) % 256) + gas := ae.touchAddressAndChargeGas(contractAddr, treeIndex, subIndex, isWrite) + var overflow bool + statelessGasCharged, overflow = math.SafeAdd(statelessGasCharged, gas) + if overflow { + panic("overflow when adding gas") + } + } + return statelessGasCharged +} + +// VersionGas adds the account's version to the accessed data, and returns the +// amount of gas that it costs. +// Note that an access in write mode implies an access in read mode, whereas an +// access in read mode does not imply an access in write mode. +func (ae *AccessEvents) VersionGas(addr common.Address, isWrite bool) uint64 { + return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.VersionLeafKey, isWrite) +} + +// BalanceGas adds the account's balance to the accessed data, and returns the +// amount of gas that it costs. +// in write mode. If false, the charged gas corresponds to an access in read mode. +// Note that an access in write mode implies an access in read mode, whereas an access in +// read mode does not imply an access in write mode. +func (ae *AccessEvents) BalanceGas(addr common.Address, isWrite bool) uint64 { + return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BalanceLeafKey, isWrite) +} + +// NonceGas adds the account's nonce to the accessed data, and returns the +// amount of gas that it costs. +// in write mode. If false, the charged gas corresponds to an access in read mode. +// Note that an access in write mode implies an access in read mode, whereas an access in +// read mode does not imply an access in write mode. +func (ae *AccessEvents) NonceGas(addr common.Address, isWrite bool) uint64 { + return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.NonceLeafKey, isWrite) +} + +// CodeSizeGas adds the account's code size to the accessed data, and returns the +// amount of gas that it costs. +// in write mode. If false, the charged gas corresponds to an access in read mode. +// Note that an access in write mode implies an access in read mode, whereas an access in +// read mode does not imply an access in write mode. +func (ae *AccessEvents) CodeSizeGas(addr common.Address, isWrite bool) uint64 { + return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeSizeLeafKey, isWrite) +} + +// CodeHashGas adds the account's code hash to the accessed data, and returns the +// amount of gas that it costs. +// in write mode. If false, the charged gas corresponds to an access in read mode. +// Note that an access in write mode implies an access in read mode, whereas an access in +// read mode does not imply an access in write mode. +func (ae *AccessEvents) CodeHashGas(addr common.Address, isWrite bool) uint64 { + return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeKeccakLeafKey, isWrite) +} diff --git a/core/state/access_events_test.go b/core/state/access_events_test.go new file mode 100644 index 0000000000..c8c93accfd --- /dev/null +++ b/core/state/access_events_test.go @@ -0,0 +1,153 @@ +// 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 . + +package state + +import ( + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/trie/utils" +) + +var ( + testAddr [20]byte + testAddr2 [20]byte +) + +func init() { + for i := byte(0); i < 20; i++ { + testAddr[i] = i + testAddr[2] = 2 * i + } +} + +func TestAccountHeaderGas(t *testing.T) { + ae := NewAccessEvents(utils.NewPointCache(1024)) + + // Check cold read cost + gas := ae.VersionGas(testAddr, false) + if want := params.WitnessBranchReadCost + params.WitnessChunkReadCost; gas != want { + t.Fatalf("incorrect gas computed, got %d, want %d", gas, want) + } + + // Check warm read cost + gas = ae.VersionGas(testAddr, false) + if gas != 0 { + t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0) + } + + // Check cold read costs in the same group no longer incur the branch read cost + gas = ae.BalanceGas(testAddr, false) + if gas != params.WitnessChunkReadCost { + t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessChunkReadCost) + } + gas = ae.NonceGas(testAddr, false) + if gas != params.WitnessChunkReadCost { + t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessChunkReadCost) + } + gas = ae.CodeSizeGas(testAddr, false) + if gas != params.WitnessChunkReadCost { + t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessChunkReadCost) + } + gas = ae.CodeHashGas(testAddr, false) + if gas != params.WitnessChunkReadCost { + t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessChunkReadCost) + } + + // Check cold write cost + gas = ae.VersionGas(testAddr, true) + if want := params.WitnessBranchWriteCost + params.WitnessChunkWriteCost; gas != want { + t.Fatalf("incorrect gas computed, got %d, want %d", gas, want) + } + + // Check warm write cost + gas = ae.VersionGas(testAddr, true) + if gas != 0 { + t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0) + } + + // Check a write without a read charges both read and write costs + gas = ae.BalanceGas(testAddr2, true) + if want := params.WitnessBranchReadCost + params.WitnessBranchWriteCost + params.WitnessChunkWriteCost + params.WitnessChunkReadCost; gas != want { + t.Fatalf("incorrect gas computed, got %d, want %d", gas, want) + } + + // Check that a write followed by a read charges nothing + gas = ae.BalanceGas(testAddr2, false) + if gas != 0 { + t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0) + } + + // Check that reading a slot from the account header only charges the + // chunk read cost. + gas = ae.SlotGas(testAddr, common.Hash{}, false) + if gas != params.WitnessChunkReadCost { + t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessChunkReadCost) + } +} + +// TestContractCreateInitGas checks that the gas cost of contract creation is correctly +// calculated. +func TestContractCreateInitGas(t *testing.T) { + ae := NewAccessEvents(utils.NewPointCache(1024)) + + var testAddr [20]byte + for i := byte(0); i < 20; i++ { + testAddr[i] = i + } + + // Check cold read cost, without a value + gas := ae.ContractCreateInitGas(testAddr, false) + if want := params.WitnessBranchWriteCost + params.WitnessBranchReadCost + params.WitnessChunkWriteCost*2 + params.WitnessChunkReadCost*2; gas != want { + t.Fatalf("incorrect gas computed, got %d, want %d", gas, want) + } + + // Check warm read cost + gas = ae.ContractCreateInitGas(testAddr, false) + if gas != 0 { + t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0) + } +} + +// TestMessageCallGas checks that the gas cost of message calls is correctly +// calculated. +func TestMessageCallGas(t *testing.T) { + ae := NewAccessEvents(utils.NewPointCache(1024)) + + // Check cold read cost, without a value + gas := ae.MessageCallGas(testAddr) + if want := params.WitnessBranchReadCost + params.WitnessChunkReadCost*2; gas != want { + t.Fatalf("incorrect gas computed, got %d, want %d", gas, want) + } + + // Check that reading the version and code size of the same account does not incur the branch read cost + gas = ae.VersionGas(testAddr, false) + if gas != 0 { + t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0) + } + gas = ae.CodeSizeGas(testAddr, false) + if gas != 0 { + t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0) + } + + // Check warm read cost + gas = ae.MessageCallGas(testAddr) + if gas != 0 { + t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0) + } +} diff --git a/core/state/access_list.go b/core/state/access_list.go index 718bf17cf7..90e5590748 100644 --- a/core/state/access_list.go +++ b/core/state/access_list.go @@ -17,7 +17,10 @@ package state import ( + "fmt" "maps" + "slices" + "strings" "github.com/ethereum/go-ethereum/common" ) @@ -57,11 +60,11 @@ func newAccessList() *accessList { } // Copy creates an independent copy of an accessList. -func (a *accessList) Copy() *accessList { +func (al *accessList) Copy() *accessList { cp := newAccessList() - cp.addresses = maps.Clone(a.addresses) - cp.slots = make([]map[common.Hash]struct{}, len(a.slots)) - for i, slotMap := range a.slots { + cp.addresses = maps.Clone(al.addresses) + cp.slots = make([]map[common.Hash]struct{}, len(al.slots)) + for i, slotMap := range al.slots { cp.slots[i] = maps.Clone(slotMap) } return cp @@ -130,3 +133,35 @@ func (al *accessList) DeleteSlot(address common.Address, slot common.Hash) { func (al *accessList) DeleteAddress(address common.Address) { delete(al.addresses, address) } + +// Equal returns true if the two access lists are identical +func (al *accessList) Equal(other *accessList) bool { + if !maps.Equal(al.addresses, other.addresses) { + return false + } + return slices.EqualFunc(al.slots, other.slots, + func(m map[common.Hash]struct{}, m2 map[common.Hash]struct{}) bool { + return maps.Equal(m, m2) + }) +} + +// PrettyPrint prints the contents of the access list in a human-readable form +func (al *accessList) PrettyPrint() string { + out := new(strings.Builder) + var sortedAddrs []common.Address + for addr := range al.addresses { + sortedAddrs = append(sortedAddrs, addr) + } + slices.SortFunc(sortedAddrs, common.Address.Cmp) + for _, addr := range sortedAddrs { + idx := al.addresses[addr] + fmt.Fprintf(out, "%#x : (idx %d)\n", addr, idx) + if idx >= 0 { + slotmap := al.slots[idx] + for h := range slotmap { + fmt.Fprintf(out, " %#x\n", h) + } + } + } + return out.String() +} diff --git a/core/state/database.go b/core/state/database.go index 498f02a582..23453ba7b1 100644 --- a/core/state/database.go +++ b/core/state/database.go @@ -20,7 +20,6 @@ import ( "errors" "fmt" - "github.com/crate-crypto/go-ipa/banderwagon" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/lru" "github.com/ethereum/go-ethereum/core/rawdb" @@ -40,11 +39,8 @@ const ( // Cache size granted for caching clean code. codeCacheSize = 64 * 1024 * 1024 - // commitmentSize is the size of commitment stored in cache. - commitmentSize = banderwagon.UncompressedSize - - // Cache item granted for caching commitment results. - commitmentCacheItems = 64 * 1024 * 1024 / (commitmentSize + common.AddressLength) + // Number of address->curve point associations to keep. + pointCacheSize = 4096 ) // Database wraps access to tries and contract code. @@ -67,6 +63,9 @@ type Database interface { // DiskDB returns the underlying key-value disk database. DiskDB() ethdb.KeyValueStore + // PointCache returns the cache holding points used in verkle tree key computation + PointCache() *utils.PointCache + // TrieDB returns the underlying trie database for managing trie nodes. TrieDB() *triedb.Database @@ -127,7 +126,11 @@ type Trie interface { // The returned nodeset can be nil if the trie is clean(nothing to commit). // Once the trie is committed, it's not usable anymore. A new trie must // be created with new root and updated trie database for following usage - Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet, error) + Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet) + + // Witness returns a set containing all trie nodes that have been accessed. + // The returned map could be nil if the witness is empty. + Witness() map[string]struct{} // NodeIterator returns an iterator that returns nodes of the trie. Iteration // starts at the key after the given start key. And error will be returned @@ -142,6 +145,9 @@ type Trie interface { // nodes of the longest existing prefix of the key (at least the root), ending // with the node that proves the absence of the key. Prove(key []byte, proofDb ethdb.KeyValueWriter) error + + // IsVerkle returns true if the trie is verkle-tree based + IsVerkle() bool } // NewDatabase creates a backing store for state. The returned database is safe for @@ -163,6 +169,7 @@ func NewDatabaseWithConfig(db ethdb.Database, config *triedb.Config) Database { codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize), triedb: triedb.NewDatabase(db, config), noTries: noTries, + pointCache: utils.NewPointCache(pointCacheSize), } } @@ -176,6 +183,7 @@ func NewDatabaseWithNodeDB(db ethdb.Database, triedb *triedb.Database) Database codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize), triedb: triedb, noTries: noTries, + pointCache: utils.NewPointCache(pointCacheSize), } } @@ -185,6 +193,7 @@ type cachingDB struct { codeCache *lru.SizeConstrainedCache[common.Hash, []byte] triedb *triedb.Database noTries bool + pointCache *utils.PointCache } // OpenTrie opens the main account trie at a specific root hash. @@ -193,7 +202,7 @@ func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) { return trie.NewEmptyTrie(), nil } if db.triedb.IsVerkle() { - return trie.NewVerkleTrie(root, db.triedb, utils.NewPointCache(commitmentCacheItems)) + return trie.NewVerkleTrie(root, db.triedb, db.pointCache) } tr, err := trie.NewStateTrie(trie.StateTrieID(root), db.triedb) if err != nil { @@ -292,3 +301,8 @@ func (db *cachingDB) DiskDB() ethdb.KeyValueStore { func (db *cachingDB) TrieDB() *triedb.Database { return db.triedb } + +// PointCache returns the cache of evaluated curve points. +func (db *cachingDB) PointCache() *utils.PointCache { + return db.pointCache +} diff --git a/core/state/journal.go b/core/state/journal.go index 198c8da554..5ddbea238d 100644 --- a/core/state/journal.go +++ b/core/state/journal.go @@ -17,6 +17,8 @@ package state import ( + "maps" + "github.com/ethereum/go-ethereum/common" "github.com/holiman/uint256" ) @@ -29,6 +31,9 @@ type journalEntry interface { // dirtied returns the Ethereum address modified by this journal entry. dirtied() *common.Address + + // copy returns a deep-copied journal entry. + copy() journalEntry } // journal contains the list of state modifications applied since the last state @@ -84,22 +89,31 @@ func (j *journal) length() int { return len(j.entries) } +// copy returns a deep-copied journal. +func (j *journal) copy() *journal { + entries := make([]journalEntry, 0, j.length()) + for i := 0; i < j.length(); i++ { + entries = append(entries, j.entries[i].copy()) + } + return &journal{ + entries: entries, + dirties: maps.Clone(j.dirties), + } +} + type ( // Changes to the account trie. createObjectChange struct { account *common.Address } - resetObjectChange struct { - account *common.Address - prev *stateObject - prevdestruct bool - prevAccount []byte - prevStorage map[common.Hash][]byte - prevAccountOriginExist bool - prevAccountOrigin []byte - prevStorageOrigin map[common.Hash][]byte + // createContractChange represents an account becoming a contract-account. + // This event happens prior to executing initcode. The journal-event simply + // manages the created-flag, in order to allow same-tx destruction. + createContractChange struct { + account common.Address } + selfDestructChange struct { account *common.Address prev bool // whether account had already self-destructed @@ -116,8 +130,10 @@ type ( prev uint64 } storageChange struct { - account *common.Address - key, prevalue common.Hash + account *common.Address + key common.Hash + prevvalue common.Hash + origvalue common.Hash } codeChange struct { account *common.Address @@ -137,6 +153,7 @@ type ( touchChange struct { account *common.Address } + // Changes to the access list accessListAddAccountChange struct { address *common.Address @@ -146,6 +163,7 @@ type ( slot *common.Hash } + // Changes to transient storage transientStorageChange struct { account *common.Address key, prevalue common.Hash @@ -154,34 +172,30 @@ type ( func (ch createObjectChange) revert(s *StateDB) { delete(s.stateObjects, *ch.account) - delete(s.stateObjectsDirty, *ch.account) } func (ch createObjectChange) dirtied() *common.Address { return ch.account } -func (ch resetObjectChange) revert(s *StateDB) { - s.setStateObject(ch.prev) - if !ch.prevdestruct { - delete(s.stateObjectsDestruct, ch.prev.address) - } - if ch.prevAccount != nil { - s.accounts[ch.prev.addrHash] = ch.prevAccount - } - if ch.prevStorage != nil { - s.storages[ch.prev.addrHash] = ch.prevStorage - } - if ch.prevAccountOriginExist { - s.accountsOrigin[ch.prev.address] = ch.prevAccountOrigin - } - if ch.prevStorageOrigin != nil { - s.storagesOrigin[ch.prev.address] = ch.prevStorageOrigin +func (ch createObjectChange) copy() journalEntry { + return createObjectChange{ + account: ch.account, } } -func (ch resetObjectChange) dirtied() *common.Address { - return ch.account +func (ch createContractChange) revert(s *StateDB) { + s.getStateObject(ch.account).newContract = false +} + +func (ch createContractChange) dirtied() *common.Address { + return nil +} + +func (ch createContractChange) copy() journalEntry { + return createContractChange{ + account: ch.account, + } } func (ch selfDestructChange) revert(s *StateDB) { @@ -196,6 +210,14 @@ func (ch selfDestructChange) dirtied() *common.Address { return ch.account } +func (ch selfDestructChange) copy() journalEntry { + return selfDestructChange{ + account: ch.account, + prev: ch.prev, + prevbalance: new(uint256.Int).Set(ch.prevbalance), + } +} + var ripemd = common.HexToAddress("0000000000000000000000000000000000000003") func (ch touchChange) revert(s *StateDB) { @@ -205,6 +227,12 @@ func (ch touchChange) dirtied() *common.Address { return ch.account } +func (ch touchChange) copy() journalEntry { + return touchChange{ + account: ch.account, + } +} + func (ch balanceChange) revert(s *StateDB) { s.getStateObject(*ch.account).setBalance(ch.prev) } @@ -213,6 +241,13 @@ func (ch balanceChange) dirtied() *common.Address { return ch.account } +func (ch balanceChange) copy() journalEntry { + return balanceChange{ + account: ch.account, + prev: new(uint256.Int).Set(ch.prev), + } +} + func (ch nonceChange) revert(s *StateDB) { s.getStateObject(*ch.account).setNonce(ch.prev) } @@ -221,6 +256,13 @@ func (ch nonceChange) dirtied() *common.Address { return ch.account } +func (ch nonceChange) copy() journalEntry { + return nonceChange{ + account: ch.account, + prev: ch.prev, + } +} + func (ch codeChange) revert(s *StateDB) { s.getStateObject(*ch.account).setCode(common.BytesToHash(ch.prevhash), ch.prevcode) } @@ -229,14 +271,30 @@ func (ch codeChange) dirtied() *common.Address { return ch.account } +func (ch codeChange) copy() journalEntry { + return codeChange{ + account: ch.account, + prevhash: common.CopyBytes(ch.prevhash), + prevcode: common.CopyBytes(ch.prevcode), + } +} + func (ch storageChange) revert(s *StateDB) { - s.getStateObject(*ch.account).setState(ch.key, ch.prevalue) + s.getStateObject(*ch.account).setState(ch.key, ch.prevvalue, ch.origvalue) } func (ch storageChange) dirtied() *common.Address { return ch.account } +func (ch storageChange) copy() journalEntry { + return storageChange{ + account: ch.account, + key: ch.key, + prevvalue: ch.prevvalue, + } +} + func (ch transientStorageChange) revert(s *StateDB) { s.setTransientState(*ch.account, ch.key, ch.prevalue) } @@ -245,6 +303,14 @@ func (ch transientStorageChange) dirtied() *common.Address { return nil } +func (ch transientStorageChange) copy() journalEntry { + return transientStorageChange{ + account: ch.account, + key: ch.key, + prevalue: ch.prevalue, + } +} + func (ch refundChange) revert(s *StateDB) { s.refund = ch.prev } @@ -253,6 +319,12 @@ func (ch refundChange) dirtied() *common.Address { return nil } +func (ch refundChange) copy() journalEntry { + return refundChange{ + prev: ch.prev, + } +} + func (ch addLogChange) revert(s *StateDB) { logs := s.logs[ch.txhash] if len(logs) == 1 { @@ -267,6 +339,12 @@ func (ch addLogChange) dirtied() *common.Address { return nil } +func (ch addLogChange) copy() journalEntry { + return addLogChange{ + txhash: ch.txhash, + } +} + func (ch addPreimageChange) revert(s *StateDB) { delete(s.preimages, ch.hash) } @@ -275,6 +353,12 @@ func (ch addPreimageChange) dirtied() *common.Address { return nil } +func (ch addPreimageChange) copy() journalEntry { + return addPreimageChange{ + hash: ch.hash, + } +} + func (ch accessListAddAccountChange) revert(s *StateDB) { /* One important invariant here, is that whenever a (addr, slot) is added, if the @@ -294,6 +378,12 @@ func (ch accessListAddAccountChange) dirtied() *common.Address { return nil } +func (ch accessListAddAccountChange) copy() journalEntry { + return accessListAddAccountChange{ + address: ch.address, + } +} + func (ch accessListAddSlotChange) revert(s *StateDB) { if s.accessList != nil { s.accessList.DeleteSlot(*ch.address, *ch.slot) @@ -303,3 +393,10 @@ func (ch accessListAddSlotChange) revert(s *StateDB) { func (ch accessListAddSlotChange) dirtied() *common.Address { return nil } + +func (ch accessListAddSlotChange) copy() journalEntry { + return accessListAddSlotChange{ + address: ch.address, + slot: ch.slot, + } +} diff --git a/core/state/pruner/pruner.go b/core/state/pruner/pruner.go index 629af94517..8011c16355 100644 --- a/core/state/pruner/pruner.go +++ b/core/state/pruner/pruner.go @@ -242,8 +242,7 @@ func pruneAll(maindb ethdb.Database, g *core.Genesis) error { statedb.SetState(addr, key, value) } } - root := statedb.IntermediateRoot(false) - statedb.Commit(0, nil) + root, _, _ := statedb.Commit(0, false) statedb.Database().TrieDB().Commit(root, true) log.Info("State pruning successful", "pruned", size, "elapsed", common.PrettyDuration(time.Since(start))) return nil diff --git a/core/state/snapshot/generate.go b/core/state/snapshot/generate.go index 5b20d9e275..002d54533e 100644 --- a/core/state/snapshot/generate.go +++ b/core/state/snapshot/generate.go @@ -360,10 +360,7 @@ func (dl *diskLayer) generateRange(ctx *generatorContext, trieId *trie.ID, prefi for i, key := range result.keys { snapTrie.Update(key, result.vals[i]) } - root, nodes, err := snapTrie.Commit(false) - if err != nil { - return false, nil, err - } + root, nodes := snapTrie.Commit(false) if nodes != nil { tdb.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil) tdb.Commit(root, false) diff --git a/core/state/snapshot/generate_test.go b/core/state/snapshot/generate_test.go index da93ebc875..891111973a 100644 --- a/core/state/snapshot/generate_test.go +++ b/core/state/snapshot/generate_test.go @@ -210,7 +210,7 @@ func (t *testHelper) makeStorageTrie(owner common.Hash, keys []string, vals []st if !commit { return stTrie.Hash() } - root, nodes, _ := stTrie.Commit(false) + root, nodes := stTrie.Commit(false) if nodes != nil { t.nodes.Merge(nodes) } @@ -218,7 +218,7 @@ func (t *testHelper) makeStorageTrie(owner common.Hash, keys []string, vals []st } func (t *testHelper) Commit() common.Hash { - root, nodes, _ := t.accTrie.Commit(true) + root, nodes := t.accTrie.Commit(true) if nodes != nil { t.nodes.Merge(nodes) } diff --git a/core/state/snapshot/iterator_test.go b/core/state/snapshot/iterator_test.go index 54614427a5..daa8cdcc54 100644 --- a/core/state/snapshot/iterator_test.go +++ b/core/state/snapshot/iterator_test.go @@ -815,7 +815,7 @@ func TestStorageIteratorDeletions(t *testing.T) { verifyIterator(t, 2, snaps.Snapshot(common.HexToHash("0x06")).(*diffLayer).newBinaryStorageIterator(common.HexToHash("0xaa")), verifyStorage) } -// BenchmarkAccountIteratorTraversal is a bit a bit notorious -- all layers contain the +// BenchmarkAccountIteratorTraversal is a bit notorious -- all layers contain the // exact same 200 accounts. That means that we need to process 2000 items, but // only spit out 200 values eventually. // diff --git a/core/state/snapshot/snapshot.go b/core/state/snapshot/snapshot.go index 312859c548..a85d23b4b7 100644 --- a/core/state/snapshot/snapshot.go +++ b/core/state/snapshot/snapshot.go @@ -679,6 +679,9 @@ func diffToDisk(bottom *diffLayer) *diskLayer { // Release releases resources func (t *Tree) Release() { + t.lock.RLock() + defer t.lock.RUnlock() + if dl := t.disklayer(); dl != nil { dl.Release() } @@ -842,6 +845,8 @@ func (t *Tree) disklayer() *diskLayer { case *diskLayer: return layer case *diffLayer: + layer.lock.RLock() + defer layer.lock.RUnlock() return layer.origin default: panic(fmt.Sprintf("%T: undefined layer", snap)) @@ -861,8 +866,8 @@ func (t *Tree) diskRoot() common.Hash { // generating is an internal helper function which reports whether the snapshot // is still under the construction. func (t *Tree) generating() (bool, error) { - t.lock.Lock() - defer t.lock.Unlock() + t.lock.RLock() + defer t.lock.RUnlock() layer := t.disklayer() if layer == nil { @@ -873,10 +878,10 @@ func (t *Tree) generating() (bool, error) { return layer.genMarker != nil, nil } -// DiskRoot is a external helper function to return the disk layer root. +// DiskRoot is an external helper function to return the disk layer root. func (t *Tree) DiskRoot() common.Hash { - t.lock.Lock() - defer t.lock.Unlock() + t.lock.RLock() + defer t.lock.RUnlock() return t.diskRoot() } diff --git a/core/state/state_object.go b/core/state/state_object.go index e322691806..c0cd916766 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -19,7 +19,6 @@ package state import ( "bytes" "fmt" - "io" "maps" "sync" "time" @@ -28,26 +27,14 @@ import ( "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie/trienode" "github.com/holiman/uint256" ) -type Code []byte - -func (c Code) String() string { - return string(c) //strings.Join(Disassemble(c), " ") -} - type Storage map[common.Hash]common.Hash -func (s Storage) String() (str string) { - for key, value := range s { - str += fmt.Sprintf("%X : %X\n", key, value) - } - return -} - func (s Storage) Copy() Storage { return maps.Clone(s) } @@ -66,28 +53,38 @@ type stateObject struct { data types.StateAccount // Account data with all mutations applied in the scope of block // Write caches. - trie Trie // storage trie, which becomes non-nil on first access - code Code // contract bytecode, which gets set when code is loaded + trie Trie // storage trie, which becomes non-nil on first access + code []byte // contract bytecode, which gets set when code is loaded sharedOriginStorage *sync.Map // Point to the entry of the stateObject in sharedPool - originStorage Storage // Storage cache of original entries to dedup rewrites - pendingStorage Storage // Storage entries that need to be flushed to disk, at the end of an entire block - dirtyStorage Storage // Storage entries that have been modified in the current transaction execution, reset for every transaction + originStorage Storage // Storage entries that have been accessed within the current block + dirtyStorage Storage // Storage entries that have been modified within the current transaction + pendingStorage Storage // Storage entries that have been modified within the current block + + // uncommittedStorage tracks a set of storage entries that have been modified + // but not yet committed since the "last commit operation", along with their + // original values before mutation. + // + // Specifically, the commit will be performed after each transaction before + // the byzantium fork, therefore the map is already reset at the transaction + // boundary; however post the byzantium fork, the commit will only be performed + // at the end of block, this set essentially tracks all the modifications + // made within the block. + uncommittedStorage Storage // Cache flags. dirtyCode bool // true if the code was updated - // Flag whether the account was marked as self-destructed. The self-destructed account - // is still accessible in the scope of same transaction. + // Flag whether the account was marked as self-destructed. The self-destructed + // account is still accessible in the scope of same transaction. selfDestructed bool - // Flag whether the account was marked as deleted. A self-destructed account - // or an account that is considered as empty will be marked as deleted at - // the end of transaction and no longer accessible anymore. - deleted bool - - // Flag whether the object was created in the current transaction - created bool + // This is an EIP-6780 flag indicating whether the object is eligible for + // self-destruct according to EIP-6780. The flag could be set either when + // the contract is just created within the current transaction, or when the + // object was previously existent and is being deployed as a contract within + // the current transaction. + newContract bool } // empty returns whether the account is considered empty. @@ -97,10 +94,7 @@ func (s *stateObject) empty() bool { // newObject creates a state object. func newObject(db *StateDB, address common.Address, acct *types.StateAccount) *stateObject { - var ( - origin = acct - created = acct == nil // true if the account was not existent - ) + origin := acct if acct == nil { acct = types.NewEmptyStateAccount() } @@ -120,15 +114,10 @@ func newObject(db *StateDB, address common.Address, acct *types.StateAccount) *s originStorage: make(Storage), pendingStorage: make(Storage), dirtyStorage: make(Storage), - created: created, + uncommittedStorage: make(Storage), } } -// EncodeRLP implements rlp.Encoder. -func (s *stateObject) EncodeRLP(w io.Writer) error { - return rlp.Encode(w, &s.data) -} - func (s *stateObject) markSelfdestructed() { s.selfDestructed = true } @@ -144,36 +133,37 @@ func (s *stateObject) touch() { } } -// getTrie returns the associated storage trie. The trie will be opened -// if it's not loaded previously. An error will be returned if trie can't -// be loaded. +// getTrie returns the associated storage trie. The trie will be opened if it's +// not loaded previously. An error will be returned if trie can't be loaded. +// +// If a new trie is opened, it will be cached within the state object to allow +// subsequent reads to expand the same trie instead of reloading from disk. func (s *stateObject) getTrie() (Trie, error) { if s.trie == nil { - // Try fetching from prefetcher first - // if s.data.Root != types.EmptyRootHash && s.db.prefetcher != nil { - // When the miner is creating the pending state, there is no prefetcher - // s.trie = s.db.prefetcher.trie(s.addrHash, s.data.Root) - // } - // if s.trie == nil { tr, err := s.db.db.OpenStorageTrie(s.db.originalRoot, s.address, s.data.Root, s.db.trie) if err != nil { return nil, err } s.trie = tr - // } } return s.trie, nil } -// GetState retrieves a value from the account storage trie. -func (s *stateObject) GetState(key common.Hash) common.Hash { - // If we have a dirty value for this state entry, return it - value, dirty := s.dirtyStorage[key] - if dirty { - return value +// getPrefetchedTrie returns the associated trie, as populated by the prefetcher +// if it's available. +// +// Note, opposed to getTrie, this method will *NOT* blindly cache the resulting +// trie in the state object. The caller might want to do that, but it's cleaner +// to break the hidden interdependency between retrieving tries from the db or +// from the prefetcher. +func (s *stateObject) getPrefetchedTrie() Trie { + // If there's nothing to meaningfully return, let the user figure it out by + // pulling the trie from disk. + if s.data.Root == types.EmptyRootHash || s.db.prefetcher == nil { + return nil } - // Otherwise return the entry's original value - return s.GetCommittedState(key) + // Attempt to retrieve the trie from the prefetcher + return s.db.prefetcher.trie(s.addrHash, s.data.Root) } func (s *stateObject) getOriginStorage(key common.Hash) (common.Hash, bool) { @@ -201,6 +191,25 @@ func (s *stateObject) setOriginStorage(key common.Hash, value common.Hash) { } // GetCommittedState retrieves a value from the committed account storage trie. +// GetState retrieves a value associated with the given storage key. +func (s *stateObject) GetState(key common.Hash) common.Hash { + value, _ := s.getState(key) + return value +} + +// getState retrieves a value associated with the given storage key, along with +// its original value. +func (s *stateObject) getState(key common.Hash) (common.Hash, common.Hash) { + origin := s.GetCommittedState(key) + value, dirty := s.dirtyStorage[key] + if dirty { + return value, origin + } + return origin, origin +} + +// GetCommittedState retrieves the value associated with the specific key +// without any mutations caused in the current execution. func (s *stateObject) GetCommittedState(key common.Hash) common.Hash { // If we have a pending write or clean cached, return that if value, pending := s.pendingStorage[key]; pending { @@ -217,6 +226,7 @@ func (s *stateObject) GetCommittedState(key common.Hash) common.Hash { // have been handles via pendingStorage above. // 2) we don't have new values, and can deliver empty response back if _, destructed := s.db.stateObjectsDestruct[s.address]; destructed { + s.originStorage[key] = common.Hash{} // track the empty slot as origin value return common.Hash{} } // If no live objects are available, attempt to use snapshots @@ -255,49 +265,88 @@ func (s *stateObject) GetCommittedState(key common.Hash) common.Hash { } value.SetBytes(val) } + // Independent of where we loaded the data from, add it to the prefetcher. + // Whilst this would be a bit weird if snapshots are disabled, but we still + // want the trie nodes to end up in the prefetcher too, so just push through. + if s.db.prefetcher != nil && s.data.Root != types.EmptyRootHash { + if err = s.db.prefetcher.prefetch(s.addrHash, s.origin.Root, s.address, [][]byte{key[:]}, true); err != nil { + log.Error("Failed to prefetch storage slot", "addr", s.address, "key", key, "err", err) + } + } s.setOriginStorage(key, value) return value } // SetState updates a value in account storage. func (s *stateObject) SetState(key, value common.Hash) { - // If the new value is the same as old, don't set - prev := s.GetState(key) + // If the new value is the same as old, don't set. Otherwise, track only the + // dirty changes, supporting reverting all of it back to no change. + prev, origin := s.getState(key) if prev == value { return } // New value is different, update and journal the change s.db.journal.append(storageChange{ - account: &s.address, - key: key, - prevalue: prev, + account: &s.address, + key: key, + prevvalue: prev, + origvalue: origin, }) if s.db.logger != nil && s.db.logger.OnStorageChange != nil { s.db.logger.OnStorageChange(s.address, key, prev, value) } - s.setState(key, value) + s.setState(key, value, origin) } -func (s *stateObject) setState(key, value common.Hash) { +// setState updates a value in account dirty storage. The dirtiness will be +// removed if the value being set equals to the original value. +func (s *stateObject) setState(key common.Hash, value common.Hash, origin common.Hash) { + // Storage slot is set back to its original value, undo the dirty marker + if value == origin { + delete(s.dirtyStorage, key) + return + } s.dirtyStorage[key] = value } // finalise moves all dirty storage slots into the pending area to be hashed or // committed later. It is invoked at the end of every transaction. -func (s *stateObject) finalise(prefetch bool) { +func (s *stateObject) finalise() { slotsToPrefetch := make([][]byte, 0, len(s.dirtyStorage)) for key, value := range s.dirtyStorage { - s.pendingStorage[key] = value - if value != s.originStorage[key] { + if origin, exist := s.uncommittedStorage[key]; exist && origin == value { + // The slot is reverted to its original value, delete the entry + // to avoid thrashing the data structures. + delete(s.uncommittedStorage, key) + } else if exist { + // The slot is modified to another value and the slot has been + // tracked for commit, do nothing here. + } else { + // The slot is different from its original value and hasn't been + // tracked for commit yet. + s.uncommittedStorage[key] = s.GetCommittedState(key) slotsToPrefetch = append(slotsToPrefetch, common.CopyBytes(key[:])) // Copy needed for closure } + // Aggregate the dirty storage slots into the pending area. It might + // be possible that the value of tracked slot here is same with the + // one in originStorage (e.g. the slot was modified in tx_a and then + // modified back in tx_b). We can't blindly remove it from pending + // map as the dirty slot might have been committed already (before the + // byzantium fork) and entry is necessary to modify the value back. + s.pendingStorage[key] = value } - if s.db.prefetcher != nil && prefetch && len(slotsToPrefetch) > 0 && s.data.Root != types.EmptyRootHash { - s.db.prefetcher.prefetch(s.addrHash, s.data.Root, s.address, slotsToPrefetch) + if s.db.prefetcher != nil && len(slotsToPrefetch) > 0 && s.data.Root != types.EmptyRootHash { + if err := s.db.prefetcher.prefetch(s.addrHash, s.data.Root, s.address, slotsToPrefetch, false); err != nil { + log.Error("Failed to prefetch slots", "addr", s.address, "slots", len(slotsToPrefetch), "err", err) + } } if len(s.dirtyStorage) > 0 { s.dirtyStorage = make(Storage) } + // Revoke the flag at the end of the transaction. It finalizes the status + // of the newly-created object as it's no longer eligible for self-destruct + // by EIP-6780. For non-newly-created objects, it's a no-op. + s.newContract = false } // updateTrie is responsible for persisting cached storage changes into the @@ -306,38 +355,39 @@ func (s *stateObject) finalise(prefetch bool) { // loading or updating of the trie, an error will be returned. Furthermore, // this function will return the mutated storage trie, or nil if there is no // storage change at all. +// +// It assumes all the dirty storage slots have been finalized before. func (s *stateObject) updateTrie() (Trie, error) { - // Make sure all dirty slots are finalized into the pending storage area - s.finalise(false) - + // Short circuit if nothing was accessed, don't trigger a prefetcher warning + if len(s.uncommittedStorage) == 0 { + // Nothing was written, so we could stop early. Unless we have both reads + // and witness collection enabled, in which case we need to fetch the trie. + if s.db.witness == nil || len(s.originStorage) == 0 { + return s.trie, nil + } + } + // Retrieve a pretecher populated trie, or fall back to the database. This will + // block until all prefetch tasks are done, which are needed for witnesses even + // for unmodified state objects. + tr := s.getPrefetchedTrie() + if tr != nil { + // Prefetcher returned a live trie, swap it out for the current one + s.trie = tr + } else { + // Fetcher not running or empty trie, fallback to the database trie + var err error + tr, err = s.getTrie() + if err != nil { + s.db.setError(err) + return nil, err + } + } // Short circuit if nothing changed, don't bother with hashing anything - if len(s.pendingStorage) == 0 { + if len(s.uncommittedStorage) == 0 { return s.trie, nil } - // Track the amount of time wasted on updating the storage trie - defer func(start time.Time) { - s.db.MetricsMux.Lock() - s.db.StorageUpdates += time.Since(start) - s.db.MetricsMux.Unlock() - }(time.Now()) - - // The snapshot storage map for the object - var ( - storage map[common.Hash][]byte - origin map[common.Hash][]byte - hasher = crypto.NewKeccakState() - ) - tr, err := s.getTrie() - if err != nil { - s.db.setError(err) - return nil, err - } - // Insert all the pending storage updates into the trie - usedStorage := make([][]byte, 0, len(s.pendingStorage)) - dirtyStorage := make(map[common.Hash][]byte) - - // Perform trie updates before deletions. This prevents resolution of unnecessary trie nodes - // in circumstances similar to the following: + // Perform trie updates before deletions. This prevents resolution of unnecessary trie nodes + // in circumstances similar to the following: // // Consider nodes `A` and `B` who share the same full node parent `P` and have no other siblings. // During the execution of a block: @@ -346,90 +396,44 @@ func (s *stateObject) updateTrie() (Trie, error) { // If the deletion is handled first, then `P` would be left with only one child, thus collapsed // into a shortnode. This requires `B` to be resolved from disk. // Whereas if the created node is handled first, then the collapse is avoided, and `B` is not resolved. - var deletions []common.Hash - for key, value := range s.pendingStorage { + var ( + deletions []common.Hash + used = make([][]byte, 0, len(s.uncommittedStorage)) + ) + for key, origin := range s.uncommittedStorage { // Skip noop changes, persist actual changes - if value == s.originStorage[key] { + value, exist := s.pendingStorage[key] + if value == origin { + log.Error("Storage update was noop", "address", s.address, "slot", key) continue } - var v []byte - if value != (common.Hash{}) { - value := value - v = common.TrimLeftZeroes(value[:]) - } - dirtyStorage[key] = v - } - var wg sync.WaitGroup - wg.Add(1) - go func() { - defer wg.Done() - for key, value := range dirtyStorage { - if len(value) != 0 { - if err := tr.UpdateStorage(s.address, key[:], value); err != nil { - s.db.setError(err) - } - s.db.StorageUpdated += 1 - } else { - deletions = append(deletions, key) - } - // Cache the items for preloading - usedStorage = append(usedStorage, common.CopyBytes(key[:])) + if !exist { + log.Error("Storage slot is not found in pending area", s.address, "slot", key) + continue } - for _, key := range deletions { - if err := tr.DeleteStorage(s.address, key[:]); err != nil { + if (value != common.Hash{}) { + if err := tr.UpdateStorage(s.address, key[:], common.TrimLeftZeroes(value[:])); err != nil { s.db.setError(err) + return nil, err } - s.db.StorageDeleted += 1 - } - }() - // If state snapshotting is active, cache the data til commit - wg.Add(1) - go func() { - defer wg.Done() - s.db.StorageMux.Lock() - // The snapshot storage map for the object - storage = s.db.storages[s.addrHash] - if storage == nil { - storage = make(map[common.Hash][]byte, len(dirtyStorage)) - s.db.storages[s.addrHash] = storage - } - // Cache the original value of mutated storage slots - origin = s.db.storagesOrigin[s.address] - if origin == nil { - origin = make(map[common.Hash][]byte) - s.db.storagesOrigin[s.address] = origin + s.db.StorageUpdated.Add(1) + } else { + deletions = append(deletions, key) } - s.db.StorageMux.Unlock() - for key, value := range dirtyStorage { - khash := crypto.HashData(hasher, key[:]) - - // rlp-encoded value to be used by the snapshot - var encoded []byte - if len(value) != 0 { - encoded, _ = rlp.EncodeToBytes(value) - } - storage[khash] = encoded // encoded will be nil if it's deleted - - // Track the original value of slot only if it's mutated first time - prev := s.originStorage[key] - s.originStorage[key] = common.BytesToHash(value) // fill back left zeroes by BytesToHash - if _, ok := origin[khash]; !ok { - if prev == (common.Hash{}) { - origin[khash] = nil // nil if it was not present previously - } else { - // Encoding []byte cannot fail, ok to ignore the error. - b, _ := rlp.EncodeToBytes(common.TrimLeftZeroes(prev[:])) - origin[khash] = b - } - } + // Cache the items for preloading + used = append(used, common.CopyBytes(key[:])) // Copy needed for closure + } + for _, key := range deletions { + if err := tr.DeleteStorage(s.address, key[:]); err != nil { + s.db.setError(err) + return nil, err } - }() - wg.Wait() - + s.db.StorageDeleted.Add(1) + } if s.db.prefetcher != nil { - s.db.prefetcher.used(s.addrHash, s.data.Root, usedStorage) + s.db.prefetcher.used(s.addrHash, s.data.Root, used) } - s.pendingStorage = make(Storage) // reset pending map + s.uncommittedStorage = make(Storage) // empties the commit markers return tr, nil } @@ -449,39 +453,79 @@ func (s *stateObject) updateRoot() { if err != nil || tr == nil { return } - // Track the amount of time wasted on hashing the storage trie - defer func(start time.Time) { - s.db.MetricsMux.Lock() - s.db.StorageHashes += time.Since(start) - s.db.MetricsMux.Unlock() - }(time.Now()) s.data.Root = tr.Hash() } -// commit obtains a set of dirty storage trie nodes and updates the account data. -// The returned set can be nil if nothing to commit. This function assumes all -// storage mutations have already been flushed into trie by updateRoot. -func (s *stateObject) commit() (*trienode.NodeSet, error) { - // Short circuit if trie is not even loaded, don't bother with committing anything - if s.trie == nil { - s.origin = s.data.Copy() - return nil, nil +// commitStorage overwrites the clean storage with the storage changes and +// fulfills the storage diffs into the given accountUpdate struct. +func (s *stateObject) commitStorage(op *accountUpdate) { + var ( + buf = crypto.NewKeccakState() + encode = func(val common.Hash) []byte { + if val == (common.Hash{}) { + return nil + } + blob, _ := rlp.EncodeToBytes(common.TrimLeftZeroes(val[:])) + return blob + } + ) + for key, val := range s.pendingStorage { + // Skip the noop storage changes, it might be possible the value + // of tracked slot is same in originStorage and pendingStorage + // map, e.g. the storage slot is modified in tx_a and then reset + // back in tx_b. + if val == s.originStorage[key] { + continue + } + hash := crypto.HashData(buf, key[:]) + if op.storages == nil { + op.storages = make(map[common.Hash][]byte) + } + op.storages[hash] = encode(val) + if op.storagesOrigin == nil { + op.storagesOrigin = make(map[common.Hash][]byte) + } + op.storagesOrigin[hash] = encode(s.originStorage[key]) + + // Overwrite the clean value of storage slots + s.originStorage[key] = val } - // Track the amount of time wasted on committing the storage trie - defer func(start time.Time) { s.db.StorageCommits += time.Since(start) }(time.Now()) + s.pendingStorage = make(Storage) +} - // The trie is currently in an open state and could potentially contain - // cached mutations. Call commit to acquire a set of nodes that have been - // modified, the set can be nil if nothing to commit. - root, nodes, err := s.trie.Commit(false) - if err != nil { - return nil, err +// commit obtains the account changes (metadata, storage slots, code) caused by +// state execution along with the dirty storage trie nodes. +// +// Note, commit may run concurrently across all the state objects. Do not assume +// thread-safe access to the statedb. +func (s *stateObject) commit() (*accountUpdate, *trienode.NodeSet, error) { + // commit the account metadata changes + op := &accountUpdate{ + address: s.address, + data: types.SlimAccountRLP(s.data), + } + if s.origin != nil { + op.origin = types.SlimAccountRLP(*s.origin) + } + // commit the contract code if it's modified + if s.dirtyCode { + op.code = &contractCode{ + hash: common.BytesToHash(s.CodeHash()), + blob: s.code, + } + s.dirtyCode = false // reset the dirty flag } + // Commit storage changes and the associated storage trie + s.commitStorage(op) + if len(op.storages) == 0 { + // nothing changed, don't bother to commit the trie + s.origin = s.data.Copy() + return op, nil, nil + } + root, nodes := s.trie.Commit(false) s.data.Root = root - - // Update original account data after commit s.origin = s.data.Copy() - return nodes, nil + return op, nodes, nil } // AddBalance adds amount to s's balance. @@ -524,22 +568,23 @@ func (s *stateObject) setBalance(amount *uint256.Int) { func (s *stateObject) deepCopy(db *StateDB) *stateObject { obj := &stateObject{ - db: db, - address: s.address, - addrHash: s.addrHash, - origin: s.origin, - data: s.data, + db: db, + address: s.address, + addrHash: s.addrHash, + origin: s.origin, + data: s.data, + code: s.code, + originStorage: s.originStorage.Copy(), + pendingStorage: s.pendingStorage.Copy(), + dirtyStorage: s.dirtyStorage.Copy(), + uncommittedStorage: s.uncommittedStorage.Copy(), + dirtyCode: s.dirtyCode, + selfDestructed: s.selfDestructed, + newContract: s.newContract, } if s.trie != nil { obj.trie = db.db.CopyTrie(s.trie) } - obj.code = s.code - obj.dirtyStorage = s.dirtyStorage.Copy() - obj.originStorage = s.originStorage.Copy() - obj.pendingStorage = s.pendingStorage.Copy() - obj.selfDestructed = s.selfDestructed - obj.dirtyCode = s.dirtyCode - obj.deleted = s.deleted return obj } @@ -554,7 +599,7 @@ func (s *stateObject) Address() common.Address { // Code returns the contract code associated with this object, if any. func (s *stateObject) Code() []byte { - if s.code != nil { + if len(s.code) != 0 { return s.code } if bytes.Equal(s.CodeHash(), types.EmptyCodeHash.Bytes()) { @@ -572,7 +617,7 @@ func (s *stateObject) Code() []byte { // or zero if none. This method is an almost mirror of Code, but uses a cache // inside the database to avoid loading codes seen recently. func (s *stateObject) CodeSize() int { - if s.code != nil { + if len(s.code) != 0 { return len(s.code) } if bytes.Equal(s.CodeHash(), types.EmptyCodeHash.Bytes()) { diff --git a/core/state/state_test.go b/core/state/state_test.go index bd48ae3d5f..eda14e6d3e 100644 --- a/core/state/state_test.go +++ b/core/state/state_test.go @@ -200,108 +200,20 @@ func TestSnapshotEmpty(t *testing.T) { s.state.RevertToSnapshot(s.state.Snapshot()) } -func TestSnapshot2(t *testing.T) { +func TestCreateObjectRevert(t *testing.T) { state, _ := New(types.EmptyRootHash, NewDatabase(rawdb.NewMemoryDatabase()), nil) + addr := common.BytesToAddress([]byte("so0")) + snap := state.Snapshot() - stateobjaddr0 := common.BytesToAddress([]byte("so0")) - stateobjaddr1 := common.BytesToAddress([]byte("so1")) - var storageaddr common.Hash - - data0 := common.BytesToHash([]byte{17}) - data1 := common.BytesToHash([]byte{18}) - - state.SetState(stateobjaddr0, storageaddr, data0) - state.SetState(stateobjaddr1, storageaddr, data1) - - // db, trie are already non-empty values - so0 := state.getStateObject(stateobjaddr0) + state.CreateAccount(addr) + so0 := state.getStateObject(addr) so0.SetBalance(uint256.NewInt(42), tracing.BalanceChangeUnspecified) so0.SetNonce(43) so0.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e'}), []byte{'c', 'a', 'f', 'e'}) - so0.selfDestructed = false - so0.deleted = false state.setStateObject(so0) - state.Finalise(false) - state.AccountsIntermediateRoot() - root, _, _ := state.Commit(0, nil) - state, _ = New(root, state.db, state.snaps) - - // and one with deleted == true - so1 := state.getStateObject(stateobjaddr1) - so1.SetBalance(uint256.NewInt(52), tracing.BalanceChangeUnspecified) - so1.SetNonce(53) - so1.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e', '2'}), []byte{'c', 'a', 'f', 'e', '2'}) - so1.selfDestructed = true - so1.deleted = true - state.setStateObject(so1) - - so1 = state.getStateObject(stateobjaddr1) - if so1 != nil { - t.Fatalf("deleted object not nil when getting") - } - - snapshot := state.Snapshot() - state.RevertToSnapshot(snapshot) - - so0Restored := state.getStateObject(stateobjaddr0) - // Update lazily-loaded values before comparing. - so0Restored.GetState(storageaddr) - so0Restored.Code() - // non-deleted is equal (restored) - compareStateObjects(so0Restored, so0, t) - - // deleted should be nil, both before and after restore of state copy - so1Restored := state.getStateObject(stateobjaddr1) - if so1Restored != nil { - t.Fatalf("deleted object not nil after restoring snapshot: %+v", so1Restored) - } -} - -func compareStateObjects(so0, so1 *stateObject, t *testing.T) { - if so0.Address() != so1.Address() { - t.Fatalf("Address mismatch: have %v, want %v", so0.address, so1.address) - } - if so0.Balance().Cmp(so1.Balance()) != 0 { - t.Fatalf("Balance mismatch: have %v, want %v", so0.Balance(), so1.Balance()) - } - if so0.Nonce() != so1.Nonce() { - t.Fatalf("Nonce mismatch: have %v, want %v", so0.Nonce(), so1.Nonce()) - } - if so0.data.Root != so1.data.Root { - t.Errorf("Root mismatch: have %x, want %x", so0.data.Root[:], so1.data.Root[:]) - } - if !bytes.Equal(so0.CodeHash(), so1.CodeHash()) { - t.Fatalf("CodeHash mismatch: have %v, want %v", so0.CodeHash(), so1.CodeHash()) - } - if !bytes.Equal(so0.code, so1.code) { - t.Fatalf("Code mismatch: have %v, want %v", so0.code, so1.code) - } - - if len(so1.dirtyStorage) != len(so0.dirtyStorage) { - t.Errorf("Dirty storage size mismatch: have %d, want %d", len(so1.dirtyStorage), len(so0.dirtyStorage)) - } - for k, v := range so1.dirtyStorage { - if so0.dirtyStorage[k] != v { - t.Errorf("Dirty storage key %x mismatch: have %v, want %v", k, so0.dirtyStorage[k], v) - } - } - for k, v := range so0.dirtyStorage { - if so1.dirtyStorage[k] != v { - t.Errorf("Dirty storage key %x mismatch: have %v, want none.", k, v) - } - } - if len(so1.originStorage) != len(so0.originStorage) { - t.Errorf("Origin storage size mismatch: have %d, want %d", len(so1.originStorage), len(so0.originStorage)) - } - for k, v := range so1.originStorage { - if so0.originStorage[k] != v { - t.Errorf("Origin storage key %x mismatch: have %v, want %v", k, so0.originStorage[k], v) - } - } - for k, v := range so0.originStorage { - if so1.originStorage[k] != v { - t.Errorf("Origin storage key %x mismatch: have %v, want none.", k, v) - } + state.RevertToSnapshot(snap) + if state.Exist(addr) { + t.Error("Unexpected account after revert") } } diff --git a/core/state/statedb.go b/core/state/statedb.go index 2300c99ed0..254ae366e8 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -22,35 +22,59 @@ import ( "fmt" "maps" "math/big" - "runtime" "slices" "sort" "sync" + "sync/atomic" "time" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/gopool" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state/snapshot" + "github.com/ethereum/go-ethereum/core/stateless" "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie/trienode" "github.com/ethereum/go-ethereum/trie/triestate" + "github.com/ethereum/go-ethereum/trie/utils" "github.com/holiman/uint256" + "golang.org/x/sync/errgroup" ) const defaultNumOfSlots = 100 +// TriesInMemory represents the number of layers that are kept in RAM. +const TriesInMemory = 128 + type revision struct { id int journalIndex int } +type mutationType int + +const ( + update mutationType = iota + deletion +) + +type mutation struct { + typ mutationType + applied bool +} + +func (m *mutation) copy() *mutation { + return &mutation{typ: m.typ, applied: m.applied} +} + +func (m *mutation) isDelete() bool { + return m.typ == deletion +} + // StateDB structs within the ethereum protocol are used to store anything // within the merkle trie. StateDBs take care of caching and storing // nested states. It's the general query interface to retrieve: @@ -77,25 +101,25 @@ type StateDB struct { // It will be updated when the Commit is called. originalRoot common.Hash expectedRoot common.Hash // The state root in the block header - stateRoot common.Hash // The calculation result of IntermediateRoot fullProcessed bool - // These maps hold the state changes (including the corresponding - // original value) that occurred in this **block**. - AccountMux sync.Mutex // Mutex for accounts access - StorageMux sync.Mutex // Mutex for storages access - accounts map[common.Hash][]byte // The mutated accounts in 'slim RLP' encoding - storages map[common.Hash]map[common.Hash][]byte // The mutated slots in prefix-zero trimmed rlp format - accountsOrigin map[common.Address][]byte // The original value of mutated accounts in 'slim RLP' encoding - storagesOrigin map[common.Address]map[common.Hash][]byte // The original value of mutated slots in prefix-zero trimmed rlp format - - // This map holds 'live' objects, which will get modified while processing - // a state transition. - stateObjects map[common.Address]*stateObject - stateObjectsPending map[common.Address]struct{} // State objects finalized but not yet written to the trie - stateObjectsDirty map[common.Address]struct{} // State objects modified in the current execution - stateObjectsDestruct map[common.Address]*types.StateAccount // State objects destructed in the block along with its previous value + // This map holds 'live' objects, which will get modified while + // processing a state transition. + stateObjects map[common.Address]*stateObject + + // This map holds 'deleted' objects. An object with the same address + // might also occur in the 'stateObjects' map due to account + // resurrection. The account value is tracked as the original value + // before the transition. This map is populated at the transaction + // boundaries. + stateObjectsDestruct map[common.Address]*stateObject + + // This map tracks the account mutations that occurred during the + // transition. Uncommitted mutations belonging to the same account + // can be merged into a single one which is equivalent from database's + // perspective. This map is populated at the transaction boundaries. + mutations map[common.Address]*mutation storagePool *StoragePool // sharedPool to store L1 originStorage of stateObjects writeOnSharedStorage bool // Write to the shared origin storage of a stateObject while reading from the underlying storage layer. @@ -132,6 +156,9 @@ type StateDB struct { validRevisions []revision nextRevisionId int + // State witness if cross validation is needed + witness *stateless.Witness + // Measurements gathered during execution for debugging purposes // MetricsMux should be used in more places, but will affect on performance, so following meteration is not accruate MetricsMux sync.Mutex @@ -140,7 +167,6 @@ type StateDB struct { AccountUpdates time.Duration AccountCommits time.Duration StorageReads time.Duration - StorageHashes time.Duration StorageUpdates time.Duration StorageCommits time.Duration SnapshotAccountReads time.Duration @@ -149,12 +175,9 @@ type StateDB struct { TrieDBCommits time.Duration AccountUpdated int - StorageUpdated int + StorageUpdated atomic.Int64 AccountDeleted int - StorageDeleted int - - // Testing hooks - onCommit func(states *triestate.Set) // Hook invoked when commit is performed + StorageDeleted atomic.Int64 } // NewWithSharedPool creates a new state with sharedStorge on layer 1.5 @@ -173,14 +196,9 @@ func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error) db: db, originalRoot: root, snaps: snaps, - accounts: make(map[common.Hash][]byte), - storages: make(map[common.Hash]map[common.Hash][]byte), - accountsOrigin: make(map[common.Address][]byte), - storagesOrigin: make(map[common.Address]map[common.Hash][]byte), stateObjects: make(map[common.Address]*stateObject, defaultNumOfSlots), - stateObjectsPending: make(map[common.Address]struct{}, defaultNumOfSlots), - stateObjectsDirty: make(map[common.Address]struct{}, defaultNumOfSlots), - stateObjectsDestruct: make(map[common.Address]*types.StateAccount, defaultNumOfSlots), + stateObjectsDestruct: make(map[common.Address]*stateObject, defaultNumOfSlots), + mutations: make(map[common.Address]*mutation, defaultNumOfSlots), logs: make(map[common.Hash][]*types.Log), preimages: make(map[common.Hash][]byte), journal: newJournal(), @@ -233,7 +251,7 @@ func (s *StateDB) SetLogger(l *tracing.Hooks) { // StartPrefetcher initializes a new trie prefetcher to pull in nodes from the // state trie concurrently while the state is mutated so that when we reach the // commit phase, most of the needed data is already hot. -func (s *StateDB) StartPrefetcher(namespace string) { +func (s *StateDB) StartPrefetcher(namespace string, witness *stateless.Witness) { if s.noTrie { return } @@ -243,12 +261,16 @@ func (s *StateDB) StartPrefetcher(namespace string) { s.prefetcher.close() s.prefetcher = nil } + // Enable witness collection if requested + s.witness = witness + + // If snapshots are enabled, start prefethers explicitly if s.snap != nil { parent := s.snap.Parent() if parent != nil { - s.prefetcher = newTriePrefetcher(s.db, s.originalRoot, parent.Root(), namespace) + s.prefetcher = newTriePrefetcher(s.db, s.originalRoot, parent.Root(), namespace, witness == nil) } else { - s.prefetcher = newTriePrefetcher(s.db, s.originalRoot, common.Hash{}, namespace) + s.prefetcher = newTriePrefetcher(s.db, s.originalRoot, common.Hash{}, namespace, witness == nil) } } } @@ -292,7 +314,7 @@ func (s *StateDB) TriePrefetchInAdvance(block *types.Block, signer types.Signer) } if len(addressesToPrefetch) > 0 { - prefetcher.prefetch(common.Hash{}, s.originalRoot, common.Address{}, addressesToPrefetch) + prefetcher.prefetch(common.Hash{}, s.originalRoot, common.Address{}, addressesToPrefetch, true) } } @@ -425,7 +447,7 @@ func (s *StateDB) GetStorageRoot(addr common.Address) common.Hash { return common.Hash{} } -// TxIndex returns the current transaction index set by Prepare. +// TxIndex returns the current transaction index set by SetTxContext. func (s *StateDB) TxIndex() int { return s.txIndex } @@ -462,7 +484,7 @@ func (s *StateDB) GetCodeHash(addr common.Address) common.Hash { return common.Hash{} } -// GetState retrieves a value from the given account's storage trie. +// GetState retrieves the value associated with the specific key. func (s *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash { stateObject := s.getStateObject(addr) if stateObject != nil { @@ -471,7 +493,8 @@ func (s *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash { return common.Hash{} } -// GetCommittedState retrieves a value from the given account's committed storage trie. +// GetCommittedState retrieves the value associated with the specific key +// without any mutations caused in the current execution. func (s *StateDB) GetCommittedState(addr common.Address, hash common.Hash) common.Hash { stateObject := s.getStateObject(addr) if stateObject != nil { @@ -593,8 +616,7 @@ func (s *StateDB) Selfdestruct6780(addr common.Address) { if stateObject == nil { return } - - if stateObject.created { + if stateObject.newContract { s.SelfDestruct(addr) } } @@ -635,8 +657,6 @@ func (s *StateDB) updateStateObject(obj *stateObject) { if s.noTrie { return } - // Track the amount of time wasted on updating the account from the trie - defer func(start time.Time) { s.AccountUpdates += time.Since(start) }(time.Now()) // Encode the account and update the account trie addr := obj.Address() @@ -646,17 +666,6 @@ func (s *StateDB) updateStateObject(obj *stateObject) { if obj.dirtyCode { s.trie.UpdateContractCode(obj.Address(), common.BytesToHash(obj.CodeHash()), obj.code) } - - // Track the original value of mutated account, nil means it was not present. - // Skip if it has been tracked (because updateStateObject may be called - // multiple times in a block). - if _, ok := s.accountsOrigin[obj.address]; !ok { - if obj.origin == nil { - s.accountsOrigin[obj.address] = nil - } else { - s.accountsOrigin[obj.address] = types.SlimAccountRLP(*obj.origin) - } - } } // deleteStateObject removes the given object from the state trie. @@ -664,8 +673,6 @@ func (s *StateDB) deleteStateObject(addr common.Address) { if s.noTrie { return } - // Track the amount of time wasted on deleting the account from the trie - defer func(start time.Time) { s.AccountUpdates += time.Since(start) }(time.Now()) // Delete the account from the trie if err := s.trie.DeleteAccount(addr); err != nil { @@ -674,31 +681,22 @@ func (s *StateDB) deleteStateObject(addr common.Address) { } // getStateObject retrieves a state object given by the address, returning nil if -// the object is not found or was deleted in this execution context. If you need -// to differentiate between non-existent/just-deleted, use getDeletedStateObject. +// the object is not found or was deleted in this execution context. func (s *StateDB) getStateObject(addr common.Address) *stateObject { - if obj := s.getDeletedStateObject(addr); obj != nil && !obj.deleted { - return obj - } - return nil -} - -// getDeletedStateObject is similar to getStateObject, but instead of returning -// nil for a deleted state object, it returns the actual object with the deleted -// flag set. This is needed by the state journal to revert to the correct s- -// destructed object instead of wiping all knowledge about the state object. -func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject { // Prefer live objects if any is available if obj := s.stateObjects[addr]; obj != nil { return obj } + // Short circuit if the account is already destructed in this block. + if _, ok := s.stateObjectsDestruct[addr]; ok { + return nil + } // If no live objects are available, attempt to use snapshots var data *types.StateAccount if s.snap != nil { start := time.Now() acc, err := s.snap.Account(crypto.HashData(s.hasher, addr.Bytes())) s.SnapshotAccountReads += time.Since(start) - if err == nil { if acc == nil { return nil @@ -741,6 +739,14 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject { return nil } } + // Independent of where we loaded the data from, add it to the prefetcher. + // Whilst this would be a bit weird if snapshots are disabled, but we still + // want the trie nodes to end up in the prefetcher too, so just push through. + if s.prefetcher != nil { + if err := s.prefetcher.prefetch(common.Hash{}, s.originalRoot, common.Address{}, [][]byte{addr[:]}, true); err != nil { + log.Error("Failed to prefetch account", "addr", addr, "err", err) + } + } // Insert into the live set obj := newObject(s, addr, data) s.setStateObject(obj) @@ -753,69 +759,40 @@ func (s *StateDB) setStateObject(object *stateObject) { // getOrNewStateObject retrieves a state object or create a new state object if nil. func (s *StateDB) getOrNewStateObject(addr common.Address) *stateObject { - stateObject := s.getStateObject(addr) - if stateObject == nil { - stateObject, _ = s.createObject(addr) + obj := s.getStateObject(addr) + if obj == nil { + obj = s.createObject(addr) } - return stateObject + return obj } -// createObject creates a new state object. If there is an existing account with -// the given address, it is overwritten and returned as the second return value. -func (s *StateDB) createObject(addr common.Address) (newobj, prev *stateObject) { - prev = s.getDeletedStateObject(addr) // Note, prev might have been deleted, we need that! - newobj = newObject(s, addr, nil) - if prev == nil { - s.journal.append(createObjectChange{account: &addr}) - } else { - // The original account should be marked as destructed and all cached - // account and storage data should be cleared as well. Note, it must - // be done here, otherwise the destruction event of "original account" - // will be lost. - _, prevdestruct := s.stateObjectsDestruct[prev.address] - if !prevdestruct { - s.stateObjectsDestruct[prev.address] = prev.origin - } - // There may be some cached account/storage data already since IntermediateRoot - // will be called for each transaction before byzantium fork which will always - // cache the latest account/storage data. - prevAccount, ok := s.accountsOrigin[prev.address] - s.journal.append(resetObjectChange{ - account: &addr, - prev: prev, - prevdestruct: prevdestruct, - prevAccount: s.accounts[prev.addrHash], - prevStorage: s.storages[prev.addrHash], - prevAccountOriginExist: ok, - prevAccountOrigin: prevAccount, - prevStorageOrigin: s.storagesOrigin[prev.address], - }) - delete(s.accounts, prev.addrHash) - delete(s.storages, prev.addrHash) - delete(s.accountsOrigin, prev.address) - delete(s.storagesOrigin, prev.address) - } - s.setStateObject(newobj) - if prev != nil && !prev.deleted { - return newobj, prev - } - return newobj, nil +// createObject creates a new state object. The assumption is held there is no +// existing account with the given address, otherwise it will be silently overwritten. +func (s *StateDB) createObject(addr common.Address) *stateObject { + obj := newObject(s, addr, nil) + s.journal.append(createObjectChange{account: &addr}) + s.setStateObject(obj) + return obj } -// CreateAccount explicitly creates a state object. If a state object with the address -// already exists the balance is carried over to the new account. -// -// CreateAccount is called during the EVM CREATE operation. The situation might arise that -// a contract does the following: -// -// 1. sends funds to sha(account ++ (nonce + 1)) -// 2. tx_create(sha(account ++ nonce)) (note that this gets the address of 1) -// -// Carrying over the balance ensures that Ether doesn't disappear. +// CreateAccount explicitly creates a new state object, assuming that the +// account did not previously exist in the state. If the account already +// exists, this function will silently overwrite it which might lead to a +// consensus bug eventually. func (s *StateDB) CreateAccount(addr common.Address) { - newObj, prev := s.createObject(addr) - if prev != nil { - newObj.setBalance(prev.data.Balance) + s.createObject(addr) +} + +// CreateContract is used whenever a contract is created. This may be preceded +// by CreateAccount, but that is not required if it already existed in the +// state due to funds sent beforehand. +// This operation sets the 'newContract'-flag, which is required in order to +// correctly handle EIP-6780 'delete-in-same-transaction' logic. +func (s *StateDB) CreateContract(addr common.Address) { + obj := s.getStateObject(addr) + if !obj.newContract { + obj.newContract = true + s.journal.append(createContractChange{account: addr}) } } @@ -838,26 +815,25 @@ func (s *StateDB) copyInternal(doPrefetch bool) *StateDB { db: s.db, trie: s.db.CopyTrie(s.trie), // noTrie:s.noTrie, + hasher: crypto.NewKeccakState(), // expectedRoot: s.expectedRoot, - // stateRoot: s.stateRoot, originalRoot: s.originalRoot, // fullProcessed: s.fullProcessed, - accounts: copySet(s.accounts), - storages: copy2DSet(s.storages), - accountsOrigin: copySet(s.accountsOrigin), - storagesOrigin: copy2DSet(s.storagesOrigin), stateObjects: make(map[common.Address]*stateObject, len(s.journal.dirties)), - stateObjectsPending: make(map[common.Address]struct{}, len(s.stateObjectsPending)), - stateObjectsDirty: make(map[common.Address]struct{}, len(s.journal.dirties)), - stateObjectsDestruct: maps.Clone(s.stateObjectsDestruct), + stateObjectsDestruct: make(map[common.Address]*stateObject, len(s.stateObjectsDestruct)), + mutations: make(map[common.Address]*mutation, len(s.mutations)), + dbErr: s.dbErr, storagePool: s.storagePool, // writeOnSharedStorage: s.writeOnSharedStorage, - refund: s.refund, - logs: make(map[common.Hash][]*types.Log, len(s.logs)), - logSize: s.logSize, - preimages: maps.Clone(s.preimages), - journal: newJournal(), - hasher: crypto.NewKeccakState(), + refund: s.refund, + thash: s.thash, + txIndex: s.txIndex, + logs: make(map[common.Hash][]*types.Log, len(s.logs)), + logSize: s.logSize, + preimages: maps.Clone(s.preimages), + journal: s.journal.copy(), + validRevisions: slices.Clone(s.validRevisions), + nextRevisionId: s.nextRevisionId, // In order for the block producer to be able to use and make additions // to the snapshot tree, we need to copy that as well. Otherwise, any @@ -866,39 +842,21 @@ func (s *StateDB) copyInternal(doPrefetch bool) *StateDB { snaps: s.snaps, snap: s.snap, } - // Copy the dirty states, logs, and preimages - for addr := range s.journal.dirties { - // As documented [here](https://github.com/ethereum/go-ethereum/pull/16485#issuecomment-380438527), - // and in the Finalise-method, there is a case where an object is in the journal but not - // in the stateObjects: OOG after touch on ripeMD prior to Byzantium. Thus, we need to check for - // nil - if object, exist := s.stateObjects[addr]; exist { - // Even though the original object is dirty, we are not copying the journal, - // so we need to make sure that any side-effect the journal would have caused - // during a commit (or similar op) is already applied to the copy. - state.stateObjects[addr] = object.deepCopy(state) - - state.stateObjectsDirty[addr] = struct{}{} // Mark the copy dirty to force internal (code/state) commits - state.stateObjectsPending[addr] = struct{}{} // Mark the copy pending to force external (account) commits - } + if s.witness != nil { + state.witness = s.witness.Copy() } - // Above, we don't copy the actual journal. This means that if the copy - // is copied, the loop above will be a no-op, since the copy's journal - // is empty. Thus, here we iterate over stateObjects, to enable copies - // of copies. - for addr := range s.stateObjectsPending { - if _, exist := state.stateObjects[addr]; !exist { - state.stateObjects[addr] = s.stateObjects[addr].deepCopy(state) - } - state.stateObjectsPending[addr] = struct{}{} + // Deep copy cached state objects. + for addr, obj := range s.stateObjects { + state.stateObjects[addr] = obj.deepCopy(state) } - for addr := range s.stateObjectsDirty { - if _, exist := state.stateObjects[addr]; !exist { - state.stateObjects[addr] = s.stateObjects[addr].deepCopy(state) - } - state.stateObjectsDirty[addr] = struct{}{} + // Deep copy destructed state objects. + for addr, obj := range s.stateObjectsDestruct { + state.stateObjectsDestruct[addr] = obj.deepCopy(state) + } + // Deep copy the object state markers. + for addr, op := range s.mutations { + state.mutations[addr] = op.copy() } - // Deep copy the logs occurred in the scope of block for hash, logs := range s.logs { cpy := make([]*types.Log, len(logs)) @@ -908,7 +866,6 @@ func (s *StateDB) copyInternal(doPrefetch bool) *StateDB { } state.logs[hash] = cpy } - // Do we need to copy the access list and transient storage? // In practice: No. At the start of a transaction, these two lists are empty. // In practice, we only ever copy state _between_ transactions/blocks, never @@ -919,14 +876,6 @@ func (s *StateDB) copyInternal(doPrefetch bool) *StateDB { state.accessList = s.accessList.Copy() } state.transientStorage = s.transientStorage.Copy() - - state.prefetcher = s.prefetcher - if s.prefetcher != nil && !doPrefetch { - // If there's a prefetcher running, make an inactive copy of it that can - // only access data but does not actively preload (since the user will not - // know that they need to explicitly terminate an active copy). - state.prefetcher = state.prefetcher.copy() - } return state } @@ -976,7 +925,8 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) { continue } if obj.selfDestructed || (deleteEmptyObjects && obj.empty()) { - obj.deleted = true + delete(s.stateObjects, obj.address) + s.markDelete(addr) // If ether was sent to account post-selfdestruct it is burnt. if bal := obj.Balance(); s.logger != nil && s.logger.OnBalanceChange != nil && obj.selfDestructed && bal.Sign() != 0 { @@ -986,30 +936,21 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) { // set indefinitely). Note only the first occurred self-destruct // event is tracked. if _, ok := s.stateObjectsDestruct[obj.address]; !ok { - s.stateObjectsDestruct[obj.address] = obj.origin + s.stateObjectsDestruct[obj.address] = obj } - // Note, we can't do this only at the end of a block because multiple - // transactions within the same block might self destruct and then - // resurrect an account; but the snapshotter needs both events. - delete(s.accounts, obj.addrHash) // Clear out any previously updated account data (may be recreated via a resurrect) - delete(s.storages, obj.addrHash) // Clear out any previously updated storage data (may be recreated via a resurrect) - delete(s.accountsOrigin, obj.address) // Clear out any previously updated account data (may be recreated via a resurrect) - delete(s.storagesOrigin, obj.address) // Clear out any previously updated storage data (may be recreated via a resurrect) } else { - obj.finalise(true) // Prefetch slots in the background + obj.finalise() + s.markUpdate(addr) } - obj.created = false - s.stateObjectsPending[addr] = struct{}{} - s.stateObjectsDirty[addr] = struct{}{} - // At this point, also ship the address off to the precacher. The precacher // will start loading tries, and when the change is eventually committed, // the commit-phase will be a lot faster addressesToPrefetch = append(addressesToPrefetch, common.CopyBytes(addr[:])) // Copy needed for closure } - prefetcher := s.prefetcher - if prefetcher != nil && len(addressesToPrefetch) > 0 { - prefetcher.prefetch(common.Hash{}, s.originalRoot, common.Address{}, addressesToPrefetch) + if s.prefetcher != nil && len(addressesToPrefetch) > 0 { + if err := s.prefetcher.prefetch(common.Hash{}, s.originalRoot, common.Address{}, addressesToPrefetch, false); err != nil { + log.Error("Failed to prefetch addresses", "addresses", len(addressesToPrefetch), "err", err) + } } // Invalidate journal because reverting across transactions is not allowed. s.clearJournalAndRefund() @@ -1021,70 +962,95 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) { func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { // Finalise all the dirty storage states and write them into the tries s.Finalise(deleteEmptyObjects) - s.AccountsIntermediateRoot() - return s.StateIntermediateRoot() -} - -func (s *StateDB) AccountsIntermediateRoot() { - tasks := make(chan func()) - finishCh := make(chan struct{}) - defer close(finishCh) - wg := sync.WaitGroup{} - for i := 0; i < runtime.NumCPU(); i++ { - go func() { - for { - select { - case task := <-tasks: - task() - case <-finishCh: - return - } - } - }() - } - - // Although naively it makes sense to retrieve the account trie and then do - // the contract storage and account updates sequentially, that short circuits - // the account prefetcher. Instead, let's process all the storage updates - // first, giving the account prefetches just a few more milliseconds of time - // to pull useful data from disk. - for addr := range s.stateObjectsPending { - if obj := s.stateObjects[addr]; !obj.deleted { - wg.Add(1) - tasks <- func() { - obj.updateRoot() - // Cache the data until commit. Note, this update mechanism is not symmetric - // to the deletion, because whereas it is enough to track account updates - // at commit time, deletions need tracking at transaction boundary level to - // ensure we capture state clearing. - s.AccountMux.Lock() - s.accounts[obj.addrHash] = types.SlimAccountRLP(obj.data) - s.AccountMux.Unlock() + // If there was a trie prefetcher operating, terminate it async so that the + // individual storage tries can be updated as soon as the disk load finishes. + if s.prefetcher != nil { + defer s.StopPrefetcher() + } + // Process all storage updates concurrently. The state object update root + // method will internally call a blocking trie fetch from the prefetcher, + // so there's no need to explicitly wait for the prefetchers to finish. + var ( + start = time.Now() + workers errgroup.Group + ) + if s.db.TrieDB().IsVerkle() { + // Whilst MPT storage tries are independent, Verkle has one single trie + // for all the accounts and all the storage slots merged together. The + // former can thus be simply parallelized, but updating the latter will + // need concurrency support within the trie itself. That's a TODO for a + // later time. + workers.SetLimit(1) + } + for addr, op := range s.mutations { + if op.applied || op.isDelete() { + continue + } + obj := s.stateObjects[addr] // closure for the task runner below + workers.Go(func() error { + if s.db.TrieDB().IsVerkle() { + obj.updateTrie() + } else { + obj.updateRoot() - wg.Done() + // If witness building is enabled and the state object has a trie, + // gather the witnesses for its specific storage trie + if s.witness != nil && obj.trie != nil { + s.witness.AddState(obj.trie.Witness()) + } + } + return nil + }) + } + // If witness building is enabled, gather all the read-only accesses. + // Skip witness collection in Verkle mode, they will be gathered + // together at the end. + if s.witness != nil && !s.db.TrieDB().IsVerkle() { + // Pull in anything that has been accessed before destruction + for _, obj := range s.stateObjectsDestruct { + // Skip any objects that haven't touched their storage + if len(obj.originStorage) == 0 { + continue + } + if trie := obj.getPrefetchedTrie(); trie != nil { + s.witness.AddState(trie.Witness()) + } else if obj.trie != nil { + s.witness.AddState(obj.trie.Witness()) + } + } + // Pull in only-read and non-destructed trie witnesses + for _, obj := range s.stateObjects { + // Skip any objects that have been updated + if _, ok := s.mutations[obj.address]; ok { + continue + } + // Skip any objects that haven't touched their storage + if len(obj.originStorage) == 0 { + continue + } + if trie := obj.getPrefetchedTrie(); trie != nil { + s.witness.AddState(trie.Witness()) + } else if obj.trie != nil { + s.witness.AddState(obj.trie.Witness()) } } } - wg.Wait() -} - -func (s *StateDB) StateIntermediateRoot() common.Hash { - // If there was a trie prefetcher operating, it gets aborted and irrevocably - // modified after we start retrieving tries. Remove it from the statedb after - // this round of use. - // - // This is weird pre-byzantium since the first tx runs with a prefetcher and - // the remainder without, but pre-byzantium even the initial prefetcher is - // useless, so no sleep lost. - prefetcher := s.prefetcher - defer s.StopPrefetcher() + workers.Wait() + s.StorageUpdates += time.Since(start) // Now we're about to start to write changes to the trie. The trie is so far // _untouched_. We can check with the prefetcher, if it can give us a trie // which has the same root, but also has some content loaded into it. - if prefetcher != nil { - if trie := prefetcher.trie(common.Hash{}, s.originalRoot); trie != nil { + // + // Don't check prefetcher if verkle trie has been used. In the context of verkle, + // only a single trie is used for state hashing. Replacing a non-nil verkle tree + // here could result in losing uncommitted changes from storage. + start = time.Now() + if s.prefetcher != nil { + if trie := s.prefetcher.trie(common.Hash{}, s.originalRoot); trie == nil { + log.Error("Failed to retrieve account pre-fetcher trie") + } else { s.trie = trie } } @@ -1095,8 +1061,6 @@ func (s *StateDB) StateIntermediateRoot() common.Hash { } s.trie = tr } - - usedAddrs := make([][]byte, 0, len(s.stateObjectsPending)) // Perform updates before deletions. This prevents resolution of unnecessary trie nodes // in circumstances similar to the following: // @@ -1107,35 +1071,49 @@ func (s *StateDB) StateIntermediateRoot() common.Hash { // If the self-destruct is handled first, then `P` would be left with only one child, thus collapsed // into a shortnode. This requires `B` to be resolved from disk. // Whereas if the created node is handled first, then the collapse is avoided, and `B` is not resolved. - var deletedAddrs []common.Address + var ( + usedAddrs [][]byte + deletedAddrs []common.Address + ) if !s.noTrie { - for addr := range s.stateObjectsPending { - if obj := s.stateObjects[addr]; !obj.deleted { - s.updateStateObject(obj) - s.AccountUpdated += 1 + for addr, op := range s.mutations { + if op.applied { + continue + } + op.applied = true + + if op.isDelete() { + deletedAddrs = append(deletedAddrs, addr) } else { - deletedAddrs = append(deletedAddrs, obj.address) + s.updateStateObject(s.stateObjects[addr]) + s.AccountUpdated += 1 } + usedAddrs = append(usedAddrs, common.CopyBytes(addr[:])) // Copy needed for closure } for _, deletedAddr := range deletedAddrs { s.deleteStateObject(deletedAddr) s.AccountDeleted += 1 } - if prefetcher != nil { - prefetcher.used(common.Hash{}, s.originalRoot, usedAddrs) - } - } + s.AccountUpdates += time.Since(start) - if len(s.stateObjectsPending) > 0 { - s.stateObjectsPending = make(map[common.Address]struct{}) + if s.prefetcher != nil { + s.prefetcher.used(common.Hash{}, s.originalRoot, usedAddrs) + } } // Track the amount of time wasted on hashing the account trie defer func(start time.Time) { s.AccountHashes += time.Since(start) }(time.Now()) + hash := s.trie.Hash() + + // If witness building is enabled, gather the account trie witness + if s.witness != nil { + s.witness.AddState(s.trie.Witness()) + } + if s.noTrie { return s.expectedRoot } else { - return s.trie.Hash() + return hash } } @@ -1145,7 +1123,7 @@ func (s *StateDB) StateIntermediateRoot() common.Hash { func (s *StateDB) SetTxContext(thash common.Hash, ti int) { s.thash = thash s.txIndex = ti - s.accessList = nil // can't delete this line now, because StateDB.Prepare is not called before processsing a system transaction + s.accessList = nil // can't delete this line now, because StateDB.Prepare is not called before processing a system transaction } func (s *StateDB) clearJournalAndRefund() { @@ -1237,10 +1215,9 @@ func (s *StateDB) slowDeleteStorage(addr common.Address, addrHash common.Hash, r } // deleteStorage is designed to delete the storage trie of a designated account. -// It could potentially be terminated if the storage size is excessively large, -// potentially leading to an out-of-memory panic. The function will make an attempt -// to utilize an efficient strategy if the associated state snapshot is reachable; -// otherwise, it will resort to a less-efficient approach. +// The function will make an attempt to utilize an efficient strategy if the +// associated state snapshot is reachable; otherwise, it will resort to a less +// efficient approach. func (s *StateDB) deleteStorage(addr common.Address, addrHash common.Hash, root common.Hash) (map[common.Hash][]byte, *trienode.NodeSet, error) { var ( start = time.Now() @@ -1275,75 +1252,63 @@ func (s *StateDB) deleteStorage(addr common.Address, addrHash common.Hash, root } // handleDestruction processes all destruction markers and deletes the account -// and associated storage slots if necessary. There are four possible situations -// here: -// -// - the account was not existent and be marked as destructed -// -// - the account was not existent and be marked as destructed, -// however, it's resurrected later in the same block. +// and associated storage slots if necessary. There are four potential scenarios +// as following: // -// - the account was existent and be marked as destructed -// -// - the account was existent and be marked as destructed, -// however it's resurrected later in the same block. +// (a) the account was not existent and be marked as destructed +// (b) the account was not existent and be marked as destructed, +// however, it's resurrected later in the same block. +// (c) the account was existent and be marked as destructed +// (d) the account was existent and be marked as destructed, +// however it's resurrected later in the same block. // // In case (a), nothing needs be deleted, nil to nil transition can be ignored. -// // In case (b), nothing needs be deleted, nil is used as the original value for // newly created account and storages -// // In case (c), **original** account along with its storages should be deleted, // with their values be tracked as original value. -// // In case (d), **original** account along with its storages should be deleted, // with their values be tracked as original value. -func (s *StateDB) handleDestruction(nodes *trienode.MergedNodeSet) error { - // Short circuit if geth is running with hash mode. This procedure can consume - // considerable time and storage deletion isn't supported in hash mode, thus - // preemptively avoiding unnecessary expenses. - if s.db.TrieDB().Scheme() == rawdb.HashScheme { - return nil - } - for addr, prev := range s.stateObjectsDestruct { - // The original account was non-existing, and it's marked as destructed - // in the scope of block. It can be case (a) or (b). - // - for (a), skip it without doing anything. - // - for (b), track account's original value as nil. It may overwrite - // the data cached in s.accountsOrigin set by 'updateStateObject'. - addrHash := crypto.Keccak256Hash(addr[:]) +func (s *StateDB) handleDestruction() (map[common.Hash]*accountDelete, []*trienode.NodeSet, error) { + var ( + nodes []*trienode.NodeSet + buf = crypto.NewKeccakState() + deletes = make(map[common.Hash]*accountDelete) + ) + for addr, prevObj := range s.stateObjectsDestruct { + prev := prevObj.origin + + // The account was non-existent, and it's marked as destructed in the scope + // of block. It can be either case (a) or (b) and will be interpreted as + // null->null state transition. + // - for (a), skip it without doing anything + // - for (b), the resurrected account with nil as original will be handled afterwards if prev == nil { - if _, ok := s.accounts[addrHash]; ok { - s.accountsOrigin[addr] = nil // case (b) - } continue } - // It can overwrite the data in s.accountsOrigin set by 'updateStateObject'. - s.accountsOrigin[addr] = types.SlimAccountRLP(*prev) // case (c) or (d) + // The account was existent, it can be either case (c) or (d). + addrHash := crypto.HashData(buf, addr.Bytes()) + op := &accountDelete{ + address: addr, + origin: types.SlimAccountRLP(*prev), + } + deletes[addrHash] = op - // Short circuit if the storage was empty. + // Short circuit if the origin storage was empty. if prev.Root == types.EmptyRootHash { continue } - // Remove storage slots belong to the account. + // Remove storage slots belonging to the account. slots, set, err := s.deleteStorage(addr, addrHash, prev.Root) if err != nil { - return fmt.Errorf("failed to delete storage, err: %w", err) - } - if s.storagesOrigin[addr] == nil { - s.storagesOrigin[addr] = slots - } else { - // It can overwrite the data in s.storagesOrigin[addrHash] set by - // 'object.updateTrie'. - for key, val := range slots { - s.storagesOrigin[addr][key] = val - } - } - if err := nodes.Merge(set); err != nil { - return err + return nil, nil, fmt.Errorf("failed to delete storage, err: %w", err) } + op.storagesOrigin = slots + + // Aggregate the associated trie node changes. + nodes = append(nodes, set) } - return nil + return deletes, nodes, nil } // GetTrie returns the account trie. @@ -1351,268 +1316,250 @@ func (s *StateDB) GetTrie() Trie { return s.trie } -// Commit writes the state to the underlying in-memory trie database. -// Once the state is committed, tries cached in stateDB (including account -// trie, storage tries) will no longer be functional. A new state instance -// must be created with new root and updated database for accessing post- -// commit states. -// -// The associated block number of the state transition is also provided -// for more chain context. -func (s *StateDB) Commit(block uint64, postCommitFunc func() error) (common.Hash, *types.DiffLayer, error) { +// commit gathers the state mutations accumulated along with the associated +// trie changes, resetting all internal flags with the new state as the base. +func (s *StateDB) commit(deleteEmptyObjects bool) (*stateUpdate, error) { // Short circuit in case any database failure occurred earlier. if s.dbErr != nil { s.StopPrefetcher() - return common.Hash{}, nil, fmt.Errorf("commit aborted due to earlier error: %v", s.dbErr) + return nil, fmt.Errorf("commit aborted due to earlier error: %v", s.dbErr) } // Finalize any pending changes and merge everything into the tries - var ( - diffLayer *types.DiffLayer - nodes = trienode.NewMergedNodeSet() - ) + s.IntermediateRoot(deleteEmptyObjects) - if s.snap != nil { - diffLayer = &types.DiffLayer{} + // Short circuit if any error occurs within the IntermediateRoot. + if s.dbErr != nil { + return nil, fmt.Errorf("commit aborted due to database error: %v", s.dbErr) } + // Commit objects to the trie, measuring the elapsed time + var ( + accountTrieNodesUpdated int + accountTrieNodesDeleted int + storageTrieNodesUpdated int + storageTrieNodesDeleted int + + lock sync.Mutex // protect two maps below + nodes = trienode.NewMergedNodeSet() // aggregated trie nodes + updates = make(map[common.Hash]*accountUpdate, len(s.mutations)) // aggregated account updates + + // merge aggregates the dirty trie nodes into the global set. + // + // Given that some accounts may be destroyed and then recreated within + // the same block, it's possible that a node set with the same owner + // may already exists. In such cases, these two sets are combined, with + // the later one overwriting the previous one if any nodes are modified + // or deleted in both sets. + // + // merge run concurrently across all the state objects and account trie. + merge = func(set *trienode.NodeSet) error { + if set == nil { + return nil + } + lock.Lock() + defer lock.Unlock() - commmitTrie := func() error { - commitErr := func() error { - if s.stateRoot = s.StateIntermediateRoot(); s.fullProcessed && s.expectedRoot != s.stateRoot { - log.Error("Invalid merkle root", "remote", s.expectedRoot, "local", s.stateRoot) - return fmt.Errorf("invalid merkle root (remote: %x local: %x)", s.expectedRoot, s.stateRoot) + updates, deletes := set.Size() + if set.Owner == (common.Hash{}) { + accountTrieNodesUpdated += updates + accountTrieNodesDeleted += deletes + } else { + storageTrieNodesUpdated += updates + storageTrieNodesDeleted += deletes + } + return nodes.Merge(set) + } + ) + // Given that some accounts could be destroyed and then recreated within + // the same block, account deletions must be processed first. This ensures + // that the storage trie nodes deleted during destruction and recreated + // during subsequent resurrection can be combined correctly. + deletes, delNodes, err := s.handleDestruction() + if err != nil { + return nil, err + } + for _, set := range delNodes { + if err := merge(set); err != nil { + return nil, err + } + } + // Handle all state updates afterwards, concurrently to one another to shave + // off some milliseconds from the commit operation. Also accumulate the code + // writes to run in parallel with the computations. + var ( + start = time.Now() + root common.Hash + workers errgroup.Group + ) + // Schedule the account trie first since that will be the biggest, so give + // it the most time to crunch. + // + // TODO(karalabe): This account trie commit is *very* heavy. 5-6ms at chain + // heads, which seems excessive given that it doesn't do hashing, it just + // shuffles some data. For comparison, the *hashing* at chain head is 2-3ms. + // We need to investigate what's happening as it seems something's wonky. + // Obviously it's not an end of the world issue, just something the original + // code didn't anticipate for. + workers.Go(func() error { + if !s.noTrie { + // Write the account trie changes, measuring the amount of wasted time + newroot, set := s.trie.Commit(true) + root = newroot + if s.fullProcessed && s.expectedRoot != root { + log.Error("Invalid merkle root", "remote", s.expectedRoot, "local", root) + return fmt.Errorf("invalid merkle root (remote: %x local: %x)", s.expectedRoot, root) } - // Handle all state deletions first - if err := s.handleDestruction(nodes); err != nil { + if err := merge(set); err != nil { return err } - - tasks := make(chan func()) - type taskResult struct { - err error - nodeSet *trienode.NodeSet + s.AccountCommits = time.Since(start) + } + return nil + }) + // Schedule each of the storage tries that need to be updated, so they can + // run concurrently to one another. + // + // TODO(karalabe): Experimentally, the account commit takes approximately the + // same time as all the storage commits combined, so we could maybe only have + // 2 threads in total. But that kind of depends on the account commit being + // more expensive than it should be, so let's fix that and revisit this todo. + for addr, op := range s.mutations { + if op.isDelete() { + continue + } + // Write any contract code associated with the state object + obj := s.stateObjects[addr] + if obj == nil { + return nil, errors.New("missing state object") + } + // Run the storage updates concurrently to one another + workers.Go(func() error { + // Write any storage changes in the state object to its storage trie + update, set, err := obj.commit() + if err != nil { + return err } - taskResults := make(chan taskResult, len(s.stateObjectsDirty)) - tasksNum := 0 - finishCh := make(chan struct{}) - - threads := gopool.Threads(len(s.stateObjectsDirty)) - wg := sync.WaitGroup{} - for i := 0; i < threads; i++ { - wg.Add(1) - go func() { - defer wg.Done() - for { - select { - case task := <-tasks: - task() - case <-finishCh: - return - } - } - }() + if err := merge(set); err != nil { + return err } + lock.Lock() + updates[obj.addrHash] = update + s.StorageCommits = time.Since(start) // overwrite with the longest storage commit runtime + lock.Unlock() + return nil + }) + } + // Wait for everything to finish and update the metrics + if err := workers.Wait(); err != nil { + return nil, err + } + accountUpdatedMeter.Mark(int64(s.AccountUpdated)) + storageUpdatedMeter.Mark(s.StorageUpdated.Load()) + accountDeletedMeter.Mark(int64(s.AccountDeleted)) + storageDeletedMeter.Mark(s.StorageDeleted.Load()) + accountTrieUpdatedMeter.Mark(int64(accountTrieNodesUpdated)) + accountTrieDeletedMeter.Mark(int64(accountTrieNodesDeleted)) + storageTriesUpdatedMeter.Mark(int64(storageTrieNodesUpdated)) + storageTriesDeletedMeter.Mark(int64(storageTrieNodesDeleted)) + s.AccountUpdated, s.AccountDeleted = 0, 0 + s.StorageUpdated.Store(0) + s.StorageDeleted.Store(0) + + // Clear all internal flags and update state root at the end. + s.mutations = make(map[common.Address]*mutation) + s.stateObjectsDestruct = make(map[common.Address]*stateObject) + + origin := s.originalRoot + if root == (common.Hash{}) { + root = types.EmptyRootHash + } + s.originalRoot = root + return newStateUpdate(origin, root, deletes, updates, nodes), nil +} - for addr := range s.stateObjectsDirty { - if obj := s.stateObjects[addr]; !obj.deleted { - tasks <- func() { - // Write any storage changes in the state object to its storage trie - if !s.noTrie { - if set, err := obj.commit(); err != nil { - taskResults <- taskResult{err, nil} - return - } else { - taskResults <- taskResult{nil, set} - } - } else { - taskResults <- taskResult{nil, nil} - } - } - tasksNum++ - } - } +// commitAndFlush is a wrapper of commit which also commits the state mutations +// to the configured data stores. +func (s *StateDB) commitAndFlush(block uint64, deleteEmptyObjects bool) (*stateUpdate, error) { + ret, err := s.commit(deleteEmptyObjects) + if err != nil { + return nil, err + } - for i := 0; i < tasksNum; i++ { - res := <-taskResults - if res.err != nil { - close(finishCh) - return res.err - } - // Merge the dirty nodes of storage trie into global set. It is possible - // that the account was destructed and then resurrected in the same block. - // In this case, the node set is shared by both accounts. - if res.nodeSet != nil { - if err := nodes.Merge(res.nodeSet); err != nil { - return err - } - } + if s.snap != nil { + ret.diffLayer = &types.DiffLayer{} + } + // Commit dirty contract code if any exists + if db := s.db.DiskDB(); db != nil && len(ret.codes) > 0 { + batch := db.NewBatch() + for _, code := range ret.codes { + rawdb.WriteCode(batch, code.hash, code.blob) + if s.snap != nil { + ret.diffLayer.Codes = append(ret.diffLayer.Codes, types.DiffCode{ + Hash: code.hash, + Code: code.blob, + }) } - close(finishCh) - - if !s.noTrie { - root, set, err := s.trie.Commit(true) - if err != nil { - return err - } - // Merge the dirty nodes of account trie into global set - if set != nil { - if err := nodes.Merge(set); err != nil { - return err - } - } - - origin := s.originalRoot - if origin == (common.Hash{}) { - origin = types.EmptyRootHash - } + } - if root != origin { - start := time.Now() - set := triestate.New(s.accountsOrigin, s.storagesOrigin) - if err := s.db.TrieDB().Update(root, origin, block, nodes, set); err != nil { - return err - } - s.originalRoot = root - s.TrieDBCommits += time.Since(start) - if s.onCommit != nil { - s.onCommit(set) - } - } - } + if err := batch.Write(); err != nil { + return nil, err + } + } + if !ret.empty() { + // If snapshotting is enabled, update the snapshot tree with this new version + if s.snap != nil { + s.snap = nil - if postCommitFunc != nil { - err := postCommitFunc() - if err != nil { - return err - } - } - wg.Wait() - return nil - }() - - return commitErr - } - - commitFuncs := []func() error{ - func() error { - codeWriter := s.db.DiskDB().NewBatch() - for addr := range s.stateObjectsDirty { - if obj := s.stateObjects[addr]; !obj.deleted { - // Write any contract code associated with the state object - if obj.code != nil && obj.dirtyCode { - rawdb.WriteCode(codeWriter, common.BytesToHash(obj.CodeHash()), obj.code) - obj.dirtyCode = false - if s.snap != nil { - diffLayer.Codes = append(diffLayer.Codes, types.DiffCode{ - Hash: common.BytesToHash(obj.CodeHash()), - Code: obj.code, - }) - } - if codeWriter.ValueSize() > ethdb.IdealBatchSize { - if err := codeWriter.Write(); err != nil { - return err - } - codeWriter.Reset() - } - } - } - } - if codeWriter.ValueSize() > 0 { - if err := codeWriter.Write(); err != nil { - log.Crit("Failed to commit dirty codes", "error", err) - return err - } + start := time.Now() + ret.diffLayer.Destructs, ret.diffLayer.Accounts, ret.diffLayer.Storages = ret.SnapToDiffLayer() + if err := s.snaps.Update(ret.root, ret.originRoot, ret.destructs, ret.accounts, ret.storages); err != nil { + log.Warn("Failed to update snapshot tree", "from", ret.originRoot, "to", ret.root, "err", err) } - return nil - }, - func() error { - // If snapshotting is enabled, update the snapshot tree with this new version - if s.snap != nil { - defer func(start time.Time) { s.SnapshotCommits += time.Since(start) }(time.Now()) - - diffLayer.Destructs, diffLayer.Accounts, diffLayer.Storages = s.SnapToDiffLayer() - // Only update if there's a state transition (skip empty Clique blocks) - if parent := s.snap.Root(); parent != s.expectedRoot { - err := s.snaps.Update(s.expectedRoot, parent, s.convertAccountSet(s.stateObjectsDestruct), s.accounts, s.storages) - - if err != nil { - log.Warn("Failed to update snapshot tree", "from", parent, "to", s.expectedRoot, "err", err) - } - - // Keep n diff layers in the memory - // - head layer is paired with HEAD state - // - head-1 layer is paired with HEAD-1 state - // - head-(n-1) layer(bottom-most diff layer) is paired with HEAD-(n-1)state - go func() { - if err := s.snaps.Cap(s.expectedRoot, s.snaps.CapLimit()); err != nil { - log.Warn("Failed to cap snapshot tree", "root", s.expectedRoot, "layers", s.snaps.CapLimit(), "err", err) - } - }() + // Keep 128 diff layers in the memory, persistent layer is 129th. + // - head layer is paired with HEAD state + // - head-1 layer is paired with HEAD-1 state + // - head-127 layer(bottom-most diff layer) is paired with HEAD-127 state + go func() { + if err := s.snaps.Cap(ret.root, TriesInMemory); err != nil { + log.Warn("Failed to cap snapshot tree", "root", ret.root, "layers", TriesInMemory, "err", err) } + }() + s.SnapshotCommits += time.Since(start) + } + // If trie database is enabled, commit the state update as a new layer + if db := s.db.TrieDB(); db != nil { + start := time.Now() + set := triestate.New(ret.accountsOrigin, ret.storagesOrigin) + if err := db.Update(ret.root, ret.originRoot, block, ret.nodes, set); err != nil { + return nil, err } - return nil - }, - } - - defer s.StopPrefetcher() - commitFuncs = append(commitFuncs, commmitTrie) - commitRes := make(chan error, len(commitFuncs)) - for _, f := range commitFuncs { - // commitFuncs[0] and commitFuncs[1] both read map `stateObjects`, but no conflicts - tmpFunc := f - go func() { - commitRes <- tmpFunc() - }() - } - for i := 0; i < len(commitFuncs); i++ { - r := <-commitRes - if r != nil { - return common.Hash{}, nil, r + s.TrieDBCommits += time.Since(start) } } + return ret, err +} - root := s.stateRoot - s.snap = nil - if root == (common.Hash{}) { - root = types.EmptyRootHash - } - // Clear all internal flags at the end of commit operation. - s.accounts = make(map[common.Hash][]byte) - s.storages = make(map[common.Hash]map[common.Hash][]byte) - s.accountsOrigin = make(map[common.Address][]byte) - s.storagesOrigin = make(map[common.Address]map[common.Hash][]byte) - s.stateObjectsDirty = make(map[common.Address]struct{}) - s.stateObjectsDestruct = make(map[common.Address]*types.StateAccount) - return root, diffLayer, nil -} - -func (s *StateDB) SnapToDiffLayer() ([]common.Address, []types.DiffAccount, []types.DiffStorage) { - destructs := make([]common.Address, 0, len(s.stateObjectsDestruct)) - for account := range s.stateObjectsDestruct { - destructs = append(destructs, account) - } - accounts := make([]types.DiffAccount, 0, len(s.accounts)) - for accountHash, account := range s.accounts { - accounts = append(accounts, types.DiffAccount{ - Account: accountHash, - Blob: account, - }) +// Commit writes the state mutations into the configured data stores. +// +// Once the state is committed, tries cached in stateDB (including account +// trie, storage tries) will no longer be functional. A new state instance +// must be created with new root and updated database for accessing post- +// commit states. +// +// The associated block number of the state transition is also provided +// for more chain context. +func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool, postCommitFuncs ...func() error) (common.Hash, *types.DiffLayer, error) { + ret, err := s.commitAndFlush(block, deleteEmptyObjects) + if err != nil { + return common.Hash{}, nil, err } - storages := make([]types.DiffStorage, 0, len(s.storages)) - for accountHash, storage := range s.storages { - keys := make([]common.Hash, 0, len(storage)) - values := make([][]byte, 0, len(storage)) - for k, v := range storage { - keys = append(keys, k) - values = append(values, v) + for _, postFunc := range postCommitFuncs { + err := postFunc() + if err != nil { + return common.Hash{}, nil, err } - storages = append(storages, types.DiffStorage{ - Account: accountHash, - Keys: keys, - Vals: values, - }) } - return destructs, accounts, storages + return ret.root, ret.diffLayer, nil } // Prepare handles the preparatory steps for executing a state transition with. @@ -1629,7 +1576,10 @@ func (s *StateDB) SnapToDiffLayer() ([]common.Address, []types.DiffAccount, []ty // - Add coinbase to access list (EIP-3651) // - Reset transient storage (EIP-1153) func (s *StateDB) Prepare(rules params.Rules, sender, coinbase common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList) { - if rules.IsBerlin { + if rules.IsEIP2929 && rules.IsEIP4762 { + panic("eip2929 and eip4762 are both activated") + } + if rules.IsEIP2929 { // Clear out any leftover from previous executions al := newAccessList() s.accessList = al @@ -1707,41 +1657,34 @@ func (s *StateDB) GetStorage(address common.Address) *sync.Map { return s.storagePool.getStorage(address) } -// convertAccountSet converts a provided account set from address keyed to hash keyed. -func (s *StateDB) convertAccountSet(set map[common.Address]*types.StateAccount) map[common.Hash]struct{} { - ret := make(map[common.Hash]struct{}, len(set)) - for addr := range set { - obj, exist := s.stateObjects[addr] - if !exist { - ret[crypto.Keccak256Hash(addr[:])] = struct{}{} - } else { - ret[obj.addrHash] = struct{}{} - } - } - return ret -} - func (s *StateDB) GetSnap() snapshot.Snapshot { return s.snap } -// copySet returns a deep-copied set. -func copySet[k comparable](set map[k][]byte) map[k][]byte { - copied := make(map[k][]byte, len(set)) - for key, val := range set { - copied[key] = common.CopyBytes(val) +// markDelete is invoked when an account is deleted but the deletion is +// not yet committed. The pending mutation is cached and will be applied +// all together +func (s *StateDB) markDelete(addr common.Address) { + if _, ok := s.mutations[addr]; !ok { + s.mutations[addr] = &mutation{} } - return copied + s.mutations[addr].applied = false + s.mutations[addr].typ = deletion } -// copy2DSet returns a two-dimensional deep-copied set. -func copy2DSet[k comparable](set map[k]map[common.Hash][]byte) map[k]map[common.Hash][]byte { - copied := make(map[k]map[common.Hash][]byte, len(set)) - for addr, subset := range set { - copied[addr] = make(map[common.Hash][]byte, len(subset)) - for key, val := range subset { - copied[addr][key] = common.CopyBytes(val) - } +func (s *StateDB) markUpdate(addr common.Address) { + if _, ok := s.mutations[addr]; !ok { + s.mutations[addr] = &mutation{} } - return copied + s.mutations[addr].applied = false + s.mutations[addr].typ = update +} + +func (s *StateDB) PointCache() *utils.PointCache { + return s.db.PointCache() +} + +// Witness retrieves the current state witness being collected. +func (s *StateDB) Witness() *stateless.Witness { + return s.witness } diff --git a/core/state/statedb_fuzz_test.go b/core/state/statedb_fuzz_test.go index 41254c2acb..f34c678205 100644 --- a/core/state/statedb_fuzz_test.go +++ b/core/state/statedb_fuzz_test.go @@ -36,7 +36,6 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" - "github.com/ethereum/go-ethereum/trie/triestate" "github.com/ethereum/go-ethereum/triedb" "github.com/ethereum/go-ethereum/triedb/pathdb" "github.com/holiman/uint256" @@ -96,7 +95,9 @@ func newStateTestAction(addr common.Address, r *rand.Rand, index int) testAction { name: "CreateAccount", fn: func(a testAction, s *StateDB) { - s.CreateAccount(addr) + if !s.Exist(addr) { + s.CreateAccount(addr) + } }, }, { @@ -178,9 +179,21 @@ func (test *stateTest) run() bool { roots []common.Hash accountList []map[common.Address][]byte storageList []map[common.Address]map[common.Hash][]byte - onCommit = func(states *triestate.Set) { - accountList = append(accountList, copySet(states.Accounts)) - storageList = append(storageList, copy2DSet(states.Storages)) + copyUpdate = func(update *stateUpdate) { + accounts := make(map[common.Address][]byte, len(update.accountsOrigin)) + for key, val := range update.accountsOrigin { + accounts[key] = common.CopyBytes(val) + } + accountList = append(accountList, accounts) + + storages := make(map[common.Address]map[common.Hash][]byte, len(update.storagesOrigin)) + for addr, subset := range update.storagesOrigin { + storages[addr] = make(map[common.Hash][]byte, len(subset)) + for key, val := range subset { + storages[addr][key] = common.CopyBytes(val) + } + } + storageList = append(storageList, storages) } disk = rawdb.NewMemoryDatabase() tdb = triedb.NewDatabase(disk, &triedb.Config{PathDB: pathdb.Defaults}) @@ -208,8 +221,6 @@ func (test *stateTest) run() bool { if err != nil { panic(err) } - state.onCommit = onCommit - for i, action := range actions { if i%test.chunk == 0 && i != 0 { if byzantium { @@ -226,14 +237,15 @@ func (test *stateTest) run() bool { } else { state.IntermediateRoot(true) // call intermediateRoot at the transaction boundary } - nroot, _, err := state.Commit(0, nil) // call commit at the block boundary + nroot, _, err := state.Commit(0, true) // call commit at the block boundary if err != nil { panic(err) } - if nroot == root { - return true // filter out non-change state transition + if ret.empty() { + return true } - roots = append(roots, nroot) + copyUpdate(ret) + roots = append(roots, ret.root) } for i := 0; i < len(test.actions); i++ { root := types.EmptyRootHash diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go index ca7c149d37..6c4c4a973b 100644 --- a/core/state/statedb_test.go +++ b/core/state/statedb_test.go @@ -20,9 +20,11 @@ import ( "bytes" "encoding/binary" "fmt" + "maps" "math" "math/rand" "reflect" + "slices" "strings" "sync" "testing" @@ -228,6 +230,78 @@ func TestCopy(t *testing.T) { } } +// TestCopyWithDirtyJournal tests if Copy can correct create a equal copied +// stateDB with dirty journal present. +func TestCopyWithDirtyJournal(t *testing.T) { + db := NewDatabase(rawdb.NewMemoryDatabase()) + orig, _ := New(types.EmptyRootHash, db, nil) + + // Fill up the initial states + for i := byte(0); i < 255; i++ { + obj := orig.getOrNewStateObject(common.BytesToAddress([]byte{i})) + obj.AddBalance(uint256.NewInt(uint64(i)), tracing.BalanceChangeUnspecified) + obj.data.Root = common.HexToHash("0xdeadbeef") + orig.updateStateObject(obj) + } + root, _ := orig.Commit(0, true) + orig, _ = New(root, db, nil) + + // modify all in memory without finalizing + for i := byte(0); i < 255; i++ { + obj := orig.getOrNewStateObject(common.BytesToAddress([]byte{i})) + obj.SubBalance(uint256.NewInt(uint64(i)), tracing.BalanceChangeUnspecified) + orig.updateStateObject(obj) + } + cpy := orig.Copy() + + orig.Finalise(true) + for i := byte(0); i < 255; i++ { + root := orig.GetStorageRoot(common.BytesToAddress([]byte{i})) + if root != (common.Hash{}) { + t.Errorf("Unexpected storage root %x", root) + } + } + cpy.Finalise(true) + for i := byte(0); i < 255; i++ { + root := cpy.GetStorageRoot(common.BytesToAddress([]byte{i})) + if root != (common.Hash{}) { + t.Errorf("Unexpected storage root %x", root) + } + } + if cpy.IntermediateRoot(true) != orig.IntermediateRoot(true) { + t.Error("State is not equal after copy") + } +} + +// TestCopyObjectState creates an original state, S1, and makes a copy S2. +// It then proceeds to make changes to S1. Those changes are _not_ supposed +// to affect S2. This test checks that the copy properly deep-copies the objectstate +func TestCopyObjectState(t *testing.T) { + db := NewDatabase(rawdb.NewMemoryDatabase()) + orig, _ := New(types.EmptyRootHash, db, nil) + + // Fill up the initial states + for i := byte(0); i < 5; i++ { + obj := orig.getOrNewStateObject(common.BytesToAddress([]byte{i})) + obj.AddBalance(uint256.NewInt(uint64(i)), tracing.BalanceChangeUnspecified) + obj.data.Root = common.HexToHash("0xdeadbeef") + orig.updateStateObject(obj) + } + orig.Finalise(true) + cpy := orig.Copy() + for _, op := range cpy.mutations { + if have, want := op.applied, false; have != want { + t.Fatalf("Error in test itself, the 'done' flag should not be set before Commit, have %v want %v", have, want) + } + } + orig.Commit(0, true) + for _, op := range cpy.mutations { + if have, want := op.applied, false; have != want { + t.Fatalf("Error: original state affected copy, have %v want %v", have, want) + } + } +} + func TestSnapshotRandom(t *testing.T) { config := &quick.Config{MaxCount: 1000} err := quick.Check((*snapshotTest).run, config) @@ -311,7 +385,30 @@ func newTestAction(addr common.Address, r *rand.Rand) testAction { { name: "CreateAccount", fn: func(a testAction, s *StateDB) { - s.CreateAccount(addr) + if !s.Exist(addr) { + s.CreateAccount(addr) + } + }, + }, + { + name: "CreateContract", + fn: func(a testAction, s *StateDB) { + if !s.Exist(addr) { + s.CreateAccount(addr) + } + contractHash := s.GetCodeHash(addr) + emptyCode := contractHash == (common.Hash{}) || contractHash == types.EmptyCodeHash + storageRoot := s.GetStorageRoot(addr) + emptyStorage := storageRoot == (common.Hash{}) || storageRoot == types.EmptyRootHash + if s.GetNonce(addr) == 0 && emptyCode && emptyStorage { + s.CreateContract(addr) + // We also set some code here, to prevent the + // CreateContract action from being performed twice in a row, + // which would cause a difference in state when unrolling + // the journal. (CreateContact assumes created was false prior to + // invocation, and the journal rollback sets it to false). + s.SetCode(addr, []byte{1}) + } }, }, { @@ -465,10 +562,14 @@ func forEachStorage(s *StateDB, addr common.Address, cb func(key, value common.H if err != nil { return err } - it := trie.NewIterator(trieIt) + var ( + it = trie.NewIterator(trieIt) + visited = make(map[common.Hash]bool) + ) for it.Next() { key := common.BytesToHash(s.trie.GetKey(it.Key)) + visited[key] = true if value, dirty := so.dirtyStorage[key]; dirty { if !cb(key, value) { return nil @@ -508,6 +609,10 @@ func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error { checkeq("GetCode", state.GetCode(addr), checkstate.GetCode(addr)) checkeq("GetCodeHash", state.GetCodeHash(addr), checkstate.GetCodeHash(addr)) checkeq("GetCodeSize", state.GetCodeSize(addr), checkstate.GetCodeSize(addr)) + // Check newContract-flag + if obj := state.getStateObject(addr); obj != nil { + checkeq("IsNewContract", obj.newContract, checkstate.getStateObject(addr).newContract) + } // Check storage. if obj := state.getStateObject(addr); obj != nil { forEachStorage(state, addr, func(key, value common.Hash) bool { @@ -516,12 +621,49 @@ func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error { forEachStorage(checkstate, addr, func(key, value common.Hash) bool { return checkeq("GetState("+key.Hex()+")", checkstate.GetState(addr, key), value) }) + other := checkstate.getStateObject(addr) + // Check dirty storage which is not in trie + if !maps.Equal(obj.dirtyStorage, other.dirtyStorage) { + print := func(dirty map[common.Hash]common.Hash) string { + var keys []common.Hash + out := new(strings.Builder) + for key := range dirty { + keys = append(keys, key) + } + slices.SortFunc(keys, common.Hash.Cmp) + for i, key := range keys { + fmt.Fprintf(out, " %d. %v %v\n", i, key, dirty[key]) + } + return out.String() + } + return fmt.Errorf("dirty storage err, have\n%v\nwant\n%v", + print(obj.dirtyStorage), + print(other.dirtyStorage)) + } + } + // Check transient storage. + { + have := state.transientStorage + want := checkstate.transientStorage + eq := maps.EqualFunc(have, want, + func(a Storage, b Storage) bool { + return maps.Equal(a, b) + }) + if !eq { + return fmt.Errorf("transient storage differs ,have\n%v\nwant\n%v", + have.PrettyPrint(), + want.PrettyPrint()) + } } if err != nil { return err } } - + if !checkstate.accessList.Equal(state.accessList) { // Check access lists + return fmt.Errorf("AccessLists are wrong, have \n%v\nwant\n%v", + checkstate.accessList.PrettyPrint(), + state.accessList.PrettyPrint()) + } if state.GetRefund() != checkstate.GetRefund() { return fmt.Errorf("got GetRefund() == %d, want GetRefund() == %d", state.GetRefund(), checkstate.GetRefund()) @@ -530,6 +672,23 @@ func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error { return fmt.Errorf("got GetLogs(common.Hash{}) == %v, want GetLogs(common.Hash{}) == %v", state.GetLogs(common.Hash{}, 0, common.Hash{}), checkstate.GetLogs(common.Hash{}, 0, common.Hash{})) } + if !maps.Equal(state.journal.dirties, checkstate.journal.dirties) { + getKeys := func(dirty map[common.Address]int) string { + var keys []common.Address + out := new(strings.Builder) + for key := range dirty { + keys = append(keys, key) + } + slices.SortFunc(keys, common.Address.Cmp) + for i, key := range keys { + fmt.Fprintf(out, " %d. %v\n", i, key) + } + return out.String() + } + have := getKeys(state.journal.dirties) + want := getKeys(checkstate.journal.dirties) + return fmt.Errorf("dirty-journal set mismatch.\nhave:\n%v\nwant:\n%v\n", have, want) + } return nil } @@ -716,18 +875,19 @@ func TestCopyCopyCommitCopy(t *testing.T) { } } -// TestCommitCopy tests the copy from a committed state is not functional. +// TestCommitCopy tests the copy from a committed state is not fully functional. func TestCommitCopy(t *testing.T) { - state, _ := New(types.EmptyRootHash, NewDatabase(rawdb.NewMemoryDatabase()), nil) + db := NewDatabase(rawdb.NewMemoryDatabase()) + state, _ := New(types.EmptyRootHash, db, nil) // Create an account and check if the retrieved balance is correct addr := common.HexToAddress("0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe") - skey := common.HexToHash("aaa") - sval := common.HexToHash("bbb") + skey1, skey2 := common.HexToHash("a1"), common.HexToHash("a2") + sval1, sval2 := common.HexToHash("b1"), common.HexToHash("b2") state.SetBalance(addr, uint256.NewInt(42), tracing.BalanceChangeUnspecified) // Change the account trie state.SetCode(addr, []byte("hello")) // Change an external metadata - state.SetState(addr, skey, sval) // Change the storage trie + state.SetState(addr, skey1, sval1) // Change the storage trie if balance := state.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 { t.Fatalf("initial balance mismatch: have %v, want %v", balance, 42) @@ -735,27 +895,38 @@ func TestCommitCopy(t *testing.T) { if code := state.GetCode(addr); !bytes.Equal(code, []byte("hello")) { t.Fatalf("initial code mismatch: have %x, want %x", code, []byte("hello")) } - if val := state.GetState(addr, skey); val != sval { - t.Fatalf("initial non-committed storage slot mismatch: have %x, want %x", val, sval) + if val := state.GetState(addr, skey1); val != sval1 { + t.Fatalf("initial non-committed storage slot mismatch: have %x, want %x", val, sval1) } - if val := state.GetCommittedState(addr, skey); val != (common.Hash{}) { + if val := state.GetCommittedState(addr, skey1); val != (common.Hash{}) { t.Fatalf("initial committed storage slot mismatch: have %x, want %x", val, common.Hash{}) } - // Copy the committed state database, the copied one is not functional. - state.Finalise(true) - state.AccountsIntermediateRoot() - state.Commit(0, nil) + root, _, _ := state.Commit(0, true) + + state, _ = New(root, db, nil) + state.SetState(addr, skey2, sval2) + state.Commit(1, true) + + // Copy the committed state database, the copied one is not fully functional. copied := state.Copy() - if balance := copied.GetBalance(addr); balance.Cmp(uint256.NewInt(0)) != 0 { + if balance := copied.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 { t.Fatalf("unexpected balance: have %v", balance) } - if code := copied.GetCode(addr); code != nil { + if code := copied.GetCode(addr); !bytes.Equal(code, []byte("hello")) { t.Fatalf("unexpected code: have %x", code) } - if val := copied.GetState(addr, skey); val != (common.Hash{}) { + // Miss slots because of non-functional trie after commit + if val := copied.GetState(addr, skey1); val != (common.Hash{}) { + t.Fatalf("unexpected storage slot: have %x", sval1) + } + if val := copied.GetCommittedState(addr, skey1); val != (common.Hash{}) { t.Fatalf("unexpected storage slot: have %x", val) } - if val := copied.GetCommittedState(addr, skey); val != (common.Hash{}) { + // Slots cached in the stateDB, available after commit + if val := copied.GetState(addr, skey2); val != sval2 { + t.Fatalf("unexpected storage slot: have %x", sval1) + } + if val := copied.GetCommittedState(addr, skey2); val != sval2 { t.Fatalf("unexpected storage slot: have %x", val) } } @@ -1118,42 +1289,6 @@ func TestStateDBTransientStorage(t *testing.T) { } } -func TestResetObject(t *testing.T) { - var ( - disk = rawdb.NewMemoryDatabase() - tdb = triedb.NewDatabase(disk, nil) - db = NewDatabaseWithNodeDB(disk, tdb) - snaps, _ = snapshot.New(snapshot.Config{CacheSize: 10}, disk, tdb, types.EmptyRootHash, 128, false) - state, _ = New(types.EmptyRootHash, db, snaps) - addr = common.HexToAddress("0x1") - slotA = common.HexToHash("0x1") - slotB = common.HexToHash("0x2") - ) - // Initialize account with balance and storage in first transaction. - state.SetBalance(addr, uint256.NewInt(1), tracing.BalanceChangeUnspecified) - state.SetState(addr, slotA, common.BytesToHash([]byte{0x1})) - state.IntermediateRoot(true) - - // Reset account and mutate balance and storages - state.CreateAccount(addr) - state.SetBalance(addr, uint256.NewInt(2), tracing.BalanceChangeUnspecified) - state.SetState(addr, slotB, common.BytesToHash([]byte{0x2})) - root := state.IntermediateRoot(true) - state.SetExpectedStateRoot(root) - state.Commit(0, nil) - - // Ensure the original account is wiped properly - snap := snaps.Snapshot(root) - slot, _ := snap.Storage(crypto.Keccak256Hash(addr.Bytes()), crypto.Keccak256Hash(slotA.Bytes())) - if len(slot) != 0 { - t.Fatalf("Unexpected storage slot") - } - slot, _ = snap.Storage(crypto.Keccak256Hash(addr.Bytes()), crypto.Keccak256Hash(slotB.Bytes())) - if !bytes.Equal(slot, []byte{0x2}) { - t.Fatalf("Unexpected storage slot value %v", slot) - } -} - func TestDeleteStorage(t *testing.T) { var ( disk = rawdb.NewMemoryDatabase() @@ -1207,3 +1342,47 @@ func TestDeleteStorage(t *testing.T) { t.Fatalf("difference found:\nfast: %v\nslow: %v\n", fastRes, slowRes) } } + +func TestStorageDirtiness(t *testing.T) { + var ( + disk = rawdb.NewMemoryDatabase() + tdb = triedb.NewDatabase(disk, nil) + db = NewDatabaseWithNodeDB(disk, tdb) + state, _ = New(types.EmptyRootHash, db, nil) + addr = common.HexToAddress("0x1") + checkDirty = func(key common.Hash, value common.Hash, dirty bool) { + obj := state.getStateObject(addr) + v, exist := obj.dirtyStorage[key] + if exist != dirty { + t.Fatalf("Unexpected dirty marker, want: %t, got: %t", dirty, exist) + } + if v != value { + t.Fatalf("Unexpected storage slot, want: %t, got: %t", value, v) + } + } + ) + state.CreateAccount(addr) + + // the storage change is noop, no dirty marker + state.SetState(addr, common.Hash{0x1}, common.Hash{}) + checkDirty(common.Hash{0x1}, common.Hash{}, false) + + // the storage change is valid, dirty marker is expected + snap := state.Snapshot() + state.SetState(addr, common.Hash{0x1}, common.Hash{0x1}) + checkDirty(common.Hash{0x1}, common.Hash{0x1}, true) + + // the storage change is reverted, dirtiness should be revoked + state.RevertToSnapshot(snap) + checkDirty(common.Hash{0x1}, common.Hash{}, false) + + // the storage is reset back to its original value, dirtiness should be revoked + state.SetState(addr, common.Hash{0x1}, common.Hash{0x1}) + snap = state.Snapshot() + state.SetState(addr, common.Hash{0x1}, common.Hash{}) + checkDirty(common.Hash{0x1}, common.Hash{}, false) + + // the storage change is reverted, dirty value should be set back + state.RevertToSnapshot(snap) + checkDirty(common.Hash{0x1}, common.Hash{0x1}, true) +} diff --git a/core/state/stateupdate.go b/core/state/stateupdate.go new file mode 100644 index 0000000000..0b73e823d9 --- /dev/null +++ b/core/state/stateupdate.go @@ -0,0 +1,169 @@ +// Copyright 2024 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package state + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/trie/trienode" +) + +// contractCode represents a contract code with associated metadata. +type contractCode struct { + hash common.Hash // hash is the cryptographic hash of the contract code. + blob []byte // blob is the binary representation of the contract code. +} + +// accountDelete represents an operation for deleting an Ethereum account. +type accountDelete struct { + address common.Address // address is the unique account identifier + origin []byte // origin is the original value of account data in slim-RLP encoding. + storagesOrigin map[common.Hash][]byte // storagesOrigin stores the original values of mutated slots in prefix-zero-trimmed RLP format. +} + +// accountUpdate represents an operation for updating an Ethereum account. +type accountUpdate struct { + address common.Address // address is the unique account identifier + data []byte // data is the slim-RLP encoded account data. + origin []byte // origin is the original value of account data in slim-RLP encoding. + code *contractCode // code represents mutated contract code; nil means it's not modified. + storages map[common.Hash][]byte // storages stores mutated slots in prefix-zero-trimmed RLP format. + storagesOrigin map[common.Hash][]byte // storagesOrigin stores the original values of mutated slots in prefix-zero-trimmed RLP format. +} + +// stateUpdate represents the difference between two states resulting from state +// execution. It contains information about mutated contract codes, accounts, +// and storage slots, along with their original values. +type stateUpdate struct { + originRoot common.Hash // hash of the state before applying mutation + root common.Hash // hash of the state after applying mutation + destructs map[common.Hash]struct{} // destructs contains the list of destructed accounts + accounts map[common.Hash][]byte // accounts stores mutated accounts in 'slim RLP' encoding + accountsOrigin map[common.Address][]byte // accountsOrigin stores the original values of mutated accounts in 'slim RLP' encoding + storages map[common.Hash]map[common.Hash][]byte // storages stores mutated slots in 'prefix-zero-trimmed' RLP format + storagesOrigin map[common.Address]map[common.Hash][]byte // storagesOrigin stores the original values of mutated slots in 'prefix-zero-trimmed' RLP format + codes map[common.Address]contractCode // codes contains the set of dirty codes + nodes *trienode.MergedNodeSet // Aggregated dirty nodes caused by state changes + + destructsAddrs map[common.Address]struct{} + diffLayer *types.DiffLayer // snapshot diffLayer generated by current block + +} + +// empty returns a flag indicating the state transition is empty or not. +func (sc *stateUpdate) empty() bool { + return sc.originRoot == sc.root +} + +// newStateUpdate constructs a state update object, representing the differences +// between two states by performing state execution. It aggregates the given +// account deletions and account updates to form a comprehensive state update. +func newStateUpdate(originRoot common.Hash, root common.Hash, deletes map[common.Hash]*accountDelete, updates map[common.Hash]*accountUpdate, nodes *trienode.MergedNodeSet) *stateUpdate { + var ( + destructs = make(map[common.Hash]struct{}) + accounts = make(map[common.Hash][]byte) + accountsOrigin = make(map[common.Address][]byte) + storages = make(map[common.Hash]map[common.Hash][]byte) + storagesOrigin = make(map[common.Address]map[common.Hash][]byte) + codes = make(map[common.Address]contractCode) + destructsAddrs = make(map[common.Address]struct{}) + ) + // Due to the fact that some accounts could be destructed and resurrected + // within the same block, the deletions must be aggregated first. + for addrHash, op := range deletes { + addr := op.address + destructs[addrHash] = struct{}{} + destructsAddrs[addr] = struct{}{} + accountsOrigin[addr] = op.origin + if len(op.storagesOrigin) > 0 { + storagesOrigin[addr] = op.storagesOrigin + } + } + // Aggregate account updates then. + for addrHash, op := range updates { + // Aggregate dirty contract codes if they are available. + addr := op.address + if op.code != nil { + codes[addr] = *op.code + } + // Aggregate the account changes. The original account value will only + // be tracked if it's not present yet. + accounts[addrHash] = op.data + if _, found := accountsOrigin[addr]; !found { + accountsOrigin[addr] = op.origin + } + // Aggregate the storage changes. The original storage slot value will + // only be tracked if it's not present yet. + if len(op.storages) > 0 { + storages[addrHash] = op.storages + } + if len(op.storagesOrigin) > 0 { + origin := storagesOrigin[addr] + if origin == nil { + storagesOrigin[addr] = op.storagesOrigin + continue + } + for key, slot := range op.storagesOrigin { + if _, found := origin[key]; !found { + origin[key] = slot + } + } + storagesOrigin[addr] = origin + } + } + return &stateUpdate{ + originRoot: types.TrieRootHash(originRoot), + root: types.TrieRootHash(root), + destructs: destructs, + accounts: accounts, + accountsOrigin: accountsOrigin, + storages: storages, + storagesOrigin: storagesOrigin, + codes: codes, + nodes: nodes, + destructsAddrs: destructsAddrs, + } +} + +func (sc *stateUpdate) SnapToDiffLayer() ([]common.Address, []types.DiffAccount, []types.DiffStorage) { + destructs := make([]common.Address, 0, len(sc.destructsAddrs)) + for account := range sc.destructsAddrs { + destructs = append(destructs, account) + } + accounts := make([]types.DiffAccount, 0, len(sc.accounts)) + for accountHash, account := range sc.accounts { + accounts = append(accounts, types.DiffAccount{ + Account: accountHash, + Blob: account, + }) + } + storages := make([]types.DiffStorage, 0, len(sc.storages)) + for accountHash, storage := range sc.storages { + keys := make([]common.Hash, 0, len(storage)) + values := make([][]byte, 0, len(storage)) + for k, v := range storage { + keys = append(keys, k) + values = append(values, v) + } + storages = append(storages, types.DiffStorage{ + Account: accountHash, + Keys: keys, + Vals: values, + }) + } + return destructs, accounts, storages +} diff --git a/core/state/transient_storage.go b/core/state/transient_storage.go index 66e563efa7..e63db39eba 100644 --- a/core/state/transient_storage.go +++ b/core/state/transient_storage.go @@ -17,6 +17,10 @@ package state import ( + "fmt" + "slices" + "strings" + "github.com/ethereum/go-ethereum/common" ) @@ -30,10 +34,19 @@ func newTransientStorage() transientStorage { // Set sets the transient-storage `value` for `key` at the given `addr`. func (t transientStorage) Set(addr common.Address, key, value common.Hash) { - if _, ok := t[addr]; !ok { - t[addr] = make(Storage) + if value == (common.Hash{}) { // this is a 'delete' + if _, ok := t[addr]; ok { + delete(t[addr], key) + if len(t[addr]) == 0 { + delete(t, addr) + } + } + } else { + if _, ok := t[addr]; !ok { + t[addr] = make(Storage) + } + t[addr][key] = value } - t[addr][key] = value } // Get gets the transient storage for `key` at the given `addr`. @@ -53,3 +66,27 @@ func (t transientStorage) Copy() transientStorage { } return storage } + +// PrettyPrint prints the contents of the access list in a human-readable form +func (t transientStorage) PrettyPrint() string { + out := new(strings.Builder) + var sortedAddrs []common.Address + for addr := range t { + sortedAddrs = append(sortedAddrs, addr) + slices.SortFunc(sortedAddrs, common.Address.Cmp) + } + + for _, addr := range sortedAddrs { + fmt.Fprintf(out, "%#x:", addr) + var sortedKeys []common.Hash + storage := t[addr] + for key := range storage { + sortedKeys = append(sortedKeys, key) + } + slices.SortFunc(sortedKeys, common.Hash.Cmp) + for _, key := range sortedKeys { + fmt.Fprintf(out, " %X : %X\n", key, storage[key]) + } + } + return out.String() +} diff --git a/core/state/trie_prefetcher.go b/core/state/trie_prefetcher.go index 1e68bb1d81..b636411937 100644 --- a/core/state/trie_prefetcher.go +++ b/core/state/trie_prefetcher.go @@ -56,6 +56,8 @@ type triePrefetcher struct { fetches map[string]Trie // Partially or fully fetched tries. Only populated for inactive copies fetchers map[string]*subfetcher // Subfetchers for each trie + noreads bool // Whether to ignore state-read-only prefetch requests + abortChan chan *subfetcher // to abort a single subfetcher and its children closed int32 closeMainChan chan struct{} // it is to inform the mainLoop @@ -80,7 +82,7 @@ type triePrefetcher struct { } // newTriePrefetcher -func newTriePrefetcher(db Database, root, rootParent common.Hash, namespace string) *triePrefetcher { +func newTriePrefetcher(db Database, root, rootParent common.Hash, namespace string, noreads bool) *triePrefetcher { prefix := triePrefetchMetricsPrefix + namespace p := &triePrefetcher{ db: db, @@ -89,6 +91,8 @@ func newTriePrefetcher(db Database, root, rootParent common.Hash, namespace stri fetchers: make(map[string]*subfetcher), // Active prefetchers use the fetchers map abortChan: make(chan *subfetcher, abortChanSize), + noreads: noreads, + closeMainChan: make(chan struct{}), closeMainDoneChan: make(chan struct{}), prefetchChan: make(chan *prefetchMsg, concurrentChanSize), @@ -263,15 +267,23 @@ func (p *triePrefetcher) copy() *triePrefetcher { } // prefetch schedules a batch of trie items to prefetch. -func (p *triePrefetcher) prefetch(owner common.Hash, root common.Hash, addr common.Address, keys [][]byte) { +func (p *triePrefetcher) prefetch(owner common.Hash, root common.Hash, addr common.Address, keys [][]byte, read bool) error { + // If the state item is only being read, but reads are disabled, return + if read && p.noreads { + return nil + } + // If the prefetcher is an inactive one, bail out if p.fetches != nil { - return + return nil } + select { case <-p.closeMainChan: // skip closed trie prefetcher + // TODO: ignore whether it's terminated? case p.prefetchChan <- &prefetchMsg{owner, root, addr, keys}: } + return nil } // trie returns the trie matching the root hash, or nil if the prefetcher doesn't diff --git a/core/state_prefetcher.go b/core/state_prefetcher.go index 5e616b29e5..6da6406c0e 100644 --- a/core/state_prefetcher.go +++ b/core/state_prefetcher.go @@ -17,7 +17,6 @@ package core import ( - "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" @@ -32,16 +31,14 @@ const checkInterval = 10 // data from disk before the main block processor start executing. type statePrefetcher struct { config *params.ChainConfig // Chain configuration options - bc *BlockChain // Canonical block chain - engine consensus.Engine // Consensus engine used for block rewards + chain *HeaderChain // Canonical block chain } // NewStatePrefetcher initialises a new statePrefetcher. -func NewStatePrefetcher(config *params.ChainConfig, bc *BlockChain, engine consensus.Engine) *statePrefetcher { +func NewStatePrefetcher(config *params.ChainConfig, chain *HeaderChain) *statePrefetcher { return &statePrefetcher{ config: config, - bc: bc, - engine: engine, + chain: chain, } } @@ -63,7 +60,7 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c newStatedb.EnableWriteOnSharedStorage() } gaspool := new(GasPool).AddGas(block.GasLimit()) - blockContext := NewEVMBlockContext(header, p.bc, nil) + blockContext := NewEVMBlockContext(header, p.chain, nil) evm := vm.NewEVM(blockContext, vm.TxContext{}, statedb, p.config, *cfg) // Iterate over and process the individual transactions for { @@ -112,7 +109,7 @@ func (p *statePrefetcher) PrefetchMining(txs TransactionsByPriceAndNonce, header newStatedb.EnableWriteOnSharedStorage() } gaspool := new(GasPool).AddGas(gasLimit) - blockContext := NewEVMBlockContext(header, p.bc, nil) + blockContext := NewEVMBlockContext(header, p.chain, nil) evm := vm.NewEVM(blockContext, vm.TxContext{}, statedb, p.config, cfg) // Iterate over and process the individual transactions for { diff --git a/core/state_processor.go b/core/state_processor.go index b2769f7905..d6a566d5eb 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -38,16 +38,14 @@ import ( // StateProcessor implements Processor. type StateProcessor struct { config *params.ChainConfig // Chain configuration options - bc *BlockChain // Canonical block chain - engine consensus.Engine // Consensus engine used for block rewards + chain *HeaderChain // Canonical header chain } // NewStateProcessor initialises a new StateProcessor. -func NewStateProcessor(config *params.ChainConfig, bc *BlockChain, engine consensus.Engine) *StateProcessor { +func NewStateProcessor(config *params.ChainConfig, chain *HeaderChain) *StateProcessor { return &StateProcessor{ config: config, - bc: bc, - engine: engine, + chain: chain, } } @@ -74,26 +72,27 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg misc.ApplyDAOHardFork(statedb) } - lastBlock := p.bc.GetBlockByHash(block.ParentHash()) + lastBlock := p.chain.GetHeaderByHash(block.ParentHash()) if lastBlock == nil { return statedb, nil, nil, 0, errors.New("could not get parent block") } if !p.config.IsFeynman(block.Number(), block.Time()) { // Handle upgrade build-in system contract code - systemcontracts.UpgradeBuildInSystemContract(p.config, blockNumber, lastBlock.Time(), block.Time(), statedb) + systemcontracts.UpgradeBuildInSystemContract(p.config, blockNumber, lastBlock.Time, block.Time(), statedb) } var ( - context = NewEVMBlockContext(header, p.bc, nil) - vmenv = vm.NewEVM(context, vm.TxContext{}, statedb, p.config, cfg) + context vm.BlockContext signer = types.MakeSigner(p.config, header.Number, header.Time) txNum = len(block.Transactions()) ) + context = NewEVMBlockContext(header, p.chain, nil) + vmenv := vm.NewEVM(context, vm.TxContext{}, statedb, p.config, cfg) if beaconRoot := block.BeaconRoot(); beaconRoot != nil { ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb) } // Iterate over and process the individual transactions - posa, isPoSA := p.engine.(consensus.PoSA) + posa, isPoSA := p.chain.engine.(consensus.PoSA) commonTxs := make([]*types.Transaction, 0, txNum) // initialise bloom processors @@ -144,7 +143,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg } // Finalize the block, applying any consensus engine specific extras (e.g. block rewards) - err := p.engine.Finalize(p.bc, header, statedb, &commonTxs, block.Uncles(), withdrawals, &receipts, &systemTxs, usedGas) + err := p.chain.engine.Finalize(p.chain, header, statedb, &commonTxs, block.Uncles(), withdrawals, &receipts, &systemTxs, usedGas) if err != nil { return statedb, receipts, allLogs, *usedGas, err } @@ -248,6 +247,12 @@ func ProcessBeaconBlockRoot(beaconRoot common.Hash, vmenv *vm.EVM, statedb *stat return } } + if vmenv.Config.Tracer != nil && vmenv.Config.Tracer.OnSystemCallStart != nil { + vmenv.Config.Tracer.OnSystemCallStart() + } + if vmenv.Config.Tracer != nil && vmenv.Config.Tracer.OnSystemCallEnd != nil { + defer vmenv.Config.Tracer.OnSystemCallEnd() + } // If EIP-4788 is enabled, we need to invoke the beaconroot storage contract with // the new root diff --git a/core/state_processor_test.go b/core/state_processor_test.go index 2ec52278c6..dbbf77153b 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -424,10 +424,11 @@ func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Tr } } // Assemble and return the final block for sealing + body := &types.Body{Transactions: txs} if config.IsShanghai(header.Number, header.Time) { - return types.NewBlockWithWithdrawals(header, txs, nil, receipts, []*types.Withdrawal{}, trie.NewStackTrie(nil)) + body.Withdrawals = []*types.Withdrawal{} } - return types.NewBlock(header, txs, nil, receipts, trie.NewStackTrie(nil)) + return types.NewBlock(header, body, receipts, trie.NewStackTrie(nil)) } var ( @@ -488,7 +489,7 @@ func TestProcessVerkle(t *testing.T) { txCost1 := params.TxGas txCost2 := params.TxGas contractCreationCost := intrinsicContractCreationGas + uint64(2039 /* execution costs */) - codeWithExtCodeCopyGas := intrinsicCodeWithExtCodeCopyGas + uint64(293644 /* execution costs */) + codeWithExtCodeCopyGas := intrinsicCodeWithExtCodeCopyGas + uint64(57444 /* execution costs */) blockGasUsagesExpected := []uint64{ txCost1*2 + txCost2, txCost1*2 + txCost2 + contractCreationCost + codeWithExtCodeCopyGas, diff --git a/core/state_transition.go b/core/state_transition.go index 0a545160e2..2f46f92dd2 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -70,7 +70,7 @@ func (result *ExecutionResult) Revert() []byte { } // IntrinsicGas computes the 'intrinsic gas' for a message with the given data. -func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation bool, isHomestead, isEIP2028, isEIP3860 bool) (uint64, error) { +func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation, isHomestead, isEIP2028, isEIP3860 bool) (uint64, error) { // Set the starting gas for the raw transaction var gas uint64 if isContractCreation && isHomestead { @@ -242,8 +242,9 @@ func (st *StateTransition) buyGas() error { if st.msg.GasFeeCap != nil { balanceCheck.SetUint64(st.msg.GasLimit) balanceCheck = balanceCheck.Mul(balanceCheck, st.msg.GasFeeCap) - balanceCheck.Add(balanceCheck, st.msg.Value) } + balanceCheck.Add(balanceCheck, st.msg.Value) + if st.evm.ChainConfig().IsCancun(st.evm.Context.BlockNumber, st.evm.Context.Time) { if blobGas := st.blobGasUsed(); blobGas > 0 { // Check that the user has enough funds to cover blobGasUsed * tx.BlobGasFeeCap @@ -416,6 +417,14 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { } st.gasRemaining -= gas + if rules.IsEIP4762 { + st.evm.AccessEvents.AddTxOrigin(msg.From) + + if targetAddr := msg.To; targetAddr != nil { + st.evm.AccessEvents.AddTxDestination(*targetAddr, msg.Value.Sign() != 0) + } + } + // Check clause 6 value, overflow := uint256.FromBig(msg.Value) if overflow { @@ -475,6 +484,11 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { } } else { st.state.AddBalance(st.evm.Context.Coinbase, fee, tracing.BalanceIncreaseRewardTransactionFee) + + // add the coinbase to the witness iff the fee is greater than 0 + if rules.IsEIP4762 && fee.Sign() != 0 { + st.evm.AccessEvents.BalanceGas(st.evm.Context.Coinbase, true) + } } return &ExecutionResult{ diff --git a/core/stateless.go b/core/stateless.go new file mode 100644 index 0000000000..b0d29e051f --- /dev/null +++ b/core/stateless.go @@ -0,0 +1,73 @@ +// Copyright 2024 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package core + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/lru" + "github.com/ethereum/go-ethereum/consensus/beacon" + "github.com/ethereum/go-ethereum/consensus/ethash" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/stateless" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/trie" + "github.com/ethereum/go-ethereum/triedb" +) + +// ExecuteStateless runs a stateless execution based on a witness, verifies +// everything it can locally and returns the two computed fields that need the +// other side to explicitly check. +// +// This method is a bit of a sore thumb here, but: +// - It cannot be placed in core/stateless, because state.New prodces a circular dep +// - It cannot be placed outside of core, because it needs to construct a dud headerchain +// +// TODO(karalabe): Would be nice to resolve both issues above somehow and move it. +func ExecuteStateless(config *params.ChainConfig, witness *stateless.Witness) (common.Hash, common.Hash, error) { + // Create and populate the state database to serve as the stateless backend + memdb := witness.MakeHashDB() + + db, err := state.New(witness.Root(), state.NewDatabaseWithConfig(memdb, triedb.HashDefaults), nil) + if err != nil { + return common.Hash{}, common.Hash{}, err + } + // Create a blockchain that is idle, but can be used to access headers through + chain := &HeaderChain{ + config: config, + chainDb: memdb, + headerCache: lru.NewCache[common.Hash, *types.Header](256), + engine: beacon.New(ethash.NewFaker()), + } + processor := NewStateProcessor(config, chain) + validator := NewBlockValidator(config, nil) // No chain, we only validate the state, not the block + + // Run the stateless blocks processing and self-validate certain fields + _, receipts, _, usedGas, err := processor.Process(witness.Block, db, vm.Config{}) + if err != nil { + return common.Hash{}, common.Hash{}, err + } + if err = validator.ValidateState(witness.Block, db, receipts, usedGas, true); err != nil { + return common.Hash{}, common.Hash{}, err + } + // Almost everything validated, but receipt and state root needs to be returned + receiptRoot := types.DeriveSha(receipts, trie.NewStackTrie(nil)) + stateRoot := db.IntermediateRoot(config.IsEIP158(witness.Block.Number())) + + return receiptRoot, stateRoot, nil +} diff --git a/core/stateless/database.go b/core/stateless/database.go new file mode 100644 index 0000000000..135da62193 --- /dev/null +++ b/core/stateless/database.go @@ -0,0 +1,60 @@ +// Copyright 2024 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package stateless + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" +) + +// MakeHashDB imports tries, codes and block hashes from a witness into a new +// hash-based memory db. We could eventually rewrite this into a pathdb, but +// simple is better for now. +func (w *Witness) MakeHashDB() ethdb.Database { + var ( + memdb = rawdb.NewMemoryDatabase() + hasher = crypto.NewKeccakState() + hash = make([]byte, 32) + ) + // Inject all the "block hashes" (i.e. headers) into the ephemeral database + for _, header := range w.Headers { + rawdb.WriteHeader(memdb, header) + } + // Inject all the bytecodes into the ephemeral database + for code := range w.Codes { + blob := []byte(code) + + hasher.Reset() + hasher.Write(blob) + hasher.Read(hash) + + rawdb.WriteCode(memdb, common.BytesToHash(hash), blob) + } + // Inject all the MPT trie nodes into the ephemeral database + for node := range w.State { + blob := []byte(node) + + hasher.Reset() + hasher.Write(blob) + hasher.Read(hash) + + rawdb.WriteLegacyTrieNode(memdb, common.BytesToHash(hash), blob) + } + return memdb +} diff --git a/core/stateless/encoding.go b/core/stateless/encoding.go new file mode 100644 index 0000000000..2b7245d377 --- /dev/null +++ b/core/stateless/encoding.go @@ -0,0 +1,129 @@ +// Copyright 2024 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package stateless + +import ( + "bytes" + "errors" + "fmt" + "io" + "slices" + + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rlp" +) + +//go:generate go run github.com/fjl/gencodec -type extWitness -field-override extWitnessMarshalling -out gen_encoding_json.go + +// toExtWitness converts our internal witness representation to the consensus one. +func (w *Witness) toExtWitness() *extWitness { + ext := &extWitness{ + Block: w.Block, + Headers: w.Headers, + } + ext.Codes = make([][]byte, 0, len(w.Codes)) + for code := range w.Codes { + ext.Codes = append(ext.Codes, []byte(code)) + } + slices.SortFunc(ext.Codes, bytes.Compare) + + ext.State = make([][]byte, 0, len(w.State)) + for node := range w.State { + ext.State = append(ext.State, []byte(node)) + } + slices.SortFunc(ext.State, bytes.Compare) + return ext +} + +// fromExtWitness converts the consensus witness format into our internal one. +func (w *Witness) fromExtWitness(ext *extWitness) error { + w.Block, w.Headers = ext.Block, ext.Headers + + w.Codes = make(map[string]struct{}, len(ext.Codes)) + for _, code := range ext.Codes { + w.Codes[string(code)] = struct{}{} + } + w.State = make(map[string]struct{}, len(ext.State)) + for _, node := range ext.State { + w.State[string(node)] = struct{}{} + } + return w.sanitize() +} + +// MarshalJSON marshals a witness as JSON. +func (w *Witness) MarshalJSON() ([]byte, error) { + return w.toExtWitness().MarshalJSON() +} + +// EncodeRLP serializes a witness as RLP. +func (w *Witness) EncodeRLP(wr io.Writer) error { + return rlp.Encode(wr, w.toExtWitness()) +} + +// UnmarshalJSON unmarshals from JSON. +func (w *Witness) UnmarshalJSON(input []byte) error { + var ext extWitness + if err := ext.UnmarshalJSON(input); err != nil { + return err + } + return w.fromExtWitness(&ext) +} + +// DecodeRLP decodes a witness from RLP. +func (w *Witness) DecodeRLP(s *rlp.Stream) error { + var ext extWitness + if err := s.Decode(&ext); err != nil { + return err + } + return w.fromExtWitness(&ext) +} + +// sanitize checks for some mandatory fields in the witness after decoding so +// the rest of the code can assume invariants and doesn't have to deal with +// corrupted data. +func (w *Witness) sanitize() error { + // Verify that the "parent" header (i.e. index 0) is available, and is the + // true parent of the block-to-be executed, since we use that to link the + // current block to the pre-state. + if len(w.Headers) == 0 { + return errors.New("parent header (for pre-root hash) missing") + } + for i, header := range w.Headers { + if header == nil { + return fmt.Errorf("witness header nil at position %d", i) + } + } + if w.Headers[0].Hash() != w.Block.ParentHash() { + return fmt.Errorf("parent hash different: witness %v, block parent %v", w.Headers[0].Hash(), w.Block.ParentHash()) + } + return nil +} + +// extWitness is a witness RLP encoding for transferring across clients. +type extWitness struct { + Block *types.Block `json:"block" gencodec:"required"` + Headers []*types.Header `json:"headers" gencodec:"required"` + Codes [][]byte `json:"codes"` + State [][]byte `json:"state"` +} + +// extWitnessMarshalling defines the hex marshalling types for a witness. +type extWitnessMarshalling struct { + Codes []hexutil.Bytes + State []hexutil.Bytes +} diff --git a/core/stateless/gen_encoding_json.go b/core/stateless/gen_encoding_json.go new file mode 100644 index 0000000000..1d0497976e --- /dev/null +++ b/core/stateless/gen_encoding_json.go @@ -0,0 +1,74 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package stateless + +import ( + "encoding/json" + "errors" + + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" +) + +var _ = (*extWitnessMarshalling)(nil) + +// MarshalJSON marshals as JSON. +func (e extWitness) MarshalJSON() ([]byte, error) { + type extWitness struct { + Block *types.Block `json:"block" gencodec:"required"` + Headers []*types.Header `json:"headers" gencodec:"required"` + Codes []hexutil.Bytes `json:"codes"` + State []hexutil.Bytes `json:"state"` + } + var enc extWitness + enc.Block = e.Block + enc.Headers = e.Headers + if e.Codes != nil { + enc.Codes = make([]hexutil.Bytes, len(e.Codes)) + for k, v := range e.Codes { + enc.Codes[k] = v + } + } + if e.State != nil { + enc.State = make([]hexutil.Bytes, len(e.State)) + for k, v := range e.State { + enc.State[k] = v + } + } + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (e *extWitness) UnmarshalJSON(input []byte) error { + type extWitness struct { + Block *types.Block `json:"block" gencodec:"required"` + Headers []*types.Header `json:"headers" gencodec:"required"` + Codes []hexutil.Bytes `json:"codes"` + State []hexutil.Bytes `json:"state"` + } + var dec extWitness + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.Block == nil { + return errors.New("missing required field 'block' for extWitness") + } + e.Block = dec.Block + if dec.Headers == nil { + return errors.New("missing required field 'headers' for extWitness") + } + e.Headers = dec.Headers + if dec.Codes != nil { + e.Codes = make([][]byte, len(dec.Codes)) + for k, v := range dec.Codes { + e.Codes[k] = v + } + } + if dec.State != nil { + e.State = make([][]byte, len(dec.State)) + for k, v := range dec.State { + e.State[k] = v + } + } + return nil +} diff --git a/core/stateless/witness.go b/core/stateless/witness.go new file mode 100644 index 0000000000..7622c5eb61 --- /dev/null +++ b/core/stateless/witness.go @@ -0,0 +1,159 @@ +// Copyright 2024 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package stateless + +import ( + "bytes" + "errors" + "fmt" + "maps" + "slices" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rlp" +) + +// HeaderReader is an interface to pull in headers in place of block hashes for +// the witness. +type HeaderReader interface { + // GetHeader retrieves a block header from the database by hash and number, + GetHeader(hash common.Hash, number uint64) *types.Header +} + +// Witness encompasses a block, state and any other chain data required to apply +// a set of transactions and derive a post state/receipt root. +type Witness struct { + Block *types.Block // Current block with rootHash and receiptHash zeroed out + Headers []*types.Header // Past headers in reverse order (0=parent, 1=parent's-parent, etc). First *must* be set. + Codes map[string]struct{} // Set of bytecodes ran or accessed + State map[string]struct{} // Set of MPT state trie nodes (account and storage together) + + chain HeaderReader // Chain reader to convert block hash ops to header proofs + lock sync.Mutex // Lock to allow concurrent state insertions +} + +// NewWitness creates an empty witness ready for population. +func NewWitness(chain HeaderReader, block *types.Block) (*Witness, error) { + // Zero out the result fields to avoid accidentally sending them to the verifier + header := block.Header() + header.Root = common.Hash{} + header.ReceiptHash = common.Hash{} + + // Retrieve the parent header, which will *always* be included to act as a + // trustless pre-root hash container + parent := chain.GetHeader(block.ParentHash(), block.NumberU64()-1) + if parent == nil { + return nil, errors.New("failed to retrieve parent header") + } + // Create the wtness with a reconstructed gutted out block + return &Witness{ + Block: types.NewBlockWithHeader(header).WithBody(*block.Body()), + Codes: make(map[string]struct{}), + State: make(map[string]struct{}), + Headers: []*types.Header{parent}, + chain: chain, + }, nil +} + +// AddBlockHash adds a "blockhash" to the witness with the designated offset from +// chain head. Under the hood, this method actually pulls in enough headers from +// the chain to cover the block being added. +func (w *Witness) AddBlockHash(number uint64) { + // Keep pulling in headers until this hash is populated + for int(w.Block.NumberU64()-number) > len(w.Headers) { + tail := w.Block.Header() + if len(w.Headers) > 0 { + tail = w.Headers[len(w.Headers)-1] + } + w.Headers = append(w.Headers, w.chain.GetHeader(tail.ParentHash, tail.Number.Uint64()-1)) + } +} + +// AddCode adds a bytecode blob to the witness. +func (w *Witness) AddCode(code []byte) { + if len(code) == 0 { + return + } + w.Codes[string(code)] = struct{}{} +} + +// AddState inserts a batch of MPT trie nodes into the witness. +func (w *Witness) AddState(nodes map[string]struct{}) { + if len(nodes) == 0 { + return + } + w.lock.Lock() + defer w.lock.Unlock() + + for node := range nodes { + w.State[node] = struct{}{} + } +} + +// Copy deep-copies the witness object. Witness.Block isn't deep-copied as it +// is never mutated by Witness +func (w *Witness) Copy() *Witness { + return &Witness{ + Block: w.Block, + Headers: slices.Clone(w.Headers), + Codes: maps.Clone(w.Codes), + State: maps.Clone(w.State), + } +} + +// String prints a human-readable summary containing the total size of the +// witness and the sizes of the underlying components +func (w *Witness) String() string { + blob, _ := rlp.EncodeToBytes(w) + bytesTotal := len(blob) + + blob, _ = rlp.EncodeToBytes(w.Block) + bytesBlock := len(blob) + + bytesHeaders := 0 + for _, header := range w.Headers { + blob, _ = rlp.EncodeToBytes(header) + bytesHeaders += len(blob) + } + bytesCodes := 0 + for code := range w.Codes { + bytesCodes += len(code) + } + bytesState := 0 + for node := range w.State { + bytesState += len(node) + } + buf := new(bytes.Buffer) + + fmt.Fprintf(buf, "Witness #%d: %v\n", w.Block.Number(), common.StorageSize(bytesTotal)) + fmt.Fprintf(buf, " block (%4d txs): %10v\n", len(w.Block.Transactions()), common.StorageSize(bytesBlock)) + fmt.Fprintf(buf, "%4d headers: %10v\n", len(w.Headers), common.StorageSize(bytesHeaders)) + fmt.Fprintf(buf, "%4d trie nodes: %10v\n", len(w.State), common.StorageSize(bytesState)) + fmt.Fprintf(buf, "%4d codes: %10v\n", len(w.Codes), common.StorageSize(bytesCodes)) + + return buf.String() +} + +// Root returns the pre-state root from the first header. +// +// Note, this method will panic in case of a bad witness (but RLP decoding will +// sanitize it and fail before that). +func (w *Witness) Root() common.Hash { + return w.Headers[0].Root +} diff --git a/core/tracing/CHANGELOG.md b/core/tracing/CHANGELOG.md index 77eda4ad76..93b91cf479 100644 --- a/core/tracing/CHANGELOG.md +++ b/core/tracing/CHANGELOG.md @@ -4,6 +4,15 @@ All notable changes to the tracing interface will be documented in this file. ## [Unreleased] +There have been minor backwards-compatible changes to the tracing interface to explicitly mark the execution of **system** contracts. As of now the only system call updates the parent beacon block root as per [EIP-4788](https://eips.ethereum.org/EIPS/eip-4788). Other system calls are being considered for the future hardfork. + +### New methods + +- `OnSystemCallStart()`: This hook is called when EVM starts processing a system call. Note system calls happen outside the scope of a transaction. This event will be followed by normal EVM execution events. +- `OnSystemCallEnd()`: This hook is called when EVM finishes processing a system call. + +## [v1.14.0] + There has been a major breaking change in the tracing interface for custom native tracers. JS and built-in tracers are not affected by this change and tracing API methods may be used as before. This overhaul has been done as part of the new live tracing feature ([#29189](https://github.com/ethereum/go-ethereum/pull/29189)). To learn more about live tracing please refer to the [docs](https://geth.ethereum.org/docs/developers/evm-tracing/live-tracing). **The `EVMLogger` interface which the tracers implemented has been removed.** It has been replaced by a new struct `tracing.Hooks`. `Hooks` keeps pointers to event listening functions. Internally the EVM will use these function pointers to emit events and can skip an event if the tracer has opted not to implement it. In fact this is the main reason for this change of approach. Another benefit is the ease of adding new hooks in future, and dynamically assigning event receivers. @@ -66,4 +75,5 @@ The hooks `CaptureStart` and `CaptureEnd` have been removed. These hooks signale - `CaptureState` -> `OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error)`. `op` is of type `byte` which can be cast to `vm.OpCode` when necessary. A `*vm.ScopeContext` is not passed anymore. It is replaced by `tracing.OpContext` which offers access to the memory, stack and current contract. - `CaptureFault` -> `OnFault(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, depth int, err error)`. Similar to above. -[unreleased]: https://github.com/ethereum/go-ethereum/compare/v1.13.14...master \ No newline at end of file +[unreleased]: https://github.com/ethereum/go-ethereum/compare/v1.14.0...master +[v1.14.0]: https://github.com/ethereum/go-ethereum/releases/tag/v1.14.0 \ No newline at end of file diff --git a/core/tracing/gen_balance_change_reason_stringer.go b/core/tracing/gen_balance_change_reason_stringer.go new file mode 100644 index 0000000000..d3a515a12d --- /dev/null +++ b/core/tracing/gen_balance_change_reason_stringer.go @@ -0,0 +1,37 @@ +// Code generated by "stringer -type=BalanceChangeReason -output gen_balance_change_reason_stringer.go"; DO NOT EDIT. + +package tracing + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[BalanceChangeUnspecified-0] + _ = x[BalanceIncreaseRewardMineUncle-1] + _ = x[BalanceIncreaseRewardMineBlock-2] + _ = x[BalanceIncreaseWithdrawal-3] + _ = x[BalanceIncreaseGenesisBalance-4] + _ = x[BalanceIncreaseRewardTransactionFee-5] + _ = x[BalanceDecreaseGasBuy-6] + _ = x[BalanceIncreaseGasReturn-7] + _ = x[BalanceIncreaseDaoContract-8] + _ = x[BalanceDecreaseDaoAccount-9] + _ = x[BalanceChangeTransfer-10] + _ = x[BalanceChangeTouchAccount-11] + _ = x[BalanceIncreaseSelfdestruct-12] + _ = x[BalanceDecreaseSelfdestruct-13] + _ = x[BalanceDecreaseSelfdestructBurn-14] +} + +const _BalanceChangeReason_name = "BalanceChangeUnspecifiedBalanceIncreaseRewardMineUncleBalanceIncreaseRewardMineBlockBalanceIncreaseWithdrawalBalanceIncreaseGenesisBalanceBalanceIncreaseRewardTransactionFeeBalanceDecreaseGasBuyBalanceIncreaseGasReturnBalanceIncreaseDaoContractBalanceDecreaseDaoAccountBalanceChangeTransferBalanceChangeTouchAccountBalanceIncreaseSelfdestructBalanceDecreaseSelfdestructBalanceDecreaseSelfdestructBurn" + +var _BalanceChangeReason_index = [...]uint16{0, 24, 54, 84, 109, 138, 173, 194, 218, 244, 269, 290, 315, 342, 369, 400} + +func (i BalanceChangeReason) String() string { + if i >= BalanceChangeReason(len(_BalanceChangeReason_index)-1) { + return "BalanceChangeReason(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _BalanceChangeReason_name[_BalanceChangeReason_index[i]:_BalanceChangeReason_index[i+1]] +} diff --git a/core/tracing/hooks.go b/core/tracing/hooks.go index 9ca6ee39fb..aa66dc49ff 100644 --- a/core/tracing/hooks.go +++ b/core/tracing/hooks.go @@ -81,6 +81,10 @@ type ( TxEndHook = func(receipt *types.Receipt, err error) // EnterHook is invoked when the processing of a message starts. + // + // Take note that EnterHook, when in the context of a live tracer, can be invoked + // outside of the `OnTxStart` and `OnTxEnd` hooks when dealing with system calls, + // see [OnSystemCallStartHook] and [OnSystemCallEndHook] for more information. EnterHook = func(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) // ExitHook is invoked when the processing of a message ends. @@ -89,6 +93,10 @@ type ( // ran out of gas when attempting to persist the code to database did not // count as a call failure and did not cause a revert of the call. This will // be indicated by `reverted == false` and `err == ErrCodeStoreOutOfGas`. + // + // Take note that ExitHook, when in the context of a live tracer, can be invoked + // outside of the `OnTxStart` and `OnTxEnd` hooks when dealing with system calls, + // see [OnSystemCallStartHook] and [OnSystemCallEndHook] for more information. ExitHook = func(depth int, output []byte, gasUsed uint64, err error, reverted bool) // OpcodeHook is invoked just prior to the execution of an opcode. @@ -125,6 +133,22 @@ type ( // GenesisBlockHook is called when the genesis block is being processed. GenesisBlockHook = func(genesis *types.Block, alloc types.GenesisAlloc) + // OnSystemCallStartHook is called when a system call is about to be executed. Today, + // this hook is invoked when the EIP-4788 system call is about to be executed to set the + // beacon block root. + // + // After this hook, the EVM call tracing will happened as usual so you will receive a `OnEnter/OnExit` + // as well as state hooks between this hook and the `OnSystemCallEndHook`. + // + // Note that system call happens outside normal transaction execution, so the `OnTxStart/OnTxEnd` hooks + // will not be invoked. + OnSystemCallStartHook = func() + + // OnSystemCallEndHook is called when a system call has finished executing. Today, + // this hook is invoked when the EIP-4788 system call is about to be executed to set the + // beacon block root. + OnSystemCallEndHook = func() + /* - State events - */ @@ -155,12 +179,14 @@ type Hooks struct { OnFault FaultHook OnGasChange GasChangeHook // Chain events - OnBlockchainInit BlockchainInitHook - OnClose CloseHook - OnBlockStart BlockStartHook - OnBlockEnd BlockEndHook - OnSkippedBlock SkippedBlockHook - OnGenesisBlock GenesisBlockHook + OnBlockchainInit BlockchainInitHook + OnClose CloseHook + OnBlockStart BlockStartHook + OnBlockEnd BlockEndHook + OnSkippedBlock SkippedBlockHook + OnGenesisBlock GenesisBlockHook + OnSystemCallStart OnSystemCallStartHook + OnSystemCallEnd OnSystemCallEndHook // State events OnBalanceChange BalanceChangeHook OnNonceChange NonceChangeHook @@ -173,6 +199,8 @@ type Hooks struct { // for tracing and reporting. type BalanceChangeReason byte +//go:generate go run golang.org/x/tools/cmd/stringer -type=BalanceChangeReason -output gen_balance_change_reason_stringer.go + const ( BalanceChangeUnspecified BalanceChangeReason = 0 @@ -272,6 +300,12 @@ const ( GasChangeCallStorageColdAccess GasChangeReason = 13 // GasChangeCallFailedExecution is the burning of the remaining gas when the execution failed without a revert. GasChangeCallFailedExecution GasChangeReason = 14 + // GasChangeWitnessContractInit is the amount charged for adding to the witness during the contract creation initialization step + GasChangeWitnessContractInit GasChangeReason = 15 + // GasChangeWitnessContractCreation is the amount charged for adding to the witness during the contract creation finalization step + GasChangeWitnessContractCreation GasChangeReason = 16 + // GasChangeWitnessCodeChunk is the amount charged for touching one or more contract code chunks + GasChangeWitnessCodeChunk GasChangeReason = 17 // GasChangeIgnored is a special value that can be used to indicate that the gas change should be ignored as // it will be "manually" tracked by a direct emit of the gas change event. diff --git a/core/txindexer_test.go b/core/txindexer_test.go index d078046355..220b0db298 100644 --- a/core/txindexer_test.go +++ b/core/txindexer_test.go @@ -18,7 +18,6 @@ package core import ( "math/big" - "os" "testing" "github.com/ethereum/go-ethereum/common" @@ -211,8 +210,7 @@ func TestTxIndexer(t *testing.T) { }, } for _, c := range cases { - frdir := t.TempDir() - db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), frdir, "", false, false, false, false, false) + db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false, false, false, false, false) rawdb.WriteAncientBlocks(db, append([]*types.Block{gspec.ToBlock()}, blocks...), append([]types.Receipts{{}}, receipts...), big.NewInt(0)) // Index the initial blocks from ancient store @@ -238,6 +236,5 @@ func TestTxIndexer(t *testing.T) { verify(db, 0, indexer) db.Close() - os.RemoveAll(frdir) } } diff --git a/core/txpool/blobpool/blobpool.go b/core/txpool/blobpool/blobpool.go index d1e31f2c0c..44b9ac9bfb 100644 --- a/core/txpool/blobpool/blobpool.go +++ b/core/txpool/blobpool/blobpool.go @@ -411,7 +411,7 @@ func (p *BlobPool) Init(gasTip uint64, head *types.Header, reserve txpool.Addres if p.head.ExcessBlobGas != nil { blobfee = uint256.MustFromBig(eip4844.CalcBlobFee(*p.head.ExcessBlobGas)) } - p.evict = newPriceHeap(basefee, blobfee, &p.index) + p.evict = newPriceHeap(basefee, blobfee, p.index) // Pool initialized, attach the blob limbo to it to track blobs included // recently but not yet finalized @@ -1619,8 +1619,8 @@ func (pool *BlobPool) SubscribeReannoTxsEvent(ch chan<- core.ReannoTxsEvent) eve // Nonce returns the next nonce of an account, with all transactions executable // by the pool already applied on top. func (p *BlobPool) Nonce(addr common.Address) uint64 { - p.lock.Lock() - defer p.lock.Unlock() + p.lock.RLock() + defer p.lock.RUnlock() if txs, ok := p.index[addr]; ok { return txs[len(txs)-1].nonce + 1 @@ -1631,8 +1631,8 @@ func (p *BlobPool) Nonce(addr common.Address) uint64 { // Stats retrieves the current pool stats, namely the number of pending and the // number of queued (non-executable) transactions. func (p *BlobPool) Stats() (int, int) { - p.lock.Lock() - defer p.lock.Unlock() + p.lock.RLock() + defer p.lock.RUnlock() var pending int for _, txs := range p.index { diff --git a/core/txpool/blobpool/blobpool_test.go b/core/txpool/blobpool/blobpool_test.go index 9738107715..f7737303b5 100644 --- a/core/txpool/blobpool/blobpool_test.go +++ b/core/txpool/blobpool/blobpool_test.go @@ -143,7 +143,7 @@ func (bc *testBlockChain) CurrentFinalBlock() *types.Header { } } -func (bt *testBlockChain) GetBlock(hash common.Hash, number uint64) *types.Block { +func (bc *testBlockChain) GetBlock(hash common.Hash, number uint64) *types.Block { return nil } diff --git a/core/txpool/blobpool/evictheap.go b/core/txpool/blobpool/evictheap.go index bc4543a352..5e285e6c53 100644 --- a/core/txpool/blobpool/evictheap.go +++ b/core/txpool/blobpool/evictheap.go @@ -17,13 +17,13 @@ package blobpool import ( - "bytes" "container/heap" "math" - "sort" + "slices" "github.com/ethereum/go-ethereum/common" "github.com/holiman/uint256" + "golang.org/x/exp/maps" ) // evictHeap is a helper data structure to keep track of the cheapest bottleneck @@ -35,7 +35,7 @@ import ( // The goal of the heap is to decide which account has the worst bottleneck to // evict transactions from. type evictHeap struct { - metas *map[common.Address][]*blobTxMeta // Pointer to the blob pool's index for price retrievals + metas map[common.Address][]*blobTxMeta // Pointer to the blob pool's index for price retrievals basefeeJumps float64 // Pre-calculated absolute dynamic fee jumps for the base fee blobfeeJumps float64 // Pre-calculated absolute dynamic fee jumps for the blob fee @@ -46,23 +46,18 @@ type evictHeap struct { // newPriceHeap creates a new heap of cheapest accounts in the blob pool to evict // from in case of over saturation. -func newPriceHeap(basefee *uint256.Int, blobfee *uint256.Int, index *map[common.Address][]*blobTxMeta) *evictHeap { +func newPriceHeap(basefee *uint256.Int, blobfee *uint256.Int, index map[common.Address][]*blobTxMeta) *evictHeap { heap := &evictHeap{ metas: index, - index: make(map[common.Address]int), + index: make(map[common.Address]int, len(index)), } // Populate the heap in account sort order. Not really needed in practice, // but it makes the heap initialization deterministic and less annoying to // test in unit tests. - addrs := make([]common.Address, 0, len(*index)) - for addr := range *index { - addrs = append(addrs, addr) - } - sort.Slice(addrs, func(i, j int) bool { return bytes.Compare(addrs[i][:], addrs[j][:]) < 0 }) - - for _, addr := range addrs { - heap.index[addr] = len(heap.addrs) - heap.addrs = append(heap.addrs, addr) + heap.addrs = maps.Keys(index) + slices.SortFunc(heap.addrs, common.Address.Cmp) + for i, addr := range heap.addrs { + heap.index[addr] = i } heap.reinit(basefee, blobfee, true) return heap @@ -94,8 +89,8 @@ func (h *evictHeap) Len() int { // Less implements sort.Interface as part of heap.Interface, returning which of // the two requested accounts has a cheaper bottleneck. func (h *evictHeap) Less(i, j int) bool { - txsI := (*(h.metas))[h.addrs[i]] - txsJ := (*(h.metas))[h.addrs[j]] + txsI := h.metas[h.addrs[i]] + txsJ := h.metas[h.addrs[j]] lastI := txsI[len(txsI)-1] lastJ := txsJ[len(txsJ)-1] diff --git a/core/txpool/blobpool/evictheap_test.go b/core/txpool/blobpool/evictheap_test.go index 01b136551c..1cf577cb00 100644 --- a/core/txpool/blobpool/evictheap_test.go +++ b/core/txpool/blobpool/evictheap_test.go @@ -37,17 +37,17 @@ func verifyHeapInternals(t *testing.T, evict *evictHeap) { seen := make(map[common.Address]struct{}) for i, addr := range evict.addrs { seen[addr] = struct{}{} - if _, ok := (*evict.metas)[addr]; !ok { + if _, ok := evict.metas[addr]; !ok { t.Errorf("heap contains unexpected address at slot %d: %v", i, addr) } } - for addr := range *evict.metas { + for addr := range evict.metas { if _, ok := seen[addr]; !ok { t.Errorf("heap is missing required address %v", addr) } } - if len(evict.addrs) != len(*evict.metas) { - t.Errorf("heap size %d mismatches metadata size %d", len(evict.addrs), len(*evict.metas)) + if len(evict.addrs) != len(evict.metas) { + t.Errorf("heap size %d mismatches metadata size %d", len(evict.addrs), len(evict.metas)) } // Ensure that all accounts are present in the heap order index and no extras have := make([]common.Address, len(evict.index)) @@ -159,7 +159,7 @@ func TestPriceHeapSorting(t *testing.T) { }} } // Create a price heap and check the pop order - priceheap := newPriceHeap(uint256.NewInt(tt.basefee), uint256.NewInt(tt.blobfee), &index) + priceheap := newPriceHeap(uint256.NewInt(tt.basefee), uint256.NewInt(tt.blobfee), index) verifyHeapInternals(t, priceheap) for j := 0; j < len(tt.order); j++ { @@ -218,7 +218,7 @@ func benchmarkPriceHeapReinit(b *testing.B, datacap uint64) { }} } // Create a price heap and reinit it over and over - heap := newPriceHeap(uint256.NewInt(rand.Uint64()), uint256.NewInt(rand.Uint64()), &index) + heap := newPriceHeap(uint256.NewInt(rand.Uint64()), uint256.NewInt(rand.Uint64()), index) basefees := make([]*uint256.Int, b.N) blobfees := make([]*uint256.Int, b.N) @@ -278,7 +278,7 @@ func benchmarkPriceHeapOverflow(b *testing.B, datacap uint64) { }} } // Create a price heap and overflow it over and over - evict := newPriceHeap(uint256.NewInt(rand.Uint64()), uint256.NewInt(rand.Uint64()), &index) + evict := newPriceHeap(uint256.NewInt(rand.Uint64()), uint256.NewInt(rand.Uint64()), index) var ( addrs = make([]common.Address, b.N) metas = make([]*blobTxMeta, b.N) diff --git a/core/txpool/legacypool/legacypool_test.go b/core/txpool/legacypool/legacypool_test.go index fab5b7499b..94949ad393 100644 --- a/core/txpool/legacypool/legacypool_test.go +++ b/core/txpool/legacypool/legacypool_test.go @@ -88,7 +88,7 @@ func (bc *testBlockChain) CurrentBlock() *types.Header { } func (bc *testBlockChain) GetBlock(hash common.Hash, number uint64) *types.Block { - return types.NewBlock(bc.CurrentBlock(), nil, nil, nil, trie.NewStackTrie(nil)) + return types.NewBlock(bc.CurrentBlock(), nil, nil, trie.NewStackTrie(nil)) } func (bc *testBlockChain) StateAt(common.Hash) (*state.StateDB, error) { diff --git a/core/types.go b/core/types.go index 09c15940c3..0b31bc3b53 100644 --- a/core/types.go +++ b/core/types.go @@ -17,7 +17,9 @@ package core import ( + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/stateless" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" ) @@ -31,7 +33,10 @@ type Validator interface { // ValidateState validates the given statedb and optionally the receipts and // gas used. - ValidateState(block *types.Block, state *state.StateDB, receipts types.Receipts, usedGas uint64) error + ValidateState(block *types.Block, state *state.StateDB, receipts types.Receipts, usedGas uint64, stateless bool) error + + // ValidateWitness cross validates a block execution with stateless remote clients. + ValidateWitness(witness *stateless.Witness, receiptRoot common.Hash, stateRoot common.Hash) error // RemoteVerifyManager return remoteVerifyManager of validator. RemoteVerifyManager() *remoteVerifyManager diff --git a/core/types/block.go b/core/types/block.go index fefe89fbea..0e395f9d4d 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -23,6 +23,7 @@ import ( "io" "math/big" "reflect" + "slices" "sync/atomic" "time" @@ -253,13 +254,19 @@ type extblock struct { // NewBlock creates a new block. The input data is copied, changes to header and to the // field values will not affect the block. // -// The values of TxHash, UncleHash, ReceiptHash and Bloom in header -// are ignored and set to values derived from the given txs, uncles -// and receipts. -func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []*Receipt, hasher TrieHasher) *Block { - b := &Block{header: CopyHeader(header)} +// The body elements and the receipts are used to recompute and overwrite the +// relevant portions of the header. +func NewBlock(header *Header, body *Body, receipts []*Receipt, hasher TrieHasher) *Block { + if body == nil { + body = &Body{} + } + var ( + b = NewBlockWithHeader(header) + txs = body.Transactions + uncles = body.Uncles + withdrawals = body.Withdrawals + ) - // TODO: panic if len(txs) != len(receipts) if len(txs) == 0 { b.header.TxHash = EmptyTxsHash } else { @@ -285,27 +292,18 @@ func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []* } } - return b -} - -// NewBlockWithWithdrawals creates a new block with withdrawals. The input data is copied, -// changes to header and to the field values will not affect the block. -// -// The values of TxHash, UncleHash, ReceiptHash and Bloom in header are ignored and set to -// values derived from the given txs, uncles and receipts. -func NewBlockWithWithdrawals(header *Header, txs []*Transaction, uncles []*Header, receipts []*Receipt, withdrawals []*Withdrawal, hasher TrieHasher) *Block { - b := NewBlock(header, txs, uncles, receipts, hasher) - if withdrawals == nil { b.header.WithdrawalsHash = nil } else if len(withdrawals) == 0 { b.header.WithdrawalsHash = &EmptyWithdrawalsHash + b.withdrawals = Withdrawals{} } else { - h := DeriveSha(Withdrawals(withdrawals), hasher) - b.header.WithdrawalsHash = &h + hash := DeriveSha(Withdrawals(withdrawals), hasher) + b.header.WithdrawalsHash = &hash + b.withdrawals = slices.Clone(withdrawals) } - return b.WithWithdrawals(withdrawals) + return b } // CopyHeader creates a deep copy of a block header. @@ -505,18 +503,18 @@ func (b *Block) WithSeal(header *Header) *Block { } } -// WithBody returns a copy of the block with the given transaction and uncle contents. -func (b *Block) WithBody(transactions []*Transaction, uncles []*Header) *Block { +// WithBody returns a new block with the original header and a deep copy of the +// provided body. +func (b *Block) WithBody(body Body) *Block { block := &Block{ header: b.header, - transactions: make([]*Transaction, len(transactions)), - uncles: make([]*Header, len(uncles)), - withdrawals: b.withdrawals, + transactions: slices.Clone(body.Transactions), + uncles: make([]*Header, len(body.Uncles)), + withdrawals: slices.Clone(body.Withdrawals), sidecars: b.sidecars, } - copy(block.transactions, transactions) - for i := range uncles { - block.uncles[i] = CopyHeader(uncles[i]) + for i := range body.Uncles { + block.uncles[i] = CopyHeader(body.Uncles[i]) } return block } diff --git a/core/types/block_test.go b/core/types/block_test.go index ced930bc81..2193696cfe 100644 --- a/core/types/block_test.go +++ b/core/types/block_test.go @@ -255,7 +255,7 @@ func makeBenchBlock() *Block { Extra: []byte("benchmark uncle"), } } - return NewBlock(header, txs, uncles, receipts, blocktest.NewHasher()) + return NewBlock(header, &Body{Transactions: txs, Uncles: uncles}, receipts, blocktest.NewHasher()) } func TestRlpDecodeParentHash(t *testing.T) { diff --git a/core/types/transaction.go b/core/types/transaction.go index 330ba18175..8a12d31a7c 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -566,7 +566,7 @@ func (s Transactions) EncodeIndex(i int, w *bytes.Buffer) { } } -// TxDifference returns a new set which is the difference between a and b. +// TxDifference returns a new set of transactions that are present in a but not in b. func TxDifference(a, b Transactions) Transactions { keep := make(Transactions, 0, len(a)) @@ -584,7 +584,7 @@ func TxDifference(a, b Transactions) Transactions { return keep } -// HashDifference returns a new set which is the difference between a and b. +// HashDifference returns a new set of hashes that are present in a but not in b. func HashDifference(a, b []common.Hash) []common.Hash { keep := make([]common.Hash, 0, len(a)) diff --git a/core/types/transaction_signing.go b/core/types/transaction_signing.go index 6e5f6712f8..339fee6f97 100644 --- a/core/types/transaction_signing.go +++ b/core/types/transaction_signing.go @@ -459,11 +459,11 @@ func (s EIP155Signer) Hash(tx *Transaction) common.Hash { // homestead rules. type HomesteadSigner struct{ FrontierSigner } -func (s HomesteadSigner) ChainID() *big.Int { +func (hs HomesteadSigner) ChainID() *big.Int { return nil } -func (s HomesteadSigner) Equal(s2 Signer) bool { +func (hs HomesteadSigner) Equal(s2 Signer) bool { _, ok := s2.(HomesteadSigner) return ok } @@ -486,11 +486,11 @@ func (hs HomesteadSigner) Sender(tx *Transaction) (common.Address, error) { // frontier rules. type FrontierSigner struct{} -func (s FrontierSigner) ChainID() *big.Int { +func (fs FrontierSigner) ChainID() *big.Int { return nil } -func (s FrontierSigner) Equal(s2 Signer) bool { +func (fs FrontierSigner) Equal(s2 Signer) bool { _, ok := s2.(FrontierSigner) return ok } @@ -572,6 +572,6 @@ func deriveChainId(v *big.Int) *big.Int { } return new(big.Int).SetUint64((v - 35) / 2) } - v = new(big.Int).Sub(v, big.NewInt(35)) - return v.Div(v, big.NewInt(2)) + v.Sub(v, big.NewInt(35)) + return v.Rsh(v, 1) } diff --git a/core/types/transaction_test.go b/core/types/transaction_test.go index 361b977611..5dbf367073 100644 --- a/core/types/transaction_test.go +++ b/core/types/transaction_test.go @@ -379,7 +379,7 @@ func assertEqual(orig *Transaction, cpy *Transaction) error { } if orig.AccessList() != nil { if !reflect.DeepEqual(orig.AccessList(), cpy.AccessList()) { - return errors.New("access list wrong!") + return errors.New("access list wrong") } } return nil diff --git a/core/vm/common.go b/core/vm/common.go index 90ba4a4ad1..ba75950e37 100644 --- a/core/vm/common.go +++ b/core/vm/common.go @@ -63,6 +63,18 @@ func getData(data []byte, start uint64, size uint64) []byte { return common.RightPadBytes(data[start:end], int(size)) } +func getDataAndAdjustedBounds(data []byte, start uint64, size uint64) (codeCopyPadded []byte, actualStart uint64, sizeNonPadded uint64) { + length := uint64(len(data)) + if start > length { + start = length + } + end := start + size + if end > length { + end = length + } + return common.RightPadBytes(data[start:end], int(size)), start, end - start +} + // toWordSize returns the ceiled word size required for memory expansion. func toWordSize(size uint64) uint64 { if size > math.MaxUint64-31 { diff --git a/core/vm/contract.go b/core/vm/contract.go index 1eafd5aec6..525a605df4 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -68,6 +68,9 @@ type Contract struct { CodeAddr *common.Address Input []byte + // is the execution frame represented by this object a contract deployment + IsDeployment bool + Gas uint64 value *uint256.Int } diff --git a/core/vm/contracts.go b/core/vm/contracts.go index fa4395a45f..cfbef98d99 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -317,6 +317,8 @@ var PrecompiledContractsPrague = map[common.Address]PrecompiledContract{ var PrecompiledContractsBLS = PrecompiledContractsPrague +var PrecompiledContractsVerkle = PrecompiledContractsPrague + var ( PrecompiledAddressesPrague []common.Address PrecompiledAddressesHaber []common.Address @@ -525,10 +527,7 @@ type bigModExp struct { var ( big1 = big.NewInt(1) big3 = big.NewInt(3) - big4 = big.NewInt(4) big7 = big.NewInt(7) - big8 = big.NewInt(8) - big16 = big.NewInt(16) big20 = big.NewInt(20) big32 = big.NewInt(32) big64 = big.NewInt(64) @@ -554,13 +553,13 @@ func modexpMultComplexity(x *big.Int) *big.Int { case x.Cmp(big1024) <= 0: // (x ** 2 // 4 ) + ( 96 * x - 3072) x = new(big.Int).Add( - new(big.Int).Div(new(big.Int).Mul(x, x), big4), + new(big.Int).Rsh(new(big.Int).Mul(x, x), 2), new(big.Int).Sub(new(big.Int).Mul(big96, x), big3072), ) default: // (x ** 2 // 16) + (480 * x - 199680) x = new(big.Int).Add( - new(big.Int).Div(new(big.Int).Mul(x, x), big16), + new(big.Int).Rsh(new(big.Int).Mul(x, x), 4), new(big.Int).Sub(new(big.Int).Mul(big480, x), big199680), ) } @@ -598,7 +597,7 @@ func (c *bigModExp) RequiredGas(input []byte) uint64 { adjExpLen := new(big.Int) if expLen.Cmp(big32) > 0 { adjExpLen.Sub(expLen, big32) - adjExpLen.Mul(big8, adjExpLen) + adjExpLen.Lsh(adjExpLen, 3) } adjExpLen.Add(adjExpLen, big.NewInt(int64(msb))) // Calculate the gas cost of the operation @@ -611,9 +610,9 @@ func (c *bigModExp) RequiredGas(input []byte) uint64 { // def mult_complexity(x): // ceiling(x/8)^2 // - // where is x is max(length_of_MODULUS, length_of_BASE) - gas = gas.Add(gas, big7) - gas = gas.Div(gas, big8) + //where is x is max(length_of_MODULUS, length_of_BASE) + gas.Add(gas, big7) + gas.Rsh(gas, 3) gas.Mul(gas, gas) gas.Mul(gas, math.BigMax(adjExpLen, big1)) @@ -936,6 +935,8 @@ func (c *bls12381G1Add) Run(input []byte) ([]byte, error) { return nil, err } + // No need to check the subgroup here, as specified by EIP-2537 + // Compute r = p_0 + p_1 p0.Add(p0, p1) @@ -965,6 +966,11 @@ func (c *bls12381G1Mul) Run(input []byte) ([]byte, error) { if p0, err = decodePointG1(input[:128]); err != nil { return nil, err } + // 'point is on curve' check already done, + // Here we need to apply subgroup checks. + if !p0.IsInSubGroup() { + return nil, errBLS12381G1PointSubgroup + } // Decode scalar value e := new(big.Int).SetBytes(input[128:]) @@ -1018,6 +1024,11 @@ func (c *bls12381G1MultiExp) Run(input []byte) ([]byte, error) { if err != nil { return nil, err } + // 'point is on curve' check already done, + // Here we need to apply subgroup checks. + if !p.IsInSubGroup() { + return nil, errBLS12381G1PointSubgroup + } points[i] = *p // Decode scalar value scalars[i] = *new(fr.Element).SetBytes(input[t1:t2]) @@ -1058,6 +1069,8 @@ func (c *bls12381G2Add) Run(input []byte) ([]byte, error) { return nil, err } + // No need to check the subgroup here, as specified by EIP-2537 + // Compute r = p_0 + p_1 r := new(bls12381.G2Affine) r.Add(p0, p1) @@ -1088,6 +1101,11 @@ func (c *bls12381G2Mul) Run(input []byte) ([]byte, error) { if p0, err = decodePointG2(input[:256]); err != nil { return nil, err } + // 'point is on curve' check already done, + // Here we need to apply subgroup checks. + if !p0.IsInSubGroup() { + return nil, errBLS12381G2PointSubgroup + } // Decode scalar value e := new(big.Int).SetBytes(input[256:]) @@ -1141,6 +1159,11 @@ func (c *bls12381G2MultiExp) Run(input []byte) ([]byte, error) { if err != nil { return nil, err } + // 'point is on curve' check already done, + // Here we need to apply subgroup checks. + if !p.IsInSubGroup() { + return nil, errBLS12381G2PointSubgroup + } points[i] = *p // Decode scalar value scalars[i] = *new(fr.Element).SetBytes(input[t1:t2]) @@ -1330,9 +1353,6 @@ func (c *bls12381MapG1) Run(input []byte) ([]byte, error) { // Compute mapping r := bls12381.MapToG1(fe) - if err != nil { - return nil, err - } // Encode the G1 point to 128 bytes return encodePointG1(&r), nil @@ -1366,9 +1386,6 @@ func (c *bls12381MapG2) Run(input []byte) ([]byte, error) { // Compute mapping r := bls12381.MapToG2(bls12381.E2{A0: c0, A1: c1}) - if err != nil { - return nil, err - } // Encode the G2 point to 256 bytes return encodePointG2(&r), nil diff --git a/core/vm/eips.go b/core/vm/eips.go index 9f06b2818f..edd6ec8d0a 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -18,9 +18,11 @@ package vm import ( "fmt" + "math" "sort" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/params" "github.com/holiman/uint256" ) @@ -37,6 +39,7 @@ var activators = map[int]func(*JumpTable){ 1884: enable1884, 1344: enable1344, 1153: enable1153, + 4762: enable4762, } // EnableEIP enables the given EIP on the config. @@ -319,3 +322,214 @@ func enable6780(jt *JumpTable) { maxStack: maxStack(1, 0), } } + +func opExtCodeCopyEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + var ( + stack = scope.Stack + a = stack.pop() + memOffset = stack.pop() + codeOffset = stack.pop() + length = stack.pop() + ) + uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow() + if overflow { + uint64CodeOffset = math.MaxUint64 + } + addr := common.Address(a.Bytes20()) + code := interpreter.evm.StateDB.GetCode(addr) + contract := &Contract{ + Code: code, + self: AccountRef(addr), + } + paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(code, uint64CodeOffset, length.Uint64()) + statelessGas := interpreter.evm.AccessEvents.CodeChunksRangeGas(addr, copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), false) + if !scope.Contract.UseGas(statelessGas, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified) { + scope.Contract.Gas = 0 + return nil, ErrOutOfGas + } + scope.Memory.Set(memOffset.Uint64(), length.Uint64(), paddedCodeCopy) + + return nil, nil +} + +// opPush1EIP4762 handles the special case of PUSH1 opcode for EIP-4762, which +// need not worry about the adjusted bound logic when adding the PUSHDATA to +// the list of access events. +func opPush1EIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + var ( + codeLen = uint64(len(scope.Contract.Code)) + integer = new(uint256.Int) + ) + *pc += 1 + if *pc < codeLen { + scope.Stack.push(integer.SetUint64(uint64(scope.Contract.Code[*pc]))) + + if !scope.Contract.IsDeployment && *pc%31 == 0 { + // touch next chunk if PUSH1 is at the boundary. if so, *pc has + // advanced past this boundary. + contractAddr := scope.Contract.Address() + statelessGas := interpreter.evm.AccessEvents.CodeChunksRangeGas(contractAddr, *pc+1, uint64(1), uint64(len(scope.Contract.Code)), false) + if !scope.Contract.UseGas(statelessGas, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified) { + scope.Contract.Gas = 0 + return nil, ErrOutOfGas + } + } + } else { + scope.Stack.push(integer.Clear()) + } + return nil, nil +} + +func makePushEIP4762(size uint64, pushByteSize int) executionFunc { + return func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + var ( + codeLen = len(scope.Contract.Code) + start = min(codeLen, int(*pc+1)) + end = min(codeLen, start+pushByteSize) + ) + scope.Stack.push(new(uint256.Int).SetBytes( + common.RightPadBytes( + scope.Contract.Code[start:end], + pushByteSize, + )), + ) + + if !scope.Contract.IsDeployment { + contractAddr := scope.Contract.Address() + statelessGas := interpreter.evm.AccessEvents.CodeChunksRangeGas(contractAddr, uint64(start), uint64(pushByteSize), uint64(len(scope.Contract.Code)), false) + if !scope.Contract.UseGas(statelessGas, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified) { + scope.Contract.Gas = 0 + return nil, ErrOutOfGas + } + } + + *pc += size + return nil, nil + } +} + +func enable4762(jt *JumpTable) { + jt[SSTORE] = &operation{ + dynamicGas: gasSStore4762, + execute: opSstore, + minStack: minStack(2, 0), + maxStack: maxStack(2, 0), + } + jt[SLOAD] = &operation{ + dynamicGas: gasSLoad4762, + execute: opSload, + minStack: minStack(1, 1), + maxStack: maxStack(1, 1), + } + + jt[BALANCE] = &operation{ + execute: opBalance, + dynamicGas: gasBalance4762, + minStack: minStack(1, 1), + maxStack: maxStack(1, 1), + } + + jt[EXTCODESIZE] = &operation{ + execute: opExtCodeSize, + dynamicGas: gasExtCodeSize4762, + minStack: minStack(1, 1), + maxStack: maxStack(1, 1), + } + + jt[EXTCODEHASH] = &operation{ + execute: opExtCodeHash, + dynamicGas: gasExtCodeHash4762, + minStack: minStack(1, 1), + maxStack: maxStack(1, 1), + } + + jt[EXTCODECOPY] = &operation{ + execute: opExtCodeCopyEIP4762, + dynamicGas: gasExtCodeCopyEIP4762, + minStack: minStack(4, 0), + maxStack: maxStack(4, 0), + memorySize: memoryExtCodeCopy, + } + + jt[CODECOPY] = &operation{ + execute: opCodeCopy, + constantGas: GasFastestStep, + dynamicGas: gasCodeCopyEip4762, + minStack: minStack(3, 0), + maxStack: maxStack(3, 0), + memorySize: memoryCodeCopy, + } + + jt[SELFDESTRUCT] = &operation{ + execute: opSelfdestruct6780, + dynamicGas: gasSelfdestructEIP4762, + constantGas: params.SelfdestructGasEIP150, + minStack: minStack(1, 0), + maxStack: maxStack(1, 0), + } + + jt[CREATE] = &operation{ + execute: opCreate, + constantGas: params.CreateNGasEip4762, + dynamicGas: gasCreateEip3860, + minStack: minStack(3, 1), + maxStack: maxStack(3, 1), + memorySize: memoryCreate, + } + + jt[CREATE2] = &operation{ + execute: opCreate2, + constantGas: params.CreateNGasEip4762, + dynamicGas: gasCreate2Eip3860, + minStack: minStack(4, 1), + maxStack: maxStack(4, 1), + memorySize: memoryCreate2, + } + + jt[CALL] = &operation{ + execute: opCall, + dynamicGas: gasCallEIP4762, + minStack: minStack(7, 1), + maxStack: maxStack(7, 1), + memorySize: memoryCall, + } + + jt[CALLCODE] = &operation{ + execute: opCallCode, + dynamicGas: gasCallCodeEIP4762, + minStack: minStack(7, 1), + maxStack: maxStack(7, 1), + memorySize: memoryCall, + } + + jt[STATICCALL] = &operation{ + execute: opStaticCall, + dynamicGas: gasStaticCallEIP4762, + minStack: minStack(6, 1), + maxStack: maxStack(6, 1), + memorySize: memoryStaticCall, + } + + jt[DELEGATECALL] = &operation{ + execute: opDelegateCall, + dynamicGas: gasDelegateCallEIP4762, + minStack: minStack(6, 1), + maxStack: maxStack(6, 1), + memorySize: memoryDelegateCall, + } + + jt[PUSH1] = &operation{ + execute: opPush1EIP4762, + constantGas: GasFastestStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + } + for i := 1; i < 32; i++ { + jt[PUSH1+OpCode(i)] = &operation{ + execute: makePushEIP4762(uint64(i+1), i+1), + constantGas: GasFastestStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + } + } +} diff --git a/core/vm/evm.go b/core/vm/evm.go index b703a632e5..bc72b86544 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -25,6 +25,7 @@ import ( "github.com/holiman/uint256" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" @@ -50,6 +51,8 @@ type ( func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) { var precompiles map[common.Address]PrecompiledContract switch { + case evm.chainRules.IsVerkle: + precompiles = PrecompiledContractsVerkle case evm.chainRules.IsPrague: precompiles = PrecompiledContractsPrague case evm.chainRules.IsHaber: @@ -109,10 +112,11 @@ type BlockContext struct { // All fields can change between transactions. type TxContext struct { // Message information - Origin common.Address // Provides information for ORIGIN - GasPrice *big.Int // Provides information for GASPRICE (and is used to zero the basefee if NoBaseFee is set) - BlobHashes []common.Hash // Provides information for BLOBHASH - BlobFeeCap *big.Int // Is used to zero the blobbasefee if NoBaseFee is set + Origin common.Address // Provides information for ORIGIN + GasPrice *big.Int // Provides information for GASPRICE (and is used to zero the basefee if NoBaseFee is set) + BlobHashes []common.Hash // Provides information for BLOBHASH + BlobFeeCap *big.Int // Is used to zero the blobbasefee if NoBaseFee is set + AccessEvents *state.AccessEvents // Capture all state accesses for this tx } // EVM is the Ethereum Virtual Machine base object and provides @@ -185,6 +189,9 @@ func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, chainConfig // Reset resets the EVM with a new transaction context.Reset // This is not threadsafe and should only be done very cautiously. func (evm *EVM) Reset(txCtx TxContext, statedb StateDB) { + if evm.chainRules.IsEIP4762 { + txCtx.AccessEvents = state.NewAccessEvents(statedb.PointCache()) + } evm.TxContext = txCtx evm.StateDB = statedb } @@ -229,6 +236,16 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas p, isPrecompile := evm.precompile(addr) if !evm.StateDB.Exist(addr) { + if !isPrecompile && evm.chainRules.IsEIP4762 { + // add proof of absence to witness + wgas := evm.AccessEvents.AddAccount(addr, false) + if gas < wgas { + evm.StateDB.RevertToSnapshot(snapshot) + return nil, 0, ErrOutOfGas + } + gas -= wgas + } + if !isPrecompile && evm.chainRules.IsEIP158 && value.IsZero() { // Calling a non-existing account, don't do anything. return nil, gas, nil @@ -243,6 +260,9 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas // Initialise a new contract and set the code that is to be used by the EVM. // The contract is a scoped environment for this execution context only. code := evm.StateDB.GetCode(addr) + if witness := evm.StateDB.Witness(); witness != nil { + witness.AddCode(code) + } if len(code) == 0 { ret, err = nil, nil // gas is unchanged } else { @@ -310,6 +330,9 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, // Initialise a new contract and set the code that is to be used by the EVM. // The contract is a scoped environment for this execution context only. contract := NewContract(caller, AccountRef(caller.Address()), value, gas) + if witness := evm.StateDB.Witness(); witness != nil { + witness.AddCode(evm.StateDB.GetCode(addrCopy)) + } contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy)) ret, err = evm.interpreter.Run(contract, input, false) gas = contract.Gas @@ -357,6 +380,9 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by addrCopy := addr // Initialise a new contract and make initialise the delegate values contract := NewContract(caller, AccountRef(caller.Address()), nil, gas).AsDelegate() + if witness := evm.StateDB.Witness(); witness != nil { + witness.AddCode(evm.StateDB.GetCode(addrCopy)) + } contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy)) ret, err = evm.interpreter.Run(contract, input, false) gas = contract.Gas @@ -412,6 +438,9 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte // Initialise a new contract and set the code that is to be used by the EVM. // The contract is a scoped environment for this execution context only. contract := NewContract(caller, AccountRef(addrCopy), new(uint256.Int), gas) + if witness := evm.StateDB.Witness(); witness != nil { + witness.AddCode(evm.StateDB.GetCode(addrCopy)) + } contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy)) // When an error was returned by the EVM or when setting the creation code // above we revert to the snapshot and consume any gas remaining. Additionally @@ -465,14 +494,15 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, return nil, common.Address{}, gas, ErrNonceUintOverflow } evm.StateDB.SetNonce(caller.Address(), nonce+1) - // We add this to the access list _before_ taking a snapshot. Even if the creation fails, - // the access-list change should not be rolled back - if evm.chainRules.IsBerlin { + + // We add this to the access list _before_ taking a snapshot. Even if the + // creation fails, the access-list change should not be rolled back. + if evm.chainRules.IsEIP2929 { evm.StateDB.AddAddressToAccessList(address) } // Ensure there's no existing contract already at the designated address. // Account is regarded as existent if any of these three conditions is met: - // - the nonce is nonzero + // - the nonce is non-zero // - the code is non-empty // - the storage is non-empty contractHash := evm.StateDB.GetCodeHash(address) @@ -485,9 +515,19 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, } return nil, common.Address{}, 0, ErrContractAddressCollision } - // Create a new account on the state + // Create a new account on the state only if the object was not present. + // It might be possible the contract code is deployed to a pre-existent + // account with non-zero balance. snapshot := evm.StateDB.Snapshot() - evm.StateDB.CreateAccount(address) + if !evm.StateDB.Exist(address) { + evm.StateDB.CreateAccount(address) + } + // CreateContract means that regardless of whether the account previously existed + // in the state trie or not, it _now_ becomes created as a _contract_ account. + // This is performed _prior_ to executing the initcode, since the initcode + // acts inside that account. + evm.StateDB.CreateContract(address) + if evm.chainRules.IsEIP158 { evm.StateDB.SetNonce(address, 1) } @@ -497,8 +537,18 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, // The contract is a scoped environment for this execution context only. contract := NewContract(caller, AccountRef(address), value, gas) contract.SetCodeOptionalHash(&address, codeAndHash) + contract.IsDeployment = true + + // Charge the contract creation init gas in verkle mode + if evm.chainRules.IsEIP4762 { + if !contract.UseGas(evm.AccessEvents.ContractCreateInitGas(address, value.Sign() != 0), evm.Config.Tracer, tracing.GasChangeWitnessContractInit) { + err = ErrOutOfGas + } + } - ret, err = evm.interpreter.Run(contract, nil, false) + if err == nil { + ret, err = evm.interpreter.Run(contract, nil, false) + } // Check whether the max code size has been exceeded, assign err if the case. if err == nil && evm.chainRules.IsEIP158 && len(ret) > params.MaxCodeSize { @@ -515,11 +565,24 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, // be stored due to not enough gas set an error and let it be handled // by the error checking condition below. if err == nil { - createDataGas := uint64(len(ret)) * params.CreateDataGas - if contract.UseGas(createDataGas, evm.Config.Tracer, tracing.GasChangeCallCodeStorage) { - evm.StateDB.SetCode(address, ret) + if !evm.chainRules.IsEIP4762 { + createDataGas := uint64(len(ret)) * params.CreateDataGas + if !contract.UseGas(createDataGas, evm.Config.Tracer, tracing.GasChangeCallCodeStorage) { + err = ErrCodeStoreOutOfGas + } } else { - err = ErrCodeStoreOutOfGas + // Contract creation completed, touch the missing fields in the contract + if !contract.UseGas(evm.AccessEvents.AddAccount(address, true), evm.Config.Tracer, tracing.GasChangeWitnessContractCreation) { + err = ErrCodeStoreOutOfGas + } + + if err == nil && len(ret) > 0 && !contract.UseGas(evm.AccessEvents.CodeChunksRangeGas(address, 0, uint64(len(ret)), uint64(len(ret)), true), evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk) { + err = ErrCodeStoreOutOfGas + } + } + + if err == nil { + evm.StateDB.SetCode(address, ret) } } diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index fd5fa14cf5..d294324b08 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -383,7 +383,7 @@ func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize } else if !evm.StateDB.Exist(address) { gas += params.CallNewAccountGas } - if transfersValue { + if transfersValue && !evm.chainRules.IsEIP4762 { gas += params.CallValueTransferGas } memoryGas, err := memoryGasCost(mem, memorySize) @@ -394,7 +394,14 @@ func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize if gas, overflow = math.SafeAdd(gas, memoryGas); overflow { return 0, ErrGasUintOverflow } - + if evm.chainRules.IsEIP4762 { + if transfersValue { + gas, overflow = math.SafeAdd(gas, evm.AccessEvents.ValueTransferGas(contract.Address(), address)) + if overflow { + return 0, ErrGasUintOverflow + } + } + } evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0)) if err != nil { return 0, err @@ -402,6 +409,7 @@ func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow { return 0, ErrGasUintOverflow } + return gas, nil } @@ -414,12 +422,22 @@ func gasCallCode(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memory gas uint64 overflow bool ) - if stack.Back(2).Sign() != 0 { + if stack.Back(2).Sign() != 0 && !evm.chainRules.IsEIP4762 { gas += params.CallValueTransferGas } if gas, overflow = math.SafeAdd(gas, memoryGas); overflow { return 0, ErrGasUintOverflow } + if evm.chainRules.IsEIP4762 { + address := common.Address(stack.Back(1).Bytes20()) + transfersValue := !stack.Back(2).IsZero() + if transfersValue { + gas, overflow = math.SafeAdd(gas, evm.AccessEvents.ValueTransferGas(contract.Address(), address)) + if overflow { + return 0, ErrGasUintOverflow + } + } + } evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0)) if err != nil { return 0, err diff --git a/core/vm/instructions.go b/core/vm/instructions.go index a062bb15ff..9ec4544643 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -173,11 +173,7 @@ func opByte(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt func opAddmod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { x, y, z := scope.Stack.pop(), scope.Stack.pop(), scope.Stack.peek() - if z.IsZero() { - z.Clear() - } else { - z.AddMod(&x, &y, z) - } + z.AddMod(&x, &y, z) return nil, nil } @@ -344,6 +340,10 @@ func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeConte func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { slot := scope.Stack.peek() + address := slot.Bytes20() + if witness := interpreter.evm.StateDB.Witness(); witness != nil { + witness.AddCode(interpreter.evm.StateDB.GetCode(address)) + } slot.SetUint64(uint64(interpreter.evm.StateDB.GetCodeSize(slot.Bytes20()))) return nil, nil } @@ -363,9 +363,9 @@ func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ if overflow { uint64CodeOffset = math.MaxUint64 } + codeCopy := getData(scope.Contract.Code, uint64CodeOffset, length.Uint64()) scope.Memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy) - return nil, nil } @@ -382,7 +382,11 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) uint64CodeOffset = math.MaxUint64 } addr := common.Address(a.Bytes20()) - codeCopy := getData(interpreter.evm.StateDB.GetCode(addr), uint64CodeOffset, length.Uint64()) + code := interpreter.evm.StateDB.GetCode(addr) + if witness := interpreter.evm.StateDB.Witness(); witness != nil { + witness.AddCode(code) + } + codeCopy := getData(code, uint64CodeOffset, length.Uint64()) scope.Memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy) return nil, nil @@ -438,6 +442,7 @@ func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ( num.Clear() return nil, nil } + var upper, lower uint64 upper = interpreter.evm.Context.BlockNumber.Uint64() if upper < 257 { @@ -446,7 +451,11 @@ func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ( lower = upper - 256 } if num64 >= lower && num64 < upper { - num.SetBytes(interpreter.evm.Context.GetHash(num64).Bytes()) + res := interpreter.evm.Context.GetHash(num64) + if witness := interpreter.evm.StateDB.Witness(); witness != nil { + witness.AddBlockHash(num64) + } + num.SetBytes(res[:]) } else { num.Clear() } @@ -587,6 +596,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b if interpreter.evm.chainRules.IsEIP150 { gas -= gas / 64 } + // reuse size int for stackvalue stackvalue := size @@ -627,6 +637,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] input = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64())) gas = scope.Contract.Gas ) + // Apply EIP150 gas -= gas / 64 scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer, tracing.GasChangeCallContractCreation2) @@ -641,7 +652,6 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] stackvalue.SetBytes(addr.Bytes()) } scope.Stack.push(&stackvalue) - scope.Contract.RefundGas(returnGas, interpreter.evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded) if suberr == ErrExecutionReverted { @@ -900,6 +910,7 @@ func makePush(size uint64, pushByteSize int) executionFunc { pushByteSize, )), ) + *pc += size return nil, nil } diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index 8653864d11..e17e913aa3 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -643,7 +643,7 @@ func BenchmarkOpKeccak256(bench *testing.B) { } } -func TestCreate2Addreses(t *testing.T) { +func TestCreate2Addresses(t *testing.T) { type testcase struct { origin string salt string diff --git a/core/vm/interface.go b/core/vm/interface.go index 30742e96de..5f42643565 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -20,15 +20,18 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/stateless" "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/trie/utils" "github.com/holiman/uint256" ) // StateDB is an EVM database for full state querying. type StateDB interface { CreateAccount(common.Address) + CreateContract(common.Address) SubBalance(common.Address, *uint256.Int, tracing.BalanceChangeReason) AddBalance(common.Address, *uint256.Int, tracing.BalanceChangeReason) @@ -74,6 +77,10 @@ type StateDB interface { // AddSlotToAccessList adds the given (address,slot) to the access list. This operation is safe to perform // even if the feature/fork is not active yet AddSlotToAccessList(addr common.Address, slot common.Hash) + + // PointCache returns the point cache used in computations + PointCache() *utils.PointCache + Prepare(rules params.Rules, sender, coinbase common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList) RevertToSnapshot(int) @@ -81,6 +88,8 @@ type StateDB interface { AddLog(*types.Log) AddPreimage(common.Hash, []byte) + + Witness() *stateless.Witness } // CallContext provides a basic interface for the EVM calling conventions. The EVM diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index c28c1cf953..a7efa6db97 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -40,6 +40,7 @@ type Config struct { NoBaseFee bool // Forces the EIP-1559 baseFee to 0 (needed for 0 price calls) EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages ExtraEips []int // Additional EIPS that are to be enabled + EnableWitnessCollection bool // true if witness collection is enabled } // ScopeContext contains the things that are per-call, such as stack and memory, @@ -106,6 +107,9 @@ func NewEVMInterpreter(evm *EVM) *EVMInterpreter { // If jump table was not initialised we set the default one. var table *JumpTable switch { + case evm.chainRules.IsVerkle: + // TODO replace with proper instruction set when fork is specified + table = &verkleInstructionSet case evm.chainRules.IsCancun: table = &cancunInstructionSet case evm.chainRules.IsShanghai: @@ -233,6 +237,14 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( // Capture pre-execution values for tracing. logged, pcCopy, gasCopy = false, pc, contract.Gas } + + if in.evm.chainRules.IsEIP4762 && !contract.IsDeployment { + // if the PC ends up in a new "chunk" of verkleized code, charge the + // associated costs. + contractAddr := contract.Address() + contract.Gas -= in.evm.TxContext.AccessEvents.CodeChunksRangeGas(contractAddr, pc, 1, uint64(len(contract.Code)), false) + } + // Get the operation from the jump table and validate the stack to ensure there are // enough stack items available to perform the operation. op = contract.GetOp(pc) diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index 70c543f14d..0d62ade807 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -57,6 +57,7 @@ var ( mergeInstructionSet = newMergeInstructionSet() shanghaiInstructionSet = newShanghaiInstructionSet() cancunInstructionSet = newCancunInstructionSet() + verkleInstructionSet = newVerkleInstructionSet() ) // JumpTable contains the EVM opcodes supported at a given fork. @@ -80,6 +81,12 @@ func validate(jt JumpTable) JumpTable { return jt } +func newVerkleInstructionSet() JumpTable { + instructionSet := newCancunInstructionSet() + enable4762(&instructionSet) + return validate(instructionSet) +} + func newCancunInstructionSet() JumpTable { instructionSet := newShanghaiInstructionSet() enable4844(&instructionSet) // EIP-4844 (BLOBHASH opcode) diff --git a/core/vm/operations_verkle.go b/core/vm/operations_verkle.go new file mode 100644 index 0000000000..73eb05974d --- /dev/null +++ b/core/vm/operations_verkle.go @@ -0,0 +1,159 @@ +// Copyright 2024 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package vm + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/params" +) + +func gasSStore4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + gas := evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), true) + if gas == 0 { + gas = params.WarmStorageReadCostEIP2929 + } + return gas, nil +} + +func gasSLoad4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + gas := evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), false) + if gas == 0 { + gas = params.WarmStorageReadCostEIP2929 + } + return gas, nil +} + +func gasBalance4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + address := stack.peek().Bytes20() + gas := evm.AccessEvents.BalanceGas(address, false) + if gas == 0 { + gas = params.WarmStorageReadCostEIP2929 + } + return gas, nil +} + +func gasExtCodeSize4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + address := stack.peek().Bytes20() + if _, isPrecompile := evm.precompile(address); isPrecompile { + return 0, nil + } + gas := evm.AccessEvents.VersionGas(address, false) + gas += evm.AccessEvents.CodeSizeGas(address, false) + if gas == 0 { + gas = params.WarmStorageReadCostEIP2929 + } + return gas, nil +} + +func gasExtCodeHash4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + address := stack.peek().Bytes20() + if _, isPrecompile := evm.precompile(address); isPrecompile { + return 0, nil + } + gas := evm.AccessEvents.CodeHashGas(address, false) + if gas == 0 { + gas = params.WarmStorageReadCostEIP2929 + } + return gas, nil +} + +func makeCallVariantGasEIP4762(oldCalculator gasFunc) gasFunc { + return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + gas, err := oldCalculator(evm, contract, stack, mem, memorySize) + if err != nil { + return 0, err + } + if _, isPrecompile := evm.precompile(contract.Address()); isPrecompile { + return gas, nil + } + witnessGas := evm.AccessEvents.MessageCallGas(contract.Address()) + if witnessGas == 0 { + witnessGas = params.WarmStorageReadCostEIP2929 + } + return witnessGas + gas, nil + } +} + +var ( + gasCallEIP4762 = makeCallVariantGasEIP4762(gasCall) + gasCallCodeEIP4762 = makeCallVariantGasEIP4762(gasCallCode) + gasStaticCallEIP4762 = makeCallVariantGasEIP4762(gasStaticCall) + gasDelegateCallEIP4762 = makeCallVariantGasEIP4762(gasDelegateCall) +) + +func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + beneficiaryAddr := common.Address(stack.peek().Bytes20()) + if _, isPrecompile := evm.precompile(beneficiaryAddr); isPrecompile { + return 0, nil + } + contractAddr := contract.Address() + statelessGas := evm.AccessEvents.VersionGas(contractAddr, false) + statelessGas += evm.AccessEvents.CodeSizeGas(contractAddr, false) + statelessGas += evm.AccessEvents.BalanceGas(contractAddr, false) + if contractAddr != beneficiaryAddr { + statelessGas += evm.AccessEvents.BalanceGas(beneficiaryAddr, false) + } + // Charge write costs if it transfers value + if evm.StateDB.GetBalance(contractAddr).Sign() != 0 { + statelessGas += evm.AccessEvents.BalanceGas(contractAddr, true) + if contractAddr != beneficiaryAddr { + statelessGas += evm.AccessEvents.BalanceGas(beneficiaryAddr, true) + } + } + return statelessGas, nil +} + +func gasCodeCopyEip4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + gas, err := gasCodeCopy(evm, contract, stack, mem, memorySize) + if err != nil { + return 0, err + } + var ( + codeOffset = stack.Back(1) + length = stack.Back(2) + ) + uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow() + if overflow { + uint64CodeOffset = math.MaxUint64 + } + _, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(contract.Code, uint64CodeOffset, length.Uint64()) + if !contract.IsDeployment { + gas += evm.AccessEvents.CodeChunksRangeGas(contract.Address(), copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), false) + } + return gas, nil +} + +func gasExtCodeCopyEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + // memory expansion first (dynamic part of pre-2929 implementation) + gas, err := gasExtCodeCopy(evm, contract, stack, mem, memorySize) + if err != nil { + return 0, err + } + addr := common.Address(stack.peek().Bytes20()) + wgas := evm.AccessEvents.VersionGas(addr, false) + wgas += evm.AccessEvents.CodeSizeGas(addr, false) + if wgas == 0 { + wgas = params.WarmStorageReadCostEIP2929 + } + var overflow bool + // We charge (cold-warm), since 'warm' is already charged as constantGas + if gas, overflow = math.SafeAdd(gas, wgas); overflow { + return 0, ErrGasUintOverflow + } + return gas, nil +} diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index b587d6d5a0..1181e5fccd 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -57,24 +57,33 @@ type Config struct { // sets defaults on the config func setDefaults(cfg *Config) { if cfg.ChainConfig == nil { + var ( + shanghaiTime = uint64(0) + cancunTime = uint64(0) + ) cfg.ChainConfig = ¶ms.ChainConfig{ - ChainID: big.NewInt(1), - HomesteadBlock: new(big.Int), - DAOForkBlock: new(big.Int), - DAOForkSupport: false, - EIP150Block: new(big.Int), - EIP155Block: new(big.Int), - EIP158Block: new(big.Int), - ByzantiumBlock: new(big.Int), - ConstantinopleBlock: new(big.Int), - PetersburgBlock: new(big.Int), - IstanbulBlock: new(big.Int), - MuirGlacierBlock: new(big.Int), - BerlinBlock: new(big.Int), - LondonBlock: new(big.Int), - } + ChainID: big.NewInt(1), + HomesteadBlock: new(big.Int), + DAOForkBlock: new(big.Int), + DAOForkSupport: false, + EIP150Block: new(big.Int), + EIP155Block: new(big.Int), + EIP158Block: new(big.Int), + ByzantiumBlock: new(big.Int), + ConstantinopleBlock: new(big.Int), + PetersburgBlock: new(big.Int), + IstanbulBlock: new(big.Int), + MuirGlacierBlock: new(big.Int), + BerlinBlock: new(big.Int), + LondonBlock: new(big.Int), + ArrowGlacierBlock: nil, + GrayGlacierBlock: nil, + TerminalTotalDifficulty: big.NewInt(0), + TerminalTotalDifficultyPassed: true, + MergeNetsplitBlock: nil, + ShanghaiTime: &shanghaiTime, + CancunTime: &cancunTime} } - if cfg.Difficulty == nil { cfg.Difficulty = new(big.Int) } @@ -101,6 +110,10 @@ func setDefaults(cfg *Config) { if cfg.BlobBaseFee == nil { cfg.BlobBaseFee = big.NewInt(params.BlobTxMinBlobGasprice) } + // Merge indicators + if t := cfg.ChainConfig.ShanghaiTime; cfg.ChainConfig.TerminalTotalDifficultyPassed || (t != nil && *t == 0) { + cfg.Random = &(common.Hash{}) + } } // Execute executes the code using the input as call data during the execution. diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go index 45228e78c4..04abc5480e 100644 --- a/core/vm/runtime/runtime_test.go +++ b/core/vm/runtime/runtime_test.go @@ -105,7 +105,7 @@ func TestExecute(t *testing.T) { func TestCall(t *testing.T) { state, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) - address := common.HexToAddress("0x0a") + address := common.HexToAddress("0xaa") state.SetCode(address, []byte{ byte(vm.PUSH1), 10, byte(vm.PUSH1), 0, @@ -725,7 +725,7 @@ func TestRuntimeJSTracer(t *testing.T) { byte(vm.CREATE), byte(vm.POP), }, - results: []string{`"1,1,952855,6,12"`, `"1,1,952855,6,0"`}, + results: []string{`"1,1,952853,6,12"`, `"1,1,952853,6,0"`}, }, { // CREATE2 @@ -741,7 +741,7 @@ func TestRuntimeJSTracer(t *testing.T) { byte(vm.CREATE2), byte(vm.POP), }, - results: []string{`"1,1,952846,6,13"`, `"1,1,952846,6,0"`}, + results: []string{`"1,1,952844,6,13"`, `"1,1,952844,6,0"`}, }, { // CALL diff --git a/crypto/secp256k1/curve.go b/crypto/secp256k1/curve.go index 9b26ab2928..85ba885d6f 100644 --- a/crypto/secp256k1/curve.go +++ b/crypto/secp256k1/curve.go @@ -79,52 +79,52 @@ type BitCurve struct { BitSize int // the size of the underlying field } -func (BitCurve *BitCurve) Params() *elliptic.CurveParams { +func (bitCurve *BitCurve) Params() *elliptic.CurveParams { return &elliptic.CurveParams{ - P: BitCurve.P, - N: BitCurve.N, - B: BitCurve.B, - Gx: BitCurve.Gx, - Gy: BitCurve.Gy, - BitSize: BitCurve.BitSize, + P: bitCurve.P, + N: bitCurve.N, + B: bitCurve.B, + Gx: bitCurve.Gx, + Gy: bitCurve.Gy, + BitSize: bitCurve.BitSize, } } // IsOnCurve returns true if the given (x,y) lies on the BitCurve. -func (BitCurve *BitCurve) IsOnCurve(x, y *big.Int) bool { +func (bitCurve *BitCurve) IsOnCurve(x, y *big.Int) bool { // y² = x³ + b y2 := new(big.Int).Mul(y, y) //y² - y2.Mod(y2, BitCurve.P) //y²%P + y2.Mod(y2, bitCurve.P) //y²%P x3 := new(big.Int).Mul(x, x) //x² x3.Mul(x3, x) //x³ - x3.Add(x3, BitCurve.B) //x³+B - x3.Mod(x3, BitCurve.P) //(x³+B)%P + x3.Add(x3, bitCurve.B) //x³+B + x3.Mod(x3, bitCurve.P) //(x³+B)%P return x3.Cmp(y2) == 0 } // affineFromJacobian reverses the Jacobian transform. See the comment at the // top of the file. -func (BitCurve *BitCurve) affineFromJacobian(x, y, z *big.Int) (xOut, yOut *big.Int) { +func (bitCurve *BitCurve) affineFromJacobian(x, y, z *big.Int) (xOut, yOut *big.Int) { if z.Sign() == 0 { return new(big.Int), new(big.Int) } - zinv := new(big.Int).ModInverse(z, BitCurve.P) + zinv := new(big.Int).ModInverse(z, bitCurve.P) zinvsq := new(big.Int).Mul(zinv, zinv) xOut = new(big.Int).Mul(x, zinvsq) - xOut.Mod(xOut, BitCurve.P) + xOut.Mod(xOut, bitCurve.P) zinvsq.Mul(zinvsq, zinv) yOut = new(big.Int).Mul(y, zinvsq) - yOut.Mod(yOut, BitCurve.P) + yOut.Mod(yOut, bitCurve.P) return } // Add returns the sum of (x1,y1) and (x2,y2) -func (BitCurve *BitCurve) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) { +func (bitCurve *BitCurve) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) { // If one point is at infinity, return the other point. // Adding the point at infinity to any point will preserve the other point. if x1.Sign() == 0 && y1.Sign() == 0 { @@ -135,27 +135,27 @@ func (BitCurve *BitCurve) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) { } z := new(big.Int).SetInt64(1) if x1.Cmp(x2) == 0 && y1.Cmp(y2) == 0 { - return BitCurve.affineFromJacobian(BitCurve.doubleJacobian(x1, y1, z)) + return bitCurve.affineFromJacobian(bitCurve.doubleJacobian(x1, y1, z)) } - return BitCurve.affineFromJacobian(BitCurve.addJacobian(x1, y1, z, x2, y2, z)) + return bitCurve.affineFromJacobian(bitCurve.addJacobian(x1, y1, z, x2, y2, z)) } // addJacobian takes two points in Jacobian coordinates, (x1, y1, z1) and // (x2, y2, z2) and returns their sum, also in Jacobian form. -func (BitCurve *BitCurve) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int, *big.Int, *big.Int) { +func (bitCurve *BitCurve) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int, *big.Int, *big.Int) { // See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl z1z1 := new(big.Int).Mul(z1, z1) - z1z1.Mod(z1z1, BitCurve.P) + z1z1.Mod(z1z1, bitCurve.P) z2z2 := new(big.Int).Mul(z2, z2) - z2z2.Mod(z2z2, BitCurve.P) + z2z2.Mod(z2z2, bitCurve.P) u1 := new(big.Int).Mul(x1, z2z2) - u1.Mod(u1, BitCurve.P) + u1.Mod(u1, bitCurve.P) u2 := new(big.Int).Mul(x2, z1z1) - u2.Mod(u2, BitCurve.P) + u2.Mod(u2, bitCurve.P) h := new(big.Int).Sub(u2, u1) if h.Sign() == -1 { - h.Add(h, BitCurve.P) + h.Add(h, bitCurve.P) } i := new(big.Int).Lsh(h, 1) i.Mul(i, i) @@ -163,13 +163,13 @@ func (BitCurve *BitCurve) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int s1 := new(big.Int).Mul(y1, z2) s1.Mul(s1, z2z2) - s1.Mod(s1, BitCurve.P) + s1.Mod(s1, bitCurve.P) s2 := new(big.Int).Mul(y2, z1) s2.Mul(s2, z1z1) - s2.Mod(s2, BitCurve.P) + s2.Mod(s2, bitCurve.P) r := new(big.Int).Sub(s2, s1) if r.Sign() == -1 { - r.Add(r, BitCurve.P) + r.Add(r, bitCurve.P) } r.Lsh(r, 1) v := new(big.Int).Mul(u1, i) @@ -179,7 +179,7 @@ func (BitCurve *BitCurve) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int x3.Sub(x3, j) x3.Sub(x3, v) x3.Sub(x3, v) - x3.Mod(x3, BitCurve.P) + x3.Mod(x3, bitCurve.P) y3 := new(big.Int).Set(r) v.Sub(v, x3) @@ -187,33 +187,33 @@ func (BitCurve *BitCurve) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int s1.Mul(s1, j) s1.Lsh(s1, 1) y3.Sub(y3, s1) - y3.Mod(y3, BitCurve.P) + y3.Mod(y3, bitCurve.P) z3 := new(big.Int).Add(z1, z2) z3.Mul(z3, z3) z3.Sub(z3, z1z1) if z3.Sign() == -1 { - z3.Add(z3, BitCurve.P) + z3.Add(z3, bitCurve.P) } z3.Sub(z3, z2z2) if z3.Sign() == -1 { - z3.Add(z3, BitCurve.P) + z3.Add(z3, bitCurve.P) } z3.Mul(z3, h) - z3.Mod(z3, BitCurve.P) + z3.Mod(z3, bitCurve.P) return x3, y3, z3 } // Double returns 2*(x,y) -func (BitCurve *BitCurve) Double(x1, y1 *big.Int) (*big.Int, *big.Int) { +func (bitCurve *BitCurve) Double(x1, y1 *big.Int) (*big.Int, *big.Int) { z1 := new(big.Int).SetInt64(1) - return BitCurve.affineFromJacobian(BitCurve.doubleJacobian(x1, y1, z1)) + return bitCurve.affineFromJacobian(bitCurve.doubleJacobian(x1, y1, z1)) } // doubleJacobian takes a point in Jacobian coordinates, (x, y, z), and // returns its double, also in Jacobian form. -func (BitCurve *BitCurve) doubleJacobian(x, y, z *big.Int) (*big.Int, *big.Int, *big.Int) { +func (bitCurve *BitCurve) doubleJacobian(x, y, z *big.Int) (*big.Int, *big.Int, *big.Int) { // See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l a := new(big.Int).Mul(x, x) //X1² @@ -231,30 +231,30 @@ func (BitCurve *BitCurve) doubleJacobian(x, y, z *big.Int) (*big.Int, *big.Int, x3 := new(big.Int).Mul(big.NewInt(2), d) //2*D x3.Sub(f, x3) //F-2*D - x3.Mod(x3, BitCurve.P) + x3.Mod(x3, bitCurve.P) y3 := new(big.Int).Sub(d, x3) //D-X3 y3.Mul(e, y3) //E*(D-X3) y3.Sub(y3, new(big.Int).Mul(big.NewInt(8), c)) //E*(D-X3)-8*C - y3.Mod(y3, BitCurve.P) + y3.Mod(y3, bitCurve.P) z3 := new(big.Int).Mul(y, z) //Y1*Z1 z3.Mul(big.NewInt(2), z3) //3*Y1*Z1 - z3.Mod(z3, BitCurve.P) + z3.Mod(z3, bitCurve.P) return x3, y3, z3 } // ScalarBaseMult returns k*G, where G is the base point of the group and k is // an integer in big-endian form. -func (BitCurve *BitCurve) ScalarBaseMult(k []byte) (*big.Int, *big.Int) { - return BitCurve.ScalarMult(BitCurve.Gx, BitCurve.Gy, k) +func (bitCurve *BitCurve) ScalarBaseMult(k []byte) (*big.Int, *big.Int) { + return bitCurve.ScalarMult(bitCurve.Gx, bitCurve.Gy, k) } // Marshal converts a point into the form specified in section 4.3.6 of ANSI // X9.62. -func (BitCurve *BitCurve) Marshal(x, y *big.Int) []byte { - byteLen := (BitCurve.BitSize + 7) >> 3 +func (bitCurve *BitCurve) Marshal(x, y *big.Int) []byte { + byteLen := (bitCurve.BitSize + 7) >> 3 ret := make([]byte, 1+2*byteLen) ret[0] = 4 // uncompressed point flag readBits(x, ret[1:1+byteLen]) @@ -264,8 +264,8 @@ func (BitCurve *BitCurve) Marshal(x, y *big.Int) []byte { // Unmarshal converts a point, serialised by Marshal, into an x, y pair. On // error, x = nil. -func (BitCurve *BitCurve) Unmarshal(data []byte) (x, y *big.Int) { - byteLen := (BitCurve.BitSize + 7) >> 3 +func (bitCurve *BitCurve) Unmarshal(data []byte) (x, y *big.Int) { + byteLen := (bitCurve.BitSize + 7) >> 3 if len(data) != 1+2*byteLen { return } diff --git a/crypto/secp256k1/scalar_mult_cgo.go b/crypto/secp256k1/scalar_mult_cgo.go index bdf8eeede7..d11c11faf8 100644 --- a/crypto/secp256k1/scalar_mult_cgo.go +++ b/crypto/secp256k1/scalar_mult_cgo.go @@ -21,7 +21,7 @@ extern int secp256k1_ext_scalar_mul(const secp256k1_context* ctx, const unsigned */ import "C" -func (BitCurve *BitCurve) ScalarMult(Bx, By *big.Int, scalar []byte) (*big.Int, *big.Int) { +func (bitCurve *BitCurve) ScalarMult(Bx, By *big.Int, scalar []byte) (*big.Int, *big.Int) { // Ensure scalar is exactly 32 bytes. We pad always, even if // scalar is 32 bytes long, to avoid a timing side channel. if len(scalar) > 32 { diff --git a/crypto/secp256k1/scalar_mult_nocgo.go b/crypto/secp256k1/scalar_mult_nocgo.go index 22f53ac6ae..feb13a8dfd 100644 --- a/crypto/secp256k1/scalar_mult_nocgo.go +++ b/crypto/secp256k1/scalar_mult_nocgo.go @@ -9,6 +9,6 @@ package secp256k1 import "math/big" -func (BitCurve *BitCurve) ScalarMult(Bx, By *big.Int, scalar []byte) (*big.Int, *big.Int) { +func (bitCurve *BitCurve) ScalarMult(Bx, By *big.Int, scalar []byte) (*big.Int, *big.Int) { panic("ScalarMult is not available when secp256k1 is built without cgo") } diff --git a/eth/backend.go b/eth/backend.go index 57b1f46643..ed8aca9a08 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -125,9 +125,6 @@ type Ethereum struct { // whose lifecycle will be managed by the provided node. func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { // Ensure configuration values are compatible and sane - if config.SyncMode == downloader.LightSync { - return nil, errors.New("can't run eth.Ethereum in light sync mode, light mode has been deprecated") - } if !config.SyncMode.IsValid() { return nil, fmt.Errorf("invalid sync mode %d", config.SyncMode) } @@ -251,6 +248,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { var ( vmConfig = vm.Config{ EnablePreimageRecording: config.EnablePreimageRecording, + EnableWitnessCollection: config.EnableWitnessCollection, } cacheConfig = &core.CacheConfig{ TrieCleanLimit: config.TrieCleanCache, @@ -276,7 +274,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { } t, err := tracers.LiveDirectory.New(config.VMTrace, traceConfig) if err != nil { - return nil, fmt.Errorf("Failed to create tracer %s: %v", config.VMTrace, err) + return nil, fmt.Errorf("failed to create tracer %s: %v", config.VMTrace, err) } vmConfig.Tracer = t } diff --git a/eth/catalyst/simulated_beacon.go b/eth/catalyst/simulated_beacon.go index fecd83f276..2d6569e422 100644 --- a/eth/catalyst/simulated_beacon.go +++ b/eth/catalyst/simulated_beacon.go @@ -279,9 +279,12 @@ func (c *SimulatedBeacon) Rollback() { // Fork sets the head to the provided hash. func (c *SimulatedBeacon) Fork(parentHash common.Hash) error { + // Ensure no pending transactions. + c.eth.TxPool().Sync() if len(c.eth.TxPool().Pending(txpool.PendingFilter{})) != 0 { return errors.New("pending block dirty") } + parent := c.eth.BlockChain().GetBlockByHash(parentHash) if parent == nil { return errors.New("parent not found") diff --git a/eth/downloader/api.go b/eth/downloader/api.go index 9495de2107..daef356744 100644 --- a/eth/downloader/api.go +++ b/eth/downloader/api.go @@ -130,7 +130,7 @@ func (api *DownloaderAPI) eventLoop() { } } -// Syncing provides information when this nodes starts synchronising with the Ethereum network and when it's finished. +// Syncing provides information when this node starts synchronising with the Ethereum network and when it's finished. func (api *DownloaderAPI) Syncing(ctx context.Context) (*rpc.Subscription, error) { notifier, supported := rpc.NotifierFromContext(ctx) if !supported { diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index a3ad67a25a..01b763d4a1 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -1401,7 +1401,7 @@ func (d *Downloader) importBlockResults(results []*fetchResult) error { ) blocks := make([]*types.Block, len(results)) for i, result := range results { - blocks[i] = types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Uncles).WithWithdrawals(result.Withdrawals).WithSidecars(result.Sidecars) + blocks[i] = types.NewBlockWithHeader(result.Header).WithBody(types.Body{Transactions: result.Transactions, Uncles: result.Uncles, Withdrawals: result.Withdrawals}).WithSidecars(result.Sidecars) } // Downloaded blocks are always regarded as trusted after the // transition. Because the downloaded chain is guided by the @@ -1616,7 +1616,7 @@ func (d *Downloader) commitSnapSyncData(results []*fetchResult, stateSync *state blocks := make([]*types.Block, len(results)) receipts := make([]types.Receipts, len(results)) for i, result := range results { - blocks[i] = types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Uncles).WithWithdrawals(result.Withdrawals).WithSidecars(result.Sidecars) + blocks[i] = types.NewBlockWithHeader(result.Header).WithBody(types.Body{Transactions: result.Transactions, Uncles: result.Uncles, Withdrawals: result.Withdrawals}).WithSidecars(result.Sidecars) receipts[i] = result.Receipts } if index, err := d.blockchain.InsertReceiptChain(blocks, receipts, d.ancientLimit); err != nil { @@ -1627,7 +1627,7 @@ func (d *Downloader) commitSnapSyncData(results []*fetchResult, stateSync *state } func (d *Downloader) commitPivotBlock(result *fetchResult) error { - block := types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Uncles).WithWithdrawals(result.Withdrawals).WithSidecars(result.Sidecars) + block := types.NewBlockWithHeader(result.Header).WithBody(types.Body{Transactions: result.Transactions, Uncles: result.Uncles, Withdrawals: result.Withdrawals}).WithSidecars(result.Sidecars) log.Debug("Committing snap sync pivot as new head", "number", block.Number(), "hash", block.Hash()) // Commit the pivot block as the new head, will require full sync from here on diff --git a/eth/downloader/modes.go b/eth/downloader/modes.go index d388b9ee4d..340cf7a61a 100644 --- a/eth/downloader/modes.go +++ b/eth/downloader/modes.go @@ -29,7 +29,7 @@ const ( ) func (mode SyncMode) IsValid() bool { - return mode >= FullSync && mode <= LightSync + return mode == FullSync || mode == SnapSync } // String implements the stringer interface. @@ -39,8 +39,6 @@ func (mode SyncMode) String() string { return "full" case SnapSync: return "snap" - case LightSync: - return "light" default: return "unknown" } @@ -52,8 +50,6 @@ func (mode SyncMode) MarshalText() ([]byte, error) { return []byte("full"), nil case SnapSync: return []byte("snap"), nil - case LightSync: - return []byte("light"), nil default: return nil, fmt.Errorf("unknown sync mode %d", mode) } @@ -65,10 +61,8 @@ func (mode *SyncMode) UnmarshalText(text []byte) error { *mode = FullSync case "snap": *mode = SnapSync - case "light": - *mode = LightSync default: - return fmt.Errorf(`unknown sync mode %q, want "full", "snap" or "light"`, text) + return fmt.Errorf(`unknown sync mode %q, want "full" or "snap"`, text) } return nil } diff --git a/eth/downloader/queue.go b/eth/downloader/queue.go index 72d21c329e..e55de6e51e 100644 --- a/eth/downloader/queue.go +++ b/eth/downloader/queue.go @@ -90,6 +90,15 @@ func newFetchResult(header *types.Header, fastSync bool, pid string) *fetchResul return item } +// body returns a representation of the fetch result as a types.Body object. +func (f *fetchResult) body() types.Body { + return types.Body{ + Transactions: f.Transactions, + Uncles: f.Uncles, + Withdrawals: f.Withdrawals, + } +} + // SetBodyDone flags the body as finished. func (f *fetchResult) SetBodyDone() { if v := f.pending.Load(); (v & (1 << bodyType)) != 0 { diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index 10b6ccb938..a75fa388e4 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -173,6 +173,9 @@ type Config struct { // Enables tracking of SHA3 preimages in the VM EnablePreimageRecording bool + // Enables prefetching trie nodes for read operations too + EnableWitnessCollection bool `toml:"-"` + // Enables VM tracing VMTrace string VMTraceJsonConfig string diff --git a/eth/ethconfig/gen_config.go b/eth/ethconfig/gen_config.go index b3c36fc9aa..c1d1e98dfe 100644 --- a/eth/ethconfig/gen_config.go +++ b/eth/ethconfig/gen_config.go @@ -65,6 +65,7 @@ func (c Config) MarshalTOML() (interface{}, error) { BlobPool blobpool.Config GPO gasprice.Config EnablePreimageRecording bool + EnableWitnessCollection bool `toml:"-"` VMTrace string VMTraceJsonConfig string DocRoot string `toml:"-"` @@ -126,6 +127,7 @@ func (c Config) MarshalTOML() (interface{}, error) { enc.BlobPool = c.BlobPool enc.GPO = c.GPO enc.EnablePreimageRecording = c.EnablePreimageRecording + enc.EnableWitnessCollection = c.EnableWitnessCollection enc.VMTrace = c.VMTrace enc.VMTraceJsonConfig = c.VMTraceJsonConfig enc.DocRoot = c.DocRoot @@ -191,6 +193,7 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { BlobPool *blobpool.Config GPO *gasprice.Config EnablePreimageRecording *bool + EnableWitnessCollection *bool `toml:"-"` VMTrace *string VMTraceJsonConfig *string DocRoot *string `toml:"-"` @@ -351,6 +354,9 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { if dec.EnablePreimageRecording != nil { c.EnablePreimageRecording = *dec.EnablePreimageRecording } + if dec.EnableWitnessCollection != nil { + c.EnableWitnessCollection = *dec.EnableWitnessCollection + } if dec.VMTrace != nil { c.VMTrace = *dec.VMTrace } diff --git a/eth/fetcher/block_fetcher.go b/eth/fetcher/block_fetcher.go index 18b20bd061..c5694f7f51 100644 --- a/eth/fetcher/block_fetcher.go +++ b/eth/fetcher/block_fetcher.go @@ -727,7 +727,7 @@ func (f *BlockFetcher) loop() { // Mark the body matched, reassemble if still unknown matched = true if f.getBlock(hash) == nil { - block := types.NewBlockWithHeader(announce.header).WithBody(task.transactions[i], task.uncles[i]) + block := types.NewBlockWithHeader(announce.header).WithBody(types.Body{Transactions: task.transactions[i], Uncles: task.uncles[i]}) block = block.WithSidecars(task.sidecars[i]) block.ReceivedAt = task.time blocks = append(blocks, block) diff --git a/eth/gasestimator/gasestimator.go b/eth/gasestimator/gasestimator.go index f07f98956e..ac3b59e97e 100644 --- a/eth/gasestimator/gasestimator.go +++ b/eth/gasestimator/gasestimator.go @@ -80,6 +80,16 @@ func Estimate(ctx context.Context, call *core.Message, opts *Options, gasCap uin } available.Sub(available, call.Value) } + if opts.Config.IsCancun(opts.Header.Number, opts.Header.Time) && len(call.BlobHashes) > 0 { + blobGasPerBlob := new(big.Int).SetInt64(params.BlobTxBlobGasPerBlob) + blobBalanceUsage := new(big.Int).SetInt64(int64(len(call.BlobHashes))) + blobBalanceUsage.Mul(blobBalanceUsage, blobGasPerBlob) + blobBalanceUsage.Mul(blobBalanceUsage, call.BlobGasFeeCap) + if blobBalanceUsage.Cmp(available) >= 0 { + return 0, nil, core.ErrInsufficientFunds + } + available.Sub(available, blobBalanceUsage) + } allowance := new(big.Int).Div(available, feeCap) // If the allowance is larger than maximum uint64, skip checking diff --git a/eth/gasprice/feehistory.go b/eth/gasprice/feehistory.go index d039bcb401..1e625e21c0 100644 --- a/eth/gasprice/feehistory.go +++ b/eth/gasprice/feehistory.go @@ -44,7 +44,8 @@ const ( // maxBlockFetchers is the max number of goroutines to spin up to pull blocks // for the fee history calculation (mostly relevant for LES). maxBlockFetchers = 4 - maxQueryLimit = 100 + // maxQueryLimit is the max number of requested percentiles. + maxQueryLimit = 100 ) // blockFees represents a single block for processing diff --git a/eth/handler.go b/eth/handler.go index 3ff37f79af..e107756b1d 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -46,7 +46,6 @@ import ( "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/enode" - "golang.org/x/crypto/sha3" ) const ( @@ -805,7 +804,7 @@ func (h *handler) BroadcastTransactions(txs types.Transactions) { var ( signer = types.LatestSignerForChainID(h.chain.Config().ChainID) // Don't care about chain status, we just need *a* sender - hasher = sha3.NewLegacyKeccak256().(crypto.KeccakState) + hasher = crypto.NewKeccakState() hash = make([]byte, 32) ) for _, tx := range txs { diff --git a/eth/protocols/eth/handlers.go b/eth/protocols/eth/handlers.go index 583dd2f67c..c89694f261 100644 --- a/eth/protocols/eth/handlers.go +++ b/eth/protocols/eth/handlers.go @@ -189,7 +189,7 @@ func serviceContiguousBlockHeaderQuery(chain *core.BlockChain, query *GetBlockHe return headers } { // Last mode: deliver ancestors of H - for i := uint64(1); header != nil && i < count; i++ { + for i := uint64(1); i < count; i++ { header = chain.GetHeaderByHash(header.ParentHash) if header == nil { break diff --git a/eth/protocols/snap/gentrie.go b/eth/protocols/snap/gentrie.go index 81c2640b62..6255fb221d 100644 --- a/eth/protocols/snap/gentrie.go +++ b/eth/protocols/snap/gentrie.go @@ -164,7 +164,7 @@ func (t *pathTrie) deleteAccountNode(path []byte, inner bool) { } else { accountOuterLookupGauge.Inc(1) } - if !rawdb.ExistsAccountTrieNode(t.db, path) { + if !rawdb.HasAccountTrieNode(t.db, path) { return } if inner { @@ -181,7 +181,7 @@ func (t *pathTrie) deleteStorageNode(path []byte, inner bool) { } else { storageOuterLookupGauge.Inc(1) } - if !rawdb.ExistsStorageTrieNode(t.db, t.owner, path) { + if !rawdb.HasStorageTrieNode(t.db, t.owner, path) { return } if inner { diff --git a/eth/protocols/snap/handler.go b/eth/protocols/snap/handler.go index bd7ce9e715..5cbe9d1270 100644 --- a/eth/protocols/snap/handler.go +++ b/eth/protocols/snap/handler.go @@ -332,11 +332,7 @@ func ServiceGetAccountRangeQuery(chain *core.BlockChain, req *GetAccountRangePac return nil, nil } } - var proofs [][]byte - for _, blob := range proof.List() { - proofs = append(proofs, blob) - } - return accounts, proofs + return accounts, proof.List() } func ServiceGetStorageRangesQuery(chain *core.BlockChain, req *GetStorageRangesPacket) ([][]*StorageData, [][]byte) { @@ -438,9 +434,7 @@ func ServiceGetStorageRangesQuery(chain *core.BlockChain, req *GetStorageRangesP return nil, nil } } - for _, blob := range proof.List() { - proofs = append(proofs, blob) - } + proofs = append(proofs, proof.List()...) // Proof terminates the reply as proofs are only added if a node // refuses to serve more data (exception when a contract fetch is // finishing, but that's that). diff --git a/eth/protocols/snap/progress_test.go b/eth/protocols/snap/progress_test.go index 9d923bd2f5..1d9a6b8474 100644 --- a/eth/protocols/snap/progress_test.go +++ b/eth/protocols/snap/progress_test.go @@ -80,7 +80,7 @@ func makeLegacyProgress() legacyProgress { Next: common.Hash{}, Last: common.Hash{0x77}, SubTasks: map[common.Hash][]*legacyStorageTask{ - common.Hash{0x1}: { + {0x1}: { { Next: common.Hash{}, Last: common.Hash{0xff}, diff --git a/eth/protocols/snap/sync.go b/eth/protocols/snap/sync.go index e80ae8ffdb..27aede21a2 100644 --- a/eth/protocols/snap/sync.go +++ b/eth/protocols/snap/sync.go @@ -43,7 +43,6 @@ import ( "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie/trienode" - "golang.org/x/crypto/sha3" ) const ( @@ -105,7 +104,7 @@ var ( // to allow concurrent retrievals. accountConcurrency = 16 - // storageConcurrency is the number of chunks to split the a large contract + // storageConcurrency is the number of chunks to split a large contract // storage trie into to allow concurrent retrievals. storageConcurrency = 16 ) @@ -2393,7 +2392,7 @@ func (s *Syncer) commitHealer(force bool) { err = s.healer.scheduler.Commit(batch, nil) } if err != nil { - log.Error("Failed to commit healing data", "err", err) + log.Crit("Failed to commit healing data", "err", err) } if err := batch.Write(); err != nil { log.Crit("Failed to persist healing data", "err", err) @@ -2692,7 +2691,7 @@ func (s *Syncer) onByteCodes(peer SyncPeer, id uint64, bytecodes [][]byte) error // Cross reference the requested bytecodes with the response to find gaps // that the serving node is missing - hasher := sha3.NewLegacyKeccak256().(crypto.KeccakState) + hasher := crypto.NewKeccakState() hash := make([]byte, 32) codes := make([][]byte, len(req.hashes)) @@ -2940,7 +2939,7 @@ func (s *Syncer) OnTrieNodes(peer SyncPeer, id uint64, trienodes [][]byte) error // Cross reference the requested trienodes with the response to find gaps // that the serving node is missing var ( - hasher = sha3.NewLegacyKeccak256().(crypto.KeccakState) + hasher = crypto.NewKeccakState() hash = make([]byte, 32) nodes = make([][]byte, len(req.hashes)) fills uint64 @@ -3046,7 +3045,7 @@ func (s *Syncer) onHealByteCodes(peer SyncPeer, id uint64, bytecodes [][]byte) e // Cross reference the requested bytecodes with the response to find gaps // that the serving node is missing - hasher := sha3.NewLegacyKeccak256().(crypto.KeccakState) + hasher := crypto.NewKeccakState() hash := make([]byte, 32) codes := make([][]byte, len(req.hashes)) @@ -3290,9 +3289,9 @@ func (t *healRequestSort) Merge() []TrieNodePathSet { // sortByAccountPath takes hashes and paths, and sorts them. After that, it generates // the TrieNodePaths and merges paths which belongs to the same account path. func sortByAccountPath(paths []string, hashes []common.Hash) ([]string, []common.Hash, []trie.SyncPath, []TrieNodePathSet) { - var syncPaths []trie.SyncPath - for _, path := range paths { - syncPaths = append(syncPaths, trie.NewSyncPath([]byte(path))) + syncPaths := make([]trie.SyncPath, len(paths)) + for i, path := range paths { + syncPaths[i] = trie.NewSyncPath([]byte(path)) } n := &healRequestSort{paths, hashes, syncPaths} sort.Sort(n) diff --git a/eth/protocols/snap/sync_test.go b/eth/protocols/snap/sync_test.go index f35babb731..c97c3b99b3 100644 --- a/eth/protocols/snap/sync_test.go +++ b/eth/protocols/snap/sync_test.go @@ -64,7 +64,7 @@ func TestHashing(t *testing.T) { } } var new = func() { - hasher := sha3.NewLegacyKeccak256().(crypto.KeccakState) + hasher := crypto.NewKeccakState() var hash = make([]byte, 32) for i := 0; i < len(bytecodes); i++ { hasher.Reset() @@ -96,7 +96,7 @@ func BenchmarkHashing(b *testing.B) { } } var new = func() { - hasher := sha3.NewLegacyKeccak256().(crypto.KeccakState) + hasher := crypto.NewKeccakState() var hash = make([]byte, 32) for i := 0; i < len(bytecodes); i++ { hasher.Reset() @@ -286,10 +286,7 @@ func createAccountRequestResponse(t *testPeer, root common.Hash, origin common.H t.logger.Error("Could not prove last item", "error", err) } } - for _, blob := range proof.List() { - proofs = append(proofs, blob) - } - return keys, vals, proofs + return keys, vals, proof.List() } // defaultStorageRequestHandler is a well-behaving storage request handler @@ -371,9 +368,7 @@ func createStorageRequestResponse(t *testPeer, root common.Hash, accounts []comm t.logger.Error("Could not prove last item", "error", err) } } - for _, blob := range proof.List() { - proofs = append(proofs, blob) - } + proofs = append(proofs, proof.List()...) break } } @@ -430,9 +425,7 @@ func createStorageRequestResponseAlwaysProve(t *testPeer, root common.Hash, acco t.logger.Error("Could not prove last item", "error", err) } } - for _, blob := range proof.List() { - proofs = append(proofs, blob) - } + proofs = append(proofs, proof.List()...) break } } @@ -586,9 +579,8 @@ func testSyncBloatedProof(t *testing.T, scheme string) { source.accountRequestHandler = func(t *testPeer, requestId uint64, root common.Hash, origin common.Hash, limit common.Hash, cap uint64) error { var ( - proofs [][]byte - keys []common.Hash - vals [][]byte + keys []common.Hash + vals [][]byte ) // The values for _, entry := range t.accountValues { @@ -618,10 +610,7 @@ func testSyncBloatedProof(t *testing.T, scheme string) { keys = append(keys[:1], keys[2:]...) vals = append(vals[:1], vals[2:]...) } - for _, blob := range proof.List() { - proofs = append(proofs, blob) - } - if err := t.remote.OnAccounts(t, requestId, keys, vals, proofs); err != nil { + if err := t.remote.OnAccounts(t, requestId, keys, vals, proof.List()); err != nil { t.logger.Info("remote error on delivery (as expected)", "error", err) t.term() // This is actually correct, signal to exit the test successfully @@ -1525,7 +1514,7 @@ func makeAccountTrieNoStorage(n int, scheme string) (string, *trie.Trie, []*kv) // Commit the state changes into db and re-create the trie // for accessing later. - root, nodes, _ := accTrie.Commit(false) + root, nodes := accTrie.Commit(false) db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil) accTrie, _ = trie.New(trie.StateTrieID(root), db) @@ -1587,7 +1576,7 @@ func makeBoundaryAccountTrie(scheme string, n int) (string, *trie.Trie, []*kv) { // Commit the state changes into db and re-create the trie // for accessing later. - root, nodes, _ := accTrie.Commit(false) + root, nodes := accTrie.Commit(false) db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil) accTrie, _ = trie.New(trie.StateTrieID(root), db) @@ -1633,7 +1622,7 @@ func makeAccountTrieWithStorageWithUniqueStorage(scheme string, accounts, slots slices.SortFunc(entries, (*kv).cmp) // Commit account trie - root, set, _ := accTrie.Commit(true) + root, set := accTrie.Commit(true) nodes.Merge(set) // Commit gathered dirty nodes into database @@ -1700,7 +1689,7 @@ func makeAccountTrieWithStorage(scheme string, accounts, slots int, code, bounda slices.SortFunc(entries, (*kv).cmp) // Commit account trie - root, set, _ := accTrie.Commit(true) + root, set := accTrie.Commit(true) nodes.Merge(set) // Commit gathered dirty nodes into database @@ -1742,7 +1731,7 @@ func makeStorageTrieWithSeed(owner common.Hash, n, seed uint64, db *triedb.Datab entries = append(entries, elem) } slices.SortFunc(entries, (*kv).cmp) - root, nodes, _ := trie.Commit(false) + root, nodes := trie.Commit(false) return root, nodes, entries } @@ -1793,7 +1782,7 @@ func makeBoundaryStorageTrie(owner common.Hash, n int, db *triedb.Database) (com entries = append(entries, elem) } slices.SortFunc(entries, (*kv).cmp) - root, nodes, _ := trie.Commit(false) + root, nodes := trie.Commit(false) return root, nodes, entries } @@ -1825,7 +1814,7 @@ func makeUnevenStorageTrie(owner common.Hash, slots int, db *triedb.Database) (c } } slices.SortFunc(entries, (*kv).cmp) - root, nodes, _ := tr.Commit(false) + root, nodes := tr.Commit(false) return root, nodes, entries } diff --git a/eth/state_accessor.go b/eth/state_accessor.go index 9d78df4aeb..c6d5e96816 100644 --- a/eth/state_accessor.go +++ b/eth/state_accessor.go @@ -158,9 +158,7 @@ func (eth *Ethereum) hashState(ctx context.Context, block *types.Block, reexec u return nil, nil, fmt.Errorf("processing block %d failed: %v", current.NumberU64(), err) } // Finalize the state so any modifications are written to the trie - statedb.Finalise(eth.blockchain.Config().IsEIP158(current.Number())) - statedb.AccountsIntermediateRoot() - root, _, err := statedb.Commit(current.NumberU64(), nil) + root, _, err := statedb.Commit(current.NumberU64(), eth.blockchain.Config().IsEIP158(current.Number())) if err != nil { return nil, nil, fmt.Errorf("stateAtBlock commit failed, number %d root %v: %w", current.NumberU64(), current.Root().Hex(), err) diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 86d4c858d2..0f0d70efb0 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -22,7 +22,6 @@ import ( "encoding/json" "errors" "fmt" - "math/big" "os" "runtime" "sync" @@ -938,9 +937,13 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block // Execute the transaction and flush any traces to disk vmenv := vm.NewEVM(vmctx, txContext, statedb, chainConfig, vmConf) statedb.SetTxContext(tx.Hash(), i) - vmConf.Tracer.OnTxStart(vmenv.GetVMContext(), tx, msg.From) + if vmConf.Tracer.OnTxStart != nil { + vmConf.Tracer.OnTxStart(vmenv.GetVMContext(), tx, msg.From) + } vmRet, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.GasLimit)) - vmConf.Tracer.OnTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas}, err) + if vmConf.Tracer.OnTxEnd != nil { + vmConf.Tracer.OnTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas}, err) + } if writer != nil { writer.Flush() } @@ -1133,7 +1136,8 @@ func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *cor return nil, err } } - vmenv := vm.NewEVM(vmctx, vm.TxContext{GasPrice: big.NewInt(0)}, statedb, api.backend.ChainConfig(), vm.Config{Tracer: tracer.Hooks, NoBaseFee: true}) + // The actual TxContext will be created as part of ApplyTransactionWithEVM. + vmenv := vm.NewEVM(vmctx, vm.TxContext{GasPrice: message.GasPrice, BlobFeeCap: message.BlobGasFeeCap}, statedb, api.backend.ChainConfig(), vm.Config{Tracer: tracer.Hooks, NoBaseFee: true}) statedb.SetLogger(tracer.Hooks) // Define a meaningful timeout of a single transaction trace diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index 86f504de60..0fd32268be 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -32,6 +32,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/beacon" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" @@ -312,7 +313,7 @@ func TestTraceCall(t *testing.T) { config: &TraceCallConfig{TxIndex: uintPtr(1)}, expectErr: fmt.Errorf("tracing failed: insufficient funds for gas * price + value: address %s have 1000000000000000000 want 1000000000000000100", accounts[2].addr), }, - // After the target transaction, should be succeed + // After the target transaction, should be succeeded { blockNumber: rpc.BlockNumber(genBlocks - 1), call: ethapi.TransactionArgs{ @@ -995,3 +996,90 @@ func TestTraceChain(t *testing.T) { } } } + +// newTestMergedBackend creates a post-merge chain +func newTestMergedBackend(t *testing.T, n int, gspec *core.Genesis, generator func(i int, b *core.BlockGen)) *testBackend { + backend := &testBackend{ + chainConfig: gspec.Config, + engine: beacon.NewFaker(), + chaindb: rawdb.NewMemoryDatabase(), + } + // Generate blocks for testing + _, blocks, _ := core.GenerateChainWithGenesis(gspec, backend.engine, n, generator) + + // Import the canonical chain + cacheConfig := &core.CacheConfig{ + TrieCleanLimit: 256, + TrieDirtyLimit: 256, + TrieTimeLimit: 5 * time.Minute, + SnapshotLimit: 0, + TrieDirtyDisabled: true, // Archive mode + } + chain, err := core.NewBlockChain(backend.chaindb, cacheConfig, gspec, nil, backend.engine, vm.Config{}, nil, nil) + if err != nil { + t.Fatalf("failed to create tester chain: %v", err) + } + if n, err := chain.InsertChain(blocks); err != nil { + t.Fatalf("block %d: failed to insert into chain: %v", n, err) + } + backend.chain = chain + return backend +} + +func TestTraceBlockWithBasefee(t *testing.T) { + t.Parallel() + accounts := newAccounts(1) + target := common.HexToAddress("0x1111111111111111111111111111111111111111") + genesis := &core.Genesis{ + Config: params.AllDevChainProtocolChanges, + Alloc: types.GenesisAlloc{ + accounts[0].addr: {Balance: big.NewInt(1 * params.Ether)}, + target: {Nonce: 1, Code: []byte{ + byte(vm.BASEFEE), byte(vm.STOP), + }}, + }, + } + genBlocks := 1 + signer := types.HomesteadSigner{} + var txHash common.Hash + var baseFee = new(big.Int) + backend := newTestMergedBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) { + tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{ + Nonce: uint64(i), + To: &target, + Value: big.NewInt(0), + Gas: 5 * params.TxGas, + GasPrice: b.BaseFee(), + Data: nil}), + signer, accounts[0].key) + b.AddTx(tx) + txHash = tx.Hash() + baseFee.Set(b.BaseFee()) + }) + defer backend.chain.Stop() + api := NewAPI(backend) + + var testSuite = []struct { + blockNumber rpc.BlockNumber + config *TraceConfig + want string + }{ + // Trace head block + { + blockNumber: rpc.BlockNumber(genBlocks), + want: fmt.Sprintf(`[{"txHash":"%#x","result":{"gas":21002,"failed":false,"returnValue":"","structLogs":[{"pc":0,"op":"BASEFEE","gas":84000,"gasCost":2,"depth":1,"stack":[]},{"pc":1,"op":"STOP","gas":83998,"gasCost":0,"depth":1,"stack":["%#x"]}]}}]`, txHash, baseFee), + }, + } + for i, tc := range testSuite { + result, err := api.TraceBlockByNumber(context.Background(), tc.blockNumber, tc.config) + if err != nil { + t.Errorf("test %d, want no error, have %v", i, err) + continue + } + have, _ := json.Marshal(result) + want := tc.want + if string(have) != want { + t.Errorf("test %d, result mismatch\nhave: %v\nwant: %v\n", i, string(have), want) + } + } +} diff --git a/eth/tracers/internal/tracetest/supply_test.go b/eth/tracers/internal/tracetest/supply_test.go new file mode 100644 index 0000000000..d608b1e002 --- /dev/null +++ b/eth/tracers/internal/tracetest/supply_test.go @@ -0,0 +1,613 @@ +// 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 . + +package tracetest + +import ( + "bufio" + "bytes" + "encoding/json" + "fmt" + "math/big" + "os" + "path" + "path/filepath" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/consensus/beacon" + "github.com/ethereum/go-ethereum/consensus/ethash" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth/tracers" + "github.com/ethereum/go-ethereum/params" + + // Force-load live packages, to trigger registration + _ "github.com/ethereum/go-ethereum/eth/tracers/live" +) + +type supplyInfoIssuance struct { + GenesisAlloc *hexutil.Big `json:"genesisAlloc,omitempty"` + Reward *hexutil.Big `json:"reward,omitempty"` + Withdrawals *hexutil.Big `json:"withdrawals,omitempty"` +} + +type supplyInfoBurn struct { + EIP1559 *hexutil.Big `json:"1559,omitempty"` + Blob *hexutil.Big `json:"blob,omitempty"` + Misc *hexutil.Big `json:"misc,omitempty"` +} + +type supplyInfo struct { + Issuance *supplyInfoIssuance `json:"issuance,omitempty"` + Burn *supplyInfoBurn `json:"burn,omitempty"` + + // Block info + Number uint64 `json:"blockNumber"` + Hash common.Hash `json:"hash"` + ParentHash common.Hash `json:"parentHash"` +} + +func emptyBlockGenerationFunc(b *core.BlockGen) {} + +func TestSupplyOmittedFields(t *testing.T) { + var ( + config = *params.MergedTestChainConfig + gspec = &core.Genesis{ + Config: &config, + } + ) + + gspec.Config.TerminalTotalDifficulty = big.NewInt(0) + + out, _, err := testSupplyTracer(t, gspec, func(b *core.BlockGen) { + b.SetPoS() + }) + if err != nil { + t.Fatalf("failed to test supply tracer: %v", err) + } + + expected := supplyInfo{ + Number: 0, + Hash: common.HexToHash("0x52f276d96f0afaaf2c3cb358868bdc2779c4b0cb8de3e7e5302e247c0b66a703"), + ParentHash: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), + } + actual := out[expected.Number] + + compareAsJSON(t, expected, actual) +} + +func TestSupplyGenesisAlloc(t *testing.T) { + var ( + key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") + addr1 = crypto.PubkeyToAddress(key1.PublicKey) + addr2 = crypto.PubkeyToAddress(key2.PublicKey) + eth1 = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether)) + + config = *params.AllEthashProtocolChanges + + gspec = &core.Genesis{ + Config: &config, + Alloc: types.GenesisAlloc{ + addr1: {Balance: eth1}, + addr2: {Balance: eth1}, + }, + } + ) + + expected := supplyInfo{ + Issuance: &supplyInfoIssuance{ + GenesisAlloc: (*hexutil.Big)(new(big.Int).Mul(common.Big2, big.NewInt(params.Ether))), + }, + Number: 0, + Hash: common.HexToHash("0xbcc9466e9fc6a8b56f4b29ca353a421ff8b51a0c1a58ca4743b427605b08f2ca"), + ParentHash: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), + } + + out, _, err := testSupplyTracer(t, gspec, emptyBlockGenerationFunc) + if err != nil { + t.Fatalf("failed to test supply tracer: %v", err) + } + + actual := out[expected.Number] + + compareAsJSON(t, expected, actual) +} + +func TestSupplyRewards(t *testing.T) { + var ( + config = *params.AllEthashProtocolChanges + + gspec = &core.Genesis{ + Config: &config, + } + ) + + expected := supplyInfo{ + Issuance: &supplyInfoIssuance{ + Reward: (*hexutil.Big)(new(big.Int).Mul(common.Big2, big.NewInt(params.Ether))), + }, + Number: 1, + Hash: common.HexToHash("0xcbb08370505be503dafedc4e96d139ea27aba3cbc580148568b8a307b3f51052"), + ParentHash: common.HexToHash("0xadeda0a83e337b6c073e3f0e9a17531a04009b397a9588c093b628f21b8bc5a3"), + } + + out, _, err := testSupplyTracer(t, gspec, emptyBlockGenerationFunc) + if err != nil { + t.Fatalf("failed to test supply tracer: %v", err) + } + + actual := out[expected.Number] + + compareAsJSON(t, expected, actual) +} + +func TestSupplyEip1559Burn(t *testing.T) { + var ( + config = *params.AllEthashProtocolChanges + + aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa") + // A sender who makes transactions, has some eth1 + key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + addr1 = crypto.PubkeyToAddress(key1.PublicKey) + gwei5 = new(big.Int).Mul(big.NewInt(5), big.NewInt(params.GWei)) + eth1 = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether)) + + gspec = &core.Genesis{ + Config: &config, + BaseFee: big.NewInt(params.InitialBaseFee), + Alloc: types.GenesisAlloc{ + addr1: {Balance: eth1}, + }, + } + ) + + signer := types.LatestSigner(gspec.Config) + + eip1559BlockGenerationFunc := func(b *core.BlockGen) { + txdata := &types.DynamicFeeTx{ + ChainID: gspec.Config.ChainID, + Nonce: 0, + To: &aa, + Gas: 21000, + GasFeeCap: gwei5, + GasTipCap: big.NewInt(2), + } + tx := types.NewTx(txdata) + tx, _ = types.SignTx(tx, signer, key1) + + b.AddTx(tx) + } + + out, chain, err := testSupplyTracer(t, gspec, eip1559BlockGenerationFunc) + if err != nil { + t.Fatalf("failed to test supply tracer: %v", err) + } + var ( + head = chain.CurrentBlock() + reward = new(big.Int).Mul(common.Big2, big.NewInt(params.Ether)) + burn = new(big.Int).Mul(big.NewInt(21000), head.BaseFee) + expected = supplyInfo{ + Issuance: &supplyInfoIssuance{ + Reward: (*hexutil.Big)(reward), + }, + Burn: &supplyInfoBurn{ + EIP1559: (*hexutil.Big)(burn), + }, + Number: 1, + Hash: head.Hash(), + ParentHash: head.ParentHash, + } + ) + + actual := out[expected.Number] + compareAsJSON(t, expected, actual) +} + +func TestSupplyWithdrawals(t *testing.T) { + var ( + config = *params.MergedTestChainConfig + gspec = &core.Genesis{ + Config: &config, + } + ) + + withdrawalsBlockGenerationFunc := func(b *core.BlockGen) { + b.SetPoS() + + b.AddWithdrawal(&types.Withdrawal{ + Validator: 42, + Address: common.Address{0xee}, + Amount: 1337, + }) + } + + out, chain, err := testSupplyTracer(t, gspec, withdrawalsBlockGenerationFunc) + if err != nil { + t.Fatalf("failed to test supply tracer: %v", err) + } + + var ( + head = chain.CurrentBlock() + expected = supplyInfo{ + Issuance: &supplyInfoIssuance{ + Withdrawals: (*hexutil.Big)(big.NewInt(1337000000000)), + }, + Number: 1, + Hash: head.Hash(), + ParentHash: head.ParentHash, + } + actual = out[expected.Number] + ) + + compareAsJSON(t, expected, actual) +} + +// Tests fund retrieval after contract's selfdestruct. +// Contract A calls contract B which selfdestructs, but B receives eth1 +// after the selfdestruct opcode executes from Contract A. +// Because Contract B is removed only at the end of the transaction +// the ether sent in between is burnt before Cancun hard fork. +func TestSupplySelfdestruct(t *testing.T) { + var ( + config = *params.TestChainConfig + + aa = common.HexToAddress("0x1111111111111111111111111111111111111111") + bb = common.HexToAddress("0x2222222222222222222222222222222222222222") + dad = common.HexToAddress("0x0000000000000000000000000000000000000dad") + key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + addr1 = crypto.PubkeyToAddress(key1.PublicKey) + gwei5 = new(big.Int).Mul(big.NewInt(5), big.NewInt(params.GWei)) + eth1 = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether)) + + gspec = &core.Genesis{ + Config: &config, + BaseFee: big.NewInt(params.InitialBaseFee), + Alloc: types.GenesisAlloc{ + addr1: {Balance: eth1}, + aa: { + Code: common.FromHex("0x61face60f01b6000527322222222222222222222222222222222222222226000806002600080855af160008103603457600080fd5b60008060008034865af1905060008103604c57600080fd5b5050"), + // Nonce: 0, + Balance: big.NewInt(0), + }, + bb: { + Code: common.FromHex("0x6000357fface000000000000000000000000000000000000000000000000000000000000808203602f57610dad80ff5b5050"), + Nonce: 0, + Balance: eth1, + }, + }, + } + ) + + gspec.Config.TerminalTotalDifficulty = big.NewInt(0) + + signer := types.LatestSigner(gspec.Config) + + testBlockGenerationFunc := func(b *core.BlockGen) { + b.SetPoS() + + txdata := &types.LegacyTx{ + Nonce: 0, + To: &aa, + Value: gwei5, + Gas: 150000, + GasPrice: gwei5, + Data: []byte{}, + } + + tx := types.NewTx(txdata) + tx, _ = types.SignTx(tx, signer, key1) + + b.AddTx(tx) + } + + // 1. Test pre Cancun + preCancunOutput, preCancunChain, err := testSupplyTracer(t, gspec, testBlockGenerationFunc) + if err != nil { + t.Fatalf("Pre-cancun failed to test supply tracer: %v", err) + } + + // Check balance at state: + // 1. 0x0000...000dad has 1 ether + // 2. A has 0 ether + // 3. B has 0 ether + statedb, _ := preCancunChain.State() + if got, exp := statedb.GetBalance(dad), eth1; got.CmpBig(exp) != 0 { + t.Fatalf("Pre-cancun address \"%v\" balance, got %v exp %v\n", dad, got, exp) + } + if got, exp := statedb.GetBalance(aa), big.NewInt(0); got.CmpBig(exp) != 0 { + t.Fatalf("Pre-cancun address \"%v\" balance, got %v exp %v\n", aa, got, exp) + } + if got, exp := statedb.GetBalance(bb), big.NewInt(0); got.CmpBig(exp) != 0 { + t.Fatalf("Pre-cancun address \"%v\" balance, got %v exp %v\n", bb, got, exp) + } + + head := preCancunChain.CurrentBlock() + // Check live trace output + expected := supplyInfo{ + Burn: &supplyInfoBurn{ + EIP1559: (*hexutil.Big)(big.NewInt(55289500000000)), + Misc: (*hexutil.Big)(big.NewInt(5000000000)), + }, + Number: 1, + Hash: head.Hash(), + ParentHash: head.ParentHash, + } + + actual := preCancunOutput[expected.Number] + + compareAsJSON(t, expected, actual) + + // 2. Test post Cancun + cancunTime := uint64(0) + gspec.Config.ShanghaiTime = &cancunTime + gspec.Config.CancunTime = &cancunTime + + postCancunOutput, postCancunChain, err := testSupplyTracer(t, gspec, testBlockGenerationFunc) + if err != nil { + t.Fatalf("Post-cancun failed to test supply tracer: %v", err) + } + + // Check balance at state: + // 1. 0x0000...000dad has 1 ether + // 3. A has 0 ether + // 3. B has 5 gwei + statedb, _ = postCancunChain.State() + if got, exp := statedb.GetBalance(dad), eth1; got.CmpBig(exp) != 0 { + t.Fatalf("Post-shanghai address \"%v\" balance, got %v exp %v\n", dad, got, exp) + } + if got, exp := statedb.GetBalance(aa), big.NewInt(0); got.CmpBig(exp) != 0 { + t.Fatalf("Post-shanghai address \"%v\" balance, got %v exp %v\n", aa, got, exp) + } + if got, exp := statedb.GetBalance(bb), gwei5; got.CmpBig(exp) != 0 { + t.Fatalf("Post-shanghai address \"%v\" balance, got %v exp %v\n", bb, got, exp) + } + + // Check live trace output + head = postCancunChain.CurrentBlock() + expected = supplyInfo{ + Burn: &supplyInfoBurn{ + EIP1559: (*hexutil.Big)(big.NewInt(55289500000000)), + }, + Number: 1, + Hash: head.Hash(), + ParentHash: head.ParentHash, + } + + actual = postCancunOutput[expected.Number] + + compareAsJSON(t, expected, actual) +} + +// Tests selfdestructing contract to send its balance to itself (burn). +// It tests both cases of selfdestructing succeeding and being reverted. +// - Contract A calls B and D. +// - Contract B selfdestructs and sends the eth1 to itself (Burn amount to be counted). +// - Contract C selfdestructs and sends the eth1 to itself. +// - Contract D calls C and reverts (Burn amount of C +// has to be reverted as well). +func TestSupplySelfdestructItselfAndRevert(t *testing.T) { + var ( + config = *params.TestChainConfig + + aa = common.HexToAddress("0x1111111111111111111111111111111111111111") + bb = common.HexToAddress("0x2222222222222222222222222222222222222222") + cc = common.HexToAddress("0x3333333333333333333333333333333333333333") + dd = common.HexToAddress("0x4444444444444444444444444444444444444444") + key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + addr1 = crypto.PubkeyToAddress(key1.PublicKey) + gwei5 = new(big.Int).Mul(big.NewInt(5), big.NewInt(params.GWei)) + eth1 = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether)) + eth2 = new(big.Int).Mul(common.Big2, big.NewInt(params.Ether)) + eth5 = new(big.Int).Mul(big.NewInt(5), big.NewInt(params.Ether)) + + gspec = &core.Genesis{ + Config: &config, + // BaseFee: big.NewInt(params.InitialBaseFee), + Alloc: types.GenesisAlloc{ + addr1: {Balance: eth1}, + aa: { + // Contract code in YUL: + // + // object "ContractA" { + // code { + // let B := 0x2222222222222222222222222222222222222222 + // let D := 0x4444444444444444444444444444444444444444 + + // // Call to Contract B + // let resB:= call(gas(), B, 0, 0x0, 0x0, 0, 0) + + // // Call to Contract D + // let resD := call(gas(), D, 0, 0x0, 0x0, 0, 0) + // } + // } + Code: common.FromHex("0x73222222222222222222222222222222222222222273444444444444444444444444444444444444444460006000600060006000865af160006000600060006000865af150505050"), + Balance: common.Big0, + }, + bb: { + // Contract code in YUL: + // + // object "ContractB" { + // code { + // let self := address() + // selfdestruct(self) + // } + // } + Code: common.FromHex("0x3080ff50"), + Balance: eth5, + }, + cc: { + Code: common.FromHex("0x3080ff50"), + Balance: eth1, + }, + dd: { + // Contract code in YUL: + // + // object "ContractD" { + // code { + // let C := 0x3333333333333333333333333333333333333333 + + // // Call to Contract C + // let resC := call(gas(), C, 0, 0x0, 0x0, 0, 0) + + // // Revert + // revert(0, 0) + // } + // } + Code: common.FromHex("0x73333333333333333333333333333333333333333360006000600060006000855af160006000fd5050"), + Balance: eth2, + }, + }, + } + ) + + gspec.Config.TerminalTotalDifficulty = big.NewInt(0) + + signer := types.LatestSigner(gspec.Config) + + testBlockGenerationFunc := func(b *core.BlockGen) { + b.SetPoS() + + txdata := &types.LegacyTx{ + Nonce: 0, + To: &aa, + Value: common.Big0, + Gas: 150000, + GasPrice: gwei5, + Data: []byte{}, + } + + tx := types.NewTx(txdata) + tx, _ = types.SignTx(tx, signer, key1) + + b.AddTx(tx) + } + + output, chain, err := testSupplyTracer(t, gspec, testBlockGenerationFunc) + if err != nil { + t.Fatalf("failed to test supply tracer: %v", err) + } + + // Check balance at state: + // 1. A has 0 ether + // 2. B has 0 ether, burned + // 3. C has 2 ether, selfdestructed but parent D reverted + // 4. D has 1 ether, reverted + statedb, _ := chain.State() + if got, exp := statedb.GetBalance(aa), common.Big0; got.CmpBig(exp) != 0 { + t.Fatalf("address \"%v\" balance, got %v exp %v\n", aa, got, exp) + } + if got, exp := statedb.GetBalance(bb), common.Big0; got.CmpBig(exp) != 0 { + t.Fatalf("address \"%v\" balance, got %v exp %v\n", bb, got, exp) + } + if got, exp := statedb.GetBalance(cc), eth1; got.CmpBig(exp) != 0 { + t.Fatalf("address \"%v\" balance, got %v exp %v\n", bb, got, exp) + } + if got, exp := statedb.GetBalance(dd), eth2; got.CmpBig(exp) != 0 { + t.Fatalf("address \"%v\" balance, got %v exp %v\n", bb, got, exp) + } + + // Check live trace output + block := chain.GetBlockByNumber(1) + + expected := supplyInfo{ + Burn: &supplyInfoBurn{ + EIP1559: (*hexutil.Big)(new(big.Int).Mul(block.BaseFee(), big.NewInt(int64(block.GasUsed())))), + Misc: (*hexutil.Big)(eth5), // 5ETH burned from contract B + }, + Number: 1, + Hash: block.Hash(), + ParentHash: block.ParentHash(), + } + + actual := output[expected.Number] + + compareAsJSON(t, expected, actual) +} + +func testSupplyTracer(t *testing.T, genesis *core.Genesis, gen func(*core.BlockGen)) ([]supplyInfo, *core.BlockChain, error) { + var ( + engine = beacon.New(ethash.NewFaker()) + ) + + traceOutputPath := filepath.ToSlash(t.TempDir()) + traceOutputFilename := path.Join(traceOutputPath, "supply.jsonl") + + // Load supply tracer + tracer, err := tracers.LiveDirectory.New("supply", json.RawMessage(fmt.Sprintf(`{"path":"%s"}`, traceOutputPath))) + if err != nil { + return nil, nil, fmt.Errorf("failed to create call tracer: %v", err) + } + + chain, err := core.NewBlockChain(rawdb.NewMemoryDatabase(), core.DefaultCacheConfigWithScheme(rawdb.PathScheme), genesis, nil, engine, vm.Config{Tracer: tracer}, nil, nil) + if err != nil { + return nil, nil, fmt.Errorf("failed to create tester chain: %v", err) + } + defer chain.Stop() + + _, blocks, _ := core.GenerateChainWithGenesis(genesis, engine, 1, func(i int, b *core.BlockGen) { + b.SetCoinbase(common.Address{1}) + gen(b) + }) + + if n, err := chain.InsertChain(blocks); err != nil { + return nil, chain, fmt.Errorf("block %d: failed to insert into chain: %v", n, err) + } + + // Check and compare the results + file, err := os.OpenFile(traceOutputFilename, os.O_RDONLY, 0666) + if err != nil { + return nil, chain, fmt.Errorf("failed to open output file: %v", err) + } + defer file.Close() + + var output []supplyInfo + scanner := bufio.NewScanner(file) + + for scanner.Scan() { + blockBytes := scanner.Bytes() + + var info supplyInfo + if err := json.Unmarshal(blockBytes, &info); err != nil { + return nil, chain, fmt.Errorf("failed to unmarshal result: %v", err) + } + + output = append(output, info) + } + + return output, chain, nil +} + +func compareAsJSON(t *testing.T, expected interface{}, actual interface{}) { + want, err := json.Marshal(expected) + if err != nil { + t.Fatalf("failed to marshal expected value to JSON: %v", err) + } + + have, err := json.Marshal(actual) + if err != nil { + t.Fatalf("failed to marshal actual value to JSON: %v", err) + } + + if !bytes.Equal(want, have) { + t.Fatalf("incorrect supply info: expected %s, got %s", string(want), string(have)) + } +} diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/frontier_create_outofstorage.json b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/frontier_create_outofstorage.json index c46fe080f7..a9092bbcf0 100644 --- a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/frontier_create_outofstorage.json +++ b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/frontier_create_outofstorage.json @@ -80,8 +80,9 @@ "from": "0x0047a8033cc6d6ca2ed5044674fd421f44884de8", "gas": "0x1b7740", "gasUsed": "0x9274f", + "to": "0xc24431c1a1147456414355b1f1769de450e524da", "input": "0x606060405260018054600160a060020a0319163317905561036f600360609081527f55524c0000000000000000000000000000000000000000000000000000000000608052610120604052604c60a09081527f6a736f6e2868747470733a2f2f6170692e6b72616b656e2e636f6d2f302f707560c0527f626c69632f5469636b65723f706169723d455448584254292e726573756c742e60e0527f58455448585842542e632e3000000000000000000000000000000000000000006101005261037d919062030d417f38cc483100000000000000000000000000000000000000000000000000000000610120908152600090731d11e5eae3112dbd44f99266872ff1d07c77dce89081906338cc4831906101249060209060048188876161da5a03f1156100025750505060405180519060200150600060006101000a815481600160a060020a0302191690830217905550600060009054906101000a9004600160a060020a0316600160a060020a03166338592832600060009054906101000a9004600160a060020a0316600160a060020a0316632ef3accc8887604051837c010000000000000000000000000000000000000000000000000000000002815260040180806020018381526020018281038252848181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156102255780820380516001836020036101000a031916815260200191505b5093505050506020604051808303816000876161da5a03f11561000257505060405180517f385928320000000000000000000000000000000000000000000000000000000082526004828101888152606484018a90526080602485018181528d5160848701528d519496508a958e958e958e9594604484019360a40192909181908490829085908e906020601f850104600302600f01f150905090810190601f1680156102e65780820380516001836020036101000a031916815260200191505b508381038252858181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f16801561033f5780820380516001836020036101000a031916815260200191505b50965050505050505060206040518083038185886185025a03f11561000257505060405151979650505050505050565b611af2806103806000396000f35b5056606060405236156100985760e060020a6000350463056e1059811461009a57806327dc297e14610391578063346b306a146103e257806341c0e1b51461075e578063489306eb146107855780635731f35714610a5e57806365a4dfb314610de05780637975c56e14611179578063a2e6204514611458578063ae152cf414611528578063b77644751461181b578063d594877014611876575b005b60408051602060248035600481810135601f81018590048502860185019096528585526119559581359591946044949293909201918190840183828082843750506040805160209735808a0135601f81018a90048a0283018a019093528282529698976064979196506024919091019450909250829150840183828082843750949650509335935050505060006000731d11e5eae3112dbd44f99266872ff1d07c77dce8905080600160a060020a03166338cc48316040518160e060020a0281526004018090506020604051808303816000876161da5a03f1156100025750505060405180519060200150600060006101000a815481600160a060020a0302191690830217905550600060009054906101000a9004600160a060020a0316600160a060020a03166338592832600060009054906101000a9004600160a060020a0316600160a060020a0316632ef3accc88876040518360e060020a02815260040180806020018381526020018281038252848181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f16801561025e5780820380516001836020036101000a031916815260200191505b5093505050506020604051808303816000876161da5a03f1156100025750505060405180519060200150888888886040518660e060020a0281526004018085815260200180602001806020018481526020018381038352868181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156103085780820380516001836020036101000a031916815260200191505b508381038252858181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156103615780820380516001836020036101000a031916815260200191505b50965050505050505060206040518083038185886185025a03f115610002575050604051519350610dd892505050565b60408051602060248035600481810135601f81018590048502860185019096528585526100989581359591946044949293909201918190840183828082843750949650505050505050611a2761187a565b6040805160206004803580820135601f8101849004840285018401909552848452611955949193602493909291840191908190840183828082843750506040805160208835808b0135601f8101839004830284018301909452838352979998604498929750919091019450909250829150840183828082843750506040805160209735808a0135601f81018a90048a0283018a019093528282529698976064979196506024919091019450909250829150840183828082843750506040805160e060020a6338cc48310281529051959760009750731d11e5eae3112dbd44f99266872ff1d07c77dce8968796506338cc4831955082820194506020935091829003018188876161da5a03f1156100025750505060405180519060200150600060006101000a815481600160a060020a0302191690830217905550600060009054906101000a9004600160a060020a0316600160a060020a03166377228659600060009054906101000a9004600160a060020a0316600160a060020a031663524f3889886040518260e060020a02815260040180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156105d15780820380516001836020036101000a031916815260200191505b50925050506020604051808303816000876161da5a03f115610002575050506040518051906020015060008888886040518660e060020a028152600401808581526020018060200180602001806020018481038452878181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156106795780820380516001836020036101000a031916815260200191505b508481038352868181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156106d25780820380516001836020036101000a031916815260200191505b508481038252858181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f16801561072b5780820380516001836020036101000a031916815260200191505b5097505050505050505060206040518083038185886185025a03f1156100025750506040515193505050505b9392505050565b610098600154600160a060020a03908116339091161415611a255733600160a060020a0316ff5b6040805160206004803580820135601f8101849004840285018401909552848452611955949193602493909291840191908190840183828082843750506040805160208835808b0135601f8101839004830284018301909452838352979998604498929750919091019450909250829150840183828082843750506040805160e060020a6338cc48310281529051959760009750731d11e5eae3112dbd44f99266872ff1d07c77dce8968796506338cc4831955082820194506020935091829003018188876161da5a03f1156100025750505060405180519060200150600060006101000a815481600160a060020a0302191690830217905550600060009054906101000a9004600160a060020a0316600160a060020a031663adf59f99600060009054906101000a9004600160a060020a0316600160a060020a031663524f3889876040518260e060020a02815260040180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156109345780820380516001836020036101000a031916815260200191505b50925050506020604051808303816000876161da5a03f1156100025750505060405180519060200150600087876040518560e060020a0281526004018084815260200180602001806020018381038352858181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156109d75780820380516001836020036101000a031916815260200191505b508381038252848181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f168015610a305780820380516001836020036101000a031916815260200191505b509550505050505060206040518083038185886185025a03f115610002575050604051519695505050505050565b60408051602060248035600481810135601f81018590048502860185019096528585526119559581359591946044949293909201918190840183828082843750506040805160209735808a0135601f81018a90048a0283018a019093528282529698976064979196506024919091019450909250829150840183828082843750506040805160209735808a0135601f81018a90048a0283018a019093528282529698976084979196506024919091019450909250829150840183828082843750506040805160e060020a6338cc48310281529051959760009750731d11e5eae3112dbd44f99266872ff1d07c77dce8968796506338cc4831955082820194506020935091829003018188876161da5a03f1156100025750505060405180519060200150600060006101000a815481600160a060020a0302191690830217905550600060009054906101000a9004600160a060020a0316600160a060020a03166377228659600060009054906101000a9004600160a060020a0316600160a060020a031663524f3889886040518260e060020a02815260040180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f168015610c535780820380516001836020036101000a031916815260200191505b50925050506020604051808303816000876161da5a03f1156100025750505060405180519060200150888888886040518660e060020a028152600401808581526020018060200180602001806020018481038452878181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f168015610cfa5780820380516001836020036101000a031916815260200191505b508481038352868181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f168015610d535780820380516001836020036101000a031916815260200191505b508481038252858181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f168015610dac5780820380516001836020036101000a031916815260200191505b5097505050505050505060206040518083038185886185025a03f1156100025750506040515193505050505b949350505050565b60408051602060248035600481810135601f81018590048502860185019096528585526119559581359591946044949293909201918190840183828082843750506040805160209735808a0135601f81018a90048a0283018a019093528282529698976064979196506024919091019450909250829150840183828082843750506040805160209735808a0135601f81018a90048a0283018a019093528282529698976084979196506024919091019450909250829150840183828082843750949650509335935050505060006000731d11e5eae3112dbd44f99266872ff1d07c77dce8905080600160a060020a03166338cc48316040518160e060020a0281526004018090506020604051808303816000876161da5a03f1156100025750505060405180519060200150600060006101000a815481600160a060020a0302191690830217905550600060009054906101000a9004600160a060020a0316600160a060020a031663fbf80418600060009054906101000a9004600160a060020a0316600160a060020a0316632ef3accc89876040518360e060020a02815260040180806020018381526020018281038252848181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f168015610fe45780820380516001836020036101000a031916815260200191505b5093505050506020604051808303816000876161da5a03f115610002575050506040518051906020015089898989896040518760e060020a028152600401808681526020018060200180602001806020018581526020018481038452888181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156110935780820380516001836020036101000a031916815260200191505b508481038352878181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156110ec5780820380516001836020036101000a031916815260200191505b508481038252868181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156111455780820380516001836020036101000a031916815260200191505b509850505050505050505060206040518083038185886185025a03f115610002575050604051519998505050505050505050565b60408051602060248035600481810135601f81018590048502860185019096528585526119559581359591946044949293909201918190840183828082843750506040805160209735808a0135601f81018a90048a0283018a019093528282529698976064979196506024919091019450909250829150840183828082843750506040805160e060020a6338cc48310281529051959760009750731d11e5eae3112dbd44f99266872ff1d07c77dce8968796506338cc4831955082820194506020935091829003018188876161da5a03f1156100025750505060405180519060200150600060006101000a815481600160a060020a0302191690830217905550600060009054906101000a9004600160a060020a0316600160a060020a031663adf59f99600060009054906101000a9004600160a060020a0316600160a060020a031663524f3889876040518260e060020a02815260040180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f16801561132e5780820380516001836020036101000a031916815260200191505b50925050506020604051808303816000876161da5a03f11561000257505050604051805190602001508787876040518560e060020a0281526004018084815260200180602001806020018381038352858181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156113d05780820380516001836020036101000a031916815260200191505b508381038252848181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156114295780820380516001836020036101000a031916815260200191505b509550505050505060206040518083038185886185025a03f11561000257505060405151935061075792505050565b6100985b611aef604060405190810160405280600381526020017f55524c0000000000000000000000000000000000000000000000000000000000815260200150608060405190810160405280604c81526020017f6a736f6e2868747470733a2f2f6170692e6b72616b656e2e636f6d2f302f707581526020017f626c69632f5469636b65723f706169723d455448584254292e726573756c742e81526020017f58455448585842542e632e30000000000000000000000000000000000000000081526020015062030d416115ae565b6040805160206004803580820135601f8101849004840285018401909552848452611955949193602493909291840191908190840183828082843750506040805160208835808b0135601f810183900483028401830190945283835297999860449892975091909101945090925082915084018382808284375094965050933593505050505b60006000731d11e5eae3112dbd44f99266872ff1d07c77dce8905080600160a060020a03166338cc48316040518160e060020a0281526004018090506020604051808303816000876161da5a03f1156100025750505060405180519060200150600060006101000a815481600160a060020a0302191690830217905550600060009054906101000a9004600160a060020a0316600160a060020a03166338592832600060009054906101000a9004600160a060020a0316600160a060020a0316632ef3accc88876040518360e060020a02815260040180806020018381526020018281038252848181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156116e75780820380516001836020036101000a031916815260200191505b5093505050506020604051808303816000876161da5a03f115610002575050506040518051906020015060008888886040518660e060020a0281526004018085815260200180602001806020018481526020018381038352868181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156117925780820380516001836020036101000a031916815260200191505b508381038252858181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156117eb5780820380516001836020036101000a031916815260200191505b50965050505050505060206040518083038185886185025a03f11561000257505060405151935061075792505050565b6040805160028054602060018216156101000260001901909116829004601f81018290048202840182019094528383526119679390830182828015611a1d5780601f106119f257610100808354040283529160200191611a1d565b6119d55b60006000731d11e5eae3112dbd44f99266872ff1d07c77dce8905080600160a060020a03166338cc48316040518160e060020a0281526004018090506020604051808303816000876161da5a03f115610002575050604080518051855473ffffffffffffffffffffffffffffffffffffffff1916178086557f4c7737950000000000000000000000000000000000000000000000000000000082529151600160a060020a03929092169250634c773795916004828101926020929190829003018188876161da5a03f115610002575050604051519250505090565b60408051918252519081900360200190f35b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156119c75780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b60408051600160a060020a03929092168252519081900360200190f35b820191906000526020600020905b815481529060010190602001808311611a0057829003601f168201915b505050505081565b565b600160a060020a031633600160a060020a0316141515611a4657610002565b8060026000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10611aad57805160ff19168380011785555b50611add9291505b80821115611ae75760008155600101611a99565b82800160010185558215611a91579182015b82811115611a91578251826000505591602001919060010190611abf565b5050611aeb61145c565b5090565b5050565b5056", - "error": "contract creation code storage out of gas", + "output": "0x606060405236156100985760e060020a6000350463056e1059811461009a57806327dc297e14610391578063346b306a146103e257806341c0e1b51461075e578063489306eb146107855780635731f35714610a5e57806365a4dfb314610de05780637975c56e14611179578063a2e6204514611458578063ae152cf414611528578063b77644751461181b578063d594877014611876575b005b60408051602060248035600481810135601f81018590048502860185019096528585526119559581359591946044949293909201918190840183828082843750506040805160209735808a0135601f81018a90048a0283018a019093528282529698976064979196506024919091019450909250829150840183828082843750949650509335935050505060006000731d11e5eae3112dbd44f99266872ff1d07c77dce8905080600160a060020a03166338cc48316040518160e060020a0281526004018090506020604051808303816000876161da5a03f1156100025750505060405180519060200150600060006101000a815481600160a060020a0302191690830217905550600060009054906101000a9004600160a060020a0316600160a060020a03166338592832600060009054906101000a9004600160a060020a0316600160a060020a0316632ef3accc88876040518360e060020a02815260040180806020018381526020018281038252848181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f16801561025e5780820380516001836020036101000a031916815260200191505b5093505050506020604051808303816000876161da5a03f1156100025750505060405180519060200150888888886040518660e060020a0281526004018085815260200180602001806020018481526020018381038352868181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156103085780820380516001836020036101000a031916815260200191505b508381038252858181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156103615780820380516001836020036101000a031916815260200191505b50965050505050505060206040518083038185886185025a03f115610002575050604051519350610dd892505050565b60408051602060248035600481810135601f81018590048502860185019096528585526100989581359591946044949293909201918190840183828082843750949650505050505050611a2761187a565b6040805160206004803580820135601f8101849004840285018401909552848452611955949193602493909291840191908190840183828082843750506040805160208835808b0135601f8101839004830284018301909452838352979998604498929750919091019450909250829150840183828082843750506040805160209735808a0135601f81018a90048a0283018a019093528282529698976064979196506024919091019450909250829150840183828082843750506040805160e060020a6338cc48310281529051959760009750731d11e5eae3112dbd44f99266872ff1d07c77dce8968796506338cc4831955082820194506020935091829003018188876161da5a03f1156100025750505060405180519060200150600060006101000a815481600160a060020a0302191690830217905550600060009054906101000a9004600160a060020a0316600160a060020a03166377228659600060009054906101000a9004600160a060020a0316600160a060020a031663524f3889886040518260e060020a02815260040180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156105d15780820380516001836020036101000a031916815260200191505b50925050506020604051808303816000876161da5a03f115610002575050506040518051906020015060008888886040518660e060020a028152600401808581526020018060200180602001806020018481038452878181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156106795780820380516001836020036101000a031916815260200191505b508481038352868181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156106d25780820380516001836020036101000a031916815260200191505b508481038252858181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f16801561072b5780820380516001836020036101000a031916815260200191505b5097505050505050505060206040518083038185886185025a03f1156100025750506040515193505050505b9392505050565b610098600154600160a060020a03908116339091161415611a255733600160a060020a0316ff5b6040805160206004803580820135601f8101849004840285018401909552848452611955949193602493909291840191908190840183828082843750506040805160208835808b0135601f8101839004830284018301909452838352979998604498929750919091019450909250829150840183828082843750506040805160e060020a6338cc48310281529051959760009750731d11e5eae3112dbd44f99266872ff1d07c77dce8968796506338cc4831955082820194506020935091829003018188876161da5a03f1156100025750505060405180519060200150600060006101000a815481600160a060020a0302191690830217905550600060009054906101000a9004600160a060020a0316600160a060020a031663adf59f99600060009054906101000a9004600160a060020a0316600160a060020a031663524f3889876040518260e060020a02815260040180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156109345780820380516001836020036101000a031916815260200191505b50925050506020604051808303816000876161da5a03f1156100025750505060405180519060200150600087876040518560e060020a0281526004018084815260200180602001806020018381038352858181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156109d75780820380516001836020036101000a031916815260200191505b508381038252848181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f168015610a305780820380516001836020036101000a031916815260200191505b509550505050505060206040518083038185886185025a03f115610002575050604051519695505050505050565b60408051602060248035600481810135601f81018590048502860185019096528585526119559581359591946044949293909201918190840183828082843750506040805160209735808a0135601f81018a90048a0283018a019093528282529698976064979196506024919091019450909250829150840183828082843750506040805160209735808a0135601f81018a90048a0283018a019093528282529698976084979196506024919091019450909250829150840183828082843750506040805160e060020a6338cc48310281529051959760009750731d11e5eae3112dbd44f99266872ff1d07c77dce8968796506338cc4831955082820194506020935091829003018188876161da5a03f1156100025750505060405180519060200150600060006101000a815481600160a060020a0302191690830217905550600060009054906101000a9004600160a060020a0316600160a060020a03166377228659600060009054906101000a9004600160a060020a0316600160a060020a031663524f3889886040518260e060020a02815260040180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f168015610c535780820380516001836020036101000a031916815260200191505b50925050506020604051808303816000876161da5a03f1156100025750505060405180519060200150888888886040518660e060020a028152600401808581526020018060200180602001806020018481038452878181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f168015610cfa5780820380516001836020036101000a031916815260200191505b508481038352868181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f168015610d535780820380516001836020036101000a031916815260200191505b508481038252858181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f168015610dac5780820380516001836020036101000a031916815260200191505b5097505050505050505060206040518083038185886185025a03f1156100025750506040515193505050505b949350505050565b60408051602060248035600481810135601f81018590048502860185019096528585526119559581359591946044949293909201918190840183828082843750506040805160209735808a0135601f81018a90048a0283018a019093528282529698976064979196506024919091019450909250829150840183828082843750506040805160209735808a0135601f81018a90048a0283018a019093528282529698976084979196506024919091019450909250829150840183828082843750949650509335935050505060006000731d11e5eae3112dbd44f99266872ff1d07c77dce8905080600160a060020a03166338cc48316040518160e060020a0281526004018090506020604051808303816000876161da5a03f1156100025750505060405180519060200150600060006101000a815481600160a060020a0302191690830217905550600060009054906101000a9004600160a060020a0316600160a060020a031663fbf80418600060009054906101000a9004600160a060020a0316600160a060020a0316632ef3accc89876040518360e060020a02815260040180806020018381526020018281038252848181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f168015610fe45780820380516001836020036101000a031916815260200191505b5093505050506020604051808303816000876161da5a03f115610002575050506040518051906020015089898989896040518760e060020a028152600401808681526020018060200180602001806020018581526020018481038452888181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156110935780820380516001836020036101000a031916815260200191505b508481038352878181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156110ec5780820380516001836020036101000a031916815260200191505b508481038252868181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156111455780820380516001836020036101000a031916815260200191505b509850505050505050505060206040518083038185886185025a03f115610002575050604051519998505050505050505050565b60408051602060248035600481810135601f81018590048502860185019096528585526119559581359591946044949293909201918190840183828082843750506040805160209735808a0135601f81018a90048a0283018a019093528282529698976064979196506024919091019450909250829150840183828082843750506040805160e060020a6338cc48310281529051959760009750731d11e5eae3112dbd44f99266872ff1d07c77dce8968796506338cc4831955082820194506020935091829003018188876161da5a03f1156100025750505060405180519060200150600060006101000a815481600160a060020a0302191690830217905550600060009054906101000a9004600160a060020a0316600160a060020a031663adf59f99600060009054906101000a9004600160a060020a0316600160a060020a031663524f3889876040518260e060020a02815260040180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f16801561132e5780820380516001836020036101000a031916815260200191505b50925050506020604051808303816000876161da5a03f11561000257505050604051805190602001508787876040518560e060020a0281526004018084815260200180602001806020018381038352858181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156113d05780820380516001836020036101000a031916815260200191505b508381038252848181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156114295780820380516001836020036101000a031916815260200191505b509550505050505060206040518083038185886185025a03f11561000257505060405151935061075792505050565b6100985b611aef604060405190810160405280600381526020017f55524c0000000000000000000000000000000000000000000000000000000000815260200150608060405190810160405280604c81526020017f6a736f6e2868747470733a2f2f6170692e6b72616b656e2e636f6d2f302f707581526020017f626c69632f5469636b65723f706169723d455448584254292e726573756c742e81526020017f58455448585842542e632e30000000000000000000000000000000000000000081526020015062030d416115ae565b6040805160206004803580820135601f8101849004840285018401909552848452611955949193602493909291840191908190840183828082843750506040805160208835808b0135601f810183900483028401830190945283835297999860449892975091909101945090925082915084018382808284375094965050933593505050505b60006000731d11e5eae3112dbd44f99266872ff1d07c77dce8905080600160a060020a03166338cc48316040518160e060020a0281526004018090506020604051808303816000876161da5a03f1156100025750505060405180519060200150600060006101000a815481600160a060020a0302191690830217905550600060009054906101000a9004600160a060020a0316600160a060020a03166338592832600060009054906101000a9004600160a060020a0316600160a060020a0316632ef3accc88876040518360e060020a02815260040180806020018381526020018281038252848181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156116e75780820380516001836020036101000a031916815260200191505b5093505050506020604051808303816000876161da5a03f115610002575050506040518051906020015060008888886040518660e060020a0281526004018085815260200180602001806020018481526020018381038352868181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156117925780820380516001836020036101000a031916815260200191505b508381038252858181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156117eb5780820380516001836020036101000a031916815260200191505b50965050505050505060206040518083038185886185025a03f11561000257505060405151935061075792505050565b6040805160028054602060018216156101000260001901909116829004601f81018290048202840182019094528383526119679390830182828015611a1d5780601f106119f257610100808354040283529160200191611a1d565b6119d55b60006000731d11e5eae3112dbd44f99266872ff1d07c77dce8905080600160a060020a03166338cc48316040518160e060020a0281526004018090506020604051808303816000876161da5a03f115610002575050604080518051855473ffffffffffffffffffffffffffffffffffffffff1916178086557f4c7737950000000000000000000000000000000000000000000000000000000082529151600160a060020a03929092169250634c773795916004828101926020929190829003018188876161da5a03f115610002575050604051519250505090565b60408051918252519081900360200190f35b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156119c75780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b60408051600160a060020a03929092168252519081900360200190f35b820191906000526020600020905b815481529060010190602001808311611a0057829003601f168201915b505050505081565b565b600160a060020a031633600160a060020a0316141515611a4657610002565b8060026000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10611aad57805160ff19168380011785555b50611add9291505b80821115611ae75760008155600101611a99565b82800160010185558215611a91579182015b82811115611a91578251826000505591602001919060010190611abf565b5050611aeb61145c565b5090565b5050565b5056", "calls": [ { "from": "0xc24431c1a1147456414355b1f1769de450e524da", diff --git a/eth/tracers/live/gen_supplyinfoburn.go b/eth/tracers/live/gen_supplyinfoburn.go new file mode 100644 index 0000000000..d01eda3975 --- /dev/null +++ b/eth/tracers/live/gen_supplyinfoburn.go @@ -0,0 +1,49 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package live + +import ( + "encoding/json" + "math/big" + + "github.com/ethereum/go-ethereum/common/hexutil" +) + +var _ = (*supplyInfoBurnMarshaling)(nil) + +// MarshalJSON marshals as JSON. +func (s supplyInfoBurn) MarshalJSON() ([]byte, error) { + type supplyInfoBurn struct { + EIP1559 *hexutil.Big `json:"1559,omitempty"` + Blob *hexutil.Big `json:"blob,omitempty"` + Misc *hexutil.Big `json:"misc,omitempty"` + } + var enc supplyInfoBurn + enc.EIP1559 = (*hexutil.Big)(s.EIP1559) + enc.Blob = (*hexutil.Big)(s.Blob) + enc.Misc = (*hexutil.Big)(s.Misc) + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (s *supplyInfoBurn) UnmarshalJSON(input []byte) error { + type supplyInfoBurn struct { + EIP1559 *hexutil.Big `json:"1559,omitempty"` + Blob *hexutil.Big `json:"blob,omitempty"` + Misc *hexutil.Big `json:"misc,omitempty"` + } + var dec supplyInfoBurn + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.EIP1559 != nil { + s.EIP1559 = (*big.Int)(dec.EIP1559) + } + if dec.Blob != nil { + s.Blob = (*big.Int)(dec.Blob) + } + if dec.Misc != nil { + s.Misc = (*big.Int)(dec.Misc) + } + return nil +} diff --git a/eth/tracers/live/gen_supplyinfoissuance.go b/eth/tracers/live/gen_supplyinfoissuance.go new file mode 100644 index 0000000000..e2536ee325 --- /dev/null +++ b/eth/tracers/live/gen_supplyinfoissuance.go @@ -0,0 +1,49 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package live + +import ( + "encoding/json" + "math/big" + + "github.com/ethereum/go-ethereum/common/hexutil" +) + +var _ = (*supplyInfoIssuanceMarshaling)(nil) + +// MarshalJSON marshals as JSON. +func (s supplyInfoIssuance) MarshalJSON() ([]byte, error) { + type supplyInfoIssuance struct { + GenesisAlloc *hexutil.Big `json:"genesisAlloc,omitempty"` + Reward *hexutil.Big `json:"reward,omitempty"` + Withdrawals *hexutil.Big `json:"withdrawals,omitempty"` + } + var enc supplyInfoIssuance + enc.GenesisAlloc = (*hexutil.Big)(s.GenesisAlloc) + enc.Reward = (*hexutil.Big)(s.Reward) + enc.Withdrawals = (*hexutil.Big)(s.Withdrawals) + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (s *supplyInfoIssuance) UnmarshalJSON(input []byte) error { + type supplyInfoIssuance struct { + GenesisAlloc *hexutil.Big `json:"genesisAlloc,omitempty"` + Reward *hexutil.Big `json:"reward,omitempty"` + Withdrawals *hexutil.Big `json:"withdrawals,omitempty"` + } + var dec supplyInfoIssuance + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.GenesisAlloc != nil { + s.GenesisAlloc = (*big.Int)(dec.GenesisAlloc) + } + if dec.Reward != nil { + s.Reward = (*big.Int)(dec.Reward) + } + if dec.Withdrawals != nil { + s.Withdrawals = (*big.Int)(dec.Withdrawals) + } + return nil +} diff --git a/eth/tracers/live/supply.go b/eth/tracers/live/supply.go new file mode 100644 index 0000000000..96f7059454 --- /dev/null +++ b/eth/tracers/live/supply.go @@ -0,0 +1,308 @@ +package live + +import ( + "encoding/json" + "errors" + "fmt" + "math/big" + "path/filepath" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/consensus/misc/eip4844" + "github.com/ethereum/go-ethereum/core/tracing" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth/tracers" + "github.com/ethereum/go-ethereum/log" + "gopkg.in/natefinch/lumberjack.v2" +) + +func init() { + tracers.LiveDirectory.Register("supply", newSupply) +} + +type supplyInfoIssuance struct { + GenesisAlloc *big.Int `json:"genesisAlloc,omitempty"` + Reward *big.Int `json:"reward,omitempty"` + Withdrawals *big.Int `json:"withdrawals,omitempty"` +} + +//go:generate go run github.com/fjl/gencodec -type supplyInfoIssuance -field-override supplyInfoIssuanceMarshaling -out gen_supplyinfoissuance.go +type supplyInfoIssuanceMarshaling struct { + GenesisAlloc *hexutil.Big + Reward *hexutil.Big + Withdrawals *hexutil.Big +} + +type supplyInfoBurn struct { + EIP1559 *big.Int `json:"1559,omitempty"` + Blob *big.Int `json:"blob,omitempty"` + Misc *big.Int `json:"misc,omitempty"` +} + +//go:generate go run github.com/fjl/gencodec -type supplyInfoBurn -field-override supplyInfoBurnMarshaling -out gen_supplyinfoburn.go +type supplyInfoBurnMarshaling struct { + EIP1559 *hexutil.Big + Blob *hexutil.Big + Misc *hexutil.Big +} + +type supplyInfo struct { + Issuance *supplyInfoIssuance `json:"issuance,omitempty"` + Burn *supplyInfoBurn `json:"burn,omitempty"` + + // Block info + Number uint64 `json:"blockNumber"` + Hash common.Hash `json:"hash"` + ParentHash common.Hash `json:"parentHash"` +} + +type supplyTxCallstack struct { + calls []supplyTxCallstack + burn *big.Int +} + +type supply struct { + delta supplyInfo + txCallstack []supplyTxCallstack // Callstack for current transaction + logger *lumberjack.Logger +} + +type supplyTracerConfig struct { + Path string `json:"path"` // Path to the directory where the tracer logs will be stored + MaxSize int `json:"maxSize"` // MaxSize is the maximum size in megabytes of the tracer log file before it gets rotated. It defaults to 100 megabytes. +} + +func newSupply(cfg json.RawMessage) (*tracing.Hooks, error) { + var config supplyTracerConfig + if cfg != nil { + if err := json.Unmarshal(cfg, &config); err != nil { + return nil, fmt.Errorf("failed to parse config: %v", err) + } + } + if config.Path == "" { + return nil, errors.New("supply tracer output path is required") + } + + // Store traces in a rotating file + logger := &lumberjack.Logger{ + Filename: filepath.Join(config.Path, "supply.jsonl"), + } + if config.MaxSize > 0 { + logger.MaxSize = config.MaxSize + } + + t := &supply{ + delta: newSupplyInfo(), + logger: logger, + } + return &tracing.Hooks{ + OnBlockStart: t.OnBlockStart, + OnBlockEnd: t.OnBlockEnd, + OnGenesisBlock: t.OnGenesisBlock, + OnTxStart: t.OnTxStart, + OnBalanceChange: t.OnBalanceChange, + OnEnter: t.OnEnter, + OnExit: t.OnExit, + OnClose: t.OnClose, + }, nil +} + +func newSupplyInfo() supplyInfo { + return supplyInfo{ + Issuance: &supplyInfoIssuance{ + GenesisAlloc: big.NewInt(0), + Reward: big.NewInt(0), + Withdrawals: big.NewInt(0), + }, + Burn: &supplyInfoBurn{ + EIP1559: big.NewInt(0), + Blob: big.NewInt(0), + Misc: big.NewInt(0), + }, + + Number: 0, + Hash: common.Hash{}, + ParentHash: common.Hash{}, + } +} + +func (s *supply) resetDelta() { + s.delta = newSupplyInfo() +} + +func (s *supply) OnBlockStart(ev tracing.BlockEvent) { + s.resetDelta() + + s.delta.Number = ev.Block.NumberU64() + s.delta.Hash = ev.Block.Hash() + s.delta.ParentHash = ev.Block.ParentHash() + + // Calculate Burn for this block + if ev.Block.BaseFee() != nil { + burn := new(big.Int).Mul(new(big.Int).SetUint64(ev.Block.GasUsed()), ev.Block.BaseFee()) + s.delta.Burn.EIP1559 = burn + } + // Blob burnt gas + if blobGas := ev.Block.BlobGasUsed(); blobGas != nil && *blobGas > 0 && ev.Block.ExcessBlobGas() != nil { + var ( + excess = *ev.Block.ExcessBlobGas() + baseFee = eip4844.CalcBlobFee(excess) + burn = new(big.Int).Mul(new(big.Int).SetUint64(*blobGas), baseFee) + ) + s.delta.Burn.Blob = burn + } +} + +func (s *supply) OnBlockEnd(err error) { + s.write(s.delta) +} + +func (s *supply) OnGenesisBlock(b *types.Block, alloc types.GenesisAlloc) { + s.resetDelta() + + s.delta.Number = b.NumberU64() + s.delta.Hash = b.Hash() + s.delta.ParentHash = b.ParentHash() + + // Initialize supply with total allocation in genesis block + for _, account := range alloc { + s.delta.Issuance.GenesisAlloc.Add(s.delta.Issuance.GenesisAlloc, account.Balance) + } + + s.write(s.delta) +} + +func (s *supply) OnBalanceChange(a common.Address, prevBalance, newBalance *big.Int, reason tracing.BalanceChangeReason) { + diff := new(big.Int).Sub(newBalance, prevBalance) + + // NOTE: don't handle "BalanceIncreaseGenesisBalance" because it is handled in OnGenesisBlock + switch reason { + case tracing.BalanceIncreaseRewardMineUncle: + case tracing.BalanceIncreaseRewardMineBlock: + s.delta.Issuance.Reward.Add(s.delta.Issuance.Reward, diff) + case tracing.BalanceIncreaseWithdrawal: + s.delta.Issuance.Withdrawals.Add(s.delta.Issuance.Withdrawals, diff) + case tracing.BalanceDecreaseSelfdestructBurn: + // BalanceDecreaseSelfdestructBurn is non-reversible as it happens + // at the end of the transaction. + s.delta.Burn.Misc.Sub(s.delta.Burn.Misc, diff) + default: + return + } +} + +func (s *supply) OnTxStart(vm *tracing.VMContext, tx *types.Transaction, from common.Address) { + s.txCallstack = make([]supplyTxCallstack, 0, 1) +} + +// internalTxsHandler handles internal transactions burned amount +func (s *supply) internalTxsHandler(call *supplyTxCallstack) { + // Handle Burned amount + if call.burn != nil { + s.delta.Burn.Misc.Add(s.delta.Burn.Misc, call.burn) + } + + // Recursively handle internal calls + for _, call := range call.calls { + callCopy := call + s.internalTxsHandler(&callCopy) + } +} + +func (s *supply) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { + call := supplyTxCallstack{ + calls: make([]supplyTxCallstack, 0), + } + + // This is a special case of burned amount which has to be handled here + // which happens when type == selfdestruct and from == to. + if vm.OpCode(typ) == vm.SELFDESTRUCT && from == to && value.Cmp(common.Big0) == 1 { + call.burn = value + } + + // Append call to the callstack, so we can fill the details in CaptureExit + s.txCallstack = append(s.txCallstack, call) +} + +func (s *supply) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) { + if depth == 0 { + // No need to handle Burned amount if transaction is reverted + if !reverted { + s.internalTxsHandler(&s.txCallstack[0]) + } + return + } + + size := len(s.txCallstack) + if size <= 1 { + return + } + // Pop call + call := s.txCallstack[size-1] + s.txCallstack = s.txCallstack[:size-1] + size -= 1 + + // In case of a revert, we can drop the call and all its subcalls. + // Caution, that this has to happen after popping the call from the stack. + if reverted { + return + } + s.txCallstack[size-1].calls = append(s.txCallstack[size-1].calls, call) +} + +func (s *supply) OnClose() { + if err := s.logger.Close(); err != nil { + log.Warn("failed to close supply tracer log file", "error", err) + } +} + +func (s *supply) write(data any) { + supply, ok := data.(supplyInfo) + if !ok { + log.Warn("failed to cast supply tracer data on write to log file") + return + } + + // Remove empty fields + if supply.Issuance.GenesisAlloc.Sign() == 0 { + supply.Issuance.GenesisAlloc = nil + } + + if supply.Issuance.Reward.Sign() == 0 { + supply.Issuance.Reward = nil + } + + if supply.Issuance.Withdrawals.Sign() == 0 { + supply.Issuance.Withdrawals = nil + } + + if supply.Issuance.GenesisAlloc == nil && supply.Issuance.Reward == nil && supply.Issuance.Withdrawals == nil { + supply.Issuance = nil + } + + if supply.Burn.EIP1559.Sign() == 0 { + supply.Burn.EIP1559 = nil + } + + if supply.Burn.Blob.Sign() == 0 { + supply.Burn.Blob = nil + } + + if supply.Burn.Misc.Sign() == 0 { + supply.Burn.Misc = nil + } + + if supply.Burn.EIP1559 == nil && supply.Burn.Blob == nil && supply.Burn.Misc == nil { + supply.Burn = nil + } + + out, _ := json.Marshal(supply) + if _, err := s.logger.Write(out); err != nil { + log.Warn("failed to write to supply tracer log file", "error", err) + } + if _, err := s.logger.Write([]byte{'\n'}); err != nil { + log.Warn("failed to write to supply tracer log file", "error", err) + } +} diff --git a/eth/tracers/logger/logger_json.go b/eth/tracers/logger/logger_json.go index a1ec322bcc..be63cd87f9 100644 --- a/eth/tracers/logger/logger_json.go +++ b/eth/tracers/logger/logger_json.go @@ -58,6 +58,7 @@ type jsonLogger struct { encoder *json.Encoder cfg *Config env *tracing.VMContext + hooks *tracing.Hooks } // NewJSONLogger creates a new EVM tracer that prints execution steps as JSON objects @@ -67,12 +68,14 @@ func NewJSONLogger(cfg *Config, writer io.Writer) *tracing.Hooks { if l.cfg == nil { l.cfg = &Config{} } - return &tracing.Hooks{ - OnTxStart: l.OnTxStart, - OnExit: l.OnExit, - OnOpcode: l.OnOpcode, - OnFault: l.OnFault, + l.hooks = &tracing.Hooks{ + OnTxStart: l.OnTxStart, + OnSystemCallStart: l.onSystemCallStart, + OnExit: l.OnEnd, + OnOpcode: l.OnOpcode, + OnFault: l.OnFault, } + return l.hooks } // NewJSONLoggerWithCallFrames creates a new EVM tracer that prints execution steps as JSON objects @@ -82,13 +85,15 @@ func NewJSONLoggerWithCallFrames(cfg *Config, writer io.Writer) *tracing.Hooks { if l.cfg == nil { l.cfg = &Config{} } - return &tracing.Hooks{ - OnTxStart: l.OnTxStart, - OnEnter: l.OnEnter, - OnExit: l.OnExit, - OnOpcode: l.OnOpcode, - OnFault: l.OnFault, + l.hooks = &tracing.Hooks{ + OnTxStart: l.OnTxStart, + OnSystemCallStart: l.onSystemCallStart, + OnEnter: l.OnEnter, + OnExit: l.OnExit, + OnOpcode: l.OnOpcode, + OnFault: l.OnFault, } + return l.hooks } func (l *jsonLogger) OnFault(pc uint64, op byte, gas uint64, cost uint64, scope tracing.OpContext, depth int, err error) { @@ -122,6 +127,16 @@ func (l *jsonLogger) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracin l.encoder.Encode(log) } +func (l *jsonLogger) onSystemCallStart() { + // Process no events while in system call. + hooks := *l.hooks + *l.hooks = tracing.Hooks{ + OnSystemCallEnd: func() { + *l.hooks = hooks + }, + } +} + // OnEnter is not enabled by default. func (l *jsonLogger) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { frame := callFrame{ diff --git a/eth/tracers/native/call.go b/eth/tracers/native/call.go index 68992d8338..84750df1de 100644 --- a/eth/tracers/native/call.go +++ b/eth/tracers/native/call.go @@ -74,6 +74,11 @@ func (f callFrame) failed() bool { func (f *callFrame) processOutput(output []byte, err error, reverted bool) { output = common.CopyBytes(output) + // Clear error if tx wasn't reverted. This happened + // for pre-homestead contract storage OOG. + if err != nil && !reverted { + err = nil + } if err == nil { f.Output = output return diff --git a/eth/tracers/native/call_flat.go b/eth/tracers/native/call_flat.go index c20d0b864c..3b158ddb1b 100644 --- a/eth/tracers/native/call_flat.go +++ b/eth/tracers/native/call_flat.go @@ -23,6 +23,7 @@ import ( "math/big" "slices" "strings" + "sync/atomic" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -114,7 +115,7 @@ type flatCallTracer struct { tracer *callTracer config flatCallTracerConfig ctx *tracers.Context // Holds tracer context data - reason error // Textual reason for the interruption + interrupt atomic.Bool // Atomic flag to signal execution interruption activePrecompiles []common.Address // Updated on tx start based on given rules } @@ -154,6 +155,9 @@ func newFlatCallTracer(ctx *tracers.Context, cfg json.RawMessage) (*tracers.Trac // OnEnter is called when EVM enters a new scope (via call, create or selfdestruct). func (t *flatCallTracer) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { + if t.interrupt.Load() { + return + } t.tracer.OnEnter(depth, typ, from, to, input, gas, value) if depth == 0 { @@ -169,6 +173,9 @@ func (t *flatCallTracer) OnEnter(depth int, typ byte, from common.Address, to co // OnExit is called when EVM exits a scope, even if the scope didn't // execute any code. func (t *flatCallTracer) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) { + if t.interrupt.Load() { + return + } t.tracer.OnExit(depth, output, gasUsed, err, reverted) if depth == 0 { @@ -194,6 +201,9 @@ func (t *flatCallTracer) OnExit(depth int, output []byte, gasUsed uint64, err er } func (t *flatCallTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) { + if t.interrupt.Load() { + return + } t.tracer.OnTxStart(env, tx, from) // Update list of precompiles based on current block rules := env.ChainConfig.Rules(env.BlockNumber, env.Random != nil, env.Time) @@ -201,6 +211,9 @@ func (t *flatCallTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction } func (t *flatCallTracer) OnTxEnd(receipt *types.Receipt, err error) { + if t.interrupt.Load() { + return + } t.tracer.OnTxEnd(receipt, err) } @@ -223,12 +236,13 @@ func (t *flatCallTracer) GetResult() (json.RawMessage, error) { if err != nil { return nil, err } - return res, t.reason + return res, t.tracer.reason } // Stop terminates execution of the tracer at the first opportune moment. func (t *flatCallTracer) Stop(err error) { t.tracer.Stop(err) + t.interrupt.Store(true) } // isPrecompiled returns whether the addr is a precompile. @@ -264,16 +278,14 @@ func flatFromNested(input *callFrame, traceAddress []int, convertErrs bool, ctx } output = append(output, *frame) - if len(input.Calls) > 0 { - for i, childCall := range input.Calls { - childAddr := childTraceAddress(traceAddress, i) - childCallCopy := childCall - flat, err := flatFromNested(&childCallCopy, childAddr, convertErrs, ctx) - if err != nil { - return nil, err - } - output = append(output, flat...) + for i, childCall := range input.Calls { + childAddr := childTraceAddress(traceAddress, i) + childCallCopy := childCall + flat, err := flatFromNested(&childCallCopy, childAddr, convertErrs, ctx) + if err != nil { + return nil, err } + output = append(output, flat...) } return output, nil diff --git a/eth/tracers/native/call_flat_test.go b/eth/tracers/native/call_flat_test.go new file mode 100644 index 0000000000..d5481b868b --- /dev/null +++ b/eth/tracers/native/call_flat_test.go @@ -0,0 +1,64 @@ +// Copyright 2024 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package native_test + +import ( + "errors" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/tracing" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth/tracers" + "github.com/ethereum/go-ethereum/params" + "github.com/stretchr/testify/require" +) + +func TestCallFlatStop(t *testing.T) { + tracer, err := tracers.DefaultDirectory.New("flatCallTracer", &tracers.Context{}, nil) + require.NoError(t, err) + + // this error should be returned by GetResult + stopError := errors.New("stop error") + + // simulate a transaction + tx := types.NewTx(&types.LegacyTx{ + Nonce: 0, + To: &common.Address{}, + Value: big.NewInt(0), + Gas: 0, + GasPrice: big.NewInt(0), + Data: nil, + }) + + tracer.OnTxStart(&tracing.VMContext{ + ChainConfig: params.MainnetChainConfig, + }, tx, common.Address{}) + + tracer.OnEnter(0, byte(vm.CALL), common.Address{}, common.Address{}, nil, 0, big.NewInt(0)) + + // stop before the transaction is finished + tracer.Stop(stopError) + + tracer.OnTxEnd(&types.Receipt{GasUsed: 0}, nil) + + // check that the error is returned by GetResult + _, tracerError := tracer.GetResult() + require.Equal(t, stopError, tracerError) +} diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index 1a586ec039..5f9f891f68 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -222,7 +222,12 @@ func (ec *Client) getBlock(ctx context.Context, method string, args ...interface } txs[i] = tx.tx } - return types.NewBlockWithHeader(head).WithBody(txs, uncles).WithWithdrawals(body.Withdrawals), nil + return types.NewBlockWithHeader(head).WithBody( + types.Body{ + Transactions: txs, + Uncles: uncles, + Withdrawals: body.Withdrawals, + }), nil } // HeaderByHash returns the block header with the given hash. diff --git a/ethdb/database.go b/ethdb/database.go index 5d7d54353c..74f3da5683 100644 --- a/ethdb/database.go +++ b/ethdb/database.go @@ -43,8 +43,8 @@ type KeyValueWriter interface { // KeyValueStater wraps the Stat method of a backing data store. type KeyValueStater interface { - // Stat returns a particular internal stat of the database. - Stat(property string) (string, error) + // Stat returns the statistic data of the database. + Stat() (string, error) } // Compacter wraps the Compact method of a backing data store. @@ -92,8 +92,8 @@ type AncientReaderOp interface { // Ancients returns the ancient item numbers in the ancient store. Ancients() (uint64, error) - // Tail returns the number of first stored item in the freezer. - // This number can also be interpreted as the total deleted item numbers. + // Tail returns the number of first stored item in the ancient store. + // This number can also be interpreted as the total deleted items. Tail() (uint64, error) // AncientSize returns the ancient size of the specified category. @@ -111,7 +111,7 @@ type AncientReader interface { AncientReaderOp // ReadAncients runs the given read operation while ensuring that no writes take place - // on the underlying freezer. + // on the underlying ancient store. ReadAncients(fn func(AncientReaderOp) error) (err error) } @@ -168,11 +168,15 @@ type AncientWriteOp interface { AppendRaw(kind string, number uint64, item []byte) error } -// AncientStater wraps the Stat method of a backing data store. +// AncientStater wraps the Stat method of a backing ancient store. type AncientStater interface { - // AncientDatadir returns the path of root ancient directory. Empty string - // will be returned if ancient store is not enabled at all. The returned - // path can be used to construct the path of other freezers. + // AncientDatadir returns the path of the ancient store directory. + // + // If the ancient store is not activated, an error is returned. + // If an ephemeral ancient store is used, an empty path is returned. + // + // The path returned by AncientDatadir can be used as the root path + // of the ancient store to construct paths for other sub ancient stores. AncientDatadir() (string, error) } @@ -221,7 +225,7 @@ type Stater interface { } // AncientStore contains all the methods required to allow handling different -// ancient data stores backing immutable chain data store. +// ancient data stores backing immutable data store. type AncientStore interface { AncientReader AncientWriter @@ -245,8 +249,16 @@ type BlockStore interface { HasSeparateBlockStore() bool } +// ResettableAncientStore extends the AncientStore interface by adding a Reset method. +type ResettableAncientStore interface { + AncientStore + + // Reset is designed to reset the entire ancient store to its default state. + Reset() error +} + // Database contains all the methods required by the high level database to not -// only access the key-value data store but also the chain freezer. +// only access the key-value data store but also the ancient chain store. type Database interface { Reader Writer diff --git a/ethdb/dbtest/testsuite.go b/ethdb/dbtest/testsuite.go index 83a13c8cff..29a773ced4 100644 --- a/ethdb/dbtest/testsuite.go +++ b/ethdb/dbtest/testsuite.go @@ -381,7 +381,7 @@ func TestDatabaseSuite(t *testing.T, New func() ethdb.KeyValueStore) { } }) - t.Run("OperatonsAfterClose", func(t *testing.T) { + t.Run("OperationsAfterClose", func(t *testing.T) { db := New() db.Put([]byte("key"), []byte("value")) db.Close() @@ -530,7 +530,7 @@ func makeDataset(size, ksize, vsize int, order bool) ([][]byte, [][]byte) { vals = append(vals, randBytes(vsize)) } if order { - slices.SortFunc(keys, func(a, b []byte) int { return bytes.Compare(a, b) }) + slices.SortFunc(keys, bytes.Compare) } return keys, vals } diff --git a/ethdb/leveldb/leveldb.go b/ethdb/leveldb/leveldb.go index e58efbddbe..92838ad7ab 100644 --- a/ethdb/leveldb/leveldb.go +++ b/ethdb/leveldb/leveldb.go @@ -22,7 +22,6 @@ package leveldb import ( "fmt" - "strings" "sync" "time" @@ -244,14 +243,53 @@ func (db *Database) NewSnapshot() (ethdb.Snapshot, error) { return &snapshot{db: snap}, nil } -// Stat returns a particular internal stat of the database. -func (db *Database) Stat(property string) (string, error) { - if property == "" { - property = "leveldb.stats" - } else if !strings.HasPrefix(property, "leveldb.") { - property = "leveldb." + property +// Stat returns the statistic data of the database. +func (db *Database) Stat() (string, error) { + var stats leveldb.DBStats + if err := db.db.Stats(&stats); err != nil { + return "", err } - return db.db.GetProperty(property) + var ( + message string + totalRead int64 + totalWrite int64 + totalSize int64 + totalTables int + totalDuration time.Duration + ) + if len(stats.LevelSizes) > 0 { + message += " Level | Tables | Size(MB) | Time(sec) | Read(MB) | Write(MB)\n" + + "-------+------------+---------------+---------------+---------------+---------------\n" + for level, size := range stats.LevelSizes { + read := stats.LevelRead[level] + write := stats.LevelWrite[level] + duration := stats.LevelDurations[level] + tables := stats.LevelTablesCounts[level] + + if tables == 0 && duration == 0 { + continue + } + totalTables += tables + totalSize += size + totalRead += read + totalWrite += write + totalDuration += duration + message += fmt.Sprintf(" %3d | %10d | %13.5f | %13.5f | %13.5f | %13.5f\n", + level, tables, float64(size)/1048576.0, duration.Seconds(), + float64(read)/1048576.0, float64(write)/1048576.0) + } + message += "-------+------------+---------------+---------------+---------------+---------------\n" + message += fmt.Sprintf(" Total | %10d | %13.5f | %13.5f | %13.5f | %13.5f\n", + totalTables, float64(totalSize)/1048576.0, totalDuration.Seconds(), + float64(totalRead)/1048576.0, float64(totalWrite)/1048576.0) + message += "-------+------------+---------------+---------------+---------------+---------------\n\n" + } + message += fmt.Sprintf("Read(MB):%.5f Write(MB):%.5f\n", float64(stats.IORead)/1048576.0, float64(stats.IOWrite)/1048576.0) + message += fmt.Sprintf("BlockCache(MB):%.5f FileCache:%d\n", float64(stats.BlockCacheSize)/1048576.0, stats.OpenedTablesCount) + message += fmt.Sprintf("MemoryCompaction:%d Level0Compaction:%d NonLevel0Compaction:%d SeekCompaction:%d\n", stats.MemComp, stats.Level0Comp, stats.NonLevel0Comp, stats.SeekComp) + message += fmt.Sprintf("WriteDelayCount:%d WriteDelayDuration:%s Paused:%t\n", stats.WriteDelayCount, common.PrettyDuration(stats.WriteDelayDuration), stats.WritePaused) + message += fmt.Sprintf("Snapshots:%d Iterators:%d\n", stats.AliveSnapshots, stats.AliveIterators) + return message, nil } // Compact flattens the underlying data store for the given key range. In essence, @@ -400,7 +438,7 @@ func (b *batch) Put(key, value []byte) error { return nil } -// Delete inserts the a key removal into the batch for later committing. +// Delete inserts the key removal into the batch for later committing. func (b *batch) Delete(key []byte) error { b.b.Delete(key) b.size += len(key) diff --git a/ethdb/memorydb/memorydb.go b/ethdb/memorydb/memorydb.go index fa2456e3ee..c2864028f8 100644 --- a/ethdb/memorydb/memorydb.go +++ b/ethdb/memorydb/memorydb.go @@ -263,9 +263,9 @@ func (db *Database) NewSnapshot() (ethdb.Snapshot, error) { return newSnapshot(db), nil } -// Stat returns a particular internal stat of the database. -func (db *Database) Stat(property string) (string, error) { - return "", errors.New("unknown property") +// Stat returns the statistic data of the database. +func (db *Database) Stat() (string, error) { + return "", nil } // Compact is not supported on a memory database, but there's no need either as @@ -339,7 +339,7 @@ func (b *batch) Put(key, value []byte) error { return nil } -// Delete inserts the a key removal into the batch for later committing. +// Delete inserts the key removal into the batch for later committing. func (b *batch) Delete(key []byte) error { b.writes = append(b.writes, keyvalue{string(key), nil, true}) b.size += len(key) diff --git a/ethdb/pebble/pebble.go b/ethdb/pebble/pebble.go index aa339bf2d6..9e7d3608b7 100644 --- a/ethdb/pebble/pebble.go +++ b/ethdb/pebble/pebble.go @@ -212,7 +212,7 @@ func New(file string, cache int, handles int, namespace string, readonly bool, e // The default compaction concurrency(1 thread), // Here use all available CPUs for faster compaction. - MaxConcurrentCompactions: func() int { return runtime.NumCPU() }, + MaxConcurrentCompactions: runtime.NumCPU, // Per-level options. Options for at least one level must be specified. The // options for the last level are used for all subsequent levels. @@ -250,19 +250,19 @@ func New(file string, cache int, handles int, namespace string, readonly bool, e } db.db = innerDB - db.compTimeMeter = metrics.NewRegisteredMeter(namespace+"compact/time", nil) - db.compReadMeter = metrics.NewRegisteredMeter(namespace+"compact/input", nil) - db.compWriteMeter = metrics.NewRegisteredMeter(namespace+"compact/output", nil) - db.diskSizeGauge = metrics.NewRegisteredGauge(namespace+"disk/size", nil) - db.diskReadMeter = metrics.NewRegisteredMeter(namespace+"disk/read", nil) - db.diskWriteMeter = metrics.NewRegisteredMeter(namespace+"disk/write", nil) - db.writeDelayMeter = metrics.NewRegisteredMeter(namespace+"compact/writedelay/duration", nil) - db.writeDelayNMeter = metrics.NewRegisteredMeter(namespace+"compact/writedelay/counter", nil) - db.memCompGauge = metrics.NewRegisteredGauge(namespace+"compact/memory", nil) - db.level0CompGauge = metrics.NewRegisteredGauge(namespace+"compact/level0", nil) - db.nonlevel0CompGauge = metrics.NewRegisteredGauge(namespace+"compact/nonlevel0", nil) - db.seekCompGauge = metrics.NewRegisteredGauge(namespace+"compact/seek", nil) - db.manualMemAllocGauge = metrics.NewRegisteredGauge(namespace+"memory/manualalloc", nil) + db.compTimeMeter = metrics.GetOrRegisterMeter(namespace+"compact/time", nil) + db.compReadMeter = metrics.GetOrRegisterMeter(namespace+"compact/input", nil) + db.compWriteMeter = metrics.GetOrRegisterMeter(namespace+"compact/output", nil) + db.diskSizeGauge = metrics.GetOrRegisterGauge(namespace+"disk/size", nil) + db.diskReadMeter = metrics.GetOrRegisterMeter(namespace+"disk/read", nil) + db.diskWriteMeter = metrics.GetOrRegisterMeter(namespace+"disk/write", nil) + db.writeDelayMeter = metrics.GetOrRegisterMeter(namespace+"compact/writedelay/duration", nil) + db.writeDelayNMeter = metrics.GetOrRegisterMeter(namespace+"compact/writedelay/counter", nil) + db.memCompGauge = metrics.GetOrRegisterGauge(namespace+"compact/memory", nil) + db.level0CompGauge = metrics.GetOrRegisterGauge(namespace+"compact/level0", nil) + db.nonlevel0CompGauge = metrics.GetOrRegisterGauge(namespace+"compact/nonlevel0", nil) + db.seekCompGauge = metrics.GetOrRegisterGauge(namespace+"compact/seek", nil) + db.manualMemAllocGauge = metrics.GetOrRegisterGauge(namespace+"memory/manualalloc", nil) // Start up the metrics gathering and return go db.meter(metricsGatheringInterval, namespace) @@ -426,10 +426,8 @@ func upperBound(prefix []byte) (limit []byte) { } // Stat returns the internal metrics of Pebble in a text format. It's a developer -// method to read everything there is to read independent of Pebble version. -// -// The property is unused in Pebble as there's only one thing to retrieve. -func (d *Database) Stat(property string) (string, error) { +// method to read everything there is to read, independent of Pebble version. +func (d *Database) Stat() (string, error) { return d.db.Metrics().String(), nil } @@ -553,7 +551,7 @@ func (d *Database) meter(refresh time.Duration, namespace string) { for i, level := range stats.Levels { // Append metrics for additional layers if i >= len(d.levelsGauge) { - d.levelsGauge = append(d.levelsGauge, metrics.NewRegisteredGauge(namespace+fmt.Sprintf("tables/level%v", i), nil)) + d.levelsGauge = append(d.levelsGauge, metrics.GetOrRegisterGauge(namespace+fmt.Sprintf("tables/level%v", i), nil)) } d.levelsGauge[i].Update(level.NumFiles) } @@ -585,7 +583,7 @@ func (b *batch) Put(key, value []byte) error { return nil } -// Delete inserts the a key removal into the batch for later committing. +// Delete inserts the key removal into the batch for later committing. func (b *batch) Delete(key []byte) error { b.b.Delete(key, nil) b.size += len(key) diff --git a/ethdb/remotedb/remotedb.go b/ethdb/remotedb/remotedb.go index f755dd16bc..31dd8e15f7 100644 --- a/ethdb/remotedb/remotedb.go +++ b/ethdb/remotedb/remotedb.go @@ -192,8 +192,8 @@ func (db *Database) NewIterator(prefix []byte, start []byte) ethdb.Iterator { panic("not supported") } -func (db *Database) Stat(property string) (string, error) { - panic("not supported") +func (db *Database) Stat() (string, error) { + return "", nil } func (db *Database) AncientDatadir() (string, error) { diff --git a/event/multisub.go b/event/multisub.go index 5c8d2df48c..1f0af2a292 100644 --- a/event/multisub.go +++ b/event/multisub.go @@ -17,7 +17,7 @@ package event // JoinSubscriptions joins multiple subscriptions to be able to track them as -// one entity and collectively cancel them of consume any errors from them. +// one entity and collectively cancel them or consume any errors from them. func JoinSubscriptions(subs ...Subscription) Subscription { return NewSubscription(func(unsubbed <-chan struct{}) error { // Unsubscribe all subscriptions before returning diff --git a/go.mod b/go.mod index 5a4e210849..3df5acef81 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.21 require ( github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.2.0 - github.com/Microsoft/go-winio v0.6.1 + github.com/Microsoft/go-winio v0.6.2 github.com/VictoriaMetrics/fastcache v1.12.2 github.com/aws/aws-sdk-go-v2 v1.21.2 github.com/aws/aws-sdk-go-v2/config v1.18.45 @@ -18,19 +18,19 @@ require ( github.com/cockroachdb/pebble v1.1.0 github.com/cometbft/cometbft v0.37.0 github.com/consensys/gnark-crypto v0.12.1 - github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 + github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c github.com/crate-crypto/go-kzg-4844 v0.7.0 github.com/davecgh/go-spew v1.1.1 - github.com/deckarep/golang-set/v2 v2.5.0 + github.com/deckarep/golang-set/v2 v2.6.0 github.com/donovanhide/eventsource v0.0.0-20210830082556-c59027999da0 github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127 github.com/ethereum/c-kzg-4844 v1.0.0 + github.com/ethereum/go-verkle v0.1.1-0.20240306133620-7d920df305f0 github.com/fatih/color v1.16.0 github.com/fatih/structs v1.1.0 github.com/fjl/gencodec v0.0.0-20230517082657-f9840df7b83e github.com/fsnotify/fsnotify v1.6.0 github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 - github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 github.com/gofrs/flock v0.8.1 github.com/golang-jwt/jwt/v4 v4.5.0 github.com/golang/protobuf v1.5.4 @@ -71,7 +71,7 @@ require ( github.com/rs/cors v1.8.2 github.com/shirou/gopsutil v3.21.11+incompatible github.com/status-im/keycard-go v0.2.0 - github.com/stretchr/testify v1.8.4 + github.com/stretchr/testify v1.9.0 github.com/supranational/blst v0.3.11 github.com/syndtr/goleveldb v1.0.1 github.com/tendermint/go-amino v0.14.1 @@ -90,7 +90,8 @@ require ( golang.org/x/text v0.14.0 golang.org/x/time v0.5.0 golang.org/x/tools v0.20.0 - gopkg.in/natefinch/lumberjack.v2 v2.0.0 + google.golang.org/protobuf v1.33.0 + gopkg.in/natefinch/lumberjack.v2 v2.2.1 gopkg.in/yaml.v3 v3.0.1 ) @@ -115,9 +116,9 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.11.0 // indirect github.com/cespare/xxhash v1.1.0 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/chzyer/readline v1.5.1 // indirect - github.com/cockroachdb/errors v1.11.1 // indirect + github.com/cockroachdb/errors v1.11.3 // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect github.com/cockroachdb/redact v1.1.5 // indirect github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect @@ -139,7 +140,7 @@ require ( github.com/flynn/noise v1.1.0 // indirect github.com/francoispqt/gojay v1.2.13 // indirect github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61 // indirect - github.com/getsentry/sentry-go v0.25.0 // indirect + github.com/getsentry/sentry-go v0.27.0 // indirect github.com/go-kit/kit v0.12.0 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.5.1 // indirect @@ -283,7 +284,6 @@ require ( google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b // indirect google.golang.org/grpc v1.59.0 // indirect - google.golang.org/protobuf v1.33.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect k8s.io/apimachinery v0.20.0 // indirect diff --git a/go.sum b/go.sum index e5db8e3f1b..8c3d595fed 100644 --- a/go.sum +++ b/go.sum @@ -79,8 +79,8 @@ github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d/go.mod h1: github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= -github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= -github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= @@ -185,8 +185,9 @@ github.com/cespare/cp v1.1.1/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY= github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM= @@ -210,8 +211,8 @@ github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnht github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4= github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= -github.com/cockroachdb/errors v1.11.1 h1:xSEW75zKaKCWzR3OfxXUxgrk/NtT4G1MiOv5lWZazG8= -github.com/cockroachdb/errors v1.11.1/go.mod h1:8MUxA3Gi6b25tYlFEBGLf+D8aISL+M4MIpiWMSNRfxw= +github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I= +github.com/cockroachdb/errors v1.11.3/go.mod h1:m4UIW4CDjx+R5cybPsNrRbreomiFqt8o1h1wUVazSd8= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= github.com/cockroachdb/pebble v1.1.0 h1:pcFh8CdCIt2kmEpK0OIatq67Ln9uGDYY3d5XnE0LJG4= @@ -250,8 +251,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 h1:d28BXYi+wUpz1KBmiF9bWrjEMacUEREV6MBi2ODnrfQ= -github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= +github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c h1:uQYC5Z1mdLRPrZhHjHxufI8+2UG/i25QG92j0Er9p6I= +github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA= github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= github.com/creachadair/taskgroup v0.3.2 h1:zlfutDS+5XG40AOxcHDSThxKzns8Tnr9jnr6VqkYlkM= @@ -266,8 +267,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c h1:pFUpOrbxDR6AkioZ1ySsx5yxlDQZ8stG2b88gTPxgJU= github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6UhI8N9EjYm1c2odKpFpAYeR8dsBeM7PtzQhRgxRr9U= -github.com/deckarep/golang-set/v2 v2.5.0 h1:hn6cEZtQ0h3J8kFrHR/NrzyOoTnjgW1+FmNJzQ7y/sA= -github.com/deckarep/golang-set/v2 v2.5.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= +github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= @@ -326,6 +327,8 @@ github.com/etcd-io/bbolt v1.3.3 h1:gSJmxrs37LgTqR/oyJBWok6k6SvXEUerFTbltIhXkBM= github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= github.com/ethereum/c-kzg-4844 v1.0.0 h1:0X1LBXxaEtYD9xsyj9B9ctQEZIpnvVDeoBx8aHEwTNA= github.com/ethereum/c-kzg-4844 v1.0.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= +github.com/ethereum/go-verkle v0.1.1-0.20240306133620-7d920df305f0 h1:KrE8I4reeVvf7C1tm8elRjj4BdscTYzz/WAbYyf/JI4= +github.com/ethereum/go-verkle v0.1.1-0.20240306133620-7d920df305f0/go.mod h1:D9AJLVXSyZQXJQVk8oh1EwjISE+sJTn2duYIZC0dy3w= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= @@ -357,12 +360,10 @@ github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61/go.mod h1:Q0X6pkwTILD github.com/garyburd/redigo v1.6.0/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8xV8uYKlyuj8XIruxlh9WjVjdh1gIicAS7ays= github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= -github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 h1:BAIP2GihuqhwdILrV+7GJel5lyPV3u1+PgzrWLc0TkE= -github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46/go.mod h1:QNpY22eby74jVhqH4WhDLDwxc/vqsern6pW+u2kbkpc= github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= -github.com/getsentry/sentry-go v0.25.0 h1:q6Eo+hS+yoJlTO3uu/azhQadsD8V+jQn2D8VvX1eOyI= -github.com/getsentry/sentry-go v0.25.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= +github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= +github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= @@ -1125,8 +1126,9 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -1137,8 +1139,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= @@ -1765,8 +1767,8 @@ gopkg.in/jcmturner/dnsutils.v1 v1.0.1/go.mod h1:m3v+5svpVOhtFAP/wSz+yzh4Mc0Fg7eR gopkg.in/jcmturner/goidentity.v3 v3.0.0/go.mod h1:oG2kH0IvSYNIu80dVAyu/yoefjq1mNfM5bm88whjWx4= gopkg.in/jcmturner/gokrb5.v7 v7.5.0/go.mod h1:l8VISx+WGYp+Fp7KRbsiUuXTTOnxIc3Tuvyavf11/WM= gopkg.in/jcmturner/rpc.v1 v1.1.0/go.mod h1:YIdkC4XfD6GXbzje11McwsDuOlZQSb9W4vfLvuNnlv8= -gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= -gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/redis.v4 v4.2.4/go.mod h1:8KREHdypkCEojGKQcjMqAODMICIVwZAONWq8RowTITA= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= diff --git a/internal/build/archive.go b/internal/build/archive.go index 5b37c0edfa..645921c690 100644 --- a/internal/build/archive.go +++ b/internal/build/archive.go @@ -272,17 +272,22 @@ func extractFile(arpath string, armode os.FileMode, data io.Reader, dest string) return fmt.Errorf("path %q escapes archive destination", target) } - // Ensure the destination directory exists. + // Remove the preivously-extracted file if it exists + if err := os.RemoveAll(target); err != nil { + return err + } + + // Recreate the destination directory 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) + file, err := os.OpenFile(target, os.O_CREATE|os.O_WRONLY, armode) if err != nil { return err } - if _, err := io.Copy(file, data); err != nil { + if _, err = io.Copy(file, data); err != nil { file.Close() os.Remove(target) return err diff --git a/internal/debug/flags.go b/internal/debug/flags.go index 3cc4871bc2..0e05975c7e 100644 --- a/internal/debug/flags.go +++ b/internal/debug/flags.go @@ -228,9 +228,9 @@ func Setup(ctx *cli.Context) error { 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.JSONHandlerWithLevel(output, log.LevelInfo) + handler = log.JSONHandler(output) case logFmtFlag == "json": - handler = log.JSONHandlerWithLevel(output, log.LevelInfo) + handler = log.JSONHandler(output) case logFmtFlag == "logfmt": handler = log.LogfmtHandler(output) case logFmtFlag == "", logFmtFlag == "terminal": diff --git a/internal/era/era.go b/internal/era/era.go index 2b9e622901..6ad7339b36 100644 --- a/internal/era/era.go +++ b/internal/era/era.go @@ -151,7 +151,7 @@ func (e *Era) GetBlockByNumber(num uint64) (*types.Block, error) { if err := rlp.Decode(r, &body); err != nil { return nil, err } - return types.NewBlockWithHeader(&header).WithBody(body.Transactions, body.Uncles), nil + return types.NewBlockWithHeader(&header).WithBody(body), nil } // Accumulator reads the accumulator entry in the Era1 file. diff --git a/internal/era/iterator.go b/internal/era/iterator.go index cc4f27c201..f48aab46b4 100644 --- a/internal/era/iterator.go +++ b/internal/era/iterator.go @@ -73,7 +73,7 @@ func (it *Iterator) Block() (*types.Block, error) { if err := rlp.Decode(it.inner.Body, &body); err != nil { return nil, err } - return types.NewBlockWithHeader(&header).WithBody(body.Transactions, body.Uncles), nil + return types.NewBlockWithHeader(&header).WithBody(body), nil } // Receipts returns the receipts for the iterator's current position. diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 2aee59222c..925b6ee1ce 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -26,9 +26,6 @@ import ( "time" "github.com/davecgh/go-spew/spew" - "github.com/holiman/uint256" - "github.com/tyler-smith/go-bip39" - "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/accounts/scwallet" @@ -53,6 +50,8 @@ import ( "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/trie" + "github.com/holiman/uint256" + "github.com/tyler-smith/go-bip39" ) // max is a helper function which returns the larger of the two given integers. @@ -82,20 +81,20 @@ func NewEthereumAPI(b Backend) *EthereumAPI { } // GasPrice returns a suggestion for a gas price for legacy transactions. -func (s *EthereumAPI) GasPrice(ctx context.Context) (*hexutil.Big, error) { - tipcap, err := s.b.SuggestGasTipCap(ctx) +func (api *EthereumAPI) GasPrice(ctx context.Context) (*hexutil.Big, error) { + tipcap, err := api.b.SuggestGasTipCap(ctx) if err != nil { return nil, err } - if head := s.b.CurrentHeader(); head.BaseFee != nil { + if head := api.b.CurrentHeader(); head.BaseFee != nil { tipcap.Add(tipcap, head.BaseFee) } return (*hexutil.Big)(tipcap), err } // MaxPriorityFeePerGas returns a suggestion for a gas tip cap for dynamic fee transactions. -func (s *EthereumAPI) MaxPriorityFeePerGas(ctx context.Context) (*hexutil.Big, error) { - tipcap, err := s.b.SuggestGasTipCap(ctx) +func (api *EthereumAPI) MaxPriorityFeePerGas(ctx context.Context) (*hexutil.Big, error) { + tipcap, err := api.b.SuggestGasTipCap(ctx) if err != nil { return nil, err } @@ -112,8 +111,8 @@ type feeHistoryResult struct { } // FeeHistory returns the fee market history. -func (s *EthereumAPI) FeeHistory(ctx context.Context, blockCount math.HexOrDecimal64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*feeHistoryResult, error) { - oldest, reward, baseFee, gasUsed, blobBaseFee, blobGasUsed, err := s.b.FeeHistory(ctx, uint64(blockCount), lastBlock, rewardPercentiles) +func (api *EthereumAPI) FeeHistory(ctx context.Context, blockCount math.HexOrDecimal64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*feeHistoryResult, error) { + oldest, reward, baseFee, gasUsed, blobBaseFee, blobGasUsed, err := api.b.FeeHistory(ctx, uint64(blockCount), lastBlock, rewardPercentiles) if err != nil { return nil, err } @@ -149,19 +148,19 @@ func (s *EthereumAPI) FeeHistory(ctx context.Context, blockCount math.HexOrDecim } // BlobBaseFee returns the base fee for blob gas at the current head. -func (s *EthereumAPI) BlobBaseFee(ctx context.Context) *hexutil.Big { - return (*hexutil.Big)(s.b.BlobBaseFee(ctx)) +func (api *EthereumAPI) BlobBaseFee(ctx context.Context) *hexutil.Big { + return (*hexutil.Big)(api.b.BlobBaseFee(ctx)) } // Syncing returns false in case the node is currently not syncing with the network. It can be up-to-date or has not -// yet received the latest block headers from its pears. In case it is synchronizing: +// yet received the latest block headers from its peers. In case it is synchronizing: // - startingBlock: block number this node started to synchronize from // - currentBlock: block number this node is currently importing // - highestBlock: block number of the highest block header this node has received from peers // - pulledStates: number of state entries processed until now // - knownStates: number of known state entries that still need to be pulled -func (s *EthereumAPI) Syncing() (interface{}, error) { - progress := s.b.SyncProgress() +func (api *EthereumAPI) Syncing() (interface{}, error) { + progress := api.b.SyncProgress() // Return not syncing if the synchronisation already completed if progress.Done() { @@ -200,18 +199,18 @@ func NewTxPoolAPI(b Backend) *TxPoolAPI { } // Content returns the transactions contained within the transaction pool. -func (s *TxPoolAPI) Content() map[string]map[string]map[string]*RPCTransaction { +func (api *TxPoolAPI) Content() map[string]map[string]map[string]*RPCTransaction { content := map[string]map[string]map[string]*RPCTransaction{ "pending": make(map[string]map[string]*RPCTransaction), "queued": make(map[string]map[string]*RPCTransaction), } - pending, queue := s.b.TxPoolContent() - curHeader := s.b.CurrentHeader() + pending, queue := api.b.TxPoolContent() + curHeader := api.b.CurrentHeader() // Flatten the pending transactions for account, txs := range pending { dump := make(map[string]*RPCTransaction) for _, tx := range txs { - dump[fmt.Sprintf("%d", tx.Nonce())] = NewRPCPendingTransaction(tx, curHeader, s.b.ChainConfig()) + dump[fmt.Sprintf("%d", tx.Nonce())] = NewRPCPendingTransaction(tx, curHeader, api.b.ChainConfig()) } content["pending"][account.Hex()] = dump } @@ -219,7 +218,7 @@ func (s *TxPoolAPI) Content() map[string]map[string]map[string]*RPCTransaction { for account, txs := range queue { dump := make(map[string]*RPCTransaction) for _, tx := range txs { - dump[fmt.Sprintf("%d", tx.Nonce())] = NewRPCPendingTransaction(tx, curHeader, s.b.ChainConfig()) + dump[fmt.Sprintf("%d", tx.Nonce())] = NewRPCPendingTransaction(tx, curHeader, api.b.ChainConfig()) } content["queued"][account.Hex()] = dump } @@ -227,22 +226,22 @@ func (s *TxPoolAPI) Content() map[string]map[string]map[string]*RPCTransaction { } // ContentFrom returns the transactions contained within the transaction pool. -func (s *TxPoolAPI) ContentFrom(addr common.Address) map[string]map[string]*RPCTransaction { +func (api *TxPoolAPI) ContentFrom(addr common.Address) map[string]map[string]*RPCTransaction { content := make(map[string]map[string]*RPCTransaction, 2) - pending, queue := s.b.TxPoolContentFrom(addr) - curHeader := s.b.CurrentHeader() + pending, queue := api.b.TxPoolContentFrom(addr) + curHeader := api.b.CurrentHeader() // Build the pending transactions dump := make(map[string]*RPCTransaction, len(pending)) for _, tx := range pending { - dump[fmt.Sprintf("%d", tx.Nonce())] = NewRPCPendingTransaction(tx, curHeader, s.b.ChainConfig()) + dump[fmt.Sprintf("%d", tx.Nonce())] = NewRPCPendingTransaction(tx, curHeader, api.b.ChainConfig()) } content["pending"] = dump // Build the queued transactions dump = make(map[string]*RPCTransaction, len(queue)) for _, tx := range queue { - dump[fmt.Sprintf("%d", tx.Nonce())] = NewRPCPendingTransaction(tx, curHeader, s.b.ChainConfig()) + dump[fmt.Sprintf("%d", tx.Nonce())] = NewRPCPendingTransaction(tx, curHeader, api.b.ChainConfig()) } content["queued"] = dump @@ -250,8 +249,8 @@ func (s *TxPoolAPI) ContentFrom(addr common.Address) map[string]map[string]*RPCT } // Status returns the number of pending and queued transaction in the pool. -func (s *TxPoolAPI) Status() map[string]hexutil.Uint { - pending, queue := s.b.Stats() +func (api *TxPoolAPI) Status() map[string]hexutil.Uint { + pending, queue := api.b.Stats() return map[string]hexutil.Uint{ "pending": hexutil.Uint(pending), "queued": hexutil.Uint(queue), @@ -260,12 +259,12 @@ func (s *TxPoolAPI) Status() map[string]hexutil.Uint { // Inspect retrieves the content of the transaction pool and flattens it into an // easily inspectable list. -func (s *TxPoolAPI) Inspect() map[string]map[string]map[string]string { +func (api *TxPoolAPI) Inspect() map[string]map[string]map[string]string { content := map[string]map[string]map[string]string{ "pending": make(map[string]map[string]string), "queued": make(map[string]map[string]string), } - pending, queue := s.b.TxPoolContent() + pending, queue := api.b.TxPoolContent() // Define a formatter to flatten a transaction into a string var format = func(tx *types.Transaction) string { @@ -305,8 +304,8 @@ func NewEthereumAccountAPI(am *accounts.Manager) *EthereumAccountAPI { } // Accounts returns the collection of accounts this node manages. -func (s *EthereumAccountAPI) Accounts() []common.Address { - return s.am.Accounts() +func (api *EthereumAccountAPI) Accounts() []common.Address { + return api.am.Accounts() } // PersonalAccountAPI provides an API to access accounts managed by this node. @@ -328,8 +327,8 @@ func NewPersonalAccountAPI(b Backend, nonceLock *AddrLocker) *PersonalAccountAPI } // ListAccounts will return a list of addresses for accounts this node manages. -func (s *PersonalAccountAPI) ListAccounts() []common.Address { - return s.am.Accounts() +func (api *PersonalAccountAPI) ListAccounts() []common.Address { + return api.am.Accounts() } // rawWallet is a JSON representation of an accounts.Wallet interface, with its @@ -342,9 +341,9 @@ type rawWallet struct { } // ListWallets will return a list of wallets this node manages. -func (s *PersonalAccountAPI) ListWallets() []rawWallet { +func (api *PersonalAccountAPI) ListWallets() []rawWallet { wallets := make([]rawWallet, 0) // return [] instead of nil if empty - for _, wallet := range s.am.Wallets() { + for _, wallet := range api.am.Wallets() { status, failure := wallet.Status() raw := rawWallet{ @@ -364,8 +363,8 @@ func (s *PersonalAccountAPI) ListWallets() []rawWallet { // connection and attempting to authenticate via the provided passphrase. Note, // the method may return an extra challenge requiring a second open (e.g. the // Trezor PIN matrix challenge). -func (s *PersonalAccountAPI) OpenWallet(url string, passphrase *string) error { - wallet, err := s.am.Wallet(url) +func (api *PersonalAccountAPI) OpenWallet(url string, passphrase *string) error { + wallet, err := api.am.Wallet(url) if err != nil { return err } @@ -378,8 +377,8 @@ func (s *PersonalAccountAPI) OpenWallet(url string, passphrase *string) error { // DeriveAccount requests an HD wallet to derive a new account, optionally pinning // it for later reuse. -func (s *PersonalAccountAPI) DeriveAccount(url string, path string, pin *bool) (accounts.Account, error) { - wallet, err := s.am.Wallet(url) +func (api *PersonalAccountAPI) DeriveAccount(url string, path string, pin *bool) (accounts.Account, error) { + wallet, err := api.am.Wallet(url) if err != nil { return accounts.Account{}, err } @@ -394,8 +393,8 @@ func (s *PersonalAccountAPI) DeriveAccount(url string, path string, pin *bool) ( } // NewAccount will create a new account and returns the address for the new account. -func (s *PersonalAccountAPI) NewAccount(password string) (common.AddressEIP55, error) { - ks, err := fetchKeystore(s.am) +func (api *PersonalAccountAPI) NewAccount(password string) (common.AddressEIP55, error) { + ks, err := fetchKeystore(api.am) if err != nil { return common.AddressEIP55{}, err } @@ -420,12 +419,12 @@ func fetchKeystore(am *accounts.Manager) (*keystore.KeyStore, error) { // ImportRawKey stores the given hex encoded ECDSA key into the key directory, // encrypting it with the passphrase. -func (s *PersonalAccountAPI) ImportRawKey(privkey string, password string) (common.Address, error) { +func (api *PersonalAccountAPI) ImportRawKey(privkey string, password string) (common.Address, error) { key, err := crypto.HexToECDSA(privkey) if err != nil { return common.Address{}, err } - ks, err := fetchKeystore(s.am) + ks, err := fetchKeystore(api.am) if err != nil { return common.Address{}, err } @@ -436,11 +435,11 @@ func (s *PersonalAccountAPI) ImportRawKey(privkey string, password string) (comm // UnlockAccount will unlock the account associated with the given address with // the given password for duration seconds. If duration is nil it will use a // default of 300 seconds. It returns an indication if the account was unlocked. -func (s *PersonalAccountAPI) UnlockAccount(ctx context.Context, addr common.Address, password string, duration *uint64) (bool, error) { +func (api *PersonalAccountAPI) UnlockAccount(ctx context.Context, addr common.Address, password string, duration *uint64) (bool, error) { // When the API is exposed by external RPC(http, ws etc), unless the user // explicitly specifies to allow the insecure account unlocking, otherwise // it is disabled. - if s.b.ExtRPCEnabled() && !s.b.AccountManager().Config().InsecureUnlockAllowed { + if api.b.ExtRPCEnabled() && !api.b.AccountManager().Config().InsecureUnlockAllowed { return false, errors.New("account unlock with HTTP access is forbidden") } @@ -453,7 +452,7 @@ func (s *PersonalAccountAPI) UnlockAccount(ctx context.Context, addr common.Addr } else { d = time.Duration(*duration) * time.Second } - ks, err := fetchKeystore(s.am) + ks, err := fetchKeystore(api.am) if err != nil { return false, err } @@ -465,8 +464,8 @@ func (s *PersonalAccountAPI) UnlockAccount(ctx context.Context, addr common.Addr } // LockAccount will lock the account associated with the given address when it's unlocked. -func (s *PersonalAccountAPI) LockAccount(addr common.Address) bool { - if ks, err := fetchKeystore(s.am); err == nil { +func (api *PersonalAccountAPI) LockAccount(addr common.Address) bool { + if ks, err := fetchKeystore(api.am); err == nil { return ks.Lock(addr) == nil } return false @@ -475,49 +474,49 @@ func (s *PersonalAccountAPI) LockAccount(addr common.Address) bool { // signTransaction sets defaults and signs the given transaction // NOTE: the caller needs to ensure that the nonceLock is held, if applicable, // and release it after the transaction has been submitted to the tx pool -func (s *PersonalAccountAPI) signTransaction(ctx context.Context, args *TransactionArgs, passwd string) (*types.Transaction, error) { +func (api *PersonalAccountAPI) signTransaction(ctx context.Context, args *TransactionArgs, passwd string) (*types.Transaction, error) { // Look up the wallet containing the requested signer account := accounts.Account{Address: args.from()} - wallet, err := s.am.Find(account) + wallet, err := api.am.Find(account) if err != nil { return nil, err } // Set some sanity defaults and terminate on failure - if err := args.setDefaults(ctx, s.b, false); err != nil { + if err := args.setDefaults(ctx, api.b, false); err != nil { return nil, err } // Assemble the transaction and sign with the wallet tx := args.ToTransaction() - return wallet.SignTxWithPassphrase(account, passwd, tx, s.b.ChainConfig().ChainID) + return wallet.SignTxWithPassphrase(account, passwd, tx, api.b.ChainConfig().ChainID) } // SendTransaction will create a transaction from the given arguments and // tries to sign it with the key associated with args.From. If the given // passwd isn't able to decrypt the key it fails. -func (s *PersonalAccountAPI) SendTransaction(ctx context.Context, args TransactionArgs, passwd string) (common.Hash, error) { +func (api *PersonalAccountAPI) SendTransaction(ctx context.Context, args TransactionArgs, passwd string) (common.Hash, error) { if args.Nonce == nil { // Hold the mutex around signing to prevent concurrent assignment of // the same nonce to multiple accounts. - s.nonceLock.LockAddr(args.from()) - defer s.nonceLock.UnlockAddr(args.from()) + api.nonceLock.LockAddr(args.from()) + defer api.nonceLock.UnlockAddr(args.from()) } if args.IsEIP4844() { return common.Hash{}, errBlobTxNotSupported } - signed, err := s.signTransaction(ctx, &args, passwd) + signed, err := api.signTransaction(ctx, &args, passwd) if err != nil { log.Warn("Failed transaction send attempt", "from", args.from(), "to", args.To, "value", args.Value.ToInt(), "err", err) return common.Hash{}, err } - return SubmitTransaction(ctx, s.b, signed) + return SubmitTransaction(ctx, api.b, signed) } // SignTransaction will create a transaction from the given arguments and // tries to sign it with the key associated with args.From. If the given passwd isn't // able to decrypt the key it fails. The transaction is returned in RLP-form, not broadcast // to other nodes -func (s *PersonalAccountAPI) SignTransaction(ctx context.Context, args TransactionArgs, passwd string) (*SignTransactionResult, error) { +func (api *PersonalAccountAPI) SignTransaction(ctx context.Context, args TransactionArgs, passwd string) (*SignTransactionResult, error) { // No need to obtain the noncelock mutex, since we won't be sending this // tx into the transaction pool, but right back to the user if args.From == nil { @@ -537,10 +536,10 @@ func (s *PersonalAccountAPI) SignTransaction(ctx context.Context, args Transacti } // Before actually signing the transaction, ensure the transaction fee is reasonable. tx := args.ToTransaction() - if err := checkTxFee(tx.GasPrice(), tx.Gas(), s.b.RPCTxFeeCap()); err != nil { + if err := checkTxFee(tx.GasPrice(), tx.Gas(), api.b.RPCTxFeeCap()); err != nil { return nil, err } - signed, err := s.signTransaction(ctx, &args, passwd) + signed, err := api.signTransaction(ctx, &args, passwd) if err != nil { log.Warn("Failed transaction sign attempt", "from", args.from(), "to", args.To, "value", args.Value.ToInt(), "err", err) return nil, err @@ -561,11 +560,11 @@ func (s *PersonalAccountAPI) SignTransaction(ctx context.Context, args Transacti // The key used to calculate the signature is decrypted with the given password. // // https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-personal#personal-sign -func (s *PersonalAccountAPI) Sign(ctx context.Context, data hexutil.Bytes, addr common.Address, passwd string) (hexutil.Bytes, error) { +func (api *PersonalAccountAPI) Sign(ctx context.Context, data hexutil.Bytes, addr common.Address, passwd string) (hexutil.Bytes, error) { // Look up the wallet containing the requested signer account := accounts.Account{Address: addr} - wallet, err := s.b.AccountManager().Find(account) + wallet, err := api.b.AccountManager().Find(account) if err != nil { return nil, err } @@ -589,7 +588,7 @@ func (s *PersonalAccountAPI) Sign(ctx context.Context, data hexutil.Bytes, addr // the V value must be 27 or 28 for legacy reasons. // // https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-personal#personal-ecrecover -func (s *PersonalAccountAPI) EcRecover(ctx context.Context, data, sig hexutil.Bytes) (common.Address, error) { +func (api *PersonalAccountAPI) EcRecover(ctx context.Context, data, sig hexutil.Bytes) (common.Address, error) { if len(sig) != crypto.SignatureLength { return common.Address{}, fmt.Errorf("signature must be %d bytes long", crypto.SignatureLength) } @@ -606,8 +605,8 @@ func (s *PersonalAccountAPI) EcRecover(ctx context.Context, data, sig hexutil.By } // InitializeWallet initializes a new wallet at the provided URL, by generating and returning a new private key. -func (s *PersonalAccountAPI) InitializeWallet(ctx context.Context, url string) (string, error) { - wallet, err := s.am.Wallet(url) +func (api *PersonalAccountAPI) InitializeWallet(ctx context.Context, url string) (string, error) { + wallet, err := api.am.Wallet(url) if err != nil { return "", err } @@ -633,8 +632,8 @@ func (s *PersonalAccountAPI) InitializeWallet(ctx context.Context, url string) ( } // Unpair deletes a pairing between wallet and geth. -func (s *PersonalAccountAPI) Unpair(ctx context.Context, url string, pin string) error { - wallet, err := s.am.Wallet(url) +func (api *PersonalAccountAPI) Unpair(ctx context.Context, url string, pin string) error { + wallet, err := api.am.Wallet(url) if err != nil { return err } @@ -668,16 +667,16 @@ func (api *BlockChainAPI) ChainId() *hexutil.Big { } // BlockNumber returns the block number of the chain head. -func (s *BlockChainAPI) BlockNumber() hexutil.Uint64 { - header, _ := s.b.HeaderByNumber(context.Background(), rpc.LatestBlockNumber) // latest header should always be available +func (api *BlockChainAPI) BlockNumber() hexutil.Uint64 { + header, _ := api.b.HeaderByNumber(context.Background(), rpc.LatestBlockNumber) // latest header should always be available return hexutil.Uint64(header.Number.Uint64()) } // GetBalance returns the amount of wei for the given address in the state of the // given block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta // block numbers are also allowed. -func (s *BlockChainAPI) GetBalance(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (*hexutil.Big, error) { - state, _, err := s.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) +func (api *BlockChainAPI) GetBalance(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (*hexutil.Big, error) { + state, _, err := api.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) if state == nil || err != nil { return nil, err } @@ -716,7 +715,7 @@ func (n *proofList) Delete(key []byte) error { } // GetProof returns the Merkle-proof for a given account and optionally some storage keys. -func (s *BlockChainAPI) GetProof(ctx context.Context, address common.Address, storageKeys []string, blockNrOrHash rpc.BlockNumberOrHash) (*AccountResult, error) { +func (api *BlockChainAPI) GetProof(ctx context.Context, address common.Address, storageKeys []string, blockNrOrHash rpc.BlockNumberOrHash) (*AccountResult, error) { var ( keys = make([]common.Hash, len(storageKeys)) keyLengths = make([]int, len(storageKeys)) @@ -730,7 +729,7 @@ func (s *BlockChainAPI) GetProof(ctx context.Context, address common.Address, st return nil, err } } - statedb, header, err := s.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) + statedb, header, err := api.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) if statedb == nil || err != nil { return nil, err } @@ -816,10 +815,10 @@ func decodeHash(s string) (h common.Hash, inputLength int, err error) { // - When blockNr is -2 the chain latest header is returned. // - When blockNr is -3 the chain finalized header is returned. // - When blockNr is -4 the chain safe header is returned. -func (s *BlockChainAPI) GetHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (map[string]interface{}, error) { - header, err := s.b.HeaderByNumber(ctx, number) +func (api *BlockChainAPI) GetHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (map[string]interface{}, error) { + header, err := api.b.HeaderByNumber(ctx, number) if header != nil && err == nil { - response := s.rpcMarshalHeader(ctx, header) + response := api.rpcMarshalHeader(ctx, header) if number == rpc.PendingBlockNumber { // Pending header need to nil out a few fields for _, field := range []string{"hash", "nonce", "miner"} { @@ -832,10 +831,10 @@ func (s *BlockChainAPI) GetHeaderByNumber(ctx context.Context, number rpc.BlockN } // GetHeaderByHash returns the requested header by hash. -func (s *BlockChainAPI) GetHeaderByHash(ctx context.Context, hash common.Hash) map[string]interface{} { - header, _ := s.b.HeaderByHash(ctx, hash) +func (api *BlockChainAPI) GetHeaderByHash(ctx context.Context, hash common.Hash) map[string]interface{} { + header, _ := api.b.HeaderByHash(ctx, hash) if header != nil { - return s.rpcMarshalHeader(ctx, header) + return api.rpcMarshalHeader(ctx, header) } return nil } @@ -847,10 +846,10 @@ func (s *BlockChainAPI) GetHeaderByHash(ctx context.Context, hash common.Hash) m // - When blockNr is -4 the chain safe block is returned. // - When fullTx is true all transactions in the block are returned, otherwise // only the transaction hash is returned. -func (s *BlockChainAPI) GetBlockByNumber(ctx context.Context, number rpc.BlockNumber, fullTx bool) (map[string]interface{}, error) { - block, err := s.b.BlockByNumber(ctx, number) +func (api *BlockChainAPI) GetBlockByNumber(ctx context.Context, number rpc.BlockNumber, fullTx bool) (map[string]interface{}, error) { + block, err := api.b.BlockByNumber(ctx, number) if block != nil && err == nil { - response, err := s.rpcMarshalBlock(ctx, block, true, fullTx) + response, err := api.rpcMarshalBlock(ctx, block, true, fullTx) if err == nil && number == rpc.PendingBlockNumber { // Pending blocks need to nil out a few fields for _, field := range []string{"hash", "nonce", "miner"} { @@ -864,10 +863,10 @@ func (s *BlockChainAPI) GetBlockByNumber(ctx context.Context, number rpc.BlockNu // GetBlockByHash returns the requested block. When fullTx is true all transactions in the block are returned in full // detail, otherwise only the transaction hash is returned. -func (s *BlockChainAPI) GetBlockByHash(ctx context.Context, hash common.Hash, fullTx bool) (map[string]interface{}, error) { - block, err := s.b.BlockByHash(ctx, hash) +func (api *BlockChainAPI) GetBlockByHash(ctx context.Context, hash common.Hash, fullTx bool) (map[string]interface{}, error) { + block, err := api.b.BlockByHash(ctx, hash) if block != nil { - return s.rpcMarshalBlock(ctx, block, true, fullTx) + return api.rpcMarshalBlock(ctx, block, true, fullTx) } return nil, err } @@ -950,8 +949,8 @@ func (s *BlockChainAPI) GetFinalizedBlock(ctx context.Context, verifiedValidator } // GetUncleByBlockNumberAndIndex returns the uncle block for the given block hash and index. -func (s *BlockChainAPI) GetUncleByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index hexutil.Uint) (map[string]interface{}, error) { - block, err := s.b.BlockByNumber(ctx, blockNr) +func (api *BlockChainAPI) GetUncleByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index hexutil.Uint) (map[string]interface{}, error) { + block, err := api.b.BlockByNumber(ctx, blockNr) if block != nil { uncles := block.Uncles() if index >= hexutil.Uint(len(uncles)) { @@ -959,14 +958,14 @@ func (s *BlockChainAPI) GetUncleByBlockNumberAndIndex(ctx context.Context, block return nil, nil } block = types.NewBlockWithHeader(uncles[index]) - return s.rpcMarshalBlock(ctx, block, false, false) + return api.rpcMarshalBlock(ctx, block, false, false) } return nil, err } // GetUncleByBlockHashAndIndex returns the uncle block for the given block hash and index. -func (s *BlockChainAPI) GetUncleByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index hexutil.Uint) (map[string]interface{}, error) { - block, err := s.b.BlockByHash(ctx, blockHash) +func (api *BlockChainAPI) GetUncleByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index hexutil.Uint) (map[string]interface{}, error) { + block, err := api.b.BlockByHash(ctx, blockHash) if block != nil { uncles := block.Uncles() if index >= hexutil.Uint(len(uncles)) { @@ -974,14 +973,14 @@ func (s *BlockChainAPI) GetUncleByBlockHashAndIndex(ctx context.Context, blockHa return nil, nil } block = types.NewBlockWithHeader(uncles[index]) - return s.rpcMarshalBlock(ctx, block, false, false) + return api.rpcMarshalBlock(ctx, block, false, false) } return nil, err } // GetUncleCountByBlockNumber returns number of uncles in the block for the given block number -func (s *BlockChainAPI) GetUncleCountByBlockNumber(ctx context.Context, blockNr rpc.BlockNumber) *hexutil.Uint { - if block, _ := s.b.BlockByNumber(ctx, blockNr); block != nil { +func (api *BlockChainAPI) GetUncleCountByBlockNumber(ctx context.Context, blockNr rpc.BlockNumber) *hexutil.Uint { + if block, _ := api.b.BlockByNumber(ctx, blockNr); block != nil { n := hexutil.Uint(len(block.Uncles())) return &n } @@ -989,8 +988,8 @@ func (s *BlockChainAPI) GetUncleCountByBlockNumber(ctx context.Context, blockNr } // GetUncleCountByBlockHash returns number of uncles in the block for the given block hash -func (s *BlockChainAPI) GetUncleCountByBlockHash(ctx context.Context, blockHash common.Hash) *hexutil.Uint { - if block, _ := s.b.BlockByHash(ctx, blockHash); block != nil { +func (api *BlockChainAPI) GetUncleCountByBlockHash(ctx context.Context, blockHash common.Hash) *hexutil.Uint { + if block, _ := api.b.BlockByHash(ctx, blockHash); block != nil { n := hexutil.Uint(len(block.Uncles())) return &n } @@ -998,8 +997,8 @@ func (s *BlockChainAPI) GetUncleCountByBlockHash(ctx context.Context, blockHash } // GetCode returns the code stored at the given address in the state for the given block number. -func (s *BlockChainAPI) GetCode(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (hexutil.Bytes, error) { - state, _, err := s.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) +func (api *BlockChainAPI) GetCode(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (hexutil.Bytes, error) { + state, _, err := api.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) if state == nil || err != nil { return nil, err } @@ -1010,8 +1009,8 @@ func (s *BlockChainAPI) GetCode(ctx context.Context, address common.Address, blo // GetStorageAt returns the storage from the state at the given address, key and // block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta block // numbers are also allowed. -func (s *BlockChainAPI) GetStorageAt(ctx context.Context, address common.Address, hexKey string, blockNrOrHash rpc.BlockNumberOrHash) (hexutil.Bytes, error) { - state, _, err := s.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) +func (api *BlockChainAPI) GetStorageAt(ctx context.Context, address common.Address, hexKey string, blockNrOrHash rpc.BlockNumberOrHash) (hexutil.Bytes, error) { + state, _, err := api.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) if state == nil || err != nil { return nil, err } @@ -1024,14 +1023,14 @@ func (s *BlockChainAPI) GetStorageAt(ctx context.Context, address common.Address } // GetBlockReceipts returns the block receipts for the given block hash or number or tag. -func (s *BlockChainAPI) GetBlockReceipts(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) ([]map[string]interface{}, error) { - block, err := s.b.BlockByNumberOrHash(ctx, blockNrOrHash) +func (api *BlockChainAPI) GetBlockReceipts(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) ([]map[string]interface{}, error) { + block, err := api.b.BlockByNumberOrHash(ctx, blockNrOrHash) if block == nil || err != nil { // When the block doesn't exist, the RPC method should return JSON null // as per specification. return nil, nil } - receipts, err := s.b.GetReceipts(ctx, block.Hash()) + receipts, err := api.b.GetReceipts(ctx, block.Hash()) if err != nil { return nil, err } @@ -1041,7 +1040,7 @@ func (s *BlockChainAPI) GetBlockReceipts(ctx context.Context, blockNrOrHash rpc. } // Derive the sender. - signer := types.MakeSigner(s.b.ChainConfig(), block.Number(), block.Time()) + signer := types.MakeSigner(api.b.ChainConfig(), block.Number(), block.Time()) result := make([]map[string]interface{}, len(receipts)) for i, receipt := range receipts { @@ -1105,7 +1104,7 @@ func (s *BlockChainAPI) GetBlobSidecarByTxHash(ctx context.Context, hash common. // of a message call. // Note, state and stateDiff can't be specified at the same time. If state is // set, message execution will only use the data in the given state. Otherwise -// if statDiff is set, all diff will be applied first and then execute the call +// if stateDiff is set, all diff will be applied first and then execute the call // message. type OverrideAccount struct { Nonce *hexutil.Uint64 `json:"nonce"` @@ -1301,12 +1300,12 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash // // Note, this function doesn't make and changes in the state/blockchain and is // useful to execute and retrieve values. -func (s *BlockChainAPI) Call(ctx context.Context, args TransactionArgs, blockNrOrHash *rpc.BlockNumberOrHash, overrides *StateOverride, blockOverrides *BlockOverrides) (hexutil.Bytes, error) { +func (api *BlockChainAPI) Call(ctx context.Context, args TransactionArgs, blockNrOrHash *rpc.BlockNumberOrHash, overrides *StateOverride, blockOverrides *BlockOverrides) (hexutil.Bytes, error) { if blockNrOrHash == nil { latest := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber) blockNrOrHash = &latest } - result, err := DoCall(ctx, s.b, args, *blockNrOrHash, overrides, blockOverrides, s.b.RPCEVMTimeout(), s.b.RPCGasCap()) + result, err := DoCall(ctx, api.b, args, *blockNrOrHash, overrides, blockOverrides, api.b.RPCEVMTimeout(), api.b.RPCGasCap()) if err != nil { return nil, err } @@ -1338,11 +1337,17 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr State: state, ErrorRatio: estimateGasErrorRatio, } + // Set any required transaction default, but make sure the gas cap itself is not messed with + // if it was not specified in the original argument list. + if args.Gas == nil { + args.Gas = new(hexutil.Uint64) + } if err := args.CallDefaults(gasCap, header.BaseFee, b.ChainConfig().ChainID); err != nil { return 0, err } call := args.ToMessage(header.BaseFee) - // Run the gas estimation andwrap any revertals into a custom return + + // Run the gas estimation and wrap any revertals into a custom return estimate, revert, err := gasestimator.Estimate(ctx, call, opts, gasCap) if err != nil { if len(revert) > 0 { @@ -1359,12 +1364,12 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr // value is capped by both `args.Gas` (if non-nil & non-zero) and the backend's RPCGasCap // configuration (if non-zero). // Note: Required blob gas is not computed in this method. -func (s *BlockChainAPI) EstimateGas(ctx context.Context, args TransactionArgs, blockNrOrHash *rpc.BlockNumberOrHash, overrides *StateOverride) (hexutil.Uint64, error) { +func (api *BlockChainAPI) EstimateGas(ctx context.Context, args TransactionArgs, blockNrOrHash *rpc.BlockNumberOrHash, overrides *StateOverride) (hexutil.Uint64, error) { bNrOrHash := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber) if blockNrOrHash != nil { bNrOrHash = *blockNrOrHash } - return DoEstimateGas(ctx, s.b, args, bNrOrHash, overrides, s.b.RPCGasCap()) + return DoEstimateGas(ctx, api.b, args, bNrOrHash, overrides, api.b.RPCGasCap()) } func (s *BlockChainAPI) needToReplay(ctx context.Context, block *types.Block, accounts []common.Address) (bool, error) { @@ -1618,18 +1623,18 @@ func RPCMarshalBlock(block *types.Block, inclTx bool, fullTx bool, config *param // rpcMarshalHeader uses the generalized output filler, then adds the total difficulty field, which requires // a `BlockchainAPI`. -func (s *BlockChainAPI) rpcMarshalHeader(ctx context.Context, header *types.Header) map[string]interface{} { +func (api *BlockChainAPI) rpcMarshalHeader(ctx context.Context, header *types.Header) map[string]interface{} { fields := RPCMarshalHeader(header) - fields["totalDifficulty"] = (*hexutil.Big)(s.b.GetTd(ctx, header.Hash())) + fields["totalDifficulty"] = (*hexutil.Big)(api.b.GetTd(ctx, header.Hash())) return fields } // rpcMarshalBlock uses the generalized output filler, then adds the total difficulty field, which requires // a `BlockchainAPI`. -func (s *BlockChainAPI) rpcMarshalBlock(ctx context.Context, b *types.Block, inclTx bool, fullTx bool) (map[string]interface{}, error) { - fields := RPCMarshalBlock(b, inclTx, fullTx, s.b.ChainConfig()) +func (api *BlockChainAPI) rpcMarshalBlock(ctx context.Context, b *types.Block, inclTx bool, fullTx bool) (map[string]interface{}, error) { + fields := RPCMarshalBlock(b, inclTx, fullTx, api.b.ChainConfig()) if inclTx { - fields["totalDifficulty"] = (*hexutil.Big)(s.b.GetTd(ctx, b.Hash())) + fields["totalDifficulty"] = (*hexutil.Big)(api.b.GetTd(ctx, b.Hash())) } return fields, nil } @@ -1804,12 +1809,12 @@ type accessListResult struct { // CreateAccessList creates an EIP-2930 type AccessList for the given transaction. // Reexec and BlockNrOrHash can be specified to create the accessList on top of a certain state. -func (s *BlockChainAPI) CreateAccessList(ctx context.Context, args TransactionArgs, blockNrOrHash *rpc.BlockNumberOrHash) (*accessListResult, error) { +func (api *BlockChainAPI) CreateAccessList(ctx context.Context, args TransactionArgs, blockNrOrHash *rpc.BlockNumberOrHash) (*accessListResult, error) { bNrOrHash := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber) if blockNrOrHash != nil { bNrOrHash = *blockNrOrHash } - acl, gasUsed, vmerr, err := AccessList(ctx, s.b, bNrOrHash, args) + acl, gasUsed, vmerr, err := AccessList(ctx, api.b, bNrOrHash, args) if err != nil { return nil, err } @@ -1850,6 +1855,9 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH prevTracer = logger.NewAccessListTracer(*args.AccessList, args.from(), to, precompiles) } for { + if err := ctx.Err(); err != nil { + return nil, 0, nil, err + } // Retrieve the current access list to expand accessList := prevTracer.AccessList() log.Trace("Creating access list", "input", accessList) @@ -1891,8 +1899,8 @@ func NewTransactionAPI(b Backend, nonceLock *AddrLocker) *TransactionAPI { } // GetBlockTransactionCountByNumber returns the number of transactions in the block with the given block number. -func (s *TransactionAPI) GetBlockTransactionCountByNumber(ctx context.Context, blockNr rpc.BlockNumber) *hexutil.Uint { - if block, _ := s.b.BlockByNumber(ctx, blockNr); block != nil { +func (api *TransactionAPI) GetBlockTransactionCountByNumber(ctx context.Context, blockNr rpc.BlockNumber) *hexutil.Uint { + if block, _ := api.b.BlockByNumber(ctx, blockNr); block != nil { n := hexutil.Uint(len(block.Transactions())) return &n } @@ -1900,8 +1908,8 @@ func (s *TransactionAPI) GetBlockTransactionCountByNumber(ctx context.Context, b } // GetBlockTransactionCountByHash returns the number of transactions in the block with the given hash. -func (s *TransactionAPI) GetBlockTransactionCountByHash(ctx context.Context, blockHash common.Hash) *hexutil.Uint { - if block, _ := s.b.BlockByHash(ctx, blockHash); block != nil { +func (api *TransactionAPI) GetBlockTransactionCountByHash(ctx context.Context, blockHash common.Hash) *hexutil.Uint { + if block, _ := api.b.BlockByHash(ctx, blockHash); block != nil { n := hexutil.Uint(len(block.Transactions())) return &n } @@ -1917,49 +1925,49 @@ func (s *TransactionAPI) GetTransactionsByBlockNumber(ctx context.Context, block } // GetTransactionByBlockNumberAndIndex returns the transaction for the given block number and index. -func (s *TransactionAPI) GetTransactionByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index hexutil.Uint) *RPCTransaction { - if block, _ := s.b.BlockByNumber(ctx, blockNr); block != nil { - return newRPCTransactionFromBlockIndex(block, uint64(index), s.b.ChainConfig()) +func (api *TransactionAPI) GetTransactionByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index hexutil.Uint) *RPCTransaction { + if block, _ := api.b.BlockByNumber(ctx, blockNr); block != nil { + return newRPCTransactionFromBlockIndex(block, uint64(index), api.b.ChainConfig()) } return nil } // GetTransactionByBlockHashAndIndex returns the transaction for the given block hash and index. -func (s *TransactionAPI) GetTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index hexutil.Uint) *RPCTransaction { - if block, _ := s.b.BlockByHash(ctx, blockHash); block != nil { - return newRPCTransactionFromBlockIndex(block, uint64(index), s.b.ChainConfig()) +func (api *TransactionAPI) GetTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index hexutil.Uint) *RPCTransaction { + if block, _ := api.b.BlockByHash(ctx, blockHash); block != nil { + return newRPCTransactionFromBlockIndex(block, uint64(index), api.b.ChainConfig()) } return nil } // GetRawTransactionByBlockNumberAndIndex returns the bytes of the transaction for the given block number and index. -func (s *TransactionAPI) GetRawTransactionByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index hexutil.Uint) hexutil.Bytes { - if block, _ := s.b.BlockByNumber(ctx, blockNr); block != nil { +func (api *TransactionAPI) GetRawTransactionByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index hexutil.Uint) hexutil.Bytes { + if block, _ := api.b.BlockByNumber(ctx, blockNr); block != nil { return newRPCRawTransactionFromBlockIndex(block, uint64(index)) } return nil } // GetRawTransactionByBlockHashAndIndex returns the bytes of the transaction for the given block hash and index. -func (s *TransactionAPI) GetRawTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index hexutil.Uint) hexutil.Bytes { - if block, _ := s.b.BlockByHash(ctx, blockHash); block != nil { +func (api *TransactionAPI) GetRawTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index hexutil.Uint) hexutil.Bytes { + if block, _ := api.b.BlockByHash(ctx, blockHash); block != nil { return newRPCRawTransactionFromBlockIndex(block, uint64(index)) } return nil } // GetTransactionCount returns the number of transactions the given address has sent for the given block number -func (s *TransactionAPI) GetTransactionCount(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (*hexutil.Uint64, error) { +func (api *TransactionAPI) GetTransactionCount(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (*hexutil.Uint64, error) { // Ask transaction pool for the nonce which includes pending transactions if blockNr, ok := blockNrOrHash.Number(); ok && blockNr == rpc.PendingBlockNumber { - nonce, err := s.b.GetPoolNonce(ctx, address) + nonce, err := api.b.GetPoolNonce(ctx, address) if err != nil { return nil, err } return (*hexutil.Uint64)(&nonce), nil } // Resolve block number and use its state to ask for the nonce - state, _, err := s.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) + state, _, err := api.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) if state == nil || err != nil { return nil, err } @@ -1968,32 +1976,32 @@ func (s *TransactionAPI) GetTransactionCount(ctx context.Context, address common } // GetTransactionByHash returns the transaction for the given hash -func (s *TransactionAPI) GetTransactionByHash(ctx context.Context, hash common.Hash) (*RPCTransaction, error) { +func (api *TransactionAPI) GetTransactionByHash(ctx context.Context, hash common.Hash) (*RPCTransaction, error) { // Try to return an already finalized transaction - found, tx, blockHash, blockNumber, index, err := s.b.GetTransaction(ctx, hash) + found, tx, blockHash, blockNumber, index, err := api.b.GetTransaction(ctx, hash) if !found { // No finalized transaction, try to retrieve it from the pool - if tx := s.b.GetPoolTransaction(hash); tx != nil { - return NewRPCPendingTransaction(tx, s.b.CurrentHeader(), s.b.ChainConfig()), nil + if tx := api.b.GetPoolTransaction(hash); tx != nil { + return NewRPCPendingTransaction(tx, api.b.CurrentHeader(), api.b.ChainConfig()), nil } if err == nil { return nil, nil } return nil, NewTxIndexingError() } - header, err := s.b.HeaderByHash(ctx, blockHash) + header, err := api.b.HeaderByHash(ctx, blockHash) if err != nil { return nil, err } - return newRPCTransaction(tx, blockHash, blockNumber, header.Time, index, header.BaseFee, s.b.ChainConfig()), nil + return newRPCTransaction(tx, blockHash, blockNumber, header.Time, index, header.BaseFee, api.b.ChainConfig()), nil } // GetRawTransactionByHash returns the bytes of the transaction for the given hash. -func (s *TransactionAPI) GetRawTransactionByHash(ctx context.Context, hash common.Hash) (hexutil.Bytes, error) { +func (api *TransactionAPI) GetRawTransactionByHash(ctx context.Context, hash common.Hash) (hexutil.Bytes, error) { // Retrieve a finalized transaction, or a pooled otherwise - found, tx, _, _, _, err := s.b.GetTransaction(ctx, hash) + found, tx, _, _, _, err := api.b.GetTransaction(ctx, hash) if !found { - if tx = s.b.GetPoolTransaction(hash); tx != nil { + if tx = api.b.GetPoolTransaction(hash); tx != nil { return tx.MarshalBinary() } if err == nil { @@ -2120,19 +2128,19 @@ func (s *TransactionAPI) GetTransactionDataAndReceipt(ctx context.Context, hash } // GetTransactionReceipt returns the transaction receipt for the given transaction hash. -func (s *TransactionAPI) GetTransactionReceipt(ctx context.Context, hash common.Hash) (map[string]interface{}, error) { - found, tx, blockHash, blockNumber, index, err := s.b.GetTransaction(ctx, hash) +func (api *TransactionAPI) GetTransactionReceipt(ctx context.Context, hash common.Hash) (map[string]interface{}, error) { + found, tx, blockHash, blockNumber, index, err := api.b.GetTransaction(ctx, hash) if err != nil { return nil, NewTxIndexingError() // transaction is not fully indexed } if !found { return nil, nil // transaction is not existent or reachable } - header, err := s.b.HeaderByHash(ctx, blockHash) + header, err := api.b.HeaderByHash(ctx, blockHash) if err != nil { return nil, err } - receipts, err := s.b.GetReceipts(ctx, blockHash) + receipts, err := api.b.GetReceipts(ctx, blockHash) if err != nil { return nil, err } @@ -2142,7 +2150,7 @@ func (s *TransactionAPI) GetTransactionReceipt(ctx context.Context, hash common. receipt := receipts[index] // Derive the sender. - signer := types.MakeSigner(s.b.ChainConfig(), header.Number, header.Time) + signer := types.MakeSigner(api.b.ChainConfig(), header.Number, header.Time) return marshalReceipt(receipt, blockHash, blockNumber, signer, tx, int(index)), nil } @@ -2218,16 +2226,16 @@ func marshalBlob(blobTxSidecar types.BlobTxSidecar, fullBlob bool) map[string]in } // sign is a helper function that signs a transaction with the private key of the given address. -func (s *TransactionAPI) sign(addr common.Address, tx *types.Transaction) (*types.Transaction, error) { +func (api *TransactionAPI) sign(addr common.Address, tx *types.Transaction) (*types.Transaction, error) { // Look up the wallet containing the requested signer account := accounts.Account{Address: addr} - wallet, err := s.b.AccountManager().Find(account) + wallet, err := api.b.AccountManager().Find(account) if err != nil { return nil, err } // Request the wallet to sign the transaction - return wallet.SignTx(account, tx, s.b.ChainConfig().ChainID) + return wallet.SignTx(account, tx, api.b.ChainConfig().ChainID) } // SubmitTransaction is a helper function that submits tx to txPool and logs a message. @@ -2264,11 +2272,11 @@ func SubmitTransaction(ctx context.Context, b Backend, tx *types.Transaction) (c // SendTransaction creates a transaction for the given argument, sign it and submit it to the // transaction pool. -func (s *TransactionAPI) SendTransaction(ctx context.Context, args TransactionArgs) (common.Hash, error) { +func (api *TransactionAPI) SendTransaction(ctx context.Context, args TransactionArgs) (common.Hash, error) { // Look up the wallet containing the requested signer account := accounts.Account{Address: args.from()} - wallet, err := s.b.AccountManager().Find(account) + wallet, err := api.b.AccountManager().Find(account) if err != nil { return common.Hash{}, err } @@ -2276,35 +2284,35 @@ func (s *TransactionAPI) SendTransaction(ctx context.Context, args TransactionAr if args.Nonce == nil { // Hold the mutex around signing to prevent concurrent assignment of // the same nonce to multiple accounts. - s.nonceLock.LockAddr(args.from()) - defer s.nonceLock.UnlockAddr(args.from()) + api.nonceLock.LockAddr(args.from()) + defer api.nonceLock.UnlockAddr(args.from()) } if args.IsEIP4844() { return common.Hash{}, errBlobTxNotSupported } // Set some sanity defaults and terminate on failure - if err := args.setDefaults(ctx, s.b, false); err != nil { + if err := args.setDefaults(ctx, api.b, false); err != nil { return common.Hash{}, err } // Assemble the transaction and sign with the wallet tx := args.ToTransaction() - signed, err := wallet.SignTx(account, tx, s.b.ChainConfig().ChainID) + signed, err := wallet.SignTx(account, tx, api.b.ChainConfig().ChainID) if err != nil { return common.Hash{}, err } - return SubmitTransaction(ctx, s.b, signed) + return SubmitTransaction(ctx, api.b, signed) } // FillTransaction fills the defaults (nonce, gas, gasPrice or 1559 fields) // on a given unsigned transaction, and returns it to the caller for further // processing (signing + broadcast). -func (s *TransactionAPI) FillTransaction(ctx context.Context, args TransactionArgs) (*SignTransactionResult, error) { +func (api *TransactionAPI) FillTransaction(ctx context.Context, args TransactionArgs) (*SignTransactionResult, error) { args.blobSidecarAllowed = true // Set some sanity defaults and terminate on failure - if err := args.setDefaults(ctx, s.b, false); err != nil { + if err := args.setDefaults(ctx, api.b, false); err != nil { return nil, err } // Assemble the transaction and obtain rlp @@ -2318,12 +2326,12 @@ func (s *TransactionAPI) FillTransaction(ctx context.Context, args TransactionAr // SendRawTransaction will add the signed transaction to the transaction pool. // The sender is responsible for signing the transaction and using the correct nonce. -func (s *TransactionAPI) SendRawTransaction(ctx context.Context, input hexutil.Bytes) (common.Hash, error) { +func (api *TransactionAPI) SendRawTransaction(ctx context.Context, input hexutil.Bytes) (common.Hash, error) { tx := new(types.Transaction) if err := tx.UnmarshalBinary(input); err != nil { return common.Hash{}, err } - return SubmitTransaction(ctx, s.b, tx) + return SubmitTransaction(ctx, api.b, tx) } // SendRawTransactionConditional will add the signed transaction to the transaction pool. @@ -2353,11 +2361,11 @@ func (s *TransactionAPI) SendRawTransactionConditional(ctx context.Context, inpu // The account associated with addr must be unlocked. // // https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign -func (s *TransactionAPI) Sign(addr common.Address, data hexutil.Bytes) (hexutil.Bytes, error) { +func (api *TransactionAPI) Sign(addr common.Address, data hexutil.Bytes) (hexutil.Bytes, error) { // Look up the wallet containing the requested signer account := accounts.Account{Address: addr} - wallet, err := s.b.AccountManager().Find(account) + wallet, err := api.b.AccountManager().Find(account) if err != nil { return nil, err } @@ -2378,7 +2386,7 @@ type SignTransactionResult struct { // SignTransaction will sign the given transaction with the from account. // The node needs to have the private key of the account corresponding with // the given from address and it needs to be unlocked. -func (s *TransactionAPI) SignTransaction(ctx context.Context, args TransactionArgs) (*SignTransactionResult, error) { +func (api *TransactionAPI) SignTransaction(ctx context.Context, args TransactionArgs) (*SignTransactionResult, error) { args.blobSidecarAllowed = true if args.Gas == nil { @@ -2390,15 +2398,15 @@ func (s *TransactionAPI) SignTransaction(ctx context.Context, args TransactionAr if args.Nonce == nil { return nil, errors.New("nonce not specified") } - if err := args.setDefaults(ctx, s.b, false); err != nil { + if err := args.setDefaults(ctx, api.b, false); err != nil { return nil, err } // Before actually sign the transaction, ensure the transaction fee is reasonable. tx := args.ToTransaction() - if err := checkTxFee(tx.GasPrice(), tx.Gas(), s.b.RPCTxFeeCap()); err != nil { + if err := checkTxFee(tx.GasPrice(), tx.Gas(), api.b.RPCTxFeeCap()); err != nil { return nil, err } - signed, err := s.sign(args.from(), tx) + signed, err := api.sign(args.from(), tx) if err != nil { return nil, err } @@ -2421,23 +2429,23 @@ func (s *TransactionAPI) SignTransaction(ctx context.Context, args TransactionAr // PendingTransactions returns the transactions that are in the transaction pool // and have a from address that is one of the accounts this node manages. -func (s *TransactionAPI) PendingTransactions() ([]*RPCTransaction, error) { - pending, err := s.b.GetPoolTransactions() +func (api *TransactionAPI) PendingTransactions() ([]*RPCTransaction, error) { + pending, err := api.b.GetPoolTransactions() if err != nil { return nil, err } accounts := make(map[common.Address]struct{}) - for _, wallet := range s.b.AccountManager().Wallets() { + for _, wallet := range api.b.AccountManager().Wallets() { for _, account := range wallet.Accounts() { accounts[account.Address] = struct{}{} } } - curHeader := s.b.CurrentHeader() + curHeader := api.b.CurrentHeader() transactions := make([]*RPCTransaction, 0, len(pending)) for _, tx := range pending { - from, _ := types.Sender(s.signer, tx) + from, _ := types.Sender(api.signer, tx) if _, exists := accounts[from]; exists { - transactions = append(transactions, NewRPCPendingTransaction(tx, curHeader, s.b.ChainConfig())) + transactions = append(transactions, NewRPCPendingTransaction(tx, curHeader, api.b.ChainConfig())) } } return transactions, nil @@ -2445,11 +2453,11 @@ func (s *TransactionAPI) PendingTransactions() ([]*RPCTransaction, error) { // Resend accepts an existing transaction and a new gas price and limit. It will remove // the given transaction from the pool and reinsert it with the new gas price and limit. -func (s *TransactionAPI) Resend(ctx context.Context, sendArgs TransactionArgs, gasPrice *hexutil.Big, gasLimit *hexutil.Uint64) (common.Hash, error) { +func (api *TransactionAPI) Resend(ctx context.Context, sendArgs TransactionArgs, gasPrice *hexutil.Big, gasLimit *hexutil.Uint64) (common.Hash, error) { if sendArgs.Nonce == nil { return common.Hash{}, errors.New("missing transaction nonce in transaction spec") } - if err := sendArgs.setDefaults(ctx, s.b, false); err != nil { + if err := sendArgs.setDefaults(ctx, api.b, false); err != nil { return common.Hash{}, err } matchTx := sendArgs.ToTransaction() @@ -2463,18 +2471,18 @@ func (s *TransactionAPI) Resend(ctx context.Context, sendArgs TransactionArgs, g if gasLimit != nil { gas = uint64(*gasLimit) } - if err := checkTxFee(price, gas, s.b.RPCTxFeeCap()); err != nil { + if err := checkTxFee(price, gas, api.b.RPCTxFeeCap()); err != nil { return common.Hash{}, err } // Iterate the pending list for replacement - pending, err := s.b.GetPoolTransactions() + pending, err := api.b.GetPoolTransactions() if err != nil { return common.Hash{}, err } for _, p := range pending { - wantSigHash := s.signer.Hash(matchTx) - pFrom, err := types.Sender(s.signer, p) - if err == nil && pFrom == sendArgs.from() && s.signer.Hash(p) == wantSigHash { + wantSigHash := api.signer.Hash(matchTx) + pFrom, err := types.Sender(api.signer, p) + if err == nil && pFrom == sendArgs.from() && api.signer.Hash(p) == wantSigHash { // Match. Re-sign and send the transaction. if gasPrice != nil && (*big.Int)(gasPrice).Sign() != 0 { sendArgs.GasPrice = gasPrice @@ -2482,11 +2490,11 @@ func (s *TransactionAPI) Resend(ctx context.Context, sendArgs TransactionArgs, g if gasLimit != nil && *gasLimit != 0 { sendArgs.Gas = gasLimit } - signedTx, err := s.sign(sendArgs.from(), sendArgs.ToTransaction()) + signedTx, err := api.sign(sendArgs.from(), sendArgs.ToTransaction()) if err != nil { return common.Hash{}, err } - if err = s.b.SendTx(ctx, signedTx); err != nil { + if err = api.b.SendTx(ctx, signedTx); err != nil { return common.Hash{}, err } return signedTx.Hash(), nil @@ -2572,11 +2580,11 @@ func (api *DebugAPI) GetRawReceipts(ctx context.Context, blockNrOrHash rpc.Block } // GetRawTransaction returns the bytes of the transaction for the given hash. -func (s *DebugAPI) GetRawTransaction(ctx context.Context, hash common.Hash) (hexutil.Bytes, error) { +func (api *DebugAPI) GetRawTransaction(ctx context.Context, hash common.Hash) (hexutil.Bytes, error) { // Retrieve a finalized transaction, or a pooled otherwise - found, tx, _, _, _, err := s.b.GetTransaction(ctx, hash) + found, tx, _, _, _, err := api.b.GetTransaction(ctx, hash) if !found { - if tx = s.b.GetPoolTransaction(hash); tx != nil { + if tx = api.b.GetPoolTransaction(hash); tx != nil { return tx.MarshalBinary() } if err == nil { @@ -2597,8 +2605,8 @@ func (api *DebugAPI) PrintBlock(ctx context.Context, number uint64) (string, err } // ChaindbProperty returns leveldb properties of the key-value database. -func (api *DebugAPI) ChaindbProperty(property string) (string, error) { - return api.b.ChainDb().Stat(property) +func (api *DebugAPI) ChaindbProperty() (string, error) { + return api.b.ChainDb().Stat() } // ChaindbCompact flattens the entire key-value database into a single level, @@ -2639,18 +2647,18 @@ func NewNetAPI(net *p2p.Server, networkVersion uint64) *NetAPI { } // Listening returns an indication if the node is listening for network connections. -func (s *NetAPI) Listening() bool { +func (api *NetAPI) Listening() bool { return true // always listening } // PeerCount returns the number of connected peers -func (s *NetAPI) PeerCount() hexutil.Uint { - return hexutil.Uint(s.net.PeerCount()) +func (api *NetAPI) PeerCount() hexutil.Uint { + return hexutil.Uint(api.net.PeerCount()) } // Version returns the current ethereum protocol version. -func (s *NetAPI) Version() string { - return fmt.Sprintf("%d", s.networkVersion) +func (api *NetAPI) Version() string { + return fmt.Sprintf("%d", api.networkVersion) } // NodeInfo retrieves all the information we know about the host node at the diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index 461de4256b..04c14fa4d9 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -800,7 +800,7 @@ func TestEstimateGas(t *testing.T) { From: &accounts[0].addr, To: &accounts[1].addr, Value: (*hexutil.Big)(big.NewInt(1)), - BlobHashes: []common.Hash{common.Hash{0x01, 0x22}}, + BlobHashes: []common.Hash{{0x01, 0x22}}, BlobFeeCap: (*hexutil.Big)(big.NewInt(1)), }, want: 21000, @@ -988,7 +988,7 @@ func TestCall(t *testing.T) { call: TransactionArgs{ From: &accounts[1].addr, To: &randomAccounts[2].addr, - BlobHashes: []common.Hash{common.Hash{0x01, 0x22}}, + BlobHashes: []common.Hash{{0x01, 0x22}}, BlobFeeCap: (*hexutil.Big)(big.NewInt(1)), }, overrides: StateOverride{ @@ -1112,7 +1112,7 @@ func TestSendBlobTransaction(t *testing.T) { From: &b.acc.Address, To: &to, Value: (*hexutil.Big)(big.NewInt(1)), - BlobHashes: []common.Hash{common.Hash{0x01, 0x22}}, + BlobHashes: []common.Hash{{0x01, 0x22}}, }) if err != nil { t.Fatalf("failed to fill tx defaults: %v\n", err) @@ -1397,7 +1397,7 @@ func TestRPCMarshalBlock(t *testing.T) { } txs = append(txs, tx) } - block := types.NewBlock(&types.Header{Number: big.NewInt(100)}, txs, nil, nil, blocktest.NewHasher()) + block := types.NewBlock(&types.Header{Number: big.NewInt(100)}, &types.Body{Transactions: txs}, nil, blocktest.NewHasher()) var testSuite = []struct { inclTx bool @@ -1608,7 +1608,7 @@ func TestRPCGetBlockOrHeader(t *testing.T) { Address: common.Address{0x12, 0x34}, Amount: 10, } - pending = types.NewBlockWithWithdrawals(&types.Header{Number: big.NewInt(11), Time: 42}, []*types.Transaction{tx}, nil, nil, []*types.Withdrawal{withdrawal}, blocktest.NewHasher()) + pending = types.NewBlock(&types.Header{Number: big.NewInt(11), Time: 42}, &types.Body{Transactions: types.Transactions{tx}, Withdrawals: types.Withdrawals{withdrawal}}, nil, blocktest.NewHasher()) ) backend := newTestBackend(t, genBlocks, genesis, ethash.NewFaker(), func(i int, b *core.BlockGen) { // Transfer from account[0] to account[1] diff --git a/internal/testlog/testlog.go b/internal/testlog/testlog.go index 3740dd1f24..ad61af9eac 100644 --- a/internal/testlog/testlog.go +++ b/internal/testlog/testlog.go @@ -58,7 +58,7 @@ func (h *bufHandler) Handle(_ context.Context, r slog.Record) error { } func (h *bufHandler) Enabled(_ context.Context, lvl slog.Level) bool { - return lvl <= h.level + return lvl >= h.level } func (h *bufHandler) WithAttrs(attrs []slog.Attr) slog.Handler { diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index 46a9a6afb1..10f018f2ab 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -261,7 +261,6 @@ web3._extend({ new web3._extend.Method({ name: 'chaindbProperty', call: 'debug_chaindbProperty', - params: 1, outputFormatter: console.log }), new web3._extend.Method({ diff --git a/log/handler.go b/log/handler.go index df79fc06ca..478e272ffc 100644 --- a/log/handler.go +++ b/log/handler.go @@ -103,10 +103,10 @@ func (h *TerminalHandler) WithAttrs(attrs []slog.Attr) slog.Handler { } // ResetFieldPadding zeroes the field-padding for all attribute pairs. -func (t *TerminalHandler) ResetFieldPadding() { - t.mu.Lock() - t.fieldPadding = make(map[string]int) - t.mu.Unlock() +func (h *TerminalHandler) ResetFieldPadding() { + h.mu.Lock() + h.fieldPadding = make(map[string]int) + h.mu.Unlock() } type leveler struct{ minLevel slog.Level } diff --git a/log/handler_glog.go b/log/handler_glog.go index 625a036403..739f8c5b42 100644 --- a/log/handler_glog.go +++ b/log/handler_glog.go @@ -139,11 +139,15 @@ func (h *GlogHandler) Vmodule(ruleset string) error { return nil } +// Enabled implements slog.Handler, reporting whether the handler handles records +// at the given level. func (h *GlogHandler) Enabled(ctx context.Context, lvl slog.Level) bool { // fast-track skipping logging if override not enabled and the provided verbosity is above configured return h.override.Load() || slog.Level(h.level.Load()) <= lvl } +// WithAttrs implements slog.Handler, returning a new Handler whose attributes +// consist of both the receiver's attributes and the arguments. func (h *GlogHandler) WithAttrs(attrs []slog.Attr) slog.Handler { h.lock.RLock() siteCache := maps.Clone(h.siteCache) @@ -164,12 +168,16 @@ func (h *GlogHandler) WithAttrs(attrs []slog.Attr) slog.Handler { return &res } +// WithGroup implements slog.Handler, returning a new Handler with the given +// group appended to the receiver's existing groups. +// +// Note, this function is not implemented. func (h *GlogHandler) WithGroup(name string) slog.Handler { panic("not implemented") } -// Log implements Handler.Log, filtering a log record through the global, local -// and backtrace filters, finally emitting it if either allow it through. +// Handle implements slog.Handler, filtering a log record through the global, +// local and backtrace filters, finally emitting it if either allow it through. func (h *GlogHandler) Handle(_ context.Context, r slog.Record) error { // If the global log level allows, fast track logging if slog.Level(h.level.Load()) <= r.Level { diff --git a/log/logger.go b/log/logger.go index 60e951bb8d..f4e7acba36 100644 --- a/log/logger.go +++ b/log/logger.go @@ -36,7 +36,7 @@ const ( LvlDebug = LevelDebug ) -// convert from old Geth verbosity level constants +// FromLegacyLevel converts from old Geth verbosity level constants // to levels defined by slog func FromLegacyLevel(lvl int) slog.Level { switch lvl { @@ -129,7 +129,7 @@ type Logger interface { // With returns a new Logger that has this logger's attributes plus the given attributes With(ctx ...interface{}) Logger - // With returns a new Logger that has this logger's attributes plus the given attributes. Identical to 'With'. + // New returns a new Logger that has this logger's attributes plus the given attributes. Identical to 'With'. New(ctx ...interface{}) Logger // Log logs a message at the specified level with context key/value pairs @@ -178,7 +178,7 @@ func (l *logger) Handler() slog.Handler { return l.inner.Handler() } -// Write logs a message at the specified level: +// Write logs a message at the specified level. func (l *logger) Write(level slog.Level, msg string, attrs ...any) { if !l.inner.Enabled(context.Background(), level) { return diff --git a/log/logger_test.go b/log/logger_test.go index d999039860..067e1a0265 100644 --- a/log/logger_test.go +++ b/log/logger_test.go @@ -26,7 +26,7 @@ func TestLoggingWithVmodule(t *testing.T) { logger.Trace("a message", "foo", "bar") have := out.String() // The timestamp is locale-dependent, so we want to trim that off - // "INFO [01-01|00:00:00.000] a messag ..." -> "a messag..." + // "INFO [01-01|00:00:00.000] a message ..." -> "a message..." have = strings.Split(have, "]")[1] want := " a message foo=bar\n" if have != want { @@ -42,7 +42,7 @@ func TestTerminalHandlerWithAttrs(t *testing.T) { logger.Trace("a message", "foo", "bar") have := out.String() // The timestamp is locale-dependent, so we want to trim that off - // "INFO [01-01|00:00:00.000] a messag ..." -> "a messag..." + // "INFO [01-01|00:00:00.000] a message ..." -> "a message..." have = strings.Split(have, "]")[1] want := " a message baz=bat foo=bar\n" if have != want { @@ -97,7 +97,7 @@ func benchmarkLogger(b *testing.B, l Logger) { tt = time.Now() bigint = big.NewInt(100) nilbig *big.Int - err = errors.New("Oh nooes it's crap") + err = errors.New("oh nooes it's crap") ) b.ReportAllocs() b.ResetTimer() @@ -126,7 +126,7 @@ func TestLoggerOutput(t *testing.T) { tt = time.Time{} bigint = big.NewInt(100) nilbig *big.Int - err = errors.New("Oh nooes it's crap") + err = errors.New("oh nooes it's crap") smallUint = uint256.NewInt(500_000) bigUint = &uint256.Int{0xff, 0xff, 0xff, 0xff} ) @@ -150,7 +150,7 @@ func TestLoggerOutput(t *testing.T) { have := out.String() t.Logf("output %v", out.String()) - want := `INFO [11-07|19:14:33.821] This is a message foo=123 bytes="[0 0 0 0 0 0 0 0 0 0]" bonk="a string with text" time=0001-01-01T00:00:00+0000 bigint=100 nilbig= err="Oh nooes it's crap" struct="{A:Foo B:12}" struct="{A:Foo\nLinebreak B:122}" ptrstruct="&{A:Foo B:12}" smalluint=500,000 bigUint=1,600,660,942,523,603,594,864,898,306,482,794,244,293,965,082,972,225,630,372,095 + want := `INFO [11-07|19:14:33.821] This is a message foo=123 bytes="[0 0 0 0 0 0 0 0 0 0]" bonk="a string with text" time=0001-01-01T00:00:00+0000 bigint=100 nilbig= err="oh nooes it's crap" struct="{A:Foo B:12}" struct="{A:Foo\nLinebreak B:122}" ptrstruct="&{A:Foo B:12}" smalluint=500,000 bigUint=1,600,660,942,523,603,594,864,898,306,482,794,244,293,965,082,972,225,630,372,095 ` if !bytes.Equal([]byte(have)[25:], []byte(want)[25:]) { t.Errorf("Error\nhave: %q\nwant: %q", have, want) diff --git a/metrics/debug.go b/metrics/debug.go index de4a2739fe..9dfee1a866 100644 --- a/metrics/debug.go +++ b/metrics/debug.go @@ -19,18 +19,18 @@ var ( gcStats debug.GCStats ) -// Capture new values for the Go garbage collector statistics exported in -// debug.GCStats. This is designed to be called as a goroutine. +// CaptureDebugGCStats captures new values for the Go garbage collector statistics +// exported in debug.GCStats. This is designed to be called as a goroutine. func CaptureDebugGCStats(r Registry, d time.Duration) { for range time.Tick(d) { CaptureDebugGCStatsOnce(r) } } -// Capture new values for the Go garbage collector statistics exported in -// debug.GCStats. This is designed to be called in a background goroutine. -// Giving a registry which has not been given to RegisterDebugGCStats will -// panic. +// CaptureDebugGCStatsOnce captures new values for the Go garbage collector +// statistics exported in debug.GCStats. This is designed to be called in +// a background goroutine. Giving a registry which has not been given to +// RegisterDebugGCStats will panic. // // Be careful (but much less so) with this because debug.ReadGCStats calls // the C function runtime·lock(runtime·mheap) which, while not a stop-the-world @@ -50,9 +50,9 @@ func CaptureDebugGCStatsOnce(r Registry) { debugMetrics.GCStats.PauseTotal.Update(int64(gcStats.PauseTotal)) } -// Register metrics for the Go garbage collector statistics exported in -// debug.GCStats. The metrics are named by their fully-qualified Go symbols, -// i.e. debug.GCStats.PauseTotal. +// RegisterDebugGCStats registers metrics for the Go garbage collector statistics +// exported in debug.GCStats. The metrics are named by their fully-qualified Go +// symbols, i.e. debug.GCStats.PauseTotal. func RegisterDebugGCStats(r Registry) { debugMetrics.GCStats.LastGC = NewGauge() debugMetrics.GCStats.NumGC = NewGauge() diff --git a/metrics/sample_test.go b/metrics/sample_test.go index 9835ec1c30..4227b43ef7 100644 --- a/metrics/sample_test.go +++ b/metrics/sample_test.go @@ -103,18 +103,18 @@ func TestExpDecaySample(t *testing.T) { } snap := sample.Snapshot() if have, want := int(snap.Count()), tc.updates; have != want { - t.Errorf("have %d want %d", have, want) + t.Errorf("unexpected count: have %d want %d", have, want) } if have, want := snap.Size(), min(tc.updates, tc.reservoirSize); have != want { - t.Errorf("have %d want %d", have, want) + t.Errorf("unexpected size: have %d want %d", have, want) } values := snap.(*sampleSnapshot).values if have, want := len(values), min(tc.updates, tc.reservoirSize); have != want { - t.Errorf("have %d want %d", have, want) + t.Errorf("unexpected values length: have %d want %d", have, want) } for _, v := range values { if v > int64(tc.updates) || v < 0 { - t.Errorf("out of range [0, %d): %v", tc.updates, v) + t.Errorf("out of range [0, %d]: %v", tc.updates, v) } } } @@ -125,12 +125,12 @@ func TestExpDecaySample(t *testing.T) { // The priority becomes +Inf quickly after starting if this is done, // effectively freezing the set of samples until a rescale step happens. func TestExpDecaySampleNanosecondRegression(t *testing.T) { - sw := NewExpDecaySample(100, 0.99) - for i := 0; i < 100; i++ { + sw := NewExpDecaySample(1000, 0.99) + for i := 0; i < 1000; i++ { sw.Update(10) } time.Sleep(1 * time.Millisecond) - for i := 0; i < 100; i++ { + for i := 0; i < 1000; i++ { sw.Update(20) } s := sw.Snapshot() @@ -195,7 +195,7 @@ func TestUniformSample(t *testing.T) { } for _, v := range values { if v > 1000 || v < 0 { - t.Errorf("out of range [0, 100): %v\n", v) + t.Errorf("out of range [0, 1000]: %v\n", v) } } } @@ -251,6 +251,9 @@ func benchmarkSample(b *testing.B, s Sample) { } func testExpDecaySampleStatistics(t *testing.T, s SampleSnapshot) { + if sum := s.Sum(); sum != 496598 { + t.Errorf("s.Sum(): 496598 != %v\n", sum) + } if count := s.Count(); count != 10000 { t.Errorf("s.Count(): 10000 != %v\n", count) } diff --git a/miner/miner_test.go b/miner/miner_test.go index ce08651eba..94b7ab8e62 100644 --- a/miner/miner_test.go +++ b/miner/miner_test.go @@ -84,7 +84,7 @@ func (bc *testBlockChain) CurrentBlock() *types.Header { } func (bc *testBlockChain) GetBlock(hash common.Hash, number uint64) *types.Block { - return types.NewBlock(bc.CurrentBlock(), nil, nil, nil, trie.NewStackTrie(nil)) + return types.NewBlock(bc.CurrentBlock(), nil, nil, trie.NewStackTrie(nil)) } func (bc *testBlockChain) StateAt(common.Hash) (*state.StateDB, error) { diff --git a/miner/payload_building_test.go b/miner/payload_building_test.go index 708072b5ec..0b4d0a2db0 100644 --- a/miner/payload_building_test.go +++ b/miner/payload_building_test.go @@ -17,18 +17,135 @@ package miner import ( + "math/big" "reflect" "testing" "time" + "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/beacon/engine" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/clique" "github.com/ethereum/go-ethereum/consensus/ethash" + "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/txpool" + "github.com/ethereum/go-ethereum/core/txpool/legacypool" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/params" ) +var ( + // Test chain configurations + testTxPoolConfig legacypool.Config + ethashChainConfig *params.ChainConfig + cliqueChainConfig *params.ChainConfig + + // Test accounts + testBankKey, _ = crypto.GenerateKey() + testBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey) + testBankFunds = big.NewInt(1000000000000000000) + + testUserKey, _ = crypto.GenerateKey() + testUserAddress = crypto.PubkeyToAddress(testUserKey.PublicKey) + + // Test transactions + pendingTxs []*types.Transaction + newTxs []*types.Transaction + + testConfig = Config{ + PendingFeeRecipient: testBankAddress, + Recommit: time.Second, + GasCeil: params.GenesisGasLimit, + } +) + +func init() { + testTxPoolConfig = legacypool.DefaultConfig + testTxPoolConfig.Journal = "" + ethashChainConfig = new(params.ChainConfig) + *ethashChainConfig = *params.TestChainConfig + cliqueChainConfig = new(params.ChainConfig) + *cliqueChainConfig = *params.TestChainConfig + cliqueChainConfig.Clique = ¶ms.CliqueConfig{ + Period: 10, + Epoch: 30000, + } + + signer := types.LatestSigner(params.TestChainConfig) + tx1 := types.MustSignNewTx(testBankKey, signer, &types.AccessListTx{ + ChainID: params.TestChainConfig.ChainID, + Nonce: 0, + To: &testUserAddress, + Value: big.NewInt(1000), + Gas: params.TxGas, + GasPrice: big.NewInt(params.InitialBaseFee), + }) + pendingTxs = append(pendingTxs, tx1) + + tx2 := types.MustSignNewTx(testBankKey, signer, &types.LegacyTx{ + Nonce: 1, + To: &testUserAddress, + Value: big.NewInt(1000), + Gas: params.TxGas, + GasPrice: big.NewInt(params.InitialBaseFee), + }) + newTxs = append(newTxs, tx2) +} + +// testWorkerBackend implements worker.Backend interfaces and wraps all information needed during the testing. +type testWorkerBackend struct { + db ethdb.Database + txPool *txpool.TxPool + chain *core.BlockChain + genesis *core.Genesis +} + +func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine, db ethdb.Database, n int) *testWorkerBackend { + var gspec = &core.Genesis{ + Config: chainConfig, + Alloc: types.GenesisAlloc{testBankAddress: {Balance: testBankFunds}}, + } + switch e := engine.(type) { + case *clique.Clique: + gspec.ExtraData = make([]byte, 32+common.AddressLength+crypto.SignatureLength) + copy(gspec.ExtraData[32:32+common.AddressLength], testBankAddress.Bytes()) + e.Authorize(testBankAddress, func(account accounts.Account, s string, data []byte) ([]byte, error) { + return crypto.Sign(crypto.Keccak256(data), testBankKey) + }) + case *ethash.Ethash: + default: + t.Fatalf("unexpected consensus engine type: %T", engine) + } + chain, err := core.NewBlockChain(db, &core.CacheConfig{TrieDirtyDisabled: true}, gspec, nil, engine, vm.Config{}, nil, nil) + if err != nil { + t.Fatalf("core.NewBlockChain failed: %v", err) + } + pool := legacypool.New(testTxPoolConfig, chain) + txpool, _ := txpool.New(testTxPoolConfig.PriceLimit, chain, []txpool.SubPool{pool}) + + return &testWorkerBackend{ + db: db, + chain: chain, + txPool: txpool, + genesis: gspec, + } +} + +func (b *testWorkerBackend) BlockChain() *core.BlockChain { return b.chain } +func (b *testWorkerBackend) TxPool() *txpool.TxPool { return b.txPool } + +func newTestWorker(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine, db ethdb.Database, blocks int) (*Miner, *testWorkerBackend) { + backend := newTestWorkerBackend(t, chainConfig, engine, db, blocks) + backend.txPool.Add(pendingTxs, true, true) + w := New(backend, testConfig, engine) + return w, backend +} + func TestBuildPayload(t *testing.T) { t.Parallel() var ( diff --git a/miner/worker.go b/miner/worker.go index 7862c74860..a560cfbab6 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -238,9 +238,6 @@ type worker struct { // payload in proof-of-stake stage. recommit time.Duration - // External functions - isLocalBlock func(header *types.Header) bool // Function used to determine whether the specified block is mined by local miner. - // Test hooks newTaskHook func(*task) // Method to call upon receiving a new sealing task. skipSealHook func(*task) bool // Method to decide whether skipping the sealing. @@ -252,7 +249,7 @@ type worker struct { func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus.Engine, eth Backend, mux *event.TypeMux, init bool) *worker { recentMinedBlocks, _ := lru.New(recentMinedCacheLimit) worker := &worker{ - prefetcher: core.NewStatePrefetcher(chainConfig, eth.BlockChain(), engine), + prefetcher: core.NewStatePrefetcher(chainConfig, eth.BlockChain().HeadChain()), config: config, chainConfig: chainConfig, engine: engine, @@ -668,7 +665,7 @@ func (w *worker) makeEnv(parent *types.Header, header *types.Header, coinbase co return nil, err } if prevEnv == nil { - state.StartPrefetcher("miner") + state.StartPrefetcher("miner", nil) } else { state.TransferPrefetcher(prevEnv.state) } @@ -692,8 +689,7 @@ func (w *worker) updateSnapshot(env *environment) { w.snapshotBlock = types.NewBlock( env.header, - env.txs, - nil, + &types.Body{Transactions: env.txs}, env.receipts, trie.NewStackTrie(nil), ) diff --git a/node/api.go b/node/api.go index 93d7bdc8e2..c11e2b97cd 100644 --- a/node/api.go +++ b/node/api.go @@ -27,6 +27,7 @@ import ( "github.com/ethereum/go-ethereum/internal/debug" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/rpc" ) @@ -40,6 +41,9 @@ func (n *Node) apis() []rpc.API { }, { Namespace: "debug", Service: debug.Handler, + }, { + Namespace: "debug", + Service: &p2pDebugAPI{n}, }, { Namespace: "web3", Service: &web3API{n}, @@ -334,3 +338,16 @@ func (s *web3API) ClientVersion() string { func (s *web3API) Sha3(input hexutil.Bytes) hexutil.Bytes { return crypto.Keccak256(input) } + +// p2pDebugAPI provides access to p2p internals for debugging. +type p2pDebugAPI struct { + stack *Node +} + +func (s *p2pDebugAPI) DiscoveryV4Table() [][]discover.BucketNode { + disc := s.stack.server.DiscoveryV4() + if disc != nil { + return disc.TableBuckets() + } + return nil +} diff --git a/node/node.go b/node/node.go index 245a6a16e4..88674d6d82 100644 --- a/node/node.go +++ b/node/node.go @@ -37,6 +37,7 @@ import ( "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb/leveldb" + "github.com/ethereum/go-ethereum/ethdb/memorydb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/p2p" @@ -868,7 +869,7 @@ func (n *Node) OpenDatabaseWithFreezer(name string, cache, handles int, ancient, var db ethdb.Database var err error if n.config.DataDir == "" { - db = rawdb.NewMemoryDatabase() + db, err = rawdb.NewDatabaseWithFreezer(memorydb.New(), "", namespace, readonly, false, false, false, false) } else { db, err = rawdb.Open(rawdb.OpenOptions{ Type: n.config.DBEngine, diff --git a/p2p/dial.go b/p2p/dial.go index 3aa9f38d85..be8e153b80 100644 --- a/p2p/dial.go +++ b/p2p/dial.go @@ -66,11 +66,8 @@ type tcpDialer struct { } func (t tcpDialer) Dial(ctx context.Context, dest *enode.Node) (net.Conn, error) { - return t.d.DialContext(ctx, "tcp", nodeAddr(dest).String()) -} - -func nodeAddr(n *enode.Node) net.Addr { - return &net.TCPAddr{IP: n.IP(), Port: n.TCP()} + addr, _ := dest.TCPEndpoint() + return t.d.DialContext(ctx, "tcp", addr.String()) } // checkDial errors: @@ -249,7 +246,7 @@ loop: select { case node := <-nodesCh: if err := d.checkDial(node); err != nil { - d.log.Trace("Discarding dial candidate", "id", node.ID(), "ip", node.IP(), "reason", err) + d.log.Trace("Discarding dial candidate", "id", node.ID(), "ip", node.IPAddr(), "reason", err) } else { d.startDial(newDialTask(node, dynDialedConn)) } @@ -283,7 +280,7 @@ loop: case node := <-d.addStaticCh: id := node.ID() _, exists := d.static[id] - d.log.Trace("Adding static node", "id", id, "ip", node.IP(), "added", !exists) + d.log.Trace("Adding static node", "id", id, "ip", node.IPAddr(), "added", !exists) if exists { continue loop } @@ -382,7 +379,7 @@ func (d *dialScheduler) checkDial(n *enode.Node) error { if n.ID() == d.self { return errSelf } - if n.IP() != nil && n.TCP() == 0 { + if n.IPAddr().IsValid() && n.TCP() == 0 { // This check can trigger if a non-TCP node is found // by discovery. If there is no IP, the node is a static // node and the actual endpoint will be resolved later in dialTask. @@ -394,7 +391,7 @@ func (d *dialScheduler) checkDial(n *enode.Node) error { if _, ok := d.peers[n.ID()]; ok { return errAlreadyConnected } - if d.netRestrict != nil && !d.netRestrict.Contains(n.IP()) { + if d.netRestrict != nil && !d.netRestrict.ContainsAddr(n.IPAddr()) { return errNetRestrict } if d.history.contains(string(n.ID().Bytes())) { @@ -445,7 +442,7 @@ func (d *dialScheduler) removeFromStaticPool(idx int) { // startDial runs the given dial task in a separate goroutine. func (d *dialScheduler) startDial(task *dialTask) { node := task.dest() - d.log.Trace("Starting p2p dial", "id", node.ID(), "ip", node.IP(), "flag", task.flags) + d.log.Trace("Starting p2p dial", "id", node.ID(), "ip", node.IPAddr(), "flag", task.flags) hkey := string(node.ID().Bytes()) d.history.add(hkey, d.clock.Now().Add(dialHistoryExpiration)) d.dialing[node.ID()] = task @@ -498,7 +495,7 @@ func (t *dialTask) run(d *dialScheduler) { } func (t *dialTask) needResolve() bool { - return t.flags&staticDialedConn != 0 && t.dest().IP() == nil + return t.flags&staticDialedConn != 0 && !t.dest().IPAddr().IsValid() } // resolve attempts to find the current endpoint for the destination @@ -532,7 +529,8 @@ func (t *dialTask) resolve(d *dialScheduler) bool { // The node was found. t.resolveDelay = initialResolveDelay t.destPtr.Store(resolved) - d.log.Debug("Resolved node", "id", resolved.ID(), "addr", &net.TCPAddr{IP: resolved.IP(), Port: resolved.TCP()}) + resAddr, _ := resolved.TCPEndpoint() + d.log.Debug("Resolved node", "id", resolved.ID(), "addr", resAddr) return true } @@ -541,7 +539,8 @@ func (t *dialTask) dial(d *dialScheduler, dest *enode.Node) error { dialMeter.Mark(1) fd, err := d.dialer.Dial(d.ctx, dest) if err != nil { - d.log.Trace("Dial error", "id", dest.ID(), "addr", nodeAddr(dest), "conn", t.flags, "err", cleanupDialErr(err)) + addr, _ := dest.TCPEndpoint() + d.log.Trace("Dial error", "id", dest.ID(), "addr", addr, "conn", t.flags, "err", cleanupDialErr(err)) dialConnectionError.Mark(1) return &dialError{err} } @@ -551,7 +550,7 @@ func (t *dialTask) dial(d *dialScheduler, dest *enode.Node) error { func (t *dialTask) String() string { node := t.dest() id := node.ID() - return fmt.Sprintf("%v %x %v:%d", t.flags, id[:8], node.IP(), node.TCP()) + return fmt.Sprintf("%v %x %v:%d", t.flags, id[:8], node.IPAddr(), node.TCP()) } func cleanupDialErr(err error) error { diff --git a/p2p/discover/common.go b/p2p/discover/common.go index 891bce3b3b..29c2aa6ae5 100644 --- a/p2p/discover/common.go +++ b/p2p/discover/common.go @@ -18,8 +18,13 @@ package discover import ( "crypto/ecdsa" + crand "crypto/rand" + "encoding/binary" "fmt" + "math/rand" "net" + "net/netip" + "sync" "time" "github.com/ethereum/go-ethereum/common/mclock" @@ -35,8 +40,8 @@ import ( // UDPConn is a network connection on which discovery can operate. type UDPConn interface { - ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error) - WriteToUDP(b []byte, addr *net.UDPAddr) (n int, err error) + ReadFromUDPAddrPort(b []byte) (n int, addr netip.AddrPort, err error) + WriteToUDPAddrPort(b []byte, addr netip.AddrPort) (n int, err error) Close() error LocalAddr() net.Addr } @@ -96,7 +101,7 @@ type Config struct { func (cfg Config) withDefaults() Config { // Node table configuration: if cfg.PingInterval == 0 { - cfg.PingInterval = 10 * time.Second + cfg.PingInterval = 3 * time.Second } if cfg.RefreshInterval == 0 { cfg.RefreshInterval = 30 * time.Minute @@ -124,5 +129,46 @@ func ListenUDP(c UDPConn, ln *enode.LocalNode, cfg Config) (*UDPv4, error) { // channel if configured. type ReadPacket struct { Data []byte - Addr *net.UDPAddr + Addr netip.AddrPort +} + +type randomSource interface { + Intn(int) int + Int63n(int64) int64 + Shuffle(int, func(int, int)) +} + +// reseedingRandom is a random number generator that tracks when it was last re-seeded. +type reseedingRandom struct { + mu sync.Mutex + cur *rand.Rand +} + +func (r *reseedingRandom) seed() { + var b [8]byte + crand.Read(b[:]) + seed := binary.BigEndian.Uint64(b[:]) + new := rand.New(rand.NewSource(int64(seed))) + + r.mu.Lock() + r.cur = new + r.mu.Unlock() +} + +func (r *reseedingRandom) Intn(n int) int { + r.mu.Lock() + defer r.mu.Unlock() + return r.cur.Intn(n) +} + +func (r *reseedingRandom) Int63n(n int64) int64 { + r.mu.Lock() + defer r.mu.Unlock() + return r.cur.Int63n(n) +} + +func (r *reseedingRandom) Shuffle(n int, swap func(i, j int)) { + r.mu.Lock() + defer r.mu.Unlock() + r.cur.Shuffle(n, swap) } diff --git a/p2p/discover/lookup.go b/p2p/discover/lookup.go index 4aa3e8889c..9f024ed96f 100644 --- a/p2p/discover/lookup.go +++ b/p2p/discover/lookup.go @@ -30,16 +30,16 @@ import ( // not need to be an actual node identifier. type lookup struct { tab *Table - queryfunc func(*node) ([]*node, error) - replyCh chan []*node + queryfunc queryFunc + replyCh chan []*enode.Node cancelCh <-chan struct{} asked, seen map[enode.ID]bool result nodesByDistance - replyBuffer []*node + replyBuffer []*enode.Node queries int } -type queryFunc func(*node) ([]*node, error) +type queryFunc func(*enode.Node) ([]*enode.Node, error) func newLookup(ctx context.Context, tab *Table, target enode.ID, q queryFunc) *lookup { it := &lookup{ @@ -48,7 +48,7 @@ func newLookup(ctx context.Context, tab *Table, target enode.ID, q queryFunc) *l asked: make(map[enode.ID]bool), seen: make(map[enode.ID]bool), result: nodesByDistance{target: target}, - replyCh: make(chan []*node, alpha), + replyCh: make(chan []*enode.Node, alpha), cancelCh: ctx.Done(), queries: -1, } @@ -62,7 +62,7 @@ func newLookup(ctx context.Context, tab *Table, target enode.ID, q queryFunc) *l func (it *lookup) run() []*enode.Node { for it.advance() { } - return unwrapNodes(it.result.entries) + return it.result.entries } // advance advances the lookup until any new nodes have been found. @@ -142,33 +142,14 @@ func (it *lookup) slowdown() { } } -func (it *lookup) query(n *node, reply chan<- []*node) { - fails := it.tab.db.FindFails(n.ID(), n.IP()) +func (it *lookup) query(n *enode.Node, reply chan<- []*enode.Node) { r, err := it.queryfunc(n) - if errors.Is(err, errClosed) { - // Avoid recording failures on shutdown. - reply <- nil - return - } else if len(r) == 0 { - fails++ - it.tab.db.UpdateFindFails(n.ID(), n.IP(), fails) - // Remove the node from the local table if it fails to return anything useful too - // many times, but only if there are enough other nodes in the bucket. - dropped := false - if fails >= maxFindnodeFailures && it.tab.bucketLen(n.ID()) >= bucketSize/2 { - dropped = true - it.tab.delete(n) + if !errors.Is(err, errClosed) { // avoid recording failures on shutdown. + success := len(r) > 0 + it.tab.trackRequest(n, success, r) + if err != nil { + it.tab.log.Trace("FINDNODE failed", "id", n.ID(), "err", err) } - it.tab.log.Trace("FINDNODE failed", "id", n.ID(), "failcount", fails, "dropped", dropped, "err", err) - } else if fails > 0 { - // Reset failure counter because it counts _consecutive_ failures. - it.tab.db.UpdateFindFails(n.ID(), n.IP(), 0) - } - - // Grab as many nodes as possible. Some of them might not be alive anymore, but we'll - // just remove those again during revalidation. - for _, n := range r { - it.tab.addSeenNode(n) } reply <- r } @@ -176,7 +157,7 @@ func (it *lookup) query(n *node, reply chan<- []*node) { // lookupIterator performs lookup operations and iterates over all seen nodes. // When a lookup finishes, a new one is created through nextLookup. type lookupIterator struct { - buffer []*node + buffer []*enode.Node nextLookup lookupFunc ctx context.Context cancel func() @@ -195,7 +176,7 @@ func (it *lookupIterator) Node() *enode.Node { if len(it.buffer) == 0 { return nil } - return unwrapNode(it.buffer[0]) + return it.buffer[0] } // Next moves to the next node. diff --git a/p2p/discover/metrics.go b/p2p/discover/metrics.go index 3cd0ab0414..8deafbbce4 100644 --- a/p2p/discover/metrics.go +++ b/p2p/discover/metrics.go @@ -18,7 +18,7 @@ package discover import ( "fmt" - "net" + "net/netip" "github.com/ethereum/go-ethereum/metrics" ) @@ -58,16 +58,16 @@ func newMeteredConn(conn UDPConn) UDPConn { return &meteredUdpConn{UDPConn: conn} } -// ReadFromUDP delegates a network read to the underlying connection, bumping the udp ingress traffic meter along the way. -func (c *meteredUdpConn) ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error) { - n, addr, err = c.UDPConn.ReadFromUDP(b) +// ReadFromUDPAddrPort delegates a network read to the underlying connection, bumping the udp ingress traffic meter along the way. +func (c *meteredUdpConn) ReadFromUDPAddrPort(b []byte) (n int, addr netip.AddrPort, err error) { + n, addr, err = c.UDPConn.ReadFromUDPAddrPort(b) ingressTrafficMeter.Mark(int64(n)) return n, addr, err } -// Write delegates a network write to the underlying connection, bumping the udp egress traffic meter along the way. -func (c *meteredUdpConn) WriteToUDP(b []byte, addr *net.UDPAddr) (n int, err error) { - n, err = c.UDPConn.WriteToUDP(b, addr) +// WriteToUDP delegates a network write to the underlying connection, bumping the udp egress traffic meter along the way. +func (c *meteredUdpConn) WriteToUDP(b []byte, addr netip.AddrPort) (n int, err error) { + n, err = c.UDPConn.WriteToUDPAddrPort(b, addr) egressTrafficMeter.Mark(int64(n)) return n, err } diff --git a/p2p/discover/node.go b/p2p/discover/node.go index 9ffe101ccf..d3afb39b85 100644 --- a/p2p/discover/node.go +++ b/p2p/discover/node.go @@ -22,6 +22,8 @@ import ( "errors" "math/big" "net" + "slices" + "sort" "time" "github.com/ethereum/go-ethereum/common/math" @@ -29,12 +31,22 @@ import ( "github.com/ethereum/go-ethereum/p2p/enode" ) -// node represents a host on the network. -// The fields of Node may not be modified. -type node struct { - enode.Node - addedAt time.Time // time when the node was added to the table - livenessChecks uint // how often liveness was checked +type BucketNode struct { + Node *enode.Node `json:"node"` + AddedToTable time.Time `json:"addedToTable"` + AddedToBucket time.Time `json:"addedToBucket"` + Checks int `json:"checks"` + Live bool `json:"live"` +} + +// tableNode is an entry in Table. +type tableNode struct { + *enode.Node + revalList *revalidationList + addedToTable time.Time // first time node was added to bucket or replacement list + addedToBucket time.Time // time it was added in the actual bucket + livenessChecks uint // how often liveness was checked + isValidatedLive bool // true if existence of node is considered validated right now } type encPubkey [64]byte @@ -64,34 +76,63 @@ func (e encPubkey) id() enode.ID { return enode.ID(crypto.Keccak256Hash(e[:])) } -func wrapNode(n *enode.Node) *node { - return &node{Node: *n} -} - -func wrapNodes(ns []*enode.Node) []*node { - result := make([]*node, len(ns)) +func unwrapNodes(ns []*tableNode) []*enode.Node { + result := make([]*enode.Node, len(ns)) for i, n := range ns { - result[i] = wrapNode(n) + result[i] = n.Node } return result } -func unwrapNode(n *node) *enode.Node { - return &n.Node +func (n *tableNode) addr() *net.UDPAddr { + return &net.UDPAddr{IP: n.IP(), Port: n.UDP()} } -func unwrapNodes(ns []*node) []*enode.Node { - result := make([]*enode.Node, len(ns)) - for i, n := range ns { - result[i] = unwrapNode(n) +func (n *tableNode) String() string { + return n.Node.String() +} + +// nodesByDistance is a list of nodes, ordered by distance to target. +type nodesByDistance struct { + entries []*enode.Node + target enode.ID +} + +// push adds the given node to the list, keeping the total size below maxElems. +func (h *nodesByDistance) push(n *enode.Node, maxElems int) { + ix := sort.Search(len(h.entries), func(i int) bool { + return enode.DistCmp(h.target, h.entries[i].ID(), n.ID()) > 0 + }) + + end := len(h.entries) + if len(h.entries) < maxElems { + h.entries = append(h.entries, n) + } + if ix < end { + // Slide existing entries down to make room. + // This will overwrite the entry we just appended. + copy(h.entries[ix+1:], h.entries[ix:]) + h.entries[ix] = n } - return result } -func (n *node) addr() *net.UDPAddr { - return &net.UDPAddr{IP: n.IP(), Port: n.UDP()} +type nodeType interface { + ID() enode.ID } -func (n *node) String() string { - return n.Node.String() +// containsID reports whether ns contains a node with the given ID. +func containsID[N nodeType](ns []N, id enode.ID) bool { + for _, n := range ns { + if n.ID() == id { + return true + } + } + return false +} + +// deleteNode removes a node from the list. +func deleteNode[N nodeType](list []N, id enode.ID) []N { + return slices.DeleteFunc(list, func(n N) bool { + return n.ID() == id + }) } diff --git a/p2p/discover/table.go b/p2p/discover/table.go index dcd8d48ca8..3d4c3bb72a 100644 --- a/p2p/discover/table.go +++ b/p2p/discover/table.go @@ -24,18 +24,15 @@ package discover import ( "context" - crand "crypto/rand" - "encoding/binary" - "errors" "fmt" - mrand "math/rand" - "net" - "sort" + "net/netip" + "slices" "sync" "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/gopool" + "github.com/ethereum/go-ethereum/common/mclock" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/p2p/enode" @@ -58,22 +55,22 @@ const ( bucketIPLimit, bucketSubnet = 2, 24 // at most 2 addresses from the same /24 tableIPLimit, tableSubnet = 10, 24 - copyNodesInterval = 30 * time.Second - seedMinTableTime = 5 * time.Minute - seedCount = 30 - seedMaxAge = 5 * 24 * time.Hour + seedMinTableTime = 5 * time.Minute + seedCount = 30 + seedMaxAge = 5 * 24 * time.Hour ) // Table is the 'node table', a Kademlia-like index of neighbor nodes. The table keeps // itself up-to-date by verifying the liveness of neighbors and requesting their node // records when announcements of a new record version are received. type Table struct { - mutex sync.Mutex // protects buckets, bucket content, nursery, rand - buckets [nBuckets]*bucket // index of known nodes by distance - bucketSize int // size of bucket - nursery []*node // bootstrap nodes - rand *mrand.Rand // source of randomness, periodically reseeded - ips netutil.DistinctNetSet + mutex sync.Mutex // protects buckets, bucket content, nursery, rand + buckets [nBuckets]*bucket // index of known nodes by distance + bucketSize int // size of bucket + nursery []*enode.Node // bootstrap nodes + rand reseedingRandom // source of randomness, periodically reseeded + ips netutil.DistinctNetSet + revalidation tableRevalidation db *enode.DB // database of known nodes net transport @@ -81,15 +78,19 @@ type Table struct { log log.Logger // loop channels - refreshReq chan chan struct{} - initDone chan struct{} - closeReq chan struct{} - closed chan struct{} + refreshReq chan chan struct{} + revalResponseCh chan revalidationResponse + addNodeCh chan addNodeOp + addNodeHandled chan bool + trackRequestCh chan trackRequestOp + initDone chan struct{} + closeReq chan struct{} + closed chan struct{} enrFilter NodeFilterFunc - nodeAddedHook func(*bucket, *node) - nodeRemovedHook func(*bucket, *node) + nodeAddedHook func(*bucket, *tableNode) + nodeRemovedHook func(*bucket, *tableNode) } // transport is implemented by the UDP transports. @@ -104,75 +105,80 @@ type transport interface { // bucket contains nodes, ordered by their last activity. the entry // that was most recently active is the first element in entries. type bucket struct { - entries []*node // live entries, sorted by time of last contact - replacements []*node // recently seen nodes to be used if revalidation fails + entries []*tableNode // live entries, sorted by time of last contact + replacements []*tableNode // recently seen nodes to be used if revalidation fails ips netutil.DistinctNetSet index int } +type addNodeOp struct { + node *enode.Node + isInbound bool + forceSetLive bool // for tests +} + +type trackRequestOp struct { + node *enode.Node + foundNodes []*enode.Node + success bool +} + func newTable(t transport, db *enode.DB, cfg Config) (*Table, error) { cfg = cfg.withDefaults() tab := &Table{ - net: t, - db: db, - cfg: cfg, - log: cfg.Log, - refreshReq: make(chan chan struct{}), - initDone: make(chan struct{}), - closeReq: make(chan struct{}), - closed: make(chan struct{}), - rand: mrand.New(mrand.NewSource(0)), - ips: netutil.DistinctNetSet{Subnet: tableSubnet, Limit: tableIPLimit}, - enrFilter: cfg.FilterFunction, - bucketSize: bucketSize, + net: t, + db: db, + cfg: cfg, + log: cfg.Log, + refreshReq: make(chan chan struct{}), + revalResponseCh: make(chan revalidationResponse), + addNodeCh: make(chan addNodeOp), + addNodeHandled: make(chan bool), + trackRequestCh: make(chan trackRequestOp), + initDone: make(chan struct{}), + closeReq: make(chan struct{}), + closed: make(chan struct{}), + ips: netutil.DistinctNetSet{Subnet: tableSubnet, Limit: tableIPLimit}, + enrFilter: cfg.FilterFunction, + bucketSize: bucketSize, } if cfg.IsBootnode { tab.bucketSize = bootNodeBucketSize } - if err := tab.setFallbackNodes(cfg.Bootnodes); err != nil { - return nil, err - } for i := range tab.buckets { tab.buckets[i] = &bucket{ index: i, ips: netutil.DistinctNetSet{Subnet: bucketSubnet, Limit: bucketIPLimit}, } } - tab.seedRand() - tab.loadSeedNodes() - - return tab, nil -} + tab.rand.seed() + tab.revalidation.init(&cfg) -func newMeteredTable(t transport, db *enode.DB, cfg Config) (*Table, error) { - tab, err := newTable(t, db, cfg) - if err != nil { + // initial table content + if err := tab.setFallbackNodes(cfg.Bootnodes); err != nil { return nil, err } - if metrics.Enabled { - tab.nodeAddedHook = func(b *bucket, n *node) { - bucketsCounter[b.index].Inc(1) - } - tab.nodeRemovedHook = func(b *bucket, n *node) { - bucketsCounter[b.index].Dec(1) - } - } + tab.loadSeedNodes() + return tab, nil } // Nodes returns all nodes contained in the table. -func (tab *Table) Nodes() []*enode.Node { - if !tab.isInitDone() { - return nil - } - +func (tab *Table) Nodes() [][]BucketNode { tab.mutex.Lock() defer tab.mutex.Unlock() - var nodes []*enode.Node - for _, b := range &tab.buckets { - for _, n := range b.entries { - nodes = append(nodes, unwrapNode(n)) + nodes := make([][]BucketNode, len(tab.buckets)) + for i, b := range &tab.buckets { + nodes[i] = make([]BucketNode, len(b.entries)) + for j, n := range b.entries { + nodes[i][j] = BucketNode{ + Node: n.Node, + Checks: int(n.livenessChecks), + Live: n.isValidatedLive, + AddedToTable: n.addedToTable, + AddedToBucket: n.addedToBucket, + } } } return nodes @@ -182,15 +188,6 @@ func (tab *Table) self() *enode.Node { return tab.net.Self() } -func (tab *Table) seedRand() { - var b [8]byte - crand.Read(b[:]) - - tab.mutex.Lock() - tab.rand.Seed(int64(binary.BigEndian.Uint64(b[:]))) - tab.mutex.Unlock() -} - // getNode returns the node with the given ID or nil if it isn't in the table. func (tab *Table) getNode(id enode.ID) *enode.Node { tab.mutex.Lock() @@ -199,7 +196,7 @@ func (tab *Table) getNode(id enode.ID) *enode.Node { b := tab.bucket(id) for _, e := range b.entries { if e.ID() == id { - return unwrapNode(e) + return e.Node } } return nil @@ -215,16 +212,16 @@ func (tab *Table) close() { // are used to connect to the network if the table is empty and there // are no known nodes in the database. func (tab *Table) setFallbackNodes(nodes []*enode.Node) error { - nursery := make([]*node, 0, len(nodes)) + nursery := make([]*enode.Node, 0, len(nodes)) for _, n := range nodes { if err := n.ValidateComplete(); err != nil { return fmt.Errorf("bad bootstrap node %q: %v", n, err) } - if tab.cfg.NetRestrict != nil && !tab.cfg.NetRestrict.Contains(n.IP()) { - tab.log.Error("Bootstrap node filtered by netrestrict", "id", n.ID(), "ip", n.IP()) + if tab.cfg.NetRestrict != nil && !tab.cfg.NetRestrict.ContainsAddr(n.IPAddr()) { + tab.log.Error("Bootstrap node filtered by netrestrict", "id", n.ID(), "ip", n.IPAddr()) continue } - nursery = append(nursery, wrapNode(n)) + nursery = append(nursery, n) } tab.nursery = nursery return nil @@ -250,19 +247,126 @@ func (tab *Table) refresh() <-chan struct{} { return done } -// loop schedules runs of doRefresh, doRevalidate and copyLiveNodes. +// findnodeByID returns the n nodes in the table that are closest to the given id. +// This is used by the FINDNODE/v4 handler. +// +// The preferLive parameter says whether the caller wants liveness-checked results. If +// preferLive is true and the table contains any verified nodes, the result will not +// contain unverified nodes. However, if there are no verified nodes at all, the result +// will contain unverified nodes. +func (tab *Table) findnodeByID(target enode.ID, nresults int, preferLive bool) *nodesByDistance { + tab.mutex.Lock() + defer tab.mutex.Unlock() + + // Scan all buckets. There might be a better way to do this, but there aren't that many + // buckets, so this solution should be fine. The worst-case complexity of this loop + // is O(tab.len() * nresults). + nodes := &nodesByDistance{target: target} + liveNodes := &nodesByDistance{target: target} + for _, b := range &tab.buckets { + for _, n := range b.entries { + nodes.push(n.Node, nresults) + if preferLive && n.isValidatedLive { + liveNodes.push(n.Node, nresults) + } + } + } + + if preferLive && len(liveNodes.entries) > 0 { + return liveNodes + } + return nodes +} + +// appendLiveNodes adds nodes at the given distance to the result slice. +// This is used by the FINDNODE/v5 handler. +func (tab *Table) appendLiveNodes(dist uint, result []*enode.Node) []*enode.Node { + if dist > 256 { + return result + } + if dist == 0 { + return append(result, tab.self()) + } + + tab.mutex.Lock() + for _, n := range tab.bucketAtDistance(int(dist)).entries { + if n.isValidatedLive { + result = append(result, n.Node) + } + } + tab.mutex.Unlock() + + // Shuffle result to avoid always returning same nodes in FINDNODE/v5. + tab.rand.Shuffle(len(result), func(i, j int) { + result[i], result[j] = result[j], result[i] + }) + return result +} + +// len returns the number of nodes in the table. +func (tab *Table) len() (n int) { + tab.mutex.Lock() + defer tab.mutex.Unlock() + + for _, b := range &tab.buckets { + n += len(b.entries) + } + return n +} + +// addFoundNode adds a node which may not be live. If the bucket has space available, +// adding the node succeeds immediately. Otherwise, the node is added to the replacements +// list. +// +// The caller must not hold tab.mutex. +func (tab *Table) addFoundNode(n *enode.Node, forceSetLive bool) bool { + op := addNodeOp{node: n, isInbound: false, forceSetLive: forceSetLive} + select { + case tab.addNodeCh <- op: + return <-tab.addNodeHandled + case <-tab.closeReq: + return false + } +} + +// addInboundNode adds a node from an inbound contact. If the bucket has no space, the +// node is added to the replacements list. +// +// There is an additional safety measure: if the table is still initializing the node is +// not added. This prevents an attack where the table could be filled by just sending ping +// repeatedly. +// +// The caller must not hold tab.mutex. +func (tab *Table) addInboundNode(n *enode.Node) bool { + op := addNodeOp{node: n, isInbound: true} + select { + case tab.addNodeCh <- op: + return <-tab.addNodeHandled + case <-tab.closeReq: + return false + } +} + +func (tab *Table) trackRequest(n *enode.Node, success bool, foundNodes []*enode.Node) { + op := trackRequestOp{n, foundNodes, success} + select { + case tab.trackRequestCh <- op: + case <-tab.closeReq: + } +} + +// loop is the main loop of Table. func (tab *Table) loop() { var ( - revalidate = time.NewTimer(tab.nextRevalidateTime()) - refresh = time.NewTimer(tab.nextRefreshTime()) - copyNodes = time.NewTicker(copyNodesInterval) - refreshDone = make(chan struct{}) // where doRefresh reports completion - revalidateDone chan struct{} // where doRevalidate reports completion - waiting = []chan struct{}{tab.initDone} // holds waiting callers while doRefresh runs + refresh = time.NewTimer(tab.nextRefreshTime()) + refreshDone = make(chan struct{}) // where doRefresh reports completion + waiting = []chan struct{}{tab.initDone} // holds waiting callers while doRefresh runs + revalTimer = mclock.NewAlarm(tab.cfg.Clock) + reseedRandTimer = time.NewTicker(10 * time.Minute) ) defer refresh.Stop() - defer revalidate.Stop() - defer copyNodes.Stop() + defer revalTimer.Stop() + defer reseedRandTimer.Stop() // Start initial refresh. gopool.Submit(func() { @@ -270,15 +374,35 @@ func (tab *Table) loop() { }) loop: for { + nextTime := tab.revalidation.run(tab, tab.cfg.Clock.Now()) + revalTimer.Schedule(nextTime) + select { + case <-reseedRandTimer.C: + tab.rand.seed() + + case <-revalTimer.C(): + + case r := <-tab.revalResponseCh: + tab.revalidation.handleResponse(tab, r) + + case op := <-tab.addNodeCh: + tab.mutex.Lock() + ok := tab.handleAddNode(op) + tab.mutex.Unlock() + tab.addNodeHandled <- ok + + case op := <-tab.trackRequestCh: + tab.handleTrackRequest(op) + case <-refresh.C: - tab.seedRand() if refreshDone == nil { refreshDone = make(chan struct{}) gopool.Submit(func() { tab.doRefresh(refreshDone) }) } + case req := <-tab.refreshReq: waiting = append(waiting, req) if refreshDone == nil { @@ -288,24 +412,13 @@ loop: tab.doRefresh(refreshDone) }) } + case <-refreshDone: for _, ch := range waiting { close(ch) } waiting, refreshDone = nil, nil refresh.Reset(tab.nextRefreshTime()) - case <-revalidate.C: - revalidateDone = make(chan struct{}) - gopool.Submit(func() { - tab.doRevalidate(revalidateDone) - }) - case <-revalidateDone: - revalidate.Reset(tab.nextRevalidateTime()) - revalidateDone = nil - case <-copyNodes.C: - gopool.Submit(func() { - tab.copyLiveNodes() - }) case <-tab.closeReq: break loop @@ -318,9 +431,6 @@ loop: for _, ch := range waiting { close(ch) } - if revalidateDone != nil { - <-revalidateDone - } close(tab.closed) } @@ -349,183 +459,26 @@ func (tab *Table) doRefresh(done chan struct{}) { } func (tab *Table) loadSeedNodes() { - seeds := wrapNodes(tab.db.QuerySeeds(seedCount, seedMaxAge)) + seeds := tab.db.QuerySeeds(seedCount, seedMaxAge) seeds = append(seeds, tab.nursery...) for i := range seeds { seed := seeds[i] if tab.log.Enabled(context.Background(), log.LevelTrace) { - age := time.Since(tab.db.LastPongReceived(seed.ID(), seed.IP())) - tab.log.Trace("Found seed node in database", "id", seed.ID(), "addr", seed.addr(), "age", age) + age := time.Since(tab.db.LastPongReceived(seed.ID(), seed.IPAddr())) + addr, _ := seed.UDPEndpoint() + tab.log.Trace("Found seed node in database", "id", seed.ID(), "addr", addr, "age", age) } - tab.addSeenNode(seed) + tab.mutex.Lock() + tab.handleAddNode(addNodeOp{node: seed, isInbound: false}) + tab.mutex.Unlock() } } -// doRevalidate checks that the last node in a random bucket is still live and replaces or -// deletes the node if it isn't. -func (tab *Table) doRevalidate(done chan<- struct{}) { - defer func() { done <- struct{}{} }() - - last, bi := tab.nodeToRevalidate() - if last == nil { - // No non-empty bucket found. - return - } - - // Ping the selected node and wait for a pong. - remoteSeq, err := tab.net.ping(unwrapNode(last)) - - // Also fetch record if the node replied and returned a higher sequence number. - if last.Seq() < remoteSeq { - n, enrErr := tab.net.RequestENR(unwrapNode(last)) - if enrErr != nil { - tab.log.Debug("ENR request failed", "id", last.ID(), "addr", last.addr(), "err", enrErr) - } else { - if tab.enrFilter != nil { - if !tab.enrFilter(n.Record()) { - tab.log.Trace("ENR record filter out", "id", last.ID(), "addr", last.addr()) - err = errors.New("filtered node") - } - } - last = &node{Node: *n, addedAt: last.addedAt, livenessChecks: last.livenessChecks} - } - } - - tab.mutex.Lock() - defer tab.mutex.Unlock() - b := tab.buckets[bi] - if err == nil { - // The node responded, move it to the front. - last.livenessChecks++ - tab.log.Debug("Revalidated node", "b", bi, "id", last.ID(), "checks", last.livenessChecks) - tab.bumpInBucket(b, last) - return - } - // No reply received, pick a replacement or delete the node if there aren't - // any replacements. - if r := tab.replace(b, last); r != nil { - tab.log.Debug("Replaced dead node", "b", bi, "id", last.ID(), "ip", last.IP(), "checks", last.livenessChecks, "r", r.ID(), "rip", r.IP()) - } else { - tab.log.Debug("Removed dead node", "b", bi, "id", last.ID(), "ip", last.IP(), "checks", last.livenessChecks) - } -} - -// nodeToRevalidate returns the last node in a random, non-empty bucket. -func (tab *Table) nodeToRevalidate() (n *node, bi int) { - tab.mutex.Lock() - defer tab.mutex.Unlock() - - for _, bi = range tab.rand.Perm(len(tab.buckets)) { - b := tab.buckets[bi] - if len(b.entries) > 0 { - last := b.entries[len(b.entries)-1] - return last, bi - } - } - return nil, 0 -} - -func (tab *Table) nextRevalidateTime() time.Duration { - tab.mutex.Lock() - defer tab.mutex.Unlock() - - return time.Duration(tab.rand.Int63n(int64(tab.cfg.PingInterval))) -} - func (tab *Table) nextRefreshTime() time.Duration { - tab.mutex.Lock() - defer tab.mutex.Unlock() - half := tab.cfg.RefreshInterval / 2 return half + time.Duration(tab.rand.Int63n(int64(half))) } -// copyLiveNodes adds nodes from the table to the database if they have been in the table -// longer than seedMinTableTime. -func (tab *Table) copyLiveNodes() { - tab.mutex.Lock() - defer tab.mutex.Unlock() - - now := time.Now() - for _, b := range &tab.buckets { - for _, n := range b.entries { - if n.livenessChecks > 0 && now.Sub(n.addedAt) >= seedMinTableTime { - tab.db.UpdateNode(unwrapNode(n)) - } - } - } -} - -// findnodeByID returns the n nodes in the table that are closest to the given id. -// This is used by the FINDNODE/v4 handler. -// -// The preferLive parameter says whether the caller wants liveness-checked results. If -// preferLive is true and the table contains any verified nodes, the result will not -// contain unverified nodes. However, if there are no verified nodes at all, the result -// will contain unverified nodes. -func (tab *Table) findnodeByID(target enode.ID, nresults int, preferLive bool) *nodesByDistance { - tab.mutex.Lock() - defer tab.mutex.Unlock() - - // Scan all buckets. There might be a better way to do this, but there aren't that many - // buckets, so this solution should be fine. The worst-case complexity of this loop - // is O(tab.len() * nresults). - nodes := &nodesByDistance{target: target} - liveNodes := &nodesByDistance{target: target} - for _, b := range &tab.buckets { - for _, n := range b.entries { - nodes.push(n, nresults) - if preferLive && n.livenessChecks > 0 { - liveNodes.push(n, nresults) - } - } - } - - if preferLive && len(liveNodes.entries) > 0 { - return liveNodes - } - return nodes -} - -// appendLiveNodes adds nodes at the given distance to the result slice. -func (tab *Table) appendLiveNodes(dist uint, result []*enode.Node) []*enode.Node { - if dist > 256 { - return result - } - if dist == 0 { - return append(result, tab.self()) - } - - tab.mutex.Lock() - defer tab.mutex.Unlock() - for _, n := range tab.bucketAtDistance(int(dist)).entries { - if n.livenessChecks >= 1 { - node := n.Node // avoid handing out pointer to struct field - result = append(result, &node) - } - } - return result -} - -// len returns the number of nodes in the table. -func (tab *Table) len() (n int) { - tab.mutex.Lock() - defer tab.mutex.Unlock() - - for _, b := range &tab.buckets { - n += len(b.entries) - } - return n -} - -// bucketLen returns the number of nodes in the bucket for the given ID. -func (tab *Table) bucketLen(id enode.ID) int { - tab.mutex.Lock() - defer tab.mutex.Unlock() - - return len(tab.bucket(id).entries) -} - // bucket returns the bucket for the given node ID hash. func (tab *Table) bucket(id enode.ID) *bucket { d := enode.LogDist(tab.self().ID(), id) @@ -539,58 +492,11 @@ func (tab *Table) bucketAtDistance(d int) *bucket { return tab.buckets[d-bucketMinDistance-1] } -// addSeenNode adds a node which may or may not be live to the end of a bucket. If the -// bucket has space available, adding the node succeeds immediately. Otherwise, the node is -// added to the replacements list. -// -// The caller must not hold tab.mutex. -func (tab *Table) addSeenNode(n *node) { - gopool.Submit(func() { - tab.addSeenNodeSync(n) - }) -} - -func (tab *Table) addSeenNodeSync(n *node) { - if n.ID() == tab.self().ID() { - return - } - - if tab.filterNode(n) { - return - } - - tab.mutex.Lock() - defer tab.mutex.Unlock() - b := tab.bucket(n.ID()) - if contains(b.entries, n.ID()) { - // Already in bucket, don't add. - return - } - if len(b.entries) >= tab.bucketSize { - // Bucket full, maybe add as replacement. - tab.addReplacement(b, n) - return - } - if !tab.addIP(b, n.IP()) { - // Can't add: IP limit reached. - return - } - - // Add to end of bucket: - b.entries = append(b.entries, n) - b.replacements = deleteNode(b.replacements, n) - n.addedAt = time.Now() - - if tab.nodeAddedHook != nil { - tab.nodeAddedHook(b, n) - } -} - -func (tab *Table) filterNode(n *node) bool { +func (tab *Table) filterNode(n *tableNode) bool { if tab.enrFilter == nil { return false } - if node, err := tab.net.RequestENR(unwrapNode(n)); err != nil { + if node, err := tab.net.RequestENR(n.Node); err != nil { tab.log.Debug("ENR request failed", "id", n.ID(), "addr", n.addr(), "err", err) return false } else if !tab.enrFilter(node.Record()) { @@ -600,177 +506,222 @@ func (tab *Table) filterNode(n *node) bool { return false } -// addVerifiedNode adds a node whose existence has been verified recently to the front of a -// bucket. If the node is already in the bucket, it is moved to the front. If the bucket -// has no space, the node is added to the replacements list. -// -// There is an additional safety measure: if the table is still initializing the node -// is not added. This prevents an attack where the table could be filled by just sending -// ping repeatedly. -// -// The caller must not hold tab.mutex. - -func (tab *Table) addVerifiedNode(n *node) { - gopool.Submit(func() { - tab.addVerifiedNodeSync(n) - }) +func (tab *Table) addIP(b *bucket, ip netip.Addr) bool { + if !ip.IsValid() || ip.IsUnspecified() { + return false // Nodes without IP cannot be added. + } + if netutil.AddrIsLAN(ip) { + return true + } + if !tab.ips.AddAddr(ip) { + tab.log.Debug("IP exceeds table limit", "ip", ip) + return false + } + if !b.ips.AddAddr(ip) { + tab.log.Debug("IP exceeds bucket limit", "ip", ip) + tab.ips.RemoveAddr(ip) + return false + } + return true } -func (tab *Table) addVerifiedNodeSync(n *node) { - if !tab.isInitDone() { +func (tab *Table) removeIP(b *bucket, ip netip.Addr) { + if netutil.AddrIsLAN(ip) { return } - if n.ID() == tab.self().ID() { - return + tab.ips.RemoveAddr(ip) + b.ips.RemoveAddr(ip) +} + +// handleAddNode adds the node in the request to the table, if there is space. +// The caller must hold tab.mutex. +func (tab *Table) handleAddNode(req addNodeOp) bool { + if req.node.ID() == tab.self().ID() { + return false } - if tab.filterNode(n) { - return + + // For nodes from inbound contact, there is an additional safety measure: if the table + // is still initializing the node is not added. + if req.isInbound && !tab.isInitDone() { + return false } - tab.mutex.Lock() - defer tab.mutex.Unlock() - b := tab.bucket(n.ID()) - if tab.bumpInBucket(b, n) { - // Already in bucket, moved to front. - return + + b := tab.bucket(req.node.ID()) + n, _ := tab.bumpInBucket(b, req.node, req.isInbound) + if n != nil { + // Already in bucket. + return false } - if len(b.entries) >= tab.bucketSize { + if len(b.entries) >= bucketSize { // Bucket full, maybe add as replacement. - tab.addReplacement(b, n) - return + tab.addReplacement(b, req.node) + return false } - if !tab.addIP(b, n.IP()) { + if !tab.addIP(b, req.node.IPAddr()) { // Can't add: IP limit reached. - return + return false } - // Add to front of bucket. - b.entries, _ = pushNode(b.entries, n, tab.bucketSize) - b.replacements = deleteNode(b.replacements, n) - n.addedAt = time.Now() - - if tab.nodeAddedHook != nil { - tab.nodeAddedHook(b, n) + // Add to bucket. + wn := &tableNode{Node: req.node} + if req.forceSetLive { + wn.livenessChecks = 1 + wn.isValidatedLive = true } -} -// delete removes an entry from the node table. It is used to evacuate dead nodes. -func (tab *Table) delete(node *node) { - tab.mutex.Lock() - defer tab.mutex.Unlock() + if tab.filterNode(wn) { + return false + } - tab.deleteInBucket(tab.bucket(node.ID()), node) + b.entries = append(b.entries, wn) + b.replacements = deleteNode(b.replacements, wn.ID()) + tab.nodeAdded(b, wn) + return true } -func (tab *Table) addIP(b *bucket, ip net.IP) bool { - if len(ip) == 0 { - return false // Nodes without IP cannot be added. - } - if netutil.IsLAN(ip) { - return true +// addReplacement adds n to the replacement cache of bucket b. +func (tab *Table) addReplacement(b *bucket, n *enode.Node) { + if containsID(b.replacements, n.ID()) { + // TODO: update ENR + return } - if !tab.ips.Add(ip) { - tab.log.Debug("IP exceeds table limit", "ip", ip) - return false + if !tab.addIP(b, n.IPAddr()) { + return } - if !b.ips.Add(ip) { - tab.log.Debug("IP exceeds bucket limit", "ip", ip) - tab.ips.Remove(ip) - return false + + wn := &tableNode{Node: n, addedToTable: time.Now()} + var removed *tableNode + b.replacements, removed = pushNode(b.replacements, wn, maxReplacements) + if removed != nil { + tab.removeIP(b, removed.IPAddr()) } - return true } -func (tab *Table) removeIP(b *bucket, ip net.IP) { - if netutil.IsLAN(ip) { - return +func (tab *Table) nodeAdded(b *bucket, n *tableNode) { + if n.addedToTable == (time.Time{}) { + n.addedToTable = time.Now() + } + n.addedToBucket = time.Now() + tab.revalidation.nodeAdded(tab, n) + if tab.nodeAddedHook != nil { + tab.nodeAddedHook(b, n) + } + if metrics.Enabled { + bucketsCounter[b.index].Inc(1) } - tab.ips.Remove(ip) - b.ips.Remove(ip) } -func (tab *Table) addReplacement(b *bucket, n *node) { - for _, e := range b.replacements { - if e.ID() == n.ID() { - return // already in list - } - } - if !tab.addIP(b, n.IP()) { - return +func (tab *Table) nodeRemoved(b *bucket, n *tableNode) { + tab.revalidation.nodeRemoved(n) + if tab.nodeRemovedHook != nil { + tab.nodeRemovedHook(b, n) } - var removed *node - b.replacements, removed = pushNode(b.replacements, n, maxReplacements) - if removed != nil { - tab.removeIP(b, removed.IP()) + if metrics.Enabled { + bucketsCounter[b.index].Dec(1) } } -// replace removes n from the replacement list and replaces 'last' with it if it is the -// last entry in the bucket. If 'last' isn't the last entry, it has either been replaced -// with someone else or became active. -func (tab *Table) replace(b *bucket, last *node) *node { - if len(b.entries) == 0 || b.entries[len(b.entries)-1].ID() != last.ID() { - // Entry has moved, don't replace it. +// deleteInBucket removes node n from the table. +// If there are replacement nodes in the bucket, the node is replaced. +func (tab *Table) deleteInBucket(b *bucket, id enode.ID) *tableNode { + index := slices.IndexFunc(b.entries, func(e *tableNode) bool { return e.ID() == id }) + if index == -1 { + // Entry has been removed already. return nil } - // Still the last entry. + + // Remove the node. + n := b.entries[index] + b.entries = slices.Delete(b.entries, index, index+1) + tab.removeIP(b, n.IPAddr()) + tab.nodeRemoved(b, n) + + // Add replacement. if len(b.replacements) == 0 { - tab.deleteInBucket(b, last) + tab.log.Debug("Removed dead node", "b", b.index, "id", n.ID(), "ip", n.IPAddr()) return nil } - r := b.replacements[tab.rand.Intn(len(b.replacements))] - b.replacements = deleteNode(b.replacements, r) - b.entries[len(b.entries)-1] = r - tab.removeIP(b, last.IP()) - return r -} - -// bumpInBucket moves the given node to the front of the bucket entry list -// if it is contained in that list. -func (tab *Table) bumpInBucket(b *bucket, n *node) bool { - for i := range b.entries { - if b.entries[i].ID() == n.ID() { - if !n.IP().Equal(b.entries[i].IP()) { - // Endpoint has changed, ensure that the new IP fits into table limits. - tab.removeIP(b, b.entries[i].IP()) - if !tab.addIP(b, n.IP()) { - // It doesn't, put the previous one back. - tab.addIP(b, b.entries[i].IP()) - return false - } - } - // Move it to the front. - copy(b.entries[1:], b.entries[:i]) - b.entries[0] = n - return true + rindex := tab.rand.Intn(len(b.replacements)) + rep := b.replacements[rindex] + b.replacements = slices.Delete(b.replacements, rindex, rindex+1) + b.entries = append(b.entries, rep) + tab.nodeAdded(b, rep) + tab.log.Debug("Replaced dead node", "b", b.index, "id", n.ID(), "ip", n.IPAddr(), "r", rep.ID(), "rip", rep.IPAddr()) + return rep +} + +// bumpInBucket updates a node record if it exists in the bucket. +// The second return value reports whether the node's endpoint (IP/port) was updated. +func (tab *Table) bumpInBucket(b *bucket, newRecord *enode.Node, isInbound bool) (n *tableNode, endpointChanged bool) { + i := slices.IndexFunc(b.entries, func(elem *tableNode) bool { + return elem.ID() == newRecord.ID() + }) + if i == -1 { + return nil, false // not in bucket + } + n = b.entries[i] + + // For inbound updates (from the node itself) we accept any change, even if it sets + // back the sequence number. For found nodes (!isInbound), seq has to advance. Note + // this check also ensures found discv4 nodes (which always have seq=0) can't be + // updated. + if newRecord.Seq() <= n.Seq() && !isInbound { + return n, false + } + + // Check endpoint update against IP limits. + ipchanged := newRecord.IPAddr() != n.IPAddr() + portchanged := newRecord.UDP() != n.UDP() + if ipchanged { + tab.removeIP(b, n.IPAddr()) + if !tab.addIP(b, newRecord.IPAddr()) { + // It doesn't fit with the limit, put the previous record back. + tab.addIP(b, n.IPAddr()) + return n, false } } - return false + + // Apply update. + n.Node = newRecord + if ipchanged || portchanged { + // Ensure node is revalidated quickly for endpoint changes. + tab.revalidation.nodeEndpointChanged(tab, n) + return n, true + } + return n, false } -func (tab *Table) deleteInBucket(b *bucket, n *node) { - // Check if the node is actually in the bucket so the removed hook - // isn't called multiple times for the same node. - if !contains(b.entries, n.ID()) { - return +func (tab *Table) handleTrackRequest(op trackRequestOp) { + var fails int + if op.success { + // Reset failure counter because it counts _consecutive_ failures. + tab.db.UpdateFindFails(op.node.ID(), op.node.IPAddr(), 0) + } else { + fails = tab.db.FindFails(op.node.ID(), op.node.IPAddr()) + fails++ + tab.db.UpdateFindFails(op.node.ID(), op.node.IPAddr(), fails) } - b.entries = deleteNode(b.entries, n) - tab.removeIP(b, n.IP()) - if tab.nodeRemovedHook != nil { - tab.nodeRemovedHook(b, n) + + tab.mutex.Lock() + defer tab.mutex.Unlock() + + b := tab.bucket(op.node.ID()) + // Remove the node from the local table if it fails to return anything useful too + // many times, but only if there are enough other nodes in the bucket. This latter + // condition specifically exists to make bootstrapping in smaller test networks more + // reliable. + if fails >= maxFindnodeFailures && len(b.entries) >= bucketSize/4 { + tab.deleteInBucket(b, op.node.ID()) } -} -func contains(ns []*node, id enode.ID) bool { - for _, n := range ns { - if n.ID() == id { - return true - } + // Add found nodes. + for _, n := range op.foundNodes { + tab.handleAddNode(addNodeOp{n, false, false}) } - return false } // pushNode adds n to the front of list, keeping at most max items. -func pushNode(list []*node, n *node, max int) ([]*node, *node) { +func pushNode(list []*tableNode, n *tableNode, max int) ([]*tableNode, *tableNode) { if len(list) < max { list = append(list, nil) } @@ -779,37 +730,3 @@ func pushNode(list []*node, n *node, max int) ([]*node, *node) { list[0] = n return list, removed } - -// deleteNode removes n from list. -func deleteNode(list []*node, n *node) []*node { - for i := range list { - if list[i].ID() == n.ID() { - return append(list[:i], list[i+1:]...) - } - } - return list -} - -// nodesByDistance is a list of nodes, ordered by distance to target. -type nodesByDistance struct { - entries []*node - target enode.ID -} - -// push adds the given node to the list, keeping the total size below maxElems. -func (h *nodesByDistance) push(n *node, maxElems int) { - ix := sort.Search(len(h.entries), func(i int) bool { - return enode.DistCmp(h.target, h.entries[i].ID(), n.ID()) > 0 - }) - - end := len(h.entries) - if len(h.entries) < maxElems { - h.entries = append(h.entries, n) - } - if ix < end { - // Slide existing entries down to make room. - // This will overwrite the entry we just appended. - copy(h.entries[ix+1:], h.entries[ix:]) - h.entries[ix] = n - } -} diff --git a/p2p/discover/table_reval.go b/p2p/discover/table_reval.go new file mode 100644 index 0000000000..a875f342b8 --- /dev/null +++ b/p2p/discover/table_reval.go @@ -0,0 +1,251 @@ +// Copyright 2024 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package discover + +import ( + "errors" + "fmt" + "math" + "slices" + "time" + + "github.com/ethereum/go-ethereum/common/mclock" + "github.com/ethereum/go-ethereum/p2p/enode" +) + +const never = mclock.AbsTime(math.MaxInt64) + +const slowRevalidationFactor = 3 + +// tableRevalidation implements the node revalidation process. +// It tracks all nodes contained in Table, and schedules sending PING to them. +type tableRevalidation struct { + fast revalidationList + slow revalidationList + activeReq map[enode.ID]struct{} +} + +type revalidationResponse struct { + n *tableNode + newRecord *enode.Node + didRespond bool +} + +func (tr *tableRevalidation) init(cfg *Config) { + tr.activeReq = make(map[enode.ID]struct{}) + tr.fast.nextTime = never + tr.fast.interval = cfg.PingInterval + tr.fast.name = "fast" + tr.slow.nextTime = never + tr.slow.interval = cfg.PingInterval * slowRevalidationFactor + tr.slow.name = "slow" +} + +// nodeAdded is called when the table receives a new node. +func (tr *tableRevalidation) nodeAdded(tab *Table, n *tableNode) { + tr.fast.push(n, tab.cfg.Clock.Now(), &tab.rand) +} + +// nodeRemoved is called when a node was removed from the table. +func (tr *tableRevalidation) nodeRemoved(n *tableNode) { + if n.revalList == nil { + panic(fmt.Errorf("removed node %v has nil revalList", n.ID())) + } + n.revalList.remove(n) +} + +// nodeEndpointChanged is called when a change in IP or port is detected. +func (tr *tableRevalidation) nodeEndpointChanged(tab *Table, n *tableNode) { + n.isValidatedLive = false + tr.moveToList(&tr.fast, n, tab.cfg.Clock.Now(), &tab.rand) +} + +// run performs node revalidation. +// It returns the next time it should be invoked, which is used in the Table main loop +// to schedule a timer. However, run can be called at any time. +func (tr *tableRevalidation) run(tab *Table, now mclock.AbsTime) (nextTime mclock.AbsTime) { + if n := tr.fast.get(now, &tab.rand, tr.activeReq); n != nil { + tr.startRequest(tab, n) + tr.fast.schedule(now, &tab.rand) + } + if n := tr.slow.get(now, &tab.rand, tr.activeReq); n != nil { + tr.startRequest(tab, n) + tr.slow.schedule(now, &tab.rand) + } + + return min(tr.fast.nextTime, tr.slow.nextTime) +} + +// startRequest spawns a revalidation request for node n. +func (tr *tableRevalidation) startRequest(tab *Table, n *tableNode) { + if _, ok := tr.activeReq[n.ID()]; ok { + panic(fmt.Errorf("duplicate startRequest (node %v)", n.ID())) + } + tr.activeReq[n.ID()] = struct{}{} + resp := revalidationResponse{n: n} + + // Fetch the node while holding lock. + tab.mutex.Lock() + node := n.Node + tab.mutex.Unlock() + + go tab.doRevalidate(resp, node) +} + +func (tab *Table) doRevalidate(resp revalidationResponse, node *enode.Node) { + // Ping the selected node and wait for a pong response. + remoteSeq, err := tab.net.ping(node) + resp.didRespond = err == nil + + // Also fetch record if the node replied and returned a higher sequence number. + if remoteSeq > node.Seq() { + newrec, err := tab.net.RequestENR(node) + if err != nil { + tab.log.Debug("ENR request failed", "id", node.ID(), "err", err) + } else { + if tab.enrFilter != nil && !tab.enrFilter(newrec.Record()) { + tab.log.Trace("ENR record filter out", "id", node.ID(), "err", errors.New("filtered node")) + // TODO: use didRespond to express failure temporarily + resp.didRespond = false + } else { + resp.newRecord = newrec + } + } + } + + select { + case tab.revalResponseCh <- resp: + case <-tab.closed: + } +} + +// handleResponse processes the result of a revalidation request. +func (tr *tableRevalidation) handleResponse(tab *Table, resp revalidationResponse) { + var ( + now = tab.cfg.Clock.Now() + n = resp.n + b = tab.bucket(n.ID()) + ) + delete(tr.activeReq, n.ID()) + + // If the node was removed from the table while getting checked, we need to stop + // processing here to avoid re-adding it. + if n.revalList == nil { + return + } + + // Store potential seeds in database. + // This is done via defer to avoid holding Table lock while writing to DB. + defer func() { + if n.isValidatedLive && n.livenessChecks > 5 { + tab.db.UpdateNode(resp.n.Node) + } + }() + + // Remaining logic needs access to Table internals. + tab.mutex.Lock() + defer tab.mutex.Unlock() + + if !resp.didRespond { + n.livenessChecks /= 3 + if n.livenessChecks <= 0 { + tab.deleteInBucket(b, n.ID()) + } else { + tab.log.Debug("Node revalidation failed", "b", b.index, "id", n.ID(), "checks", n.livenessChecks, "q", n.revalList.name) + tr.moveToList(&tr.fast, n, now, &tab.rand) + } + return + } + + // The node responded. + n.livenessChecks++ + n.isValidatedLive = true + tab.log.Debug("Node revalidated", "b", b.index, "id", n.ID(), "checks", n.livenessChecks, "q", n.revalList.name) + var endpointChanged bool + if resp.newRecord != nil { + _, endpointChanged = tab.bumpInBucket(b, resp.newRecord, false) + } + + // Node moves to slow list if it passed and hasn't changed. + if !endpointChanged { + tr.moveToList(&tr.slow, n, now, &tab.rand) + } +} + +// moveToList ensures n is in the 'dest' list. +func (tr *tableRevalidation) moveToList(dest *revalidationList, n *tableNode, now mclock.AbsTime, rand randomSource) { + if n.revalList == dest { + return + } + if n.revalList != nil { + n.revalList.remove(n) + } + dest.push(n, now, rand) +} + +// revalidationList holds a list nodes and the next revalidation time. +type revalidationList struct { + nodes []*tableNode + nextTime mclock.AbsTime + interval time.Duration + name string +} + +// get returns a random node from the queue. Nodes in the 'exclude' map are not returned. +func (list *revalidationList) get(now mclock.AbsTime, rand randomSource, exclude map[enode.ID]struct{}) *tableNode { + if now < list.nextTime || len(list.nodes) == 0 { + return nil + } + for i := 0; i < len(list.nodes)*3; i++ { + n := list.nodes[rand.Intn(len(list.nodes))] + _, excluded := exclude[n.ID()] + if !excluded { + return n + } + } + return nil +} + +func (list *revalidationList) schedule(now mclock.AbsTime, rand randomSource) { + list.nextTime = now.Add(time.Duration(rand.Int63n(int64(list.interval)))) +} + +func (list *revalidationList) push(n *tableNode, now mclock.AbsTime, rand randomSource) { + list.nodes = append(list.nodes, n) + if list.nextTime == never { + list.schedule(now, rand) + } + n.revalList = list +} + +func (list *revalidationList) remove(n *tableNode) { + i := slices.Index(list.nodes, n) + if i == -1 { + panic(fmt.Errorf("node %v not found in list", n.ID())) + } + list.nodes = slices.Delete(list.nodes, i, i+1) + if len(list.nodes) == 0 { + list.nextTime = never + } + n.revalList = nil +} + +func (list *revalidationList) contains(id enode.ID) bool { + return slices.ContainsFunc(list.nodes, func(n *tableNode) bool { + return n.ID() == id + }) +} diff --git a/p2p/discover/table_reval_test.go b/p2p/discover/table_reval_test.go new file mode 100644 index 0000000000..3605443934 --- /dev/null +++ b/p2p/discover/table_reval_test.go @@ -0,0 +1,119 @@ +// Copyright 2024 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package discover + +import ( + "net" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common/mclock" + "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/ethereum/go-ethereum/p2p/enr" +) + +// This test checks that revalidation can handle a node disappearing while +// a request is active. +func TestRevalidation_nodeRemoved(t *testing.T) { + var ( + clock mclock.Simulated + transport = newPingRecorder() + tab, db = newInactiveTestTable(transport, Config{Clock: &clock}) + tr = &tab.revalidation + ) + defer db.Close() + + // Add a node to the table. + node := nodeAtDistance(tab.self().ID(), 255, net.IP{77, 88, 99, 1}) + tab.handleAddNode(addNodeOp{node: node}) + + // Start a revalidation request. Schedule once to get the next start time, + // then advance the clock to that point and schedule again to start. + next := tr.run(tab, clock.Now()) + clock.Run(time.Duration(next + 1)) + tr.run(tab, clock.Now()) + if len(tr.activeReq) != 1 { + t.Fatal("revalidation request did not start:", tr.activeReq) + } + + // Delete the node. + tab.deleteInBucket(tab.bucket(node.ID()), node.ID()) + + // Now finish the revalidation request. + var resp revalidationResponse + select { + case resp = <-tab.revalResponseCh: + case <-time.After(1 * time.Second): + t.Fatal("timed out waiting for revalidation") + } + tr.handleResponse(tab, resp) + + // Ensure the node was not re-added to the table. + if tab.getNode(node.ID()) != nil { + t.Fatal("node was re-added to Table") + } + if tr.fast.contains(node.ID()) || tr.slow.contains(node.ID()) { + t.Fatal("removed node contained in revalidation list") + } +} + +// This test checks that nodes with an updated endpoint remain in the fast revalidation list. +func TestRevalidation_endpointUpdate(t *testing.T) { + var ( + clock mclock.Simulated + transport = newPingRecorder() + tab, db = newInactiveTestTable(transport, Config{Clock: &clock}) + tr = &tab.revalidation + ) + defer db.Close() + + // Add node to table. + node := nodeAtDistance(tab.self().ID(), 255, net.IP{77, 88, 99, 1}) + tab.handleAddNode(addNodeOp{node: node}) + + // Update the record in transport, including endpoint update. + record := node.Record() + record.Set(enr.IP{100, 100, 100, 100}) + record.Set(enr.UDP(9999)) + nodev2 := enode.SignNull(record, node.ID()) + transport.updateRecord(nodev2) + + // Start a revalidation request. Schedule once to get the next start time, + // then advance the clock to that point and schedule again to start. + next := tr.run(tab, clock.Now()) + clock.Run(time.Duration(next + 1)) + tr.run(tab, clock.Now()) + if len(tr.activeReq) != 1 { + t.Fatal("revalidation request did not start:", tr.activeReq) + } + + // Now finish the revalidation request. + var resp revalidationResponse + select { + case resp = <-tab.revalResponseCh: + case <-time.After(1 * time.Second): + t.Fatal("timed out waiting for revalidation") + } + tr.handleResponse(tab, resp) + + if tr.fast.nodes[0].ID() != node.ID() { + t.Fatal("node not contained in fast revalidation list") + } + if tr.fast.nodes[0].isValidatedLive { + t.Fatal("node is marked live after endpoint change") + } +} diff --git a/p2p/discover/table_test.go b/p2p/discover/table_test.go index ffe055e843..a6666fdcbf 100644 --- a/p2p/discover/table_test.go +++ b/p2p/discover/table_test.go @@ -20,16 +20,19 @@ import ( "crypto/ecdsa" "fmt" "math/rand" - "net" "reflect" + "slices" "testing" "testing/quick" "time" + "github.com/ethereum/go-ethereum/common/mclock" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/forkid" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/internal/testlog" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/p2p/enr" "github.com/ethereum/go-ethereum/p2p/netutil" @@ -53,106 +56,109 @@ func TestTable_pingReplace(t *testing.T) { } func testPingReplace(t *testing.T, newNodeIsResponding, lastInBucketIsResponding bool) { + simclock := new(mclock.Simulated) transport := newPingRecorder() - tab, db := newTestTable(transport) + tab, db := newTestTable(transport, Config{ + Clock: simclock, + Log: testlog.Logger(t, log.LevelTrace), + }) defer db.Close() defer tab.close() <-tab.initDone // Fill up the sender's bucket. - pingKey, _ := crypto.HexToECDSA("45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8") - pingSender := wrapNode(enode.NewV4(&pingKey.PublicKey, net.IP{127, 0, 0, 1}, 99, 99)) - last := fillBucket(tab, pingSender) + replacementNodeKey, _ := crypto.HexToECDSA("45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8") + replacementNode := enode.NewV4(&replacementNodeKey.PublicKey, net.IP{127, 0, 0, 1}, 99, 99) + last := fillBucket(tab, replacementNode.ID()) + tab.mutex.Lock() + nodeEvents := newNodeEventRecorder(128) + tab.nodeAddedHook = nodeEvents.nodeAdded + tab.nodeRemovedHook = nodeEvents.nodeRemoved + tab.mutex.Unlock() - // Add the sender as if it just pinged us. Revalidate should replace the last node in - // its bucket if it is unresponsive. Revalidate again to ensure that + // The revalidation process should replace + // this node in the bucket if it is unresponsive. transport.dead[last.ID()] = !lastInBucketIsResponding - transport.dead[pingSender.ID()] = !newNodeIsResponding - tab.addSeenNodeSync(pingSender) - tab.doRevalidate(make(chan struct{}, 1)) - tab.doRevalidate(make(chan struct{}, 1)) - - if !transport.pinged[last.ID()] { - // Oldest node in bucket is pinged to see whether it is still alive. - t.Error("table did not ping last node in bucket") + transport.dead[replacementNode.ID()] = !newNodeIsResponding + + // Add replacement node to table. + tab.addFoundNode(replacementNode, false) + + t.Log("last:", last.ID()) + t.Log("replacement:", replacementNode.ID()) + + // Wait until the last node was pinged. + waitForRevalidationPing(t, transport, tab, last.ID()) + + if !lastInBucketIsResponding { + if !nodeEvents.waitNodeAbsent(last.ID(), 2*time.Second) { + t.Error("last node was not removed") + } + if !nodeEvents.waitNodePresent(replacementNode.ID(), 2*time.Second) { + t.Error("replacement node was not added") + } + + // If a replacement is expected, we also need to wait until the replacement node + // was pinged and added/removed. + waitForRevalidationPing(t, transport, tab, replacementNode.ID()) + if !newNodeIsResponding { + if !nodeEvents.waitNodeAbsent(replacementNode.ID(), 2*time.Second) { + t.Error("replacement node was not removed") + } + } } + // Check bucket content. tab.mutex.Lock() defer tab.mutex.Unlock() wantSize := bucketSize if !lastInBucketIsResponding && !newNodeIsResponding { wantSize-- } - if l := len(tab.bucket(pingSender.ID()).entries); l != wantSize { - t.Errorf("wrong bucket size after bond: got %d, want %d", l, wantSize) + bucket := tab.bucket(replacementNode.ID()) + if l := len(bucket.entries); l != wantSize { + t.Errorf("wrong bucket size after revalidation: got %d, want %d", l, wantSize) } - if found := contains(tab.bucket(pingSender.ID()).entries, last.ID()); found != lastInBucketIsResponding { - t.Errorf("last entry found: %t, want: %t", found, lastInBucketIsResponding) + if ok := containsID(bucket.entries, last.ID()); ok != lastInBucketIsResponding { + t.Errorf("revalidated node found: %t, want: %t", ok, lastInBucketIsResponding) } wantNewEntry := newNodeIsResponding && !lastInBucketIsResponding - if found := contains(tab.bucket(pingSender.ID()).entries, pingSender.ID()); found != wantNewEntry { - t.Errorf("new entry found: %t, want: %t", found, wantNewEntry) + if ok := containsID(bucket.entries, replacementNode.ID()); ok != wantNewEntry { + t.Errorf("replacement node found: %t, want: %t", ok, wantNewEntry) } } -func TestBucket_bumpNoDuplicates(t *testing.T) { - t.Parallel() - cfg := &quick.Config{ - MaxCount: 1000, - Rand: rand.New(rand.NewSource(time.Now().Unix())), - Values: func(args []reflect.Value, rand *rand.Rand) { - // generate a random list of nodes. this will be the content of the bucket. - n := rand.Intn(bucketSize-1) + 1 - nodes := make([]*node, n) - for i := range nodes { - nodes[i] = nodeAtDistance(enode.ID{}, 200, intIP(200)) - } - args[0] = reflect.ValueOf(nodes) - // generate random bump positions. - bumps := make([]int, rand.Intn(100)) - for i := range bumps { - bumps[i] = rand.Intn(len(nodes)) - } - args[1] = reflect.ValueOf(bumps) - }, - } - - prop := func(nodes []*node, bumps []int) (ok bool) { - tab, db := newTestTable(newPingRecorder()) - defer db.Close() - defer tab.close() +// waitForRevalidationPing waits until a PING message is sent to a node with the given id. +func waitForRevalidationPing(t *testing.T, transport *pingRecorder, tab *Table, id enode.ID) *enode.Node { + t.Helper() - b := &bucket{entries: make([]*node, len(nodes))} - copy(b.entries, nodes) - for i, pos := range bumps { - tab.bumpInBucket(b, b.entries[pos]) - if hasDuplicates(b.entries) { - t.Logf("bucket has duplicates after %d/%d bumps:", i+1, len(bumps)) - for _, n := range b.entries { - t.Logf(" %p", n) - } - return false - } + simclock := tab.cfg.Clock.(*mclock.Simulated) + maxAttempts := tab.len() * 8 + for i := 0; i < maxAttempts; i++ { + simclock.Run(tab.cfg.PingInterval * slowRevalidationFactor) + p := transport.waitPing(2 * time.Second) + if p == nil { + continue + } + if id == (enode.ID{}) || p.ID() == id { + return p } - checkIPLimitInvariant(t, tab) - return true - } - if err := quick.Check(prop, cfg); err != nil { - t.Error(err) } + t.Fatalf("Table did not ping node %v (%d attempts)", id, maxAttempts) + return nil } // This checks that the table-wide IP limit is applied correctly. func TestTable_IPLimit(t *testing.T) { transport := newPingRecorder() - tab, db := newTestTable(transport) + tab, db := newTestTable(transport, Config{}) defer db.Close() defer tab.close() for i := 0; i < tableIPLimit+1; i++ { n := nodeAtDistance(tab.self().ID(), i, net.IP{172, 0, 1, byte(i)}) - tab.addSeenNodeSync(n) + tab.addFoundNode(n, false) } if tab.len() > tableIPLimit { t.Errorf("too many nodes in table") @@ -163,14 +169,14 @@ func TestTable_IPLimit(t *testing.T) { // This checks that the per-bucket IP limit is applied correctly. func TestTable_BucketIPLimit(t *testing.T) { transport := newPingRecorder() - tab, db := newTestTable(transport) + tab, db := newTestTable(transport, Config{}) defer db.Close() defer tab.close() d := 3 for i := 0; i < bucketIPLimit+1; i++ { n := nodeAtDistance(tab.self().ID(), d, net.IP{172, 0, 1, byte(i)}) - tab.addSeenNode(n) + tab.addFoundNode(n, false) } if tab.len() > bucketIPLimit { t.Errorf("too many nodes in table") @@ -186,7 +192,7 @@ func checkIPLimitInvariant(t *testing.T, tab *Table) { tabset := netutil.DistinctNetSet{Subnet: tableSubnet, Limit: tableIPLimit} for _, b := range tab.buckets { for _, n := range b.entries { - tabset.Add(n.IP()) + tabset.AddAddr(n.IPAddr()) } } if tabset.String() != tab.ips.String() { @@ -200,7 +206,7 @@ func TestTable_findnodeByID(t *testing.T) { test := func(test *closeTest) bool { // for any node table, Target and N transport := newPingRecorder() - tab, db := newTestTable(transport) + tab, db := newTestTable(transport, Config{}) defer db.Close() defer tab.close() fillTable(tab, test.All, true) @@ -231,7 +237,7 @@ func TestTable_findnodeByID(t *testing.T) { // check that the result nodes have minimum distance to target. for _, b := range tab.buckets { for _, n := range b.entries { - if contains(result, n.ID()) { + if containsID(result, n.ID()) { continue // don't run the check below for nodes in result } farthestResult := result[len(result)-1].ID() @@ -254,7 +260,7 @@ func TestTable_findnodeByID(t *testing.T) { type closeTest struct { Self enode.ID Target enode.ID - All []*node + All []*enode.Node N int } @@ -266,16 +272,15 @@ func (*closeTest) Generate(rand *rand.Rand, size int) reflect.Value { } for _, id := range gen([]enode.ID{}, rand).([]enode.ID) { r := new(enr.Record) - r.Set(enr.IP(genIP(rand))) - n := wrapNode(enode.SignNull(r, id)) - n.livenessChecks = 1 + r.Set(enr.IPv4Addr(netutil.RandomAddr(rand, true))) + n := enode.SignNull(r, id) t.All = append(t.All, n) } return reflect.ValueOf(t) } -func TestTable_addVerifiedNode(t *testing.T) { - tab, db := newTestTable(newPingRecorder()) +func TestTable_addInboundNode(t *testing.T) { + tab, db := newTestTable(newPingRecorder(), Config{}) <-tab.initDone defer db.Close() defer tab.close() @@ -283,31 +288,29 @@ func TestTable_addVerifiedNode(t *testing.T) { // Insert two nodes. n1 := nodeAtDistance(tab.self().ID(), 256, net.IP{88, 77, 66, 1}) n2 := nodeAtDistance(tab.self().ID(), 256, net.IP{88, 77, 66, 2}) - tab.addSeenNodeSync(n1) - tab.addSeenNodeSync(n2) + tab.addFoundNode(n1, false) + tab.addFoundNode(n2, false) + checkBucketContent(t, tab, []*enode.Node{n1, n2}) - // Verify bucket content: - bcontent := []*node{n1, n2} - if !reflect.DeepEqual(tab.bucket(n1.ID()).entries, bcontent) { - t.Fatalf("wrong bucket content: %v", tab.bucket(n1.ID()).entries) - } - - // Add a changed version of n2. + // Add a changed version of n2. The bucket should be updated. newrec := n2.Record() newrec.Set(enr.IP{99, 99, 99, 99}) - newn2 := wrapNode(enode.SignNull(newrec, n2.ID())) - tab.addVerifiedNodeSync(newn2) - - // Check that bucket is updated correctly. - newBcontent := []*node{newn2, n1} - if !reflect.DeepEqual(tab.bucket(n1.ID()).entries, newBcontent) { - t.Fatalf("wrong bucket content after update: %v", tab.bucket(n1.ID()).entries) - } - checkIPLimitInvariant(t, tab) + n2v2 := enode.SignNull(newrec, n2.ID()) + tab.addInboundNode(n2v2) + checkBucketContent(t, tab, []*enode.Node{n1, n2v2}) + + // Try updating n2 without sequence number change. The update is accepted + // because it's inbound. + newrec = n2.Record() + newrec.Set(enr.IP{100, 100, 100, 100}) + newrec.SetSeq(n2.Seq()) + n2v3 := enode.SignNull(newrec, n2.ID()) + tab.addInboundNode(n2v3) + checkBucketContent(t, tab, []*enode.Node{n1, n2v3}) } -func TestTable_addSeenNode(t *testing.T) { - tab, db := newTestTable(newPingRecorder()) +func TestTable_addFoundNode(t *testing.T) { + tab, db := newTestTable(newPingRecorder(), Config{}) <-tab.initDone defer db.Close() defer tab.close() @@ -315,25 +318,86 @@ func TestTable_addSeenNode(t *testing.T) { // Insert two nodes. n1 := nodeAtDistance(tab.self().ID(), 256, net.IP{88, 77, 66, 1}) n2 := nodeAtDistance(tab.self().ID(), 256, net.IP{88, 77, 66, 2}) - tab.addSeenNodeSync(n1) - tab.addSeenNodeSync(n2) - - // Verify bucket content: - bcontent := []*node{n1, n2} - if !reflect.DeepEqual(tab.bucket(n1.ID()).entries, bcontent) { - t.Fatalf("wrong bucket content: %v", tab.bucket(n1.ID()).entries) - } + tab.addFoundNode(n1, false) + tab.addFoundNode(n2, false) + checkBucketContent(t, tab, []*enode.Node{n1, n2}) - // Add a changed version of n2. + // Add a changed version of n2. The bucket should be updated. newrec := n2.Record() newrec.Set(enr.IP{99, 99, 99, 99}) - newn2 := wrapNode(enode.SignNull(newrec, n2.ID())) - tab.addSeenNodeSync(newn2) + n2v2 := enode.SignNull(newrec, n2.ID()) + tab.addFoundNode(n2v2, false) + checkBucketContent(t, tab, []*enode.Node{n1, n2v2}) + + // Try updating n2 without a sequence number change. + // The update should not be accepted. + newrec = n2.Record() + newrec.Set(enr.IP{100, 100, 100, 100}) + newrec.SetSeq(n2.Seq()) + n2v3 := enode.SignNull(newrec, n2.ID()) + tab.addFoundNode(n2v3, false) + checkBucketContent(t, tab, []*enode.Node{n1, n2v2}) +} - // Check that bucket content is unchanged. - if !reflect.DeepEqual(tab.bucket(n1.ID()).entries, bcontent) { - t.Fatalf("wrong bucket content after update: %v", tab.bucket(n1.ID()).entries) +// This test checks that discv4 nodes can update their own endpoint via PING. +func TestTable_addInboundNodeUpdateV4Accept(t *testing.T) { + tab, db := newTestTable(newPingRecorder(), Config{}) + <-tab.initDone + defer db.Close() + defer tab.close() + + // Add a v4 node. + key, _ := crypto.HexToECDSA("dd3757a8075e88d0f2b1431e7d3c5b1562e1c0aab9643707e8cbfcc8dae5cfe3") + n1 := enode.NewV4(&key.PublicKey, net.IP{88, 77, 66, 1}, 9000, 9000) + tab.addInboundNode(n1) + checkBucketContent(t, tab, []*enode.Node{n1}) + + // Add an updated version with changed IP. + // The update will be accepted because it is inbound. + n1v2 := enode.NewV4(&key.PublicKey, net.IP{99, 99, 99, 99}, 9000, 9000) + tab.addInboundNode(n1v2) + checkBucketContent(t, tab, []*enode.Node{n1v2}) +} + +// This test checks that discv4 node entries will NOT be updated when a +// changed record is found. +func TestTable_addFoundNodeV4UpdateReject(t *testing.T) { + tab, db := newTestTable(newPingRecorder(), Config{}) + <-tab.initDone + defer db.Close() + defer tab.close() + + // Add a v4 node. + key, _ := crypto.HexToECDSA("dd3757a8075e88d0f2b1431e7d3c5b1562e1c0aab9643707e8cbfcc8dae5cfe3") + n1 := enode.NewV4(&key.PublicKey, net.IP{88, 77, 66, 1}, 9000, 9000) + tab.addFoundNode(n1, false) + checkBucketContent(t, tab, []*enode.Node{n1}) + + // Add an updated version with changed IP. + // The update won't be accepted because it isn't inbound. + n1v2 := enode.NewV4(&key.PublicKey, net.IP{99, 99, 99, 99}, 9000, 9000) + tab.addFoundNode(n1v2, false) + checkBucketContent(t, tab, []*enode.Node{n1}) +} + +func checkBucketContent(t *testing.T, tab *Table, nodes []*enode.Node) { + t.Helper() + + b := tab.bucket(nodes[0].ID()) + if reflect.DeepEqual(unwrapNodes(b.entries), nodes) { + return + } + t.Log("wrong bucket content. have nodes:") + for _, n := range b.entries { + t.Logf(" %v (seq=%v, ip=%v)", n.ID(), n.Seq(), n.IPAddr()) } + t.Log("want nodes:") + for _, n := range nodes { + t.Logf(" %v (seq=%v, ip=%v)", n.ID(), n.Seq(), n.IPAddr()) + } + t.FailNow() + + // Also check IP limits. checkIPLimitInvariant(t, tab) } @@ -341,7 +405,10 @@ func TestTable_addSeenNode(t *testing.T) { // announces a new sequence number, the new record should be pulled. func TestTable_revalidateSyncRecord(t *testing.T) { transport := newPingRecorder() - tab, db := newTestTable(transport) + tab, db := newTestTable(transport, Config{ + Clock: new(mclock.Simulated), + Log: testlog.Logger(t, log.LevelTrace), + }) <-tab.initDone defer db.Close() defer tab.close() @@ -350,15 +417,19 @@ func TestTable_revalidateSyncRecord(t *testing.T) { var r enr.Record r.Set(enr.IP(net.IP{127, 0, 0, 1})) id := enode.ID{1} - n1 := wrapNode(enode.SignNull(&r, id)) - tab.addSeenNodeSync(n1) + n1 := enode.SignNull(&r, id) + tab.addFoundNode(n1, false) // Update the node record. r.Set(enr.WithEntry("foo", "bar")) n2 := enode.SignNull(&r, id) transport.updateRecord(n2) - tab.doRevalidate(make(chan struct{}, 1)) + // Wait for revalidation. We wait for the node to be revalidated two times + // in order to synchronize with the update in the table. + waitForRevalidationPing(t, transport, tab, n2.ID()) + waitForRevalidationPing(t, transport, tab, n2.ID()) + intable := tab.getNode(id) if !reflect.DeepEqual(intable, n2) { t.Fatalf("table contains old record with seq %d, want seq %d", intable.Seq(), n2.Seq()) @@ -400,6 +471,47 @@ func TestTable_filterNode(t *testing.T) { t.Logf("Check correct genesis ENR record - passed") } +func TestNodesPush(t *testing.T) { + var target enode.ID + n1 := nodeAtDistance(target, 255, intIP(1)) + n2 := nodeAtDistance(target, 254, intIP(2)) + n3 := nodeAtDistance(target, 253, intIP(3)) + perm := [][]*enode.Node{ + {n3, n2, n1}, + {n3, n1, n2}, + {n2, n3, n1}, + {n2, n1, n3}, + {n1, n3, n2}, + {n1, n2, n3}, + } + + // Insert all permutations into lists with size limit 3. + for _, nodes := range perm { + list := nodesByDistance{target: target} + for _, n := range nodes { + list.push(n, 3) + } + if !slices.EqualFunc(list.entries, perm[0], nodeIDEqual) { + t.Fatal("not equal") + } + } + + // Insert all permutations into lists with size limit 2. + for _, nodes := range perm { + list := nodesByDistance{target: target} + for _, n := range nodes { + list.push(n, 2) + } + if !slices.EqualFunc(list.entries, perm[0][:2], nodeIDEqual) { + t.Fatal("not equal") + } + } +} + +func nodeIDEqual[N nodeType](n1, n2 N) bool { + return n1.ID() == n2.ID() +} + // gen wraps quick.Value so it's easier to use. // it generates a random value of the given value's type. func gen(typ interface{}, rand *rand.Rand) interface{} { @@ -410,12 +522,6 @@ func gen(typ interface{}, rand *rand.Rand) interface{} { return v.Interface() } -func genIP(rand *rand.Rand) net.IP { - ip := make(net.IP, 4) - rand.Read(ip) - return ip -} - func quickcfg() *quick.Config { return &quick.Config{ MaxCount: 5000, diff --git a/p2p/discover/table_util_test.go b/p2p/discover/table_util_test.go index 7605351127..5b2699d460 100644 --- a/p2p/discover/table_util_test.go +++ b/p2p/discover/table_util_test.go @@ -26,9 +26,10 @@ import ( "net" "slices" "sync" + "sync/atomic" + "time" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/p2p/enr" ) @@ -41,27 +42,32 @@ func init() { nullNode = enode.SignNull(&r, enode.ID{}) } -func newTestTable(t transport) (*Table, *enode.DB) { - cfg := Config{Log: log.Root()} +func newTestTable(t transport, cfg Config) (*Table, *enode.DB) { + tab, db := newInactiveTestTable(t, cfg) + go tab.loop() + return tab, db +} + +// newInactiveTestTable creates a Table without running the main loop. +func newInactiveTestTable(t transport, cfg Config) (*Table, *enode.DB) { db, _ := enode.OpenDB("") tab, _ := newTable(t, db, cfg) - go tab.loop() return tab, db } // nodeAtDistance creates a node for which enode.LogDist(base, n.id) == ld. -func nodeAtDistance(base enode.ID, ld int, ip net.IP) *node { +func nodeAtDistance(base enode.ID, ld int, ip net.IP) *enode.Node { var r enr.Record r.Set(enr.IP(ip)) r.Set(enr.UDP(30303)) - return wrapNode(enode.SignNull(&r, idAtDistance(base, ld))) + return enode.SignNull(&r, idAtDistance(base, ld)) } // nodesAtDistance creates n nodes for which enode.LogDist(base, node.ID()) == ld. func nodesAtDistance(base enode.ID, ld int, n int) []*enode.Node { results := make([]*enode.Node, n) for i := range results { - results[i] = unwrapNode(nodeAtDistance(base, ld, intIP(i))) + results[i] = nodeAtDistance(base, ld, intIP(i)) } return results } @@ -94,36 +100,39 @@ func idAtDistance(a enode.ID, n int) (b enode.ID) { return b } +// intIP returns a LAN IP address based on i. func intIP(i int) net.IP { - return net.IP{byte(i), 0, 2, byte(i)} + return net.IP{10, 0, byte(i >> 8), byte(i & 0xFF)} } // fillBucket inserts nodes into the given bucket until it is full. -func fillBucket(tab *Table, n *node) (last *node) { - ld := enode.LogDist(tab.self().ID(), n.ID()) - b := tab.bucket(n.ID()) +func fillBucket(tab *Table, id enode.ID) (last *tableNode) { + ld := enode.LogDist(tab.self().ID(), id) + b := tab.bucket(id) for len(b.entries) < bucketSize { - b.entries = append(b.entries, nodeAtDistance(tab.self().ID(), ld, intIP(ld))) + node := nodeAtDistance(tab.self().ID(), ld, intIP(ld)) + if !tab.addFoundNode(node, false) { + panic("node not added") + } } return b.entries[bucketSize-1] } // fillTable adds nodes the table to the end of their corresponding bucket // if the bucket is not full. The caller must not hold tab.mutex. -func fillTable(tab *Table, nodes []*node, setLive bool) { +func fillTable(tab *Table, nodes []*enode.Node, setLive bool) { for _, n := range nodes { - if setLive { - n.livenessChecks = 1 - } - tab.addSeenNodeSync(n) + tab.addFoundNode(n, setLive) } } type pingRecorder struct { - mu sync.Mutex - dead, pinged map[enode.ID]bool - records map[enode.ID]*enode.Node - n *enode.Node + mu sync.Mutex + cond *sync.Cond + dead map[enode.ID]bool + records map[enode.ID]*enode.Node + pinged []*enode.Node + n *enode.Node } func newPingRecorder() *pingRecorder { @@ -131,12 +140,13 @@ func newPingRecorder() *pingRecorder { r.Set(enr.IP{0, 0, 0, 0}) n := enode.SignNull(&r, enode.ID{}) - return &pingRecorder{ + t := &pingRecorder{ dead: make(map[enode.ID]bool), - pinged: make(map[enode.ID]bool), records: make(map[enode.ID]*enode.Node), n: n, } + t.cond = sync.NewCond(&t.mu) + return t } // updateRecord updates a node record. Future calls to ping and @@ -152,12 +162,40 @@ func (t *pingRecorder) Self() *enode.Node { return nullNode } func (t *pingRecorder) lookupSelf() []*enode.Node { return nil } func (t *pingRecorder) lookupRandom() []*enode.Node { return nil } +func (t *pingRecorder) waitPing(timeout time.Duration) *enode.Node { + t.mu.Lock() + defer t.mu.Unlock() + + // Wake up the loop on timeout. + var timedout atomic.Bool + timer := time.AfterFunc(timeout, func() { + timedout.Store(true) + t.cond.Broadcast() + }) + defer timer.Stop() + + // Wait for a ping. + for { + if timedout.Load() { + return nil + } + if len(t.pinged) > 0 { + n := t.pinged[0] + t.pinged = append(t.pinged[:0], t.pinged[1:]...) + return n + } + t.cond.Wait() + } +} + // ping simulates a ping request. func (t *pingRecorder) ping(n *enode.Node) (seq uint64, err error) { t.mu.Lock() defer t.mu.Unlock() - t.pinged[n.ID()] = true + t.pinged = append(t.pinged, n) + t.cond.Broadcast() + if t.dead[n.ID()] { return 0, errTimeout } @@ -178,7 +216,7 @@ func (t *pingRecorder) RequestENR(n *enode.Node) (*enode.Node, error) { return t.records[n.ID()], nil } -func hasDuplicates(slice []*node) bool { +func hasDuplicates(slice []*enode.Node) bool { seen := make(map[enode.ID]bool, len(slice)) for i, e := range slice { if e == nil { @@ -217,17 +255,17 @@ NotEqual: } func nodeEqual(n1 *enode.Node, n2 *enode.Node) bool { - return n1.ID() == n2.ID() && n1.IP().Equal(n2.IP()) + return n1.ID() == n2.ID() && n1.IPAddr() == n2.IPAddr() } -func sortByID(nodes []*enode.Node) { - slices.SortFunc(nodes, func(a, b *enode.Node) int { +func sortByID[N nodeType](nodes []N) { + slices.SortFunc(nodes, func(a, b N) int { return bytes.Compare(a.ID().Bytes(), b.ID().Bytes()) }) } -func sortedByDistanceTo(distbase enode.ID, slice []*node) bool { - return slices.IsSortedFunc(slice, func(a, b *node) int { +func sortedByDistanceTo(distbase enode.ID, slice []*enode.Node) bool { + return slices.IsSortedFunc(slice, func(a, b *enode.Node) int { return enode.DistCmp(distbase, a.ID(), b.ID()) }) } @@ -257,3 +295,57 @@ func hexEncPubkey(h string) (ret encPubkey) { copy(ret[:], b) return ret } + +type nodeEventRecorder struct { + evc chan recordedNodeEvent +} + +type recordedNodeEvent struct { + node *tableNode + added bool +} + +func newNodeEventRecorder(buffer int) *nodeEventRecorder { + return &nodeEventRecorder{ + evc: make(chan recordedNodeEvent, buffer), + } +} + +func (set *nodeEventRecorder) nodeAdded(b *bucket, n *tableNode) { + select { + case set.evc <- recordedNodeEvent{n, true}: + default: + panic("no space in event buffer") + } +} + +func (set *nodeEventRecorder) nodeRemoved(b *bucket, n *tableNode) { + select { + case set.evc <- recordedNodeEvent{n, false}: + default: + panic("no space in event buffer") + } +} + +func (set *nodeEventRecorder) waitNodePresent(id enode.ID, timeout time.Duration) bool { + return set.waitNodeEvent(id, timeout, true) +} + +func (set *nodeEventRecorder) waitNodeAbsent(id enode.ID, timeout time.Duration) bool { + return set.waitNodeEvent(id, timeout, false) +} + +func (set *nodeEventRecorder) waitNodeEvent(id enode.ID, timeout time.Duration, added bool) bool { + timer := time.NewTimer(timeout) + defer timer.Stop() + for { + select { + case ev := <-set.evc: + if ev.node.ID() == id && ev.added == added { + return true + } + case <-timer.C: + return false + } + } +} diff --git a/p2p/discover/v4_lookup_test.go b/p2p/discover/v4_lookup_test.go index 5682f262be..bc9475a8b3 100644 --- a/p2p/discover/v4_lookup_test.go +++ b/p2p/discover/v4_lookup_test.go @@ -19,7 +19,7 @@ package discover import ( "crypto/ecdsa" "fmt" - "net" + "net/netip" "slices" "testing" @@ -40,7 +40,7 @@ func TestUDPv4_Lookup(t *testing.T) { } // Seed table with initial node. - fillTable(test.table, []*node{wrapNode(lookupTestnet.node(256, 0))}, true) + fillTable(test.table, []*enode.Node{lookupTestnet.node(256, 0)}, true) // Start the lookup. resultC := make(chan []*enode.Node, 1) @@ -70,9 +70,9 @@ func TestUDPv4_LookupIterator(t *testing.T) { defer test.close() // Seed table with initial nodes. - bootnodes := make([]*node, len(lookupTestnet.dists[256])) + bootnodes := make([]*enode.Node, len(lookupTestnet.dists[256])) for i := range lookupTestnet.dists[256] { - bootnodes[i] = wrapNode(lookupTestnet.node(256, i)) + bootnodes[i] = lookupTestnet.node(256, i) } fillTable(test.table, bootnodes, true) go serveTestnet(test, lookupTestnet) @@ -105,9 +105,9 @@ func TestUDPv4_LookupIteratorClose(t *testing.T) { defer test.close() // Seed table with initial nodes. - bootnodes := make([]*node, len(lookupTestnet.dists[256])) + bootnodes := make([]*enode.Node, len(lookupTestnet.dists[256])) for i := range lookupTestnet.dists[256] { - bootnodes[i] = wrapNode(lookupTestnet.node(256, i)) + bootnodes[i] = lookupTestnet.node(256, i) } fillTable(test.table, bootnodes, true) go serveTestnet(test, lookupTestnet) @@ -136,7 +136,7 @@ func TestUDPv4_LookupIteratorClose(t *testing.T) { func serveTestnet(test *udpTest, testnet *preminedTestnet) { for done := false; !done; { - done = test.waitPacketOut(func(p v4wire.Packet, to *net.UDPAddr, hash []byte) { + done = test.waitPacketOut(func(p v4wire.Packet, to netip.AddrPort, hash []byte) { n, key := testnet.nodeByAddr(to) switch p.(type) { case *v4wire.Ping: @@ -158,10 +158,10 @@ func checkLookupResults(t *testing.T, tn *preminedTestnet, results []*enode.Node for _, e := range results { t.Logf(" ld=%d, %x", enode.LogDist(tn.target.id(), e.ID()), e.ID().Bytes()) } - if hasDuplicates(wrapNodes(results)) { + if hasDuplicates(results) { t.Errorf("result set contains duplicate entries") } - if !sortedByDistanceTo(tn.target.id(), wrapNodes(results)) { + if !sortedByDistanceTo(tn.target.id(), results) { t.Errorf("result set not sorted by distance to target") } wantNodes := tn.closest(len(results)) @@ -264,9 +264,10 @@ func (tn *preminedTestnet) node(dist, index int) *enode.Node { return n } -func (tn *preminedTestnet) nodeByAddr(addr *net.UDPAddr) (*enode.Node, *ecdsa.PrivateKey) { - dist := int(addr.IP[1])<<8 + int(addr.IP[2]) - index := int(addr.IP[3]) +func (tn *preminedTestnet) nodeByAddr(addr netip.AddrPort) (*enode.Node, *ecdsa.PrivateKey) { + ip := addr.Addr().As4() + dist := int(ip[1])<<8 + int(ip[2]) + index := int(ip[3]) key := tn.dists[dist][index] return tn.node(dist, index), key } @@ -274,7 +275,7 @@ func (tn *preminedTestnet) nodeByAddr(addr *net.UDPAddr) (*enode.Node, *ecdsa.Pr func (tn *preminedTestnet) nodesAtDistance(dist int) []v4wire.Node { result := make([]v4wire.Node, len(tn.dists[dist])) for i := range result { - result[i] = nodeToRPC(wrapNode(tn.node(dist, i))) + result[i] = nodeToRPC(tn.node(dist, i)) } return result } diff --git a/p2p/discover/v4_udp.go b/p2p/discover/v4_udp.go index 6312821e0d..7e38ea27dd 100644 --- a/p2p/discover/v4_udp.go +++ b/p2p/discover/v4_udp.go @@ -25,7 +25,7 @@ import ( "errors" "fmt" "io" - "net" + "net/netip" "sync" "time" @@ -46,6 +46,7 @@ var ( errClockWarp = errors.New("reply deadline too far in the future") errClosed = errors.New("socket closed") errLowPort = errors.New("low port") + errNoUDPEndpoint = errors.New("node has no UDP endpoint") ) const ( @@ -94,7 +95,7 @@ type UDPv4 struct { type replyMatcher struct { // these fields must match in the reply. from enode.ID - ip net.IP + ip netip.Addr ptype byte // time when the request must complete @@ -120,7 +121,7 @@ type replyMatchFunc func(v4wire.Packet) (matched bool, requestDone bool) // reply is a reply packet from a certain node. type reply struct { from enode.ID - ip net.IP + ip netip.Addr data v4wire.Packet // loop indicates whether there was // a matching request by sending on this channel. @@ -143,7 +144,7 @@ func ListenV4(c UDPConn, ln *enode.LocalNode, cfg Config) (*UDPv4, error) { log: cfg.Log, } - tab, err := newMeteredTable(t, ln.Database(), cfg) + tab, err := newTable(t, ln.Database(), cfg) if err != nil { return nil, err } @@ -202,9 +203,12 @@ func (t *UDPv4) Resolve(n *enode.Node) *enode.Node { } func (t *UDPv4) ourEndpoint() v4wire.Endpoint { - n := t.Self() - a := &net.UDPAddr{IP: n.IP(), Port: n.UDP()} - return v4wire.NewEndpoint(a, uint16(n.TCP())) + node := t.Self() + addr, ok := node.UDPEndpoint() + if !ok { + return v4wire.Endpoint{} + } + return v4wire.NewEndpoint(addr, uint16(node.TCP())) } // Ping sends a ping message to the given node. @@ -215,7 +219,11 @@ func (t *UDPv4) Ping(n *enode.Node) error { // ping sends a ping message to the given node and waits for a reply. func (t *UDPv4) ping(n *enode.Node) (seq uint64, err error) { - rm := t.sendPing(n.ID(), &net.UDPAddr{IP: n.IP(), Port: n.UDP()}, nil) + addr, ok := n.UDPEndpoint() + if !ok { + return 0, errNoUDPEndpoint + } + rm := t.sendPing(n.ID(), addr, nil) if err = <-rm.errc; err == nil { seq = rm.reply.(*v4wire.Pong).ENRSeq } @@ -224,7 +232,7 @@ func (t *UDPv4) ping(n *enode.Node) (seq uint64, err error) { // sendPing sends a ping message to the given node and invokes the callback // when the reply arrives. -func (t *UDPv4) sendPing(toid enode.ID, toaddr *net.UDPAddr, callback func()) *replyMatcher { +func (t *UDPv4) sendPing(toid enode.ID, toaddr netip.AddrPort, callback func()) *replyMatcher { req := t.makePing(toaddr) packet, hash, err := v4wire.Encode(t.priv, req) if err != nil { @@ -234,7 +242,7 @@ func (t *UDPv4) sendPing(toid enode.ID, toaddr *net.UDPAddr, callback func()) *r } // Add a matcher for the reply to the pending reply queue. Pongs are matched if they // reference the ping we're about to send. - rm := t.pending(toid, toaddr.IP, v4wire.PongPacket, func(p v4wire.Packet) (matched bool, requestDone bool) { + rm := t.pending(toid, toaddr.Addr(), v4wire.PongPacket, func(p v4wire.Packet) (matched bool, requestDone bool) { matched = bytes.Equal(p.(*v4wire.Pong).ReplyTok, hash) if matched && callback != nil { callback() @@ -247,7 +255,7 @@ func (t *UDPv4) sendPing(toid enode.ID, toaddr *net.UDPAddr, callback func()) *r return rm } -func (t *UDPv4) makePing(toaddr *net.UDPAddr) *v4wire.Ping { +func (t *UDPv4) makePing(toaddr netip.AddrPort) *v4wire.Ping { return &v4wire.Ping{ Version: 4, From: t.ourEndpoint(), @@ -291,35 +299,39 @@ func (t *UDPv4) newRandomLookup(ctx context.Context) *lookup { func (t *UDPv4) newLookup(ctx context.Context, targetKey encPubkey) *lookup { target := enode.ID(crypto.Keccak256Hash(targetKey[:])) ekey := v4wire.Pubkey(targetKey) - it := newLookup(ctx, t.tab, target, func(n *node) ([]*node, error) { - return t.findnode(n.ID(), n.addr(), ekey) + it := newLookup(ctx, t.tab, target, func(n *enode.Node) ([]*enode.Node, error) { + addr, ok := n.UDPEndpoint() + if !ok { + return nil, errNoUDPEndpoint + } + return t.findnode(n.ID(), addr, ekey) }) return it } // findnode sends a findnode request to the given node and waits until // the node has sent up to k neighbors. -func (t *UDPv4) findnode(toid enode.ID, toaddr *net.UDPAddr, target v4wire.Pubkey) ([]*node, error) { - t.ensureBond(toid, toaddr) +func (t *UDPv4) findnode(toid enode.ID, toAddrPort netip.AddrPort, target v4wire.Pubkey) ([]*enode.Node, error) { + t.ensureBond(toid, toAddrPort) // Add a matcher for 'neighbours' replies to the pending reply queue. The matcher is // active until enough nodes have been received. - nodes := make([]*node, 0, bucketSize) + nodes := make([]*enode.Node, 0, bucketSize) nreceived := 0 - rm := t.pending(toid, toaddr.IP, v4wire.NeighborsPacket, func(r v4wire.Packet) (matched bool, requestDone bool) { + rm := t.pending(toid, toAddrPort.Addr(), v4wire.NeighborsPacket, func(r v4wire.Packet) (matched bool, requestDone bool) { reply := r.(*v4wire.Neighbors) for _, rn := range reply.Nodes { nreceived++ - n, err := t.nodeFromRPC(toaddr, rn) + n, err := t.nodeFromRPC(toAddrPort, rn) if err != nil { - t.log.Trace("Invalid neighbor node received", "ip", rn.IP, "addr", toaddr, "err", err) + t.log.Trace("Invalid neighbor node received", "ip", rn.IP, "addr", toAddrPort, "err", err) continue } nodes = append(nodes, n) } return true, nreceived >= bucketSize }) - t.send(toaddr, toid, &v4wire.Findnode{ + t.send(toAddrPort, toid, &v4wire.Findnode{ Target: target, Expiration: uint64(time.Now().Add(expiration).Unix()), }) @@ -337,7 +349,7 @@ func (t *UDPv4) findnode(toid enode.ID, toaddr *net.UDPAddr, target v4wire.Pubke // RequestENR sends ENRRequest to the given node and waits for a response. func (t *UDPv4) RequestENR(n *enode.Node) (*enode.Node, error) { - addr := &net.UDPAddr{IP: n.IP(), Port: n.UDP()} + addr, _ := n.UDPEndpoint() t.ensureBond(n.ID(), addr) req := &v4wire.ENRRequest{ @@ -350,7 +362,7 @@ func (t *UDPv4) RequestENR(n *enode.Node) (*enode.Node, error) { // Add a matcher for the reply to the pending reply queue. Responses are matched if // they reference the request we're about to send. - rm := t.pending(n.ID(), addr.IP, v4wire.ENRResponsePacket, func(r v4wire.Packet) (matched bool, requestDone bool) { + rm := t.pending(n.ID(), addr.Addr(), v4wire.ENRResponsePacket, func(r v4wire.Packet) (matched bool, requestDone bool) { matched = bytes.Equal(r.(*v4wire.ENRResponse).ReplyTok, hash) return matched, matched }) @@ -370,15 +382,19 @@ func (t *UDPv4) RequestENR(n *enode.Node) (*enode.Node, error) { if respN.Seq() < n.Seq() { return n, nil // response record is older } - if err := netutil.CheckRelayIP(addr.IP, respN.IP()); err != nil { + if err := netutil.CheckRelayAddr(addr.Addr(), respN.IPAddr()); err != nil { return nil, fmt.Errorf("invalid IP in response record: %v", err) } return respN, nil } +func (t *UDPv4) TableBuckets() [][]BucketNode { + return t.tab.Nodes() +} + // pending adds a reply matcher to the pending reply queue. // see the documentation of type replyMatcher for a detailed explanation. -func (t *UDPv4) pending(id enode.ID, ip net.IP, ptype byte, callback replyMatchFunc) *replyMatcher { +func (t *UDPv4) pending(id enode.ID, ip netip.Addr, ptype byte, callback replyMatchFunc) *replyMatcher { ch := make(chan error, 1) p := &replyMatcher{from: id, ip: ip, ptype: ptype, callback: callback, errc: ch} select { @@ -392,7 +408,7 @@ func (t *UDPv4) pending(id enode.ID, ip net.IP, ptype byte, callback replyMatchF // handleReply dispatches a reply packet, invoking reply matchers. It returns // whether any matcher considered the packet acceptable. -func (t *UDPv4) handleReply(from enode.ID, fromIP net.IP, req v4wire.Packet) bool { +func (t *UDPv4) handleReply(from enode.ID, fromIP netip.Addr, req v4wire.Packet) bool { matched := make(chan bool, 1) select { case t.gotreply <- reply{from, fromIP, req, matched}: @@ -465,7 +481,7 @@ func (t *UDPv4) loop(isBootNode bool) { var matched bool // whether any replyMatcher considered the reply acceptable. for el := plist.Front(); el != nil; el = el.Next() { p := el.Value.(*replyMatcher) - if p.from == r.from && p.ptype == r.data.Kind() && p.ip.Equal(r.ip) { + if p.from == r.from && p.ptype == r.data.Kind() && p.ip == r.ip { ok, requestDone := p.callback(r.data) matched = matched || ok p.reply = r.data @@ -509,7 +525,7 @@ func (t *UDPv4) loop(isBootNode bool) { } } -func (t *UDPv4) send(toaddr *net.UDPAddr, toid enode.ID, req v4wire.Packet) ([]byte, error) { +func (t *UDPv4) send(toaddr netip.AddrPort, toid enode.ID, req v4wire.Packet) ([]byte, error) { packet, hash, err := v4wire.Encode(t.priv, req) if err != nil { return hash, err @@ -517,8 +533,8 @@ func (t *UDPv4) send(toaddr *net.UDPAddr, toid enode.ID, req v4wire.Packet) ([]b return hash, t.write(toaddr, toid, req.Name(), packet) } -func (t *UDPv4) write(toaddr *net.UDPAddr, toid enode.ID, what string, packet []byte) error { - _, err := t.conn.WriteToUDP(packet, toaddr) +func (t *UDPv4) write(toaddr netip.AddrPort, toid enode.ID, what string, packet []byte) error { + _, err := t.conn.WriteToUDPAddrPort(packet, toaddr) t.log.Trace(">> "+what, "id", toid, "addr", toaddr, "err", err) return err } @@ -532,7 +548,7 @@ func (t *UDPv4) readLoop(unhandled chan<- ReadPacket) { buf := make([]byte, maxPacketSize) for { - nbytes, from, err := t.conn.ReadFromUDP(buf) + nbytes, from, err := t.conn.ReadFromUDPAddrPort(buf) if netutil.IsTemporaryError(err) { // Ignore temporary read errors. t.log.Debug("Temporary UDP read error", "err", err) @@ -553,7 +569,12 @@ func (t *UDPv4) readLoop(unhandled chan<- ReadPacket) { } } -func (t *UDPv4) handlePacket(from *net.UDPAddr, buf []byte) error { +func (t *UDPv4) handlePacket(from netip.AddrPort, buf []byte) error { + // Unwrap IPv4-in-6 source address. + if from.Addr().Is4In6() { + from = netip.AddrPortFrom(netip.AddrFrom4(from.Addr().As4()), from.Port()) + } + rawpacket, fromKey, hash, err := v4wire.Decode(buf) if err != nil { t.log.Debug("Bad discv4 packet", "addr", from, "err", err) @@ -561,7 +582,7 @@ func (t *UDPv4) handlePacket(from *net.UDPAddr, buf []byte) error { } packet := t.wrapPacket(rawpacket) fromID := fromKey.ID() - if err == nil && packet.preverify != nil { + if packet.preverify != nil { err = packet.preverify(packet, from, fromID, fromKey) } t.log.Trace("<< "+packet.Name(), "id", fromID, "addr", from, "err", err) @@ -572,15 +593,15 @@ func (t *UDPv4) handlePacket(from *net.UDPAddr, buf []byte) error { } // checkBond checks if the given node has a recent enough endpoint proof. -func (t *UDPv4) checkBond(id enode.ID, ip net.IP) bool { - return time.Since(t.db.LastPongReceived(id, ip)) < bondExpiration +func (t *UDPv4) checkBond(id enode.ID, ip netip.AddrPort) bool { + return time.Since(t.db.LastPongReceived(id, ip.Addr())) < bondExpiration } // ensureBond solicits a ping from a node if we haven't seen a ping from it for a while. // This ensures there is a valid endpoint proof on the remote end. -func (t *UDPv4) ensureBond(toid enode.ID, toaddr *net.UDPAddr) { - tooOld := time.Since(t.db.LastPingReceived(toid, toaddr.IP)) > bondExpiration - if tooOld || t.db.FindFails(toid, toaddr.IP) > maxFindnodeFailures { +func (t *UDPv4) ensureBond(toid enode.ID, toaddr netip.AddrPort) { + tooOld := time.Since(t.db.LastPingReceived(toid, toaddr.Addr())) > bondExpiration + if tooOld || t.db.FindFails(toid, toaddr.Addr()) > maxFindnodeFailures { rm := t.sendPing(toid, toaddr, nil) <-rm.errc // Wait for them to ping back and process our pong. @@ -588,11 +609,11 @@ func (t *UDPv4) ensureBond(toid enode.ID, toaddr *net.UDPAddr) { } } -func (t *UDPv4) nodeFromRPC(sender *net.UDPAddr, rn v4wire.Node) (*node, error) { +func (t *UDPv4) nodeFromRPC(sender netip.AddrPort, rn v4wire.Node) (*enode.Node, error) { if rn.UDP <= 1024 { return nil, errLowPort } - if err := netutil.CheckRelayIP(sender.IP, rn.IP); err != nil { + if err := netutil.CheckRelayIP(sender.Addr().AsSlice(), rn.IP); err != nil { return nil, err } if t.netrestrict != nil && !t.netrestrict.Contains(rn.IP) { @@ -602,12 +623,12 @@ func (t *UDPv4) nodeFromRPC(sender *net.UDPAddr, rn v4wire.Node) (*node, error) if err != nil { return nil, err } - n := wrapNode(enode.NewV4(key, rn.IP, int(rn.TCP), int(rn.UDP))) + n := enode.NewV4(key, rn.IP, int(rn.TCP), int(rn.UDP)) err = n.ValidateComplete() return n, err } -func nodeToRPC(n *node) v4wire.Node { +func nodeToRPC(n *enode.Node) v4wire.Node { var key ecdsa.PublicKey var ekey v4wire.Pubkey if err := n.Load((*enode.Secp256k1)(&key)); err == nil { @@ -646,14 +667,14 @@ type packetHandlerV4 struct { senderKey *ecdsa.PublicKey // used for ping // preverify checks whether the packet is valid and should be handled at all. - preverify func(p *packetHandlerV4, from *net.UDPAddr, fromID enode.ID, fromKey v4wire.Pubkey) error + preverify func(p *packetHandlerV4, from netip.AddrPort, fromID enode.ID, fromKey v4wire.Pubkey) error // handle handles the packet. - handle func(req *packetHandlerV4, from *net.UDPAddr, fromID enode.ID, mac []byte) + handle func(req *packetHandlerV4, from netip.AddrPort, fromID enode.ID, mac []byte) } // PING/v4 -func (t *UDPv4) verifyPing(h *packetHandlerV4, from *net.UDPAddr, fromID enode.ID, fromKey v4wire.Pubkey) error { +func (t *UDPv4) verifyPing(h *packetHandlerV4, from netip.AddrPort, fromID enode.ID, fromKey v4wire.Pubkey) error { req := h.Packet.(*v4wire.Ping) if v4wire.Expired(req.Expiration) { @@ -667,7 +688,7 @@ func (t *UDPv4) verifyPing(h *packetHandlerV4, from *net.UDPAddr, fromID enode.I return nil } -func (t *UDPv4) handlePing(h *packetHandlerV4, from *net.UDPAddr, fromID enode.ID, mac []byte) { +func (t *UDPv4) handlePing(h *packetHandlerV4, from netip.AddrPort, fromID enode.ID, mac []byte) { req := h.Packet.(*v4wire.Ping) // Reply. @@ -679,45 +700,48 @@ func (t *UDPv4) handlePing(h *packetHandlerV4, from *net.UDPAddr, fromID enode.I }) // Ping back if our last pong on file is too far in the past. - n := wrapNode(enode.NewV4(h.senderKey, from.IP, int(req.From.TCP), from.Port)) - if time.Since(t.db.LastPongReceived(n.ID(), from.IP)) > bondExpiration { + fromIP := from.Addr().AsSlice() + n := enode.NewV4(h.senderKey, fromIP, int(req.From.TCP), int(from.Port())) + if time.Since(t.db.LastPongReceived(n.ID(), from.Addr())) > bondExpiration { t.sendPing(fromID, from, func() { - t.tab.addVerifiedNode(n) + t.tab.addInboundNode(n) }) } else { - t.tab.addVerifiedNode(n) + t.tab.addInboundNode(n) } // Update node database and endpoint predictor. - t.db.UpdateLastPingReceived(n.ID(), from.IP, time.Now()) - t.localNode.UDPEndpointStatement(from, &net.UDPAddr{IP: req.To.IP, Port: int(req.To.UDP)}) + t.db.UpdateLastPingReceived(n.ID(), from.Addr(), time.Now()) + toaddr := netip.AddrPortFrom(netutil.IPToAddr(req.To.IP), req.To.UDP) + t.localNode.UDPEndpointStatement(from, toaddr) } // PONG/v4 -func (t *UDPv4) verifyPong(h *packetHandlerV4, from *net.UDPAddr, fromID enode.ID, fromKey v4wire.Pubkey) error { +func (t *UDPv4) verifyPong(h *packetHandlerV4, from netip.AddrPort, fromID enode.ID, fromKey v4wire.Pubkey) error { req := h.Packet.(*v4wire.Pong) if v4wire.Expired(req.Expiration) { return errExpired } - if !t.handleReply(fromID, from.IP, req) { + if !t.handleReply(fromID, from.Addr(), req) { return errUnsolicitedReply } - t.localNode.UDPEndpointStatement(from, &net.UDPAddr{IP: req.To.IP, Port: int(req.To.UDP)}) - t.db.UpdateLastPongReceived(fromID, from.IP, time.Now()) + toaddr := netip.AddrPortFrom(netutil.IPToAddr(req.To.IP), req.To.UDP) + t.localNode.UDPEndpointStatement(from, toaddr) + t.db.UpdateLastPongReceived(fromID, from.Addr(), time.Now()) return nil } // FINDNODE/v4 -func (t *UDPv4) verifyFindnode(h *packetHandlerV4, from *net.UDPAddr, fromID enode.ID, fromKey v4wire.Pubkey) error { +func (t *UDPv4) verifyFindnode(h *packetHandlerV4, from netip.AddrPort, fromID enode.ID, fromKey v4wire.Pubkey) error { req := h.Packet.(*v4wire.Findnode) if v4wire.Expired(req.Expiration) { return errExpired } - if !t.checkBond(fromID, from.IP) { + if !t.checkBond(fromID, from) { // No endpoint proof pong exists, we don't process the packet. This prevents an // attack vector where the discovery protocol could be used to amplify traffic in a // DDOS attack. A malicious actor would send a findnode request with the IP address @@ -729,7 +753,7 @@ func (t *UDPv4) verifyFindnode(h *packetHandlerV4, from *net.UDPAddr, fromID eno return nil } -func (t *UDPv4) handleFindnode(h *packetHandlerV4, from *net.UDPAddr, fromID enode.ID, mac []byte) { +func (t *UDPv4) handleFindnode(h *packetHandlerV4, from netip.AddrPort, fromID enode.ID, mac []byte) { req := h.Packet.(*v4wire.Findnode) // Determine closest nodes. @@ -741,7 +765,7 @@ func (t *UDPv4) handleFindnode(h *packetHandlerV4, from *net.UDPAddr, fromID eno p := v4wire.Neighbors{Expiration: uint64(time.Now().Add(expiration).Unix())} var sent bool for _, n := range closest { - if netutil.CheckRelayIP(from.IP, n.IP()) == nil { + if netutil.CheckRelayAddr(from.Addr(), n.IPAddr()) == nil { p.Nodes = append(p.Nodes, nodeToRPC(n)) } if len(p.Nodes) == v4wire.MaxNeighbors { @@ -757,13 +781,13 @@ func (t *UDPv4) handleFindnode(h *packetHandlerV4, from *net.UDPAddr, fromID eno // NEIGHBORS/v4 -func (t *UDPv4) verifyNeighbors(h *packetHandlerV4, from *net.UDPAddr, fromID enode.ID, fromKey v4wire.Pubkey) error { +func (t *UDPv4) verifyNeighbors(h *packetHandlerV4, from netip.AddrPort, fromID enode.ID, fromKey v4wire.Pubkey) error { req := h.Packet.(*v4wire.Neighbors) if v4wire.Expired(req.Expiration) { return errExpired } - if !t.handleReply(fromID, from.IP, h.Packet) { + if !t.handleReply(fromID, from.Addr(), h.Packet) { return errUnsolicitedReply } return nil @@ -771,19 +795,19 @@ func (t *UDPv4) verifyNeighbors(h *packetHandlerV4, from *net.UDPAddr, fromID en // ENRREQUEST/v4 -func (t *UDPv4) verifyENRRequest(h *packetHandlerV4, from *net.UDPAddr, fromID enode.ID, fromKey v4wire.Pubkey) error { +func (t *UDPv4) verifyENRRequest(h *packetHandlerV4, from netip.AddrPort, fromID enode.ID, fromKey v4wire.Pubkey) error { req := h.Packet.(*v4wire.ENRRequest) if v4wire.Expired(req.Expiration) { return errExpired } - if !t.checkBond(fromID, from.IP) { + if !t.checkBond(fromID, from) { return errUnknownNode } return nil } -func (t *UDPv4) handleENRRequest(h *packetHandlerV4, from *net.UDPAddr, fromID enode.ID, mac []byte) { +func (t *UDPv4) handleENRRequest(h *packetHandlerV4, from netip.AddrPort, fromID enode.ID, mac []byte) { t.send(from, fromID, &v4wire.ENRResponse{ ReplyTok: mac, Record: *t.localNode.Node().Record(), @@ -792,8 +816,8 @@ func (t *UDPv4) handleENRRequest(h *packetHandlerV4, from *net.UDPAddr, fromID e // ENRRESPONSE/v4 -func (t *UDPv4) verifyENRResponse(h *packetHandlerV4, from *net.UDPAddr, fromID enode.ID, fromKey v4wire.Pubkey) error { - if !t.handleReply(fromID, from.IP, h.Packet) { +func (t *UDPv4) verifyENRResponse(h *packetHandlerV4, from netip.AddrPort, fromID enode.ID, fromKey v4wire.Pubkey) error { + if !t.handleReply(fromID, from.Addr(), h.Packet) { return errUnsolicitedReply } return nil diff --git a/p2p/discover/v4_udp_test.go b/p2p/discover/v4_udp_test.go index 9b80214f75..9d6df08ead 100644 --- a/p2p/discover/v4_udp_test.go +++ b/p2p/discover/v4_udp_test.go @@ -26,6 +26,7 @@ import ( "io" "math/rand" "net" + "net/netip" "reflect" "sync" "testing" @@ -55,7 +56,7 @@ type udpTest struct { udp *UDPv4 sent [][]byte localkey, remotekey *ecdsa.PrivateKey - remoteaddr *net.UDPAddr + remoteaddr netip.AddrPort } func newUDPTest(t *testing.T) *udpTest { @@ -64,7 +65,7 @@ func newUDPTest(t *testing.T) *udpTest { pipe: newpipe(), localkey: newkey(), remotekey: newkey(), - remoteaddr: &net.UDPAddr{IP: net.IP{10, 0, 1, 99}, Port: 30303}, + remoteaddr: netip.MustParseAddrPort("10.0.1.99:30303"), } test.db, _ = enode.OpenDB("") @@ -92,7 +93,7 @@ func (test *udpTest) packetIn(wantError error, data v4wire.Packet) { } // handles a packet as if it had been sent to the transport by the key/endpoint. -func (test *udpTest) packetInFrom(wantError error, key *ecdsa.PrivateKey, addr *net.UDPAddr, data v4wire.Packet) { +func (test *udpTest) packetInFrom(wantError error, key *ecdsa.PrivateKey, addr netip.AddrPort, data v4wire.Packet) { test.t.Helper() enc, _, err := v4wire.Encode(key, data) @@ -106,7 +107,7 @@ func (test *udpTest) packetInFrom(wantError error, key *ecdsa.PrivateKey, addr * } // waits for a packet to be sent by the transport. -// validate should have type func(X, *net.UDPAddr, []byte), where X is a packet type. +// validate should have type func(X, netip.AddrPort, []byte), where X is a packet type. func (test *udpTest) waitPacketOut(validate interface{}) (closed bool) { test.t.Helper() @@ -128,7 +129,7 @@ func (test *udpTest) waitPacketOut(validate interface{}) (closed bool) { test.t.Errorf("sent packet type mismatch, got: %v, want: %v", reflect.TypeOf(p), exptype) return false } - fn.Call([]reflect.Value{reflect.ValueOf(p), reflect.ValueOf(&dgram.to), reflect.ValueOf(hash)}) + fn.Call([]reflect.Value{reflect.ValueOf(p), reflect.ValueOf(dgram.to), reflect.ValueOf(hash)}) return false } @@ -236,7 +237,7 @@ func TestUDPv4_findnodeTimeout(t *testing.T) { test := newUDPTest(t) defer test.close() - toaddr := &net.UDPAddr{IP: net.ParseIP("1.2.3.4"), Port: 2222} + toaddr := netip.AddrPortFrom(netip.MustParseAddr("1.2.3.4"), 2222) toid := enode.ID{1, 2, 3, 4} target := v4wire.Pubkey{4, 5, 6, 7} result, err := test.udp.findnode(toid, toaddr, target) @@ -261,26 +262,25 @@ func TestUDPv4_findnode(t *testing.T) { for i := 0; i < numCandidates; i++ { key := newkey() ip := net.IP{10, 13, 0, byte(i)} - n := wrapNode(enode.NewV4(&key.PublicKey, ip, 0, 2000)) + n := enode.NewV4(&key.PublicKey, ip, 0, 2000) // Ensure half of table content isn't verified live yet. if i > numCandidates/2 { - n.livenessChecks = 1 live[n.ID()] = true } + test.table.addFoundNode(n, live[n.ID()]) nodes.push(n, numCandidates) } - fillTable(test.table, nodes.entries, false) // ensure there's a bond with the test node, // findnode won't be accepted otherwise. remoteID := v4wire.EncodePubkey(&test.remotekey.PublicKey).ID() - test.table.db.UpdateLastPongReceived(remoteID, test.remoteaddr.IP, time.Now()) + test.table.db.UpdateLastPongReceived(remoteID, test.remoteaddr.Addr(), time.Now()) // check that closest neighbors are returned. expected := test.table.findnodeByID(testTarget.ID(), bucketSize, true) test.packetIn(nil, &v4wire.Findnode{Target: testTarget, Expiration: futureExp}) - waitNeighbors := func(want []*node) { - test.waitPacketOut(func(p *v4wire.Neighbors, to *net.UDPAddr, hash []byte) { + waitNeighbors := func(want []*enode.Node) { + test.waitPacketOut(func(p *v4wire.Neighbors, to netip.AddrPort, hash []byte) { if len(p.Nodes) != len(want) { t.Errorf("wrong number of results: got %d, want %d", len(p.Nodes), len(want)) return @@ -309,10 +309,10 @@ func TestUDPv4_findnodeMultiReply(t *testing.T) { defer test.close() rid := enode.PubkeyToIDV4(&test.remotekey.PublicKey) - test.table.db.UpdateLastPingReceived(rid, test.remoteaddr.IP, time.Now()) + test.table.db.UpdateLastPingReceived(rid, test.remoteaddr.Addr(), time.Now()) // queue a pending findnode request - resultc, errc := make(chan []*node, 1), make(chan error, 1) + resultc, errc := make(chan []*enode.Node, 1), make(chan error, 1) go func() { rid := encodePubkey(&test.remotekey.PublicKey).id() ns, err := test.udp.findnode(rid, test.remoteaddr, testTarget) @@ -325,18 +325,18 @@ func TestUDPv4_findnodeMultiReply(t *testing.T) { // wait for the findnode to be sent. // after it is sent, the transport is waiting for a reply - test.waitPacketOut(func(p *v4wire.Findnode, to *net.UDPAddr, hash []byte) { + test.waitPacketOut(func(p *v4wire.Findnode, to netip.AddrPort, hash []byte) { if p.Target != testTarget { t.Errorf("wrong target: got %v, want %v", p.Target, testTarget) } }) // send the reply as two packets. - list := []*node{ - wrapNode(enode.MustParse("enode://ba85011c70bcc5c04d8607d3a0ed29aa6179c092cbdda10d5d32684fb33ed01bd94f588ca8f91ac48318087dcb02eaf36773a7a453f0eedd6742af668097b29c@10.0.1.16:30303?discport=30304")), - wrapNode(enode.MustParse("enode://81fa361d25f157cd421c60dcc28d8dac5ef6a89476633339c5df30287474520caca09627da18543d9079b5b288698b542d56167aa5c09111e55acdbbdf2ef799@10.0.1.16:30303")), - wrapNode(enode.MustParse("enode://9bffefd833d53fac8e652415f4973bee289e8b1a5c6c4cbe70abf817ce8a64cee11b823b66a987f51aaa9fba0d6a91b3e6bf0d5a5d1042de8e9eeea057b217f8@10.0.1.36:30301?discport=17")), - wrapNode(enode.MustParse("enode://1b5b4aa662d7cb44a7221bfba67302590b643028197a7d5214790f3bac7aaa4a3241be9e83c09cf1f6c69d007c634faae3dc1b1221793e8446c0b3a09de65960@10.0.1.16:30303")), + list := []*enode.Node{ + enode.MustParse("enode://ba85011c70bcc5c04d8607d3a0ed29aa6179c092cbdda10d5d32684fb33ed01bd94f588ca8f91ac48318087dcb02eaf36773a7a453f0eedd6742af668097b29c@10.0.1.16:30303?discport=30304"), + enode.MustParse("enode://81fa361d25f157cd421c60dcc28d8dac5ef6a89476633339c5df30287474520caca09627da18543d9079b5b288698b542d56167aa5c09111e55acdbbdf2ef799@10.0.1.16:30303"), + enode.MustParse("enode://9bffefd833d53fac8e652415f4973bee289e8b1a5c6c4cbe70abf817ce8a64cee11b823b66a987f51aaa9fba0d6a91b3e6bf0d5a5d1042de8e9eeea057b217f8@10.0.1.36:30301?discport=17"), + enode.MustParse("enode://1b5b4aa662d7cb44a7221bfba67302590b643028197a7d5214790f3bac7aaa4a3241be9e83c09cf1f6c69d007c634faae3dc1b1221793e8446c0b3a09de65960@10.0.1.16:30303"), } rpclist := make([]v4wire.Node, len(list)) for i := range list { @@ -368,8 +368,8 @@ func TestUDPv4_pingMatch(t *testing.T) { crand.Read(randToken) test.packetIn(nil, &v4wire.Ping{From: testRemote, To: testLocalAnnounced, Version: 4, Expiration: futureExp}) - test.waitPacketOut(func(*v4wire.Pong, *net.UDPAddr, []byte) {}) - test.waitPacketOut(func(*v4wire.Ping, *net.UDPAddr, []byte) {}) + test.waitPacketOut(func(*v4wire.Pong, netip.AddrPort, []byte) {}) + test.waitPacketOut(func(*v4wire.Ping, netip.AddrPort, []byte) {}) test.packetIn(errUnsolicitedReply, &v4wire.Pong{ReplyTok: randToken, To: testLocalAnnounced, Expiration: futureExp}) } @@ -379,10 +379,10 @@ func TestUDPv4_pingMatchIP(t *testing.T) { defer test.close() test.packetIn(nil, &v4wire.Ping{From: testRemote, To: testLocalAnnounced, Version: 4, Expiration: futureExp}) - test.waitPacketOut(func(*v4wire.Pong, *net.UDPAddr, []byte) {}) + test.waitPacketOut(func(*v4wire.Pong, netip.AddrPort, []byte) {}) - test.waitPacketOut(func(p *v4wire.Ping, to *net.UDPAddr, hash []byte) { - wrongAddr := &net.UDPAddr{IP: net.IP{33, 44, 1, 2}, Port: 30000} + test.waitPacketOut(func(p *v4wire.Ping, to netip.AddrPort, hash []byte) { + wrongAddr := netip.MustParseAddrPort("33.44.1.2:30000") test.packetInFrom(errUnsolicitedReply, test.remotekey, wrongAddr, &v4wire.Pong{ ReplyTok: hash, To: testLocalAnnounced, @@ -393,41 +393,36 @@ func TestUDPv4_pingMatchIP(t *testing.T) { func TestUDPv4_successfulPing(t *testing.T) { test := newUDPTest(t) - added := make(chan *node, 1) - test.table.nodeAddedHook = func(b *bucket, n *node) { added <- n } + added := make(chan *tableNode, 1) + test.table.nodeAddedHook = func(b *bucket, n *tableNode) { added <- n } defer test.close() // The remote side sends a ping packet to initiate the exchange. go test.packetIn(nil, &v4wire.Ping{From: testRemote, To: testLocalAnnounced, Version: 4, Expiration: futureExp}) // The ping is replied to. - test.waitPacketOut(func(p *v4wire.Pong, to *net.UDPAddr, hash []byte) { + test.waitPacketOut(func(p *v4wire.Pong, to netip.AddrPort, hash []byte) { pinghash := test.sent[0][:32] if !bytes.Equal(p.ReplyTok, pinghash) { t.Errorf("got pong.ReplyTok %x, want %x", p.ReplyTok, pinghash) } - wantTo := v4wire.Endpoint{ - // The mirrored UDP address is the UDP packet sender - IP: test.remoteaddr.IP, UDP: uint16(test.remoteaddr.Port), - // The mirrored TCP port is the one from the ping packet - TCP: testRemote.TCP, - } + // The mirrored UDP address is the UDP packet sender. + // The mirrored TCP port is the one from the ping packet. + wantTo := v4wire.NewEndpoint(test.remoteaddr, testRemote.TCP) if !reflect.DeepEqual(p.To, wantTo) { t.Errorf("got pong.To %v, want %v", p.To, wantTo) } }) // Remote is unknown, the table pings back. - test.waitPacketOut(func(p *v4wire.Ping, to *net.UDPAddr, hash []byte) { - if !reflect.DeepEqual(p.From, test.udp.ourEndpoint()) { + test.waitPacketOut(func(p *v4wire.Ping, to netip.AddrPort, hash []byte) { + wantFrom := test.udp.ourEndpoint() + wantFrom.IP = net.IP{} + if !reflect.DeepEqual(p.From, wantFrom) { t.Errorf("got ping.From %#v, want %#v", p.From, test.udp.ourEndpoint()) } - wantTo := v4wire.Endpoint{ - // The mirrored UDP address is the UDP packet sender. - IP: test.remoteaddr.IP, - UDP: uint16(test.remoteaddr.Port), - TCP: 0, - } + // The mirrored UDP address is the UDP packet sender. + wantTo := v4wire.NewEndpoint(test.remoteaddr, 0) if !reflect.DeepEqual(p.To, wantTo) { t.Errorf("got ping.To %v, want %v", p.To, wantTo) } @@ -442,11 +437,11 @@ func TestUDPv4_successfulPing(t *testing.T) { if n.ID() != rid { t.Errorf("node has wrong ID: got %v, want %v", n.ID(), rid) } - if !n.IP().Equal(test.remoteaddr.IP) { - t.Errorf("node has wrong IP: got %v, want: %v", n.IP(), test.remoteaddr.IP) + if n.IPAddr() != test.remoteaddr.Addr() { + t.Errorf("node has wrong IP: got %v, want: %v", n.IPAddr(), test.remoteaddr.Addr()) } - if n.UDP() != test.remoteaddr.Port { - t.Errorf("node has wrong UDP port: got %v, want: %v", n.UDP(), test.remoteaddr.Port) + if n.UDP() != int(test.remoteaddr.Port()) { + t.Errorf("node has wrong UDP port: got %v, want: %v", n.UDP(), test.remoteaddr.Port()) } if n.TCP() != int(testRemote.TCP) { t.Errorf("node has wrong TCP port: got %v, want: %v", n.TCP(), testRemote.TCP) @@ -469,12 +464,12 @@ func TestUDPv4_EIP868(t *testing.T) { // Perform endpoint proof and check for sequence number in packet tail. test.packetIn(nil, &v4wire.Ping{Expiration: futureExp}) - test.waitPacketOut(func(p *v4wire.Pong, addr *net.UDPAddr, hash []byte) { + test.waitPacketOut(func(p *v4wire.Pong, addr netip.AddrPort, hash []byte) { if p.ENRSeq != wantNode.Seq() { t.Errorf("wrong sequence number in pong: %d, want %d", p.ENRSeq, wantNode.Seq()) } }) - test.waitPacketOut(func(p *v4wire.Ping, addr *net.UDPAddr, hash []byte) { + test.waitPacketOut(func(p *v4wire.Ping, addr netip.AddrPort, hash []byte) { if p.ENRSeq != wantNode.Seq() { t.Errorf("wrong sequence number in ping: %d, want %d", p.ENRSeq, wantNode.Seq()) } @@ -483,7 +478,7 @@ func TestUDPv4_EIP868(t *testing.T) { // Request should work now. test.packetIn(nil, &v4wire.ENRRequest{Expiration: futureExp}) - test.waitPacketOut(func(p *v4wire.ENRResponse, addr *net.UDPAddr, hash []byte) { + test.waitPacketOut(func(p *v4wire.ENRResponse, addr netip.AddrPort, hash []byte) { n, err := enode.New(enode.ValidSchemes, &p.Record) if err != nil { t.Fatalf("invalid record: %v", err) @@ -584,7 +579,7 @@ type dgramPipe struct { } type dgram struct { - to net.UDPAddr + to netip.AddrPort data []byte } @@ -597,8 +592,8 @@ func newpipe() *dgramPipe { } } -// WriteToUDP queues a datagram. -func (c *dgramPipe) WriteToUDP(b []byte, to *net.UDPAddr) (n int, err error) { +// WriteToUDPAddrPort queues a datagram. +func (c *dgramPipe) WriteToUDPAddrPort(b []byte, to netip.AddrPort) (n int, err error) { msg := make([]byte, len(b)) copy(msg, b) c.mu.Lock() @@ -606,15 +601,15 @@ func (c *dgramPipe) WriteToUDP(b []byte, to *net.UDPAddr) (n int, err error) { if c.closed { return 0, errors.New("closed") } - c.queue = append(c.queue, dgram{*to, b}) + c.queue = append(c.queue, dgram{to, b}) c.cond.Signal() return len(b), nil } -// ReadFromUDP just hangs until the pipe is closed. -func (c *dgramPipe) ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error) { +// ReadFromUDPAddrPort just hangs until the pipe is closed. +func (c *dgramPipe) ReadFromUDPAddrPort(b []byte) (n int, addr netip.AddrPort, err error) { <-c.closing - return 0, nil, io.EOF + return 0, netip.AddrPort{}, io.EOF } func (c *dgramPipe) Close() error { diff --git a/p2p/discover/v4wire/v4wire.go b/p2p/discover/v4wire/v4wire.go index 9c59359fb2..958cca324d 100644 --- a/p2p/discover/v4wire/v4wire.go +++ b/p2p/discover/v4wire/v4wire.go @@ -25,6 +25,7 @@ import ( "fmt" "math/big" "net" + "net/netip" "time" "github.com/ethereum/go-ethereum/common/math" @@ -150,14 +151,15 @@ type Endpoint struct { } // NewEndpoint creates an endpoint. -func NewEndpoint(addr *net.UDPAddr, tcpPort uint16) Endpoint { - ip := net.IP{} - if ip4 := addr.IP.To4(); ip4 != nil { - ip = ip4 - } else if ip6 := addr.IP.To16(); ip6 != nil { - ip = ip6 +func NewEndpoint(addr netip.AddrPort, tcpPort uint16) Endpoint { + var ip net.IP + if addr.Addr().Is4() || addr.Addr().Is4In6() { + ip4 := addr.Addr().As4() + ip = ip4[:] + } else { + ip = addr.Addr().AsSlice() } - return Endpoint{IP: ip, UDP: uint16(addr.Port), TCP: tcpPort} + return Endpoint{IP: ip, UDP: addr.Port(), TCP: tcpPort} } type Packet interface { diff --git a/p2p/discover/v5_talk.go b/p2p/discover/v5_talk.go index 5d396f689e..feca3105ac 100644 --- a/p2p/discover/v5_talk.go +++ b/p2p/discover/v5_talk.go @@ -18,6 +18,7 @@ package discover import ( "net" + "net/netip" "sync" "time" @@ -70,7 +71,7 @@ func (t *talkSystem) register(protocol string, handler TalkRequestHandler) { } // handleRequest handles a talk request. -func (t *talkSystem) handleRequest(id enode.ID, addr *net.UDPAddr, req *v5wire.TalkRequest) { +func (t *talkSystem) handleRequest(id enode.ID, addr netip.AddrPort, req *v5wire.TalkRequest) { t.mutex.Lock() handler, ok := t.handlers[req.Protocol] t.mutex.Unlock() @@ -88,7 +89,8 @@ func (t *talkSystem) handleRequest(id enode.ID, addr *net.UDPAddr, req *v5wire.T case <-t.slots: go func() { defer func() { t.slots <- struct{}{} }() - respMessage := handler(id, addr, req.Message) + udpAddr := &net.UDPAddr{IP: addr.Addr().AsSlice(), Port: int(addr.Port())} + respMessage := handler(id, udpAddr, req.Message) resp := &v5wire.TalkResponse{ReqID: req.ReqID, Message: respMessage} t.transport.sendFromAnotherThread(id, addr, resp) }() diff --git a/p2p/discover/v5_udp.go b/p2p/discover/v5_udp.go index 20a8bccd05..81d94812aa 100644 --- a/p2p/discover/v5_udp.go +++ b/p2p/discover/v5_udp.go @@ -25,6 +25,7 @@ import ( "fmt" "io" "net" + "net/netip" "slices" "sync" "time" @@ -101,14 +102,14 @@ type UDPv5 struct { type sendRequest struct { destID enode.ID - destAddr *net.UDPAddr + destAddr netip.AddrPort msg v5wire.Packet } // callV5 represents a remote procedure call against another node. type callV5 struct { id enode.ID - addr *net.UDPAddr + addr netip.AddrPort node *enode.Node // This is required to perform handshakes. packet v5wire.Packet @@ -175,7 +176,7 @@ func newUDPv5(conn UDPConn, ln *enode.LocalNode, cfg Config) (*UDPv5, error) { cancelCloseCtx: cancelCloseCtx, } t.talk = newTalkSystem(t) - tab, err := newMeteredTable(t, t.db, cfg) + tab, err := newTable(t, t.db, cfg) if err != nil { return nil, err } @@ -233,7 +234,7 @@ func (t *UDPv5) AllNodes() []*enode.Node { for _, b := range &t.tab.buckets { for _, n := range b.entries { - nodes = append(nodes, unwrapNode(n)) + nodes = append(nodes, n.Node) } } return nodes @@ -266,7 +267,7 @@ func (t *UDPv5) TalkRequest(n *enode.Node, protocol string, request []byte) ([]b } // TalkRequestToID sends a talk request to a node and waits for a response. -func (t *UDPv5) TalkRequestToID(id enode.ID, addr *net.UDPAddr, protocol string, request []byte) ([]byte, error) { +func (t *UDPv5) TalkRequestToID(id enode.ID, addr netip.AddrPort, protocol string, request []byte) ([]byte, error) { req := &v5wire.TalkRequest{Protocol: protocol, Message: request} resp := t.callToID(id, addr, v5wire.TalkResponseMsg, req) defer t.callDone(resp) @@ -314,26 +315,26 @@ func (t *UDPv5) newRandomLookup(ctx context.Context) *lookup { } func (t *UDPv5) newLookup(ctx context.Context, target enode.ID) *lookup { - return newLookup(ctx, t.tab, target, func(n *node) ([]*node, error) { + return newLookup(ctx, t.tab, target, func(n *enode.Node) ([]*enode.Node, error) { return t.lookupWorker(n, target) }) } // lookupWorker performs FINDNODE calls against a single node during lookup. -func (t *UDPv5) lookupWorker(destNode *node, target enode.ID) ([]*node, error) { +func (t *UDPv5) lookupWorker(destNode *enode.Node, target enode.ID) ([]*enode.Node, error) { var ( dists = lookupDistances(target, destNode.ID()) nodes = nodesByDistance{target: target} err error ) var r []*enode.Node - r, err = t.findnode(unwrapNode(destNode), dists) + r, err = t.findnode(destNode, dists) if errors.Is(err, errClosed) { return nil, err } for _, n := range r { if n.ID() != t.Self().ID() { - nodes.push(wrapNode(n), findnodeResultLimit) + nodes.push(n, findnodeResultLimit) } } return nodes.entries, err @@ -427,10 +428,10 @@ func (t *UDPv5) verifyResponseNode(c *callV5, r *enr.Record, distances []uint, s if err != nil { return nil, err } - if err := netutil.CheckRelayIP(c.addr.IP, node.IP()); err != nil { + if err := netutil.CheckRelayAddr(c.addr.Addr(), node.IPAddr()); err != nil { return nil, err } - if t.netrestrict != nil && !t.netrestrict.Contains(node.IP()) { + if t.netrestrict != nil && !t.netrestrict.ContainsAddr(node.IPAddr()) { return nil, errors.New("not contained in netrestrict list") } if node.UDP() <= 1024 { @@ -452,14 +453,14 @@ func (t *UDPv5) verifyResponseNode(c *callV5, r *enr.Record, distances []uint, s // callToNode sends the given call and sets up a handler for response packets (of message // type responseType). Responses are dispatched to the call's response channel. func (t *UDPv5) callToNode(n *enode.Node, responseType byte, req v5wire.Packet) *callV5 { - addr := &net.UDPAddr{IP: n.IP(), Port: n.UDP()} + addr, _ := n.UDPEndpoint() c := &callV5{id: n.ID(), addr: addr, node: n} t.initCall(c, responseType, req) return c } // callToID is like callToNode, but for cases where the node record is not available. -func (t *UDPv5) callToID(id enode.ID, addr *net.UDPAddr, responseType byte, req v5wire.Packet) *callV5 { +func (t *UDPv5) callToID(id enode.ID, addr netip.AddrPort, responseType byte, req v5wire.Packet) *callV5 { c := &callV5{id: id, addr: addr} t.initCall(c, responseType, req) return c @@ -619,12 +620,12 @@ func (t *UDPv5) sendCall(c *callV5) { // sendResponse sends a response packet to the given node. // This doesn't trigger a handshake even if no keys are available. -func (t *UDPv5) sendResponse(toID enode.ID, toAddr *net.UDPAddr, packet v5wire.Packet) error { +func (t *UDPv5) sendResponse(toID enode.ID, toAddr netip.AddrPort, packet v5wire.Packet) error { _, err := t.send(toID, toAddr, packet, nil) return err } -func (t *UDPv5) sendFromAnotherThread(toID enode.ID, toAddr *net.UDPAddr, packet v5wire.Packet) { +func (t *UDPv5) sendFromAnotherThread(toID enode.ID, toAddr netip.AddrPort, packet v5wire.Packet) { select { case t.sendCh <- sendRequest{toID, toAddr, packet}: case <-t.closeCtx.Done(): @@ -632,7 +633,7 @@ func (t *UDPv5) sendFromAnotherThread(toID enode.ID, toAddr *net.UDPAddr, packet } // send sends a packet to the given node. -func (t *UDPv5) send(toID enode.ID, toAddr *net.UDPAddr, packet v5wire.Packet, c *v5wire.Whoareyou) (v5wire.Nonce, error) { +func (t *UDPv5) send(toID enode.ID, toAddr netip.AddrPort, packet v5wire.Packet, c *v5wire.Whoareyou) (v5wire.Nonce, error) { addr := toAddr.String() t.logcontext = append(t.logcontext[:0], "id", toID, "addr", addr) t.logcontext = packet.AppendLogInfo(t.logcontext) @@ -644,7 +645,7 @@ func (t *UDPv5) send(toID enode.ID, toAddr *net.UDPAddr, packet v5wire.Packet, c return nonce, err } - _, err = t.conn.WriteToUDP(enc, toAddr) + _, err = t.conn.WriteToUDPAddrPort(enc, toAddr) t.log.Trace(">> "+packet.Name(), t.logcontext...) return nonce, err } @@ -655,7 +656,7 @@ func (t *UDPv5) readLoop() { buf := make([]byte, maxPacketSize) for range t.readNextCh { - nbytes, from, err := t.conn.ReadFromUDP(buf) + nbytes, from, err := t.conn.ReadFromUDPAddrPort(buf) if netutil.IsTemporaryError(err) { // Ignore temporary read errors. t.log.Debug("Temporary UDP read error", "err", err) @@ -672,7 +673,11 @@ func (t *UDPv5) readLoop() { } // dispatchReadPacket sends a packet into the dispatch loop. -func (t *UDPv5) dispatchReadPacket(from *net.UDPAddr, content []byte) bool { +func (t *UDPv5) dispatchReadPacket(from netip.AddrPort, content []byte) bool { + // Unwrap IPv4-in-6 source address. + if from.Addr().Is4In6() { + from = netip.AddrPortFrom(netip.AddrFrom4(from.Addr().As4()), from.Port()) + } select { case t.packetInCh <- ReadPacket{content, from}: return true @@ -682,7 +687,7 @@ func (t *UDPv5) dispatchReadPacket(from *net.UDPAddr, content []byte) bool { } // handlePacket decodes and processes an incoming packet from the network. -func (t *UDPv5) handlePacket(rawpacket []byte, fromAddr *net.UDPAddr) error { +func (t *UDPv5) handlePacket(rawpacket []byte, fromAddr netip.AddrPort) error { addr := fromAddr.String() fromID, fromNode, packet, err := t.codec.Decode(rawpacket, addr) if err != nil { @@ -699,7 +704,7 @@ func (t *UDPv5) handlePacket(rawpacket []byte, fromAddr *net.UDPAddr) error { } if fromNode != nil { // Handshake succeeded, add to table. - t.tab.addSeenNode(wrapNode(fromNode)) + t.tab.addInboundNode(fromNode) } if packet.Kind() != v5wire.WhoareyouPacket { // WHOAREYOU logged separately to report errors. @@ -712,13 +717,13 @@ func (t *UDPv5) handlePacket(rawpacket []byte, fromAddr *net.UDPAddr) error { } // handleCallResponse dispatches a response packet to the call waiting for it. -func (t *UDPv5) handleCallResponse(fromID enode.ID, fromAddr *net.UDPAddr, p v5wire.Packet) bool { +func (t *UDPv5) handleCallResponse(fromID enode.ID, fromAddr netip.AddrPort, p v5wire.Packet) bool { ac := t.activeCallByNode[fromID] if ac == nil || !bytes.Equal(p.RequestID(), ac.reqid) { t.log.Debug(fmt.Sprintf("Unsolicited/late %s response", p.Name()), "id", fromID, "addr", fromAddr) return false } - if !fromAddr.IP.Equal(ac.addr.IP) || fromAddr.Port != ac.addr.Port { + if fromAddr != ac.addr { t.log.Debug(fmt.Sprintf("%s from wrong endpoint", p.Name()), "id", fromID, "addr", fromAddr) return false } @@ -743,7 +748,7 @@ func (t *UDPv5) getNode(id enode.ID) *enode.Node { } // handle processes incoming packets according to their message type. -func (t *UDPv5) handle(p v5wire.Packet, fromID enode.ID, fromAddr *net.UDPAddr) { +func (t *UDPv5) handle(p v5wire.Packet, fromID enode.ID, fromAddr netip.AddrPort) { switch p := p.(type) { case *v5wire.Unknown: t.handleUnknown(p, fromID, fromAddr) @@ -753,7 +758,8 @@ func (t *UDPv5) handle(p v5wire.Packet, fromID enode.ID, fromAddr *net.UDPAddr) t.handlePing(p, fromID, fromAddr) case *v5wire.Pong: if t.handleCallResponse(fromID, fromAddr, p) { - t.localNode.UDPEndpointStatement(fromAddr, &net.UDPAddr{IP: p.ToIP, Port: int(p.ToPort)}) + toAddr := netip.AddrPortFrom(netutil.IPToAddr(p.ToIP), p.ToPort) + t.localNode.UDPEndpointStatement(fromAddr, toAddr) } case *v5wire.Findnode: t.handleFindnode(p, fromID, fromAddr) @@ -767,7 +773,7 @@ func (t *UDPv5) handle(p v5wire.Packet, fromID enode.ID, fromAddr *net.UDPAddr) } // handleUnknown initiates a handshake by responding with WHOAREYOU. -func (t *UDPv5) handleUnknown(p *v5wire.Unknown, fromID enode.ID, fromAddr *net.UDPAddr) { +func (t *UDPv5) handleUnknown(p *v5wire.Unknown, fromID enode.ID, fromAddr netip.AddrPort) { challenge := &v5wire.Whoareyou{Nonce: p.Nonce} crand.Read(challenge.IDNonce[:]) if n := t.getNode(fromID); n != nil { @@ -783,7 +789,7 @@ var ( ) // handleWhoareyou resends the active call as a handshake packet. -func (t *UDPv5) handleWhoareyou(p *v5wire.Whoareyou, fromID enode.ID, fromAddr *net.UDPAddr) { +func (t *UDPv5) handleWhoareyou(p *v5wire.Whoareyou, fromID enode.ID, fromAddr netip.AddrPort) { c, err := t.matchWithCall(fromID, p.Nonce) if err != nil { t.log.Debug("Invalid "+p.Name(), "addr", fromAddr, "err", err) @@ -817,32 +823,34 @@ func (t *UDPv5) matchWithCall(fromID enode.ID, nonce v5wire.Nonce) (*callV5, err } // handlePing sends a PONG response. -func (t *UDPv5) handlePing(p *v5wire.Ping, fromID enode.ID, fromAddr *net.UDPAddr) { - remoteIP := fromAddr.IP - // Handle IPv4 mapped IPv6 addresses in the - // event the local node is binded to an - // ipv6 interface. - if remoteIP.To4() != nil { - remoteIP = remoteIP.To4() +func (t *UDPv5) handlePing(p *v5wire.Ping, fromID enode.ID, fromAddr netip.AddrPort) { + var remoteIP net.IP + // Handle IPv4 mapped IPv6 addresses in the event the local node is binded + // to an ipv6 interface. + if fromAddr.Addr().Is4() || fromAddr.Addr().Is4In6() { + ip4 := fromAddr.Addr().As4() + remoteIP = ip4[:] + } else { + remoteIP = fromAddr.Addr().AsSlice() } t.sendResponse(fromID, fromAddr, &v5wire.Pong{ ReqID: p.ReqID, ToIP: remoteIP, - ToPort: uint16(fromAddr.Port), + ToPort: fromAddr.Port(), ENRSeq: t.localNode.Node().Seq(), }) } // handleFindnode returns nodes to the requester. -func (t *UDPv5) handleFindnode(p *v5wire.Findnode, fromID enode.ID, fromAddr *net.UDPAddr) { - nodes := t.collectTableNodes(fromAddr.IP, p.Distances, findnodeResultLimit) +func (t *UDPv5) handleFindnode(p *v5wire.Findnode, fromID enode.ID, fromAddr netip.AddrPort) { + nodes := t.collectTableNodes(fromAddr.Addr(), p.Distances, findnodeResultLimit) for _, resp := range packNodes(p.ReqID, nodes) { t.sendResponse(fromID, fromAddr, resp) } } // collectTableNodes creates a FINDNODE result set for the given distances. -func (t *UDPv5) collectTableNodes(rip net.IP, distances []uint, limit int) []*enode.Node { +func (t *UDPv5) collectTableNodes(rip netip.Addr, distances []uint, limit int) []*enode.Node { var bn []*enode.Node var nodes []*enode.Node var processed = make(map[uint]struct{}) @@ -857,7 +865,7 @@ func (t *UDPv5) collectTableNodes(rip net.IP, distances []uint, limit int) []*en for _, n := range t.tab.appendLiveNodes(dist, bn[:0]) { // Apply some pre-checks to avoid sending invalid nodes. // Note liveness is checked by appendLiveNodes. - if netutil.CheckRelayIP(rip, n.IP()) != nil { + if netutil.CheckRelayAddr(rip, n.IPAddr()) != nil { continue } nodes = append(nodes, n) diff --git a/p2p/discover/v5_udp_test.go b/p2p/discover/v5_udp_test.go index 0c13e663ef..25ab64eee7 100644 --- a/p2p/discover/v5_udp_test.go +++ b/p2p/discover/v5_udp_test.go @@ -23,6 +23,7 @@ import ( "fmt" "math/rand" "net" + "net/netip" "reflect" "slices" "testing" @@ -103,7 +104,7 @@ func TestUDPv5_pingHandling(t *testing.T) { defer test.close() test.packetIn(&v5wire.Ping{ReqID: []byte("foo")}) - test.waitPacketOut(func(p *v5wire.Pong, addr *net.UDPAddr, _ v5wire.Nonce) { + test.waitPacketOut(func(p *v5wire.Pong, addr netip.AddrPort, _ v5wire.Nonce) { if !bytes.Equal(p.ReqID, []byte("foo")) { t.Error("wrong request ID in response:", p.ReqID) } @@ -135,16 +136,16 @@ func TestUDPv5_unknownPacket(t *testing.T) { // Unknown packet from unknown node. test.packetIn(&v5wire.Unknown{Nonce: nonce}) - test.waitPacketOut(func(p *v5wire.Whoareyou, addr *net.UDPAddr, _ v5wire.Nonce) { + test.waitPacketOut(func(p *v5wire.Whoareyou, addr netip.AddrPort, _ v5wire.Nonce) { check(p, 0) }) // Make node known. n := test.getNode(test.remotekey, test.remoteaddr).Node() - test.table.addSeenNodeSync(wrapNode(n)) + test.table.addFoundNode(n, false) test.packetIn(&v5wire.Unknown{Nonce: nonce}) - test.waitPacketOut(func(p *v5wire.Whoareyou, addr *net.UDPAddr, _ v5wire.Nonce) { + test.waitPacketOut(func(p *v5wire.Whoareyou, addr netip.AddrPort, _ v5wire.Nonce) { check(p, n.Seq()) }) } @@ -159,9 +160,9 @@ func TestUDPv5_findnodeHandling(t *testing.T) { nodes253 := nodesAtDistance(test.table.self().ID(), 253, 16) nodes249 := nodesAtDistance(test.table.self().ID(), 249, 4) nodes248 := nodesAtDistance(test.table.self().ID(), 248, 10) - fillTable(test.table, wrapNodes(nodes253), true) - fillTable(test.table, wrapNodes(nodes249), true) - fillTable(test.table, wrapNodes(nodes248), true) + fillTable(test.table, nodes253, true) + fillTable(test.table, nodes249, true) + fillTable(test.table, nodes248, true) // Requesting with distance zero should return the node's own record. test.packetIn(&v5wire.Findnode{ReqID: []byte{0}, Distances: []uint{0}}) @@ -199,7 +200,7 @@ func (test *udpV5Test) expectNodes(wantReqID []byte, wantTotal uint8, wantNodes } for { - test.waitPacketOut(func(p *v5wire.Nodes, addr *net.UDPAddr, _ v5wire.Nonce) { + test.waitPacketOut(func(p *v5wire.Nodes, addr netip.AddrPort, _ v5wire.Nonce) { if !bytes.Equal(p.ReqID, wantReqID) { test.t.Fatalf("wrong request ID %v in response, want %v", p.ReqID, wantReqID) } @@ -238,7 +239,7 @@ func TestUDPv5_pingCall(t *testing.T) { _, err := test.udp.ping(remote) done <- err }() - test.waitPacketOut(func(p *v5wire.Ping, addr *net.UDPAddr, _ v5wire.Nonce) {}) + test.waitPacketOut(func(p *v5wire.Ping, addr netip.AddrPort, _ v5wire.Nonce) {}) if err := <-done; err != errTimeout { t.Fatalf("want errTimeout, got %q", err) } @@ -248,7 +249,7 @@ func TestUDPv5_pingCall(t *testing.T) { _, err := test.udp.ping(remote) done <- err }() - test.waitPacketOut(func(p *v5wire.Ping, addr *net.UDPAddr, _ v5wire.Nonce) { + test.waitPacketOut(func(p *v5wire.Ping, addr netip.AddrPort, _ v5wire.Nonce) { test.packetInFrom(test.remotekey, test.remoteaddr, &v5wire.Pong{ReqID: p.ReqID}) }) if err := <-done; err != nil { @@ -260,8 +261,8 @@ func TestUDPv5_pingCall(t *testing.T) { _, err := test.udp.ping(remote) done <- err }() - test.waitPacketOut(func(p *v5wire.Ping, addr *net.UDPAddr, _ v5wire.Nonce) { - wrongAddr := &net.UDPAddr{IP: net.IP{33, 44, 55, 22}, Port: 10101} + test.waitPacketOut(func(p *v5wire.Ping, addr netip.AddrPort, _ v5wire.Nonce) { + wrongAddr := netip.MustParseAddrPort("33.44.55.22:10101") test.packetInFrom(test.remotekey, wrongAddr, &v5wire.Pong{ReqID: p.ReqID}) }) if err := <-done; err != errTimeout { @@ -291,7 +292,7 @@ func TestUDPv5_findnodeCall(t *testing.T) { }() // Serve the responses: - test.waitPacketOut(func(p *v5wire.Findnode, addr *net.UDPAddr, _ v5wire.Nonce) { + test.waitPacketOut(func(p *v5wire.Findnode, addr netip.AddrPort, _ v5wire.Nonce) { if !reflect.DeepEqual(p.Distances, distances) { t.Fatalf("wrong distances in request: %v", p.Distances) } @@ -337,15 +338,15 @@ func TestUDPv5_callResend(t *testing.T) { }() // Ping answered by WHOAREYOU. - test.waitPacketOut(func(p *v5wire.Ping, addr *net.UDPAddr, nonce v5wire.Nonce) { + test.waitPacketOut(func(p *v5wire.Ping, addr netip.AddrPort, nonce v5wire.Nonce) { test.packetIn(&v5wire.Whoareyou{Nonce: nonce}) }) // Ping should be re-sent. - test.waitPacketOut(func(p *v5wire.Ping, addr *net.UDPAddr, _ v5wire.Nonce) { + test.waitPacketOut(func(p *v5wire.Ping, addr netip.AddrPort, _ v5wire.Nonce) { test.packetIn(&v5wire.Pong{ReqID: p.ReqID}) }) // Answer the other ping. - test.waitPacketOut(func(p *v5wire.Ping, addr *net.UDPAddr, _ v5wire.Nonce) { + test.waitPacketOut(func(p *v5wire.Ping, addr netip.AddrPort, _ v5wire.Nonce) { test.packetIn(&v5wire.Pong{ReqID: p.ReqID}) }) if err := <-done; err != nil { @@ -370,11 +371,11 @@ func TestUDPv5_multipleHandshakeRounds(t *testing.T) { }() // Ping answered by WHOAREYOU. - test.waitPacketOut(func(p *v5wire.Ping, addr *net.UDPAddr, nonce v5wire.Nonce) { + test.waitPacketOut(func(p *v5wire.Ping, addr netip.AddrPort, nonce v5wire.Nonce) { test.packetIn(&v5wire.Whoareyou{Nonce: nonce}) }) // Ping answered by WHOAREYOU again. - test.waitPacketOut(func(p *v5wire.Ping, addr *net.UDPAddr, nonce v5wire.Nonce) { + test.waitPacketOut(func(p *v5wire.Ping, addr netip.AddrPort, nonce v5wire.Nonce) { test.packetIn(&v5wire.Whoareyou{Nonce: nonce}) }) if err := <-done; err != errTimeout { @@ -401,7 +402,7 @@ func TestUDPv5_callTimeoutReset(t *testing.T) { }() // Serve two responses, slowly. - test.waitPacketOut(func(p *v5wire.Findnode, addr *net.UDPAddr, _ v5wire.Nonce) { + test.waitPacketOut(func(p *v5wire.Findnode, addr netip.AddrPort, _ v5wire.Nonce) { time.Sleep(respTimeout - 50*time.Millisecond) test.packetIn(&v5wire.Nodes{ ReqID: p.ReqID, @@ -439,7 +440,7 @@ func TestUDPv5_talkHandling(t *testing.T) { Protocol: "test", Message: []byte("test request"), }) - test.waitPacketOut(func(p *v5wire.TalkResponse, addr *net.UDPAddr, _ v5wire.Nonce) { + test.waitPacketOut(func(p *v5wire.TalkResponse, addr netip.AddrPort, _ v5wire.Nonce) { if !bytes.Equal(p.ReqID, []byte("foo")) { t.Error("wrong request ID in response:", p.ReqID) } @@ -458,7 +459,7 @@ func TestUDPv5_talkHandling(t *testing.T) { Protocol: "wrong", Message: []byte("test request"), }) - test.waitPacketOut(func(p *v5wire.TalkResponse, addr *net.UDPAddr, _ v5wire.Nonce) { + test.waitPacketOut(func(p *v5wire.TalkResponse, addr netip.AddrPort, _ v5wire.Nonce) { if !bytes.Equal(p.ReqID, []byte("2")) { t.Error("wrong request ID in response:", p.ReqID) } @@ -485,7 +486,7 @@ func TestUDPv5_talkRequest(t *testing.T) { _, err := test.udp.TalkRequest(remote, "test", []byte("test request")) done <- err }() - test.waitPacketOut(func(p *v5wire.TalkRequest, addr *net.UDPAddr, _ v5wire.Nonce) {}) + test.waitPacketOut(func(p *v5wire.TalkRequest, addr netip.AddrPort, _ v5wire.Nonce) {}) if err := <-done; err != errTimeout { t.Fatalf("want errTimeout, got %q", err) } @@ -495,7 +496,7 @@ func TestUDPv5_talkRequest(t *testing.T) { _, err := test.udp.TalkRequest(remote, "test", []byte("test request")) done <- err }() - test.waitPacketOut(func(p *v5wire.TalkRequest, addr *net.UDPAddr, _ v5wire.Nonce) { + test.waitPacketOut(func(p *v5wire.TalkRequest, addr netip.AddrPort, _ v5wire.Nonce) { if p.Protocol != "test" { t.Errorf("wrong protocol ID in talk request: %q", p.Protocol) } @@ -516,7 +517,7 @@ func TestUDPv5_talkRequest(t *testing.T) { _, err := test.udp.TalkRequestToID(remote.ID(), test.remoteaddr, "test", []byte("test request 2")) done <- err }() - test.waitPacketOut(func(p *v5wire.TalkRequest, addr *net.UDPAddr, _ v5wire.Nonce) { + test.waitPacketOut(func(p *v5wire.TalkRequest, addr netip.AddrPort, _ v5wire.Nonce) { if p.Protocol != "test" { t.Errorf("wrong protocol ID in talk request: %q", p.Protocol) } @@ -583,13 +584,14 @@ func TestUDPv5_lookup(t *testing.T) { for d, nn := range lookupTestnet.dists { for i, key := range nn { n := lookupTestnet.node(d, i) - test.getNode(key, &net.UDPAddr{IP: n.IP(), Port: n.UDP()}) + addr, _ := n.UDPEndpoint() + test.getNode(key, addr) } } // Seed table with initial node. initialNode := lookupTestnet.node(256, 0) - fillTable(test.table, []*node{wrapNode(initialNode)}, true) + fillTable(test.table, []*enode.Node{initialNode}, true) // Start the lookup. resultC := make(chan []*enode.Node, 1) @@ -601,7 +603,7 @@ func TestUDPv5_lookup(t *testing.T) { // Answer lookup packets. asked := make(map[enode.ID]bool) for done := false; !done; { - done = test.waitPacketOut(func(p v5wire.Packet, to *net.UDPAddr, _ v5wire.Nonce) { + done = test.waitPacketOut(func(p v5wire.Packet, to netip.AddrPort, _ v5wire.Nonce) { recipient, key := lookupTestnet.nodeByAddr(to) switch p := p.(type) { case *v5wire.Ping: @@ -652,11 +654,8 @@ func TestUDPv5_PingWithIPV4MappedAddress(t *testing.T) { test := newUDPV5Test(t) defer test.close() - rawIP := net.IPv4(0xFF, 0x12, 0x33, 0xE5) - test.remoteaddr = &net.UDPAddr{ - IP: rawIP.To16(), - Port: 0, - } + rawIP := netip.AddrFrom4([4]byte{0xFF, 0x12, 0x33, 0xE5}) + test.remoteaddr = netip.AddrPortFrom(netip.AddrFrom16(rawIP.As16()), 0) remote := test.getNode(test.remotekey, test.remoteaddr).Node() done := make(chan struct{}, 1) @@ -665,14 +664,14 @@ func TestUDPv5_PingWithIPV4MappedAddress(t *testing.T) { test.udp.handlePing(&v5wire.Ping{ENRSeq: 1}, remote.ID(), test.remoteaddr) done <- struct{}{} }() - test.waitPacketOut(func(p *v5wire.Pong, addr *net.UDPAddr, _ v5wire.Nonce) { + test.waitPacketOut(func(p *v5wire.Pong, addr netip.AddrPort, _ v5wire.Nonce) { if len(p.ToIP) == net.IPv6len { t.Error("Received untruncated ip address") } if len(p.ToIP) != net.IPv4len { t.Errorf("Received ip address with incorrect length: %d", len(p.ToIP)) } - if !p.ToIP.Equal(rawIP) { + if !p.ToIP.Equal(rawIP.AsSlice()) { t.Errorf("Received incorrect ip address: wanted %s but received %s", rawIP.String(), p.ToIP.String()) } }) @@ -688,9 +687,9 @@ type udpV5Test struct { db *enode.DB udp *UDPv5 localkey, remotekey *ecdsa.PrivateKey - remoteaddr *net.UDPAddr + remoteaddr netip.AddrPort nodesByID map[enode.ID]*enode.LocalNode - nodesByIP map[string]*enode.LocalNode + nodesByIP map[netip.Addr]*enode.LocalNode } // testCodec is the packet encoding used by protocol tests. This codec does not perform encryption. @@ -750,9 +749,9 @@ func newUDPV5Test(t *testing.T) *udpV5Test { pipe: newpipe(), localkey: newkey(), remotekey: newkey(), - remoteaddr: &net.UDPAddr{IP: net.IP{10, 0, 1, 99}, Port: 30303}, + remoteaddr: netip.MustParseAddrPort("10.0.1.99:30303"), nodesByID: make(map[enode.ID]*enode.LocalNode), - nodesByIP: make(map[string]*enode.LocalNode), + nodesByIP: make(map[netip.Addr]*enode.LocalNode), } test.db, _ = enode.OpenDB("") ln := enode.NewLocalNode(test.db, test.localkey) @@ -777,8 +776,8 @@ func (test *udpV5Test) packetIn(packet v5wire.Packet) { test.packetInFrom(test.remotekey, test.remoteaddr, packet) } -// handles a packet as if it had been sent to the transport by the key/endpoint. -func (test *udpV5Test) packetInFrom(key *ecdsa.PrivateKey, addr *net.UDPAddr, packet v5wire.Packet) { +// packetInFrom handles a packet as if it had been sent to the transport by the key/endpoint. +func (test *udpV5Test) packetInFrom(key *ecdsa.PrivateKey, addr netip.AddrPort, packet v5wire.Packet) { test.t.Helper() ln := test.getNode(key, addr) @@ -793,22 +792,22 @@ func (test *udpV5Test) packetInFrom(key *ecdsa.PrivateKey, addr *net.UDPAddr, pa } // getNode ensures the test knows about a node at the given endpoint. -func (test *udpV5Test) getNode(key *ecdsa.PrivateKey, addr *net.UDPAddr) *enode.LocalNode { +func (test *udpV5Test) getNode(key *ecdsa.PrivateKey, addr netip.AddrPort) *enode.LocalNode { id := encodePubkey(&key.PublicKey).id() ln := test.nodesByID[id] if ln == nil { db, _ := enode.OpenDB("") ln = enode.NewLocalNode(db, key) - ln.SetStaticIP(addr.IP) - ln.Set(enr.UDP(addr.Port)) + ln.SetStaticIP(addr.Addr().AsSlice()) + ln.Set(enr.UDP(addr.Port())) test.nodesByID[id] = ln } - test.nodesByIP[string(addr.IP)] = ln + test.nodesByIP[addr.Addr()] = ln return ln } // waitPacketOut waits for the next output packet and handles it using the given 'validate' -// function. The function must be of type func (X, *net.UDPAddr, v5wire.Nonce) where X is +// function. The function must be of type func (X, netip.AddrPort, v5wire.Nonce) where X is // assignable to packetV5. func (test *udpV5Test) waitPacketOut(validate interface{}) (closed bool) { test.t.Helper() @@ -823,7 +822,7 @@ func (test *udpV5Test) waitPacketOut(validate interface{}) (closed bool) { if err == errTimeout { test.t.Fatalf("timed out waiting for %v", exptype) } - ln := test.nodesByIP[string(dgram.to.IP)] + ln := test.nodesByIP[dgram.to.Addr()] if ln == nil { test.t.Fatalf("attempt to send to non-existing node %v", &dgram.to) } @@ -837,7 +836,7 @@ func (test *udpV5Test) waitPacketOut(validate interface{}) (closed bool) { test.t.Errorf("sent packet type mismatch, got: %v, want: %v", reflect.TypeOf(p), exptype) return false } - fn.Call([]reflect.Value{reflect.ValueOf(p), reflect.ValueOf(&dgram.to), reflect.ValueOf(frame.AuthTag)}) + fn.Call([]reflect.Value{reflect.ValueOf(p), reflect.ValueOf(dgram.to), reflect.ValueOf(frame.AuthTag)}) return false } diff --git a/p2p/discover/v5wire/encoding_test.go b/p2p/discover/v5wire/encoding_test.go index a5387311a5..8dd02620eb 100644 --- a/p2p/discover/v5wire/encoding_test.go +++ b/p2p/discover/v5wire/encoding_test.go @@ -30,6 +30,7 @@ import ( "testing" "github.com/davecgh/go-spew/spew" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/mclock" "github.com/ethereum/go-ethereum/crypto" @@ -283,9 +284,38 @@ func TestDecodeErrorsV5(t *testing.T) { b = make([]byte, 63) net.nodeA.expectDecodeErr(t, errInvalidHeader, b) - // TODO some more tests would be nice :) - // - check invalid authdata sizes - // - check invalid handshake data sizes + t.Run("invalid-handshake-datasize", func(t *testing.T) { + requiredNumber := 108 + + testDataFile := filepath.Join("testdata", "v5.1-ping-handshake"+".txt") + enc := hexFile(testDataFile) + //delete some byte from handshake to make it invalid + enc = enc[:len(enc)-requiredNumber] + net.nodeB.expectDecodeErr(t, errMsgTooShort, enc) + }) + + t.Run("invalid-auth-datasize", func(t *testing.T) { + testPacket := []byte{} + testDataFiles := []string{"v5.1-whoareyou", "v5.1-ping-handshake"} + for counter, name := range testDataFiles { + file := filepath.Join("testdata", name+".txt") + enc := hexFile(file) + if counter == 0 { + //make whoareyou header + testPacket = enc[:sizeofStaticPacketData-1] + testPacket = append(testPacket, 255) + } + if counter == 1 { + //append invalid auth size + testPacket = append(testPacket, enc[sizeofStaticPacketData:]...) + } + } + + wantErr := "invalid auth size" + if _, err := net.nodeB.decode(testPacket); strings.HasSuffix(err.Error(), wantErr) { + t.Fatal(fmt.Errorf("(%s) got err %q, want %q", net.nodeB.ln.ID().TerminalString(), err, wantErr)) + } + }) } // This test checks that all test vectors can be decoded. @@ -576,7 +606,7 @@ func (n *handshakeTestNode) n() *enode.Node { } func (n *handshakeTestNode) addr() string { - return n.ln.Node().IP().String() + return n.ln.Node().IPAddr().String() } func (n *handshakeTestNode) id() enode.ID { diff --git a/p2p/enode/idscheme.go b/p2p/enode/idscheme.go index 6ad7f809a7..db7841c047 100644 --- a/p2p/enode/idscheme.go +++ b/p2p/enode/idscheme.go @@ -157,5 +157,5 @@ func SignNull(r *enr.Record, id ID) *Node { if err := r.SetSig(NullID{}, []byte{}); err != nil { panic(err) } - return &Node{r: *r, id: id} + return newNodeWithID(r, id) } diff --git a/p2p/enode/localnode.go b/p2p/enode/localnode.go index a18204e752..6e79c9cbdc 100644 --- a/p2p/enode/localnode.go +++ b/p2p/enode/localnode.go @@ -20,8 +20,8 @@ import ( "crypto/ecdsa" "fmt" "net" + "net/netip" "reflect" - "strconv" "sync" "sync/atomic" "time" @@ -175,8 +175,8 @@ func (ln *LocalNode) delete(e enr.Entry) { } } -func (ln *LocalNode) endpointForIP(ip net.IP) *lnEndpoint { - if ip.To4() != nil { +func (ln *LocalNode) endpointForIP(ip netip.Addr) *lnEndpoint { + if ip.Is4() { return &ln.endpoint4 } return &ln.endpoint6 @@ -188,7 +188,7 @@ func (ln *LocalNode) SetStaticIP(ip net.IP) { ln.mu.Lock() defer ln.mu.Unlock() - ln.endpointForIP(ip).staticIP = ip + ln.endpointForIP(netutil.IPToAddr(ip)).staticIP = ip ln.updateEndpoints() } @@ -198,7 +198,7 @@ func (ln *LocalNode) SetFallbackIP(ip net.IP) { ln.mu.Lock() defer ln.mu.Unlock() - ln.endpointForIP(ip).fallbackIP = ip + ln.endpointForIP(netutil.IPToAddr(ip)).fallbackIP = ip ln.updateEndpoints() } @@ -215,21 +215,21 @@ func (ln *LocalNode) SetFallbackUDP(port int) { // UDPEndpointStatement should be called whenever a statement about the local node's // UDP endpoint is received. It feeds the local endpoint predictor. -func (ln *LocalNode) UDPEndpointStatement(fromaddr, endpoint *net.UDPAddr) { +func (ln *LocalNode) UDPEndpointStatement(fromaddr, endpoint netip.AddrPort) { ln.mu.Lock() defer ln.mu.Unlock() - ln.endpointForIP(endpoint.IP).track.AddStatement(fromaddr.String(), endpoint.String()) + ln.endpointForIP(endpoint.Addr()).track.AddStatement(fromaddr.Addr(), endpoint) ln.updateEndpoints() } // UDPContact should be called whenever the local node has announced itself to another node // via UDP. It feeds the local endpoint predictor. -func (ln *LocalNode) UDPContact(toaddr *net.UDPAddr) { +func (ln *LocalNode) UDPContact(toaddr netip.AddrPort) { ln.mu.Lock() defer ln.mu.Unlock() - ln.endpointForIP(toaddr.IP).track.AddContact(toaddr.String()) + ln.endpointForIP(toaddr.Addr()).track.AddContact(toaddr.Addr()) ln.updateEndpoints() } @@ -268,29 +268,13 @@ func (e *lnEndpoint) get() (newIP net.IP, newPort uint16) { } if e.staticIP != nil { newIP = e.staticIP - } else if ip, port := predictAddr(e.track); ip != nil { - newIP = ip - newPort = port + } else if ap := e.track.PredictEndpoint(); ap.IsValid() { + newIP = ap.Addr().AsSlice() + newPort = ap.Port() } return newIP, newPort } -// predictAddr wraps IPTracker.PredictEndpoint, converting from its string-based -// endpoint representation to IP and port types. -func predictAddr(t *netutil.IPTracker) (net.IP, uint16) { - ep := t.PredictEndpoint() - if ep == "" { - return nil, 0 - } - ipString, portString, _ := net.SplitHostPort(ep) - ip := net.ParseIP(ipString) - port, err := strconv.ParseUint(portString, 10, 16) - if err != nil { - return nil, 0 - } - return ip, uint16(port) -} - func (ln *LocalNode) invalidate() { ln.cur.Store((*Node)(nil)) } @@ -314,7 +298,7 @@ func (ln *LocalNode) sign() { panic(fmt.Errorf("enode: can't verify local record: %v", err)) } ln.cur.Store(n) - log.Info("New local node record", "seq", ln.seq, "id", n.ID(), "ip", n.IP(), "udp", n.UDP(), "tcp", n.TCP()) + log.Info("New local node record", "seq", ln.seq, "id", n.ID(), "ip", n.IPAddr(), "udp", n.UDP(), "tcp", n.TCP()) } func (ln *LocalNode) bumpSeq() { diff --git a/p2p/enode/localnode_test.go b/p2p/enode/localnode_test.go index 7f97ad392f..86b962a74e 100644 --- a/p2p/enode/localnode_test.go +++ b/p2p/enode/localnode_test.go @@ -17,12 +17,14 @@ package enode import ( - "crypto/rand" + "math/rand" "net" + "net/netip" "testing" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/p2p/enr" + "github.com/ethereum/go-ethereum/p2p/netutil" "github.com/stretchr/testify/assert" ) @@ -88,6 +90,7 @@ func TestLocalNodeSeqPersist(t *testing.T) { // This test checks behavior of the endpoint predictor. func TestLocalNodeEndpoint(t *testing.T) { var ( + rng = rand.New(rand.NewSource(4)) fallback = &net.UDPAddr{IP: net.IP{127, 0, 0, 1}, Port: 80} predicted = &net.UDPAddr{IP: net.IP{127, 0, 1, 2}, Port: 81} staticIP = net.IP{127, 0, 1, 2} @@ -96,6 +99,7 @@ func TestLocalNodeEndpoint(t *testing.T) { defer db.Close() // Nothing is set initially. + assert.Equal(t, netip.Addr{}, ln.Node().IPAddr()) assert.Equal(t, net.IP(nil), ln.Node().IP()) assert.Equal(t, 0, ln.Node().UDP()) initialSeq := ln.Node().Seq() @@ -103,26 +107,30 @@ func TestLocalNodeEndpoint(t *testing.T) { // Set up fallback address. ln.SetFallbackIP(fallback.IP) ln.SetFallbackUDP(fallback.Port) + assert.Equal(t, netutil.IPToAddr(fallback.IP), ln.Node().IPAddr()) assert.Equal(t, fallback.IP, ln.Node().IP()) assert.Equal(t, fallback.Port, ln.Node().UDP()) assert.Equal(t, initialSeq+1, ln.Node().Seq()) // Add endpoint statements from random hosts. for i := 0; i < iptrackMinStatements; i++ { + assert.Equal(t, netutil.IPToAddr(fallback.IP), ln.Node().IPAddr()) assert.Equal(t, fallback.IP, ln.Node().IP()) assert.Equal(t, fallback.Port, ln.Node().UDP()) assert.Equal(t, initialSeq+1, ln.Node().Seq()) - from := &net.UDPAddr{IP: make(net.IP, 4), Port: 90} - rand.Read(from.IP) - ln.UDPEndpointStatement(from, predicted) + from := netip.AddrPortFrom(netutil.RandomAddr(rng, true), 9000) + endpoint := netip.AddrPortFrom(netutil.IPToAddr(predicted.IP), uint16(predicted.Port)) + ln.UDPEndpointStatement(from, endpoint) } + assert.Equal(t, netutil.IPToAddr(predicted.IP), ln.Node().IPAddr()) assert.Equal(t, predicted.IP, ln.Node().IP()) assert.Equal(t, predicted.Port, ln.Node().UDP()) assert.Equal(t, initialSeq+2, ln.Node().Seq()) // Static IP overrides prediction. ln.SetStaticIP(staticIP) + assert.Equal(t, netutil.IPToAddr(staticIP), ln.Node().IPAddr()) assert.Equal(t, staticIP, ln.Node().IP()) assert.Equal(t, fallback.Port, ln.Node().UDP()) assert.Equal(t, initialSeq+3, ln.Node().Seq()) diff --git a/p2p/enode/node.go b/p2p/enode/node.go index d7a1a9a156..cb4ac8d172 100644 --- a/p2p/enode/node.go +++ b/p2p/enode/node.go @@ -24,6 +24,7 @@ import ( "fmt" "math/bits" "net" + "net/netip" "strings" "github.com/ethereum/go-ethereum/p2p/enr" @@ -36,6 +37,10 @@ var errMissingPrefix = errors.New("missing 'enr:' prefix for base64-encoded reco type Node struct { r enr.Record id ID + // endpoint information + ip netip.Addr + udp uint16 + tcp uint16 } // New wraps a node record. The record must be valid according to the given @@ -44,11 +49,76 @@ func New(validSchemes enr.IdentityScheme, r *enr.Record) (*Node, error) { if err := r.VerifySignature(validSchemes); err != nil { return nil, err } - node := &Node{r: *r} - if n := copy(node.id[:], validSchemes.NodeAddr(&node.r)); n != len(ID{}) { - return nil, fmt.Errorf("invalid node ID length %d, need %d", n, len(ID{})) + var id ID + if n := copy(id[:], validSchemes.NodeAddr(r)); n != len(id) { + return nil, fmt.Errorf("invalid node ID length %d, need %d", n, len(id)) + } + return newNodeWithID(r, id), nil +} + +func newNodeWithID(r *enr.Record, id ID) *Node { + n := &Node{r: *r, id: id} + // Set the preferred endpoint. + // Here we decide between IPv4 and IPv6, choosing the 'most global' address. + var ip4 netip.Addr + var ip6 netip.Addr + n.Load((*enr.IPv4Addr)(&ip4)) + n.Load((*enr.IPv6Addr)(&ip6)) + valid4 := validIP(ip4) + valid6 := validIP(ip6) + switch { + case valid4 && valid6: + if localityScore(ip4) >= localityScore(ip6) { + n.setIP4(ip4) + } else { + n.setIP6(ip6) + } + case valid4: + n.setIP4(ip4) + case valid6: + n.setIP6(ip6) + } + return n +} + +// validIP reports whether 'ip' is a valid node endpoint IP address. +func validIP(ip netip.Addr) bool { + return ip.IsValid() && !ip.IsMulticast() +} + +func localityScore(ip netip.Addr) int { + switch { + case ip.IsUnspecified(): + return 0 + case ip.IsLoopback(): + return 1 + case ip.IsLinkLocalUnicast(): + return 2 + case ip.IsPrivate(): + return 3 + default: + return 4 + } +} + +func (n *Node) setIP4(ip netip.Addr) { + n.ip = ip + n.Load((*enr.UDP)(&n.udp)) + n.Load((*enr.TCP)(&n.tcp)) +} + +func (n *Node) setIP6(ip netip.Addr) { + if ip.Is4In6() { + n.setIP4(ip) + return + } + n.ip = ip + if err := n.Load((*enr.UDP6)(&n.udp)); err != nil { + n.Load((*enr.UDP)(&n.udp)) + } + if err := n.Load((*enr.TCP6)(&n.tcp)); err != nil { + n.Load((*enr.TCP)(&n.tcp)) } - return node, nil } // MustParse parses a node record or enode:// URL. It panics if the input is invalid. @@ -89,43 +159,45 @@ func (n *Node) Seq() uint64 { return n.r.Seq() } -// Incomplete returns true for nodes with no IP address. -func (n *Node) Incomplete() bool { - return n.IP() == nil -} - // Load retrieves an entry from the underlying record. func (n *Node) Load(k enr.Entry) error { return n.r.Load(k) } -// IP returns the IP address of the node. This prefers IPv4 addresses. +// IP returns the IP address of the node. func (n *Node) IP() net.IP { - var ( - ip4 enr.IPv4 - ip6 enr.IPv6 - ) - if n.Load(&ip4) == nil { - return net.IP(ip4) - } - if n.Load(&ip6) == nil { - return net.IP(ip6) - } - return nil + return net.IP(n.ip.AsSlice()) +} + +// IPAddr returns the IP address of the node. +func (n *Node) IPAddr() netip.Addr { + return n.ip } // UDP returns the UDP port of the node. func (n *Node) UDP() int { - var port enr.UDP - n.Load(&port) - return int(port) + return int(n.udp) } // TCP returns the TCP port of the node. func (n *Node) TCP() int { - var port enr.TCP - n.Load(&port) - return int(port) + return int(n.tcp) +} + +// UDPEndpoint returns the announced UDP endpoint. +func (n *Node) UDPEndpoint() (netip.AddrPort, bool) { + if !n.ip.IsValid() || n.ip.IsUnspecified() || n.udp == 0 { + return netip.AddrPort{}, false + } + return netip.AddrPortFrom(n.ip, n.udp), true +} + +// TCPEndpoint returns the announced TCP endpoint. +func (n *Node) TCPEndpoint() (netip.AddrPort, bool) { + if !n.ip.IsValid() || n.ip.IsUnspecified() || n.tcp == 0 { + return netip.AddrPort{}, false + } + return netip.AddrPortFrom(n.ip, n.tcp), true } // Pubkey returns the secp256k1 public key of the node, if present. @@ -147,16 +219,15 @@ func (n *Node) Record() *enr.Record { // ValidateComplete checks whether n has a valid IP and UDP port. // Deprecated: don't use this method. func (n *Node) ValidateComplete() error { - if n.Incomplete() { + if !n.ip.IsValid() { return errors.New("missing IP address") } - if n.UDP() == 0 { - return errors.New("missing UDP port") - } - ip := n.IP() - if ip.IsMulticast() || ip.IsUnspecified() { + if n.ip.IsMulticast() || n.ip.IsUnspecified() { return errors.New("invalid IP (multicast/unspecified)") } + if n.udp == 0 { + return errors.New("missing UDP port") + } // Validate the node key (on curve, etc.). var key Secp256k1 return n.Load(&key) diff --git a/p2p/enode/node_test.go b/p2p/enode/node_test.go index d15859c477..56e196e82e 100644 --- a/p2p/enode/node_test.go +++ b/p2p/enode/node_test.go @@ -21,6 +21,7 @@ import ( "encoding/hex" "fmt" "math/big" + "net/netip" "testing" "testing/quick" @@ -64,6 +65,167 @@ func TestPythonInterop(t *testing.T) { } } +func TestNodeEndpoints(t *testing.T) { + id := HexID("00000000000000806ad9b61fa5ae014307ebdc964253adcd9f2c0a392aa11abc") + type endpointTest struct { + name string + node *Node + wantIP netip.Addr + wantUDP int + wantTCP int + } + tests := []endpointTest{ + { + name: "no-addr", + node: func() *Node { + var r enr.Record + return SignNull(&r, id) + }(), + }, + { + name: "udp-only", + node: func() *Node { + var r enr.Record + r.Set(enr.UDP(9000)) + return SignNull(&r, id) + }(), + }, + { + name: "tcp-only", + node: func() *Node { + var r enr.Record + r.Set(enr.TCP(9000)) + return SignNull(&r, id) + }(), + }, + { + name: "ipv4-only-loopback", + node: func() *Node { + var r enr.Record + r.Set(enr.IPv4Addr(netip.MustParseAddr("127.0.0.1"))) + return SignNull(&r, id) + }(), + wantIP: netip.MustParseAddr("127.0.0.1"), + }, + { + name: "ipv4-only-unspecified", + node: func() *Node { + var r enr.Record + r.Set(enr.IPv4Addr(netip.MustParseAddr("0.0.0.0"))) + return SignNull(&r, id) + }(), + wantIP: netip.MustParseAddr("0.0.0.0"), + }, + { + name: "ipv4-only", + node: func() *Node { + var r enr.Record + r.Set(enr.IPv4Addr(netip.MustParseAddr("99.22.33.1"))) + return SignNull(&r, id) + }(), + wantIP: netip.MustParseAddr("99.22.33.1"), + }, + { + name: "ipv6-only", + node: func() *Node { + var r enr.Record + r.Set(enr.IPv6Addr(netip.MustParseAddr("2001::ff00:0042:8329"))) + return SignNull(&r, id) + }(), + wantIP: netip.MustParseAddr("2001::ff00:0042:8329"), + }, + { + name: "ipv4-loopback-and-ipv6-global", + node: func() *Node { + var r enr.Record + r.Set(enr.IPv4Addr(netip.MustParseAddr("127.0.0.1"))) + r.Set(enr.UDP(30304)) + r.Set(enr.IPv6Addr(netip.MustParseAddr("2001::ff00:0042:8329"))) + r.Set(enr.UDP6(30306)) + return SignNull(&r, id) + }(), + wantIP: netip.MustParseAddr("2001::ff00:0042:8329"), + wantUDP: 30306, + }, + { + name: "ipv4-unspecified-and-ipv6-loopback", + node: func() *Node { + var r enr.Record + r.Set(enr.IPv4Addr(netip.MustParseAddr("0.0.0.0"))) + r.Set(enr.IPv6Addr(netip.MustParseAddr("::1"))) + return SignNull(&r, id) + }(), + wantIP: netip.MustParseAddr("::1"), + }, + { + name: "ipv4-private-and-ipv6-global", + node: func() *Node { + var r enr.Record + r.Set(enr.IPv4Addr(netip.MustParseAddr("192.168.2.2"))) + r.Set(enr.UDP(30304)) + r.Set(enr.IPv6Addr(netip.MustParseAddr("2001::ff00:0042:8329"))) + r.Set(enr.UDP6(30306)) + return SignNull(&r, id) + }(), + wantIP: netip.MustParseAddr("2001::ff00:0042:8329"), + wantUDP: 30306, + }, + { + name: "ipv4-local-and-ipv6-global", + node: func() *Node { + var r enr.Record + r.Set(enr.IPv4Addr(netip.MustParseAddr("169.254.2.6"))) + r.Set(enr.UDP(30304)) + r.Set(enr.IPv6Addr(netip.MustParseAddr("2001::ff00:0042:8329"))) + r.Set(enr.UDP6(30306)) + return SignNull(&r, id) + }(), + wantIP: netip.MustParseAddr("2001::ff00:0042:8329"), + wantUDP: 30306, + }, + { + name: "ipv4-private-and-ipv6-private", + node: func() *Node { + var r enr.Record + r.Set(enr.IPv4Addr(netip.MustParseAddr("192.168.2.2"))) + r.Set(enr.UDP(30304)) + r.Set(enr.IPv6Addr(netip.MustParseAddr("fd00::abcd:1"))) + r.Set(enr.UDP6(30306)) + return SignNull(&r, id) + }(), + wantIP: netip.MustParseAddr("192.168.2.2"), + wantUDP: 30304, + }, + { + name: "ipv4-private-and-ipv6-link-local", + node: func() *Node { + var r enr.Record + r.Set(enr.IPv4Addr(netip.MustParseAddr("192.168.2.2"))) + r.Set(enr.UDP(30304)) + r.Set(enr.IPv6Addr(netip.MustParseAddr("fe80::1"))) + r.Set(enr.UDP6(30306)) + return SignNull(&r, id) + }(), + wantIP: netip.MustParseAddr("192.168.2.2"), + wantUDP: 30304, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + if test.wantIP != test.node.IPAddr() { + t.Errorf("node has wrong IP %v, want %v", test.node.IPAddr(), test.wantIP) + } + if test.wantUDP != test.node.UDP() { + t.Errorf("node has wrong UDP port %d, want %d", test.node.UDP(), test.wantUDP) + } + if test.wantTCP != test.node.TCP() { + t.Errorf("node has wrong TCP port %d, want %d", test.node.TCP(), test.wantTCP) + } + }) + } +} + func TestHexID(t *testing.T) { ref := ID{0, 0, 0, 0, 0, 0, 0, 128, 106, 217, 182, 31, 165, 174, 1, 67, 7, 235, 220, 150, 66, 83, 173, 205, 159, 44, 10, 57, 42, 161, 26, 188} id1 := HexID("0x00000000000000806ad9b61fa5ae014307ebdc964253adcd9f2c0a392aa11abc") diff --git a/p2p/enode/nodedb.go b/p2p/enode/nodedb.go index 1357448544..0237c2594f 100644 --- a/p2p/enode/nodedb.go +++ b/p2p/enode/nodedb.go @@ -21,12 +21,13 @@ import ( "crypto/rand" "encoding/binary" "fmt" - "net" + "net/netip" "os" "sync" "time" "github.com/ethereum/go-ethereum/common/gopool" + "github.com/ethereum/go-ethereum/p2p/enr" "github.com/ethereum/go-ethereum/rlp" "github.com/syndtr/goleveldb/leveldb" "github.com/syndtr/goleveldb/leveldb/errors" @@ -66,7 +67,7 @@ var ( errInvalidIP = errors.New("invalid IP") ) -var zeroIP = make(net.IP, 16) +var zeroIP = netip.IPv6Unspecified() // DB is the node database, storing previously seen nodes and any collected metadata about // them for QoS purposes. @@ -151,39 +152,37 @@ func splitNodeKey(key []byte) (id ID, rest []byte) { } // nodeItemKey returns the database key for a node metadata field. -func nodeItemKey(id ID, ip net.IP, field string) []byte { - ip16 := ip.To16() - if ip16 == nil { - panic(fmt.Errorf("invalid IP (length %d)", len(ip))) +func nodeItemKey(id ID, ip netip.Addr, field string) []byte { + if !ip.IsValid() { + panic("invalid IP") } - return bytes.Join([][]byte{nodeKey(id), ip16, []byte(field)}, []byte{':'}) + ip16 := ip.As16() + return bytes.Join([][]byte{nodeKey(id), ip16[:], []byte(field)}, []byte{':'}) } // splitNodeItemKey returns the components of a key created by nodeItemKey. -func splitNodeItemKey(key []byte) (id ID, ip net.IP, field string) { +func splitNodeItemKey(key []byte) (id ID, ip netip.Addr, field string) { id, key = splitNodeKey(key) // Skip discover root. if string(key) == dbDiscoverRoot { - return id, nil, "" + return id, netip.Addr{}, "" } key = key[len(dbDiscoverRoot)+1:] // Split out the IP. - ip = key[:16] - if ip4 := ip.To4(); ip4 != nil { - ip = ip4 - } + ip, _ = netip.AddrFromSlice(key[:16]) key = key[16+1:] // Field is the remainder of key. field = string(key) return id, ip, field } -func v5Key(id ID, ip net.IP, field string) []byte { +func v5Key(id ID, ip netip.Addr, field string) []byte { + ip16 := ip.As16() return bytes.Join([][]byte{ []byte(dbNodePrefix), id[:], []byte(dbDiscv5Root), - ip.To16(), + ip16[:], []byte(field), }, []byte{':'}) } @@ -243,13 +242,14 @@ func (db *DB) Node(id ID) *Node { } func mustDecodeNode(id, data []byte) *Node { - node := new(Node) - if err := rlp.DecodeBytes(data, &node.r); err != nil { + var r enr.Record + if err := rlp.DecodeBytes(data, &r); err != nil { panic(fmt.Errorf("p2p/enode: can't decode node %x in DB: %v", id, err)) } - // Restore node id cache. - copy(node.id[:], id) - return node + if len(id) != len(ID{}) { + panic(fmt.Errorf("invalid id length %d", len(id))) + } + return newNodeWithID(&r, ID(id)) } // UpdateNode inserts - potentially overwriting - a node into the peer database. @@ -367,24 +367,24 @@ func (db *DB) expireNodes() { // LastPingReceived retrieves the time of the last ping packet received from // a remote node. -func (db *DB) LastPingReceived(id ID, ip net.IP) time.Time { - if ip = ip.To16(); ip == nil { +func (db *DB) LastPingReceived(id ID, ip netip.Addr) time.Time { + if !ip.IsValid() { return time.Time{} } return time.Unix(db.fetchInt64(nodeItemKey(id, ip, dbNodePing)), 0) } // UpdateLastPingReceived updates the last time we tried contacting a remote node. -func (db *DB) UpdateLastPingReceived(id ID, ip net.IP, instance time.Time) error { - if ip = ip.To16(); ip == nil { +func (db *DB) UpdateLastPingReceived(id ID, ip netip.Addr, instance time.Time) error { + if !ip.IsValid() { return errInvalidIP } return db.storeInt64(nodeItemKey(id, ip, dbNodePing), instance.Unix()) } // LastPongReceived retrieves the time of the last successful pong from remote node. -func (db *DB) LastPongReceived(id ID, ip net.IP) time.Time { - if ip = ip.To16(); ip == nil { +func (db *DB) LastPongReceived(id ID, ip netip.Addr) time.Time { + if !ip.IsValid() { return time.Time{} } // Launch expirer @@ -393,40 +393,40 @@ func (db *DB) LastPongReceived(id ID, ip net.IP) time.Time { } // UpdateLastPongReceived updates the last pong time of a node. -func (db *DB) UpdateLastPongReceived(id ID, ip net.IP, instance time.Time) error { - if ip = ip.To16(); ip == nil { +func (db *DB) UpdateLastPongReceived(id ID, ip netip.Addr, instance time.Time) error { + if !ip.IsValid() { return errInvalidIP } return db.storeInt64(nodeItemKey(id, ip, dbNodePong), instance.Unix()) } // FindFails retrieves the number of findnode failures since bonding. -func (db *DB) FindFails(id ID, ip net.IP) int { - if ip = ip.To16(); ip == nil { +func (db *DB) FindFails(id ID, ip netip.Addr) int { + if !ip.IsValid() { return 0 } return int(db.fetchInt64(nodeItemKey(id, ip, dbNodeFindFails))) } // UpdateFindFails updates the number of findnode failures since bonding. -func (db *DB) UpdateFindFails(id ID, ip net.IP, fails int) error { - if ip = ip.To16(); ip == nil { +func (db *DB) UpdateFindFails(id ID, ip netip.Addr, fails int) error { + if !ip.IsValid() { return errInvalidIP } return db.storeInt64(nodeItemKey(id, ip, dbNodeFindFails), int64(fails)) } // FindFailsV5 retrieves the discv5 findnode failure counter. -func (db *DB) FindFailsV5(id ID, ip net.IP) int { - if ip = ip.To16(); ip == nil { +func (db *DB) FindFailsV5(id ID, ip netip.Addr) int { + if !ip.IsValid() { return 0 } return int(db.fetchInt64(v5Key(id, ip, dbNodeFindFails))) } // UpdateFindFailsV5 stores the discv5 findnode failure counter. -func (db *DB) UpdateFindFailsV5(id ID, ip net.IP, fails int) error { - if ip = ip.To16(); ip == nil { +func (db *DB) UpdateFindFailsV5(id ID, ip netip.Addr, fails int) error { + if !ip.IsValid() { return errInvalidIP } return db.storeInt64(v5Key(id, ip, dbNodeFindFails), int64(fails)) @@ -473,7 +473,7 @@ seek: id[0] = 0 continue seek // iterator exhausted } - if now.Sub(db.LastPongReceived(n.ID(), n.IP())) > maxAge { + if now.Sub(db.LastPongReceived(n.ID(), n.IPAddr())) > maxAge { continue seek } for i := range nodes { diff --git a/p2p/enode/nodedb_test.go b/p2p/enode/nodedb_test.go index 38764f31b1..bc0291665d 100644 --- a/p2p/enode/nodedb_test.go +++ b/p2p/enode/nodedb_test.go @@ -20,6 +20,7 @@ import ( "bytes" "fmt" "net" + "net/netip" "path/filepath" "reflect" "testing" @@ -48,8 +49,10 @@ func TestDBNodeKey(t *testing.T) { } func TestDBNodeItemKey(t *testing.T) { - wantIP := net.IP{127, 0, 0, 3} + wantIP := netip.MustParseAddr("127.0.0.3") + wantIP4in6 := netip.AddrFrom16(wantIP.As16()) wantField := "foobar" + enc := nodeItemKey(keytestID, wantIP, wantField) want := []byte{ 'n', ':', @@ -69,7 +72,7 @@ func TestDBNodeItemKey(t *testing.T) { if id != keytestID { t.Errorf("splitNodeItemKey returned wrong ID: %v", id) } - if !ip.Equal(wantIP) { + if ip != wantIP4in6 { t.Errorf("splitNodeItemKey returned wrong IP: %v", ip) } if field != wantField { @@ -123,33 +126,33 @@ func TestDBFetchStore(t *testing.T) { defer db.Close() // Check fetch/store operations on a node ping object - if stored := db.LastPingReceived(node.ID(), node.IP()); stored.Unix() != 0 { + if stored := db.LastPingReceived(node.ID(), node.IPAddr()); stored.Unix() != 0 { t.Errorf("ping: non-existing object: %v", stored) } - if err := db.UpdateLastPingReceived(node.ID(), node.IP(), inst); err != nil { + if err := db.UpdateLastPingReceived(node.ID(), node.IPAddr(), inst); err != nil { t.Errorf("ping: failed to update: %v", err) } - if stored := db.LastPingReceived(node.ID(), node.IP()); stored.Unix() != inst.Unix() { + if stored := db.LastPingReceived(node.ID(), node.IPAddr()); stored.Unix() != inst.Unix() { t.Errorf("ping: value mismatch: have %v, want %v", stored, inst) } // Check fetch/store operations on a node pong object - if stored := db.LastPongReceived(node.ID(), node.IP()); stored.Unix() != 0 { + if stored := db.LastPongReceived(node.ID(), node.IPAddr()); stored.Unix() != 0 { t.Errorf("pong: non-existing object: %v", stored) } - if err := db.UpdateLastPongReceived(node.ID(), node.IP(), inst); err != nil { + if err := db.UpdateLastPongReceived(node.ID(), node.IPAddr(), inst); err != nil { t.Errorf("pong: failed to update: %v", err) } - if stored := db.LastPongReceived(node.ID(), node.IP()); stored.Unix() != inst.Unix() { + if stored := db.LastPongReceived(node.ID(), node.IPAddr()); stored.Unix() != inst.Unix() { t.Errorf("pong: value mismatch: have %v, want %v", stored, inst) } // Check fetch/store operations on a node findnode-failure object - if stored := db.FindFails(node.ID(), node.IP()); stored != 0 { + if stored := db.FindFails(node.ID(), node.IPAddr()); stored != 0 { t.Errorf("find-node fails: non-existing object: %v", stored) } - if err := db.UpdateFindFails(node.ID(), node.IP(), num); err != nil { + if err := db.UpdateFindFails(node.ID(), node.IPAddr(), num); err != nil { t.Errorf("find-node fails: failed to update: %v", err) } - if stored := db.FindFails(node.ID(), node.IP()); stored != num { + if stored := db.FindFails(node.ID(), node.IPAddr()); stored != num { t.Errorf("find-node fails: value mismatch: have %v, want %v", stored, num) } // Check fetch/store operations on an actual node object @@ -266,7 +269,7 @@ func testSeedQuery() error { if err := db.UpdateNode(seed.node); err != nil { return fmt.Errorf("node %d: failed to insert: %v", i, err) } - if err := db.UpdateLastPongReceived(seed.node.ID(), seed.node.IP(), seed.pong); err != nil { + if err := db.UpdateLastPongReceived(seed.node.ID(), seed.node.IPAddr(), seed.pong); err != nil { return fmt.Errorf("node %d: failed to insert bondTime: %v", i, err) } } @@ -427,7 +430,7 @@ func TestDBExpiration(t *testing.T) { t.Fatalf("node %d: failed to insert: %v", i, err) } } - if err := db.UpdateLastPongReceived(seed.node.ID(), seed.node.IP(), seed.pong); err != nil { + if err := db.UpdateLastPongReceived(seed.node.ID(), seed.node.IPAddr(), seed.pong); err != nil { t.Fatalf("node %d: failed to update bondTime: %v", i, err) } } @@ -438,13 +441,13 @@ func TestDBExpiration(t *testing.T) { unixZeroTime := time.Unix(0, 0) for i, seed := range nodeDBExpirationNodes { node := db.Node(seed.node.ID()) - pong := db.LastPongReceived(seed.node.ID(), seed.node.IP()) + pong := db.LastPongReceived(seed.node.ID(), seed.node.IPAddr()) if seed.exp { if seed.storeNode && node != nil { t.Errorf("node %d (%s) shouldn't be present after expiration", i, seed.node.ID().TerminalString()) } if !pong.Equal(unixZeroTime) { - t.Errorf("pong time %d (%s %v) shouldn't be present after expiration", i, seed.node.ID().TerminalString(), seed.node.IP()) + t.Errorf("pong time %d (%s %v) shouldn't be present after expiration", i, seed.node.ID().TerminalString(), seed.node.IPAddr()) } } else { if seed.storeNode && node == nil { @@ -463,7 +466,7 @@ func TestDBExpireV5(t *testing.T) { db, _ := OpenDB("") defer db.Close() - ip := net.IP{127, 0, 0, 1} + ip := netip.MustParseAddr("127.0.0.1") db.UpdateFindFailsV5(ID{}, ip, 4) db.expireNodes() } diff --git a/p2p/enode/urlv4.go b/p2p/enode/urlv4.go index 0272eee987..a55dfa6632 100644 --- a/p2p/enode/urlv4.go +++ b/p2p/enode/urlv4.go @@ -181,7 +181,7 @@ func (n *Node) URLv4() string { nodeid = fmt.Sprintf("%s.%x", scheme, n.id[:]) } u := url.URL{Scheme: "enode"} - if n.Incomplete() { + if !n.ip.IsValid() { u.Host = nodeid } else { addr := net.TCPAddr{IP: n.IP(), Port: n.TCP()} diff --git a/p2p/enr/entries.go b/p2p/enr/entries.go index 9945a436c9..917e1becba 100644 --- a/p2p/enr/entries.go +++ b/p2p/enr/entries.go @@ -21,6 +21,7 @@ import ( "fmt" "io" "net" + "net/netip" "github.com/ethereum/go-ethereum/rlp" ) @@ -167,6 +168,60 @@ func (v *IPv6) DecodeRLP(s *rlp.Stream) error { return nil } +// IPv4Addr is the "ip" key, which holds the IP address of the node. +type IPv4Addr netip.Addr + +func (v IPv4Addr) ENRKey() string { return "ip" } + +// EncodeRLP implements rlp.Encoder. +func (v IPv4Addr) EncodeRLP(w io.Writer) error { + addr := netip.Addr(v) + if !addr.Is4() { + return fmt.Errorf("address is not IPv4") + } + enc := rlp.NewEncoderBuffer(w) + bytes := addr.As4() + enc.WriteBytes(bytes[:]) + return enc.Flush() +} + +// DecodeRLP implements rlp.Decoder. +func (v *IPv4Addr) DecodeRLP(s *rlp.Stream) error { + var bytes [4]byte + if err := s.ReadBytes(bytes[:]); err != nil { + return err + } + *v = IPv4Addr(netip.AddrFrom4(bytes)) + return nil +} + +// IPv6Addr is the "ip6" key, which holds the IP address of the node. +type IPv6Addr netip.Addr + +func (v IPv6Addr) ENRKey() string { return "ip6" } + +// EncodeRLP implements rlp.Encoder. +func (v IPv6Addr) EncodeRLP(w io.Writer) error { + addr := netip.Addr(v) + if !addr.Is6() { + return fmt.Errorf("address is not IPv6") + } + enc := rlp.NewEncoderBuffer(w) + bytes := addr.As16() + enc.WriteBytes(bytes[:]) + return enc.Flush() +} + +// DecodeRLP implements rlp.Decoder. +func (v *IPv6Addr) DecodeRLP(s *rlp.Stream) error { + var bytes [16]byte + if err := s.ReadBytes(bytes[:]); err != nil { + return err + } + *v = IPv6Addr(netip.AddrFrom16(bytes)) + return nil +} + // KeyError is an error related to a key. type KeyError struct { Key string diff --git a/p2p/netutil/addrutil.go b/p2p/netutil/addrutil.go index fb6d8d2731..b8b318571b 100644 --- a/p2p/netutil/addrutil.go +++ b/p2p/netutil/addrutil.go @@ -16,18 +16,53 @@ package netutil -import "net" +import ( + "fmt" + "math/rand" + "net" + "net/netip" +) -// AddrIP gets the IP address contained in addr. It returns nil if no address is present. -func AddrIP(addr net.Addr) net.IP { +// AddrAddr gets the IP address contained in addr. The result will be invalid if the +// address type is unsupported. +func AddrAddr(addr net.Addr) netip.Addr { switch a := addr.(type) { case *net.IPAddr: - return a.IP + return IPToAddr(a.IP) case *net.TCPAddr: - return a.IP + return IPToAddr(a.IP) case *net.UDPAddr: - return a.IP + return IPToAddr(a.IP) default: - return nil + return netip.Addr{} } } + +// IPToAddr converts net.IP to netip.Addr. Note that unlike netip.AddrFromSlice, this +// function will always ensure that the resulting Addr is IPv4 when the input is. +func IPToAddr(ip net.IP) netip.Addr { + if ip4 := ip.To4(); ip4 != nil { + addr, _ := netip.AddrFromSlice(ip4) + return addr + } else if ip6 := ip.To16(); ip6 != nil { + addr, _ := netip.AddrFromSlice(ip6) + return addr + } + return netip.Addr{} +} + +// RandomAddr creates a random IP address. +func RandomAddr(rng *rand.Rand, ipv4 bool) netip.Addr { + var bytes []byte + if ipv4 || rng.Intn(2) == 0 { + bytes = make([]byte, 4) + } else { + bytes = make([]byte, 16) + } + rng.Read(bytes) + addr, ok := netip.AddrFromSlice(bytes) + if !ok { + panic(fmt.Errorf("BUG! invalid IP %v", bytes)) + } + return addr +} diff --git a/p2p/netutil/iptrack.go b/p2p/netutil/iptrack.go index a070499e19..5140ac7539 100644 --- a/p2p/netutil/iptrack.go +++ b/p2p/netutil/iptrack.go @@ -17,6 +17,7 @@ package netutil import ( + "net/netip" "time" "github.com/ethereum/go-ethereum/common/mclock" @@ -29,14 +30,14 @@ type IPTracker struct { contactWindow time.Duration minStatements int clock mclock.Clock - statements map[string]ipStatement - contact map[string]mclock.AbsTime + statements map[netip.Addr]ipStatement + contact map[netip.Addr]mclock.AbsTime lastStatementGC mclock.AbsTime lastContactGC mclock.AbsTime } type ipStatement struct { - endpoint string + endpoint netip.AddrPort time mclock.AbsTime } @@ -51,9 +52,9 @@ func NewIPTracker(window, contactWindow time.Duration, minStatements int) *IPTra return &IPTracker{ window: window, contactWindow: contactWindow, - statements: make(map[string]ipStatement), + statements: make(map[netip.Addr]ipStatement), minStatements: minStatements, - contact: make(map[string]mclock.AbsTime), + contact: make(map[netip.Addr]mclock.AbsTime), clock: mclock.System{}, } } @@ -74,12 +75,15 @@ func (it *IPTracker) PredictFullConeNAT() bool { } // PredictEndpoint returns the current prediction of the external endpoint. -func (it *IPTracker) PredictEndpoint() string { +func (it *IPTracker) PredictEndpoint() netip.AddrPort { it.gcStatements(it.clock.Now()) // The current strategy is simple: find the endpoint with most statements. - counts := make(map[string]int, len(it.statements)) - maxcount, max := 0, "" + var ( + counts = make(map[netip.AddrPort]int, len(it.statements)) + maxcount int + max netip.AddrPort + ) for _, s := range it.statements { c := counts[s.endpoint] + 1 counts[s.endpoint] = c @@ -91,7 +95,7 @@ func (it *IPTracker) PredictEndpoint() string { } // AddStatement records that a certain host thinks our external endpoint is the one given. -func (it *IPTracker) AddStatement(host, endpoint string) { +func (it *IPTracker) AddStatement(host netip.Addr, endpoint netip.AddrPort) { now := it.clock.Now() it.statements[host] = ipStatement{endpoint, now} if time.Duration(now-it.lastStatementGC) >= it.window { @@ -101,7 +105,7 @@ func (it *IPTracker) AddStatement(host, endpoint string) { // AddContact records that a packet containing our endpoint information has been sent to a // certain host. -func (it *IPTracker) AddContact(host string) { +func (it *IPTracker) AddContact(host netip.Addr) { now := it.clock.Now() it.contact[host] = now if time.Duration(now-it.lastContactGC) >= it.contactWindow { diff --git a/p2p/netutil/iptrack_test.go b/p2p/netutil/iptrack_test.go index ee3bba861e..81653a2733 100644 --- a/p2p/netutil/iptrack_test.go +++ b/p2p/netutil/iptrack_test.go @@ -19,6 +19,7 @@ package netutil import ( crand "crypto/rand" "fmt" + "net/netip" "testing" "time" @@ -42,37 +43,37 @@ func TestIPTracker(t *testing.T) { tests := map[string][]iptrackTestEvent{ "minStatements": { {opPredict, 0, "", ""}, - {opStatement, 0, "127.0.0.1", "127.0.0.2"}, + {opStatement, 0, "127.0.0.1:8000", "127.0.0.2"}, {opPredict, 1000, "", ""}, - {opStatement, 1000, "127.0.0.1", "127.0.0.3"}, + {opStatement, 1000, "127.0.0.1:8000", "127.0.0.3"}, {opPredict, 1000, "", ""}, - {opStatement, 1000, "127.0.0.1", "127.0.0.4"}, - {opPredict, 1000, "127.0.0.1", ""}, + {opStatement, 1000, "127.0.0.1:8000", "127.0.0.4"}, + {opPredict, 1000, "127.0.0.1:8000", ""}, }, "window": { - {opStatement, 0, "127.0.0.1", "127.0.0.2"}, - {opStatement, 2000, "127.0.0.1", "127.0.0.3"}, - {opStatement, 3000, "127.0.0.1", "127.0.0.4"}, - {opPredict, 10000, "127.0.0.1", ""}, + {opStatement, 0, "127.0.0.1:8000", "127.0.0.2"}, + {opStatement, 2000, "127.0.0.1:8000", "127.0.0.3"}, + {opStatement, 3000, "127.0.0.1:8000", "127.0.0.4"}, + {opPredict, 10000, "127.0.0.1:8000", ""}, {opPredict, 10001, "", ""}, // first statement expired - {opStatement, 10100, "127.0.0.1", "127.0.0.2"}, - {opPredict, 10200, "127.0.0.1", ""}, + {opStatement, 10100, "127.0.0.1:8000", "127.0.0.2"}, + {opPredict, 10200, "127.0.0.1:8000", ""}, }, "fullcone": { {opContact, 0, "", "127.0.0.2"}, - {opStatement, 10, "127.0.0.1", "127.0.0.2"}, + {opStatement, 10, "127.0.0.1:8000", "127.0.0.2"}, {opContact, 2000, "", "127.0.0.3"}, - {opStatement, 2010, "127.0.0.1", "127.0.0.3"}, + {opStatement, 2010, "127.0.0.1:8000", "127.0.0.3"}, {opContact, 3000, "", "127.0.0.4"}, - {opStatement, 3010, "127.0.0.1", "127.0.0.4"}, + {opStatement, 3010, "127.0.0.1:8000", "127.0.0.4"}, {opCheckFullCone, 3500, "false", ""}, }, "fullcone_2": { {opContact, 0, "", "127.0.0.2"}, - {opStatement, 10, "127.0.0.1", "127.0.0.2"}, + {opStatement, 10, "127.0.0.1:8000", "127.0.0.2"}, {opContact, 2000, "", "127.0.0.3"}, - {opStatement, 2010, "127.0.0.1", "127.0.0.3"}, - {opStatement, 3000, "127.0.0.1", "127.0.0.4"}, + {opStatement, 2010, "127.0.0.1:8000", "127.0.0.3"}, + {opStatement, 3000, "127.0.0.1:8000", "127.0.0.4"}, {opContact, 3010, "", "127.0.0.4"}, {opCheckFullCone, 3500, "true", ""}, }, @@ -93,12 +94,19 @@ func runIPTrackerTest(t *testing.T, evs []iptrackTestEvent) { clock.Run(evtime - time.Duration(clock.Now())) switch ev.op { case opStatement: - it.AddStatement(ev.from, ev.ip) + it.AddStatement(netip.MustParseAddr(ev.from), netip.MustParseAddrPort(ev.ip)) case opContact: - it.AddContact(ev.from) + it.AddContact(netip.MustParseAddr(ev.from)) case opPredict: - if pred := it.PredictEndpoint(); pred != ev.ip { - t.Errorf("op %d: wrong prediction %q, want %q", i, pred, ev.ip) + pred := it.PredictEndpoint() + if ev.ip == "" { + if pred.IsValid() { + t.Errorf("op %d: wrong prediction %v, expected invalid", i, pred) + } + } else { + if pred != netip.MustParseAddrPort(ev.ip) { + t.Errorf("op %d: wrong prediction %v, want %q", i, pred, ev.ip) + } } case opCheckFullCone: pred := fmt.Sprintf("%t", it.PredictFullConeNAT()) @@ -121,12 +129,11 @@ func TestIPTrackerForceGC(t *testing.T) { it.clock = &clock for i := 0; i < 5*max; i++ { - e1 := make([]byte, 4) - e2 := make([]byte, 4) - crand.Read(e1) - crand.Read(e2) - it.AddStatement(string(e1), string(e2)) - it.AddContact(string(e1)) + var e1, e2 [4]byte + crand.Read(e1[:]) + crand.Read(e2[:]) + it.AddStatement(netip.AddrFrom4(e1), netip.AddrPortFrom(netip.AddrFrom4(e2), 9000)) + it.AddContact(netip.AddrFrom4(e1)) clock.Run(rate) } if len(it.contact) > 2*max { diff --git a/p2p/netutil/net.go b/p2p/netutil/net.go index d5da3c694f..7d8da88670 100644 --- a/p2p/netutil/net.go +++ b/p2p/netutil/net.go @@ -22,21 +22,19 @@ import ( "errors" "fmt" "net" - "sort" + "net/netip" + "slices" "strings" + + "golang.org/x/exp/maps" ) -var lan4, lan6, special4, special6 Netlist +var special4, special6 Netlist func init() { // Lists from RFC 5735, RFC 5156, // https://www.iana.org/assignments/iana-ipv4-special-registry/ - lan4.Add("0.0.0.0/8") // "This" network - lan4.Add("10.0.0.0/8") // Private Use - lan4.Add("172.16.0.0/12") // Private Use - lan4.Add("192.168.0.0/16") // Private Use - lan6.Add("fe80::/10") // Link-Local - lan6.Add("fc00::/7") // Unique-Local + special4.Add("0.0.0.0/8") // "This" network. special4.Add("192.0.0.0/29") // IPv4 Service Continuity special4.Add("192.0.0.9/32") // PCP Anycast special4.Add("192.0.0.170/32") // NAT64/DNS64 Discovery @@ -66,7 +64,7 @@ func init() { } // Netlist is a list of IP networks. -type Netlist []net.IPNet +type Netlist []netip.Prefix // ParseNetlist parses a comma-separated list of CIDR masks. // Whitespace and extra commas are ignored. @@ -78,11 +76,11 @@ func ParseNetlist(s string) (*Netlist, error) { if mask == "" { continue } - _, n, err := net.ParseCIDR(mask) + prefix, err := netip.ParsePrefix(mask) if err != nil { return nil, err } - l = append(l, *n) + l = append(l, prefix) } return &l, nil } @@ -103,11 +101,11 @@ func (l *Netlist) UnmarshalTOML(fn func(interface{}) error) error { return err } for _, mask := range masks { - _, n, err := net.ParseCIDR(mask) + prefix, err := netip.ParsePrefix(mask) if err != nil { return err } - *l = append(*l, *n) + *l = append(*l, prefix) } return nil } @@ -115,15 +113,20 @@ func (l *Netlist) UnmarshalTOML(fn func(interface{}) error) error { // Add parses a CIDR mask and appends it to the list. It panics for invalid masks and is // intended to be used for setting up static lists. func (l *Netlist) Add(cidr string) { - _, n, err := net.ParseCIDR(cidr) + prefix, err := netip.ParsePrefix(cidr) if err != nil { panic(err) } - *l = append(*l, *n) + *l = append(*l, prefix) } // Contains reports whether the given IP is contained in the list. func (l *Netlist) Contains(ip net.IP) bool { + return l.ContainsAddr(IPToAddr(ip)) +} + +// ContainsAddr reports whether the given IP is contained in the list. +func (l *Netlist) ContainsAddr(ip netip.Addr) bool { if l == nil { return false } @@ -137,25 +140,39 @@ func (l *Netlist) Contains(ip net.IP) bool { // IsLAN reports whether an IP is a local network address. func IsLAN(ip net.IP) bool { + return AddrIsLAN(IPToAddr(ip)) +} + +// AddrIsLAN reports whether an IP is a local network address. +func AddrIsLAN(ip netip.Addr) bool { + if ip.Is4In6() { + ip = netip.AddrFrom4(ip.As4()) + } if ip.IsLoopback() { return true } - if v4 := ip.To4(); v4 != nil { - return lan4.Contains(v4) - } - return lan6.Contains(ip) + return ip.IsPrivate() || ip.IsLinkLocalUnicast() } // IsSpecialNetwork reports whether an IP is located in a special-use network range // This includes broadcast, multicast and documentation addresses. func IsSpecialNetwork(ip net.IP) bool { + return AddrIsSpecialNetwork(IPToAddr(ip)) +} + +// AddrIsSpecialNetwork reports whether an IP is located in a special-use network range +// This includes broadcast, multicast and documentation addresses. +func AddrIsSpecialNetwork(ip netip.Addr) bool { + if ip.Is4In6() { + ip = netip.AddrFrom4(ip.As4()) + } if ip.IsMulticast() { return true } - if v4 := ip.To4(); v4 != nil { - return special4.Contains(v4) + if ip.Is4() { + return special4.ContainsAddr(ip) } - return special6.Contains(ip) + return special6.ContainsAddr(ip) } var ( @@ -175,19 +192,31 @@ var ( // - LAN addresses are OK if relayed by a LAN host. // - All other addresses are always acceptable. func CheckRelayIP(sender, addr net.IP) error { - if len(addr) != net.IPv4len && len(addr) != net.IPv6len { + return CheckRelayAddr(IPToAddr(sender), IPToAddr(addr)) +} + +// CheckRelayAddr reports whether an IP relayed from the given sender IP +// is a valid connection target. +// +// There are four rules: +// - Special network addresses are never valid. +// - Loopback addresses are OK if relayed by a loopback host. +// - LAN addresses are OK if relayed by a LAN host. +// - All other addresses are always acceptable. +func CheckRelayAddr(sender, addr netip.Addr) error { + if !addr.IsValid() { return errInvalid } if addr.IsUnspecified() { return errUnspecified } - if IsSpecialNetwork(addr) { + if AddrIsSpecialNetwork(addr) { return errSpecial } if addr.IsLoopback() && !sender.IsLoopback() { return errLoopback } - if IsLAN(addr) && !IsLAN(sender) { + if AddrIsLAN(addr) && !AddrIsLAN(sender) { return errLAN } return nil @@ -221,17 +250,22 @@ type DistinctNetSet struct { Subnet uint // number of common prefix bits Limit uint // maximum number of IPs in each subnet - members map[string]uint - buf net.IP + members map[netip.Prefix]uint } // Add adds an IP address to the set. It returns false (and doesn't add the IP) if the // number of existing IPs in the defined range exceeds the limit. func (s *DistinctNetSet) Add(ip net.IP) bool { + return s.AddAddr(IPToAddr(ip)) +} + +// AddAddr adds an IP address to the set. It returns false (and doesn't add the IP) if the +// number of existing IPs in the defined range exceeds the limit. +func (s *DistinctNetSet) AddAddr(ip netip.Addr) bool { key := s.key(ip) - n := s.members[string(key)] + n := s.members[key] if n < s.Limit { - s.members[string(key)] = n + 1 + s.members[key] = n + 1 return true } return false @@ -239,20 +273,30 @@ func (s *DistinctNetSet) Add(ip net.IP) bool { // Remove removes an IP from the set. func (s *DistinctNetSet) Remove(ip net.IP) { + s.RemoveAddr(IPToAddr(ip)) +} + +// RemoveAddr removes an IP from the set. +func (s *DistinctNetSet) RemoveAddr(ip netip.Addr) { key := s.key(ip) - if n, ok := s.members[string(key)]; ok { + if n, ok := s.members[key]; ok { if n == 1 { - delete(s.members, string(key)) + delete(s.members, key) } else { - s.members[string(key)] = n - 1 + s.members[key] = n - 1 } } } -// Contains whether the given IP is contained in the set. +// Contains reports whether the given IP is contained in the set. func (s DistinctNetSet) Contains(ip net.IP) bool { + return s.ContainsAddr(IPToAddr(ip)) +} + +// ContainsAddr reports whether the given IP is contained in the set. +func (s DistinctNetSet) ContainsAddr(ip netip.Addr) bool { key := s.key(ip) - _, ok := s.members[string(key)] + _, ok := s.members[key] return ok } @@ -265,54 +309,30 @@ func (s DistinctNetSet) Len() int { return int(n) } -// key encodes the map key for an address into a temporary buffer. -// -// The first byte of key is '4' or '6' to distinguish IPv4/IPv6 address types. -// The remainder of the key is the IP, truncated to the number of bits. -func (s *DistinctNetSet) key(ip net.IP) net.IP { +// key returns the map key for ip. +func (s *DistinctNetSet) key(ip netip.Addr) netip.Prefix { // Lazily initialize storage. if s.members == nil { - s.members = make(map[string]uint) - s.buf = make(net.IP, 17) - } - // Canonicalize ip and bits. - typ := byte('6') - if ip4 := ip.To4(); ip4 != nil { - typ, ip = '4', ip4 + s.members = make(map[netip.Prefix]uint) } - bits := s.Subnet - if bits > uint(len(ip)*8) { - bits = uint(len(ip) * 8) - } - // Encode the prefix into s.buf. - nb := int(bits / 8) - mask := ^byte(0xFF >> (bits % 8)) - s.buf[0] = typ - buf := append(s.buf[:1], ip[:nb]...) - if nb < len(ip) && mask != 0 { - buf = append(buf, ip[nb]&mask) + p, err := ip.Prefix(int(s.Subnet)) + if err != nil { + panic(err) } - return buf + return p } // String implements fmt.Stringer func (s DistinctNetSet) String() string { + keys := maps.Keys(s.members) + slices.SortFunc(keys, func(a, b netip.Prefix) int { + return strings.Compare(a.String(), b.String()) + }) + var buf bytes.Buffer buf.WriteString("{") - keys := make([]string, 0, len(s.members)) - for k := range s.members { - keys = append(keys, k) - } - sort.Strings(keys) for i, k := range keys { - var ip net.IP - if k[0] == '4' { - ip = make(net.IP, 4) - } else { - ip = make(net.IP, 16) - } - copy(ip, k[1:]) - fmt.Fprintf(&buf, "%v×%d", ip, s.members[k]) + fmt.Fprintf(&buf, "%v×%d", k, s.members[k]) if i != len(keys)-1 { buf.WriteString(" ") } diff --git a/p2p/netutil/net_test.go b/p2p/netutil/net_test.go index 3a6aa081f2..569c7ac454 100644 --- a/p2p/netutil/net_test.go +++ b/p2p/netutil/net_test.go @@ -18,7 +18,9 @@ package netutil import ( "fmt" + "math/rand" "net" + "net/netip" "reflect" "testing" "testing/quick" @@ -29,7 +31,7 @@ import ( func TestParseNetlist(t *testing.T) { var tests = []struct { input string - wantErr error + wantErr string wantList *Netlist }{ { @@ -38,25 +40,27 @@ func TestParseNetlist(t *testing.T) { }, { input: "127.0.0.0/8", - wantErr: nil, - wantList: &Netlist{{IP: net.IP{127, 0, 0, 0}, Mask: net.CIDRMask(8, 32)}}, + wantList: &Netlist{netip.MustParsePrefix("127.0.0.0/8")}, }, { input: "127.0.0.0/44", - wantErr: &net.ParseError{Type: "CIDR address", Text: "127.0.0.0/44"}, + wantErr: `netip.ParsePrefix("127.0.0.0/44"): prefix length out of range`, }, { input: "127.0.0.0/16, 23.23.23.23/24,", wantList: &Netlist{ - {IP: net.IP{127, 0, 0, 0}, Mask: net.CIDRMask(16, 32)}, - {IP: net.IP{23, 23, 23, 0}, Mask: net.CIDRMask(24, 32)}, + netip.MustParsePrefix("127.0.0.0/16"), + netip.MustParsePrefix("23.23.23.23/24"), }, }, } for _, test := range tests { l, err := ParseNetlist(test.input) - if !reflect.DeepEqual(err, test.wantErr) { + if err == nil && test.wantErr != "" { + t.Errorf("%q: got no error, expected %q", test.input, test.wantErr) + continue + } else if err != nil && err.Error() != test.wantErr { t.Errorf("%q: got error %q, want %q", test.input, err, test.wantErr) continue } @@ -70,14 +74,12 @@ func TestParseNetlist(t *testing.T) { func TestNilNetListContains(t *testing.T) { var list *Netlist - checkContains(t, list.Contains, nil, []string{"1.2.3.4"}) + checkContains(t, list.Contains, list.ContainsAddr, nil, []string{"1.2.3.4"}) } func TestIsLAN(t *testing.T) { - checkContains(t, IsLAN, + checkContains(t, IsLAN, AddrIsLAN, []string{ // included - "0.0.0.0", - "0.2.0.8", "127.0.0.1", "10.0.1.1", "10.22.0.3", @@ -86,25 +88,35 @@ func TestIsLAN(t *testing.T) { "fe80::f4a1:8eff:fec5:9d9d", "febf::ab32:2233", "fc00::4", + // 4-in-6 + "::ffff:127.0.0.1", + "::ffff:10.10.0.2", }, []string{ // excluded "192.0.2.1", "1.0.0.0", "172.32.0.1", "fec0::2233", + // 4-in-6 + "::ffff:88.99.100.2", }, ) } func TestIsSpecialNetwork(t *testing.T) { - checkContains(t, IsSpecialNetwork, + checkContains(t, IsSpecialNetwork, AddrIsSpecialNetwork, []string{ // included + "0.0.0.0", + "0.2.0.8", "192.0.2.1", "192.0.2.44", "2001:db8:85a3:8d3:1319:8a2e:370:7348", "255.255.255.255", "224.0.0.22", // IPv4 multicast "ff05::1:3", // IPv6 multicast + // 4-in-6 + "::ffff:255.255.255.255", + "::ffff:192.0.2.1", }, []string{ // excluded "192.0.3.1", @@ -115,15 +127,21 @@ func TestIsSpecialNetwork(t *testing.T) { ) } -func checkContains(t *testing.T, fn func(net.IP) bool, inc, exc []string) { +func checkContains(t *testing.T, fn func(net.IP) bool, fn2 func(netip.Addr) bool, inc, exc []string) { for _, s := range inc { if !fn(parseIP(s)) { - t.Error("returned false for included address", s) + t.Error("returned false for included net.IP", s) + } + if !fn2(netip.MustParseAddr(s)) { + t.Error("returned false for included netip.Addr", s) } } for _, s := range exc { if fn(parseIP(s)) { - t.Error("returned true for excluded address", s) + t.Error("returned true for excluded net.IP", s) + } + if fn2(netip.MustParseAddr(s)) { + t.Error("returned true for excluded netip.Addr", s) } } } @@ -244,14 +262,22 @@ func TestDistinctNetSet(t *testing.T) { } func TestDistinctNetSetAddRemove(t *testing.T) { - cfg := &quick.Config{} - fn := func(ips []net.IP) bool { + cfg := &quick.Config{ + Values: func(s []reflect.Value, rng *rand.Rand) { + slice := make([]netip.Addr, rng.Intn(20)+1) + for i := range slice { + slice[i] = RandomAddr(rng, false) + } + s[0] = reflect.ValueOf(slice) + }, + } + fn := func(ips []netip.Addr) bool { s := DistinctNetSet{Limit: 3, Subnet: 2} for _, ip := range ips { - s.Add(ip) + s.AddAddr(ip) } for _, ip := range ips { - s.Remove(ip) + s.RemoveAddr(ip) } return s.Len() == 0 } diff --git a/p2p/nodestate/nodestate.go b/p2p/nodestate/nodestate.go deleted file mode 100644 index 8052144465..0000000000 --- a/p2p/nodestate/nodestate.go +++ /dev/null @@ -1,1023 +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 . - -package nodestate - -import ( - "errors" - "reflect" - "sync" - "time" - "unsafe" - - "github.com/ethereum/go-ethereum/common/mclock" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/metrics" - "github.com/ethereum/go-ethereum/p2p/enode" - "github.com/ethereum/go-ethereum/p2p/enr" - "github.com/ethereum/go-ethereum/rlp" -) - -var ( - ErrInvalidField = errors.New("invalid field type") - ErrClosed = errors.New("already closed") -) - -type ( - // NodeStateMachine implements a network node-related event subscription system. - // It can assign binary state flags and fields of arbitrary type to each node and allows - // subscriptions to flag/field changes which can also modify further flags and fields, - // potentially triggering further subscriptions. An operation includes an initial change - // and all resulting subsequent changes and always ends in a consistent global state. - // It is initiated by a "top level" SetState/SetField call that blocks (also blocking other - // top-level functions) until the operation is finished. Callbacks making further changes - // should use the non-blocking SetStateSub/SetFieldSub functions. The tree of events - // resulting from the initial changes is traversed in a breadth-first order, ensuring for - // each subscription callback that all other callbacks caused by the same change triggering - // the current callback are processed before anything is triggered by the changes made in the - // current callback. In practice this logic ensures that all subscriptions "see" events in - // the logical order, callbacks are never called concurrently and "back and forth" effects - // are also possible. The state machine design should ensure that infinite event cycles - // cannot happen. - // The caller can also add timeouts assigned to a certain node and a subset of state flags. - // If the timeout elapses, the flags are reset. If all relevant flags are reset then the timer - // is dropped. State flags with no timeout are persisted in the database if the flag - // descriptor enables saving. If a node has no state flags set at any moment then it is discarded. - // Note: in order to avoid mutex deadlocks the callbacks should never lock a mutex that - // might be locked when the top level SetState/SetField functions are called. If a function - // potentially performs state/field changes then it is recommended to mention this fact in the - // function description, along with whether it should run inside an operation callback. - NodeStateMachine struct { - started, closed bool - lock sync.Mutex - clock mclock.Clock - db ethdb.KeyValueStore - dbNodeKey []byte - nodes map[enode.ID]*nodeInfo - offlineCallbackList []offlineCallback - opFlag bool // an operation has started - opWait *sync.Cond // signaled when the operation ends - opPending []func() // pending callback list of the current operation - - // Registered state flags or fields. Modifications are allowed - // only when the node state machine has not been started. - setup *Setup - fields []*fieldInfo - saveFlags bitMask - - // Installed callbacks. Modifications are allowed only when the - // node state machine has not been started. - stateSubs []stateSub - - // Testing hooks, only for testing purposes. - saveNodeHook func(*nodeInfo) - } - - // Flags represents a set of flags from a certain setup - Flags struct { - mask bitMask - setup *Setup - } - - // Field represents a field from a certain setup - Field struct { - index int - setup *Setup - } - - // flagDefinition describes a node state flag. Each registered instance is automatically - // mapped to a bit of the 64 bit node states. - // If persistent is true then the node is saved when state machine is shutdown. - flagDefinition struct { - name string - persistent bool - } - - // fieldDefinition describes an optional node field of the given type. The contents - // of the field are only retained for each node as long as at least one of the - // state flags is set. - fieldDefinition struct { - name string - ftype reflect.Type - encode func(interface{}) ([]byte, error) - decode func([]byte) (interface{}, error) - } - - // Setup contains the list of flags and fields used by the application - Setup struct { - Version uint - flags []flagDefinition - fields []fieldDefinition - } - - // bitMask describes a node state or state mask. It represents a subset - // of node flags with each bit assigned to a flag index (LSB represents flag 0). - bitMask uint64 - - // StateCallback is a subscription callback which is called when one of the - // state flags that is included in the subscription state mask is changed. - // Note: oldState and newState are also masked with the subscription mask so only - // the relevant bits are included. - StateCallback func(n *enode.Node, oldState, newState Flags) - - // FieldCallback is a subscription callback which is called when the value of - // a specific field is changed. - FieldCallback func(n *enode.Node, state Flags, oldValue, newValue interface{}) - - // nodeInfo contains node state, fields and state timeouts - nodeInfo struct { - node *enode.Node - state bitMask - timeouts []*nodeStateTimeout - fields []interface{} - fieldCount int - db, dirty bool - } - - nodeInfoEnc struct { - Enr enr.Record - Version uint - State bitMask - Fields [][]byte - } - - stateSub struct { - mask bitMask - callback StateCallback - } - - nodeStateTimeout struct { - mask bitMask - timer mclock.Timer - } - - fieldInfo struct { - fieldDefinition - subs []FieldCallback - } - - offlineCallback struct { - node *nodeInfo - state bitMask - fields []interface{} - } -) - -// offlineState is a special state that is assumed to be set before a node is loaded from -// the database and after it is shut down. -const offlineState = bitMask(1) - -// NewFlag creates a new node state flag -func (s *Setup) NewFlag(name string) Flags { - if s.flags == nil { - s.flags = []flagDefinition{{name: "offline"}} - } - f := Flags{mask: bitMask(1) << uint(len(s.flags)), setup: s} - s.flags = append(s.flags, flagDefinition{name: name}) - return f -} - -// NewPersistentFlag creates a new persistent node state flag -func (s *Setup) NewPersistentFlag(name string) Flags { - if s.flags == nil { - s.flags = []flagDefinition{{name: "offline"}} - } - f := Flags{mask: bitMask(1) << uint(len(s.flags)), setup: s} - s.flags = append(s.flags, flagDefinition{name: name, persistent: true}) - return f -} - -// OfflineFlag returns the system-defined offline flag belonging to the given setup -func (s *Setup) OfflineFlag() Flags { - return Flags{mask: offlineState, setup: s} -} - -// NewField creates a new node state field -func (s *Setup) NewField(name string, ftype reflect.Type) Field { - f := Field{index: len(s.fields), setup: s} - s.fields = append(s.fields, fieldDefinition{ - name: name, - ftype: ftype, - }) - return f -} - -// NewPersistentField creates a new persistent node field -func (s *Setup) NewPersistentField(name string, ftype reflect.Type, encode func(interface{}) ([]byte, error), decode func([]byte) (interface{}, error)) Field { - f := Field{index: len(s.fields), setup: s} - s.fields = append(s.fields, fieldDefinition{ - name: name, - ftype: ftype, - encode: encode, - decode: decode, - }) - return f -} - -// flagOp implements binary flag operations and also checks whether the operands belong to the same setup -func flagOp(a, b Flags, trueIfA, trueIfB, trueIfBoth bool) Flags { - if a.setup == nil { - if a.mask != 0 { - panic("Node state flags have no setup reference") - } - a.setup = b.setup - } - if b.setup == nil { - if b.mask != 0 { - panic("Node state flags have no setup reference") - } - b.setup = a.setup - } - if a.setup != b.setup { - panic("Node state flags belong to a different setup") - } - res := Flags{setup: a.setup} - if trueIfA { - res.mask |= a.mask & ^b.mask - } - if trueIfB { - res.mask |= b.mask & ^a.mask - } - if trueIfBoth { - res.mask |= a.mask & b.mask - } - return res -} - -// And returns the set of flags present in both a and b -func (a Flags) And(b Flags) Flags { return flagOp(a, b, false, false, true) } - -// AndNot returns the set of flags present in a but not in b -func (a Flags) AndNot(b Flags) Flags { return flagOp(a, b, true, false, false) } - -// Or returns the set of flags present in either a or b -func (a Flags) Or(b Flags) Flags { return flagOp(a, b, true, true, true) } - -// Xor returns the set of flags present in either a or b but not both -func (a Flags) Xor(b Flags) Flags { return flagOp(a, b, true, true, false) } - -// HasAll returns true if b is a subset of a -func (a Flags) HasAll(b Flags) bool { return flagOp(a, b, false, true, false).mask == 0 } - -// HasNone returns true if a and b have no shared flags -func (a Flags) HasNone(b Flags) bool { return flagOp(a, b, false, false, true).mask == 0 } - -// Equals returns true if a and b have the same flags set -func (a Flags) Equals(b Flags) bool { return flagOp(a, b, true, true, false).mask == 0 } - -// IsEmpty returns true if a has no flags set -func (a Flags) IsEmpty() bool { return a.mask == 0 } - -// MergeFlags merges multiple sets of state flags -func MergeFlags(list ...Flags) Flags { - if len(list) == 0 { - return Flags{} - } - res := list[0] - for i := 1; i < len(list); i++ { - res = res.Or(list[i]) - } - return res -} - -// String returns a list of the names of the flags specified in the bit mask -func (f Flags) String() string { - if f.mask == 0 { - return "[]" - } - s := "[" - comma := false - for index, flag := range f.setup.flags { - if f.mask&(bitMask(1)< 8*int(unsafe.Sizeof(bitMask(0))) { - panic("Too many node state flags") - } - ns := &NodeStateMachine{ - db: db, - dbNodeKey: dbKey, - clock: clock, - setup: setup, - nodes: make(map[enode.ID]*nodeInfo), - fields: make([]*fieldInfo, len(setup.fields)), - } - ns.opWait = sync.NewCond(&ns.lock) - stateNameMap := make(map[string]int, len(setup.flags)) - for index, flag := range setup.flags { - if _, ok := stateNameMap[flag.name]; ok { - panic("Node state flag name collision: " + flag.name) - } - stateNameMap[flag.name] = index - if flag.persistent { - ns.saveFlags |= bitMask(1) << uint(index) - } - } - fieldNameMap := make(map[string]int, len(setup.fields)) - for index, field := range setup.fields { - if _, ok := fieldNameMap[field.name]; ok { - panic("Node field name collision: " + field.name) - } - ns.fields[index] = &fieldInfo{fieldDefinition: field} - fieldNameMap[field.name] = index - } - return ns -} - -// stateMask checks whether the set of flags belongs to the same setup and returns its internal bit mask -func (ns *NodeStateMachine) stateMask(flags Flags) bitMask { - if flags.setup != ns.setup && flags.mask != 0 { - panic("Node state flags belong to a different setup") - } - return flags.mask -} - -// fieldIndex checks whether the field belongs to the same setup and returns its internal index -func (ns *NodeStateMachine) fieldIndex(field Field) int { - if field.setup != ns.setup { - panic("Node field belongs to a different setup") - } - return field.index -} - -// SubscribeState adds a node state subscription. The callback is called while the state -// machine mutex is not held and it is allowed to make further state updates using the -// non-blocking SetStateSub/SetFieldSub functions. All callbacks of an operation are running -// from the thread/goroutine of the initial caller and parallel operations are not permitted. -// Therefore the callback is never called concurrently. It is the responsibility of the -// implemented state logic to avoid deadlocks and to reach a stable state in a finite amount -// of steps. -// State subscriptions should be installed before loading the node database or making the -// first state update. -func (ns *NodeStateMachine) SubscribeState(flags Flags, callback StateCallback) { - ns.lock.Lock() - defer ns.lock.Unlock() - - if ns.started { - panic("state machine already started") - } - ns.stateSubs = append(ns.stateSubs, stateSub{ns.stateMask(flags), callback}) -} - -// SubscribeField adds a node field subscription. Same rules apply as for SubscribeState. -func (ns *NodeStateMachine) SubscribeField(field Field, callback FieldCallback) { - ns.lock.Lock() - defer ns.lock.Unlock() - - if ns.started { - panic("state machine already started") - } - f := ns.fields[ns.fieldIndex(field)] - f.subs = append(f.subs, callback) -} - -// newNode creates a new nodeInfo -func (ns *NodeStateMachine) newNode(n *enode.Node) *nodeInfo { - return &nodeInfo{node: n, fields: make([]interface{}, len(ns.fields))} -} - -// checkStarted checks whether the state machine has already been started and panics otherwise. -func (ns *NodeStateMachine) checkStarted() { - if !ns.started { - panic("state machine not started yet") - } -} - -// Start starts the state machine, enabling state and field operations and disabling -// further subscriptions. -func (ns *NodeStateMachine) Start() { - ns.lock.Lock() - if ns.started { - panic("state machine already started") - } - ns.started = true - if ns.db != nil { - ns.loadFromDb() - } - - ns.opStart() - ns.offlineCallbacks(true) - ns.opFinish() - ns.lock.Unlock() -} - -// Stop stops the state machine and saves its state if a database was supplied -func (ns *NodeStateMachine) Stop() { - ns.lock.Lock() - defer ns.lock.Unlock() - - ns.checkStarted() - if !ns.opStart() { - panic("already closed") - } - for _, node := range ns.nodes { - fields := make([]interface{}, len(node.fields)) - copy(fields, node.fields) - ns.offlineCallbackList = append(ns.offlineCallbackList, offlineCallback{node, node.state, fields}) - } - if ns.db != nil { - ns.saveToDb() - } - ns.offlineCallbacks(false) - ns.closed = true - ns.opFinish() -} - -// loadFromDb loads persisted node states from the database -func (ns *NodeStateMachine) loadFromDb() { - it := ns.db.NewIterator(ns.dbNodeKey, nil) - for it.Next() { - var id enode.ID - if len(it.Key()) != len(ns.dbNodeKey)+len(id) { - log.Error("Node state db entry with invalid length", "found", len(it.Key()), "expected", len(ns.dbNodeKey)+len(id)) - continue - } - copy(id[:], it.Key()[len(ns.dbNodeKey):]) - ns.decodeNode(id, it.Value()) - } -} - -type dummyIdentity enode.ID - -func (id dummyIdentity) Verify(r *enr.Record, sig []byte) error { return nil } -func (id dummyIdentity) NodeAddr(r *enr.Record) []byte { return id[:] } - -// decodeNode decodes a node database entry and adds it to the node set if successful -func (ns *NodeStateMachine) decodeNode(id enode.ID, data []byte) { - var enc nodeInfoEnc - if err := rlp.DecodeBytes(data, &enc); err != nil { - log.Error("Failed to decode node info", "id", id, "error", err) - return - } - n, _ := enode.New(dummyIdentity(id), &enc.Enr) - node := ns.newNode(n) - node.db = true - - if enc.Version != ns.setup.Version { - log.Debug("Removing stored node with unknown version", "current", ns.setup.Version, "stored", enc.Version) - ns.deleteNode(id) - return - } - if len(enc.Fields) > len(ns.setup.fields) { - log.Error("Invalid node field count", "id", id, "stored", len(enc.Fields)) - return - } - // Resolve persisted node fields - for i, encField := range enc.Fields { - if len(encField) == 0 { - continue - } - if decode := ns.fields[i].decode; decode != nil { - if field, err := decode(encField); err == nil { - node.fields[i] = field - node.fieldCount++ - } else { - log.Error("Failed to decode node field", "id", id, "field name", ns.fields[i].name, "error", err) - return - } - } else { - log.Error("Cannot decode node field", "id", id, "field name", ns.fields[i].name) - return - } - } - // It's a compatible node record, add it to set. - ns.nodes[id] = node - node.state = enc.State - fields := make([]interface{}, len(node.fields)) - copy(fields, node.fields) - ns.offlineCallbackList = append(ns.offlineCallbackList, offlineCallback{node, node.state, fields}) - log.Debug("Loaded node state", "id", id, "state", Flags{mask: enc.State, setup: ns.setup}) -} - -// saveNode saves the given node info to the database -func (ns *NodeStateMachine) saveNode(id enode.ID, node *nodeInfo) error { - if ns.db == nil { - return nil - } - - storedState := node.state & ns.saveFlags - for _, t := range node.timeouts { - storedState &= ^t.mask - } - enc := nodeInfoEnc{ - Enr: *node.node.Record(), - Version: ns.setup.Version, - State: storedState, - Fields: make([][]byte, len(ns.fields)), - } - log.Debug("Saved node state", "id", id, "state", Flags{mask: enc.State, setup: ns.setup}) - lastIndex := -1 - for i, f := range node.fields { - if f == nil { - continue - } - encode := ns.fields[i].encode - if encode == nil { - continue - } - blob, err := encode(f) - if err != nil { - return err - } - enc.Fields[i] = blob - lastIndex = i - } - if storedState == 0 && lastIndex == -1 { - if node.db { - node.db = false - ns.deleteNode(id) - } - node.dirty = false - return nil - } - enc.Fields = enc.Fields[:lastIndex+1] - data, err := rlp.EncodeToBytes(&enc) - if err != nil { - return err - } - if err := ns.db.Put(append(ns.dbNodeKey, id[:]...), data); err != nil { - return err - } - node.dirty, node.db = false, true - - if ns.saveNodeHook != nil { - ns.saveNodeHook(node) - } - return nil -} - -// deleteNode removes a node info from the database -func (ns *NodeStateMachine) deleteNode(id enode.ID) { - ns.db.Delete(append(ns.dbNodeKey, id[:]...)) -} - -// saveToDb saves the persistent flags and fields of all nodes that have been changed -func (ns *NodeStateMachine) saveToDb() { - for id, node := range ns.nodes { - if node.dirty { - err := ns.saveNode(id, node) - if err != nil { - log.Error("Failed to save node", "id", id, "error", err) - } - } - } -} - -// updateEnode updates the enode entry belonging to the given node if it already exists -func (ns *NodeStateMachine) updateEnode(n *enode.Node) (enode.ID, *nodeInfo) { - id := n.ID() - node := ns.nodes[id] - if node != nil && n.Seq() > node.node.Seq() { - node.node = n - node.dirty = true - } - return id, node -} - -// Persist saves the persistent state and fields of the given node immediately -func (ns *NodeStateMachine) Persist(n *enode.Node) error { - ns.lock.Lock() - defer ns.lock.Unlock() - - ns.checkStarted() - if id, node := ns.updateEnode(n); node != nil && node.dirty { - err := ns.saveNode(id, node) - if err != nil { - log.Error("Failed to save node", "id", id, "error", err) - } - return err - } - return nil -} - -// SetState updates the given node state flags and blocks until the operation is finished. -// If a flag with a timeout is set again, the operation removes or replaces the existing timeout. -func (ns *NodeStateMachine) SetState(n *enode.Node, setFlags, resetFlags Flags, timeout time.Duration) error { - ns.lock.Lock() - defer ns.lock.Unlock() - - if !ns.opStart() { - return ErrClosed - } - ns.setState(n, setFlags, resetFlags, timeout) - ns.opFinish() - return nil -} - -// SetStateSub updates the given node state flags without blocking (should be called -// from a subscription/operation callback). -func (ns *NodeStateMachine) SetStateSub(n *enode.Node, setFlags, resetFlags Flags, timeout time.Duration) { - ns.lock.Lock() - defer ns.lock.Unlock() - - ns.opCheck() - ns.setState(n, setFlags, resetFlags, timeout) -} - -func (ns *NodeStateMachine) setState(n *enode.Node, setFlags, resetFlags Flags, timeout time.Duration) { - ns.checkStarted() - set, reset := ns.stateMask(setFlags), ns.stateMask(resetFlags) - id, node := ns.updateEnode(n) - if node == nil { - if set == 0 { - return - } - node = ns.newNode(n) - ns.nodes[id] = node - } - oldState := node.state - newState := (node.state & (^reset)) | set - changed := oldState ^ newState - node.state = newState - - // Remove the timeout callbacks for all reset and set flags, - // even they are not existent(it's noop). - ns.removeTimeouts(node, set|reset) - - // Register the timeout callback if required - if timeout != 0 && set != 0 { - ns.addTimeout(n, set, timeout) - } - if newState == oldState { - return - } - if newState == 0 && node.fieldCount == 0 { - delete(ns.nodes, id) - if node.db { - ns.deleteNode(id) - } - } else { - if changed&ns.saveFlags != 0 { - node.dirty = true - } - } - callback := func() { - for _, sub := range ns.stateSubs { - if changed&sub.mask != 0 { - sub.callback(n, Flags{mask: oldState & sub.mask, setup: ns.setup}, Flags{mask: newState & sub.mask, setup: ns.setup}) - } - } - } - ns.opPending = append(ns.opPending, callback) -} - -// opCheck checks whether an operation is active -func (ns *NodeStateMachine) opCheck() { - if !ns.opFlag { - panic("Operation has not started") - } -} - -// opStart waits until other operations are finished and starts a new one -func (ns *NodeStateMachine) opStart() bool { - for ns.opFlag { - ns.opWait.Wait() - } - if ns.closed { - return false - } - ns.opFlag = true - return true -} - -// opFinish finishes the current operation by running all pending callbacks. -// Callbacks resulting from a state/field change performed in a previous callback are always -// put at the end of the pending list and therefore processed after all callbacks resulting -// from the previous state/field change. -func (ns *NodeStateMachine) opFinish() { - for len(ns.opPending) != 0 { - list := ns.opPending - ns.lock.Unlock() - for _, cb := range list { - cb() - } - ns.lock.Lock() - ns.opPending = ns.opPending[len(list):] - } - ns.opPending = nil - ns.opFlag = false - ns.opWait.Broadcast() -} - -// Operation calls the given function as an operation callback. This allows the caller -// to start an operation with multiple initial changes. The same rules apply as for -// subscription callbacks. -func (ns *NodeStateMachine) Operation(fn func()) error { - ns.lock.Lock() - started := ns.opStart() - ns.lock.Unlock() - if !started { - return ErrClosed - } - fn() - ns.lock.Lock() - ns.opFinish() - ns.lock.Unlock() - return nil -} - -// offlineCallbacks calls state update callbacks at startup or shutdown -func (ns *NodeStateMachine) offlineCallbacks(start bool) { - for _, cb := range ns.offlineCallbackList { - cb := cb - callback := func() { - for _, sub := range ns.stateSubs { - offState := offlineState & sub.mask - onState := cb.state & sub.mask - if offState == onState { - continue - } - if start { - sub.callback(cb.node.node, Flags{mask: offState, setup: ns.setup}, Flags{mask: onState, setup: ns.setup}) - } else { - sub.callback(cb.node.node, Flags{mask: onState, setup: ns.setup}, Flags{mask: offState, setup: ns.setup}) - } - } - for i, f := range cb.fields { - if f == nil || ns.fields[i].subs == nil { - continue - } - for _, fsub := range ns.fields[i].subs { - if start { - fsub(cb.node.node, Flags{mask: offlineState, setup: ns.setup}, nil, f) - } else { - fsub(cb.node.node, Flags{mask: offlineState, setup: ns.setup}, f, nil) - } - } - } - } - ns.opPending = append(ns.opPending, callback) - } - ns.offlineCallbackList = nil -} - -// AddTimeout adds a node state timeout associated to the given state flag(s). -// After the specified time interval, the relevant states will be reset. -func (ns *NodeStateMachine) AddTimeout(n *enode.Node, flags Flags, timeout time.Duration) error { - ns.lock.Lock() - defer ns.lock.Unlock() - - ns.checkStarted() - if ns.closed { - return ErrClosed - } - ns.addTimeout(n, ns.stateMask(flags), timeout) - return nil -} - -// addTimeout adds a node state timeout associated to the given state flag(s). -func (ns *NodeStateMachine) addTimeout(n *enode.Node, mask bitMask, timeout time.Duration) { - _, node := ns.updateEnode(n) - if node == nil { - return - } - mask &= node.state - if mask == 0 { - return - } - ns.removeTimeouts(node, mask) - t := &nodeStateTimeout{mask: mask} - t.timer = ns.clock.AfterFunc(timeout, func() { - ns.lock.Lock() - defer ns.lock.Unlock() - - if !ns.opStart() { - return - } - ns.setState(n, Flags{}, Flags{mask: t.mask, setup: ns.setup}, 0) - ns.opFinish() - }) - node.timeouts = append(node.timeouts, t) - if mask&ns.saveFlags != 0 { - node.dirty = true - } -} - -// removeTimeouts removes node state timeouts associated to the given state flag(s). -// If a timeout was associated to multiple flags which are not all included in the -// specified remove mask then only the included flags are de-associated and the timer -// stays active. -func (ns *NodeStateMachine) removeTimeouts(node *nodeInfo, mask bitMask) { - for i := 0; i < len(node.timeouts); i++ { - t := node.timeouts[i] - match := t.mask & mask - if match == 0 { - continue - } - t.mask -= match - if t.mask != 0 { - continue - } - t.timer.Stop() - node.timeouts[i] = node.timeouts[len(node.timeouts)-1] - node.timeouts = node.timeouts[:len(node.timeouts)-1] - i-- - if match&ns.saveFlags != 0 { - node.dirty = true - } - } -} - -// GetField retrieves the given field of the given node. Note that when used in a -// subscription callback the result can be out of sync with the state change represented -// by the callback parameters so extra safety checks might be necessary. -func (ns *NodeStateMachine) GetField(n *enode.Node, field Field) interface{} { - ns.lock.Lock() - defer ns.lock.Unlock() - - ns.checkStarted() - if ns.closed { - return nil - } - if _, node := ns.updateEnode(n); node != nil { - return node.fields[ns.fieldIndex(field)] - } - return nil -} - -// GetState retrieves the current state of the given node. Note that when used in a -// subscription callback the result can be out of sync with the state change represented -// by the callback parameters so extra safety checks might be necessary. -func (ns *NodeStateMachine) GetState(n *enode.Node) Flags { - ns.lock.Lock() - defer ns.lock.Unlock() - - ns.checkStarted() - if ns.closed { - return Flags{} - } - if _, node := ns.updateEnode(n); node != nil { - return Flags{mask: node.state, setup: ns.setup} - } - return Flags{} -} - -// SetField sets the given field of the given node and blocks until the operation is finished -func (ns *NodeStateMachine) SetField(n *enode.Node, field Field, value interface{}) error { - ns.lock.Lock() - defer ns.lock.Unlock() - - if !ns.opStart() { - return ErrClosed - } - err := ns.setField(n, field, value) - ns.opFinish() - return err -} - -// SetFieldSub sets the given field of the given node without blocking (should be called -// from a subscription/operation callback). -func (ns *NodeStateMachine) SetFieldSub(n *enode.Node, field Field, value interface{}) error { - ns.lock.Lock() - defer ns.lock.Unlock() - - ns.opCheck() - return ns.setField(n, field, value) -} - -func (ns *NodeStateMachine) setField(n *enode.Node, field Field, value interface{}) error { - ns.checkStarted() - id, node := ns.updateEnode(n) - if node == nil { - if value == nil { - return nil - } - node = ns.newNode(n) - ns.nodes[id] = node - } - fieldIndex := ns.fieldIndex(field) - f := ns.fields[fieldIndex] - if value != nil && reflect.TypeOf(value) != f.ftype { - log.Error("Invalid field type", "type", reflect.TypeOf(value), "required", f.ftype) - return ErrInvalidField - } - oldValue := node.fields[fieldIndex] - if value == oldValue { - return nil - } - if oldValue != nil { - node.fieldCount-- - } - if value != nil { - node.fieldCount++ - } - node.fields[fieldIndex] = value - if node.state == 0 && node.fieldCount == 0 { - delete(ns.nodes, id) - if node.db { - ns.deleteNode(id) - } - } else { - if f.encode != nil { - node.dirty = true - } - } - state := node.state - callback := func() { - for _, cb := range f.subs { - cb(n, Flags{mask: state, setup: ns.setup}, oldValue, value) - } - } - ns.opPending = append(ns.opPending, callback) - return nil -} - -// ForEach calls the callback for each node having all of the required and none of the -// disabled flags set. -// Note that this callback is not an operation callback but ForEach can be called from an -// Operation callback or Operation can also be called from a ForEach callback if necessary. -func (ns *NodeStateMachine) ForEach(requireFlags, disableFlags Flags, cb func(n *enode.Node, state Flags)) { - ns.lock.Lock() - ns.checkStarted() - type callback struct { - node *enode.Node - state bitMask - } - require, disable := ns.stateMask(requireFlags), ns.stateMask(disableFlags) - var callbacks []callback - for _, node := range ns.nodes { - if node.state&require == require && node.state&disable == 0 { - callbacks = append(callbacks, callback{node.node, node.state & (require | disable)}) - } - } - ns.lock.Unlock() - for _, c := range callbacks { - cb(c.node, Flags{mask: c.state, setup: ns.setup}) - } -} - -// GetNode returns the enode currently associated with the given ID -func (ns *NodeStateMachine) GetNode(id enode.ID) *enode.Node { - ns.lock.Lock() - defer ns.lock.Unlock() - - ns.checkStarted() - if node := ns.nodes[id]; node != nil { - return node.node - } - return nil -} - -// AddLogMetrics adds logging and/or metrics for nodes entering, exiting and currently -// being in a given set specified by required and disabled state flags -func (ns *NodeStateMachine) AddLogMetrics(requireFlags, disableFlags Flags, name string, inMeter, outMeter metrics.Meter, gauge metrics.Gauge) { - var count int64 - ns.SubscribeState(requireFlags.Or(disableFlags), func(n *enode.Node, oldState, newState Flags) { - oldMatch := oldState.HasAll(requireFlags) && oldState.HasNone(disableFlags) - newMatch := newState.HasAll(requireFlags) && newState.HasNone(disableFlags) - if newMatch == oldMatch { - return - } - - if newMatch { - count++ - if name != "" { - log.Debug("Node entered", "set", name, "id", n.ID(), "count", count) - } - if inMeter != nil { - inMeter.Mark(1) - } - } else { - count-- - if name != "" { - log.Debug("Node left", "set", name, "id", n.ID(), "count", count) - } - if outMeter != nil { - outMeter.Mark(1) - } - } - if gauge != nil { - gauge.Update(count) - } - }) -} diff --git a/p2p/nodestate/nodestate_test.go b/p2p/nodestate/nodestate_test.go deleted file mode 100644 index d06ad755e2..0000000000 --- a/p2p/nodestate/nodestate_test.go +++ /dev/null @@ -1,407 +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 . - -package nodestate - -import ( - "errors" - "fmt" - "reflect" - "testing" - "time" - - "github.com/ethereum/go-ethereum/common/mclock" - "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/p2p/enode" - "github.com/ethereum/go-ethereum/p2p/enr" - "github.com/ethereum/go-ethereum/rlp" -) - -func testSetup(flagPersist []bool, fieldType []reflect.Type) (*Setup, []Flags, []Field) { - setup := &Setup{} - flags := make([]Flags, len(flagPersist)) - for i, persist := range flagPersist { - if persist { - flags[i] = setup.NewPersistentFlag(fmt.Sprintf("flag-%d", i)) - } else { - flags[i] = setup.NewFlag(fmt.Sprintf("flag-%d", i)) - } - } - fields := make([]Field, len(fieldType)) - for i, ftype := range fieldType { - switch ftype { - case reflect.TypeOf(uint64(0)): - fields[i] = setup.NewPersistentField(fmt.Sprintf("field-%d", i), ftype, uint64FieldEnc, uint64FieldDec) - case reflect.TypeOf(""): - fields[i] = setup.NewPersistentField(fmt.Sprintf("field-%d", i), ftype, stringFieldEnc, stringFieldDec) - default: - fields[i] = setup.NewField(fmt.Sprintf("field-%d", i), ftype) - } - } - return setup, flags, fields -} - -func testNode(b byte) *enode.Node { - r := &enr.Record{} - r.SetSig(dummyIdentity{b}, []byte{42}) - n, _ := enode.New(dummyIdentity{b}, r) - return n -} - -func TestCallback(t *testing.T) { - mdb, clock := rawdb.NewMemoryDatabase(), &mclock.Simulated{} - - s, flags, _ := testSetup([]bool{false, false, false}, nil) - ns := NewNodeStateMachine(mdb, []byte("-ns"), clock, s) - - set0 := make(chan struct{}, 1) - set1 := make(chan struct{}, 1) - set2 := make(chan struct{}, 1) - ns.SubscribeState(flags[0], func(n *enode.Node, oldState, newState Flags) { set0 <- struct{}{} }) - ns.SubscribeState(flags[1], func(n *enode.Node, oldState, newState Flags) { set1 <- struct{}{} }) - ns.SubscribeState(flags[2], func(n *enode.Node, oldState, newState Flags) { set2 <- struct{}{} }) - - ns.Start() - - ns.SetState(testNode(1), flags[0], Flags{}, 0) - ns.SetState(testNode(1), flags[1], Flags{}, time.Second) - ns.SetState(testNode(1), flags[2], Flags{}, 2*time.Second) - - for i := 0; i < 3; i++ { - select { - case <-set0: - case <-set1: - case <-set2: - case <-time.After(time.Second): - t.Fatalf("failed to invoke callback") - } - } -} - -func TestPersistentFlags(t *testing.T) { - mdb, clock := rawdb.NewMemoryDatabase(), &mclock.Simulated{} - - s, flags, _ := testSetup([]bool{true, true, true, false}, nil) - ns := NewNodeStateMachine(mdb, []byte("-ns"), clock, s) - - saveNode := make(chan *nodeInfo, 5) - ns.saveNodeHook = func(node *nodeInfo) { - saveNode <- node - } - - ns.Start() - - ns.SetState(testNode(1), flags[0], Flags{}, time.Second) // state with timeout should not be saved - ns.SetState(testNode(2), flags[1], Flags{}, 0) - ns.SetState(testNode(3), flags[2], Flags{}, 0) - ns.SetState(testNode(4), flags[3], Flags{}, 0) - ns.SetState(testNode(5), flags[0], Flags{}, 0) - ns.Persist(testNode(5)) - select { - case <-saveNode: - case <-time.After(time.Second): - t.Fatalf("Timeout") - } - ns.Stop() - - for i := 0; i < 2; i++ { - select { - case <-saveNode: - case <-time.After(time.Second): - t.Fatalf("Timeout") - } - } - select { - case <-saveNode: - t.Fatalf("Unexpected saveNode") - case <-time.After(time.Millisecond * 100): - } -} - -func TestSetField(t *testing.T) { - mdb, clock := rawdb.NewMemoryDatabase(), &mclock.Simulated{} - - s, flags, fields := testSetup([]bool{true}, []reflect.Type{reflect.TypeOf("")}) - ns := NewNodeStateMachine(mdb, []byte("-ns"), clock, s) - - saveNode := make(chan *nodeInfo, 1) - ns.saveNodeHook = func(node *nodeInfo) { - saveNode <- node - } - - ns.Start() - - // Set field before setting state - ns.SetField(testNode(1), fields[0], "hello world") - field := ns.GetField(testNode(1), fields[0]) - if field == nil { - t.Fatalf("Field should be set before setting states") - } - ns.SetField(testNode(1), fields[0], nil) - field = ns.GetField(testNode(1), fields[0]) - if field != nil { - t.Fatalf("Field should be unset") - } - // Set field after setting state - ns.SetState(testNode(1), flags[0], Flags{}, 0) - ns.SetField(testNode(1), fields[0], "hello world") - field = ns.GetField(testNode(1), fields[0]) - if field == nil { - t.Fatalf("Field should be set after setting states") - } - if err := ns.SetField(testNode(1), fields[0], 123); err == nil { - t.Fatalf("Invalid field should be rejected") - } - // Dirty node should be written back - ns.Stop() - select { - case <-saveNode: - case <-time.After(time.Second): - t.Fatalf("Timeout") - } -} - -func TestSetState(t *testing.T) { - mdb, clock := rawdb.NewMemoryDatabase(), &mclock.Simulated{} - - s, flags, _ := testSetup([]bool{false, false, false}, nil) - ns := NewNodeStateMachine(mdb, []byte("-ns"), clock, s) - - type change struct{ old, new Flags } - set := make(chan change, 1) - ns.SubscribeState(flags[0].Or(flags[1]), func(n *enode.Node, oldState, newState Flags) { - set <- change{ - old: oldState, - new: newState, - } - }) - - ns.Start() - - check := func(expectOld, expectNew Flags, expectChange bool) { - if expectChange { - select { - case c := <-set: - if !c.old.Equals(expectOld) { - t.Fatalf("Old state mismatch") - } - if !c.new.Equals(expectNew) { - t.Fatalf("New state mismatch") - } - case <-time.After(time.Second): - } - return - } - select { - case <-set: - t.Fatalf("Unexpected change") - case <-time.After(time.Millisecond * 100): - return - } - } - ns.SetState(testNode(1), flags[0], Flags{}, 0) - check(Flags{}, flags[0], true) - - ns.SetState(testNode(1), flags[1], Flags{}, 0) - check(flags[0], flags[0].Or(flags[1]), true) - - ns.SetState(testNode(1), flags[2], Flags{}, 0) - check(Flags{}, Flags{}, false) - - ns.SetState(testNode(1), Flags{}, flags[0], 0) - check(flags[0].Or(flags[1]), flags[1], true) - - ns.SetState(testNode(1), Flags{}, flags[1], 0) - check(flags[1], Flags{}, true) - - ns.SetState(testNode(1), Flags{}, flags[2], 0) - check(Flags{}, Flags{}, false) - - ns.SetState(testNode(1), flags[0].Or(flags[1]), Flags{}, time.Second) - check(Flags{}, flags[0].Or(flags[1]), true) - clock.Run(time.Second) - check(flags[0].Or(flags[1]), Flags{}, true) -} - -func uint64FieldEnc(field interface{}) ([]byte, error) { - if u, ok := field.(uint64); ok { - enc, err := rlp.EncodeToBytes(&u) - return enc, err - } - return nil, errors.New("invalid field type") -} - -func uint64FieldDec(enc []byte) (interface{}, error) { - var u uint64 - err := rlp.DecodeBytes(enc, &u) - return u, err -} - -func stringFieldEnc(field interface{}) ([]byte, error) { - if s, ok := field.(string); ok { - return []byte(s), nil - } - return nil, errors.New("invalid field type") -} - -func stringFieldDec(enc []byte) (interface{}, error) { - return string(enc), nil -} - -func TestPersistentFields(t *testing.T) { - mdb, clock := rawdb.NewMemoryDatabase(), &mclock.Simulated{} - - s, flags, fields := testSetup([]bool{true}, []reflect.Type{reflect.TypeOf(uint64(0)), reflect.TypeOf("")}) - ns := NewNodeStateMachine(mdb, []byte("-ns"), clock, s) - - ns.Start() - ns.SetState(testNode(1), flags[0], Flags{}, 0) - ns.SetField(testNode(1), fields[0], uint64(100)) - ns.SetField(testNode(1), fields[1], "hello world") - ns.Stop() - - ns2 := NewNodeStateMachine(mdb, []byte("-ns"), clock, s) - - ns2.Start() - field0 := ns2.GetField(testNode(1), fields[0]) - if !reflect.DeepEqual(field0, uint64(100)) { - t.Fatalf("Field changed") - } - field1 := ns2.GetField(testNode(1), fields[1]) - if !reflect.DeepEqual(field1, "hello world") { - t.Fatalf("Field changed") - } - - s.Version++ - ns3 := NewNodeStateMachine(mdb, []byte("-ns"), clock, s) - ns3.Start() - if ns3.GetField(testNode(1), fields[0]) != nil { - t.Fatalf("Old field version should have been discarded") - } -} - -func TestFieldSub(t *testing.T) { - mdb, clock := rawdb.NewMemoryDatabase(), &mclock.Simulated{} - - s, flags, fields := testSetup([]bool{true}, []reflect.Type{reflect.TypeOf(uint64(0))}) - ns := NewNodeStateMachine(mdb, []byte("-ns"), clock, s) - - var ( - lastState Flags - lastOldValue, lastNewValue interface{} - ) - ns.SubscribeField(fields[0], func(n *enode.Node, state Flags, oldValue, newValue interface{}) { - lastState, lastOldValue, lastNewValue = state, oldValue, newValue - }) - check := func(state Flags, oldValue, newValue interface{}) { - if !lastState.Equals(state) || lastOldValue != oldValue || lastNewValue != newValue { - t.Fatalf("Incorrect field sub callback (expected [%v %v %v], got [%v %v %v])", state, oldValue, newValue, lastState, lastOldValue, lastNewValue) - } - } - ns.Start() - ns.SetState(testNode(1), flags[0], Flags{}, 0) - ns.SetField(testNode(1), fields[0], uint64(100)) - check(flags[0], nil, uint64(100)) - ns.Stop() - check(s.OfflineFlag(), uint64(100), nil) - - ns2 := NewNodeStateMachine(mdb, []byte("-ns"), clock, s) - ns2.SubscribeField(fields[0], func(n *enode.Node, state Flags, oldValue, newValue interface{}) { - lastState, lastOldValue, lastNewValue = state, oldValue, newValue - }) - ns2.Start() - check(s.OfflineFlag(), nil, uint64(100)) - ns2.SetState(testNode(1), Flags{}, flags[0], 0) - ns2.SetField(testNode(1), fields[0], nil) - check(Flags{}, uint64(100), nil) - ns2.Stop() -} - -func TestDuplicatedFlags(t *testing.T) { - mdb, clock := rawdb.NewMemoryDatabase(), &mclock.Simulated{} - - s, flags, _ := testSetup([]bool{true}, nil) - ns := NewNodeStateMachine(mdb, []byte("-ns"), clock, s) - - type change struct{ old, new Flags } - set := make(chan change, 1) - ns.SubscribeState(flags[0], func(n *enode.Node, oldState, newState Flags) { - set <- change{oldState, newState} - }) - - ns.Start() - defer ns.Stop() - - check := func(expectOld, expectNew Flags, expectChange bool) { - if expectChange { - select { - case c := <-set: - if !c.old.Equals(expectOld) { - t.Fatalf("Old state mismatch") - } - if !c.new.Equals(expectNew) { - t.Fatalf("New state mismatch") - } - case <-time.After(time.Second): - } - return - } - select { - case <-set: - t.Fatalf("Unexpected change") - case <-time.After(time.Millisecond * 100): - return - } - } - ns.SetState(testNode(1), flags[0], Flags{}, time.Second) - check(Flags{}, flags[0], true) - ns.SetState(testNode(1), flags[0], Flags{}, 2*time.Second) // extend the timeout to 2s - check(Flags{}, flags[0], false) - - clock.Run(2 * time.Second) - check(flags[0], Flags{}, true) -} - -func TestCallbackOrder(t *testing.T) { - mdb, clock := rawdb.NewMemoryDatabase(), &mclock.Simulated{} - - s, flags, _ := testSetup([]bool{false, false, false, false}, nil) - ns := NewNodeStateMachine(mdb, []byte("-ns"), clock, s) - - ns.SubscribeState(flags[0], func(n *enode.Node, oldState, newState Flags) { - if newState.Equals(flags[0]) { - ns.SetStateSub(n, flags[1], Flags{}, 0) - ns.SetStateSub(n, flags[2], Flags{}, 0) - } - }) - ns.SubscribeState(flags[1], func(n *enode.Node, oldState, newState Flags) { - if newState.Equals(flags[1]) { - ns.SetStateSub(n, flags[3], Flags{}, 0) - } - }) - lastState := Flags{} - ns.SubscribeState(MergeFlags(flags[1], flags[2], flags[3]), func(n *enode.Node, oldState, newState Flags) { - if !oldState.Equals(lastState) { - t.Fatalf("Wrong callback order") - } - lastState = newState - }) - - ns.Start() - defer ns.Stop() - - ns.SetState(testNode(1), flags[0], Flags{}, 0) -} diff --git a/p2p/rlpx/rlpx.go b/p2p/rlpx/rlpx.go index a338490e62..dd14822dee 100644 --- a/p2p/rlpx/rlpx.go +++ b/p2p/rlpx/rlpx.go @@ -604,6 +604,11 @@ func (h *handshakeState) readMsg(msg interface{}, prv *ecdsa.PrivateKey, r io.Re } size := binary.BigEndian.Uint16(prefix) + // baseProtocolMaxMsgSize = 2 * 1024 + if size > 2048 { + return nil, errors.New("message too big") + } + // Read the handshake packet. packet, err := h.rbuf.read(r, int(size)) if err != nil { diff --git a/p2p/server.go b/p2p/server.go index c83649f719..12221e84ed 100644 --- a/p2p/server.go +++ b/p2p/server.go @@ -19,11 +19,13 @@ package p2p import ( "bytes" + "cmp" "crypto/ecdsa" "encoding/hex" "errors" "fmt" "net" + "net/netip" "regexp" "slices" "sync" @@ -208,8 +210,8 @@ type Server struct { nodedb *enode.DB localnode *enode.LocalNode - ntab *discover.UDPv4 - DiscV5 *discover.UDPv5 + discv4 *discover.UDPv4 + discv5 *discover.UDPv5 discmix *enode.FairMix dialsched *dialScheduler @@ -434,6 +436,16 @@ func (srv *Server) Self() *enode.Node { return ln.Node() } +// DiscoveryV4 returns the discovery v4 instance, if configured. +func (srv *Server) DiscoveryV4() *discover.UDPv4 { + return srv.discv4 +} + +// DiscoveryV5 returns the discovery v5 instance, if configured. +func (srv *Server) DiscoveryV5() *discover.UDPv5 { + return srv.discv5 +} + // Stop terminates the server and all active peer connections. // It blocks until all active connections have been closed. func (srv *Server) Stop() { @@ -470,11 +482,11 @@ type sharedUDPConn struct { unhandled chan discover.ReadPacket } -// ReadFromUDP implements discover.UDPConn -func (s *sharedUDPConn) ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error) { +// ReadFromUDPAddrPort implements discover.UDPConn +func (s *sharedUDPConn) ReadFromUDPAddrPort(b []byte) (n int, addr netip.AddrPort, err error) { packet, ok := <-s.unhandled if !ok { - return 0, nil, errors.New("connection was closed") + return 0, netip.AddrPort{}, errors.New("connection was closed") } l := len(packet.Data) if l > len(b) { @@ -617,13 +629,13 @@ func (srv *Server) setupDiscovery() error { ) // If both versions of discovery are running, setup a shared // connection, so v5 can read unhandled messages from v4. - if srv.DiscoveryV4 && srv.DiscoveryV5 { + if srv.Config.DiscoveryV4 && srv.Config.DiscoveryV5 { unhandled = make(chan discover.ReadPacket, 100) sconn = &sharedUDPConn{conn, unhandled} } // Start discovery services. - if srv.DiscoveryV4 { + if srv.Config.DiscoveryV4 { cfg := discover.Config{ PrivateKey: srv.PrivateKey, NetRestrict: srv.NetRestrict, @@ -636,10 +648,10 @@ func (srv *Server) setupDiscovery() error { if err != nil { return err } - srv.ntab = ntab + srv.discv4 = ntab srv.discmix.AddSource(ntab.RandomNodes()) } - if srv.DiscoveryV5 { + if srv.Config.DiscoveryV5 { cfg := discover.Config{ PrivateKey: srv.PrivateKey, NetRestrict: srv.NetRestrict, @@ -647,7 +659,7 @@ func (srv *Server) setupDiscovery() error { Log: srv.log, FilterFunction: f, } - srv.DiscV5, err = discover.ListenV5(sconn, srv.localnode, cfg) + srv.discv5, err = discover.ListenV5(sconn, srv.localnode, cfg) if err != nil { return err } @@ -674,8 +686,8 @@ func (srv *Server) setupDialScheduler() { dialer: srv.Dialer, clock: srv.clock, } - if srv.ntab != nil { - config.resolver = srv.ntab + if srv.discv4 != nil { + config.resolver = srv.discv4 } if config.dialer == nil { config.dialer = tcpDialer{&net.Dialer{Timeout: defaultDialTimeout}} @@ -884,11 +896,11 @@ running: srv.log.Trace("P2P networking is spinning down") // Terminate discovery. If there is a running lookup it will terminate soon. - if srv.ntab != nil { - srv.ntab.Close() + if srv.discv4 != nil { + srv.discv4.Close() } - if srv.DiscV5 != nil { - srv.DiscV5.Close() + if srv.discv5 != nil { + srv.discv5.Close() } // Disconnect all peers. for _, p := range peers { @@ -992,14 +1004,14 @@ func (srv *Server) listenLoop() { break } - remoteIP := netutil.AddrIP(fd.RemoteAddr()) + remoteIP := netutil.AddrAddr(fd.RemoteAddr()) if err := srv.checkInboundConn(remoteIP); err != nil { srv.log.Debug("Rejected inbound connection", "addr", fd.RemoteAddr(), "err", err) fd.Close() slots <- struct{}{} continue } - if remoteIP != nil { + if remoteIP.IsValid() { fd = newMeteredConn(fd) serveMeter.Mark(1) srv.log.Trace("Accepted connection", "addr", fd.RemoteAddr()) @@ -1011,19 +1023,20 @@ func (srv *Server) listenLoop() { } } -func (srv *Server) checkInboundConn(remoteIP net.IP) error { - if remoteIP == nil { +func (srv *Server) checkInboundConn(remoteIP netip.Addr) error { + if !remoteIP.IsValid() { + // This case happens for internal test connections without remote address. return nil } // Reject connections that do not match NetRestrict. - if srv.NetRestrict != nil && !srv.NetRestrict.Contains(remoteIP) { + if srv.NetRestrict != nil && !srv.NetRestrict.ContainsAddr(remoteIP) { return errors.New("not in netrestrict list") } // Reject Internet peers that try too often. now := srv.clock.Now() srv.inboundHistory.expire(now, nil) - if !netutil.IsLAN(remoteIP) && srv.inboundHistory.contains(remoteIP.String()) { + if !netutil.AddrIsLAN(remoteIP) && srv.inboundHistory.contains(remoteIP.String()) { return errors.New("too many attempts") } srv.inboundHistory.add(remoteIP.String(), now.Add(inboundThrottleTime)) @@ -1207,7 +1220,7 @@ func (srv *Server) NodeInfo() *NodeInfo { Name: srv.Name, Enode: node.URLv4(), ID: node.ID().String(), - IP: node.IP().String(), + IP: node.IPAddr().String(), ListenAddr: srv.ListenAddr, Protocols: make(map[string]interface{}), } @@ -1238,13 +1251,10 @@ func (srv *Server) PeersInfo() []*PeerInfo { } } // Sort the result array alphabetically by node identifier - for i := 0; i < len(infos); i++ { - for j := i + 1; j < len(infos); j++ { - if infos[i].ID > infos[j].ID { - infos[i], infos[j] = infos[j], infos[i] - } - } - } + slices.SortFunc(infos, func(a, b *PeerInfo) int { + return cmp.Compare(a.ID, b.ID) + }) + return infos } diff --git a/p2p/server_nat_test.go b/p2p/server_nat_test.go index de935fcfc5..cbb1f37e0a 100644 --- a/p2p/server_nat_test.go +++ b/p2p/server_nat_test.go @@ -18,6 +18,7 @@ package p2p import ( "net" + "net/netip" "sync/atomic" "testing" "time" @@ -64,8 +65,8 @@ func TestServerPortMapping(t *testing.T) { t.Error("wrong request count:", reqCount) } enr := srv.LocalNode().Node() - if enr.IP().String() != "192.0.2.0" { - t.Error("wrong IP in ENR:", enr.IP()) + if enr.IPAddr() != netip.MustParseAddr("192.0.2.0") { + t.Error("wrong IP in ENR:", enr.IPAddr()) } if enr.TCP() != 30000 { t.Error("wrong TCP port in ENR:", enr.TCP()) diff --git a/p2p/simulations/README.md b/p2p/simulations/README.md index 023f73a098..1f9f72dcda 100644 --- a/p2p/simulations/README.md +++ b/p2p/simulations/README.md @@ -123,20 +123,25 @@ The API is initialised with a particular node adapter and has the following endpoints: ``` -GET / Get network information -POST /start Start all nodes in the network -POST /stop Stop all nodes in the network -GET /events Stream network events -GET /snapshot Take a network snapshot -POST /snapshot Load a network snapshot -POST /nodes Create a node -GET /nodes Get all nodes in the network -GET /nodes/:nodeid Get node information -POST /nodes/:nodeid/start Start a node -POST /nodes/:nodeid/stop Stop a node -POST /nodes/:nodeid/conn/:peerid Connect two nodes -DELETE /nodes/:nodeid/conn/:peerid Disconnect two nodes -GET /nodes/:nodeid/rpc Make RPC requests to a node via WebSocket +OPTIONS / Response 200 with "Access-Control-Allow-Headers"" header set to "Content-Type"" +GET / Get network information +POST /start Start all nodes in the network +POST /stop Stop all nodes in the network +POST /mocker/start Start the mocker node simulation +POST /mocker/stop Stop the mocker node simulation +GET /mocker Get a list of available mockers +POST /reset Reset all properties of a network to initial (empty) state +GET /events Stream network events +GET /snapshot Take a network snapshot +POST /snapshot Load a network snapshot +POST /nodes Create a node +GET /nodes Get all nodes in the network +GET /nodes/:nodeid Get node information +POST /nodes/:nodeid/start Start a node +POST /nodes/:nodeid/stop Stop a node +POST /nodes/:nodeid/conn/:peerid Connect two nodes +DELETE /nodes/:nodeid/conn/:peerid Disconnect two nodes +GET /nodes/:nodeid/rpc Make RPC requests to a node via WebSocket ``` For convenience, `nodeid` in the URL can be the name of a node rather than its diff --git a/p2p/simulations/adapters/inproc.go b/p2p/simulations/adapters/inproc.go index 349e496b2f..0efe9744a5 100644 --- a/p2p/simulations/adapters/inproc.go +++ b/p2p/simulations/adapters/inproc.go @@ -20,6 +20,7 @@ import ( "context" "errors" "fmt" + "maps" "math" "net" "sync" @@ -215,10 +216,7 @@ func (sn *SimNode) ServeRPC(conn *websocket.Conn) error { // simulation_snapshot RPC method func (sn *SimNode) Snapshots() (map[string][]byte, error) { sn.lock.RLock() - services := make(map[string]node.Lifecycle, len(sn.running)) - for name, service := range sn.running { - services[name] = service - } + services := maps.Clone(sn.running) sn.lock.RUnlock() if len(services) == 0 { return nil, errors.New("no running services") @@ -315,11 +313,7 @@ func (sn *SimNode) Services() []node.Lifecycle { func (sn *SimNode) ServiceMap() map[string]node.Lifecycle { sn.lock.RLock() defer sn.lock.RUnlock() - services := make(map[string]node.Lifecycle, len(sn.running)) - for name, service := range sn.running { - services[name] = service - } - return services + return maps.Clone(sn.running) } // Server returns the underlying p2p.Server diff --git a/p2p/simulations/adapters/types.go b/p2p/simulations/adapters/types.go index f34315f170..e18aaacc33 100644 --- a/p2p/simulations/adapters/types.go +++ b/p2p/simulations/adapters/types.go @@ -42,7 +42,6 @@ import ( // // - SimNode, an in-memory node in the same process // - ExecNode, a child process node -// - DockerNode, a node running in a Docker container type Node interface { // Addr returns the node's address (e.g. an Enode URL) Addr() []byte diff --git a/p2p/simulations/examples/ping-pong.go b/p2p/simulations/examples/ping-pong.go index f093c9a98b..44b79e86f3 100644 --- a/p2p/simulations/examples/ping-pong.go +++ b/p2p/simulations/examples/ping-pong.go @@ -34,7 +34,7 @@ import ( "github.com/ethereum/go-ethereum/p2p/simulations/adapters" ) -var adapterType = flag.String("adapter", "sim", `node adapter to use (one of "sim", "exec" or "docker")`) +var adapterType = flag.String("adapter", "sim", `node adapter to use (one of "sim" or "exec")`) // main() starts a simulation network which contains nodes running a simple // ping-pong protocol diff --git a/params/config.go b/params/config.go index c12517e506..1c5e2ba7aa 100644 --- a/params/config.go +++ b/params/config.go @@ -564,7 +564,7 @@ type ChainConfig struct { type EthashConfig struct{} // String implements the stringer interface, returning the consensus engine details. -func (c *EthashConfig) String() string { +func (c EthashConfig) String() string { return "ethash" } @@ -575,8 +575,8 @@ type CliqueConfig struct { } // String implements the stringer interface, returning the consensus engine details. -func (c *CliqueConfig) String() string { - return "clique" +func (c CliqueConfig) String() string { + return fmt.Sprintf("clique(period: %d, epoch: %d)", c.Period, c.Epoch) } // ParliaConfig is the consensus engine configs for proof-of-staked-authority based sealing. @@ -963,7 +963,7 @@ func (c *ChainConfig) IsOnFeynmanFix(currentBlockNumber *big.Int, lastBlockTime return !c.IsFeynmanFix(lastBlockNumber, lastBlockTime) && c.IsFeynmanFix(currentBlockNumber, currentBlockTime) } -// IsCancun returns whether num is either equal to the Cancun fork time or greater. +// IsCancun returns whether time is either equal to the Cancun fork time or greater. func (c *ChainConfig) IsCancun(num *big.Int, time uint64) bool { return c.IsLondon(num) && isTimestampForked(c.CancunTime, time) } @@ -1015,16 +1015,21 @@ func (c *ChainConfig) IsOnPascal(currentBlockNumber *big.Int, lastBlockTime uint return !c.IsPascal(lastBlockNumber, lastBlockTime) && c.IsPascal(currentBlockNumber, currentBlockTime) } -// IsPrague returns whether num is either equal to the Prague fork time or greater. +// IsPrague returns whether time is either equal to the Prague fork time or greater. func (c *ChainConfig) IsPrague(num *big.Int, time uint64) bool { return c.IsLondon(num) && isTimestampForked(c.PragueTime, time) } -// IsVerkle returns whether num is either equal to the Verkle fork time or greater. +// IsVerkle returns whether time is either equal to the Verkle fork time or greater. func (c *ChainConfig) IsVerkle(num *big.Int, time uint64) bool { return c.IsLondon(num) && isTimestampForked(c.VerkleTime, time) } +// IsEIP4762 returns whether eip 4762 has been activated at given block. +func (c *ChainConfig) IsEIP4762(num *big.Int, time uint64) bool { + return c.IsVerkle(num, time) +} + // CheckCompatible checks whether scheduled fork transitions have been imported // with a mismatching chain configuration. func (c *ChainConfig) CheckCompatible(newcfg *ChainConfig, height uint64, time uint64) *ConfigCompatError { @@ -1397,9 +1402,11 @@ func (err *ConfigCompatError) Error() string { return fmt.Sprintf("mismatching %s in database (have block %d, want block %d, rewindto block %d)", err.What, err.StoredBlock, err.NewBlock, err.RewindToBlock) } - if err.StoredTime == nil { + if err.StoredTime == nil && err.NewTime == nil { + return "" + } else if err.StoredTime == nil && err.NewTime != nil { return fmt.Sprintf("mismatching %s in database (have timestamp nil, want timestamp %d, rewindto timestamp %d)", err.What, *err.NewTime, err.RewindToTime) - } else if err.NewTime == nil { + } else if err.StoredTime != nil && err.NewTime == nil { return fmt.Sprintf("mismatching %s in database (have timestamp %d, want timestamp nil, rewindto timestamp %d)", err.What, *err.StoredTime, err.RewindToTime) } return fmt.Sprintf("mismatching %s in database (have timestamp %d, want timestamp %d, rewindto timestamp %d)", err.What, *err.StoredTime, *err.NewTime, err.RewindToTime) @@ -1413,6 +1420,7 @@ func (err *ConfigCompatError) Error() string { type Rules struct { ChainID *big.Int IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool + IsEIP2929, IsEIP4762 bool IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool IsBerlin, IsLondon bool IsMerge bool @@ -1435,6 +1443,7 @@ func (c *ChainConfig) Rules(num *big.Int, isMerge bool, timestamp uint64) Rules } // disallow setting Merge out of order isMerge = isMerge && c.IsLondon(num) + isVerkle := isMerge && c.IsVerkle(num, timestamp) return Rules{ ChainID: new(big.Int).Set(chainID), IsHomestead: c.IsHomestead(num), @@ -1446,6 +1455,7 @@ func (c *ChainConfig) Rules(num *big.Int, isMerge bool, timestamp uint64) Rules IsPetersburg: c.IsPetersburg(num), IsIstanbul: c.IsIstanbul(num), IsBerlin: c.IsBerlin(num), + IsEIP2929: c.IsBerlin(num) && !isVerkle, IsLondon: c.IsLondon(num), IsMerge: isMerge, IsNano: c.IsNano(num), @@ -1464,5 +1474,6 @@ func (c *ChainConfig) Rules(num *big.Int, isMerge bool, timestamp uint64) Rules IsPascal: c.IsPascal(num, timestamp), IsPrague: c.IsPrague(num, timestamp), IsVerkle: c.IsVerkle(num, timestamp), + IsEIP4762: isVerkle, } } diff --git a/params/config_test.go b/params/config_test.go index bf8ce2fc5e..fa444a1d0b 100644 --- a/params/config_test.go +++ b/params/config_test.go @@ -23,6 +23,7 @@ import ( "time" "github.com/ethereum/go-ethereum/common/math" + "github.com/stretchr/testify/require" ) func TestCheckCompatible(t *testing.T) { @@ -137,3 +138,20 @@ func TestConfigRules(t *testing.T) { t.Errorf("expected %v to be shanghai", stamp) } } + +func TestTimestampCompatError(t *testing.T) { + require.Equal(t, new(ConfigCompatError).Error(), "") + + errWhat := "Shanghai fork timestamp" + require.Equal(t, newTimestampCompatError(errWhat, nil, newUint64(1681338455)).Error(), + "mismatching Shanghai fork timestamp in database (have timestamp nil, want timestamp 1681338455, rewindto timestamp 1681338454)") + + require.Equal(t, newTimestampCompatError(errWhat, newUint64(1681338455), nil).Error(), + "mismatching Shanghai fork timestamp in database (have timestamp 1681338455, want timestamp nil, rewindto timestamp 1681338454)") + + require.Equal(t, newTimestampCompatError(errWhat, newUint64(1681338455), newUint64(600624000)).Error(), + "mismatching Shanghai fork timestamp in database (have timestamp 1681338455, want timestamp 600624000, rewindto timestamp 600623999)") + + require.Equal(t, newTimestampCompatError(errWhat, newUint64(0), newUint64(1681338455)).Error(), + "mismatching Shanghai fork timestamp in database (have timestamp 0, want timestamp 1681338455, rewindto timestamp 0)") +} diff --git a/params/protocol_params.go b/params/protocol_params.go index faa5ef5054..0d65968f78 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -91,6 +91,7 @@ const ( LogTopicGas uint64 = 375 // Multiplied by the * of the LOG*, per LOG transaction. e.g. LOG0 incurs 0 * c_txLogTopicGas, LOG4 incurs 4 * c_txLogTopicGas. CreateGas uint64 = 32000 // Once per CREATE operation & contract-creation transaction. Create2Gas uint64 = 32000 // Once per CREATE2 operation + CreateNGasEip4762 uint64 = 1000 // Once per CREATEn operations post-verkle SelfdestructRefundGas uint64 = 24000 // Refunded following a selfdestruct operation. MemoryGas uint64 = 3 // Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL. @@ -212,6 +213,10 @@ var ( // BeaconRootsAddress is the address where historical beacon roots are stored as per EIP-4788 BeaconRootsAddress = common.HexToAddress("0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02") + + // BeaconRootsCode is the code where historical beacon roots are stored as per EIP-4788 + BeaconRootsCode = common.FromHex("3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500") + // SystemAddress is where the system-transaction is sent from as per EIP-4788 SystemAddress = common.HexToAddress("0xfffffffffffffffffffffffffffffffffffffffe") ) diff --git a/params/verkle_params.go b/params/verkle_params.go new file mode 100644 index 0000000000..93d4f7cd64 --- /dev/null +++ b/params/verkle_params.go @@ -0,0 +1,36 @@ +// 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 . + +package params + +// Verkle tree EIP: costs associated to witness accesses +var ( + WitnessBranchReadCost uint64 = 1900 + WitnessChunkReadCost uint64 = 200 + WitnessBranchWriteCost uint64 = 3000 + WitnessChunkWriteCost uint64 = 500 + WitnessChunkFillCost uint64 = 6200 +) + +// ClearVerkleWitnessCosts sets all witness costs to 0, which is necessary +// for historical block replay simulations. +func ClearVerkleWitnessCosts() { + WitnessBranchReadCost = 0 + WitnessChunkReadCost = 0 + WitnessBranchWriteCost = 0 + WitnessChunkWriteCost = 0 + WitnessChunkFillCost = 0 +} diff --git a/rlp/raw.go b/rlp/raw.go index 773aa7e614..879e3bfe5d 100644 --- a/rlp/raw.go +++ b/rlp/raw.go @@ -30,33 +30,33 @@ var rawValueType = reflect.TypeOf(RawValue{}) // StringSize returns the encoded size of a string. func StringSize(s string) uint64 { - switch { - case len(s) == 0: + switch n := len(s); n { + case 0: return 1 - case len(s) == 1: + case 1: if s[0] <= 0x7f { return 1 } else { return 2 } default: - return uint64(headsize(uint64(len(s))) + len(s)) + return uint64(headsize(uint64(n)) + n) } } // BytesSize returns the encoded size of a byte slice. func BytesSize(b []byte) uint64 { - switch { - case len(b) == 0: + switch n := len(b); n { + case 0: return 1 - case len(b) == 1: + case 1: if b[0] <= 0x7f { return 1 } else { return 2 } default: - return uint64(headsize(uint64(len(b))) + len(b)) + return uint64(headsize(uint64(n)) + n) } } @@ -105,18 +105,20 @@ func SplitUint64(b []byte) (x uint64, rest []byte, err error) { if err != nil { return 0, b, err } - switch { - case len(content) == 0: + switch n := len(content); n { + case 0: return 0, rest, nil - case len(content) == 1: + case 1: if content[0] == 0 { return 0, b, ErrCanonInt } return uint64(content[0]), rest, nil - case len(content) > 8: - return 0, b, errUintOverflow default: - x, err = readSize(content, byte(len(content))) + if n > 8 { + return 0, b, errUintOverflow + } + + x, err = readSize(content, byte(n)) if err != nil { return 0, b, ErrCanonInt } diff --git a/rpc/handler.go b/rpc/handler.go index 2a9c8cf1b0..24310a373e 100644 --- a/rpc/handler.go +++ b/rpc/handler.go @@ -17,8 +17,11 @@ package rpc import ( + "bytes" "context" "encoding/json" + "errors" + "fmt" "reflect" "strconv" "strings" @@ -475,8 +478,8 @@ func (h *handler) handleCallMsg(ctx *callProc, reqCtx context.Context, msg *json case msg.isCall(): resp := h.handleCall(ctx, msg) - var ctx []interface{} - ctx = append(ctx, "reqid", idForLog{msg.ID}, "duration", time.Since(start)) + var logctx []any + logctx = append(logctx, "reqid", idForLog{msg.ID}, "duration", time.Since(start)) if resp.Error != nil { xForward := reqCtx.Value("X-Forwarded-For") h.log.Warn("Served "+msg.Method, "reqid", idForLog{msg.ID}, "t", time.Since(start), "err", resp.Error.Message, "X-Forwarded-For", xForward) @@ -486,13 +489,13 @@ func (h *handler) handleCallMsg(ctx *callProc, reqCtx context.Context, msg *json accountBlacklistRpcCounter.Inc(1) log.Warn("blacklist account detected from direct rpc", "remoteAddr", h.conn.remoteAddr()) } - ctx = append(ctx, "err", resp.Error.Message) + logctx = append(logctx, "err", resp.Error.Message) if resp.Error.Data != nil { - ctx = append(ctx, "errdata", resp.Error.Data) + logctx = append(logctx, "errdata", formatErrorData(resp.Error.Data)) } - h.log.Warn("Served "+msg.Method, ctx...) + h.log.Warn("Served "+msg.Method, logctx...) } else { - h.log.Debug("Served "+msg.Method, ctx...) + h.log.Debug("Served "+msg.Method, logctx...) } return resp @@ -607,3 +610,33 @@ func (id idForLog) String() string { } return string(id.RawMessage) } + +var errTruncatedOutput = errors.New("truncated output") + +type limitedBuffer struct { + output []byte + limit int +} + +func (buf *limitedBuffer) Write(data []byte) (int, error) { + avail := max(buf.limit, len(buf.output)) + if len(data) < avail { + buf.output = append(buf.output, data...) + return len(data), nil + } + buf.output = append(buf.output, data[:avail]...) + return avail, errTruncatedOutput +} + +func formatErrorData(v any) string { + buf := limitedBuffer{limit: 1024} + err := json.NewEncoder(&buf).Encode(v) + switch { + case err == nil: + return string(bytes.TrimRight(buf.output, "\n")) + case errors.Is(err, errTruncatedOutput): + return fmt.Sprintf("%s... (truncated)", buf.output) + default: + return fmt.Sprintf("bad error data (err=%v)", err) + } +} diff --git a/signer/core/apitypes/types.go b/signer/core/apitypes/types.go index af2c6212e3..5c97fc2334 100644 --- a/signer/core/apitypes/types.go +++ b/signer/core/apitypes/types.go @@ -67,9 +67,9 @@ func (vs *ValidationMessages) Info(msg string) { } // GetWarnings returns an error with all messages of type WARN of above, or nil if no warnings were present -func (v *ValidationMessages) GetWarnings() error { +func (vs *ValidationMessages) GetWarnings() error { var messages []string - for _, msg := range v.Messages { + for _, msg := range vs.Messages { if msg.Typ == WARN || msg.Typ == CRIT { messages = append(messages, msg.Message) } diff --git a/signer/core/uiapi.go b/signer/core/uiapi.go index b8c3acfb4d..43edfe7d97 100644 --- a/signer/core/uiapi.go +++ b/signer/core/uiapi.go @@ -52,9 +52,9 @@ func NewUIServerAPI(extapi *SignerAPI) *UIServerAPI { // the full Account object and not only Address. // Example call // {"jsonrpc":"2.0","method":"clef_listAccounts","params":[], "id":4} -func (s *UIServerAPI) ListAccounts(ctx context.Context) ([]accounts.Account, error) { +func (api *UIServerAPI) ListAccounts(ctx context.Context) ([]accounts.Account, error) { var accs []accounts.Account - for _, wallet := range s.am.Wallets() { + for _, wallet := range api.am.Wallets() { accs = append(accs, wallet.Accounts()...) } return accs, nil @@ -72,9 +72,9 @@ type rawWallet struct { // ListWallets will return a list of wallets that clef manages // Example call // {"jsonrpc":"2.0","method":"clef_listWallets","params":[], "id":5} -func (s *UIServerAPI) ListWallets() []rawWallet { +func (api *UIServerAPI) ListWallets() []rawWallet { wallets := make([]rawWallet, 0) // return [] instead of nil if empty - for _, wallet := range s.am.Wallets() { + for _, wallet := range api.am.Wallets() { status, failure := wallet.Status() raw := rawWallet{ @@ -94,8 +94,8 @@ func (s *UIServerAPI) ListWallets() []rawWallet { // it for later reuse. // Example call // {"jsonrpc":"2.0","method":"clef_deriveAccount","params":["ledger://","m/44'/60'/0'", false], "id":6} -func (s *UIServerAPI) DeriveAccount(url string, path string, pin *bool) (accounts.Account, error) { - wallet, err := s.am.Wallet(url) +func (api *UIServerAPI) DeriveAccount(url string, path string, pin *bool) (accounts.Account, error) { + wallet, err := api.am.Wallet(url) if err != nil { return accounts.Account{}, err } @@ -122,7 +122,7 @@ func fetchKeystore(am *accounts.Manager) *keystore.KeyStore { // encrypting it with the passphrase. // Example call (should fail on password too short) // {"jsonrpc":"2.0","method":"clef_importRawKey","params":["1111111111111111111111111111111111111111111111111111111111111111","test"], "id":6} -func (s *UIServerAPI) ImportRawKey(privkey string, password string) (accounts.Account, error) { +func (api *UIServerAPI) ImportRawKey(privkey string, password string) (accounts.Account, error) { key, err := crypto.HexToECDSA(privkey) if err != nil { return accounts.Account{}, err @@ -131,7 +131,7 @@ func (s *UIServerAPI) ImportRawKey(privkey string, password string) (accounts.Ac return accounts.Account{}, fmt.Errorf("password requirements not met: %v", err) } // No error - return fetchKeystore(s.am).ImportECDSA(key, password) + return fetchKeystore(api.am).ImportECDSA(key, password) } // OpenWallet initiates a hardware wallet opening procedure, establishing a USB @@ -140,8 +140,8 @@ func (s *UIServerAPI) ImportRawKey(privkey string, password string) (accounts.Ac // Trezor PIN matrix challenge). // Example // {"jsonrpc":"2.0","method":"clef_openWallet","params":["ledger://",""], "id":6} -func (s *UIServerAPI) OpenWallet(url string, passphrase *string) error { - wallet, err := s.am.Wallet(url) +func (api *UIServerAPI) OpenWallet(url string, passphrase *string) error { + wallet, err := api.am.Wallet(url) if err != nil { return err } @@ -155,24 +155,24 @@ func (s *UIServerAPI) OpenWallet(url string, passphrase *string) error { // ChainId returns the chainid in use for Eip-155 replay protection // Example call // {"jsonrpc":"2.0","method":"clef_chainId","params":[], "id":8} -func (s *UIServerAPI) ChainId() math.HexOrDecimal64 { - return (math.HexOrDecimal64)(s.extApi.chainID.Uint64()) +func (api *UIServerAPI) ChainId() math.HexOrDecimal64 { + return (math.HexOrDecimal64)(api.extApi.chainID.Uint64()) } // SetChainId sets the chain id to use when signing transactions. // Example call to set Ropsten: // {"jsonrpc":"2.0","method":"clef_setChainId","params":["3"], "id":8} -func (s *UIServerAPI) SetChainId(id math.HexOrDecimal64) math.HexOrDecimal64 { - s.extApi.chainID = new(big.Int).SetUint64(uint64(id)) - return s.ChainId() +func (api *UIServerAPI) SetChainId(id math.HexOrDecimal64) math.HexOrDecimal64 { + api.extApi.chainID = new(big.Int).SetUint64(uint64(id)) + return api.ChainId() } // Export returns encrypted private key associated with the given address in web3 keystore format. // Example // {"jsonrpc":"2.0","method":"clef_export","params":["0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a"], "id":4} -func (s *UIServerAPI) Export(ctx context.Context, addr common.Address) (json.RawMessage, error) { +func (api *UIServerAPI) Export(ctx context.Context, addr common.Address) (json.RawMessage, error) { // Look up the wallet containing the requested signer - wallet, err := s.am.Find(accounts.Account{Address: addr}) + wallet, err := api.am.Find(accounts.Account{Address: addr}) if err != nil { return nil, err } diff --git a/tests/block_test.go b/tests/block_test.go index 0cff886777..855a462d6a 100644 --- a/tests/block_test.go +++ b/tests/block_test.go @@ -26,11 +26,11 @@ import ( func TestBlockchain(t *testing.T) { bt := new(testMatcher) - // General state tests are 'exported' as blockchain tests, but we can run them natively. - // For speedier CI-runs, the line below can be uncommented, so those are skipped. - // For now, in hardfork-times (Berlin), we run the tests both as StateTests and - // as blockchain tests, since the latter also covers things like receipt root - // bt.skipLoad(`^GeneralStateTests/`) + + // We are running most of GeneralStatetests to tests witness support, even + // though they are ran as state tests too. Still, the performance tests are + // less about state andmore about EVM number crunching, so skip those. + bt.skipLoad(`^GeneralStateTests/VMTests/vmPerformance`) // Skip random failures due to selfish mining test bt.skipLoad(`.*bcForgedTest/bcForkUncle\.json`) @@ -65,47 +65,31 @@ func TestExecutionSpecBlocktests(t *testing.T) { } bt := new(testMatcher) - // These tests fail as of https://github.com/ethereum/go-ethereum/pull/28666, since we - // no longer delete "leftover storage" when deploying a contract. - bt.skipLoad(`^cancun/eip6780_selfdestruct/selfdestruct/self_destructing_initcode_create_tx.json`) - bt.skipLoad(`^cancun/eip6780_selfdestruct/selfdestruct/self_destructing_initcode.json`) - bt.skipLoad(`^cancun/eip6780_selfdestruct/selfdestruct/recreate_self_destructed_contract_different_txs.json`) - bt.skipLoad(`^cancun/eip6780_selfdestruct/selfdestruct/delegatecall_from_new_contract_to_pre_existing_contract.json`) - bt.skipLoad(`^cancun/eip6780_selfdestruct/selfdestruct/create_selfdestruct_same_tx.json`) - bt.walk(t, executionSpecBlockchainTestDir, func(t *testing.T, name string, test *BlockTest) { execBlockTest(t, bt, test) }) } func execBlockTest(t *testing.T, bt *testMatcher, test *BlockTest) { - // If -short flag is used, we don't execute all four permutations, only one. - executionMask := 0xf + // Define all the different flag combinations we should run the tests with, + // picking only one for short tests. + // + // Note, witness building and self-testing is always enabled as it's a very + // good test to ensure that we don't break it. + var ( + snapshotConf = []bool{false, true} + dbschemeConf = []string{rawdb.HashScheme, rawdb.PathScheme} + ) if testing.Short() { - executionMask = (1 << (rand.Int63() & 4)) - } - if executionMask&0x1 != 0 { - if err := bt.checkFailure(t, test.Run(false, rawdb.HashScheme, nil, nil)); err != nil { - t.Errorf("test in hash mode without snapshotter failed: %v", err) - return - } - } - if executionMask&0x2 != 0 { - if err := bt.checkFailure(t, test.Run(true, rawdb.HashScheme, nil, nil)); err != nil { - t.Errorf("test in hash mode with snapshotter failed: %v", err) - return - } - } - if executionMask&0x4 != 0 { - if err := bt.checkFailure(t, test.Run(false, rawdb.PathScheme, nil, nil)); err != nil { - t.Errorf("test in path mode without snapshotter failed: %v", err) - return - } + snapshotConf = []bool{snapshotConf[rand.Int()%2]} + dbschemeConf = []string{dbschemeConf[rand.Int()%2]} } - if executionMask&0x8 != 0 { - if err := bt.checkFailure(t, test.Run(true, rawdb.PathScheme, nil, nil)); err != nil { - t.Errorf("test in path mode with snapshotter failed: %v", err) - return + for _, snapshot := range snapshotConf { + for _, dbscheme := range dbschemeConf { + if err := bt.checkFailure(t, test.Run(snapshot, dbscheme, true, nil, nil)); err != nil { + t.Errorf("test with config {snapshotter:%v, scheme:%v} failed: %v", snapshot, dbscheme, err) + return + } } } } diff --git a/tests/block_test_util.go b/tests/block_test_util.go index fe44b08743..35e49a9055 100644 --- a/tests/block_test_util.go +++ b/tests/block_test_util.go @@ -110,7 +110,7 @@ type btHeaderMarshaling struct { ExcessBlobGas *math.HexOrDecimal64 } -func (t *BlockTest) Run(snapshotter bool, scheme string, tracer *tracing.Hooks, postCheck func(error, *core.BlockChain)) (result error) { +func (t *BlockTest) Run(snapshotter bool, scheme string, witness bool, tracer *tracing.Hooks, postCheck func(error, *core.BlockChain)) (result error) { config, ok := Forks[t.json.Network] if !ok { return UnsupportedForkError{t.json.Network} @@ -151,7 +151,8 @@ func (t *BlockTest) Run(snapshotter bool, scheme string, tracer *tracing.Hooks, cache.SnapshotWait = true } chain, err := core.NewBlockChain(db, cache, gspec, nil, engine, vm.Config{ - Tracer: tracer, + Tracer: tracer, + EnableWitnessCollection: witness, }, nil, nil) if err != nil { return err diff --git a/tests/init.go b/tests/init.go index e333587a07..c85e714c00 100644 --- a/tests/init.go +++ b/tests/init.go @@ -212,7 +212,7 @@ var Forks = map[string]*params.ChainConfig{ LondonBlock: big.NewInt(0), ArrowGlacierBlock: big.NewInt(0), }, - "ArrowGlacierToMergeAtDiffC0000": { + "ArrowGlacierToParisAtDiffC0000": { ChainID: big.NewInt(1), HomesteadBlock: big.NewInt(0), EIP150Block: big.NewInt(0), @@ -246,6 +246,23 @@ var Forks = map[string]*params.ChainConfig{ ArrowGlacierBlock: big.NewInt(0), GrayGlacierBlock: big.NewInt(0), }, + "Paris": { + ChainID: big.NewInt(1), + HomesteadBlock: big.NewInt(0), + 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(0), + ArrowGlacierBlock: big.NewInt(0), + MergeNetsplitBlock: big.NewInt(0), + TerminalTotalDifficulty: big.NewInt(0), + }, "Merge": { ChainID: big.NewInt(1), HomesteadBlock: big.NewInt(0), @@ -281,7 +298,7 @@ var Forks = map[string]*params.ChainConfig{ TerminalTotalDifficulty: big.NewInt(0), ShanghaiTime: u64(0), }, - "MergeToShanghaiAtTime15k": { + "ParisToShanghaiAtTime15k": { ChainID: big.NewInt(1), HomesteadBlock: big.NewInt(0), EIP150Block: big.NewInt(0), diff --git a/tests/state_test.go b/tests/state_test.go index 8c93914615..58fd10f838 100644 --- a/tests/state_test.go +++ b/tests/state_test.go @@ -54,14 +54,6 @@ func initMatcher(st *testMatcher) { // Uses 1GB RAM per tested fork st.skipLoad(`^stStaticCall/static_Call1MB`) - // These tests fail as of https://github.com/ethereum/go-ethereum/pull/28666, since we - // no longer delete "leftover storage" when deploying a contract. - st.skipLoad(`^stSStoreTest/InitCollision\.json`) - st.skipLoad(`^stRevertTest/RevertInCreateInInit\.json`) - st.skipLoad(`^stExtCodeHash/dynamicAccountOverwriteEmpty\.json`) - st.skipLoad(`^stCreate2/create2collisionStorage\.json`) - st.skipLoad(`^stCreate2/RevertInCreateInInitCreate2\.json`) - // Broken tests: // EOF is not part of cancun st.skipLoad(`^stEOF/`) diff --git a/trie/dummy_trie.go b/trie/dummy_trie.go index 41478253d9..6472c2d711 100644 --- a/trie/dummy_trie.go +++ b/trie/dummy_trie.go @@ -64,8 +64,8 @@ func (t *EmptyTrie) DeleteAccount(address common.Address) error { return nil } -func (t *EmptyTrie) Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet, error) { - return common.Hash{}, nil, nil +func (t *EmptyTrie) Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet) { + return common.Hash{}, nil } func (t *EmptyTrie) Hash() common.Hash { @@ -87,3 +87,12 @@ func (t *EmptyTrie) Copy() *EmptyTrie { cpy := *t return &cpy } + +func (t *EmptyTrie) IsVerkle() bool { + return false +} + +// Witness returns a set containing all trie nodes that have been accessed. +func (t *EmptyTrie) Witness() map[string]struct{} { + panic("not implemented") +} diff --git a/trie/hasher.go b/trie/hasher.go index 1e063d8020..abf654c709 100644 --- a/trie/hasher.go +++ b/trie/hasher.go @@ -21,7 +21,6 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/rlp" - "golang.org/x/crypto/sha3" ) // hasher is a type used for the trie Hash operation. A hasher has some @@ -38,7 +37,7 @@ var hasherPool = sync.Pool{ New: func() interface{} { return &hasher{ tmp: make([]byte, 0, 550), // cap is as large as a full fullNode. - sha: sha3.NewLegacyKeccak256().(crypto.KeccakState), + sha: crypto.NewKeccakState(), encbuf: rlp.NewEncoderBuffer(nil), } }, diff --git a/trie/iterator.go b/trie/iterator.go index 83ccc0740f..fa01611063 100644 --- a/trie/iterator.go +++ b/trie/iterator.go @@ -135,7 +135,7 @@ type nodeIteratorState struct { node node // Trie node being iterated parent common.Hash // Hash of the first full ancestor node (nil if current is the root) index int // Child to be processed next - pathlen int // Length of the path to this node + pathlen int // Length of the path to the parent node } type nodeIterator struct { @@ -145,7 +145,7 @@ type nodeIterator struct { err error // Failure set in case of an internal error in the iterator resolver NodeResolver // optional node resolver for avoiding disk hits - pool []*nodeIteratorState // local pool for iteratorstates + pool []*nodeIteratorState // local pool for iterator states } // errIteratorEnd is stored in nodeIterator.err when iteration is done. @@ -304,6 +304,7 @@ func (it *nodeIterator) seek(prefix []byte) error { // The path we're looking for is the hex encoded key without terminator. key := keybytesToHex(prefix) key = key[:len(key)-1] + // Move forward until we're just before the closest match to key. for { state, parentIndex, path, err := it.peekSeek(key) @@ -311,7 +312,7 @@ func (it *nodeIterator) seek(prefix []byte) error { return errIteratorEnd } else if err != nil { return seekError{prefix, err} - } else if bytes.Compare(path, key) >= 0 { + } else if reachedPath(path, key) { return nil } it.push(state, parentIndex, path) @@ -339,7 +340,6 @@ func (it *nodeIterator) peek(descend bool) (*nodeIteratorState, *int, []byte, er // If we're skipping children, pop the current node first it.pop() } - // Continue iteration to the next child for len(it.stack) > 0 { parent := it.stack[len(it.stack)-1] @@ -372,7 +372,6 @@ func (it *nodeIterator) peekSeek(seekKey []byte) (*nodeIteratorState, *int, []by // If we're skipping children, pop the current node first it.pop() } - // Continue iteration to the next child for len(it.stack) > 0 { parent := it.stack[len(it.stack)-1] @@ -449,16 +448,18 @@ func (it *nodeIterator) findChild(n *fullNode, index int, ancestor common.Hash) state *nodeIteratorState childPath []byte ) - for ; index < len(n.Children); index++ { + for ; index < len(n.Children); index = nextChildIndex(index) { if n.Children[index] != nil { child = n.Children[index] hash, _ := child.cache() + state = it.getFromPool() state.hash = common.BytesToHash(hash) state.node = child state.parent = ancestor state.index = -1 state.pathlen = len(path) + childPath = append(childPath, path...) childPath = append(childPath, byte(index)) return child, state, childPath, index @@ -471,8 +472,8 @@ func (it *nodeIterator) nextChild(parent *nodeIteratorState, ancestor common.Has switch node := parent.node.(type) { case *fullNode: // Full node, move to the first non-nil child. - if child, state, path, index := it.findChild(node, parent.index+1, ancestor); child != nil { - parent.index = index - 1 + if child, state, path, index := it.findChild(node, nextChildIndex(parent.index), ancestor); child != nil { + parent.index = prevChildIndex(index) return state, path, true } case *shortNode: @@ -498,23 +499,23 @@ func (it *nodeIterator) nextChildAt(parent *nodeIteratorState, ancestor common.H switch n := parent.node.(type) { case *fullNode: // Full node, move to the first non-nil child before the desired key position - child, state, path, index := it.findChild(n, parent.index+1, ancestor) + child, state, path, index := it.findChild(n, nextChildIndex(parent.index), ancestor) if child == nil { // No more children in this fullnode return parent, it.path, false } // If the child we found is already past the seek position, just return it. - if bytes.Compare(path, key) >= 0 { - parent.index = index - 1 + if reachedPath(path, key) { + parent.index = prevChildIndex(index) return state, path, true } // The child is before the seek position. Try advancing for { - nextChild, nextState, nextPath, nextIndex := it.findChild(n, index+1, ancestor) + nextChild, nextState, nextPath, nextIndex := it.findChild(n, nextChildIndex(index), ancestor) // If we run out of children, or skipped past the target, return the // previous one - if nextChild == nil || bytes.Compare(nextPath, key) >= 0 { - parent.index = index - 1 + if nextChild == nil || reachedPath(nextPath, key) { + parent.index = prevChildIndex(index) return state, path, true } // We found a better child closer to the target @@ -541,7 +542,7 @@ func (it *nodeIterator) push(state *nodeIteratorState, parentIndex *int, path [] it.path = path it.stack = append(it.stack, state) if parentIndex != nil { - *parentIndex++ + *parentIndex = nextChildIndex(*parentIndex) } } @@ -550,8 +551,54 @@ func (it *nodeIterator) pop() { it.path = it.path[:last.pathlen] it.stack[len(it.stack)-1] = nil it.stack = it.stack[:len(it.stack)-1] - // last is now unused - it.putInPool(last) + + it.putInPool(last) // last is now unused +} + +// reachedPath normalizes a path by truncating a terminator if present, and +// returns true if it is greater than or equal to the target. Using this, +// the path of a value node embedded a full node will compare less than the +// full node's children. +func reachedPath(path, target []byte) bool { + if hasTerm(path) { + path = path[:len(path)-1] + } + return bytes.Compare(path, target) >= 0 +} + +// A value embedded in a full node occupies the last slot (16) of the array of +// children. In order to produce a pre-order traversal when iterating children, +// we jump to this last slot first, then go back iterate the child nodes (and +// skip the last slot at the end): + +// prevChildIndex returns the index of a child in a full node which precedes +// the given index when performing a pre-order traversal. +func prevChildIndex(index int) int { + switch index { + case 0: // We jumped back to iterate the children, from the value slot + return 16 + case 16: // We jumped to the embedded value slot at the end, from the placeholder index + return -1 + case 17: // We skipped the value slot after iterating all the children + return 15 + default: // We are iterating the children in sequence + return index - 1 + } +} + +// nextChildIndex returns the index of a child in a full node which follows +// the given index when performing a pre-order traversal. +func nextChildIndex(index int) int { + switch index { + case -1: // Jump from the placeholder index to the embedded value slot + return 16 + case 15: // Skip the value slot after iterating the children + return 17 + case 16: // From the embedded value slot, jump back to iterate the children + return 0 + default: // Iterate children in sequence + return index + 1 + } } func compareNodes(a, b NodeIterator) int { diff --git a/trie/iterator_test.go b/trie/iterator_test.go index 41e83f6cb6..b463294b09 100644 --- a/trie/iterator_test.go +++ b/trie/iterator_test.go @@ -59,7 +59,7 @@ func TestIterator(t *testing.T) { all[val.k] = val.v trie.MustUpdate([]byte(val.k), []byte(val.v)) } - root, nodes, _ := trie.Commit(false) + root, nodes := trie.Commit(false) db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) trie, _ = New(TrieID(root), db) @@ -182,14 +182,14 @@ func testNodeIteratorCoverage(t *testing.T, scheme string) { type kvs struct{ k, v string } var testdata1 = []kvs{ + {"bar", "b"}, {"barb", "ba"}, {"bard", "bc"}, {"bars", "bb"}, - {"bar", "b"}, {"fab", "z"}, + {"foo", "a"}, {"food", "ab"}, {"foos", "aa"}, - {"foo", "a"}, } var testdata2 = []kvs{ @@ -218,7 +218,7 @@ func TestIteratorSeek(t *testing.T) { // Seek to a non-existent key. it = NewIterator(trie.MustNodeIterator([]byte("barc"))) - if err := checkIteratorOrder(testdata1[1:], it); err != nil { + if err := checkIteratorOrder(testdata1[2:], it); err != nil { t.Fatal(err) } @@ -227,6 +227,12 @@ func TestIteratorSeek(t *testing.T) { if err := checkIteratorOrder(nil, it); err != nil { t.Fatal(err) } + + // Seek to a key for which a prefixing key exists. + it = NewIterator(trie.MustNodeIterator([]byte("food"))) + if err := checkIteratorOrder(testdata1[6:], it); err != nil { + t.Fatal(err) + } } func checkIteratorOrder(want []kvs, it *Iterator) error { @@ -251,7 +257,7 @@ func TestDifferenceIterator(t *testing.T) { for _, val := range testdata1 { triea.MustUpdate([]byte(val.k), []byte(val.v)) } - rootA, nodesA, _ := triea.Commit(false) + rootA, nodesA := triea.Commit(false) dba.Update(rootA, types.EmptyRootHash, trienode.NewWithNodeSet(nodesA)) triea, _ = New(TrieID(rootA), dba) @@ -260,7 +266,7 @@ func TestDifferenceIterator(t *testing.T) { for _, val := range testdata2 { trieb.MustUpdate([]byte(val.k), []byte(val.v)) } - rootB, nodesB, _ := trieb.Commit(false) + rootB, nodesB := trieb.Commit(false) dbb.Update(rootB, types.EmptyRootHash, trienode.NewWithNodeSet(nodesB)) trieb, _ = New(TrieID(rootB), dbb) @@ -293,7 +299,7 @@ func TestUnionIterator(t *testing.T) { for _, val := range testdata1 { triea.MustUpdate([]byte(val.k), []byte(val.v)) } - rootA, nodesA, _ := triea.Commit(false) + rootA, nodesA := triea.Commit(false) dba.Update(rootA, types.EmptyRootHash, trienode.NewWithNodeSet(nodesA)) triea, _ = New(TrieID(rootA), dba) @@ -302,7 +308,7 @@ func TestUnionIterator(t *testing.T) { for _, val := range testdata2 { trieb.MustUpdate([]byte(val.k), []byte(val.v)) } - rootB, nodesB, _ := trieb.Commit(false) + rootB, nodesB := trieb.Commit(false) dbb.Update(rootB, types.EmptyRootHash, trienode.NewWithNodeSet(nodesB)) trieb, _ = New(TrieID(rootB), dbb) @@ -311,16 +317,16 @@ func TestUnionIterator(t *testing.T) { all := []struct{ k, v string }{ {"aardvark", "c"}, + {"bar", "b"}, {"barb", "ba"}, {"barb", "bd"}, {"bard", "bc"}, {"bars", "bb"}, {"bars", "be"}, - {"bar", "b"}, {"fab", "z"}, + {"foo", "a"}, {"food", "ab"}, {"foos", "aa"}, - {"foo", "a"}, {"jars", "d"}, } @@ -365,7 +371,7 @@ func testIteratorContinueAfterError(t *testing.T, memonly bool, scheme string) { for _, val := range testdata1 { tr.MustUpdate([]byte(val.k), []byte(val.v)) } - root, nodes, _ := tr.Commit(false) + root, nodes := tr.Commit(false) tdb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) if !memonly { tdb.Commit(root) @@ -475,7 +481,7 @@ func testIteratorContinueAfterSeekError(t *testing.T, memonly bool, scheme strin for _, val := range testdata1 { ctr.MustUpdate([]byte(val.k), []byte(val.v)) } - root, nodes, _ := ctr.Commit(false) + root, nodes := ctr.Commit(false) for path, n := range nodes.Nodes { if n.Hash == barNodeHash { barNodePath = []byte(path) @@ -512,7 +518,7 @@ func testIteratorContinueAfterSeekError(t *testing.T, memonly bool, scheme strin rawdb.WriteTrieNode(diskdb, common.Hash{}, barNodePath, barNodeHash, barNodeBlob, triedb.Scheme()) } // Check that iteration produces the right set of values. - if err := checkIteratorOrder(testdata1[2:], NewIterator(it)); err != nil { + if err := checkIteratorOrder(testdata1[3:], NewIterator(it)); err != nil { t.Fatal(err) } } @@ -555,7 +561,7 @@ func testIteratorNodeBlob(t *testing.T, scheme string) { all[val.k] = val.v trie.MustUpdate([]byte(val.k), []byte(val.v)) } - root, nodes, _ := trie.Commit(false) + root, nodes := trie.Commit(false) triedb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) triedb.Commit(root) diff --git a/trie/secure_trie.go b/trie/secure_trie.go index 5462f6eb7e..f683537952 100644 --- a/trie/secure_trie.go +++ b/trie/secure_trie.go @@ -24,6 +24,16 @@ import ( "github.com/ethereum/go-ethereum/triedb/database" ) +// preimageStore wraps the methods of a backing store for reading and writing +// trie node preimages. +type preimageStore interface { + // Preimage retrieves the preimage of the specified hash. + Preimage(hash common.Hash) []byte + + // InsertPreimage commits a set of preimages along with their hashes. + InsertPreimage(preimages map[common.Hash][]byte) +} + // SecureTrie is the old name of StateTrie. // Deprecated: use StateTrie. type SecureTrie = StateTrie @@ -52,6 +62,7 @@ func NewSecure(stateRoot common.Hash, owner common.Hash, root common.Hash, db da type StateTrie struct { trie Trie db database.Database + preimages preimageStore hashKeyBuf [common.HashLength]byte //nolint:unused secKeyCache map[string][]byte secKeyCacheOwner *StateTrie // Pointer to self, replace the key cache on mismatch @@ -70,7 +81,14 @@ func NewStateTrie(id *ID, db database.Database) (*StateTrie, error) { if err != nil { return nil, err } - return &StateTrie{trie: *trie, db: db}, nil + tr := &StateTrie{trie: *trie, db: db} + + // link the preimage store if it's supported + preimages, ok := db.(preimageStore) + if ok { + tr.preimages = preimages + } + return tr, nil } // MustGet returns the value for key stored in the trie. @@ -211,7 +229,15 @@ func (t *StateTrie) GetKey(shaKey []byte) []byte { if key, ok := t.getSecKeyCache()[string(shaKey)]; ok { return key } - return t.db.Preimage(common.BytesToHash(shaKey)) + if t.preimages == nil { + return nil + } + return t.preimages.Preimage(common.BytesToHash(shaKey)) +} + +// Witness returns a set containing all trie nodes that have been accessed. +func (t *StateTrie) Witness() map[string]struct{} { + return t.trie.Witness() } // Commit collects all dirty nodes in the trie and replaces them with the @@ -221,14 +247,16 @@ func (t *StateTrie) GetKey(shaKey []byte) []byte { // All cached preimages will be also flushed if preimages recording is enabled. // Once the trie is committed, it's not usable anymore. A new trie must // be created with new root and updated trie database for following usage -func (t *StateTrie) Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet, error) { +func (t *StateTrie) Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet) { // Write all the pre-images to the actual disk database if len(t.getSecKeyCache()) > 0 { - preimages := make(map[common.Hash][]byte) + preimages := make(map[common.Hash][]byte, len(t.secKeyCache)) for hk, key := range t.secKeyCache { preimages[common.BytesToHash([]byte(hk))] = key } - t.db.InsertPreimage(preimages) + if t.preimages != nil { + t.preimages.InsertPreimage(preimages) + } t.secKeyCache = make(map[string][]byte) } // Commit the trie and return its modified nodeset. @@ -298,3 +326,7 @@ func (t *StateTrie) getSecKeyCache() map[string][]byte { } return t.secKeyCache } + +func (t *StateTrie) IsVerkle() bool { + return false +} diff --git a/trie/secure_trie_test.go b/trie/secure_trie_test.go index 2f0ef5f046..9053d16c63 100644 --- a/trie/secure_trie_test.go +++ b/trie/secure_trie_test.go @@ -61,7 +61,7 @@ func makeTestStateTrie() (*testDb, *StateTrie, map[string][]byte) { trie.MustUpdate(key, val) } } - root, nodes, _ := trie.Commit(false) + root, nodes := trie.Commit(false) if err := triedb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)); err != nil { panic(fmt.Errorf("failed to commit db %v", err)) } diff --git a/trie/stacktrie_fuzzer_test.go b/trie/stacktrie_fuzzer_test.go index 5126e0bd07..df487d16bf 100644 --- a/trie/stacktrie_fuzzer_test.go +++ b/trie/stacktrie_fuzzer_test.go @@ -28,7 +28,6 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/trie/trienode" - "golang.org/x/crypto/sha3" ) func FuzzStackTrie(f *testing.F) { @@ -41,10 +40,10 @@ func fuzz(data []byte, debugging bool) { // This spongeDb is used to check the sequence of disk-db-writes var ( input = bytes.NewReader(data) - spongeA = &spongeDb{sponge: sha3.NewLegacyKeccak256()} + spongeA = &spongeDb{sponge: crypto.NewKeccakState()} dbA = newTestDatabase(rawdb.NewDatabase(spongeA), rawdb.HashScheme) trieA = NewEmpty(dbA) - spongeB = &spongeDb{sponge: sha3.NewLegacyKeccak256()} + spongeB = &spongeDb{sponge: crypto.NewKeccakState()} dbB = newTestDatabase(rawdb.NewDatabase(spongeB), rawdb.HashScheme) trieB = NewStackTrie(func(path []byte, hash common.Hash, blob []byte) { rawdb.WriteTrieNode(spongeB, common.Hash{}, path, hash, blob, dbB.Scheme()) @@ -80,10 +79,7 @@ func fuzz(data []byte, debugging bool) { return } // Flush trie -> database - rootA, nodes, err := trieA.Commit(false) - if err != nil { - panic(err) - } + rootA, nodes := trieA.Commit(false) if nodes != nil { dbA.Update(rootA, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) } diff --git a/trie/sync.go b/trie/sync.go index 5c74dfcc03..d0f3866c37 100644 --- a/trie/sync.go +++ b/trie/sync.go @@ -22,9 +22,11 @@ import ( "sync" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/prque" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" @@ -148,15 +150,42 @@ type CodeSyncResult struct { // nodeOp represents an operation upon the trie node. It can either represent a // deletion to the specific node or a node write for persisting retrieved node. type nodeOp struct { + del bool // flag if op stands for a delete operation owner common.Hash // identifier of the trie (empty for account trie) path []byte // path from the root to the specified node. blob []byte // the content of the node (nil for deletion) hash common.Hash // hash of the node content (empty for node deletion) } -// isDelete indicates if the operation is a database deletion. -func (op *nodeOp) isDelete() bool { - return len(op.blob) == 0 +// valid checks whether the node operation is valid. +func (op *nodeOp) valid() bool { + if op.del && len(op.blob) != 0 { + return false + } + if !op.del && len(op.blob) == 0 { + return false + } + return true +} + +// string returns the node operation in string representation. +func (op *nodeOp) string() string { + var node string + if op.owner == (common.Hash{}) { + node = fmt.Sprintf("node: (%v)", op.path) + } else { + node = fmt.Sprintf("node: (%x-%v)", op.owner, op.path) + } + var blobHex string + if len(op.blob) == 0 { + blobHex = "nil" + } else { + blobHex = hexutil.Encode(op.blob) + } + if op.del { + return fmt.Sprintf("del %s %s %s", node, blobHex, op.hash.Hex()) + } + return fmt.Sprintf("write %s %s %s", node, blobHex, op.hash.Hex()) } // syncMemBatch is an in-memory buffer of successfully downloaded but not yet @@ -219,6 +248,7 @@ func (batch *syncMemBatch) delNode(owner common.Hash, path []byte) { batch.size += common.HashLength + uint64(len(path)) } batch.nodes = append(batch.nodes, nodeOp{ + del: true, owner: owner, path: path, }) @@ -427,7 +457,10 @@ func (s *Sync) Commit(dbw ethdb.Batch, stateBatch ethdb.Batch) error { storage int ) for _, op := range s.membatch.nodes { - if op.isDelete() { + if !op.valid() { + return fmt.Errorf("invalid op, %s", op.string()) + } + if op.del { // node deletion is only supported in path mode. if op.owner == (common.Hash{}) { if stateBatch != nil { @@ -558,9 +591,9 @@ func (s *Sync) children(req *nodeRequest, object node) ([]*nodeRequest, error) { // the performance impact negligible. var exists bool if owner == (common.Hash{}) { - exists = rawdb.ExistsAccountTrieNode(s.database.StateStoreReader(), append(inner, key[:i]...)) + exists = rawdb.HasAccountTrieNode(s.database.StateStoreReader(), append(inner, key[:i]...)) } else { - exists = rawdb.ExistsStorageTrieNode(s.database.StateStoreReader(), owner, append(inner, key[:i]...)) + exists = rawdb.HasStorageTrieNode(s.database.StateStoreReader(), owner, append(inner, key[:i]...)) } if exists { s.membatch.delNode(owner, append(inner, key[:i]...)) @@ -703,13 +736,14 @@ func (s *Sync) hasNode(owner common.Hash, path []byte, hash common.Hash) (exists } // If node is running with path scheme, check the presence with node path. var blob []byte - var dbHash common.Hash if owner == (common.Hash{}) { - blob, dbHash = rawdb.ReadAccountTrieNode(s.database.StateStoreReader(), path) + blob = rawdb.ReadAccountTrieNode(s.database.StateStoreReader(), path) } else { - blob, dbHash = rawdb.ReadStorageTrieNode(s.database.StateStoreReader(), owner, path) + blob = rawdb.ReadStorageTrieNode(s.database.StateStoreReader(), owner, path) } - exists = hash == dbHash + h := newBlobHasher() + defer h.release() + exists = hash == h.hash(blob) inconsistent = !exists && len(blob) != 0 return exists, inconsistent } @@ -724,3 +758,23 @@ func ResolvePath(path []byte) (common.Hash, []byte) { } return owner, path } + +// blobHasher is used to compute the sha256 hash of the provided data. +type blobHasher struct{ state crypto.KeccakState } + +// blobHasherPool is the pool for reusing pre-allocated hash state. +var blobHasherPool = sync.Pool{ + New: func() interface{} { return &blobHasher{state: crypto.NewKeccakState()} }, +} + +func newBlobHasher() *blobHasher { + return blobHasherPool.Get().(*blobHasher) +} + +func (h *blobHasher) hash(data []byte) common.Hash { + return crypto.HashData(h.state, data) +} + +func (h *blobHasher) release() { + blobHasherPool.Put(h) +} diff --git a/trie/sync_test.go b/trie/sync_test.go index a06d3a8b61..7841c6a0d2 100644 --- a/trie/sync_test.go +++ b/trie/sync_test.go @@ -57,7 +57,7 @@ func makeTestTrie(scheme string) (ethdb.Database, *testDb, *StateTrie, map[strin trie.MustUpdate(key, val) } } - root, nodes, _ := trie.Commit(false) + root, nodes := trie.Commit(false) if err := triedb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)); err != nil { panic(fmt.Errorf("failed to commit db %v", err)) } @@ -770,7 +770,7 @@ func testSyncMovingTarget(t *testing.T, scheme string) { srcTrie.MustUpdate(key, val) diff[string(key)] = val } - root, nodes, _ := srcTrie.Commit(false) + root, nodes := srcTrie.Commit(false) if err := srcDb.Update(root, preRoot, trienode.NewWithNodeSet(nodes)); err != nil { panic(err) } @@ -795,7 +795,7 @@ func testSyncMovingTarget(t *testing.T, scheme string) { srcTrie.MustUpdate([]byte(k), val) reverted[k] = val } - root, nodes, _ = srcTrie.Commit(false) + root, nodes = srcTrie.Commit(false) if err := srcDb.Update(root, preRoot, trienode.NewWithNodeSet(nodes)); err != nil { panic(err) } @@ -846,7 +846,7 @@ func testPivotMove(t *testing.T, scheme string, tiny bool) { writeFn([]byte{0x02, 0x34}, nil, srcTrie, stateA) writeFn([]byte{0x13, 0x44}, nil, srcTrie, stateA) - rootA, nodesA, _ := srcTrie.Commit(false) + rootA, nodesA := srcTrie.Commit(false) if err := srcTrieDB.Update(rootA, types.EmptyRootHash, trienode.NewWithNodeSet(nodesA)); err != nil { panic(err) } @@ -865,7 +865,7 @@ func testPivotMove(t *testing.T, scheme string, tiny bool) { deleteFn([]byte{0x13, 0x44}, srcTrie, stateB) writeFn([]byte{0x01, 0x24}, nil, srcTrie, stateB) - rootB, nodesB, _ := srcTrie.Commit(false) + rootB, nodesB := srcTrie.Commit(false) if err := srcTrieDB.Update(rootB, rootA, trienode.NewWithNodeSet(nodesB)); err != nil { panic(err) } @@ -883,7 +883,7 @@ func testPivotMove(t *testing.T, scheme string, tiny bool) { writeFn([]byte{0x02, 0x34}, nil, srcTrie, stateC) writeFn([]byte{0x13, 0x44}, nil, srcTrie, stateC) - rootC, nodesC, _ := srcTrie.Commit(false) + rootC, nodesC := srcTrie.Commit(false) if err := srcTrieDB.Update(rootC, rootB, trienode.NewWithNodeSet(nodesC)); err != nil { panic(err) } @@ -945,7 +945,7 @@ func testSyncAbort(t *testing.T, scheme string) { } writeFn(key, val, srcTrie, stateA) - rootA, nodesA, _ := srcTrie.Commit(false) + rootA, nodesA := srcTrie.Commit(false) if err := srcTrieDB.Update(rootA, types.EmptyRootHash, trienode.NewWithNodeSet(nodesA)); err != nil { panic(err) } @@ -962,7 +962,7 @@ func testSyncAbort(t *testing.T, scheme string) { srcTrie, _ = New(TrieID(rootA), srcTrieDB) deleteFn(key, srcTrie, stateB) - rootB, nodesB, _ := srcTrie.Commit(false) + rootB, nodesB := srcTrie.Commit(false) if err := srcTrieDB.Update(rootB, rootA, trienode.NewWithNodeSet(nodesB)); err != nil { panic(err) } @@ -989,7 +989,7 @@ func testSyncAbort(t *testing.T, scheme string) { srcTrie, _ = New(TrieID(rootB), srcTrieDB) writeFn(key, val, srcTrie, stateC) - rootC, nodesC, _ := srcTrie.Commit(false) + rootC, nodesC := srcTrie.Commit(false) if err := srcTrieDB.Update(rootC, rootB, trienode.NewWithNodeSet(nodesC)); err != nil { panic(err) } diff --git a/trie/tracer.go b/trie/tracer.go index 5786af4d3e..90b9666f0b 100644 --- a/trie/tracer.go +++ b/trie/tracer.go @@ -17,6 +17,8 @@ package trie import ( + "maps" + "github.com/ethereum/go-ethereum/common" ) @@ -92,23 +94,13 @@ func (t *tracer) reset() { // copy returns a deep copied tracer instance. func (t *tracer) copy() *tracer { - var ( - inserts = make(map[string]struct{}) - deletes = make(map[string]struct{}) - accessList = make(map[string][]byte) - ) - for path := range t.inserts { - inserts[path] = struct{}{} - } - for path := range t.deletes { - deletes[path] = struct{}{} - } + accessList := make(map[string][]byte, len(t.accessList)) for path, blob := range t.accessList { accessList[path] = common.CopyBytes(blob) } return &tracer{ - inserts: inserts, - deletes: deletes, + inserts: maps.Clone(t.inserts), + deletes: maps.Clone(t.deletes), accessList: accessList, } } diff --git a/trie/tracer_test.go b/trie/tracer_test.go index 27e42d497a..6344e481fd 100644 --- a/trie/tracer_test.go +++ b/trie/tracer_test.go @@ -52,6 +52,15 @@ var ( } ) +// copySet returns a deep-copied set. +func copySet[k comparable](set map[k][]byte) map[k][]byte { + copied := make(map[k][]byte, len(set)) + for key, val := range set { + copied[key] = common.CopyBytes(val) + } + return copied +} + func TestTrieTracer(t *testing.T) { testTrieTracer(t, tiny) testTrieTracer(t, nonAligned) @@ -70,7 +79,7 @@ func testTrieTracer(t *testing.T, vals []struct{ k, v string }) { } insertSet := copySet(trie.tracer.inserts) // copy before commit deleteSet := copySet(trie.tracer.deletes) // copy before commit - root, nodes, _ := trie.Commit(false) + root, nodes := trie.Commit(false) db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) seen := setKeys(iterNodes(db, root)) @@ -137,7 +146,7 @@ func testAccessList(t *testing.T, vals []struct{ k, v string }) { for _, val := range vals { trie.MustUpdate([]byte(val.k), []byte(val.v)) } - root, nodes, _ := trie.Commit(false) + root, nodes := trie.Commit(false) db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) trie, _ = New(TrieID(root), db) @@ -152,7 +161,7 @@ func testAccessList(t *testing.T, vals []struct{ k, v string }) { for _, val := range vals { trie.MustUpdate([]byte(val.k), randBytes(32)) } - root, nodes, _ = trie.Commit(false) + root, nodes = trie.Commit(false) db.Update(root, parent, trienode.NewWithNodeSet(nodes)) trie, _ = New(TrieID(root), db) @@ -170,7 +179,7 @@ func testAccessList(t *testing.T, vals []struct{ k, v string }) { keys = append(keys, string(key)) trie.MustUpdate(key, randBytes(32)) } - root, nodes, _ = trie.Commit(false) + root, nodes = trie.Commit(false) db.Update(root, parent, trienode.NewWithNodeSet(nodes)) trie, _ = New(TrieID(root), db) @@ -185,7 +194,7 @@ func testAccessList(t *testing.T, vals []struct{ k, v string }) { for _, key := range keys { trie.MustUpdate([]byte(key), nil) } - root, nodes, _ = trie.Commit(false) + root, nodes = trie.Commit(false) db.Update(root, parent, trienode.NewWithNodeSet(nodes)) trie, _ = New(TrieID(root), db) @@ -200,7 +209,7 @@ func testAccessList(t *testing.T, vals []struct{ k, v string }) { for _, val := range vals { trie.MustUpdate([]byte(val.k), nil) } - root, nodes, _ = trie.Commit(false) + root, nodes = trie.Commit(false) db.Update(root, parent, trienode.NewWithNodeSet(nodes)) trie, _ = New(TrieID(root), db) @@ -219,7 +228,7 @@ func TestAccessListLeak(t *testing.T) { for _, val := range standard { trie.MustUpdate([]byte(val.k), []byte(val.v)) } - root, nodes, _ := trie.Commit(false) + root, nodes := trie.Commit(false) db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) var cases = []struct { @@ -269,7 +278,7 @@ func TestTinyTree(t *testing.T) { for _, val := range tiny { trie.MustUpdate([]byte(val.k), randBytes(32)) } - root, set, _ := trie.Commit(false) + root, set := trie.Commit(false) db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(set)) parent := root @@ -278,7 +287,7 @@ func TestTinyTree(t *testing.T) { for _, val := range tiny { trie.MustUpdate([]byte(val.k), []byte(val.v)) } - root, set, _ = trie.Commit(false) + root, set = trie.Commit(false) db.Update(root, parent, trienode.NewWithNodeSet(set)) trie, _ = New(TrieID(root), db) diff --git a/trie/trie.go b/trie/trie.go index a65bd596f5..9b18e8d006 100644 --- a/trie/trie.go +++ b/trie/trie.go @@ -53,7 +53,6 @@ type Trie struct { reader *trieReader // tracer is the tool to track the trie changes. - // It will be reset after each commit operation. tracer *tracer } @@ -608,8 +607,7 @@ func (t *Trie) Hash() common.Hash { // The returned nodeset can be nil if the trie is clean (nothing to commit). // Once the trie is committed, it's not usable anymore. A new trie must // be created with new root and updated trie database for following usage -func (t *Trie) Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet, error) { - defer t.tracer.reset() +func (t *Trie) Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet) { defer func() { t.committed = true }() @@ -620,13 +618,13 @@ func (t *Trie) Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet, error) if t.root == nil { paths := t.tracer.deletedNodes() if len(paths) == 0 { - return types.EmptyRootHash, nil, nil // case (a) + return types.EmptyRootHash, nil // case (a) } nodes := trienode.NewNodeSet(t.owner) for _, path := range paths { nodes.AddNode([]byte(path), trienode.NewDeleted()) } - return types.EmptyRootHash, nodes, nil // case (b) + return types.EmptyRootHash, nodes // case (b) } // Derive the hash for all dirty nodes first. We hold the assumption // in the following procedure that all nodes are hashed. @@ -638,14 +636,14 @@ func (t *Trie) Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet, error) // Replace the root node with the origin hash in order to // ensure all resolved nodes are dropped after the commit. t.root = hashedNode - return rootHash, nil, nil + return rootHash, nil } nodes := trienode.NewNodeSet(t.owner) for _, path := range t.tracer.deletedNodes() { nodes.AddNode([]byte(path), trienode.NewDeleted()) } t.root = newCommitter(nodes, t.tracer, collectLeaf).Commit(t.root) - return rootHash, nodes, nil + return rootHash, nodes } // hashRoot calculates the root hash of the given trie @@ -663,6 +661,18 @@ func (t *Trie) hashRoot() (node, node) { return hashed, cached } +// Witness returns a set containing all trie nodes that have been accessed. +func (t *Trie) Witness() map[string]struct{} { + if len(t.tracer.accessList) == 0 { + return nil + } + witness := make(map[string]struct{}) + for _, node := range t.tracer.accessList { + witness[string(node)] = struct{}{} + } + return witness +} + // Reset drops the referenced root node and cleans all internal state. func (t *Trie) Reset() { t.root = nil diff --git a/trie/trie_reader.go b/trie/trie_reader.go index 42bc4316fe..adbf43d287 100644 --- a/trie/trie_reader.go +++ b/trie/trie_reader.go @@ -20,7 +20,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/trie/triestate" "github.com/ethereum/go-ethereum/triedb/database" ) @@ -72,23 +71,3 @@ func (r *trieReader) node(path []byte, hash common.Hash) ([]byte, error) { } return blob, nil } - -// MerkleLoader implements triestate.TrieLoader for constructing tries. -type MerkleLoader struct { - db database.Database -} - -// NewMerkleLoader creates the merkle trie loader. -func NewMerkleLoader(db database.Database) *MerkleLoader { - return &MerkleLoader{db: db} -} - -// OpenTrie opens the main account trie. -func (l *MerkleLoader) OpenTrie(root common.Hash) (triestate.Trie, error) { - return New(TrieID(root), l.db) -} - -// OpenStorageTrie opens the storage trie of an account. -func (l *MerkleLoader) OpenStorageTrie(stateRoot common.Hash, addrHash, root common.Hash) (triestate.Trie, error) { - return New(StorageTrieID(stateRoot, addrHash, root), l.db) -} diff --git a/trie/trie_test.go b/trie/trie_test.go index 6ecd20c218..5f706a28be 100644 --- a/trie/trie_test.go +++ b/trie/trie_test.go @@ -95,7 +95,7 @@ func testMissingNode(t *testing.T, memonly bool, scheme string) { trie := NewEmpty(triedb) updateString(trie, "120000", "qwerqwerqwerqwerqwerqwerqwerqwer") updateString(trie, "123456", "asdfasdfasdfasdfasdfasdfasdfasdf") - root, nodes, _ := trie.Commit(false) + root, nodes := trie.Commit(false) triedb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) if !memonly { @@ -184,7 +184,7 @@ func TestInsert(t *testing.T) { updateString(trie, "A", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") exp = common.HexToHash("d23786fb4a010da3ce639d66d5e904a11dbc02746d1ce25029e53290cabf28ab") - root, _, _ = trie.Commit(false) + root, _ = trie.Commit(false) if root != exp { t.Errorf("case 2: exp %x got %x", exp, root) } @@ -209,7 +209,7 @@ func TestGet(t *testing.T) { if i == 1 { return } - root, nodes, _ := trie.Commit(false) + root, nodes := trie.Commit(false) db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) trie, _ = New(TrieID(root), db) } @@ -282,7 +282,7 @@ func TestReplication(t *testing.T) { for _, val := range vals { updateString(trie, val.k, val.v) } - root, nodes, _ := trie.Commit(false) + root, nodes := trie.Commit(false) db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) // create a new trie on top of the database and check that lookups work. @@ -295,7 +295,7 @@ func TestReplication(t *testing.T) { t.Errorf("trie2 doesn't have %q => %q", kv.k, kv.v) } } - hash, nodes, _ := trie2.Commit(false) + hash, nodes := trie2.Commit(false) if hash != root { t.Errorf("root failure. expected %x got %x", root, hash) } @@ -531,7 +531,7 @@ func runRandTest(rt randTest) error { case opHash: tr.Hash() case opCommit: - root, nodes, _ := tr.Commit(true) + root, nodes := tr.Commit(true) if nodes != nil { triedb.Update(root, origin, trienode.NewWithNodeSet(nodes)) } @@ -768,7 +768,7 @@ func TestCommitAfterHash(t *testing.T) { if exp != root { t.Errorf("got %x, exp %x", root, exp) } - root, _, _ = trie.Commit(false) + root, _ = trie.Commit(false) if exp != root { t.Errorf("got %x, exp %x", root, exp) } @@ -820,7 +820,7 @@ func (s *spongeDb) Delete(key []byte) error { panic("implement func (s *spongeDb) NewBatch() ethdb.Batch { return &spongeBatch{s} } func (s *spongeDb) NewBatchWithSize(size int) ethdb.Batch { return &spongeBatch{s} } func (s *spongeDb) NewSnapshot() (ethdb.Snapshot, error) { panic("implement me") } -func (s *spongeDb) Stat(property string) (string, error) { panic("implement me") } +func (s *spongeDb) Stat() (string, error) { panic("implement me") } func (s *spongeDb) Compact(start []byte, limit []byte) error { panic("implement me") } func (s *spongeDb) Close() error { return nil } func (s *spongeDb) Put(key []byte, value []byte) error { @@ -886,7 +886,7 @@ func TestCommitSequence(t *testing.T) { } { addresses, accounts := makeAccounts(tc.count) // This spongeDb is used to check the sequence of disk-db-writes - s := &spongeDb{sponge: sha3.NewLegacyKeccak256()} + s := &spongeDb{sponge: crypto.NewKeccakState()} db := newTestDatabase(rawdb.NewDatabase(s), rawdb.HashScheme) trie := NewEmpty(db) // Fill the trie with elements @@ -894,7 +894,7 @@ func TestCommitSequence(t *testing.T) { trie.MustUpdate(crypto.Keccak256(addresses[i][:]), accounts[i]) } // Flush trie -> database - root, nodes, _ := trie.Commit(false) + root, nodes := trie.Commit(false) db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) // Flush memdb -> disk (sponge) db.Commit(root) @@ -917,7 +917,7 @@ func TestCommitSequenceRandomBlobs(t *testing.T) { } { prng := rand.New(rand.NewSource(int64(i))) // This spongeDb is used to check the sequence of disk-db-writes - s := &spongeDb{sponge: sha3.NewLegacyKeccak256()} + s := &spongeDb{sponge: crypto.NewKeccakState()} db := newTestDatabase(rawdb.NewDatabase(s), rawdb.HashScheme) trie := NewEmpty(db) // Fill the trie with elements @@ -935,7 +935,7 @@ func TestCommitSequenceRandomBlobs(t *testing.T) { trie.MustUpdate(key, val) } // Flush trie -> database - root, nodes, _ := trie.Commit(false) + root, nodes := trie.Commit(false) db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) // Flush memdb -> disk (sponge) db.Commit(root) @@ -984,7 +984,7 @@ func TestCommitSequenceStackTrie(t *testing.T) { stTrie.Update(key, val) } // Flush trie -> database - root, nodes, _ := trie.Commit(false) + root, nodes := trie.Commit(false) // Flush memdb -> disk (sponge) db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) db.Commit(root) @@ -1042,7 +1042,7 @@ func TestCommitSequenceSmallRoot(t *testing.T) { stTrie.Update(key, []byte{0x1}) // Flush trie -> database - root, nodes, _ := trie.Commit(false) + root, nodes := trie.Commit(false) // Flush memdb -> disk (sponge) db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) db.Commit(root) diff --git a/trie/trienode/node.go b/trie/trienode/node.go index 95315c2e9a..09f355f3b5 100644 --- a/trie/trienode/node.go +++ b/trie/trienode/node.go @@ -78,7 +78,7 @@ func NewNodeSet(owner common.Hash) *NodeSet { // ForEachWithOrder iterates the nodes with the order from bottom to top, // right to left, nodes with the longest path will be iterated first. func (set *NodeSet) ForEachWithOrder(callback func(path string, n *Node)) { - var paths []string + paths := make([]string, 0, len(set.Nodes)) for path := range set.Nodes { paths = append(paths, path) } @@ -114,7 +114,12 @@ func (set *NodeSet) Merge(owner common.Hash, nodes map[string]*Node) error { set.updates -= 1 } } - set.AddNode([]byte(path), node) + if node.IsDeleted() { + set.deletes += 1 + } else { + set.updates += 1 + } + set.Nodes[path] = node } return nil } @@ -130,30 +135,18 @@ func (set *NodeSet) Size() (int, int) { return set.updates, set.deletes } -// Hashes returns the hashes of all updated nodes. TODO(rjl493456442) how can -// we get rid of it? -func (set *NodeSet) Hashes() []common.Hash { - var ret []common.Hash - for _, node := range set.Nodes { - ret = append(ret, node.Hash) - } - return ret -} - // Summary returns a string-representation of the NodeSet. func (set *NodeSet) Summary() string { var out = new(strings.Builder) fmt.Fprintf(out, "nodeset owner: %v\n", set.Owner) - if set.Nodes != nil { - for path, n := range set.Nodes { - // Deletion - if n.IsDeleted() { - fmt.Fprintf(out, " [-]: %x\n", path) - continue - } - // Insertion or update - fmt.Fprintf(out, " [+/*]: %x -> %v \n", path, n.Hash) + for path, n := range set.Nodes { + // Deletion + if n.IsDeleted() { + fmt.Fprintf(out, " [-]: %x\n", path) + continue } + // Insertion or update + fmt.Fprintf(out, " [+/*]: %x -> %v \n", path, n.Hash) } for _, n := range set.Leaves { fmt.Fprintf(out, "[leaf]: %v\n", n) @@ -191,7 +184,7 @@ func (set *MergedNodeSet) Merge(other *NodeSet) error { // Flatten returns a two-dimensional map for internal nodes. func (set *MergedNodeSet) Flatten() map[common.Hash]map[string]*Node { - nodes := make(map[common.Hash]map[string]*Node) + nodes := make(map[common.Hash]map[string]*Node, len(set.Sets)) for owner, set := range set.Sets { nodes[owner] = set.Nodes } diff --git a/trie/trienode/node_test.go b/trie/trienode/node_test.go new file mode 100644 index 0000000000..bcb3a2202b --- /dev/null +++ b/trie/trienode/node_test.go @@ -0,0 +1,61 @@ +// 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 + +package trienode + +import ( + "crypto/rand" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" +) + +func BenchmarkMerge(b *testing.B) { + b.Run("1K", func(b *testing.B) { + benchmarkMerge(b, 1000) + }) + b.Run("10K", func(b *testing.B) { + benchmarkMerge(b, 10_000) + }) +} + +func benchmarkMerge(b *testing.B, count int) { + x := NewNodeSet(common.Hash{}) + y := NewNodeSet(common.Hash{}) + addNode := func(s *NodeSet) { + path := make([]byte, 4) + rand.Read(path) + blob := make([]byte, 32) + rand.Read(blob) + hash := crypto.Keccak256Hash(blob) + s.AddNode(path, New(hash, blob)) + } + for i := 0; i < count; i++ { + // Random path of 4 nibbles + addNode(x) + addNode(y) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + // Store set x into a backup + z := NewNodeSet(common.Hash{}) + z.Merge(common.Hash{}, x.Nodes) + // Merge y into x + x.Merge(common.Hash{}, y.Nodes) + x = z + } +} diff --git a/trie/trienode/proof.go b/trie/trienode/proof.go index 012f0087dd..d3075ecccf 100644 --- a/trie/trienode/proof.go +++ b/trie/trienode/proof.go @@ -102,14 +102,14 @@ func (db *ProofSet) DataSize() int { return db.dataSize } -// List converts the node set to a ProofList -func (db *ProofSet) List() ProofList { +// List converts the node set to a slice of bytes. +func (db *ProofSet) List() [][]byte { db.lock.RLock() defer db.lock.RUnlock() - var values ProofList - for _, key := range db.order { - values = append(values, db.nodes[key]) + values := make([][]byte, len(db.order)) + for i, key := range db.order { + values[i] = db.nodes[key] } return values } diff --git a/trie/triestate/state.go b/trie/triestate/state.go index aa4d32f852..62a9043873 100644 --- a/trie/triestate/state.go +++ b/trie/triestate/state.go @@ -16,44 +16,7 @@ package triestate -import ( - "errors" - "fmt" - "sync" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/rlp" - "github.com/ethereum/go-ethereum/trie/trienode" - "golang.org/x/crypto/sha3" -) - -// Trie is an Ethereum state trie, can be implemented by Ethereum Merkle Patricia -// tree or Verkle tree. -type Trie interface { - // Get returns the value for key stored in the trie. - Get(key []byte) ([]byte, error) - - // Update associates key with value in the trie. - Update(key, value []byte) error - - // Delete removes any existing value for key from the trie. - Delete(key []byte) error - - // Commit the trie and returns a set of dirty nodes generated along with - // the new root hash. - Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet, error) -} - -// TrieLoader wraps functions to load tries. -type TrieLoader interface { - // OpenTrie opens the main account trie. - OpenTrie(root common.Hash) (Trie, error) - - // OpenStorageTrie opens the storage trie of an account. - OpenStorageTrie(stateRoot common.Hash, addrHash, root common.Hash) (Trie, error) -} +import "github.com/ethereum/go-ethereum/common" // Set represents a collection of mutated states during a state transition. // The value refers to the original content of state before the transition @@ -88,186 +51,3 @@ func (s *Set) Size() common.StorageSize { } return s.size } - -// context wraps all fields for executing state diffs. -type context struct { - prevRoot common.Hash - postRoot common.Hash - accounts map[common.Address][]byte - storages map[common.Address]map[common.Hash][]byte - accountTrie Trie - nodes *trienode.MergedNodeSet -} - -// Apply traverses the provided state diffs, apply them in the associated -// post-state and return the generated dirty trie nodes. The state can be -// loaded via the provided trie loader. -func Apply(prevRoot common.Hash, postRoot common.Hash, accounts map[common.Address][]byte, storages map[common.Address]map[common.Hash][]byte, loader TrieLoader) (map[common.Hash]map[string]*trienode.Node, error) { - tr, err := loader.OpenTrie(postRoot) - if err != nil { - return nil, err - } - ctx := &context{ - prevRoot: prevRoot, - postRoot: postRoot, - accounts: accounts, - storages: storages, - accountTrie: tr, - nodes: trienode.NewMergedNodeSet(), - } - for addr, account := range accounts { - var err error - if len(account) == 0 { - err = deleteAccount(ctx, loader, addr) - } else { - err = updateAccount(ctx, loader, addr) - } - if err != nil { - return nil, fmt.Errorf("failed to revert state, err: %w", err) - } - } - root, result, err := tr.Commit(false) - if err != nil { - return nil, err - } - if root != prevRoot { - return nil, fmt.Errorf("failed to revert state, want %#x, got %#x", prevRoot, root) - } - if err := ctx.nodes.Merge(result); err != nil { - return nil, err - } - return ctx.nodes.Flatten(), nil -} - -// updateAccount the account was present in prev-state, and may or may not -// existent in post-state. Apply the reverse diff and verify if the storage -// root matches the one in prev-state account. -func updateAccount(ctx *context, loader TrieLoader, addr common.Address) error { - // The account was present in prev-state, decode it from the - // 'slim-rlp' format bytes. - h := newHasher() - defer h.release() - - addrHash := h.hash(addr.Bytes()) - prev, err := types.FullAccount(ctx.accounts[addr]) - if err != nil { - return err - } - // The account may or may not existent in post-state, try to - // load it and decode if it's found. - blob, err := ctx.accountTrie.Get(addrHash.Bytes()) - if err != nil { - return err - } - post := types.NewEmptyStateAccount() - if len(blob) != 0 { - if err := rlp.DecodeBytes(blob, &post); err != nil { - return err - } - } - // Apply all storage changes into the post-state storage trie. - st, err := loader.OpenStorageTrie(ctx.postRoot, addrHash, post.Root) - if err != nil { - return err - } - for key, val := range ctx.storages[addr] { - var err error - if len(val) == 0 { - err = st.Delete(key.Bytes()) - } else { - err = st.Update(key.Bytes(), val) - } - if err != nil { - return err - } - } - root, result, err := st.Commit(false) - if err != nil { - return err - } - if root != prev.Root { - return errors.New("failed to reset storage trie") - } - // The returned set can be nil if storage trie is not changed - // at all. - if result != nil { - if err := ctx.nodes.Merge(result); err != nil { - return err - } - } - // Write the prev-state account into the main trie - full, err := rlp.EncodeToBytes(prev) - if err != nil { - return err - } - return ctx.accountTrie.Update(addrHash.Bytes(), full) -} - -// deleteAccount the account was not present in prev-state, and is expected -// to be existent in post-state. Apply the reverse diff and verify if the -// account and storage is wiped out correctly. -func deleteAccount(ctx *context, loader TrieLoader, addr common.Address) error { - // The account must be existent in post-state, load the account. - h := newHasher() - defer h.release() - - addrHash := h.hash(addr.Bytes()) - blob, err := ctx.accountTrie.Get(addrHash.Bytes()) - if err != nil { - return err - } - if len(blob) == 0 { - return fmt.Errorf("account is non-existent %#x", addrHash) - } - var post types.StateAccount - if err := rlp.DecodeBytes(blob, &post); err != nil { - return err - } - st, err := loader.OpenStorageTrie(ctx.postRoot, addrHash, post.Root) - if err != nil { - return err - } - for key, val := range ctx.storages[addr] { - if len(val) != 0 { - return errors.New("expect storage deletion") - } - if err := st.Delete(key.Bytes()); err != nil { - return err - } - } - root, result, err := st.Commit(false) - if err != nil { - return err - } - if root != types.EmptyRootHash { - return errors.New("failed to clear storage trie") - } - // The returned set can be nil if storage trie is not changed - // at all. - if result != nil { - if err := ctx.nodes.Merge(result); err != nil { - return err - } - } - // Delete the post-state account from the main trie. - return ctx.accountTrie.Delete(addrHash.Bytes()) -} - -// hasher is used to compute the sha256 hash of the provided data. -type hasher struct{ sha crypto.KeccakState } - -var hasherPool = sync.Pool{ - New: func() interface{} { return &hasher{sha: sha3.NewLegacyKeccak256().(crypto.KeccakState)} }, -} - -func newHasher() *hasher { - return hasherPool.Get().(*hasher) -} - -func (h *hasher) hash(data []byte) common.Hash { - return crypto.HashData(h.sha, data) -} - -func (h *hasher) release() { - hasherPool.Put(h) -} diff --git a/trie/utils/verkle.go b/trie/utils/verkle.go index 328b2d2527..2a4a632d49 100644 --- a/trie/utils/verkle.go +++ b/trie/utils/verkle.go @@ -23,7 +23,7 @@ import ( "github.com/crate-crypto/go-ipa/bandersnatch/fr" "github.com/ethereum/go-ethereum/common/lru" "github.com/ethereum/go-ethereum/metrics" - "github.com/gballet/go-verkle" + "github.com/ethereum/go-verkle" "github.com/holiman/uint256" ) @@ -219,7 +219,7 @@ func CodeChunkKey(address []byte, chunk *uint256.Int) []byte { return GetTreeKey(address, treeIndex, subIndex) } -func storageIndex(bytes []byte) (*uint256.Int, byte) { +func StorageIndex(bytes []byte) (*uint256.Int, byte) { // If the storage slot is in the header, we need to add the header offset. var key uint256.Int key.SetBytes(bytes) @@ -245,7 +245,7 @@ func storageIndex(bytes []byte) (*uint256.Int, byte) { // StorageSlotKey returns the verkle tree key of the storage slot for the // specified account. func StorageSlotKey(address []byte, storageKey []byte) []byte { - treeIndex, subIndex := storageIndex(storageKey) + treeIndex, subIndex := StorageIndex(storageKey) return GetTreeKey(address, treeIndex, subIndex) } @@ -296,7 +296,7 @@ func CodeChunkKeyWithEvaluatedAddress(addressPoint *verkle.Point, chunk *uint256 // slot for the specified account. The difference between StorageSlotKey is the // address evaluation is already computed to minimize the computational overhead. func StorageSlotKeyWithEvaluatedAddress(evaluated *verkle.Point, storageKey []byte) []byte { - treeIndex, subIndex := storageIndex(storageKey) + treeIndex, subIndex := StorageIndex(storageKey) return GetTreeKeyWithEvaluatedAddress(evaluated, treeIndex, subIndex) } diff --git a/trie/utils/verkle_test.go b/trie/utils/verkle_test.go index 28b059c379..c29504a6d0 100644 --- a/trie/utils/verkle_test.go +++ b/trie/utils/verkle_test.go @@ -20,7 +20,7 @@ import ( "bytes" "testing" - "github.com/gballet/go-verkle" + "github.com/ethereum/go-verkle" "github.com/holiman/uint256" ) diff --git a/trie/verkle.go b/trie/verkle.go index 01d813d9ec..a457097e95 100644 --- a/trie/verkle.go +++ b/trie/verkle.go @@ -27,7 +27,7 @@ import ( "github.com/ethereum/go-ethereum/trie/trienode" "github.com/ethereum/go-ethereum/trie/utils" "github.com/ethereum/go-ethereum/triedb/database" - "github.com/gballet/go-verkle" + "github.com/ethereum/go-verkle" "github.com/holiman/uint256" ) @@ -144,10 +144,8 @@ func (t *VerkleTrie) UpdateAccount(addr common.Address, acc *types.StateAccount) // Encode balance in little-endian bytes := acc.Balance.Bytes() - if len(bytes) > 0 { - for i, b := range bytes { - balance[len(bytes)-i-1] = b - } + for i, b := range bytes { + balance[len(bytes)-i-1] = b } values[utils.BalanceLeafKey] = balance[:] @@ -217,22 +215,21 @@ func (t *VerkleTrie) Hash() common.Hash { } // Commit writes all nodes to the tree's memory database. -func (t *VerkleTrie) Commit(_ bool) (common.Hash, *trienode.NodeSet, error) { - root, ok := t.root.(*verkle.InternalNode) - if !ok { - return common.Hash{}, nil, errors.New("unexpected root node type") - } +func (t *VerkleTrie) Commit(_ bool) (common.Hash, *trienode.NodeSet) { + root := t.root.(*verkle.InternalNode) nodes, err := root.BatchSerialize() if err != nil { - return common.Hash{}, nil, fmt.Errorf("serializing tree nodes: %s", err) + // Error return from this function indicates error in the code logic + // of BatchSerialize, and we fail catastrophically if this is the case. + panic(fmt.Errorf("BatchSerialize failed: %v", err)) } nodeset := trienode.NewNodeSet(common.Hash{}) for _, node := range nodes { - // hash parameter is not used in pathdb + // Hash parameter is not used in pathdb nodeset.AddNode(node.Path, trienode.New(common.Hash{}, node.SerializedBytes)) } // Serialize root commitment form - return t.Hash(), nodeset, nil + return t.Hash(), nodeset } // NodeIterator implements state.Trie, returning an iterator that returns @@ -370,3 +367,8 @@ func (t *VerkleTrie) ToDot() string { func (t *VerkleTrie) nodeResolver(path []byte) ([]byte, error) { return t.reader.node(path, common.Hash{}) } + +// Witness returns a set containing all trie nodes that have been accessed. +func (t *VerkleTrie) Witness() map[string]struct{} { + panic("not implemented") +} diff --git a/triedb/database.go b/triedb/database.go index f1b8b7e253..612d29e909 100644 --- a/triedb/database.go +++ b/triedb/database.go @@ -52,9 +52,6 @@ var HashDefaults = &Config{ // backend defines the methods needed to access/update trie nodes in different // state scheme. type backend interface { - // Scheme returns the identifier of used storage scheme. - Scheme() string - // Initialized returns an indicator if the state data is already initialized // according to the state scheme. Initialized(genesisRoot common.Hash) bool @@ -77,6 +74,10 @@ type backend interface { // Close closes the trie database backend and releases all held resources. Close() error + + // Reader returns a reader for accessing all trie nodes with provided state + // root. An error will be returned if the requested state is not available. + Reader(root common.Hash) (database.Reader, error) } // Database is the wrapper of the underlying backend which is shared by different @@ -175,13 +176,7 @@ func (db *Database) DiskDB() ethdb.Database { // Reader returns a reader for accessing all trie nodes with provided state root. // An error will be returned if the requested state is not available. func (db *Database) Reader(blockRoot common.Hash) (database.Reader, error) { - switch b := db.backend.(type) { - case *hashdb.Database: - return b.Reader(blockRoot) - case *pathdb.Database: - return b.Reader(blockRoot) - } - return nil, errors.New("unknown backend") + return db.backend.Reader(blockRoot) } // Update performs a state transition by committing dirty nodes contained in the @@ -231,7 +226,10 @@ func (db *Database) Initialized(genesisRoot common.Hash) bool { // Scheme returns the node scheme used in the database. func (db *Database) Scheme() string { - return db.backend.Scheme() + if db.config.PathDB != nil { + return rawdb.PathScheme + } + return rawdb.HashScheme } // Close flushes the dangling preimages to disk and closes the trie database. @@ -315,14 +313,7 @@ func (db *Database) Recover(target common.Hash) error { if !ok { return errors.New("not supported") } - var loader triestate.TrieLoader - if db.config.IsVerkle { - // TODO define verkle loader - log.Crit("Verkle loader is not defined") - } else { - loader = trie.NewMerkleLoader(db) - } - return pdb.Recover(target, loader) + return pdb.Recover(target) } // Recoverable returns the indicator if the specified state is enabled to be diff --git a/triedb/database/database.go b/triedb/database/database.go index 18a8f454e2..9bd5da08d1 100644 --- a/triedb/database/database.go +++ b/triedb/database/database.go @@ -16,32 +16,21 @@ package database -import ( - "github.com/ethereum/go-ethereum/common" -) +import "github.com/ethereum/go-ethereum/common" // Reader wraps the Node method of a backing trie reader. type Reader interface { // Node retrieves the trie node blob with the provided trie identifier, // node path and the corresponding node hash. No error will be returned // if the node is not found. + // + // Don't modify the returned byte slice since it's not deep-copied and + // still be referenced by database. Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, error) } -// PreimageStore wraps the methods of a backing store for reading and writing -// trie node preimages. -type PreimageStore interface { - // Preimage retrieves the preimage of the specified hash. - Preimage(hash common.Hash) []byte - - // InsertPreimage commits a set of preimages along with their hashes. - InsertPreimage(preimages map[common.Hash][]byte) -} - // Database wraps the methods of a backing trie store. type Database interface { - PreimageStore - // Reader returns a node reader associated with the specific state. // An error will be returned if the specified state is not available. Reader(stateRoot common.Hash) (Reader, error) diff --git a/triedb/hashdb/database.go b/triedb/hashdb/database.go index 366cbb4756..d7c203eca1 100644 --- a/triedb/hashdb/database.go +++ b/triedb/hashdb/database.go @@ -33,6 +33,7 @@ import ( "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie/trienode" "github.com/ethereum/go-ethereum/trie/triestate" + "github.com/ethereum/go-ethereum/triedb/database" ) var ( @@ -625,14 +626,9 @@ func (db *Database) Close() error { return nil } -// Scheme returns the node scheme used in the database. -func (db *Database) Scheme() string { - return rawdb.HashScheme -} - // Reader retrieves a node reader belonging to the given state root. // An error will be returned if the requested state is not available. -func (db *Database) Reader(root common.Hash) (*reader, error) { +func (db *Database) Reader(root common.Hash) (database.Reader, error) { if _, err := db.node(root); err != nil { return nil, fmt.Errorf("state %#x is not available, %v", root, err) } diff --git a/triedb/pathdb/asyncnodebuffer.go b/triedb/pathdb/asyncnodebuffer.go index 7955836c9f..71c90991bb 100644 --- a/triedb/pathdb/asyncnodebuffer.go +++ b/triedb/pathdb/asyncnodebuffer.go @@ -1,6 +1,7 @@ package pathdb import ( + "bytes" "errors" "fmt" "sync" @@ -411,14 +412,13 @@ func (nc *nodecache) revert(db ethdb.KeyValueReader, nodes map[common.Hash]map[s // // In case of database rollback, don't panic if this "clean" // node occurs which is not present in buffer. - var nhash common.Hash + var blob []byte if owner == (common.Hash{}) { - _, nhash = rawdb.ReadAccountTrieNode(db, []byte(path)) + blob = rawdb.ReadAccountTrieNode(db, []byte(path)) } else { - _, nhash = rawdb.ReadStorageTrieNode(db, owner, []byte(path)) + blob = rawdb.ReadStorageTrieNode(db, owner, []byte(path)) } - // Ignore the clean node in the case described above. - if nhash == n.Hash { + if bytes.Equal(blob, n.Blob) { continue } panic(fmt.Sprintf("non-existent node (%x %v) blob: %v", owner, path, crypto.Keccak256Hash(n.Blob).Hex())) diff --git a/triedb/pathdb/database.go b/triedb/pathdb/database.go index 6f538f2e84..b80d4b72f3 100644 --- a/triedb/pathdb/database.go +++ b/triedb/pathdb/database.go @@ -29,6 +29,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" @@ -153,15 +154,15 @@ type Database struct { // readOnly is the flag whether the mutation is allowed to be applied. // It will be set automatically when the database is journaled during // the shutdown to reject all following unexpected mutations. - readOnly bool // Flag if database is opened in read only mode - waitSync bool // Flag if database is deactivated due to initial state sync - isVerkle bool // Flag if database is used for verkle tree - bufferSize int // Memory allowance (in bytes) for caching dirty nodes - config *Config // Configuration for database - diskdb ethdb.Database // Persistent storage for matured trie nodes - tree *layerTree // The group for all known layers - freezer *rawdb.ResettableFreezer // Freezer for storing trie histories, nil possible in tests - lock sync.RWMutex // Lock to prevent mutations from happening at the same time + readOnly bool // Flag if database is opened in read only mode + waitSync bool // Flag if database is deactivated due to initial state sync + isVerkle bool // Flag if database is used for verkle tree + bufferSize int // Memory allowance (in bytes) for caching dirty nodes + config *Config // Configuration for database + diskdb ethdb.Database // Persistent storage for matured trie nodes + tree *layerTree // The group for all known layers + freezer ethdb.ResettableAncientStore // Freezer for storing trie histories, nil possible in tests + lock sync.RWMutex // Lock to prevent mutations from happening at the same time } // New attempts to load an already existing layer from a persistent key-value @@ -184,46 +185,10 @@ func New(diskdb ethdb.Database, config *Config, isVerkle bool) *Database { // and in-memory layer journal. db.tree = newLayerTree(db.loadLayers()) - // Open the freezer for state history if the passed database contains an - // ancient store. Otherwise, all the relevant functionalities are disabled. - // - // Because the freezer can only be opened once at the same time, this - // mechanism also ensures that at most one **non-readOnly** database - // is opened at the same time to prevent accidental mutation. - if ancient, err := diskdb.AncientDatadir(); err == nil && ancient != "" && !db.readOnly && !config.NoTries { - offset := uint64(0) // differ from in block data, only metadata is used in state data - freezer, err := rawdb.NewStateFreezer(ancient, false, offset) - if err != nil { - log.Crit("Failed to open state history freezer", "err", err) - } - db.freezer = freezer - - diskLayerID := db.tree.bottom().stateID() - if diskLayerID == 0 { - // Reset the entire state histories in case the trie database is - // not initialized yet, as these state histories are not expected. - frozen, err := db.freezer.Ancients() - if err != nil { - log.Crit("Failed to retrieve head of state history", "err", err) - } - if frozen != 0 { - err := db.freezer.Reset() - if err != nil { - log.Crit("Failed to reset state histories", "err", err) - } - log.Info("Truncated extraneous state history") - } - } else { - // Truncate the extra state histories above in freezer in case - // it's not aligned with the disk layer. - pruned, err := truncateFromHead(db.diskdb, freezer, diskLayerID) - if err != nil { - log.Crit("Failed to truncate extra state histories", "err", err) - } - if pruned != 0 { - log.Warn("Truncated extra state histories", "number", pruned) - } - } + // Repair the state history, which might not be aligned with the state + // in the key-value store due to an unclean shutdown. + if err := db.repairHistory(); err != nil { + log.Crit("Failed to repair pathdb", "err", err) } // Disable database in case node is still in the initial state sync stage. if rawdb.ReadSnapSyncStatusFlag(diskdb) == rawdb.StateSyncRunning && !db.readOnly { @@ -234,6 +199,59 @@ func New(diskdb ethdb.Database, config *Config, isVerkle bool) *Database { return db } +// repairHistory truncates leftover state history objects, which may occur due +// to an unclean shutdown or other unexpected reasons. +func (db *Database) repairHistory() error { + if !db.config.NoTries { + return nil + } + // Open the freezer for state history. This mechanism ensures that + // only one database instance can be opened at a time to prevent + // accidental mutation. + ancient, err := db.diskdb.AncientDatadir() + if err != nil { + // TODO error out if ancient store is disabled. A tons of unit tests + // disable the ancient store thus the error here will immediately fail + // all of them. Fix the tests first. + return nil + } + offset := uint64(0) // differ from in block data, only metadata is used in state data + freezer, err := rawdb.NewStateFreezer(ancient, db.readOnly, offset) + if err != nil { + log.Crit("Failed to open state history freezer", "err", err) + } + db.freezer = freezer + + // Reset the entire state histories if the trie database is not initialized + // yet. This action is necessary because these state histories are not + // expected to exist without an initialized trie database. + id := db.tree.bottom().stateID() + if id == 0 { + frozen, err := db.freezer.Ancients() + if err != nil { + log.Crit("Failed to retrieve head of state history", "err", err) + } + if frozen != 0 { + err := db.freezer.Reset() + if err != nil { + log.Crit("Failed to reset state histories", "err", err) + } + log.Info("Truncated extraneous state history") + } + return nil + } + // Truncate the extra state histories above in freezer in case it's not + // aligned with the disk layer. It might happen after a unclean shutdown. + pruned, err := truncateFromHead(db.diskdb, db.freezer, id) + if err != nil { + log.Crit("Failed to truncate extra state histories", "err", err) + } + if pruned != 0 { + log.Warn("Truncated extra state histories", "number", pruned) + } + return nil +} + // Update adds a new layer into the tree, if that can be linked to an existing // old parent. It is disallowed to insert a disk layer (the origin of all). Apart // from that this function will flatten the extra diff layers at bottom into disk @@ -315,8 +333,10 @@ func (db *Database) Enable(root common.Hash) error { } // Ensure the provided state root matches the stored one. root = types.TrieRootHash(root) - _, stored := rawdb.ReadAccountTrieNode(db.diskdb, nil) - stored = types.TrieRootHash(stored) + stored := types.EmptyRootHash + if blob := rawdb.ReadAccountTrieNode(db.diskdb, nil); len(blob) > 0 { + stored = crypto.Keccak256Hash(blob) + } if stored != root { return fmt.Errorf("state root mismatch: stored %x, synced %x", stored, root) } @@ -352,7 +372,7 @@ func (db *Database) Enable(root common.Hash) error { // Recover rollbacks the database to a specified historical point. // The state is supported as the rollback destination only if it's // canonical state and the corresponding trie histories are existent. -func (db *Database) Recover(root common.Hash, loader triestate.TrieLoader) error { +func (db *Database) Recover(root common.Hash) error { db.lock.Lock() defer db.lock.Unlock() @@ -378,7 +398,7 @@ func (db *Database) Recover(root common.Hash, loader triestate.TrieLoader) error if err != nil { return err } - dl, err = dl.revert(h, loader) + dl, err = dl.revert(h) if err != nil { return err } diff --git a/triedb/pathdb/database_test.go b/triedb/pathdb/database_test.go index 959d4aa895..be2c052047 100644 --- a/triedb/pathdb/database_test.go +++ b/triedb/pathdb/database_test.go @@ -29,28 +29,31 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/internal/testrand" "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie/trienode" "github.com/ethereum/go-ethereum/trie/triestate" "github.com/holiman/uint256" ) -func updateTrie(addrHash common.Hash, root common.Hash, dirties, cleans map[common.Hash][]byte) (common.Hash, *trienode.NodeSet) { - h, err := newTestHasher(addrHash, root, cleans) +func updateTrie(db *Database, stateRoot common.Hash, addrHash common.Hash, root common.Hash, dirties map[common.Hash][]byte) (common.Hash, *trienode.NodeSet) { + var id *trie.ID + if addrHash == (common.Hash{}) { + id = trie.StateTrieID(stateRoot) + } else { + id = trie.StorageTrieID(stateRoot, addrHash, root) + } + tr, err := trie.New(id, db) if err != nil { - panic(fmt.Errorf("failed to create hasher, err: %w", err)) + panic(fmt.Errorf("failed to load trie, err: %w", err)) } for key, val := range dirties { if len(val) == 0 { - h.Delete(key.Bytes()) + tr.Delete(key.Bytes()) } else { - h.Update(key.Bytes(), val) + tr.Update(key.Bytes(), val) } } - root, nodes, err := h.Commit(false) - if err != nil { - panic(fmt.Errorf("failed to commit hasher, err: %w", err)) - } - return root, nodes + return tr.Commit(false) } func generateAccount(storageRoot common.Hash) types.StateAccount { @@ -70,6 +73,7 @@ const ( ) type genctx struct { + stateRoot common.Hash accounts map[common.Hash][]byte storages map[common.Hash]map[common.Hash][]byte accountOrigin map[common.Address][]byte @@ -77,8 +81,9 @@ type genctx struct { nodes *trienode.MergedNodeSet } -func newCtx() *genctx { +func newCtx(stateRoot common.Hash) *genctx { return &genctx{ + stateRoot: stateRoot, accounts: make(map[common.Hash][]byte), storages: make(map[common.Hash]map[common.Hash][]byte), accountOrigin: make(map[common.Address][]byte), @@ -116,7 +121,7 @@ func newTester(t *testing.T, historyLimit uint64) *tester { snapStorages: make(map[common.Hash]map[common.Hash]map[common.Hash][]byte), } ) - for i := 0; i < 8; i++ { + for i := 0; i < 12; i++ { var parent = types.EmptyRootHash if len(obj.roots) != 0 { parent = obj.roots[len(obj.roots)-1] @@ -155,7 +160,7 @@ func (t *tester) generateStorage(ctx *genctx, addr common.Address) common.Hash { storage[hash] = v origin[hash] = nil } - root, set := updateTrie(addrHash, types.EmptyRootHash, storage, nil) + root, set := updateTrie(t.db, ctx.stateRoot, addrHash, types.EmptyRootHash, storage) ctx.storages[addrHash] = storage ctx.storageOrigin[addr] = origin @@ -184,7 +189,7 @@ func (t *tester) mutateStorage(ctx *genctx, addr common.Address, root common.Has storage[hash] = v origin[hash] = nil } - root, set := updateTrie(crypto.Keccak256Hash(addr.Bytes()), root, storage, t.storages[addrHash]) + root, set := updateTrie(t.db, ctx.stateRoot, crypto.Keccak256Hash(addr.Bytes()), root, storage) ctx.storages[addrHash] = storage ctx.storageOrigin[addr] = origin @@ -202,7 +207,7 @@ func (t *tester) clearStorage(ctx *genctx, addr common.Address, root common.Hash origin[hash] = val storage[hash] = nil } - root, set := updateTrie(addrHash, root, storage, t.storages[addrHash]) + root, set := updateTrie(t.db, ctx.stateRoot, addrHash, root, storage) if root != types.EmptyRootHash { panic("failed to clear storage trie") } @@ -214,7 +219,7 @@ func (t *tester) clearStorage(ctx *genctx, addr common.Address, root common.Hash func (t *tester) generate(parent common.Hash) (common.Hash, *trienode.MergedNodeSet, *triestate.Set) { var ( - ctx = newCtx() + ctx = newCtx(parent) dirties = make(map[common.Hash]struct{}) ) for i := 0; i < 20; i++ { @@ -275,7 +280,7 @@ func (t *tester) generate(parent common.Hash) (common.Hash, *trienode.MergedNode ctx.accountOrigin[addr] = account } } - root, set := updateTrie(common.Hash{}, parent, ctx.accounts, t.accounts) + root, set := updateTrie(t.db, parent, common.Hash{}, parent, ctx.accounts) ctx.nodes.Merge(set) // Save state snapshot before commit @@ -301,6 +306,9 @@ func (t *tester) generate(parent common.Hash) (common.Hash, *trienode.MergedNode t.storages[addrHash][sHash] = slot } } + if len(t.storages[addrHash]) == 0 { + delete(t.storages, addrHash) + } } return root, ctx.nodes, triestate.New(ctx.accountOrigin, ctx.storageOrigin) } @@ -314,25 +322,31 @@ func (t *tester) lastHash() common.Hash { } func (t *tester) verifyState(root common.Hash) error { - reader, err := t.db.Reader(root) + tr, err := trie.New(trie.StateTrieID(root), t.db) if err != nil { return err } - _, err = reader.Node(common.Hash{}, nil, root) - if err != nil { - return errors.New("root node is not available") - } for addrHash, account := range t.snapAccounts[root] { - path := crypto.Keccak256(addrHash.Bytes()) - blob, err := reader.Node(common.Hash{}, path, crypto.Keccak256Hash(account)) + blob, err := tr.Get(addrHash.Bytes()) if err != nil || !bytes.Equal(blob, account) { return fmt.Errorf("account is mismatched: %w", err) } } for addrHash, slots := range t.snapStorages[root] { + blob := t.snapAccounts[root][addrHash] + if len(blob) == 0 { + return fmt.Errorf("account %x is missing", addrHash) + } + account := new(types.StateAccount) + if err := rlp.DecodeBytes(blob, account); err != nil { + return err + } + storageIt, err := trie.New(trie.StorageTrieID(root, addrHash, account.Root), t.db) + if err != nil { + return err + } for hash, slot := range slots { - path := crypto.Keccak256(hash.Bytes()) - blob, err := reader.Node(addrHash, path, crypto.Keccak256Hash(slot)) + blob, err := storageIt.Get(hash.Bytes()) if err != nil || !bytes.Equal(blob, slot) { return fmt.Errorf("slot is mismatched: %w", err) } @@ -399,13 +413,11 @@ func TestDatabaseRollback(t *testing.T) { } // Revert database from top to bottom for i := tester.bottomIndex(); i >= 0; i-- { - root := tester.roots[i] parent := types.EmptyRootHash if i > 0 { parent = tester.roots[i-1] } - loader := newHashLoader(tester.snapAccounts[root], tester.snapStorages[root]) - if err := tester.db.Recover(parent, loader); err != nil { + if err := tester.db.Recover(parent); err != nil { t.Fatalf("Failed to revert db, err: %v", err) } if i > 0 { @@ -474,7 +486,7 @@ func TestDisable(t *testing.T) { tester := newTester(t, 0) defer tester.release() - _, stored := rawdb.ReadAccountTrieNode(tester.db.diskdb, nil) + stored := crypto.Keccak256Hash(rawdb.ReadAccountTrieNode(tester.db.diskdb, nil)) if err := tester.db.Disable(); err != nil { t.Fatalf("Failed to deactivate database: %v", err) } @@ -580,7 +592,7 @@ func TestCorruptedJournal(t *testing.T) { t.Errorf("Failed to journal, err: %v", err) } tester.db.Close() - _, root := rawdb.ReadAccountTrieNode(tester.db.diskdb, nil) + root := crypto.Keccak256Hash(rawdb.ReadAccountTrieNode(tester.db.diskdb, nil)) // Mutate the journal in disk, it should be regarded as invalid blob := rawdb.ReadTrieJournal(tester.db.diskdb) diff --git a/triedb/pathdb/difflayer_test.go b/triedb/pathdb/difflayer_test.go index 2e14593cd0..14e021c88c 100644 --- a/triedb/pathdb/difflayer_test.go +++ b/triedb/pathdb/difflayer_test.go @@ -70,10 +70,10 @@ func benchmarkSearch(b *testing.B, depth int, total int) { blob = testrand.Bytes(100) node = trienode.New(crypto.Keccak256Hash(blob), blob) ) - nodes[common.Hash{}][string(path)] = trienode.New(node.Hash, node.Blob) + nodes[common.Hash{}][string(path)] = node if npath == nil && depth == index { npath = common.CopyBytes(path) - nblob = common.CopyBytes(node.Blob) + nblob = common.CopyBytes(blob) } } return newDiffLayer(parent, common.Hash{}, 0, 0, nodes, nil) @@ -116,7 +116,7 @@ func BenchmarkPersist(b *testing.B) { blob = testrand.Bytes(100) node = trienode.New(crypto.Keccak256Hash(blob), blob) ) - nodes[common.Hash{}][string(path)] = trienode.New(node.Hash, node.Blob) + nodes[common.Hash{}][string(path)] = node } return newDiffLayer(parent, common.Hash{}, 0, 0, nodes, nil) } @@ -154,7 +154,7 @@ func BenchmarkJournal(b *testing.B) { blob = testrand.Bytes(100) node = trienode.New(crypto.Keccak256Hash(blob), blob) ) - nodes[common.Hash{}][string(path)] = trienode.New(node.Hash, node.Blob) + nodes[common.Hash{}][string(path)] = node } // TODO(rjl493456442) a non-nil state set is expected. return newDiffLayer(parent, common.Hash{}, 0, 0, nodes, nil) diff --git a/triedb/pathdb/disklayer.go b/triedb/pathdb/disklayer.go index dd92af15e2..e41beb9122 100644 --- a/triedb/pathdb/disklayer.go +++ b/triedb/pathdb/disklayer.go @@ -28,7 +28,6 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/trie/trienode" "github.com/ethereum/go-ethereum/trie/triestate" - "golang.org/x/crypto/sha3" ) // trienodebuffer is a collection of modified trie nodes to aggregate the disk @@ -172,12 +171,12 @@ func (dl *diskLayer) node(owner common.Hash, path []byte, depth int) ([]byte, co dirtyMissMeter.Mark(1) // Try to retrieve the trie node from the clean memory cache + h := newHasher() + defer h.release() + key := cacheKey(owner, path) if dl.cleans != nil { if blob := dl.cleans.Get(nil, key); len(blob) > 0 { - h := newHasher() - defer h.release() - cleanHitMeter.Mark(1) cleanReadMeter.Mark(int64(len(blob))) return blob, h.hash(blob), &nodeLoc{loc: locCleanCache, depth: depth}, nil @@ -185,20 +184,18 @@ func (dl *diskLayer) node(owner common.Hash, path []byte, depth int) ([]byte, co cleanMissMeter.Mark(1) } // Try to retrieve the trie node from the disk. - var ( - nBlob []byte - nHash common.Hash - ) + var blob []byte if owner == (common.Hash{}) { - nBlob, nHash = rawdb.ReadAccountTrieNode(dl.db.diskdb, path) + blob = rawdb.ReadAccountTrieNode(dl.db.diskdb, path) } else { - nBlob, nHash = rawdb.ReadStorageTrieNode(dl.db.diskdb, owner, path) + blob = rawdb.ReadStorageTrieNode(dl.db.diskdb, owner, path) } - if dl.cleans != nil && len(nBlob) > 0 { - dl.cleans.Set(key, nBlob) - cleanWriteMeter.Mark(int64(len(nBlob))) + if dl.cleans != nil && len(blob) > 0 { + dl.cleans.Set(key, blob) + cleanWriteMeter.Mark(int64(len(blob))) } - return nBlob, nHash, &nodeLoc{loc: locDiskLayer, depth: depth}, nil + + return blob, h.hash(blob), &nodeLoc{loc: locDiskLayer, depth: depth}, nil } // update implements the layer interface, returning a new diff layer on top @@ -280,7 +277,7 @@ func (dl *diskLayer) commit(bottom *diffLayer, force bool) (*diskLayer, error) { } // revert applies the given state history and return a reverted disk layer. -func (dl *diskLayer) revert(h *history, loader triestate.TrieLoader) (*diskLayer, error) { +func (dl *diskLayer) revert(h *history) (*diskLayer, error) { if h.meta.root != dl.rootHash() { return nil, errUnexpectedHistory } @@ -290,7 +287,7 @@ func (dl *diskLayer) revert(h *history, loader triestate.TrieLoader) (*diskLayer // Apply the reverse state changes upon the current state. This must // be done before holding the lock in order to access state in "this" // layer. - nodes, err := triestate.Apply(h.meta.parent, h.meta.root, h.accounts, h.storages, loader) + nodes, err := apply(dl.db, h.meta.parent, h.meta.root, h.accounts, h.storages) if err != nil { return nil, err } @@ -362,7 +359,7 @@ func (dl *diskLayer) resetCache() { type hasher struct{ sha crypto.KeccakState } var hasherPool = sync.Pool{ - New: func() interface{} { return &hasher{sha: sha3.NewLegacyKeccak256().(crypto.KeccakState)} }, + New: func() interface{} { return &hasher{sha: crypto.NewKeccakState()} }, } func newHasher() *hasher { diff --git a/triedb/pathdb/execute.go b/triedb/pathdb/execute.go new file mode 100644 index 0000000000..9074e4debf --- /dev/null +++ b/triedb/pathdb/execute.go @@ -0,0 +1,186 @@ +// Copyright 2024 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see + +package pathdb + +import ( + "errors" + "fmt" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/trie" + "github.com/ethereum/go-ethereum/trie/trienode" + "github.com/ethereum/go-ethereum/triedb/database" +) + +// context wraps all fields for executing state diffs. +type context struct { + prevRoot common.Hash + postRoot common.Hash + accounts map[common.Address][]byte + storages map[common.Address]map[common.Hash][]byte + nodes *trienode.MergedNodeSet + + // TODO (rjl493456442) abstract out the state hasher + // for supporting verkle tree. + accountTrie *trie.Trie +} + +// apply processes the given state diffs, updates the corresponding post-state +// and returns the trie nodes that have been modified. +func apply(db database.Database, prevRoot common.Hash, postRoot common.Hash, accounts map[common.Address][]byte, storages map[common.Address]map[common.Hash][]byte) (map[common.Hash]map[string]*trienode.Node, error) { + tr, err := trie.New(trie.TrieID(postRoot), db) + if err != nil { + return nil, err + } + ctx := &context{ + prevRoot: prevRoot, + postRoot: postRoot, + accounts: accounts, + storages: storages, + accountTrie: tr, + nodes: trienode.NewMergedNodeSet(), + } + for addr, account := range accounts { + var err error + if len(account) == 0 { + err = deleteAccount(ctx, db, addr) + } else { + err = updateAccount(ctx, db, addr) + } + if err != nil { + return nil, fmt.Errorf("failed to revert state, err: %w", err) + } + } + root, result := tr.Commit(false) + if root != prevRoot { + return nil, fmt.Errorf("failed to revert state, want %#x, got %#x", prevRoot, root) + } + if err := ctx.nodes.Merge(result); err != nil { + return nil, err + } + return ctx.nodes.Flatten(), nil +} + +// updateAccount the account was present in prev-state, and may or may not +// existent in post-state. Apply the reverse diff and verify if the storage +// root matches the one in prev-state account. +func updateAccount(ctx *context, db database.Database, addr common.Address) error { + // The account was present in prev-state, decode it from the + // 'slim-rlp' format bytes. + h := newHasher() + defer h.release() + + addrHash := h.hash(addr.Bytes()) + prev, err := types.FullAccount(ctx.accounts[addr]) + if err != nil { + return err + } + // The account may or may not existent in post-state, try to + // load it and decode if it's found. + blob, err := ctx.accountTrie.Get(addrHash.Bytes()) + if err != nil { + return err + } + post := types.NewEmptyStateAccount() + if len(blob) != 0 { + if err := rlp.DecodeBytes(blob, &post); err != nil { + return err + } + } + // Apply all storage changes into the post-state storage trie. + st, err := trie.New(trie.StorageTrieID(ctx.postRoot, addrHash, post.Root), db) + if err != nil { + return err + } + for key, val := range ctx.storages[addr] { + var err error + if len(val) == 0 { + err = st.Delete(key.Bytes()) + } else { + err = st.Update(key.Bytes(), val) + } + if err != nil { + return err + } + } + root, result := st.Commit(false) + if root != prev.Root { + return errors.New("failed to reset storage trie") + } + // The returned set can be nil if storage trie is not changed + // at all. + if result != nil { + if err := ctx.nodes.Merge(result); err != nil { + return err + } + } + // Write the prev-state account into the main trie + full, err := rlp.EncodeToBytes(prev) + if err != nil { + return err + } + return ctx.accountTrie.Update(addrHash.Bytes(), full) +} + +// deleteAccount the account was not present in prev-state, and is expected +// to be existent in post-state. Apply the reverse diff and verify if the +// account and storage is wiped out correctly. +func deleteAccount(ctx *context, db database.Database, addr common.Address) error { + // The account must be existent in post-state, load the account. + h := newHasher() + defer h.release() + + addrHash := h.hash(addr.Bytes()) + blob, err := ctx.accountTrie.Get(addrHash.Bytes()) + if err != nil { + return err + } + if len(blob) == 0 { + return fmt.Errorf("account is non-existent %#x", addrHash) + } + var post types.StateAccount + if err := rlp.DecodeBytes(blob, &post); err != nil { + return err + } + st, err := trie.New(trie.StorageTrieID(ctx.postRoot, addrHash, post.Root), db) + if err != nil { + return err + } + for key, val := range ctx.storages[addr] { + if len(val) != 0 { + return errors.New("expect storage deletion") + } + if err := st.Delete(key.Bytes()); err != nil { + return err + } + } + root, result := st.Commit(false) + if root != types.EmptyRootHash { + return errors.New("failed to clear storage trie") + } + // The returned set can be nil if storage trie is not changed + // at all. + if result != nil { + if err := ctx.nodes.Merge(result); err != nil { + return err + } + } + // Delete the post-state account from the main trie. + return ctx.accountTrie.Delete(addrHash.Bytes()) +} diff --git a/triedb/pathdb/history.go b/triedb/pathdb/history.go index 7099b2b381..d77f7aa04d 100644 --- a/triedb/pathdb/history.go +++ b/triedb/pathdb/history.go @@ -29,6 +29,7 @@ import ( "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/trie/triestate" + "golang.org/x/exp/maps" ) // State history records the state changes involved in executing a block. The @@ -244,19 +245,13 @@ type history struct { // newHistory constructs the state history object with provided state change set. func newHistory(root common.Hash, parent common.Hash, block uint64, states *triestate.Set) *history { var ( - accountList []common.Address + accountList = maps.Keys(states.Accounts) storageList = make(map[common.Address][]common.Hash) ) - for addr := range states.Accounts { - accountList = append(accountList, addr) - } slices.SortFunc(accountList, common.Address.Cmp) for addr, slots := range states.Storages { - slist := make([]common.Hash, 0, len(slots)) - for slotHash := range slots { - slist = append(slist, slotHash) - } + slist := maps.Keys(slots) slices.SortFunc(slist, common.Hash.Cmp) storageList[addr] = slist } @@ -384,10 +379,11 @@ func (r *decoder) readAccount(pos int) (accountIndex, []byte, error) { func (r *decoder) readStorage(accIndex accountIndex) ([]common.Hash, map[common.Hash][]byte, error) { var ( last common.Hash - list []common.Hash - storage = make(map[common.Hash][]byte) + count = int(accIndex.storageSlots) + list = make([]common.Hash, 0, count) + storage = make(map[common.Hash][]byte, count) ) - for j := 0; j < int(accIndex.storageSlots); j++ { + for j := 0; j < count; j++ { var ( index slotIndex start = (accIndex.storageOffset + uint32(j)) * uint32(slotIndexSize) @@ -430,9 +426,10 @@ func (r *decoder) readStorage(accIndex accountIndex) ([]common.Hash, map[common. // decode deserializes the account and storage data from the provided byte stream. func (h *history) decode(accountData, storageData, accountIndexes, storageIndexes []byte) error { var ( - accounts = make(map[common.Address][]byte) + count = len(accountIndexes) / accountIndexSize + accounts = make(map[common.Address][]byte, count) storages = make(map[common.Address]map[common.Hash][]byte) - accountList []common.Address + accountList = make([]common.Address, 0, count) storageList = make(map[common.Address][]common.Hash) r = &decoder{ @@ -445,7 +442,7 @@ func (h *history) decode(accountData, storageData, accountIndexes, storageIndexe if err := r.verify(); err != nil { return err } - for i := 0; i < len(accountIndexes)/accountIndexSize; i++ { + for i := 0; i < count; i++ { // Resolve account first accIndex, accData, err := r.readAccount(i) if err != nil { @@ -472,8 +469,8 @@ func (h *history) decode(accountData, storageData, accountIndexes, storageIndexe } // readHistory reads and decodes the state history object by the given id. -func readHistory(freezer *rawdb.ResettableFreezer, id uint64) (*history, error) { - blob := rawdb.ReadStateHistoryMeta(freezer, id) +func readHistory(reader ethdb.AncientReader, id uint64) (*history, error) { + blob := rawdb.ReadStateHistoryMeta(reader, id) if len(blob) == 0 { return nil, fmt.Errorf("state history not found %d", id) } @@ -483,10 +480,10 @@ func readHistory(freezer *rawdb.ResettableFreezer, id uint64) (*history, error) } var ( dec = history{meta: &m} - accountData = rawdb.ReadStateAccountHistory(freezer, id) - storageData = rawdb.ReadStateStorageHistory(freezer, id) - accountIndexes = rawdb.ReadStateAccountIndex(freezer, id) - storageIndexes = rawdb.ReadStateStorageIndex(freezer, id) + accountData = rawdb.ReadStateAccountHistory(reader, id) + storageData = rawdb.ReadStateStorageHistory(reader, id) + accountIndexes = rawdb.ReadStateAccountIndex(reader, id) + storageIndexes = rawdb.ReadStateStorageIndex(reader, id) ) if err := dec.decode(accountData, storageData, accountIndexes, storageIndexes); err != nil { return nil, err @@ -495,7 +492,7 @@ func readHistory(freezer *rawdb.ResettableFreezer, id uint64) (*history, error) } // writeHistory persists the state history with the provided state set. -func writeHistory(freezer *rawdb.ResettableFreezer, dl *diffLayer) error { +func writeHistory(writer ethdb.AncientWriter, dl *diffLayer) error { // Short circuit if state set is not available. if dl.states == nil { return errors.New("state change set is not available") @@ -509,7 +506,7 @@ func writeHistory(freezer *rawdb.ResettableFreezer, dl *diffLayer) error { indexSize := common.StorageSize(len(accountIndex) + len(storageIndex)) // Write history data into five freezer table respectively. - rawdb.WriteStateHistory(freezer, dl.stateID(), history.meta.encode(), accountIndex, storageIndex, accountData, storageData) + rawdb.WriteStateHistory(writer, dl.stateID(), history.meta.encode(), accountIndex, storageIndex, accountData, storageData) historyDataBytesMeter.Mark(int64(dataSize)) historyIndexBytesMeter.Mark(int64(indexSize)) @@ -521,13 +518,13 @@ func writeHistory(freezer *rawdb.ResettableFreezer, dl *diffLayer) error { // checkHistories retrieves a batch of meta objects with the specified range // and performs the callback on each item. -func checkHistories(freezer *rawdb.ResettableFreezer, start, count uint64, check func(*meta) error) error { +func checkHistories(reader ethdb.AncientReader, start, count uint64, check func(*meta) error) error { for count > 0 { number := count if number > 10000 { number = 10000 // split the big read into small chunks } - blobs, err := rawdb.ReadStateHistoryMetaList(freezer, start, number) + blobs, err := rawdb.ReadStateHistoryMetaList(reader, start, number) if err != nil { return err } @@ -548,12 +545,12 @@ func checkHistories(freezer *rawdb.ResettableFreezer, start, count uint64, check // truncateFromHead removes the extra state histories from the head with the given // parameters. It returns the number of items removed from the head. -func truncateFromHead(db ethdb.Batcher, freezer *rawdb.ResettableFreezer, nhead uint64) (int, error) { - ohead, err := freezer.Ancients() +func truncateFromHead(db ethdb.Batcher, store ethdb.AncientStore, nhead uint64) (int, error) { + ohead, err := store.Ancients() if err != nil { return 0, err } - otail, err := freezer.Tail() + otail, err := store.Tail() if err != nil { return 0, err } @@ -566,7 +563,7 @@ func truncateFromHead(db ethdb.Batcher, freezer *rawdb.ResettableFreezer, nhead return 0, nil } // Load the meta objects in range [nhead+1, ohead] - blobs, err := rawdb.ReadStateHistoryMetaList(freezer, nhead+1, ohead-nhead) + blobs, err := rawdb.ReadStateHistoryMetaList(store, nhead+1, ohead-nhead) if err != nil { return 0, err } @@ -581,7 +578,7 @@ func truncateFromHead(db ethdb.Batcher, freezer *rawdb.ResettableFreezer, nhead if err := batch.Write(); err != nil { return 0, err } - ohead, err = freezer.TruncateHead(nhead) + ohead, err = store.TruncateHead(nhead) if err != nil { return 0, err } @@ -590,12 +587,12 @@ func truncateFromHead(db ethdb.Batcher, freezer *rawdb.ResettableFreezer, nhead // truncateFromTail removes the extra state histories from the tail with the given // parameters. It returns the number of items removed from the tail. -func truncateFromTail(db ethdb.Batcher, freezer *rawdb.ResettableFreezer, ntail uint64) (int, error) { - ohead, err := freezer.Ancients() +func truncateFromTail(db ethdb.Batcher, store ethdb.AncientStore, ntail uint64) (int, error) { + ohead, err := store.Ancients() if err != nil { return 0, err } - otail, err := freezer.Tail() + otail, err := store.Tail() if err != nil { return 0, err } @@ -608,7 +605,7 @@ func truncateFromTail(db ethdb.Batcher, freezer *rawdb.ResettableFreezer, ntail return 0, nil } // Load the meta objects in range [otail+1, ntail] - blobs, err := rawdb.ReadStateHistoryMetaList(freezer, otail+1, ntail-otail) + blobs, err := rawdb.ReadStateHistoryMetaList(store, otail+1, ntail-otail) if err != nil { return 0, err } @@ -623,7 +620,7 @@ func truncateFromTail(db ethdb.Batcher, freezer *rawdb.ResettableFreezer, ntail if err := batch.Write(); err != nil { return 0, err } - otail, err = freezer.TruncateTail(ntail) + otail, err = store.TruncateTail(ntail) if err != nil { return 0, err } diff --git a/triedb/pathdb/history_inspect.go b/triedb/pathdb/history_inspect.go index d8a761b916..240474da37 100644 --- a/triedb/pathdb/history_inspect.go +++ b/triedb/pathdb/history_inspect.go @@ -21,7 +21,7 @@ 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" ) @@ -34,7 +34,7 @@ type HistoryStats struct { } // sanitizeRange limits the given range to fit within the local history store. -func sanitizeRange(start, end uint64, freezer *rawdb.ResettableFreezer) (uint64, uint64, error) { +func sanitizeRange(start, end uint64, freezer ethdb.AncientReader) (uint64, uint64, error) { // Load the id of the first history object in local store. tail, err := freezer.Tail() if err != nil { @@ -60,7 +60,7 @@ func sanitizeRange(start, end uint64, freezer *rawdb.ResettableFreezer) (uint64, return first, last, nil } -func inspectHistory(freezer *rawdb.ResettableFreezer, start, end uint64, onHistory func(*history, *HistoryStats)) (*HistoryStats, error) { +func inspectHistory(freezer ethdb.AncientReader, start, end uint64, onHistory func(*history, *HistoryStats)) (*HistoryStats, error) { var ( stats = &HistoryStats{} init = time.Now() @@ -96,7 +96,7 @@ func inspectHistory(freezer *rawdb.ResettableFreezer, start, end uint64, onHisto } // accountHistory inspects the account history within the range. -func accountHistory(freezer *rawdb.ResettableFreezer, address common.Address, start, end uint64) (*HistoryStats, error) { +func accountHistory(freezer ethdb.AncientReader, address common.Address, start, end uint64) (*HistoryStats, error) { return inspectHistory(freezer, start, end, func(h *history, stats *HistoryStats) { blob, exists := h.accounts[address] if !exists { @@ -108,7 +108,7 @@ func accountHistory(freezer *rawdb.ResettableFreezer, address common.Address, st } // storageHistory inspects the storage history within the range. -func storageHistory(freezer *rawdb.ResettableFreezer, address common.Address, slot common.Hash, start uint64, end uint64) (*HistoryStats, error) { +func storageHistory(freezer ethdb.AncientReader, address common.Address, slot common.Hash, start uint64, end uint64) (*HistoryStats, error) { return inspectHistory(freezer, start, end, func(h *history, stats *HistoryStats) { slots, exists := h.storages[address] if !exists { @@ -124,7 +124,7 @@ func storageHistory(freezer *rawdb.ResettableFreezer, address common.Address, sl } // historyRange returns the block number range of local state histories. -func historyRange(freezer *rawdb.ResettableFreezer) (uint64, uint64, error) { +func historyRange(freezer ethdb.AncientReader) (uint64, uint64, error) { // Load the id of the first history object in local store. tail, err := freezer.Tail() if err != nil { diff --git a/triedb/pathdb/history_test.go b/triedb/pathdb/history_test.go index 18809329b6..4114aa1185 100644 --- a/triedb/pathdb/history_test.go +++ b/triedb/pathdb/history_test.go @@ -102,7 +102,7 @@ func TestEncodeDecodeHistory(t *testing.T) { } } -func checkHistory(t *testing.T, db ethdb.KeyValueReader, freezer *rawdb.ResettableFreezer, id uint64, root common.Hash, exist bool) { +func checkHistory(t *testing.T, db ethdb.KeyValueReader, freezer ethdb.AncientReader, id uint64, root common.Hash, exist bool) { blob := rawdb.ReadStateHistoryMeta(freezer, id) if exist && len(blob) == 0 { t.Fatalf("Failed to load trie history, %d", id) @@ -118,7 +118,7 @@ func checkHistory(t *testing.T, db ethdb.KeyValueReader, freezer *rawdb.Resettab } } -func checkHistoriesInRange(t *testing.T, db ethdb.KeyValueReader, freezer *rawdb.ResettableFreezer, from, to uint64, roots []common.Hash, exist bool) { +func checkHistoriesInRange(t *testing.T, db ethdb.KeyValueReader, freezer ethdb.AncientReader, from, to uint64, roots []common.Hash, exist bool) { for i, j := from, 0; i <= to; i, j = i+1, j+1 { checkHistory(t, db, freezer, i, roots[j], exist) } @@ -129,7 +129,7 @@ func TestTruncateHeadHistory(t *testing.T) { roots []common.Hash hs = makeHistories(10) db = rawdb.NewMemoryDatabase() - freezer, _ = openFreezer(t.TempDir(), false) + freezer, _ = rawdb.NewStateFreezer(t.TempDir(), false) ) defer freezer.Close() @@ -157,7 +157,7 @@ func TestTruncateTailHistory(t *testing.T) { roots []common.Hash hs = makeHistories(10) db = rawdb.NewMemoryDatabase() - freezer, _ = openFreezer(t.TempDir(), false) + freezer, _ = rawdb.NewStateFreezer(t.TempDir(), false) ) defer freezer.Close() @@ -200,7 +200,7 @@ func TestTruncateTailHistories(t *testing.T) { roots []common.Hash hs = makeHistories(10) db = rawdb.NewMemoryDatabase() - freezer, _ = openFreezer(t.TempDir()+fmt.Sprintf("%d", i), false) + freezer, _ = rawdb.NewStateFreezer(t.TempDir()+fmt.Sprintf("%d", i), false) ) defer freezer.Close() @@ -228,7 +228,7 @@ func TestTruncateOutOfRange(t *testing.T) { var ( hs = makeHistories(10) db = rawdb.NewMemoryDatabase() - freezer, _ = openFreezer(t.TempDir(), false) + freezer, _ = rawdb.NewStateFreezer(t.TempDir(), false) ) defer freezer.Close() @@ -268,11 +268,6 @@ func TestTruncateOutOfRange(t *testing.T) { } } -// openFreezer initializes the freezer instance for storing state histories. -func openFreezer(datadir string, readOnly bool) (*rawdb.ResettableFreezer, error) { - return rawdb.NewStateFreezer(datadir, readOnly, 0) -} - func compareSet[k comparable](a, b map[k][]byte) bool { if len(a) != len(b) { return false diff --git a/triedb/pathdb/journal.go b/triedb/pathdb/journal.go index 86b02f3876..1bd447cfce 100644 --- a/triedb/pathdb/journal.go +++ b/triedb/pathdb/journal.go @@ -250,9 +250,10 @@ func (db *Database) loadJournal(diskRoot common.Hash) (layer, error) { // loadLayers loads a pre-existing state layer backed by a key-value store. func (db *Database) loadLayers() layer { // Retrieve the root node of persistent state. - _, root := rawdb.ReadAccountTrieNode(db.diskdb, nil) - root = types.TrieRootHash(root) - + var root = types.EmptyRootHash + if blob := rawdb.ReadAccountTrieNode(db.diskdb, nil); len(blob) > 0 { + root = crypto.Keccak256Hash(blob) + } // Load the layers by resolving the journal head, err := db.loadJournal(root) if err == nil { @@ -592,14 +593,13 @@ func (db *Database) Journal(root common.Hash) error { if err := rlp.Encode(journal, journalVersion); err != nil { return err } - // The stored state in disk might be empty, convert the - // root to emptyRoot in this case. - _, diskroot := rawdb.ReadAccountTrieNode(db.diskdb, nil) - diskroot = types.TrieRootHash(diskroot) - // Secondly write out the state root in disk, ensure all layers // on top are continuous with disk. - if err := rlp.Encode(journal, diskroot); err != nil { + diskRoot := types.EmptyRootHash + if blob := rawdb.ReadAccountTrieNode(db.diskdb, nil); len(blob) > 0 { + diskRoot = crypto.Keccak256Hash(blob) + } + if err := rlp.Encode(journal, diskRoot); err != nil { return err } // Finally write out the journal of each layer in reverse order. diff --git a/triedb/pathdb/nodebuffer.go b/triedb/pathdb/nodebuffer.go index 7f49b7ab36..dd40898ada 100644 --- a/triedb/pathdb/nodebuffer.go +++ b/triedb/pathdb/nodebuffer.go @@ -17,7 +17,9 @@ package pathdb import ( + "bytes" "fmt" + "maps" "time" "github.com/VictoriaMetrics/fastcache" @@ -91,12 +93,10 @@ func (b *nodebuffer) commit(nodes map[common.Hash]map[string]*trienode.Node) tri // The nodes belong to original diff layer are still accessible even // after merging, thus the ownership of nodes map should still belong // to original layer and any mutation on it should be prevented. - current = make(map[string]*trienode.Node) for path, n := range subset { - current[path] = n delta += int64(len(n.Blob) + len(path) + len(owner)) } - b.nodes[owner] = current + b.nodes[owner] = maps.Clone(subset) continue } for path, n := range subset { @@ -150,14 +150,14 @@ func (b *nodebuffer) revert(db ethdb.KeyValueReader, nodes map[common.Hash]map[s // // In case of database rollback, don't panic if this "clean" // node occurs which is not present in buffer. - var nhash common.Hash + var blob []byte if owner == (common.Hash{}) { - _, nhash = rawdb.ReadAccountTrieNode(db, []byte(path)) + blob = rawdb.ReadAccountTrieNode(db, []byte(path)) } else { - _, nhash = rawdb.ReadStorageTrieNode(db, owner, []byte(path)) + blob = rawdb.ReadStorageTrieNode(db, owner, []byte(path)) } // Ignore the clean node in the case described above. - if nhash == n.Hash { + if bytes.Equal(blob, n.Blob) { continue } panic(fmt.Sprintf("non-existent node (%x %v) blob: %v", owner, path, crypto.Keccak256Hash(n.Blob).Hex())) diff --git a/triedb/pathdb/testutils.go b/triedb/pathdb/testutils.go deleted file mode 100644 index 0c99565b8e..0000000000 --- a/triedb/pathdb/testutils.go +++ /dev/null @@ -1,159 +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 . - -package pathdb - -import ( - "bytes" - "fmt" - "slices" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/trie/trienode" - "github.com/ethereum/go-ethereum/trie/triestate" -) - -// testHasher is a test utility for computing root hash of a batch of state -// elements. The hash algorithm is to sort all the elements in lexicographical -// order, concat the key and value in turn, and perform hash calculation on -// the concatenated bytes. Except the root hash, a nodeset will be returned -// once Commit is called, which contains all the changes made to hasher. -type testHasher struct { - owner common.Hash // owner identifier - root common.Hash // original root - dirties map[common.Hash][]byte // dirty states - cleans map[common.Hash][]byte // clean states -} - -// newTestHasher constructs a hasher object with provided states. -func newTestHasher(owner common.Hash, root common.Hash, cleans map[common.Hash][]byte) (*testHasher, error) { - if cleans == nil { - cleans = make(map[common.Hash][]byte) - } - if got, _ := hash(cleans); got != root { - return nil, fmt.Errorf("state root mismatched, want: %x, got: %x", root, got) - } - return &testHasher{ - owner: owner, - root: root, - dirties: make(map[common.Hash][]byte), - cleans: cleans, - }, nil -} - -// Get returns the value for key stored in the trie. -func (h *testHasher) Get(key []byte) ([]byte, error) { - hash := common.BytesToHash(key) - val, ok := h.dirties[hash] - if ok { - return val, nil - } - return h.cleans[hash], nil -} - -// Update associates key with value in the trie. -func (h *testHasher) Update(key, value []byte) error { - h.dirties[common.BytesToHash(key)] = common.CopyBytes(value) - return nil -} - -// Delete removes any existing value for key from the trie. -func (h *testHasher) Delete(key []byte) error { - h.dirties[common.BytesToHash(key)] = nil - return nil -} - -// Commit computes the new hash of the states and returns the set with all -// state changes. -func (h *testHasher) Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet, error) { - var ( - nodes = make(map[common.Hash][]byte) - set = trienode.NewNodeSet(h.owner) - ) - for hash, val := range h.cleans { - nodes[hash] = val - } - for hash, val := range h.dirties { - nodes[hash] = val - if bytes.Equal(val, h.cleans[hash]) { - continue - } - // Utilize the hash of the state key as the node path to mitigate - // potential collisions within the path. - path := crypto.Keccak256(hash.Bytes()) - if len(val) == 0 { - set.AddNode(path, trienode.NewDeleted()) - } else { - set.AddNode(path, trienode.New(crypto.Keccak256Hash(val), val)) - } - } - root, blob := hash(nodes) - - // Include the dirty root node as well. - if root != types.EmptyRootHash && root != h.root { - set.AddNode(nil, trienode.New(root, blob)) - } - if root == types.EmptyRootHash && h.root != types.EmptyRootHash { - set.AddNode(nil, trienode.NewDeleted()) - } - return root, set, nil -} - -// hash performs the hash computation upon the provided states. -func hash(states map[common.Hash][]byte) (common.Hash, []byte) { - var hs []common.Hash - for hash := range states { - hs = append(hs, hash) - } - slices.SortFunc(hs, common.Hash.Cmp) - - var input []byte - for _, hash := range hs { - if len(states[hash]) == 0 { - continue - } - input = append(input, hash.Bytes()...) - input = append(input, states[hash]...) - } - if len(input) == 0 { - return types.EmptyRootHash, nil - } - return crypto.Keccak256Hash(input), input -} - -type hashLoader struct { - accounts map[common.Hash][]byte - storages map[common.Hash]map[common.Hash][]byte -} - -func newHashLoader(accounts map[common.Hash][]byte, storages map[common.Hash]map[common.Hash][]byte) *hashLoader { - return &hashLoader{ - accounts: accounts, - storages: storages, - } -} - -// OpenTrie opens the main account trie. -func (l *hashLoader) OpenTrie(root common.Hash) (triestate.Trie, error) { - return newTestHasher(common.Hash{}, root, l.accounts) -} - -// OpenStorageTrie opens the storage trie of an account. -func (l *hashLoader) OpenStorageTrie(stateRoot common.Hash, addrHash, root common.Hash) (triestate.Trie, error) { - return newTestHasher(addrHash, root, l.storages[addrHash]) -}