From 25be8c291e5cbd40cee74040b0614a590a458e35 Mon Sep 17 00:00:00 2001 From: Sam Calder-Mason Date: Fri, 17 Mar 2023 16:08:18 +1000 Subject: [PATCH 01/11] feat: Add Debug Forkchoice endpoint --- api/v1/forkchoice.go | 237 +++++++++++++++++++++++++++++++++++++++++++ go.mod | 2 +- http/forkchoice.go | 40 ++++++++ service.go | 6 ++ 4 files changed, 284 insertions(+), 1 deletion(-) create mode 100644 api/v1/forkchoice.go create mode 100644 http/forkchoice.go diff --git a/api/v1/forkchoice.go b/api/v1/forkchoice.go new file mode 100644 index 00000000..58ce962c --- /dev/null +++ b/api/v1/forkchoice.go @@ -0,0 +1,237 @@ +// Copyright © 2020, 2021 Attestant Limited. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package v1 + +import ( + "encoding/hex" + "encoding/json" + "fmt" + "strconv" + "strings" + + "github.com/attestantio/go-eth2-client/spec/phase0" + "github.com/pkg/errors" +) + +// ForkChoice is the data regarding the node's current fork choice context. +type ForkChoice struct { + // JustifiedCheckpoint is the current justified checkpoint. + JustifiedCheckpoint phase0.Checkpoint + // FInalizedCheckpoint is the current finalized checkpoint. + FinalizedCheckpoint phase0.Checkpoint + // ForkChoiceNodes contains the fork choice nodes. + ForkChoiceNodes []*ForkChoiceNode +} + +// MarshalJSON implements json.Marshaler. +func (f *ForkChoice) MarshalJSON() ([]byte, error) { + return json.Marshal(&forkChoiceJSON{ + JustifiedCheckpoint: &f.JustifiedCheckpoint, + FinalizedCheckpoint: &f.FinalizedCheckpoint, + ForkChoiceNodes: f.ForkChoiceNodes, + }) +} + +// UnmarshalJSON implements json.Unmarshaler. +func (f *ForkChoice) UnmarshalJSON(input []byte) error { + var err error + + var forkChoiceJSON forkChoiceJSON + if err = json.Unmarshal(input, &forkChoiceJSON); err != nil { + return errors.Wrap(err, "invalid JSON") + } + + if forkChoiceJSON.JustifiedCheckpoint == nil { + return errors.New("justified checkpoint missing") + } + f.JustifiedCheckpoint = *forkChoiceJSON.JustifiedCheckpoint + + if forkChoiceJSON.FinalizedCheckpoint == nil { + return errors.New("finalized checkpoint missing") + } + f.FinalizedCheckpoint = *forkChoiceJSON.FinalizedCheckpoint + + if forkChoiceJSON.ForkChoiceNodes == nil { + return errors.New("fork choice nodes missing") + } + f.ForkChoiceNodes = forkChoiceJSON.ForkChoiceNodes + + return nil +} + +// String returns a string version of the structure. +func (f *ForkChoice) String() string { + data, err := json.Marshal(f) + if err != nil { + return fmt.Sprintf("ERR: %v", err) + } + return string(data) +} + +// forkChoiceJSON is the json representation of the struct. +type forkChoiceJSON struct { + JustifiedCheckpoint *phase0.Checkpoint `json:"justified_checkpoint"` + FinalizedCheckpoint *phase0.Checkpoint `json:"finalized_checkpoint"` + ForkChoiceNodes []*ForkChoiceNode `json:"fork_choice_nodes"` +} + +type ForkChoiceNodeValidity string + +const ( + ForkChoiceNodeValidityInvalid ForkChoiceNodeValidity = "INVALID" + ForkChoiceNodeValidityValid ForkChoiceNodeValidity = "VALID" + ForkChoiceNodeValidityOptimistic ForkChoiceNodeValidity = "OPTIMISTIC" +) + +func parseForkChoiceValidity(input string) (ForkChoiceNodeValidity, error) { + switch input { + case "INVALID": + return ForkChoiceNodeValidityInvalid, nil + case "VALID": + return ForkChoiceNodeValidityValid, nil + case "OPTIMISTIC": + return ForkChoiceNodeValidityOptimistic, nil + default: + return "", errors.New(fmt.Sprintf("invalid value for fork choice node validity: %s", input)) + } +} + +type ForkChoiceNode struct { + // Slot is the slot of the node. + Slot phase0.Slot + // BlockRoot is the block root of the node. + BlockRoot phase0.Root + // ParentRoot is the parent root of the node. + ParentRoot phase0.Root + // JustifiedEpcih is the justified epoch of the node. + JustifiedEpoch phase0.Epoch + // FinalizedEpoch is the finalized epoch of the node. + FinalizedEpoch phase0.Epoch + // Weight is the weight of the node. + Weight uint64 + // Validity is the validity of the node. + Validity ForkChoiceNodeValidity + // ExecutiionBlockHash is the execution block hash of the node. + ExecutionBlockHash phase0.Root + // ExtraData is the extra data of the node. + ExtraData interface{} +} + +// forkChoiceNodeJSON is the json representation of the struct. +type forkChoiceNodeJSON struct { + Slot string `json:"slot"` + BlockRoot string `json:"block_root"` + ParentRoot string `json:"parent_root"` + JustifiedEpoch string `json:"justified_epoch"` + FinalizedEpoch string `json:"finalized_epoch"` + Weight string `json:"weight"` + Validity string `json:"validity"` + ExecutionBlockHash string `json:"execution_block_hash"` + ExtraData interface{} `json:"extra_data"` +} + +// MarshalJSON implements json.Marshaler. +func (f *ForkChoiceNode) MarshalJSON() ([]byte, error) { + return json.Marshal(&forkChoiceNodeJSON{ + Slot: fmt.Sprintf("%d", f.Slot), + BlockRoot: fmt.Sprintf("%#x", f.BlockRoot), + ParentRoot: fmt.Sprintf("%#x", f.ParentRoot), + JustifiedEpoch: fmt.Sprintf("%d", f.JustifiedEpoch), + FinalizedEpoch: fmt.Sprintf("%d", f.FinalizedEpoch), + Weight: fmt.Sprintf("%d", f.Weight), + Validity: string(f.Validity), + ExecutionBlockHash: fmt.Sprintf("%#x", f.ExecutionBlockHash), + ExtraData: f.ExtraData, + }) +} + +// UnmarshalJSON implements json.Unmarshaler. +func (f *ForkChoiceNode) UnmarshalJSON(input []byte) error { + var err error + + var forkChoiceNodeJSON forkChoiceNodeJSON + if err = json.Unmarshal(input, &forkChoiceNodeJSON); err != nil { + return errors.Wrap(err, "invalid JSON") + } + + slot, err := strconv.ParseUint(forkChoiceNodeJSON.Slot, 10, 64) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("invalid value for slot: %s", forkChoiceNodeJSON.Slot)) + } + f.Slot = phase0.Slot(slot) + + blockRoot, err := hex.DecodeString(strings.TrimPrefix(forkChoiceNodeJSON.BlockRoot, "0x")) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("invalid value for block root: %s", forkChoiceNodeJSON.BlockRoot)) + } + if len(blockRoot) != rootLength { + return fmt.Errorf("incorrect length %d for block root", len(blockRoot)) + } + copy(f.BlockRoot[:], blockRoot) + + parentRoot, err := hex.DecodeString(strings.TrimPrefix(forkChoiceNodeJSON.ParentRoot, "0x")) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("invalid value for parent root: %s", forkChoiceNodeJSON.ParentRoot)) + } + if len(parentRoot) != rootLength { + return fmt.Errorf("incorrect length %d for parent root", len(parentRoot)) + } + copy(f.ParentRoot[:], parentRoot) + + justifiedEpoch, err := strconv.ParseUint(forkChoiceNodeJSON.JustifiedEpoch, 10, 64) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("invalid value for justified epoch: %s", forkChoiceNodeJSON.JustifiedEpoch)) + } + f.JustifiedEpoch = phase0.Epoch(justifiedEpoch) + + finalizedEpoch, err := strconv.ParseUint(forkChoiceNodeJSON.FinalizedEpoch, 10, 64) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("invalid value for finalized epoch: %s", forkChoiceNodeJSON.FinalizedEpoch)) + } + f.FinalizedEpoch = phase0.Epoch(finalizedEpoch) + + weight, err := strconv.ParseUint(forkChoiceNodeJSON.Weight, 10, 64) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("invalid value for weight: %s", forkChoiceNodeJSON.Weight)) + } + f.Weight = weight + + validity, err := parseForkChoiceValidity(forkChoiceNodeJSON.Validity) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("invalid value for validity: %s", forkChoiceNodeJSON.Validity)) + } + f.Validity = validity + + executionBlockHash, err := hex.DecodeString(strings.TrimPrefix(forkChoiceNodeJSON.ExecutionBlockHash, "0x")) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("invalid value for execution block hash: %s", forkChoiceNodeJSON.ExecutionBlockHash)) + } + if len(executionBlockHash) != rootLength { + return fmt.Errorf("incorrect length %d for execution block hash", len(executionBlockHash)) + } + copy(f.ExecutionBlockHash[:], executionBlockHash) + + f.ExtraData = forkChoiceNodeJSON.ExtraData + + return nil +} + +// String returns a string version of the structure. +func (f *ForkChoiceNode) String() string { + data, err := json.Marshal(f) + if err != nil { + return fmt.Sprintf("ERR: %v", err) + } + return string(data) +} diff --git a/go.mod b/go.mod index ba2fc276..fa06890e 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/attestantio/go-eth2-client +module github.com/attestantsaio/go-eth2-client go 1.14 diff --git a/http/forkchoice.go b/http/forkchoice.go new file mode 100644 index 00000000..e3408953 --- /dev/null +++ b/http/forkchoice.go @@ -0,0 +1,40 @@ +// Copyright © 2020, 2021 Attestant Limited. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package http + +import ( + "context" + "encoding/json" + + api "github.com/attestantio/go-eth2-client/api/v1" + "github.com/pkg/errors" +) + +// ForkChoice fetches all current fork choice context. +func (s *Service) ForkChoice(ctx context.Context) (*api.ForkChoice, error) { + respBodyReader, err := s.get(ctx, "/eth/v1/debug/fork_choice") + if err != nil { + return nil, errors.Wrap(err, "failed to request fork choice") + } + if respBodyReader == nil { + return nil, errors.New("failed to obtain fork choice") + } + + var forkChoice *api.ForkChoice + if err := json.NewDecoder(respBodyReader).Decode(&forkChoice); err != nil { + return nil, errors.Wrap(err, "failed to parse fork choice") + } + + return forkChoice, nil +} diff --git a/service.go b/service.go index b8ee8f26..49790c1b 100644 --- a/service.go +++ b/service.go @@ -290,6 +290,12 @@ type FinalityProvider interface { Finality(ctx context.Context, stateID string) (*apiv1.Finality, error) } +// ForkChoiceProvider is the interface for providing fork choice information. +type ForkChoiceProvider interface { + // Fork fetches all current fork choice context. + ForkChoice(ctx context.Context) (*apiv1.ForkChoice, error) +} + // ForkProvider is the interface for providing fork information. type ForkProvider interface { // Fork fetches fork information for the given state. From 19acddfcba92896e0bf89765eda7c5301e738371 Mon Sep 17 00:00:00 2001 From: Sam Calder-Mason Date: Mon, 20 Mar 2023 09:57:16 +1000 Subject: [PATCH 02/11] chore(go.mod): fix typo in module name from "attestantsaio" to "attestantio" --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index fa06890e..ba2fc276 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/attestantsaio/go-eth2-client +module github.com/attestantio/go-eth2-client go 1.14 From dbb8edb6751737d509b8d31450d9a1dbc38039b4 Mon Sep 17 00:00:00 2001 From: Sam Calder-Mason Date: Mon, 20 Mar 2023 13:54:13 +1000 Subject: [PATCH 03/11] feat(api/forkchoice): Add JSON marshalling tests --- api/v1/forkchoice.go | 2 +- api/v1/forkchoice_test.go | 164 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 api/v1/forkchoice_test.go diff --git a/api/v1/forkchoice.go b/api/v1/forkchoice.go index 58ce962c..5c8e3cd6 100644 --- a/api/v1/forkchoice.go +++ b/api/v1/forkchoice.go @@ -138,7 +138,7 @@ type forkChoiceNodeJSON struct { Weight string `json:"weight"` Validity string `json:"validity"` ExecutionBlockHash string `json:"execution_block_hash"` - ExtraData interface{} `json:"extra_data"` + ExtraData interface{} `json:"extra_data,omitempty"` } // MarshalJSON implements json.Marshaler. diff --git a/api/v1/forkchoice_test.go b/api/v1/forkchoice_test.go new file mode 100644 index 00000000..fda88220 --- /dev/null +++ b/api/v1/forkchoice_test.go @@ -0,0 +1,164 @@ +package v1_test + +import ( + "encoding/json" + "testing" + + api "github.com/attestantio/go-eth2-client/api/v1" + "github.com/stretchr/testify/require" + "gotest.tools/assert" +) + +func TestForkChoiceJSON(t *testing.T) { + tests := []struct { + name string + input []byte + expected string + err string + }{ + { + name: "Empty", + err: "unexpected end of JSON input", + }, + { + name: "JSONBad", + input: []byte("[]"), + err: "invalid JSON: json: cannot unmarshal array into Go value of type v1.forkChoiceJSON", + }, + { + name: "Good", + input: []byte(`{"justified_checkpoint":{"epoch":"1","root":"0x0000000000000000000000000000000000000000000000000000000000000000"},"finalized_checkpoint":{"epoch":"2","root":"0x0100000000000000000000000000000000000000000000000000000000000000"},"fork_choice_nodes":[{"slot":"1962336","block_root":"0x0f61e82f7b51f41fcd552cbdc64547bd9e1ba54b8404482732927645c2e13ec6","parent_root":"0xe399a2ee74cf0570b4f980772983bdc5cfbfde1f87f3ab395f2bee96103978c7","justified_epoch":"61322","finalized_epoch":"61321","weight":"57481550000000","validity":"VALID","execution_block_hash":"0x06a0277e02eae44c332bcec82d6715c3113dddce427982014cf5f43432f479e9","extra_data":{"justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7","state_root":"0x4bcecf56081291ab95df1dba25b0f83343d38217e9cec198510b61f6f35afdb3","unrealised_finalized_epoch":"61321","unrealised_justified_epoch":"61322","unrealized_finalized_root":"0x57a41f26678190d3e319c19fe9f4ea3830c4b21710a1e1ae41adcc23d0f030a2","unrealized_justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7"}}]}`), + expected: `{"justified_checkpoint":{"epoch":"1","root":"0x0000000000000000000000000000000000000000000000000000000000000000"},"finalized_checkpoint":{"epoch":"2","root":"0x0100000000000000000000000000000000000000000000000000000000000000"},"fork_choice_nodes":[{"slot":"1962336","block_root":"0x0f61e82f7b51f41fcd552cbdc64547bd9e1ba54b8404482732927645c2e13ec6","parent_root":"0xe399a2ee74cf0570b4f980772983bdc5cfbfde1f87f3ab395f2bee96103978c7","justified_epoch":"61322","finalized_epoch":"61321","weight":"57481550000000","validity":"VALID","execution_block_hash":"0x06a0277e02eae44c332bcec82d6715c3113dddce427982014cf5f43432f479e9","extra_data":{"justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7","state_root":"0x4bcecf56081291ab95df1dba25b0f83343d38217e9cec198510b61f6f35afdb3","unrealised_finalized_epoch":"61321","unrealised_justified_epoch":"61322","unrealized_finalized_root":"0x57a41f26678190d3e319c19fe9f4ea3830c4b21710a1e1ae41adcc23d0f030a2","unrealized_justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7"}}]}`, + err: "", + }, + { + name: "JustifiedCheckpointMissing", + input: []byte(`{"finalized_checkpoint":{"epoch":"2","root":"0x0100000000000000000000000000000000000000000000000000000000000000"},"fork_choice_nodes":[]}`), + err: "justified checkpoint missing", + }, + { + name: "JustifiedCheckpointInvalid", + input: []byte(`{"finalized_checkpoint":{"epoch":"1","root":"0x0000000000000000000000000000000000000000000000000000000000000000"},"justified_checkpoint":-1,"fork_choice_nodes":[]}`), + err: "invalid JSON: invalid JSON: json: cannot unmarshal number into Go value of type phase0.checkpointJSON", + }, + { + name: "FinalizedCheckpointMissing", + input: []byte(`{"justified_checkpoint":{"epoch":"2","root":"0x0100000000000000000000000000000000000000000000000000000000000000"},"fork_choice_nodes":[]}`), + err: "finalized checkpoint missing", + }, + { + name: "FinalizedCheckpointInvalid", + input: []byte(`{"justified_checkpoint":{"epoch":"1","root":"0x0000000000000000000000000000000000000000000000000000000000000000"},"finalized_checkpoint":-1,"fork_choice_nodes":[]}`), + err: "invalid JSON: invalid JSON: json: cannot unmarshal number into Go value of type phase0.checkpointJSON", + }, + { + name: "ForkChoiceNodesInvalid", + input: []byte(`{"justified_checkpoint":{"epoch":"1","root":"0x0000000000000000000000000000000000000000000000000000000000000000"},"finalized_checkpoint":{"epoch":"2","root":"0x0100000000000000000000000000000000000000000000000000000000000000"},"fork_choice_nodes":-1}`), + expected: `{"justified_checkpoint":{"epoch":"1","root":"0x0000000000000000000000000000000000000000000000000000000000000000"},"finalized_checkpoint":{"epoch":"2","root":"0x0100000000000000000000000000000000000000000000000000000000000000"},"fork_choice_nodes":[]}`, + err: "invalid JSON: json: cannot unmarshal number into Go struct field forkChoiceJSON.fork_choice_nodes of type []*v1.ForkChoiceNode", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + var fc api.ForkChoice + err := json.Unmarshal(test.input, &fc) + if test.err != "" { + require.EqualError(t, err, test.err) + } else { + require.NoError(t, err) + rt, err := json.Marshal(&fc) + require.NoError(t, err) + assert.Equal(t, string(test.input), string(rt)) + assert.Equal(t, string(rt), fc.String()) + } + }) + } +} + +func TestForkChoiceNodeJSON(t *testing.T) { + tests := []struct { + name string + input []byte + expected string + err string + }{ + { + name: "Empty", + err: "unexpected end of JSON input", + }, + { + name: "JSONBad", + input: []byte("[]"), + err: "invalid JSON: json: cannot unmarshal array into Go value of type v1.forkChoiceNodeJSON", + }, + { + name: "Good", + input: []byte(`{"slot":"1962336","block_root":"0x0f61e82f7b51f41fcd552cbdc64547bd9e1ba54b8404482732927645c2e13ec6","parent_root":"0xe399a2ee74cf0570b4f980772983bdc5cfbfde1f87f3ab395f2bee96103978c7","justified_epoch":"61322","finalized_epoch":"61321","weight":"57481550000000","validity":"VALID","execution_block_hash":"0x06a0277e02eae44c332bcec82d6715c3113dddce427982014cf5f43432f479e9","extra_data":{"justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7","state_root":"0x4bcecf56081291ab95df1dba25b0f83343d38217e9cec198510b61f6f35afdb3","unrealised_finalized_epoch":"61321","unrealised_justified_epoch":"61322","unrealized_finalized_root":"0x57a41f26678190d3e319c19fe9f4ea3830c4b21710a1e1ae41adcc23d0f030a2","unrealized_justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7"}}`), + expected: `{"slot":"1962336","block_root":"0x0f61e82f7b51f41fcd552cbdc64547bd9e1ba54b8404482732927645c2e13ec6","parent_root":"0xe399a2ee74cf0570b4f980772983bdc5cfbfde1f87f3ab395f2bee96103978c7","justified_epoch":"61322","finalized_epoch":"61321","weight":"57481550000000","validity":"VALID","execution_block_hash":"0x06a0277e02eae44c332bcec82d6715c3113dddce427982014cf5f43432f479e9","extra_data":{"justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7","state_root":"0x4bcecf56081291ab95df1dba25b0f83343d38217e9cec198510b61f6f35afdb3","unrealised_finalized_epoch":"61321","unrealised_justified_epoch":"61322","unrealized_finalized_root":"0x57a41f26678190d3e319c19fe9f4ea3830c4b21710a1e1ae41adcc23d0f030a2","unrealized_justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7"}}`, + err: "", + }, + { + name: "SlotInvalid", + input: []byte(`{"slot":1,"block_root":"0x0f61e82f7b51f41fcd552cbdc64547bd9e1ba54b8404482732927645c2e13ec6","parent_root":"0xe399a2ee74cf0570b4f980772983bdc5cfbfde1f87f3ab395f2bee96103978c7","justified_epoch":"61322","finalized_epoch":"61321","weight":"57481550000000","validity":"VALID","execution_block_hash":"0x06a0277e02eae44c332bcec82d6715c3113dddce427982014cf5f43432f479e9","extra_data":{"justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7","state_root":"0x4bcecf56081291ab95df1dba25b0f83343d38217e9cec198510b61f6f35afdb3","unrealised_finalized_epoch":"61321","unrealised_justified_epoch":"61322","unrealized_finalized_root":"0x57a41f26678190d3e319c19fe9f4ea3830c4b21710a1e1ae41adcc23d0f030a2","unrealized_justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7"}}`), + err: "invalid JSON: json: cannot unmarshal number into Go struct field forkChoiceNodeJSON.slot of type string", + }, + { + name: "BlockRootInvalid", + input: []byte(`{"slot":"1962336","block_root":"","parent_root":"0xe399a2ee74cf0570b4f980772983bdc5cfbfde1f87f3ab395f2bee96103978c7","justified_epoch":"61322","finalized_epoch":"61321","weight":"57481550000000","validity":"VALID","execution_block_hash":"0x06a0277e02eae44c332bcec82d6715c3113dddce427982014cf5f43432f479e9","extra_data":{"justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7","state_root":"0x4bcecf56081291ab95df1dba25b0f83343d38217e9cec198510b61f6f35afdb3","unrealised_finalized_epoch":"61321","unrealised_justified_epoch":"61322","unrealized_finalized_root":"0x57a41f26678190d3e319c19fe9f4ea3830c4b21710a1e1ae41adcc23d0f030a2","unrealized_justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7"}}`), + err: "incorrect length 0 for block root", + }, + { + name: "ParentRootInvalid", + input: []byte(`{"slot":"1962336","block_root":"0x0f61e82f7b51f41fcd552cbdc64547bd9e1ba54b8404482732927645c2e13ec6","parent_root":"","justified_epoch":"61322","finalized_epoch":"61321","weight":"57481550000000","validity":"VALID","execution_block_hash":"0x06a0277e02eae44c332bcec82d6715c3113dddce427982014cf5f43432f479e9","extra_data":{"justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7","state_root":"0x4bcecf56081291ab95df1dba25b0f83343d38217e9cec198510b61f6f35afdb3","unrealised_finalized_epoch":"61321","unrealised_justified_epoch":"61322","unrealized_finalized_root":"0x57a41f26678190d3e319c19fe9f4ea3830c4b21710a1e1ae41adcc23d0f030a2","unrealized_justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7"}}`), + err: "incorrect length 0 for parent root", + }, + { + name: "JustifiedEpochInvalid", + input: []byte(`{"slot":"1962336","block_root":"0x0f61e82f7b51f41fcd552cbdc64547bd9e1ba54b8404482732927645c2e13ec6","parent_root":"0xe399a2ee74cf0570b4f980772983bdc5cfbfde1f87f3ab395f2bee96103978c7","justified_epoch":-1,"finalized_epoch":"61321","weight":"57481550000000","validity":"VALID","execution_block_hash":"0x06a0277e02eae44c332bcec82d6715c3113dddce427982014cf5f43432f479e9","extra_data":{"justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7","state_root":"0x4bcecf56081291ab95df1dba25b0f83343d38217e9cec198510b61f6f35afdb3","unrealised_finalized_epoch":"61321","unrealised_justified_epoch":"61322","unrealized_finalized_root":"0x57a41f26678190d3e319c19fe9f4ea3830c4b21710a1e1ae41adcc23d0f030a2","unrealized_justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7"}}`), + err: "invalid JSON: json: cannot unmarshal number into Go struct field forkChoiceNodeJSON.justified_epoch of type string", + }, + { + name: "FinalizedEpochInvalid", + input: []byte(`{"slot":"1962336","block_root":"0x0f61e82f7b51f41fcd552cbdc64547bd9e1ba54b8404482732927645c2e13ec6","parent_root":"0xe399a2ee74cf0570b4f980772983bdc5cfbfde1f87f3ab395f2bee96103978c7","justified_epoch":"61322","finalized_epoch":-1,"weight":"57481550000000","validity":"VALID","execution_block_hash":"0x06a0277e02eae44c332bcec82d6715c3113dddce427982014cf5f43432f479e9","extra_data":{"justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7","state_root":"0x4bcecf56081291ab95df1dba25b0f83343d38217e9cec198510b61f6f35afdb3","unrealised_finalized_epoch":"61321","unrealised_justified_epoch":"61322","unrealized_finalized_root":"0x57a41f26678190d3e319c19fe9f4ea3830c4b21710a1e1ae41adcc23d0f030a2","unrealized_justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7"}}`), + err: "invalid JSON: json: cannot unmarshal number into Go struct field forkChoiceNodeJSON.finalized_epoch of type string", + }, + { + name: "WeightInvalid", + input: []byte(`{"slot":"1962336","block_root":"0x0f61e82f7b51f41fcd552cbdc64547bd9e1ba54b8404482732927645c2e13ec6","parent_root":"0xe399a2ee74cf0570b4f980772983bdc5cfbfde1f87f3ab395f2bee96103978c7","justified_epoch":"61322","finalized_epoch":"61321","weight":400,"validity":"VALID","execution_block_hash":"0x06a0277e02eae44c332bcec82d6715c3113dddce427982014cf5f43432f479e9","extra_data":{"justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7","state_root":"0x4bcecf56081291ab95df1dba25b0f83343d38217e9cec198510b61f6f35afdb3","unrealised_finalized_epoch":"61321","unrealised_justified_epoch":"61322","unrealized_finalized_root":"0x57a41f26678190d3e319c19fe9f4ea3830c4b21710a1e1ae41adcc23d0f030a2","unrealized_justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7"}}`), + err: "invalid JSON: json: cannot unmarshal number into Go struct field forkChoiceNodeJSON.weight of type string", + }, + { + name: "ValidityInvalid", + input: []byte(`{"slot":"1962336","block_root":"0x0f61e82f7b51f41fcd552cbdc64547bd9e1ba54b8404482732927645c2e13ec6","parent_root":"0xe399a2ee74cf0570b4f980772983bdc5cfbfde1f87f3ab395f2bee96103978c7","justified_epoch":"61322","finalized_epoch":"61321","weight":"57481550000000","validity":"NOT_A_VALID_VALIDITY","execution_block_hash":"0x06a0277e02eae44c332bcec82d6715c3113dddce427982014cf5f43432f479e9","extra_data":{"justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7","state_root":"0x4bcecf56081291ab95df1dba25b0f83343d38217e9cec198510b61f6f35afdb3","unrealised_finalized_epoch":"61321","unrealised_justified_epoch":"61322","unrealized_finalized_root":"0x57a41f26678190d3e319c19fe9f4ea3830c4b21710a1e1ae41adcc23d0f030a2","unrealized_justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7"}}`), + err: "invalid value for validity: NOT_A_VALID_VALIDITY: invalid value for fork choice node validity: NOT_A_VALID_VALIDITY", + }, + { + name: "ExecutionBlockHashInvalid", + input: []byte(`{"slot":"1962336","block_root":"0x0f61e82f7b51f41fcd552cbdc64547bd9e1ba54b8404482732927645c2e13ec6","parent_root":"0xe399a2ee74cf0570b4f980772983bdc5cfbfde1f87f3ab395f2bee96103978c7","justified_epoch":"61322","finalized_epoch":"61321","weight":"57481550000000","validity":"VALID","execution_block_hash":"abc","extra_data":{"justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7","state_root":"0x4bcecf56081291ab95df1dba25b0f83343d38217e9cec198510b61f6f35afdb3","unrealised_finalized_epoch":"61321","unrealised_justified_epoch":"61322","unrealized_finalized_root":"0x57a41f26678190d3e319c19fe9f4ea3830c4b21710a1e1ae41adcc23d0f030a2","unrealized_justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7"}}`), + err: "invalid value for execution block hash: abc: encoding/hex: odd length hex string", + }, + { + name: "ExtraDataMissing", + input: []byte(`{"slot":"1962336","block_root":"0x0f61e82f7b51f41fcd552cbdc64547bd9e1ba54b8404482732927645c2e13ec6","parent_root":"0xe399a2ee74cf0570b4f980772983bdc5cfbfde1f87f3ab395f2bee96103978c7","justified_epoch":"61322","finalized_epoch":"61321","weight":"57481550000000","validity":"VALID","execution_block_hash":"0x06a0277e02eae44c332bcec82d6715c3113dddce427982014cf5f43432f479e9"}`), + expected: `{"slot":"1962336","block_root":"0x0f61e82f7b51f41fcd552cbdc64547bd9e1ba54b8404482732927645c2e13ec6","parent_root":"0xe399a2ee74cf0570b4f980772983bdc5cfbfde1f87f3ab395f2bee96103978c7","justified_epoch":"61322","finalized_epoch":"61321","weight":"57481550000000","validity":"VALID","execution_block_hash":"0x06a0277e02eae44c332bcec82d6715c3113dddce427982014cf5f43432f479e9"}`, + err: "", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + var fc api.ForkChoiceNode + err := json.Unmarshal(test.input, &fc) + if test.err != "" { + require.EqualError(t, err, test.err) + } else { + require.NoError(t, err) + rt, err := json.Marshal(&fc) + require.NoError(t, err) + assert.Equal(t, string(test.input), string(rt)) + assert.Equal(t, string(rt), fc.String()) + } + }) + } +} From 7216e6e3b7309534c815e201f456d31d0b097fee Mon Sep 17 00:00:00 2001 From: Sam Calder-Mason Date: Mon, 20 Mar 2023 13:58:33 +1000 Subject: [PATCH 04/11] test(forkchoice_test.go): add unit test for ForkChoice function in http package --- http/forkchoice_test.go | 54 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 http/forkchoice_test.go diff --git a/http/forkchoice_test.go b/http/forkchoice_test.go new file mode 100644 index 00000000..e29a3f3e --- /dev/null +++ b/http/forkchoice_test.go @@ -0,0 +1,54 @@ +// Copyright © 2020, 2021 Attestant Limited. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package http_test + +import ( + "context" + "os" + "testing" + + client "github.com/attestantio/go-eth2-client" + "github.com/attestantio/go-eth2-client/http" + "github.com/stretchr/testify/require" +) + +func TestForkChoice(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + tests := []struct { + name string + }{ + { + name: "Good", + }, + } + + service, err := http.New(ctx, + http.WithTimeout(timeout), + http.WithAddress(os.Getenv("HTTP_ADDRESS")), + ) + require.NoError(t, err) + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + fc, err := service.(client.ForkChoiceProvider).ForkChoice(ctx) + require.NoError(t, err) + require.NotNil(t, fc) + require.NotNil(t, fc.FinalizedCheckpoint) + require.NotNil(t, fc.JustifiedCheckpoint) + require.NotNil(t, fc.ForkChoiceNodes) + }) + } +} From 56dde0baed95e143a06a867cd1dc779ae54ccfd9 Mon Sep 17 00:00:00 2001 From: Sam Calder-Mason Date: Tue, 4 Apr 2023 10:27:49 +1000 Subject: [PATCH 05/11] feat(forkchoice): Convert validity to upper case before parsing --- api/v1/forkchoice.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/v1/forkchoice.go b/api/v1/forkchoice.go index 5c8e3cd6..2e5be3c1 100644 --- a/api/v1/forkchoice.go +++ b/api/v1/forkchoice.go @@ -207,7 +207,7 @@ func (f *ForkChoiceNode) UnmarshalJSON(input []byte) error { } f.Weight = weight - validity, err := parseForkChoiceValidity(forkChoiceNodeJSON.Validity) + validity, err := parseForkChoiceValidity(strings.ToUpper(forkChoiceNodeJSON.Validity)) if err != nil { return errors.Wrap(err, fmt.Sprintf("invalid value for validity: %s", forkChoiceNodeJSON.Validity)) } From c97bfb5747a93ac377f26b5af4d6f68182687024 Mon Sep 17 00:00:00 2001 From: Sam Calder-Mason Date: Tue, 4 Apr 2023 14:19:40 +1000 Subject: [PATCH 06/11] feat: Store extraData as string --- api/v1/forkchoice.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/api/v1/forkchoice.go b/api/v1/forkchoice.go index 2e5be3c1..299ab0d7 100644 --- a/api/v1/forkchoice.go +++ b/api/v1/forkchoice.go @@ -125,20 +125,20 @@ type ForkChoiceNode struct { // ExecutiionBlockHash is the execution block hash of the node. ExecutionBlockHash phase0.Root // ExtraData is the extra data of the node. - ExtraData interface{} + ExtraData string } // forkChoiceNodeJSON is the json representation of the struct. type forkChoiceNodeJSON struct { - Slot string `json:"slot"` - BlockRoot string `json:"block_root"` - ParentRoot string `json:"parent_root"` - JustifiedEpoch string `json:"justified_epoch"` - FinalizedEpoch string `json:"finalized_epoch"` - Weight string `json:"weight"` - Validity string `json:"validity"` - ExecutionBlockHash string `json:"execution_block_hash"` - ExtraData interface{} `json:"extra_data,omitempty"` + Slot string `json:"slot"` + BlockRoot string `json:"block_root"` + ParentRoot string `json:"parent_root"` + JustifiedEpoch string `json:"justified_epoch"` + FinalizedEpoch string `json:"finalized_epoch"` + Weight string `json:"weight"` + Validity string `json:"validity"` + ExecutionBlockHash string `json:"execution_block_hash"` + ExtraData string `json:"extra_data,omitempty"` } // MarshalJSON implements json.Marshaler. From a277030741bbefc47c007ccfac9a1a4efcbef18d Mon Sep 17 00:00:00 2001 From: Sam Calder-Mason Date: Tue, 4 Apr 2023 14:59:14 +1000 Subject: [PATCH 07/11] Update forkChoiceNodeJSON to use a map[string]interface{} for ExtraData. --- api/v1/forkchoice.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/api/v1/forkchoice.go b/api/v1/forkchoice.go index 299ab0d7..9482269f 100644 --- a/api/v1/forkchoice.go +++ b/api/v1/forkchoice.go @@ -125,20 +125,20 @@ type ForkChoiceNode struct { // ExecutiionBlockHash is the execution block hash of the node. ExecutionBlockHash phase0.Root // ExtraData is the extra data of the node. - ExtraData string + ExtraData map[string]interface{} } // forkChoiceNodeJSON is the json representation of the struct. type forkChoiceNodeJSON struct { - Slot string `json:"slot"` - BlockRoot string `json:"block_root"` - ParentRoot string `json:"parent_root"` - JustifiedEpoch string `json:"justified_epoch"` - FinalizedEpoch string `json:"finalized_epoch"` - Weight string `json:"weight"` - Validity string `json:"validity"` - ExecutionBlockHash string `json:"execution_block_hash"` - ExtraData string `json:"extra_data,omitempty"` + Slot string `json:"slot"` + BlockRoot string `json:"block_root"` + ParentRoot string `json:"parent_root"` + JustifiedEpoch string `json:"justified_epoch"` + FinalizedEpoch string `json:"finalized_epoch"` + Weight string `json:"weight"` + Validity string `json:"validity"` + ExecutionBlockHash string `json:"execution_block_hash"` + ExtraData map[string]interface{} `json:"extra_data,omitempty"` } // MarshalJSON implements json.Marshaler. From dba34999021d248014b6488726cf6d0f67d33a0b Mon Sep 17 00:00:00 2001 From: Sam Calder-Mason Date: Tue, 11 Apr 2023 14:07:14 +1000 Subject: [PATCH 08/11] Drop parent_root check --- api/v1/forkchoice.go | 3 --- api/v1/forkchoice_test.go | 5 ----- 2 files changed, 8 deletions(-) diff --git a/api/v1/forkchoice.go b/api/v1/forkchoice.go index 9482269f..d9c20419 100644 --- a/api/v1/forkchoice.go +++ b/api/v1/forkchoice.go @@ -184,9 +184,6 @@ func (f *ForkChoiceNode) UnmarshalJSON(input []byte) error { if err != nil { return errors.Wrap(err, fmt.Sprintf("invalid value for parent root: %s", forkChoiceNodeJSON.ParentRoot)) } - if len(parentRoot) != rootLength { - return fmt.Errorf("incorrect length %d for parent root", len(parentRoot)) - } copy(f.ParentRoot[:], parentRoot) justifiedEpoch, err := strconv.ParseUint(forkChoiceNodeJSON.JustifiedEpoch, 10, 64) diff --git a/api/v1/forkchoice_test.go b/api/v1/forkchoice_test.go index fda88220..4891ad73 100644 --- a/api/v1/forkchoice_test.go +++ b/api/v1/forkchoice_test.go @@ -108,11 +108,6 @@ func TestForkChoiceNodeJSON(t *testing.T) { input: []byte(`{"slot":"1962336","block_root":"","parent_root":"0xe399a2ee74cf0570b4f980772983bdc5cfbfde1f87f3ab395f2bee96103978c7","justified_epoch":"61322","finalized_epoch":"61321","weight":"57481550000000","validity":"VALID","execution_block_hash":"0x06a0277e02eae44c332bcec82d6715c3113dddce427982014cf5f43432f479e9","extra_data":{"justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7","state_root":"0x4bcecf56081291ab95df1dba25b0f83343d38217e9cec198510b61f6f35afdb3","unrealised_finalized_epoch":"61321","unrealised_justified_epoch":"61322","unrealized_finalized_root":"0x57a41f26678190d3e319c19fe9f4ea3830c4b21710a1e1ae41adcc23d0f030a2","unrealized_justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7"}}`), err: "incorrect length 0 for block root", }, - { - name: "ParentRootInvalid", - input: []byte(`{"slot":"1962336","block_root":"0x0f61e82f7b51f41fcd552cbdc64547bd9e1ba54b8404482732927645c2e13ec6","parent_root":"","justified_epoch":"61322","finalized_epoch":"61321","weight":"57481550000000","validity":"VALID","execution_block_hash":"0x06a0277e02eae44c332bcec82d6715c3113dddce427982014cf5f43432f479e9","extra_data":{"justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7","state_root":"0x4bcecf56081291ab95df1dba25b0f83343d38217e9cec198510b61f6f35afdb3","unrealised_finalized_epoch":"61321","unrealised_justified_epoch":"61322","unrealized_finalized_root":"0x57a41f26678190d3e319c19fe9f4ea3830c4b21710a1e1ae41adcc23d0f030a2","unrealized_justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7"}}`), - err: "incorrect length 0 for parent root", - }, { name: "JustifiedEpochInvalid", input: []byte(`{"slot":"1962336","block_root":"0x0f61e82f7b51f41fcd552cbdc64547bd9e1ba54b8404482732927645c2e13ec6","parent_root":"0xe399a2ee74cf0570b4f980772983bdc5cfbfde1f87f3ab395f2bee96103978c7","justified_epoch":-1,"finalized_epoch":"61321","weight":"57481550000000","validity":"VALID","execution_block_hash":"0x06a0277e02eae44c332bcec82d6715c3113dddce427982014cf5f43432f479e9","extra_data":{"justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7","state_root":"0x4bcecf56081291ab95df1dba25b0f83343d38217e9cec198510b61f6f35afdb3","unrealised_finalized_epoch":"61321","unrealised_justified_epoch":"61322","unrealized_finalized_root":"0x57a41f26678190d3e319c19fe9f4ea3830c4b21710a1e1ae41adcc23d0f030a2","unrealized_justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7"}}`), From 6a8c8094fc056d1d5b8163bbb85479823485caa0 Mon Sep 17 00:00:00 2001 From: Sam Calder-Mason Date: Mon, 26 Jun 2023 11:03:36 +1000 Subject: [PATCH 09/11] feat: Add sleepy/erroring functions --- testclients/erroring.go | 12 ++++++++++++ testclients/sleepy.go | 10 ++++++++++ 2 files changed, 22 insertions(+) diff --git a/testclients/erroring.go b/testclients/erroring.go index 19343233..e59278ac 100644 --- a/testclients/erroring.go +++ b/testclients/erroring.go @@ -688,3 +688,15 @@ func (s *Erroring) BeaconStateRoot(ctx context.Context, stateID string) (*phase0 } return next.BeaconStateRoot(ctx, stateID) } + +// Fork fetches the node's current fork choice context. +func (s *Erroring) ForkChoice(ctx context.Context) (*apiv1.ForkChoice, error) { + if err := s.maybeError(ctx); err != nil { + return nil, err + } + next, isNext := s.next.(consensusclient.ForkChoiceProvider) + if !isNext { + return nil, fmt.Errorf("%s@%s does not support this call", s.next.Name(), s.next.Address()) + } + return next.ForkChoice(ctx) +} diff --git a/testclients/sleepy.go b/testclients/sleepy.go index 8471bcc7..0ddf52e8 100644 --- a/testclients/sleepy.go +++ b/testclients/sleepy.go @@ -455,3 +455,13 @@ func (s *Sleepy) GenesisTime(ctx context.Context) (time.Time, error) { } return next.GenesisTime(ctx) } + +// ForkChoice fetches the node's current fork choice context. +func (s *Sleepy) ForkChoice(ctx context.Context) (*apiv1.ForkChoice, error) { + s.sleep(ctx) + next, isNext := s.next.(consensusclient.ForkChoiceProvider) + if !isNext { + return nil, errors.New("next does not support this call") + } + return next.ForkChoice(ctx) +} From 4e01ba3303523fbc02861634321ff012348981e9 Mon Sep 17 00:00:00 2001 From: Sam Calder-Mason Date: Mon, 26 Jun 2023 11:26:08 +1000 Subject: [PATCH 10/11] feat: Create ForkChoiceNodeValidity --- api/v1/forkchoice.go | 62 +++++++++++++++++++++++++++++++-------- api/v1/forkchoice_test.go | 26 ++++++++-------- 2 files changed, 63 insertions(+), 25 deletions(-) diff --git a/api/v1/forkchoice.go b/api/v1/forkchoice.go index d9c20419..cfb85371 100644 --- a/api/v1/forkchoice.go +++ b/api/v1/forkchoice.go @@ -86,25 +86,63 @@ type forkChoiceJSON struct { ForkChoiceNodes []*ForkChoiceNode `json:"fork_choice_nodes"` } -type ForkChoiceNodeValidity string +// ForkChoiceNodeValidity represents the validity of a fork choice node. +type ForkChoiceNodeValidity uint64 const ( - ForkChoiceNodeValidityInvalid ForkChoiceNodeValidity = "INVALID" - ForkChoiceNodeValidityValid ForkChoiceNodeValidity = "VALID" - ForkChoiceNodeValidityOptimistic ForkChoiceNodeValidity = "OPTIMISTIC" + // ForkChoiceNodeValidityUnknown is an unknown fork choice node. + ForkChoiceNodeValidityUnknown ForkChoiceNodeValidity = iota + // ForkChoiceNodeValidityInvalid is an invalid fork choice node. + ForkChoiceNodeValidityInvalid + // ForkChoiceNodeValidityValid is a valid fork choice node. + ForkChoiceNodeValidityValid + // ForkChoiceNodeValidityOptimistic is an optimistic fork choice node. + ForkChoiceNodeValidityOptimistic ) -func parseForkChoiceValidity(input string) (ForkChoiceNodeValidity, error) { - switch input { - case "INVALID": +var ForkChoiceNodeValidityStrings = [...]string{ + "unknown", + "invalid", + "valid", + "optimistic", +} + +func ForkChoiceNodeValidityFromString(input string) (ForkChoiceNodeValidity, error) { + switch strings.ToLower(string(input)) { + case "invalid": return ForkChoiceNodeValidityInvalid, nil - case "VALID": + case "valid": return ForkChoiceNodeValidityValid, nil - case "OPTIMISTIC": + case "optimistic": return ForkChoiceNodeValidityOptimistic, nil default: - return "", errors.New(fmt.Sprintf("invalid value for fork choice node validity: %s", input)) + return ForkChoiceNodeValidityUnknown, fmt.Errorf("unrecognised fork choice validity: %s", string(input)) + } +} + +// MarshalJSON implements json.Marshaler. +func (d *ForkChoiceNodeValidity) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf("%q", ForkChoiceNodeValidityStrings[*d])), nil +} + +// UnmarshalJSON implements json.Unmarshaler. +func (d *ForkChoiceNodeValidity) UnmarshalJSON(input []byte) error { + var err error + + inputString := strings.Trim(string(input), "\"") + if *d, err = ForkChoiceNodeValidityFromString(inputString); err != nil { + return err + } + + return nil +} + +// String returns a string representation of the +func (d ForkChoiceNodeValidity) String() string { + if int(d) >= len(ForkChoiceNodeValidityStrings) { + return "unknown" } + return ForkChoiceNodeValidityStrings[d] } type ForkChoiceNode struct { @@ -150,7 +188,7 @@ func (f *ForkChoiceNode) MarshalJSON() ([]byte, error) { JustifiedEpoch: fmt.Sprintf("%d", f.JustifiedEpoch), FinalizedEpoch: fmt.Sprintf("%d", f.FinalizedEpoch), Weight: fmt.Sprintf("%d", f.Weight), - Validity: string(f.Validity), + Validity: f.Validity.String(), ExecutionBlockHash: fmt.Sprintf("%#x", f.ExecutionBlockHash), ExtraData: f.ExtraData, }) @@ -204,7 +242,7 @@ func (f *ForkChoiceNode) UnmarshalJSON(input []byte) error { } f.Weight = weight - validity, err := parseForkChoiceValidity(strings.ToUpper(forkChoiceNodeJSON.Validity)) + validity, err := ForkChoiceNodeValidityFromString(forkChoiceNodeJSON.Validity) if err != nil { return errors.Wrap(err, fmt.Sprintf("invalid value for validity: %s", forkChoiceNodeJSON.Validity)) } diff --git a/api/v1/forkchoice_test.go b/api/v1/forkchoice_test.go index 4891ad73..bf26c245 100644 --- a/api/v1/forkchoice_test.go +++ b/api/v1/forkchoice_test.go @@ -27,8 +27,8 @@ func TestForkChoiceJSON(t *testing.T) { }, { name: "Good", - input: []byte(`{"justified_checkpoint":{"epoch":"1","root":"0x0000000000000000000000000000000000000000000000000000000000000000"},"finalized_checkpoint":{"epoch":"2","root":"0x0100000000000000000000000000000000000000000000000000000000000000"},"fork_choice_nodes":[{"slot":"1962336","block_root":"0x0f61e82f7b51f41fcd552cbdc64547bd9e1ba54b8404482732927645c2e13ec6","parent_root":"0xe399a2ee74cf0570b4f980772983bdc5cfbfde1f87f3ab395f2bee96103978c7","justified_epoch":"61322","finalized_epoch":"61321","weight":"57481550000000","validity":"VALID","execution_block_hash":"0x06a0277e02eae44c332bcec82d6715c3113dddce427982014cf5f43432f479e9","extra_data":{"justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7","state_root":"0x4bcecf56081291ab95df1dba25b0f83343d38217e9cec198510b61f6f35afdb3","unrealised_finalized_epoch":"61321","unrealised_justified_epoch":"61322","unrealized_finalized_root":"0x57a41f26678190d3e319c19fe9f4ea3830c4b21710a1e1ae41adcc23d0f030a2","unrealized_justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7"}}]}`), - expected: `{"justified_checkpoint":{"epoch":"1","root":"0x0000000000000000000000000000000000000000000000000000000000000000"},"finalized_checkpoint":{"epoch":"2","root":"0x0100000000000000000000000000000000000000000000000000000000000000"},"fork_choice_nodes":[{"slot":"1962336","block_root":"0x0f61e82f7b51f41fcd552cbdc64547bd9e1ba54b8404482732927645c2e13ec6","parent_root":"0xe399a2ee74cf0570b4f980772983bdc5cfbfde1f87f3ab395f2bee96103978c7","justified_epoch":"61322","finalized_epoch":"61321","weight":"57481550000000","validity":"VALID","execution_block_hash":"0x06a0277e02eae44c332bcec82d6715c3113dddce427982014cf5f43432f479e9","extra_data":{"justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7","state_root":"0x4bcecf56081291ab95df1dba25b0f83343d38217e9cec198510b61f6f35afdb3","unrealised_finalized_epoch":"61321","unrealised_justified_epoch":"61322","unrealized_finalized_root":"0x57a41f26678190d3e319c19fe9f4ea3830c4b21710a1e1ae41adcc23d0f030a2","unrealized_justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7"}}]}`, + input: []byte(`{"justified_checkpoint":{"epoch":"1","root":"0x0000000000000000000000000000000000000000000000000000000000000000"},"finalized_checkpoint":{"epoch":"2","root":"0x0100000000000000000000000000000000000000000000000000000000000000"},"fork_choice_nodes":[{"slot":"1962336","block_root":"0x0f61e82f7b51f41fcd552cbdc64547bd9e1ba54b8404482732927645c2e13ec6","parent_root":"0xe399a2ee74cf0570b4f980772983bdc5cfbfde1f87f3ab395f2bee96103978c7","justified_epoch":"61322","finalized_epoch":"61321","weight":"57481550000000","validity":"valid","execution_block_hash":"0x06a0277e02eae44c332bcec82d6715c3113dddce427982014cf5f43432f479e9","extra_data":{"justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7","state_root":"0x4bcecf56081291ab95df1dba25b0f83343d38217e9cec198510b61f6f35afdb3","unrealised_finalized_epoch":"61321","unrealised_justified_epoch":"61322","unrealized_finalized_root":"0x57a41f26678190d3e319c19fe9f4ea3830c4b21710a1e1ae41adcc23d0f030a2","unrealized_justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7"}}]}`), + expected: `{"justified_checkpoint":{"epoch":"1","root":"0x0000000000000000000000000000000000000000000000000000000000000000"},"finalized_checkpoint":{"epoch":"2","root":"0x0100000000000000000000000000000000000000000000000000000000000000"},"fork_choice_nodes":[{"slot":"1962336","block_root":"0x0f61e82f7b51f41fcd552cbdc64547bd9e1ba54b8404482732927645c2e13ec6","parent_root":"0xe399a2ee74cf0570b4f980772983bdc5cfbfde1f87f3ab395f2bee96103978c7","justified_epoch":"61322","finalized_epoch":"61321","weight":"57481550000000","validity":"valid","execution_block_hash":"0x06a0277e02eae44c332bcec82d6715c3113dddce427982014cf5f43432f479e9","extra_data":{"justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7","state_root":"0x4bcecf56081291ab95df1dba25b0f83343d38217e9cec198510b61f6f35afdb3","unrealised_finalized_epoch":"61321","unrealised_justified_epoch":"61322","unrealized_finalized_root":"0x57a41f26678190d3e319c19fe9f4ea3830c4b21710a1e1ae41adcc23d0f030a2","unrealized_justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7"}}]}`, err: "", }, { @@ -94,49 +94,49 @@ func TestForkChoiceNodeJSON(t *testing.T) { }, { name: "Good", - input: []byte(`{"slot":"1962336","block_root":"0x0f61e82f7b51f41fcd552cbdc64547bd9e1ba54b8404482732927645c2e13ec6","parent_root":"0xe399a2ee74cf0570b4f980772983bdc5cfbfde1f87f3ab395f2bee96103978c7","justified_epoch":"61322","finalized_epoch":"61321","weight":"57481550000000","validity":"VALID","execution_block_hash":"0x06a0277e02eae44c332bcec82d6715c3113dddce427982014cf5f43432f479e9","extra_data":{"justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7","state_root":"0x4bcecf56081291ab95df1dba25b0f83343d38217e9cec198510b61f6f35afdb3","unrealised_finalized_epoch":"61321","unrealised_justified_epoch":"61322","unrealized_finalized_root":"0x57a41f26678190d3e319c19fe9f4ea3830c4b21710a1e1ae41adcc23d0f030a2","unrealized_justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7"}}`), - expected: `{"slot":"1962336","block_root":"0x0f61e82f7b51f41fcd552cbdc64547bd9e1ba54b8404482732927645c2e13ec6","parent_root":"0xe399a2ee74cf0570b4f980772983bdc5cfbfde1f87f3ab395f2bee96103978c7","justified_epoch":"61322","finalized_epoch":"61321","weight":"57481550000000","validity":"VALID","execution_block_hash":"0x06a0277e02eae44c332bcec82d6715c3113dddce427982014cf5f43432f479e9","extra_data":{"justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7","state_root":"0x4bcecf56081291ab95df1dba25b0f83343d38217e9cec198510b61f6f35afdb3","unrealised_finalized_epoch":"61321","unrealised_justified_epoch":"61322","unrealized_finalized_root":"0x57a41f26678190d3e319c19fe9f4ea3830c4b21710a1e1ae41adcc23d0f030a2","unrealized_justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7"}}`, + input: []byte(`{"slot":"1962336","block_root":"0x0f61e82f7b51f41fcd552cbdc64547bd9e1ba54b8404482732927645c2e13ec6","parent_root":"0xe399a2ee74cf0570b4f980772983bdc5cfbfde1f87f3ab395f2bee96103978c7","justified_epoch":"61322","finalized_epoch":"61321","weight":"57481550000000","validity":"valid","execution_block_hash":"0x06a0277e02eae44c332bcec82d6715c3113dddce427982014cf5f43432f479e9","extra_data":{"justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7","state_root":"0x4bcecf56081291ab95df1dba25b0f83343d38217e9cec198510b61f6f35afdb3","unrealised_finalized_epoch":"61321","unrealised_justified_epoch":"61322","unrealized_finalized_root":"0x57a41f26678190d3e319c19fe9f4ea3830c4b21710a1e1ae41adcc23d0f030a2","unrealized_justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7"}}`), + expected: `{"slot":"1962336","block_root":"0x0f61e82f7b51f41fcd552cbdc64547bd9e1ba54b8404482732927645c2e13ec6","parent_root":"0xe399a2ee74cf0570b4f980772983bdc5cfbfde1f87f3ab395f2bee96103978c7","justified_epoch":"61322","finalized_epoch":"61321","weight":"57481550000000","validity":"valid","execution_block_hash":"0x06a0277e02eae44c332bcec82d6715c3113dddce427982014cf5f43432f479e9","extra_data":{"justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7","state_root":"0x4bcecf56081291ab95df1dba25b0f83343d38217e9cec198510b61f6f35afdb3","unrealised_finalized_epoch":"61321","unrealised_justified_epoch":"61322","unrealized_finalized_root":"0x57a41f26678190d3e319c19fe9f4ea3830c4b21710a1e1ae41adcc23d0f030a2","unrealized_justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7"}}`, err: "", }, { name: "SlotInvalid", - input: []byte(`{"slot":1,"block_root":"0x0f61e82f7b51f41fcd552cbdc64547bd9e1ba54b8404482732927645c2e13ec6","parent_root":"0xe399a2ee74cf0570b4f980772983bdc5cfbfde1f87f3ab395f2bee96103978c7","justified_epoch":"61322","finalized_epoch":"61321","weight":"57481550000000","validity":"VALID","execution_block_hash":"0x06a0277e02eae44c332bcec82d6715c3113dddce427982014cf5f43432f479e9","extra_data":{"justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7","state_root":"0x4bcecf56081291ab95df1dba25b0f83343d38217e9cec198510b61f6f35afdb3","unrealised_finalized_epoch":"61321","unrealised_justified_epoch":"61322","unrealized_finalized_root":"0x57a41f26678190d3e319c19fe9f4ea3830c4b21710a1e1ae41adcc23d0f030a2","unrealized_justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7"}}`), + input: []byte(`{"slot":1,"block_root":"0x0f61e82f7b51f41fcd552cbdc64547bd9e1ba54b8404482732927645c2e13ec6","parent_root":"0xe399a2ee74cf0570b4f980772983bdc5cfbfde1f87f3ab395f2bee96103978c7","justified_epoch":"61322","finalized_epoch":"61321","weight":"57481550000000","validity":"valid","execution_block_hash":"0x06a0277e02eae44c332bcec82d6715c3113dddce427982014cf5f43432f479e9","extra_data":{"justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7","state_root":"0x4bcecf56081291ab95df1dba25b0f83343d38217e9cec198510b61f6f35afdb3","unrealised_finalized_epoch":"61321","unrealised_justified_epoch":"61322","unrealized_finalized_root":"0x57a41f26678190d3e319c19fe9f4ea3830c4b21710a1e1ae41adcc23d0f030a2","unrealized_justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7"}}`), err: "invalid JSON: json: cannot unmarshal number into Go struct field forkChoiceNodeJSON.slot of type string", }, { name: "BlockRootInvalid", - input: []byte(`{"slot":"1962336","block_root":"","parent_root":"0xe399a2ee74cf0570b4f980772983bdc5cfbfde1f87f3ab395f2bee96103978c7","justified_epoch":"61322","finalized_epoch":"61321","weight":"57481550000000","validity":"VALID","execution_block_hash":"0x06a0277e02eae44c332bcec82d6715c3113dddce427982014cf5f43432f479e9","extra_data":{"justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7","state_root":"0x4bcecf56081291ab95df1dba25b0f83343d38217e9cec198510b61f6f35afdb3","unrealised_finalized_epoch":"61321","unrealised_justified_epoch":"61322","unrealized_finalized_root":"0x57a41f26678190d3e319c19fe9f4ea3830c4b21710a1e1ae41adcc23d0f030a2","unrealized_justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7"}}`), + input: []byte(`{"slot":"1962336","block_root":"","parent_root":"0xe399a2ee74cf0570b4f980772983bdc5cfbfde1f87f3ab395f2bee96103978c7","justified_epoch":"61322","finalized_epoch":"61321","weight":"57481550000000","validity":"valid","execution_block_hash":"0x06a0277e02eae44c332bcec82d6715c3113dddce427982014cf5f43432f479e9","extra_data":{"justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7","state_root":"0x4bcecf56081291ab95df1dba25b0f83343d38217e9cec198510b61f6f35afdb3","unrealised_finalized_epoch":"61321","unrealised_justified_epoch":"61322","unrealized_finalized_root":"0x57a41f26678190d3e319c19fe9f4ea3830c4b21710a1e1ae41adcc23d0f030a2","unrealized_justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7"}}`), err: "incorrect length 0 for block root", }, { name: "JustifiedEpochInvalid", - input: []byte(`{"slot":"1962336","block_root":"0x0f61e82f7b51f41fcd552cbdc64547bd9e1ba54b8404482732927645c2e13ec6","parent_root":"0xe399a2ee74cf0570b4f980772983bdc5cfbfde1f87f3ab395f2bee96103978c7","justified_epoch":-1,"finalized_epoch":"61321","weight":"57481550000000","validity":"VALID","execution_block_hash":"0x06a0277e02eae44c332bcec82d6715c3113dddce427982014cf5f43432f479e9","extra_data":{"justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7","state_root":"0x4bcecf56081291ab95df1dba25b0f83343d38217e9cec198510b61f6f35afdb3","unrealised_finalized_epoch":"61321","unrealised_justified_epoch":"61322","unrealized_finalized_root":"0x57a41f26678190d3e319c19fe9f4ea3830c4b21710a1e1ae41adcc23d0f030a2","unrealized_justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7"}}`), + input: []byte(`{"slot":"1962336","block_root":"0x0f61e82f7b51f41fcd552cbdc64547bd9e1ba54b8404482732927645c2e13ec6","parent_root":"0xe399a2ee74cf0570b4f980772983bdc5cfbfde1f87f3ab395f2bee96103978c7","justified_epoch":-1,"finalized_epoch":"61321","weight":"57481550000000","validity":"valid","execution_block_hash":"0x06a0277e02eae44c332bcec82d6715c3113dddce427982014cf5f43432f479e9","extra_data":{"justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7","state_root":"0x4bcecf56081291ab95df1dba25b0f83343d38217e9cec198510b61f6f35afdb3","unrealised_finalized_epoch":"61321","unrealised_justified_epoch":"61322","unrealized_finalized_root":"0x57a41f26678190d3e319c19fe9f4ea3830c4b21710a1e1ae41adcc23d0f030a2","unrealized_justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7"}}`), err: "invalid JSON: json: cannot unmarshal number into Go struct field forkChoiceNodeJSON.justified_epoch of type string", }, { name: "FinalizedEpochInvalid", - input: []byte(`{"slot":"1962336","block_root":"0x0f61e82f7b51f41fcd552cbdc64547bd9e1ba54b8404482732927645c2e13ec6","parent_root":"0xe399a2ee74cf0570b4f980772983bdc5cfbfde1f87f3ab395f2bee96103978c7","justified_epoch":"61322","finalized_epoch":-1,"weight":"57481550000000","validity":"VALID","execution_block_hash":"0x06a0277e02eae44c332bcec82d6715c3113dddce427982014cf5f43432f479e9","extra_data":{"justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7","state_root":"0x4bcecf56081291ab95df1dba25b0f83343d38217e9cec198510b61f6f35afdb3","unrealised_finalized_epoch":"61321","unrealised_justified_epoch":"61322","unrealized_finalized_root":"0x57a41f26678190d3e319c19fe9f4ea3830c4b21710a1e1ae41adcc23d0f030a2","unrealized_justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7"}}`), + input: []byte(`{"slot":"1962336","block_root":"0x0f61e82f7b51f41fcd552cbdc64547bd9e1ba54b8404482732927645c2e13ec6","parent_root":"0xe399a2ee74cf0570b4f980772983bdc5cfbfde1f87f3ab395f2bee96103978c7","justified_epoch":"61322","finalized_epoch":-1,"weight":"57481550000000","validity":"valid","execution_block_hash":"0x06a0277e02eae44c332bcec82d6715c3113dddce427982014cf5f43432f479e9","extra_data":{"justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7","state_root":"0x4bcecf56081291ab95df1dba25b0f83343d38217e9cec198510b61f6f35afdb3","unrealised_finalized_epoch":"61321","unrealised_justified_epoch":"61322","unrealized_finalized_root":"0x57a41f26678190d3e319c19fe9f4ea3830c4b21710a1e1ae41adcc23d0f030a2","unrealized_justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7"}}`), err: "invalid JSON: json: cannot unmarshal number into Go struct field forkChoiceNodeJSON.finalized_epoch of type string", }, { name: "WeightInvalid", - input: []byte(`{"slot":"1962336","block_root":"0x0f61e82f7b51f41fcd552cbdc64547bd9e1ba54b8404482732927645c2e13ec6","parent_root":"0xe399a2ee74cf0570b4f980772983bdc5cfbfde1f87f3ab395f2bee96103978c7","justified_epoch":"61322","finalized_epoch":"61321","weight":400,"validity":"VALID","execution_block_hash":"0x06a0277e02eae44c332bcec82d6715c3113dddce427982014cf5f43432f479e9","extra_data":{"justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7","state_root":"0x4bcecf56081291ab95df1dba25b0f83343d38217e9cec198510b61f6f35afdb3","unrealised_finalized_epoch":"61321","unrealised_justified_epoch":"61322","unrealized_finalized_root":"0x57a41f26678190d3e319c19fe9f4ea3830c4b21710a1e1ae41adcc23d0f030a2","unrealized_justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7"}}`), + input: []byte(`{"slot":"1962336","block_root":"0x0f61e82f7b51f41fcd552cbdc64547bd9e1ba54b8404482732927645c2e13ec6","parent_root":"0xe399a2ee74cf0570b4f980772983bdc5cfbfde1f87f3ab395f2bee96103978c7","justified_epoch":"61322","finalized_epoch":"61321","weight":400,"validity":"valid","execution_block_hash":"0x06a0277e02eae44c332bcec82d6715c3113dddce427982014cf5f43432f479e9","extra_data":{"justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7","state_root":"0x4bcecf56081291ab95df1dba25b0f83343d38217e9cec198510b61f6f35afdb3","unrealised_finalized_epoch":"61321","unrealised_justified_epoch":"61322","unrealized_finalized_root":"0x57a41f26678190d3e319c19fe9f4ea3830c4b21710a1e1ae41adcc23d0f030a2","unrealized_justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7"}}`), err: "invalid JSON: json: cannot unmarshal number into Go struct field forkChoiceNodeJSON.weight of type string", }, { name: "ValidityInvalid", input: []byte(`{"slot":"1962336","block_root":"0x0f61e82f7b51f41fcd552cbdc64547bd9e1ba54b8404482732927645c2e13ec6","parent_root":"0xe399a2ee74cf0570b4f980772983bdc5cfbfde1f87f3ab395f2bee96103978c7","justified_epoch":"61322","finalized_epoch":"61321","weight":"57481550000000","validity":"NOT_A_VALID_VALIDITY","execution_block_hash":"0x06a0277e02eae44c332bcec82d6715c3113dddce427982014cf5f43432f479e9","extra_data":{"justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7","state_root":"0x4bcecf56081291ab95df1dba25b0f83343d38217e9cec198510b61f6f35afdb3","unrealised_finalized_epoch":"61321","unrealised_justified_epoch":"61322","unrealized_finalized_root":"0x57a41f26678190d3e319c19fe9f4ea3830c4b21710a1e1ae41adcc23d0f030a2","unrealized_justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7"}}`), - err: "invalid value for validity: NOT_A_VALID_VALIDITY: invalid value for fork choice node validity: NOT_A_VALID_VALIDITY", + err: "invalid value for validity: NOT_A_VALID_VALIDITY: unrecognised fork choice validity: NOT_A_VALID_VALIDITY", }, { name: "ExecutionBlockHashInvalid", - input: []byte(`{"slot":"1962336","block_root":"0x0f61e82f7b51f41fcd552cbdc64547bd9e1ba54b8404482732927645c2e13ec6","parent_root":"0xe399a2ee74cf0570b4f980772983bdc5cfbfde1f87f3ab395f2bee96103978c7","justified_epoch":"61322","finalized_epoch":"61321","weight":"57481550000000","validity":"VALID","execution_block_hash":"abc","extra_data":{"justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7","state_root":"0x4bcecf56081291ab95df1dba25b0f83343d38217e9cec198510b61f6f35afdb3","unrealised_finalized_epoch":"61321","unrealised_justified_epoch":"61322","unrealized_finalized_root":"0x57a41f26678190d3e319c19fe9f4ea3830c4b21710a1e1ae41adcc23d0f030a2","unrealized_justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7"}}`), + input: []byte(`{"slot":"1962336","block_root":"0x0f61e82f7b51f41fcd552cbdc64547bd9e1ba54b8404482732927645c2e13ec6","parent_root":"0xe399a2ee74cf0570b4f980772983bdc5cfbfde1f87f3ab395f2bee96103978c7","justified_epoch":"61322","finalized_epoch":"61321","weight":"57481550000000","validity":"valid","execution_block_hash":"abc","extra_data":{"justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7","state_root":"0x4bcecf56081291ab95df1dba25b0f83343d38217e9cec198510b61f6f35afdb3","unrealised_finalized_epoch":"61321","unrealised_justified_epoch":"61322","unrealized_finalized_root":"0x57a41f26678190d3e319c19fe9f4ea3830c4b21710a1e1ae41adcc23d0f030a2","unrealized_justified_root":"0xdee6c83ee7dc6c0916a8d43c4e7cda93655857da0487f193a62852699e5c39f7"}}`), err: "invalid value for execution block hash: abc: encoding/hex: odd length hex string", }, { name: "ExtraDataMissing", - input: []byte(`{"slot":"1962336","block_root":"0x0f61e82f7b51f41fcd552cbdc64547bd9e1ba54b8404482732927645c2e13ec6","parent_root":"0xe399a2ee74cf0570b4f980772983bdc5cfbfde1f87f3ab395f2bee96103978c7","justified_epoch":"61322","finalized_epoch":"61321","weight":"57481550000000","validity":"VALID","execution_block_hash":"0x06a0277e02eae44c332bcec82d6715c3113dddce427982014cf5f43432f479e9"}`), - expected: `{"slot":"1962336","block_root":"0x0f61e82f7b51f41fcd552cbdc64547bd9e1ba54b8404482732927645c2e13ec6","parent_root":"0xe399a2ee74cf0570b4f980772983bdc5cfbfde1f87f3ab395f2bee96103978c7","justified_epoch":"61322","finalized_epoch":"61321","weight":"57481550000000","validity":"VALID","execution_block_hash":"0x06a0277e02eae44c332bcec82d6715c3113dddce427982014cf5f43432f479e9"}`, + input: []byte(`{"slot":"1962336","block_root":"0x0f61e82f7b51f41fcd552cbdc64547bd9e1ba54b8404482732927645c2e13ec6","parent_root":"0xe399a2ee74cf0570b4f980772983bdc5cfbfde1f87f3ab395f2bee96103978c7","justified_epoch":"61322","finalized_epoch":"61321","weight":"57481550000000","validity":"valid","execution_block_hash":"0x06a0277e02eae44c332bcec82d6715c3113dddce427982014cf5f43432f479e9"}`), + expected: `{"slot":"1962336","block_root":"0x0f61e82f7b51f41fcd552cbdc64547bd9e1ba54b8404482732927645c2e13ec6","parent_root":"0xe399a2ee74cf0570b4f980772983bdc5cfbfde1f87f3ab395f2bee96103978c7","justified_epoch":"61322","finalized_epoch":"61321","weight":"57481550000000","validity":"valid","execution_block_hash":"0x06a0277e02eae44c332bcec82d6715c3113dddce427982014cf5f43432f479e9"}`, err: "", }, } From 3914369a744e78b5a08cb28e4a979dcd51c7e607 Mon Sep 17 00:00:00 2001 From: Sam Calder-Mason Date: Mon, 26 Jun 2023 11:27:14 +1000 Subject: [PATCH 11/11] fix: ForkChoiceNodeValidity String() comment --- api/v1/forkchoice.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/v1/forkchoice.go b/api/v1/forkchoice.go index cfb85371..f170a493 100644 --- a/api/v1/forkchoice.go +++ b/api/v1/forkchoice.go @@ -137,7 +137,7 @@ func (d *ForkChoiceNodeValidity) UnmarshalJSON(input []byte) error { return nil } -// String returns a string representation of the +// String returns a string representation of the ForkChoiceNodeValidity. func (d ForkChoiceNodeValidity) String() string { if int(d) >= len(ForkChoiceNodeValidityStrings) { return "unknown"