From fc6294144b1de2079246c7c226b34ca4cd660851 Mon Sep 17 00:00:00 2001 From: Nikita Kryuchkov Date: Wed, 4 Dec 2024 18:48:28 +0400 Subject: [PATCH] alan: cleanup after fork (#1820) * alan: cleanup after fork * fix linter * cleanup spec tests jobs * cleanup discovery leftovers * remove genesis differ * fix unknown validator test * fix issues after merging stage * fix message validation tests * get rid of next domain type * fix bug with validation initialization * cleanup * fix imports * increase custom domain by 1 * get rid of github.com/ssvlabs/ssv-spec-pre-cc * fix issues after merging * approve spec diff * fix linter --------- Co-authored-by: moshe-blox --- .github/workflows/genesis-spec-alignment.yml | 34 - .github/workflows/spec-test-raceless.yml | 6 - Makefile | 12 - beacon/goclient/genesisgoclient/adapter.go | 41 - cli/operator/node.go | 82 +- cli/operator/node_test.go | 2 +- ekm/eth_key_manager_signer.go | 27 +- ekm/genesis_adapter.go | 77 - ekm/signer_key_manager_test.go | 77 +- eth/eventhandler/handlers.go | 2 +- exporter/api/query_handlers_test.go | 12 +- go.mod | 1 - go.sum | 2 - ibft/genesisstorage/store.go | 191 -- ibft/genesisstorage/store_test.go | 183 -- ibft/genesisstorage/stores.go | 53 - ibft/genesisstorage/stores_test.go | 49 - message/validation/consensus_validation.go | 3 +- message/validation/fork.go | 33 - message/validation/genesis/consensus_state.go | 36 - .../genesis/consensus_validation.go | 466 ---- .../genesis/consensus_validation_test.go | 106 - message/validation/genesis/errors.go | 100 - message/validation/genesis/message_counts.go | 158 -- .../validation/genesis/partial_validation.go | 198 -- message/validation/genesis/rsa.go | 45 - message/validation/genesis/signer_state.go | 45 - message/validation/genesis/utils_test.go | 197 -- message/validation/genesis/validation.go | 581 ---- message/validation/genesis/validation_test.go | 2477 ----------------- message/validation/logger_fields.go | 29 - message/validation/signed_ssv_message.go | 8 +- message/validation/validation_test.go | 98 +- ...onfiglock_add_alan_fork_to_network_name.go | 2 +- monitoring/metricsreporter/genesis.go | 57 - .../metricsreporter/metrics_reporter.go | 8 - .../metricsreporter/nop_metrics_reporter.go | 59 +- network/commons/common.go | 88 - network/discovery/dv5_service.go | 66 +- network/discovery/dv5_service_test.go | 54 +- network/discovery/forking_dv5_listener.go | 103 - .../discovery/forking_dv5_listener_test.go | 267 -- network/discovery/iterator_test.go | 123 - network/discovery/service_test.go | 69 +- network/discovery/util_test.go | 91 +- network/p2p/p2p.go | 9 - network/p2p/p2p_genesis.go | 66 - network/p2p/p2p_pubsub.go | 34 +- network/p2p/p2p_setup.go | 22 +- network/p2p/p2p_test.go | 11 +- network/peers/connections/conn_gater.go | 3 +- network/peers/connections/handshaker.go | 51 +- network/peers/connections/helpers_test.go | 18 +- network/records/entries.go | 3 +- network/topics/controller.go | 6 - network/topics/controller_test.go | 145 +- network/topics/msg_id.go | 12 - network/topics/msg_validator_test.go | 9 +- network/topics/pubsub.go | 5 +- network/topics/scoring.go | 21 - networkconfig/config.go | 43 +- networkconfig/holesky-e2e.go | 4 +- networkconfig/holesky-stage.go | 4 +- networkconfig/holesky.go | 4 +- networkconfig/local-testnet.go | 3 +- networkconfig/mainnet.go | 4 +- networkconfig/test-network.go | 3 +- operator/duties/attester.go | 29 +- operator/duties/attester_genesis_test.go | 1003 ------- operator/duties/attester_test.go | 39 +- operator/duties/committee.go | 8 - operator/duties/committee_test.go | 209 +- operator/duties/proposer.go | 24 +- operator/duties/proposer_genesis_test.go | 456 --- operator/duties/proposer_test.go | 83 +- operator/duties/scheduler.go | 35 +- operator/duties/scheduler_mock.go | 25 - operator/duties/scheduler_test.go | 85 +- operator/duties/sync_committee.go | 30 +- .../duties/sync_committee_genesis_test.go | 705 ----- operator/duties/sync_committee_test.go | 24 +- operator/duties/validatorregistration.go | 28 +- operator/duties/voluntary_exit.go | 27 +- .../duties/voluntary_exit_genesis_test.go | 148 - operator/duties/voluntary_exit_test.go | 2 +- operator/node.go | 3 +- operator/validator/mocks/controller.go | 31 +- operator/validator/router_test.go | 2 +- operator/validator/task_executor.go | 15 +- operator/validators/validator_container.go | 81 - operator/validators/validators_map.go | 14 +- protocol/genesis/blockchain/beacon/beacon.go | 55 - .../genesis/blockchain/beacon/mock_client.go | 693 ----- .../blockchain/eth1/registry_storage.go | 7 - protocol/genesis/message/consensus.go | 19 - protocol/genesis/message/consensus_test.go | 40 - protocol/genesis/message/encoding.go | 9 - protocol/genesis/message/msg.go | 68 - protocol/genesis/qbft/config.go | 81 - .../genesis/qbft/controller/controller.go | 250 -- .../qbft/controller/controller_test.go | 91 - protocol/genesis/qbft/controller/decided.go | 117 - .../qbft/controller/highest_instance.go | 69 - protocol/genesis/qbft/controller/timer.go | 32 - protocol/genesis/qbft/controller/types.go | 98 - .../genesis/qbft/controller/types_test.go | 163 -- protocol/genesis/qbft/instance/commit.go | 176 -- protocol/genesis/qbft/instance/compact.go | 99 - .../genesis/qbft/instance/compact_test.go | 181 -- protocol/genesis/qbft/instance/instance.go | 259 -- .../genesis/qbft/instance/instance_test.go | 98 - .../genesis/qbft/instance/marshalutils.go | 47 - protocol/genesis/qbft/instance/metrics.go | 76 - protocol/genesis/qbft/instance/prepare.go | 199 -- protocol/genesis/qbft/instance/proposal.go | 295 -- .../genesis/qbft/instance/round_change.go | 404 --- protocol/genesis/qbft/instance/timeout.go | 48 - .../genesis/qbft/roundtimer/mocks/timer.go | 105 - .../genesis/qbft/roundtimer/testing_timer.go | 23 - protocol/genesis/qbft/roundtimer/timer.go | 192 -- .../genesis/qbft/roundtimer/timer_test.go | 168 -- .../genesis/qbft/spectest/controller_type.go | 190 -- .../genesis/qbft/spectest/create_msg_type.go | 100 - .../qbft/spectest/msg_processing_type.go | 105 - protocol/genesis/qbft/spectest/msg_type.go | 43 - .../qbft/spectest/qbft_mapping_test.go | 120 - protocol/genesis/qbft/spectest/tests.json | 1 - .../genesis/qbft/spectest/timeout_type.go | 62 - protocol/genesis/qbft/storage/ibft_store.go | 54 - protocol/genesis/qbft/testing/storage.go | 43 - protocol/genesis/qbft/testing/utils.go | 93 - protocol/genesis/qbft/testing_utils.go | 109 - protocol/genesis/queue/exec_queue.go | 17 - .../ssv/genesisqueue/message_prioritizer.go | 73 - .../genesisqueue/message_prioritizer_test.go | 389 --- protocol/genesis/ssv/genesisqueue/messages.go | 251 -- protocol/genesis/ssv/genesisqueue/metrics.go | 32 - protocol/genesis/ssv/genesisqueue/queue.go | 203 -- .../genesis/ssv/genesisqueue/queue_test.go | 504 ---- protocol/genesis/ssv/runner/aggregator.go | 340 --- protocol/genesis/ssv/runner/attester.go | 299 -- protocol/genesis/ssv/runner/compact.go | 18 - protocol/genesis/ssv/runner/duty_runners.go | 12 - .../genesis/ssv/runner/metrics/metrics.go | 231 -- .../ssv/runner/pre_consensus_justification.go | 163 -- protocol/genesis/ssv/runner/proposer.go | 499 ---- protocol/genesis/ssv/runner/runner.go | 307 -- .../genesis/ssv/runner/runner_signatures.go | 123 - protocol/genesis/ssv/runner/runner_state.go | 65 - .../ssv/runner/runner_state_helpers.go | 25 - .../genesis/ssv/runner/runner_validations.go | 140 - protocol/genesis/ssv/runner/sync_committee.go | 274 -- .../ssv/runner/sync_committee_aggregator.go | 444 --- protocol/genesis/ssv/runner/timer.go | 20 - .../ssv/runner/validator_registration.go | 228 -- protocol/genesis/ssv/runner/voluntary_exit.go | 240 -- .../ssv/spectest/msg_processing_type.go | 198 -- .../ssv/spectest/multi_msg_processing_type.go | 44 - .../multi_start_new_runner_duty_type.go | 167 -- .../genesis/ssv/spectest/ssv_mapping_test.go | 378 --- .../sync_committee_aggregator_proof_type.go | 85 - protocol/genesis/ssv/testing/runner.go | 239 -- protocol/genesis/ssv/testing/ssv_msgs.go | 882 ------ protocol/genesis/ssv/testing/validator.go | 40 - .../genesis/ssv/validator/duty_executer.go | 29 - protocol/genesis/ssv/validator/events.go | 33 - protocol/genesis/ssv/validator/metrics.go | 45 - .../ssv/validator/msgqueue_consumer.go | 211 -- .../ssv/validator/msgqueue_consumer_test.go | 39 - protocol/genesis/ssv/validator/opts.go | 53 - protocol/genesis/ssv/validator/startup.go | 74 - protocol/genesis/ssv/validator/timer.go | 77 - protocol/genesis/ssv/validator/validator.go | 176 -- protocol/genesis/testing/test_utils.go | 254 -- protocol/genesis/types/bls.go | 33 - protocol/genesis/types/crypto.go | 100 - protocol/genesis/types/domain.go | 25 - protocol/genesis/types/messages.go | 68 - protocol/genesis/types/share.go | 58 - protocol/genesis/types/share_test.go | 111 - .../types/signature_benchmark_linux_test.go | 70 - .../genesis/types/signature_benchmark_test.go | 180 -- protocol/genesis/types/ssvshare.go | 122 - protocol/genesis/types/ssvshare_test.go | 188 -- protocol/v2/ssv/runner/committee.go | 2 +- protocol/v2/ssv/spectest/ssv_mapping_test.go | 4 +- protocol/v2/ssv/testing/runner.go | 20 +- protocol/v2/ssv/validator/opts.go | 15 - registry/storage/shares.go | 3 +- registry/storage/shares_test.go | 4 +- .../spec-alignment/genesis_differ.config.yaml | 42 - scripts/spec-alignment/genesis_differ.sh | 11 - utils/boot_node/node.go | 35 +- 193 files changed, 386 insertions(+), 23712 deletions(-) delete mode 100644 .github/workflows/genesis-spec-alignment.yml delete mode 100644 beacon/goclient/genesisgoclient/adapter.go delete mode 100644 ekm/genesis_adapter.go delete mode 100644 ibft/genesisstorage/store.go delete mode 100644 ibft/genesisstorage/store_test.go delete mode 100644 ibft/genesisstorage/stores.go delete mode 100644 ibft/genesisstorage/stores_test.go delete mode 100644 message/validation/fork.go delete mode 100644 message/validation/genesis/consensus_state.go delete mode 100644 message/validation/genesis/consensus_validation.go delete mode 100644 message/validation/genesis/consensus_validation_test.go delete mode 100644 message/validation/genesis/errors.go delete mode 100644 message/validation/genesis/message_counts.go delete mode 100644 message/validation/genesis/partial_validation.go delete mode 100644 message/validation/genesis/rsa.go delete mode 100644 message/validation/genesis/signer_state.go delete mode 100644 message/validation/genesis/utils_test.go delete mode 100644 message/validation/genesis/validation.go delete mode 100644 message/validation/genesis/validation_test.go delete mode 100644 monitoring/metricsreporter/genesis.go delete mode 100644 network/discovery/forking_dv5_listener.go delete mode 100644 network/discovery/forking_dv5_listener_test.go delete mode 100644 network/discovery/iterator_test.go delete mode 100644 network/p2p/p2p_genesis.go delete mode 100644 operator/duties/attester_genesis_test.go delete mode 100644 operator/duties/proposer_genesis_test.go delete mode 100644 operator/duties/sync_committee_genesis_test.go delete mode 100644 operator/duties/voluntary_exit_genesis_test.go delete mode 100644 operator/validators/validator_container.go delete mode 100644 protocol/genesis/blockchain/beacon/beacon.go delete mode 100644 protocol/genesis/blockchain/beacon/mock_client.go delete mode 100644 protocol/genesis/blockchain/eth1/registry_storage.go delete mode 100644 protocol/genesis/message/consensus.go delete mode 100644 protocol/genesis/message/consensus_test.go delete mode 100644 protocol/genesis/message/encoding.go delete mode 100644 protocol/genesis/message/msg.go delete mode 100644 protocol/genesis/qbft/config.go delete mode 100644 protocol/genesis/qbft/controller/controller.go delete mode 100644 protocol/genesis/qbft/controller/controller_test.go delete mode 100644 protocol/genesis/qbft/controller/decided.go delete mode 100644 protocol/genesis/qbft/controller/highest_instance.go delete mode 100644 protocol/genesis/qbft/controller/timer.go delete mode 100644 protocol/genesis/qbft/controller/types.go delete mode 100644 protocol/genesis/qbft/controller/types_test.go delete mode 100644 protocol/genesis/qbft/instance/commit.go delete mode 100644 protocol/genesis/qbft/instance/compact.go delete mode 100644 protocol/genesis/qbft/instance/compact_test.go delete mode 100644 protocol/genesis/qbft/instance/instance.go delete mode 100644 protocol/genesis/qbft/instance/instance_test.go delete mode 100644 protocol/genesis/qbft/instance/marshalutils.go delete mode 100644 protocol/genesis/qbft/instance/metrics.go delete mode 100644 protocol/genesis/qbft/instance/prepare.go delete mode 100644 protocol/genesis/qbft/instance/proposal.go delete mode 100644 protocol/genesis/qbft/instance/round_change.go delete mode 100644 protocol/genesis/qbft/instance/timeout.go delete mode 100644 protocol/genesis/qbft/roundtimer/mocks/timer.go delete mode 100644 protocol/genesis/qbft/roundtimer/testing_timer.go delete mode 100644 protocol/genesis/qbft/roundtimer/timer.go delete mode 100644 protocol/genesis/qbft/roundtimer/timer_test.go delete mode 100644 protocol/genesis/qbft/spectest/controller_type.go delete mode 100644 protocol/genesis/qbft/spectest/create_msg_type.go delete mode 100644 protocol/genesis/qbft/spectest/msg_processing_type.go delete mode 100644 protocol/genesis/qbft/spectest/msg_type.go delete mode 100644 protocol/genesis/qbft/spectest/qbft_mapping_test.go delete mode 100644 protocol/genesis/qbft/spectest/tests.json delete mode 100644 protocol/genesis/qbft/spectest/timeout_type.go delete mode 100644 protocol/genesis/qbft/storage/ibft_store.go delete mode 100644 protocol/genesis/qbft/testing/storage.go delete mode 100644 protocol/genesis/qbft/testing/utils.go delete mode 100644 protocol/genesis/qbft/testing_utils.go delete mode 100644 protocol/genesis/queue/exec_queue.go delete mode 100644 protocol/genesis/ssv/genesisqueue/message_prioritizer.go delete mode 100644 protocol/genesis/ssv/genesisqueue/message_prioritizer_test.go delete mode 100644 protocol/genesis/ssv/genesisqueue/messages.go delete mode 100644 protocol/genesis/ssv/genesisqueue/metrics.go delete mode 100644 protocol/genesis/ssv/genesisqueue/queue.go delete mode 100644 protocol/genesis/ssv/genesisqueue/queue_test.go delete mode 100644 protocol/genesis/ssv/runner/aggregator.go delete mode 100644 protocol/genesis/ssv/runner/attester.go delete mode 100644 protocol/genesis/ssv/runner/compact.go delete mode 100644 protocol/genesis/ssv/runner/duty_runners.go delete mode 100644 protocol/genesis/ssv/runner/metrics/metrics.go delete mode 100644 protocol/genesis/ssv/runner/pre_consensus_justification.go delete mode 100644 protocol/genesis/ssv/runner/proposer.go delete mode 100644 protocol/genesis/ssv/runner/runner.go delete mode 100644 protocol/genesis/ssv/runner/runner_signatures.go delete mode 100644 protocol/genesis/ssv/runner/runner_state.go delete mode 100644 protocol/genesis/ssv/runner/runner_state_helpers.go delete mode 100644 protocol/genesis/ssv/runner/runner_validations.go delete mode 100644 protocol/genesis/ssv/runner/sync_committee.go delete mode 100644 protocol/genesis/ssv/runner/sync_committee_aggregator.go delete mode 100644 protocol/genesis/ssv/runner/timer.go delete mode 100644 protocol/genesis/ssv/runner/validator_registration.go delete mode 100644 protocol/genesis/ssv/runner/voluntary_exit.go delete mode 100644 protocol/genesis/ssv/spectest/msg_processing_type.go delete mode 100644 protocol/genesis/ssv/spectest/multi_msg_processing_type.go delete mode 100644 protocol/genesis/ssv/spectest/multi_start_new_runner_duty_type.go delete mode 100644 protocol/genesis/ssv/spectest/ssv_mapping_test.go delete mode 100644 protocol/genesis/ssv/spectest/sync_committee_aggregator_proof_type.go delete mode 100644 protocol/genesis/ssv/testing/runner.go delete mode 100644 protocol/genesis/ssv/testing/ssv_msgs.go delete mode 100644 protocol/genesis/ssv/testing/validator.go delete mode 100644 protocol/genesis/ssv/validator/duty_executer.go delete mode 100644 protocol/genesis/ssv/validator/events.go delete mode 100644 protocol/genesis/ssv/validator/metrics.go delete mode 100644 protocol/genesis/ssv/validator/msgqueue_consumer.go delete mode 100644 protocol/genesis/ssv/validator/msgqueue_consumer_test.go delete mode 100644 protocol/genesis/ssv/validator/opts.go delete mode 100644 protocol/genesis/ssv/validator/startup.go delete mode 100644 protocol/genesis/ssv/validator/timer.go delete mode 100644 protocol/genesis/ssv/validator/validator.go delete mode 100644 protocol/genesis/testing/test_utils.go delete mode 100644 protocol/genesis/types/bls.go delete mode 100644 protocol/genesis/types/crypto.go delete mode 100644 protocol/genesis/types/domain.go delete mode 100644 protocol/genesis/types/messages.go delete mode 100644 protocol/genesis/types/share.go delete mode 100644 protocol/genesis/types/share_test.go delete mode 100644 protocol/genesis/types/signature_benchmark_linux_test.go delete mode 100644 protocol/genesis/types/signature_benchmark_test.go delete mode 100644 protocol/genesis/types/ssvshare.go delete mode 100644 protocol/genesis/types/ssvshare_test.go delete mode 100644 scripts/spec-alignment/genesis_differ.config.yaml delete mode 100755 scripts/spec-alignment/genesis_differ.sh diff --git a/.github/workflows/genesis-spec-alignment.yml b/.github/workflows/genesis-spec-alignment.yml deleted file mode 100644 index 406ccbf732..0000000000 --- a/.github/workflows/genesis-spec-alignment.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: Genesis Spec Alignment - -on: - push: - branches: - - "**" - -jobs: - align: - runs-on: ubuntu-latest - steps: - - name: Check out code - uses: actions/checkout@v3 - - - name: Set up Go - uses: actions/setup-go@v4 - with: - go-version: "1.22.x" - - - name: Tidy up dependencies - run: go mod tidy - - - name: Install Differ - run: cd ./scripts/differ && go install . - - - name: Run Differ - run: cd ./scripts/spec-alignment && ./genesis_differ.sh - - - name: Upload genesis_output.diff - if: failure() - uses: actions/upload-artifact@v3 - with: - name: genesis_output.diff - path: ./scripts/spec-alignment/genesis_output.diff diff --git a/.github/workflows/spec-test-raceless.yml b/.github/workflows/spec-test-raceless.yml index c320f450d7..b3fa2344f2 100644 --- a/.github/workflows/spec-test-raceless.yml +++ b/.github/workflows/spec-test-raceless.yml @@ -22,11 +22,5 @@ jobs: with: go-version: "1.22.x" - - name: Run pre-fork spec tests - run: make pre-fork-spec-test-raceless - - - name: Run post-fork spec tests - run: make post-fork-spec-test-raceless - - name: Run all spec tests run: make all-spec-test-raceless diff --git a/Makefile b/Makefile index 6696eb355f..f8927037e9 100644 --- a/Makefile +++ b/Makefile @@ -62,23 +62,11 @@ spec-test: @echo "Running spec tests" @go test -tags blst_enabled -timeout 90m ${COV_CMD} -race -count=1 -p 1 -v `go list ./... | grep spectest` - .PHONY: all-spec-test-raceless all-spec-test-raceless: @echo "Running spec tests" @go test -tags blst_enabled -timeout 90m ${COV_CMD} -p 1 -v ./protocol/... - -.PHONY: pre-fork-spec-test-raceless -pre-fork-spec-test-raceless: - @echo "Running spec tests" - @go test -tags blst_enabled -timeout 90m ${COV_CMD} -p 1 -v ./protocol/genesis/... - -.PHONY: post-fork-spec-test-raceless -post-fork-spec-test-raceless: - @echo "Running spec tests" - @go test -tags blst_enabled -timeout 90m ${COV_CMD} -p 1 -v ./protocol/v2/... - .PHONY: spec-test-raceless spec-test-raceless: @echo "Running spec tests without race flag" diff --git a/beacon/goclient/genesisgoclient/adapter.go b/beacon/goclient/genesisgoclient/adapter.go deleted file mode 100644 index 0e4cf3f16a..0000000000 --- a/beacon/goclient/genesisgoclient/adapter.go +++ /dev/null @@ -1,41 +0,0 @@ -package genesisgoclient - -import ( - "github.com/attestantio/go-eth2-client/spec" - "github.com/attestantio/go-eth2-client/spec/altair" - "github.com/attestantio/go-eth2-client/spec/phase0" - ssz "github.com/ferranbt/fastssz" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - "github.com/ssvlabs/ssv/beacon/goclient" - genesisbeacon "github.com/ssvlabs/ssv/protocol/genesis/blockchain/beacon" -) - -type adapter struct { - *goclient.GoClient -} - -func NewAdapter(bn *goclient.GoClient) genesisbeacon.BeaconNode { - return &adapter{ - GoClient: bn, - } -} - -func (a *adapter) GetAttestationData(slot phase0.Slot, committeeIndex phase0.CommitteeIndex) (ssz.Marshaler, spec.DataVersion, error) { - return a.GoClient.GetAttestationData(slot, committeeIndex) -} - -func (a *adapter) GetBeaconNetwork() genesisspectypes.BeaconNetwork { - return genesisspectypes.BeaconNetwork(a.GoClient.GetBeaconNetwork()) -} - -func (a *adapter) GetBlindedBeaconBlock(slot phase0.Slot, graffiti []byte, sig []byte) (ssz.Marshaler, spec.DataVersion, error) { - return a.GoClient.GetBeaconBlock(slot, graffiti, sig) -} - -func (a *adapter) SubmitAttestation(attestation *phase0.Attestation) error { - return a.GoClient.SubmitAttestations([]*phase0.Attestation{attestation}) -} - -func (a *adapter) SubmitSyncMessage(message *altair.SyncCommitteeMessage) error { - return a.GoClient.SubmitSyncMessages([]*altair.SyncCommitteeMessage{message}) -} diff --git a/cli/operator/node.go b/cli/operator/node.go index ce0c75174c..39513ce0fe 100644 --- a/cli/operator/node.go +++ b/cli/operator/node.go @@ -17,14 +17,12 @@ import ( "github.com/ilyakaznacheev/cleanenv" "github.com/pkg/errors" "github.com/spf13/cobra" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" spectypes "github.com/ssvlabs/ssv-spec/types" "go.uber.org/zap" "github.com/ssvlabs/ssv/api/handlers" apiserver "github.com/ssvlabs/ssv/api/server" "github.com/ssvlabs/ssv/beacon/goclient" - "github.com/ssvlabs/ssv/beacon/goclient/genesisgoclient" global_config "github.com/ssvlabs/ssv/cli/config" "github.com/ssvlabs/ssv/ekm" "github.com/ssvlabs/ssv/eth/eventhandler" @@ -35,14 +33,12 @@ import ( exporterapi "github.com/ssvlabs/ssv/exporter/api" "github.com/ssvlabs/ssv/exporter/api/decided" "github.com/ssvlabs/ssv/exporter/convert" - genesisibftstorage "github.com/ssvlabs/ssv/ibft/genesisstorage" ibftstorage "github.com/ssvlabs/ssv/ibft/storage" ssv_identity "github.com/ssvlabs/ssv/identity" "github.com/ssvlabs/ssv/logging" "github.com/ssvlabs/ssv/logging/fields" "github.com/ssvlabs/ssv/message/signatureverifier" "github.com/ssvlabs/ssv/message/validation" - genesisvalidation "github.com/ssvlabs/ssv/message/validation/genesis" "github.com/ssvlabs/ssv/migrations" "github.com/ssvlabs/ssv/monitoring/metrics" "github.com/ssvlabs/ssv/monitoring/metricsreporter" @@ -59,7 +55,6 @@ import ( operatorstorage "github.com/ssvlabs/ssv/operator/storage" "github.com/ssvlabs/ssv/operator/validator" "github.com/ssvlabs/ssv/operator/validators" - genesisssvtypes "github.com/ssvlabs/ssv/protocol/genesis/types" beaconprotocol "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon" "github.com/ssvlabs/ssv/protocol/v2/types" registrystorage "github.com/ssvlabs/ssv/registry/storage" @@ -169,7 +164,7 @@ var StartNodeCmd = &cobra.Command{ usingLocalEvents := len(cfg.LocalEventsPath) != 0 - if err := validateConfig(nodeStorage, networkConfig.AlanForkNetworkName(), usingLocalEvents); err != nil { + if err := validateConfig(nodeStorage, networkConfig.NetworkName(), usingLocalEvents); err != nil { logger.Fatal("failed to validate config", zap.Error(err)) } @@ -228,9 +223,7 @@ var StartNodeCmd = &cobra.Command{ validatorStore := nodeStorage.ValidatorStore() - var messageValidator validation.MessageValidator - - alanMsgValidator := validation.New( + messageValidator := validation.New( networkConfig, validatorStore, dutyStore, @@ -239,27 +232,11 @@ var StartNodeCmd = &cobra.Command{ validation.WithMetrics(metricsReporter), ) - if networkConfig.PastAlanFork() { - messageValidator = alanMsgValidator - } else { - messageValidator = &validation.ForkingMessageValidation{ - NetworkConfig: networkConfig, - Alan: alanMsgValidator, - Genesis: genesisvalidation.New( - networkConfig, - genesisvalidation.WithNodeStorage(nodeStorage), - genesisvalidation.WithLogger(logger), - genesisvalidation.WithMetrics(metricsReporter), - genesisvalidation.WithDutyStore(dutyStore), - ), - } - } - cfg.P2pNetworkConfig.Metrics = metricsReporter cfg.P2pNetworkConfig.MessageValidator = messageValidator cfg.SSVOptions.ValidatorOptions.MessageValidator = messageValidator - p2pNetwork, genesisP2pNetwork := setupP2P(logger, db, metricsReporter) + p2pNetwork := setupP2P(logger, db, metricsReporter) cfg.SSVOptions.Context = cmd.Context() cfg.SSVOptions.DB = db @@ -268,12 +245,10 @@ var StartNodeCmd = &cobra.Command{ cfg.SSVOptions.Network = networkConfig cfg.SSVOptions.P2PNetwork = p2pNetwork cfg.SSVOptions.ValidatorOptions.NetworkConfig = networkConfig - cfg.SSVOptions.ValidatorOptions.BeaconNetwork = networkConfig.Beacon.GetNetwork() cfg.SSVOptions.ValidatorOptions.Context = cmd.Context() cfg.SSVOptions.ValidatorOptions.DB = db cfg.SSVOptions.ValidatorOptions.Network = p2pNetwork cfg.SSVOptions.ValidatorOptions.Beacon = consensusClient - cfg.SSVOptions.ValidatorOptions.GenesisBeacon = genesisgoclient.NewAdapter(consensusClient) cfg.SSVOptions.ValidatorOptions.BeaconSigner = keyManager cfg.SSVOptions.ValidatorOptions.ValidatorsMap = validatorsMap cfg.SSVOptions.ValidatorOptions.NetworkConfig = networkConfig @@ -283,8 +258,6 @@ var StartNodeCmd = &cobra.Command{ cfg.SSVOptions.ValidatorOptions.RecipientsStorage = nodeStorage cfg.SSVOptions.ValidatorOptions.GasLimit = cfg.ConsensusClient.GasLimit - cfg.SSVOptions.ValidatorOptions.GenesisControllerOptions.KeyManager = &ekm.GenesisKeyManagerAdapter{KeyManager: keyManager} - if cfg.WsAPIPort != 0 { ws := exporterapi.NewWsServer(cmd.Context(), nil, http.NewServeMux(), cfg.WithPing) cfg.SSVOptions.WS = ws @@ -311,22 +284,6 @@ var StartNodeCmd = &cobra.Command{ storageMap.Add(storageRole, ibftstorage.New(cfg.SSVOptions.ValidatorOptions.DB, storageRole.String())) } - genesisStorageRoles := []genesisspectypes.BeaconRole{ - genesisspectypes.BNRoleAttester, - genesisspectypes.BNRoleAggregator, - genesisspectypes.BNRoleProposer, - genesisspectypes.BNRoleSyncCommittee, - genesisspectypes.BNRoleSyncCommitteeContribution, - genesisspectypes.BNRoleValidatorRegistration, - genesisspectypes.BNRoleVoluntaryExit, - } - - genesisStorageMap := genesisibftstorage.NewStores() - - for _, storageRole := range genesisStorageRoles { - genesisStorageMap.Add(storageRole, genesisibftstorage.New(cfg.SSVOptions.ValidatorOptions.DB, "genesis_"+storageRole.String())) - } - cfg.SSVOptions.ValidatorOptions.StorageMap = storageMap cfg.SSVOptions.ValidatorOptions.Metrics = metricsReporter cfg.SSVOptions.ValidatorOptions.Graffiti = []byte(cfg.Graffiti) @@ -334,9 +291,6 @@ var StartNodeCmd = &cobra.Command{ cfg.SSVOptions.ValidatorOptions.OperatorSigner = types.NewSsvOperatorSigner(operatorPrivKey, operatorDataStore.GetOperatorID) cfg.SSVOptions.Metrics = metricsReporter - cfg.SSVOptions.ValidatorOptions.GenesisControllerOptions.StorageMap = genesisStorageMap - cfg.SSVOptions.ValidatorOptions.GenesisControllerOptions.Network = &genesisP2pNetwork - validatorCtrl := validator.NewController(logger, cfg.SSVOptions.ValidatorOptions) cfg.SSVOptions.ValidatorController = validatorCtrl cfg.SSVOptions.ValidatorStore = validatorStore @@ -410,7 +364,7 @@ var StartNodeCmd = &cobra.Command{ Shares: nodeStorage.Shares(), }, &handlers.Exporter{ - DomainType: networkConfig.AlanDomainType, + DomainType: networkConfig.DomainType, QBFTStores: storageMap, }, ) @@ -606,41 +560,31 @@ func setupSSVNetwork(logger *zap.Logger) (networkconfig.NetworkConfig, error) { if !strings.HasPrefix(cfg.SSVOptions.CustomDomainType, "0x") { return networkconfig.NetworkConfig{}, errors.New("custom domain type must be a hex string") } - byts, err := hex.DecodeString(cfg.SSVOptions.CustomDomainType[2:]) + domainBytes, err := hex.DecodeString(cfg.SSVOptions.CustomDomainType[2:]) if err != nil { return networkconfig.NetworkConfig{}, errors.Wrap(err, "failed to decode custom domain type") } - if len(byts) != 4 { + if len(domainBytes) != 4 { return networkconfig.NetworkConfig{}, errors.New("custom domain type must be 4 bytes") } - // Assign domain type as-is for Genesis. - networkConfig.GenesisDomainType = spectypes.DomainType(byts) - - // Assign domain type incremented by 1 for Alan. - alanDomainType := binary.BigEndian.Uint32(byts) + 1 - binary.BigEndian.PutUint32(networkConfig.AlanDomainType[:], alanDomainType) + // https://github.com/ssvlabs/ssv/pull/1808 incremented the post-fork domain type by 1, so we have to maintain the compatibility. + postForkDomain := binary.BigEndian.Uint32(domainBytes) + 1 + binary.BigEndian.PutUint32(networkConfig.DomainType[:], postForkDomain) logger.Info("running with custom domain type", - zap.Stringer("genesis", format.DomainType(networkConfig.GenesisDomainType)), - zap.Stringer("alan", format.DomainType(networkConfig.AlanDomainType)), + fields.Domain(networkConfig.DomainType), ) } - genesisssvtypes.SetDefaultDomain(genesisspectypes.DomainType(networkConfig.GenesisDomainType)) - nodeType := "light" if cfg.SSVOptions.ValidatorOptions.FullNode { nodeType = "full" } - if !networkConfig.PastAlanFork() { - logger = logger.With(zap.Stringer("alan_domain", format.DomainType(networkConfig.AlanDomainType))) - } - logger.Info("setting ssv network", fields.Network(networkConfig.Name), - fields.Domain(networkConfig.DomainType()), + fields.Domain(networkConfig.DomainType), zap.String("nodeType", nodeType), zap.Any("beaconNetwork", networkConfig.Beacon.GetNetwork().BeaconNetwork), zap.Uint64("genesisEpoch", uint64(networkConfig.GenesisEpoch)), @@ -650,7 +594,7 @@ func setupSSVNetwork(logger *zap.Logger) (networkconfig.NetworkConfig, error) { return networkConfig, nil } -func setupP2P(logger *zap.Logger, db basedb.Database, mr metricsreporter.MetricsReporter) (network.P2PNetwork, p2pv1.GenesisP2P) { +func setupP2P(logger *zap.Logger, db basedb.Database, mr metricsreporter.MetricsReporter) network.P2PNetwork { istore := ssv_identity.NewIdentityStore(db) netPrivKey, err := istore.SetupNetworkKey(logger, cfg.NetworkPrivateKey) if err != nil { @@ -662,7 +606,7 @@ func setupP2P(logger *zap.Logger, db basedb.Database, mr metricsreporter.Metrics if err != nil { logger.Fatal("failed to setup p2p network", zap.Error(err)) } - return n, p2pv1.GenesisP2P{Network: n} + return n } func setupConsensusClient( diff --git a/cli/operator/node_test.go b/cli/operator/node_test.go index 297a6f61fd..3411f68861 100644 --- a/cli/operator/node_test.go +++ b/cli/operator/node_test.go @@ -22,7 +22,7 @@ func Test_verifyConfig(t *testing.T) { nodeStorage, err := operatorstorage.NewNodeStorage(logger, db) require.NoError(t, err) - testNetworkName := networkconfig.TestNetwork.AlanForkNetworkName() + testNetworkName := networkconfig.TestNetwork.NetworkName() t.Run("no config in DB", func(t *testing.T) { c := &operatorstorage.ConfigLock{ diff --git a/ekm/eth_key_manager_signer.go b/ekm/eth_key_manager_signer.go index f977abca4a..763e9e85e4 100644 --- a/ekm/eth_key_manager_signer.go +++ b/ekm/eth_key_manager_signer.go @@ -64,9 +64,6 @@ type KeyManager interface { AddShare(shareKey *bls.SecretKey) error // RemoveShare removes a share key RemoveShare(pubKey string) error - - // SignRoot TODO: (Alan) genesis support - should be removed after alan fork - SignRoot(data spectypes.Root, sigType spectypes.SignatureType, pk []byte) (spectypes.Signature, error) } // NewETHKeyManagerSigner returns a new instance of ethKeyManagerSigner @@ -105,7 +102,7 @@ func NewETHKeyManagerSigner(logger *zap.Logger, db basedb.Database, network netw walletLock: &sync.RWMutex{}, signer: beaconSigner, storage: signerStore, - domain: network.DomainType(), + domain: network.DomainType, slashingProtector: slashingProtector, }, nil } @@ -256,28 +253,6 @@ func (km *ethKeyManagerSigner) IsBeaconBlockSlashable(pk []byte, slot phase0.Slo return nil } -func (km *ethKeyManagerSigner) SignRoot(data spectypes.Root, sigType spectypes.SignatureType, pk []byte) (spectypes.Signature, error) { - km.walletLock.RLock() - defer km.walletLock.RUnlock() - - account, err := km.wallet.AccountByPublicKey(hex.EncodeToString(pk)) - if err != nil { - return nil, errors.Wrap(err, "could not get signing account") - } - - root, err := spectypes.ComputeSigningRoot(data, spectypes.ComputeSignatureDomain(km.domain, sigType)) - if err != nil { - return nil, errors.Wrap(err, "could not compute signing root") - } - - sig, err := account.ValidationKeySign(root[:]) - if err != nil { - return nil, errors.Wrap(err, "could not sign message") - } - - return sig, nil -} - func (km *ethKeyManagerSigner) AddShare(shareKey *bls.SecretKey) error { km.walletLock.Lock() defer km.walletLock.Unlock() diff --git a/ekm/genesis_adapter.go b/ekm/genesis_adapter.go deleted file mode 100644 index f0286d2eff..0000000000 --- a/ekm/genesis_adapter.go +++ /dev/null @@ -1,77 +0,0 @@ -package ekm - -import ( - "github.com/attestantio/go-eth2-client/spec/phase0" - ssz "github.com/ferranbt/fastssz" - "github.com/herumi/bls-eth-go-binary/bls" - "github.com/pkg/errors" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - spectypes "github.com/ssvlabs/ssv-spec/types" -) - -type GenesisKeyManagerAdapter struct { - KeyManager KeyManager -} - -func (k *GenesisKeyManagerAdapter) SignBeaconObject(obj ssz.HashRoot, domain phase0.Domain, pk []byte, domainType phase0.DomainType) (genesisspectypes.Signature, [32]byte, error) { - // Convert genesisspectypes to spectypes before passing on to KeyManager. - switch domainType { - case genesisspectypes.DomainAttester: - case genesisspectypes.DomainProposer: - case genesisspectypes.DomainVoluntaryExit: - case genesisspectypes.DomainAggregateAndProof: - case genesisspectypes.DomainSelectionProof: - data, ok := obj.(genesisspectypes.SSZUint64) - if !ok { - return nil, [32]byte{}, errors.New("could not cast obj to SSZUint64") - } - obj = spectypes.SSZUint64(data) - case genesisspectypes.DomainRandao: - data, ok := obj.(genesisspectypes.SSZUint64) - if !ok { - return nil, [32]byte{}, errors.New("could not cast obj to SSZUint64") - } - obj = spectypes.SSZUint64(data) - case genesisspectypes.DomainSyncCommittee: - data, ok := obj.(genesisspectypes.SSZBytes) - if !ok { - return nil, [32]byte{}, errors.New("could not cast obj to SSZBytes") - } - obj = spectypes.SSZBytes(data) - case genesisspectypes.DomainSyncCommitteeSelectionProof: - case genesisspectypes.DomainContributionAndProof: - case genesisspectypes.DomainApplicationBuilder: - default: - return nil, [32]byte{}, errors.New("domain unknown") - } - - signature, root, err := k.KeyManager.SignBeaconObject(obj, domain, pk, domainType) - if err != nil { - return nil, [32]byte{}, err - } - return genesisspectypes.Signature(signature), root, nil -} - -func (k *GenesisKeyManagerAdapter) IsAttestationSlashable(pk []byte, data *phase0.AttestationData) error { - return k.KeyManager.IsAttestationSlashable(pk, data) -} - -func (k *GenesisKeyManagerAdapter) IsBeaconBlockSlashable(pk []byte, slot phase0.Slot) error { - return k.KeyManager.IsBeaconBlockSlashable(pk, slot) -} - -func (k *GenesisKeyManagerAdapter) SignRoot(data genesisspectypes.Root, sigType genesisspectypes.SignatureType, pk []byte) (genesisspectypes.Signature, error) { - signature, err := k.KeyManager.SignRoot(data, spectypes.SignatureType(sigType), pk) - if err != nil { - return nil, err - } - return genesisspectypes.Signature(signature), nil -} - -func (k *GenesisKeyManagerAdapter) AddShare(shareKey *bls.SecretKey) error { - return k.KeyManager.AddShare(shareKey) -} - -func (k *GenesisKeyManagerAdapter) RemoveShare(pubKey string) error { - return k.KeyManager.RemoveShare(pubKey) -} diff --git a/ekm/signer_key_manager_test.go b/ekm/signer_key_manager_test.go index 89b7ed722e..e31d6796da 100644 --- a/ekm/signer_key_manager_test.go +++ b/ekm/signer_key_manager_test.go @@ -19,8 +19,6 @@ import ( "github.com/prysmaticlabs/go-bitfield" "github.com/ssvlabs/eth2-key-manager/core" "github.com/ssvlabs/eth2-key-manager/wallets/hd" - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" spectypes "github.com/ssvlabs/ssv-spec/types" "github.com/ssvlabs/ssv-spec/types/testingutils" "github.com/stretchr/testify/require" @@ -51,9 +49,8 @@ func testKeyManager(t *testing.T, network *networkconfig.NetworkConfig) KeyManag if network == nil { network = &networkconfig.NetworkConfig{ - Beacon: utils.SetupMockBeaconNetwork(t, nil), - GenesisDomainType: networkconfig.TestNetwork.DomainType(), - AlanDomainType: networkconfig.TestNetwork.DomainType(), + Beacon: utils.SetupMockBeaconNetwork(t, nil), + DomainType: networkconfig.TestNetwork.DomainType, } } @@ -688,76 +685,6 @@ func TestSlashing_Attestation(t *testing.T) { signAttestation(secretKeys[2], phase0.Root{7}, createAttestationData(6, 6), true, "HighestAttestationVote") } -func TestSignRoot(t *testing.T) { - require.NoError(t, bls.Init(bls.BLS12_381)) - - km := testKeyManager(t, nil) - - t.Run("pk 1", func(t *testing.T) { - pk := &bls.PublicKey{} - require.NoError(t, pk.Deserialize(_byteArray(pk1Str))) - - msg := genesisspecqbft.Message{ - MsgType: genesisspecqbft.CommitMsgType, - Height: genesisspecqbft.Height(3), - Round: genesisspecqbft.Round(2), - Identifier: []byte("identifier1"), - Root: [32]byte{1, 2, 3}, - } - - // sign - sig, err := km.(*ethKeyManagerSigner).SignRoot(&msg, spectypes.QBFTSignatureType, pk.Serialize()) - require.NoError(t, err) - - // verify - signed := &genesisspecqbft.SignedMessage{ - Signature: genesisspectypes.Signature(sig), - Signers: []spectypes.OperatorID{1}, - Message: msg, - } - - err = signed.Signature.VerifyByOperators( - signed, - genesisspectypes.DomainType(networkconfig.TestNetwork.DomainType()), - genesisspectypes.QBFTSignatureType, - []*genesisspectypes.Operator{{OperatorID: spectypes.OperatorID(1), PubKey: pk.Serialize()}}, - ) - require.NoError(t, err) - }) - - t.Run("pk 2", func(t *testing.T) { - pk := &bls.PublicKey{} - require.NoError(t, pk.Deserialize(_byteArray(pk2Str))) - - msg := genesisspecqbft.Message{ - MsgType: genesisspecqbft.CommitMsgType, - Height: genesisspecqbft.Height(1), - Round: genesisspecqbft.Round(3), - Identifier: []byte("identifier2"), - Root: [32]byte{4, 5, 6}, - } - - // sign - sig, err := km.(*ethKeyManagerSigner).SignRoot(&msg, spectypes.QBFTSignatureType, pk.Serialize()) - require.NoError(t, err) - - // verify - signed := &genesisspecqbft.SignedMessage{ - Signature: genesisspectypes.Signature(sig), - Signers: []spectypes.OperatorID{1}, - Message: msg, - } - - err = signed.Signature.VerifyByOperators( - signed, - genesisspectypes.DomainType(networkconfig.TestNetwork.DomainType()), - genesisspectypes.QBFTSignatureType, - []*genesisspectypes.Operator{{OperatorID: spectypes.OperatorID(1), PubKey: pk.Serialize()}}, - ) - require.NoError(t, err) - }) -} - func TestRemoveShare(t *testing.T) { require.NoError(t, bls.Init(bls.BLS12_381)) diff --git a/eth/eventhandler/handlers.go b/eth/eventhandler/handlers.go index 79ba93ed36..38682e12bf 100644 --- a/eth/eventhandler/handlers.go +++ b/eth/eventhandler/handlers.go @@ -332,7 +332,7 @@ func (eh *EventHandler) validatorAddedEventToShare( } } - validatorShare.DomainType = eh.networkConfig.DomainType() + validatorShare.DomainType = eh.networkConfig.DomainType validatorShare.Committee = shareMembers return &validatorShare, shareSecret, nil diff --git a/exporter/api/query_handlers_test.go b/exporter/api/query_handlers_test.go index a0c0d4ccb7..4c3bc5b004 100644 --- a/exporter/api/query_handlers_test.go +++ b/exporter/api/query_handlers_test.go @@ -109,7 +109,7 @@ func TestHandleDecidedQuery(t *testing.T) { networkConfig, err := networkconfig.GetNetworkConfigByName(networkconfig.HoleskyStage.Name) require.NoError(t, err) decided250Seq, err := protocoltesting.CreateMultipleStoredInstances(rsaKeys, specqbft.Height(0), specqbft.Height(250), func(height specqbft.Height) ([]spectypes.OperatorID, *specqbft.Message) { - id := convert.NewMsgID(networkConfig.DomainType(), pk.Serialize(), role) + id := convert.NewMsgID(networkConfig.DomainType, pk.Serialize(), role) return oids, &specqbft.Message{ MsgType: specqbft.CommitMsgType, Height: height, @@ -133,7 +133,7 @@ func TestHandleDecidedQuery(t *testing.T) { t.Run("valid range", func(t *testing.T) { nm := newParticipantsAPIMsg(pk.SerializeToHexStr(), spectypes.BNRoleAttester, 0, 250) - HandleParticipantsQuery(l, ibftStorage, nm, networkConfig.DomainType()) + HandleParticipantsQuery(l, ibftStorage, nm, networkConfig.DomainType) require.NotNil(t, nm.Msg.Data) msgs, ok := nm.Msg.Data.([]*ParticipantsAPI) @@ -143,7 +143,7 @@ func TestHandleDecidedQuery(t *testing.T) { t.Run("invalid range", func(t *testing.T) { nm := newParticipantsAPIMsg(pk.SerializeToHexStr(), spectypes.BNRoleAttester, 400, 404) - HandleParticipantsQuery(l, ibftStorage, nm, networkConfig.DomainType()) + HandleParticipantsQuery(l, ibftStorage, nm, networkConfig.DomainType) require.NotNil(t, nm.Msg.Data) data, ok := nm.Msg.Data.([]string) require.True(t, ok) @@ -152,7 +152,7 @@ func TestHandleDecidedQuery(t *testing.T) { t.Run("non-existing validator", func(t *testing.T) { nm := newParticipantsAPIMsg("xxx", spectypes.BNRoleAttester, 400, 404) - HandleParticipantsQuery(l, ibftStorage, nm, networkConfig.DomainType()) + HandleParticipantsQuery(l, ibftStorage, nm, networkConfig.DomainType) require.NotNil(t, nm.Msg.Data) errs, ok := nm.Msg.Data.([]string) require.True(t, ok) @@ -161,7 +161,7 @@ func TestHandleDecidedQuery(t *testing.T) { t.Run("non-existing role", func(t *testing.T) { nm := newParticipantsAPIMsg(pk.SerializeToHexStr(), math.MaxUint64, 0, 250) - HandleParticipantsQuery(l, ibftStorage, nm, networkConfig.DomainType()) + HandleParticipantsQuery(l, ibftStorage, nm, networkConfig.DomainType) require.NotNil(t, nm.Msg.Data) errs, ok := nm.Msg.Data.([]string) require.True(t, ok) @@ -170,7 +170,7 @@ func TestHandleDecidedQuery(t *testing.T) { t.Run("non-existing storage", func(t *testing.T) { nm := newParticipantsAPIMsg(pk.SerializeToHexStr(), spectypes.BNRoleSyncCommitteeContribution, 0, 250) - HandleParticipantsQuery(l, ibftStorage, nm, networkConfig.DomainType()) + HandleParticipantsQuery(l, ibftStorage, nm, networkConfig.DomainType) require.NotNil(t, nm.Msg.Data) errs, ok := nm.Msg.Data.([]string) require.True(t, ok) diff --git a/go.mod b/go.mod index 251d3afd46..02714a6d96 100644 --- a/go.mod +++ b/go.mod @@ -37,7 +37,6 @@ require ( github.com/spf13/cobra v1.8.1 github.com/ssvlabs/eth2-key-manager v1.4.2 github.com/ssvlabs/ssv-spec v1.0.0 - github.com/ssvlabs/ssv-spec-pre-cc v0.0.0-20240725052506-c48532da6a63 github.com/status-im/keycard-go v0.2.0 github.com/stretchr/testify v1.9.0 github.com/wealdtech/go-eth2-types/v2 v2.8.1 diff --git a/go.sum b/go.sum index ab6d6484ca..5c258c15e6 100644 --- a/go.sum +++ b/go.sum @@ -736,8 +736,6 @@ github.com/ssvlabs/eth2-key-manager v1.4.2 h1:YdeI7vk9jSa8vfSKogx2aSjtYz4eS9Bp85 github.com/ssvlabs/eth2-key-manager v1.4.2/go.mod h1:RqsGIMCsOeUJQmC2nytr82z5vqn5gN3i3VAoY0OydV8= github.com/ssvlabs/ssv-spec v1.0.0 h1:h/EoKLsLXVO6GIzKoBi1O05xdHlirpLi8DLhVcTSEBE= github.com/ssvlabs/ssv-spec v1.0.0/go.mod h1:lTqsNeTUIfpacMoztbN7YqvFttDigCLjINjy/8I2Wuc= -github.com/ssvlabs/ssv-spec-pre-cc v0.0.0-20240725052506-c48532da6a63 h1:su82+QgRipnMdYyV/ux8+Pd6psdutbN97ltWl1Rr6Xc= -github.com/ssvlabs/ssv-spec-pre-cc v0.0.0-20240725052506-c48532da6a63/go.mod h1:uYNLeK2YWjlGidlA1co2uXA0JBp15m+6088ZZmXaVBI= github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= diff --git a/ibft/genesisstorage/store.go b/ibft/genesisstorage/store.go deleted file mode 100644 index 17944b0ca4..0000000000 --- a/ibft/genesisstorage/store.go +++ /dev/null @@ -1,191 +0,0 @@ -package genesisstorage - -import ( - "encoding/binary" - - "github.com/pkg/errors" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" - "go.uber.org/zap" - - "github.com/ssvlabs/ssv/protocol/genesis/qbft/instance" - genesisqbftstorage "github.com/ssvlabs/ssv/protocol/genesis/qbft/storage" - "github.com/ssvlabs/ssv/storage/basedb" - - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" -) - -const ( - highestInstanceKey = "highest_instance" - instanceKey = "instance" -) - -var ( - metricsHighestDecidedGenesis = promauto.NewGaugeVec(prometheus.GaugeOpts{ - Name: "ssv:validator:genesis:ibft_highest_decided_genesis", - Help: "The highest decided sequence number", - }, []string{"identifier", "pubKey"}) -) - -func init() { - logger := zap.L() - if err := prometheus.Register(metricsHighestDecidedGenesis); err != nil { - logger.Debug("could not register prometheus collector") - } -} - -// ibftStorage struct -// instanceType is what separates different iBFT eth2 duty types (attestation, proposal and aggregation) -type ibftStorage struct { - prefix []byte - db basedb.Database -} - -// New create new ibft storage -func New(db basedb.Database, prefix string) genesisqbftstorage.QBFTStore { - return &ibftStorage{ - prefix: []byte(prefix), - db: db, - } -} - -// GetHighestInstance returns the StoredInstance for the highest instance. -func (i *ibftStorage) GetHighestInstance(identifier []byte) (*genesisqbftstorage.StoredInstance, error) { - val, found, err := i.get(highestInstanceKey, identifier[:]) - if !found { - return nil, nil - } - if err != nil { - return nil, err - } - ret := &genesisqbftstorage.StoredInstance{} - if err := ret.Decode(val); err != nil { - return nil, errors.Wrap(err, "could not decode instance") - } - return ret, nil -} - -func (i *ibftStorage) SaveInstance(instance *genesisqbftstorage.StoredInstance) error { - return i.saveInstance(instance, true, false) -} - -func (i *ibftStorage) SaveHighestInstance(instance *genesisqbftstorage.StoredInstance) error { - return i.saveInstance(instance, false, true) -} - -func (i *ibftStorage) SaveHighestAndHistoricalInstance(instance *genesisqbftstorage.StoredInstance) error { - return i.saveInstance(instance, true, true) -} - -func (i *ibftStorage) saveInstance(inst *genesisqbftstorage.StoredInstance, toHistory, asHighest bool) error { - inst.State = instance.CompactCopy(inst.State, inst.DecidedMessage) - - value, err := inst.Encode() - if err != nil { - return errors.Wrap(err, "could not encode instance") - } - - if asHighest { - err = i.save(value, highestInstanceKey, inst.State.ID) - if err != nil { - return errors.Wrap(err, "could not save highest instance") - } - } - - if toHistory { - err = i.save(value, instanceKey, inst.State.ID, uInt64ToByteSlice(uint64(inst.State.Height))) - if err != nil { - return errors.Wrap(err, "could not save historical instance") - } - } - - return nil -} - -// GetInstance returns historical StoredInstance for the given identifier and height. -func (i *ibftStorage) GetInstance(identifier []byte, height genesisspecqbft.Height) (*genesisqbftstorage.StoredInstance, error) { - val, found, err := i.get(instanceKey, identifier[:], uInt64ToByteSlice(uint64(height))) - if !found { - return nil, nil - } - if err != nil { - return nil, err - } - ret := &genesisqbftstorage.StoredInstance{} - if err := ret.Decode(val); err != nil { - return nil, errors.Wrap(err, "could not decode instance") - } - return ret, nil -} - -// GetInstancesInRange returns historical StoredInstance's in the given range. -func (i *ibftStorage) GetInstancesInRange(identifier []byte, from genesisspecqbft.Height, to genesisspecqbft.Height) ([]*genesisqbftstorage.StoredInstance, error) { - instances := make([]*genesisqbftstorage.StoredInstance, 0) - - for seq := from; seq <= to; seq++ { - instance, err := i.GetInstance(identifier, seq) - if err != nil { - return nil, errors.Wrap(err, "failed to get instance") - } - if instance != nil { - instances = append(instances, instance) - } - } - - return instances, nil -} - -// CleanAllInstances removes all StoredInstance's & highest StoredInstance's for msgID. -func (i *ibftStorage) CleanAllInstances(logger *zap.Logger, msgID []byte) error { - prefix := i.prefix - prefix = append(prefix, msgID[:]...) - prefix = append(prefix, []byte(instanceKey)...) - err := i.db.DropPrefix(prefix) - if err != nil { - return errors.Wrap(err, "failed to remove decided") - } - - if err := i.delete(highestInstanceKey, msgID[:]); err != nil { - return errors.Wrap(err, "failed to remove last decided") - } - return nil -} - -func (i *ibftStorage) save(value []byte, id string, pk []byte, keyParams ...[]byte) error { - prefix := append(i.prefix, pk...) - key := i.key(id, keyParams...) - return i.db.Set(prefix, key, value) -} - -func (i *ibftStorage) get(id string, pk []byte, keyParams ...[]byte) ([]byte, bool, error) { - prefix := append(i.prefix, pk...) - key := i.key(id, keyParams...) - obj, found, err := i.db.Get(prefix, key) - if !found { - return nil, found, nil - } - if err != nil { - return nil, found, err - } - return obj.Value, found, nil -} - -func (i *ibftStorage) delete(id string, pk []byte, keyParams ...[]byte) error { - prefix := append(i.prefix, pk...) - key := i.key(id, keyParams...) - return i.db.Delete(prefix, key) -} - -func (i *ibftStorage) key(id string, params ...[]byte) []byte { - ret := []byte(id) - for _, p := range params { - ret = append(ret, p...) - } - return ret -} - -func uInt64ToByteSlice(n uint64) []byte { - b := make([]byte, 8) - binary.LittleEndian.PutUint64(b, n) - return b -} diff --git a/ibft/genesisstorage/store_test.go b/ibft/genesisstorage/store_test.go deleted file mode 100644 index 9654597c66..0000000000 --- a/ibft/genesisstorage/store_test.go +++ /dev/null @@ -1,183 +0,0 @@ -package genesisstorage - -import ( - "testing" - - "github.com/stretchr/testify/require" - "go.uber.org/zap" - - "github.com/ssvlabs/ssv/logging" - genesisqbftstorage "github.com/ssvlabs/ssv/protocol/genesis/qbft/storage" - "github.com/ssvlabs/ssv/protocol/genesis/types" - "github.com/ssvlabs/ssv/storage/basedb" - "github.com/ssvlabs/ssv/storage/kv" - - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" -) - -func TestCleanInstances(t *testing.T) { - logger := logging.TestLogger(t) - msgID := genesisspectypes.NewMsgID(types.GetDefaultDomain(), []byte("pk"), genesisspectypes.BNRoleAttester) - storage, err := newTestIbftStorage(logger, "test") - require.NoError(t, err) - - generateInstance := func(id genesisspectypes.MessageID, h genesisspecqbft.Height) *genesisqbftstorage.StoredInstance { - return &genesisqbftstorage.StoredInstance{ - State: &genesisspecqbft.State{ - ID: id[:], - Round: 1, - Height: h, - LastPreparedRound: 1, - LastPreparedValue: []byte("value"), - Decided: true, - DecidedValue: []byte("value"), - ProposeContainer: genesisspecqbft.NewMsgContainer(), - PrepareContainer: genesisspecqbft.NewMsgContainer(), - CommitContainer: genesisspecqbft.NewMsgContainer(), - RoundChangeContainer: genesisspecqbft.NewMsgContainer(), - }, - DecidedMessage: &genesisspecqbft.SignedMessage{ - Signature: []byte("sig"), - Signers: []genesisspectypes.OperatorID{1}, - Message: genesisspecqbft.Message{ - MsgType: genesisspecqbft.CommitMsgType, - Height: h, - Round: 1, - Identifier: id[:], - Root: [32]byte{}, - }, - }, - } - } - - msgsCount := 10 - for i := 0; i < msgsCount; i++ { - require.NoError(t, storage.SaveInstance(generateInstance(msgID, genesisspecqbft.Height(i)))) - } - require.NoError(t, storage.SaveHighestInstance(generateInstance(msgID, genesisspecqbft.Height(msgsCount)))) - - // add different msgID - differMsgID := genesisspectypes.NewMsgID(types.GetDefaultDomain(), []byte("differ_pk"), genesisspectypes.BNRoleAttester) - require.NoError(t, storage.SaveInstance(generateInstance(differMsgID, genesisspecqbft.Height(1)))) - require.NoError(t, storage.SaveHighestInstance(generateInstance(differMsgID, genesisspecqbft.Height(msgsCount)))) - require.NoError(t, storage.SaveHighestAndHistoricalInstance(generateInstance(differMsgID, genesisspecqbft.Height(1)))) - - res, err := storage.GetInstancesInRange(msgID[:], 0, genesisspecqbft.Height(msgsCount)) - require.NoError(t, err) - require.Equal(t, msgsCount, len(res)) - - last, err := storage.GetHighestInstance(msgID[:]) - require.NoError(t, err) - require.NotNil(t, last) - require.Equal(t, genesisspecqbft.Height(msgsCount), last.State.Height) - - // remove all instances - require.NoError(t, storage.CleanAllInstances(logger, msgID[:])) - res, err = storage.GetInstancesInRange(msgID[:], 0, genesisspecqbft.Height(msgsCount)) - require.NoError(t, err) - require.Equal(t, 0, len(res)) - - last, err = storage.GetHighestInstance(msgID[:]) - require.NoError(t, err) - require.Nil(t, last) - - // check other msgID - res, err = storage.GetInstancesInRange(differMsgID[:], 0, genesisspecqbft.Height(msgsCount)) - require.NoError(t, err) - require.Equal(t, 1, len(res)) - - last, err = storage.GetHighestInstance(differMsgID[:]) - require.NoError(t, err) - require.NotNil(t, last) -} - -func TestSaveAndFetchLastState(t *testing.T) { - identifier := genesisspectypes.NewMsgID(types.GetDefaultDomain(), []byte("pk"), genesisspectypes.BNRoleAttester) - - instance := &genesisqbftstorage.StoredInstance{ - State: &genesisspecqbft.State{ - Share: nil, - ID: identifier[:], - Round: 1, - Height: 1, - LastPreparedRound: 1, - LastPreparedValue: []byte("value"), - ProposalAcceptedForCurrentRound: nil, - Decided: true, - DecidedValue: []byte("value"), - ProposeContainer: genesisspecqbft.NewMsgContainer(), - PrepareContainer: genesisspecqbft.NewMsgContainer(), - CommitContainer: genesisspecqbft.NewMsgContainer(), - RoundChangeContainer: genesisspecqbft.NewMsgContainer(), - }, - } - - storage, err := newTestIbftStorage(logging.TestLogger(t), "test") - require.NoError(t, err) - - require.NoError(t, storage.SaveHighestInstance(instance)) - - savedInstance, err := storage.GetHighestInstance(identifier[:]) - require.NoError(t, err) - require.NotNil(t, savedInstance) - require.Equal(t, genesisspecqbft.Height(1), savedInstance.State.Height) - require.Equal(t, genesisspecqbft.Round(1), savedInstance.State.Round) - require.Equal(t, identifier.String(), genesisspecqbft.ControllerIdToMessageID(savedInstance.State.ID).String()) - require.Equal(t, genesisspecqbft.Round(1), savedInstance.State.LastPreparedRound) - require.Equal(t, true, savedInstance.State.Decided) - require.Equal(t, []byte("value"), savedInstance.State.LastPreparedValue) - require.Equal(t, []byte("value"), savedInstance.State.DecidedValue) -} - -func TestSaveAndFetchState(t *testing.T) { - identifier := genesisspectypes.NewMsgID(types.GetDefaultDomain(), []byte("pk"), genesisspectypes.BNRoleAttester) - - instance := &genesisqbftstorage.StoredInstance{ - State: &genesisspecqbft.State{ - Share: nil, - ID: identifier[:], - Round: 1, - Height: 1, - LastPreparedRound: 1, - LastPreparedValue: []byte("value"), - ProposalAcceptedForCurrentRound: nil, - Decided: true, - DecidedValue: []byte("value"), - ProposeContainer: genesisspecqbft.NewMsgContainer(), - PrepareContainer: genesisspecqbft.NewMsgContainer(), - CommitContainer: genesisspecqbft.NewMsgContainer(), - RoundChangeContainer: genesisspecqbft.NewMsgContainer(), - }, - } - - storage, err := newTestIbftStorage(logging.TestLogger(t), "test") - require.NoError(t, err) - - require.NoError(t, storage.SaveInstance(instance)) - - savedInstances, err := storage.GetInstancesInRange(identifier[:], 1, 1) - require.NoError(t, err) - require.NotNil(t, savedInstances) - require.Len(t, savedInstances, 1) - savedInstance := savedInstances[0] - - require.Equal(t, genesisspecqbft.Height(1), savedInstance.State.Height) - require.Equal(t, genesisspecqbft.Round(1), savedInstance.State.Round) - require.Equal(t, identifier.String(), genesisspecqbft.ControllerIdToMessageID(savedInstance.State.ID).String()) - require.Equal(t, genesisspecqbft.Round(1), savedInstance.State.LastPreparedRound) - require.Equal(t, true, savedInstance.State.Decided) - require.Equal(t, []byte("value"), savedInstance.State.LastPreparedValue) - require.Equal(t, []byte("value"), savedInstance.State.DecidedValue) -} - -func newTestIbftStorage(logger *zap.Logger, prefix string) (genesisqbftstorage.QBFTStore, error) { - db, err := kv.NewInMemory(logger.Named(logging.NameBadgerDBLog), basedb.Options{ - Reporting: true, - }) - if err != nil { - return nil, err - } - - return New(db, prefix), nil -} diff --git a/ibft/genesisstorage/stores.go b/ibft/genesisstorage/stores.go deleted file mode 100644 index d08a92bc66..0000000000 --- a/ibft/genesisstorage/stores.go +++ /dev/null @@ -1,53 +0,0 @@ -package genesisstorage - -import ( - "github.com/ssvlabs/ssv/utils/hashmap" - - genesisqbftstorage "github.com/ssvlabs/ssv/protocol/genesis/qbft/storage" - "github.com/ssvlabs/ssv/storage/basedb" - - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" -) - -// QBFTStores wraps sync map with cast functions to qbft store -type QBFTStores struct { - m *hashmap.Map[genesisspectypes.BeaconRole, genesisqbftstorage.QBFTStore] -} - -func NewStores() *QBFTStores { - return &QBFTStores{ - m: hashmap.New[genesisspectypes.BeaconRole, genesisqbftstorage.QBFTStore](), - } -} - -func NewStoresFromRoles(db basedb.Database, roles ...genesisspectypes.BeaconRole) *QBFTStores { - stores := NewStores() - for _, role := range roles { - stores.Add(role, New(db, role.String())) - } - return stores -} - -// Get store from sync map by role type -func (qs *QBFTStores) Get(role genesisspectypes.BeaconRole) genesisqbftstorage.QBFTStore { - s, ok := qs.m.Get(role) - if !ok { - return nil - } - return s -} - -// Add store to sync map by role as a key -func (qs *QBFTStores) Add(role genesisspectypes.BeaconRole, store genesisqbftstorage.QBFTStore) { - qs.m.Set(role, store) -} - -// Each iterates over all stores and calls the given function -func (qs *QBFTStores) Each(f func(role genesisspectypes.BeaconRole, store genesisqbftstorage.QBFTStore) error) error { - var err error - qs.m.Range(func(role genesisspectypes.BeaconRole, store genesisqbftstorage.QBFTStore) bool { - err = f(role, store) - return err == nil - }) - return err -} diff --git a/ibft/genesisstorage/stores_test.go b/ibft/genesisstorage/stores_test.go deleted file mode 100644 index 361686d335..0000000000 --- a/ibft/genesisstorage/stores_test.go +++ /dev/null @@ -1,49 +0,0 @@ -package genesisstorage - -import ( - "testing" - - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - "github.com/stretchr/testify/require" - - "github.com/ssvlabs/ssv/logging" - genesisqbftstorage "github.com/ssvlabs/ssv/protocol/genesis/qbft/storage" - "github.com/ssvlabs/ssv/storage/basedb" - "github.com/ssvlabs/ssv/storage/kv" -) - -func TestQBFTStores(t *testing.T) { - logger := logging.TestLogger(t) - - qbftMap := NewStores() - - store, err := newTestIbftStorage(logger, "") - require.NoError(t, err) - qbftMap.Add(genesisspectypes.BNRoleAttester, store) - qbftMap.Add(genesisspectypes.BNRoleProposer, store) - - require.NotNil(t, qbftMap.Get(genesisspectypes.BNRoleAttester)) - require.NotNil(t, qbftMap.Get(genesisspectypes.BNRoleProposer)) - - db, err := kv.NewInMemory(logger.Named(logging.NameBadgerDBLog), basedb.Options{ - Reporting: true, - }) - require.NoError(t, err) - qbftMap = NewStoresFromRoles(db, genesisspectypes.BNRoleAttester, genesisspectypes.BNRoleProposer) - - require.NotNil(t, qbftMap.Get(genesisspectypes.BNRoleAttester)) - require.NotNil(t, qbftMap.Get(genesisspectypes.BNRoleProposer)) - - id := []byte{1, 2, 3} - - qbftMap.Each(func(role genesisspectypes.BeaconRole, store genesisqbftstorage.QBFTStore) error { - return store.SaveInstance(&genesisqbftstorage.StoredInstance{State: &genesisspecqbft.State{Height: 1, ID: id}}) - }) - - instance, err := qbftMap.Get(genesisspectypes.BNRoleAttester).GetInstance(id, 1) - require.NoError(t, err) - require.NotNil(t, instance) - require.Equal(t, genesisspecqbft.Height(1), instance.State.Height) - require.Equal(t, id, instance.State.ID) -} diff --git a/message/validation/consensus_validation.go b/message/validation/consensus_validation.go index 2bab0a73b4..c22cda44f5 100644 --- a/message/validation/consensus_validation.go +++ b/message/validation/consensus_validation.go @@ -11,7 +11,6 @@ import ( "time" "github.com/attestantio/go-eth2-client/spec/phase0" - "github.com/ssvlabs/ssv-spec-pre-cc/types" specqbft "github.com/ssvlabs/ssv-spec/qbft" spectypes "github.com/ssvlabs/ssv-spec/types" @@ -443,7 +442,7 @@ func (mv *messageValidator) roundBelongsToAllowedSpread( return nil } -func (mv *messageValidator) roundRobinProposer(height specqbft.Height, round specqbft.Round, committee []spectypes.OperatorID) types.OperatorID { +func (mv *messageValidator) roundRobinProposer(height specqbft.Height, round specqbft.Round, committee []spectypes.OperatorID) spectypes.OperatorID { firstRoundIndex := uint64(0) if height != specqbft.FirstHeight { firstRoundIndex += uint64(height) % uint64(len(committee)) diff --git a/message/validation/fork.go b/message/validation/fork.go deleted file mode 100644 index 263bfb1335..0000000000 --- a/message/validation/fork.go +++ /dev/null @@ -1,33 +0,0 @@ -package validation - -import ( - "context" - - pubsub "github.com/libp2p/go-libp2p-pubsub" - "github.com/libp2p/go-libp2p/core/peer" - - genesisvalidation "github.com/ssvlabs/ssv/message/validation/genesis" - "github.com/ssvlabs/ssv/networkconfig" -) - -type ForkingMessageValidation struct { - NetworkConfig networkconfig.NetworkConfig - Genesis genesisvalidation.MessageValidator - Alan MessageValidator -} - -func (f *ForkingMessageValidation) Validate(ctx context.Context, p peer.ID, pmsg *pubsub.Message) pubsub.ValidationResult { - if f.NetworkConfig.PastAlanFork() { - return f.Alan.Validate(ctx, p, pmsg) - } - return f.Genesis.Validate(ctx, p, pmsg) -} - -func (f *ForkingMessageValidation) ValidatorForTopic(topic string) func(ctx context.Context, p peer.ID, pmsg *pubsub.Message) pubsub.ValidationResult { - return func(ctx context.Context, p peer.ID, pmsg *pubsub.Message) pubsub.ValidationResult { - if f.NetworkConfig.PastAlanFork() { - return f.Alan.ValidatorForTopic(topic)(ctx, p, pmsg) - } - return f.Genesis.ValidatorForTopic(topic)(ctx, p, pmsg) - } -} diff --git a/message/validation/genesis/consensus_state.go b/message/validation/genesis/consensus_state.go deleted file mode 100644 index c1956c23e1..0000000000 --- a/message/validation/genesis/consensus_state.go +++ /dev/null @@ -1,36 +0,0 @@ -package validation - -import ( - "github.com/attestantio/go-eth2-client/spec/phase0" - spectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - - "github.com/ssvlabs/ssv/utils/hashmap" -) - -// ConsensusID uniquely identifies a public key and role pair to keep track of state. -// TODO change ConsensusID struct to use recipient id(to determine if message is for validator or cluster) instead of public key -type ConsensusID struct { - PubKey phase0.BLSPubKey - Role spectypes.BeaconRole -} - -// ConsensusState keeps track of the signers for a given public key and role. -type ConsensusState struct { - // TODO: consider evicting old data to avoid excessive memory consumption - Signers *hashmap.Map[spectypes.OperatorID, *SignerState] -} - -// GetSignerState retrieves the state for the given signer. -// Returns nil if the signer is not found. -func (cs *ConsensusState) GetSignerState(signer spectypes.OperatorID) *SignerState { - signerState, _ := cs.Signers.Get(signer) - return signerState -} - -// CreateSignerState initializes and sets a new SignerState for the given signer. -func (cs *ConsensusState) CreateSignerState(signer spectypes.OperatorID) *SignerState { - signerState := &SignerState{} - cs.Signers.Set(signer, signerState) - - return signerState -} diff --git a/message/validation/genesis/consensus_validation.go b/message/validation/genesis/consensus_validation.go deleted file mode 100644 index ca0a0623f4..0000000000 --- a/message/validation/genesis/consensus_validation.go +++ /dev/null @@ -1,466 +0,0 @@ -package validation - -// consensus_validation.go contains methods for validating consensus messages - -import ( - "bytes" - "fmt" - "slices" - "time" - - "github.com/attestantio/go-eth2-client/spec/phase0" - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - alanspecqbft "github.com/ssvlabs/ssv-spec/qbft" - - "github.com/ssvlabs/ssv/protocol/v2/qbft/roundtimer" - ssvtypes "github.com/ssvlabs/ssv/protocol/v2/types" - "github.com/ssvlabs/ssv/utils/casts" -) - -func (mv *messageValidator) validateConsensusMessage( - share *ssvtypes.SSVShare, - signedMsg *genesisspecqbft.SignedMessage, - messageID genesisspectypes.MessageID, - receivedAt time.Time, - signatureVerifier func() error, -) (ConsensusDescriptor, phase0.Slot, error) { - var consensusDescriptor ConsensusDescriptor - - msgSlot := phase0.Slot(signedMsg.Message.Height) - msgRound := signedMsg.Message.Round - - consensusDescriptor = ConsensusDescriptor{ - QBFTMessageType: signedMsg.Message.MsgType, - Round: msgRound, - Signers: signedMsg.Signers, - Committee: share.Committee, - } - - mv.metrics.ConsensusMsgType(alanspecqbft.MessageType(signedMsg.Message.MsgType), len(signedMsg.Signers)) - - switch messageID.GetRoleType() { - case genesisspectypes.BNRoleValidatorRegistration, genesisspectypes.BNRoleVoluntaryExit: - e := ErrUnexpectedConsensusMessage - e.got = messageID.GetRoleType() - return consensusDescriptor, msgSlot, e - } - - if err := mv.validateSignatureFormat(signedMsg.Signature); err != nil { - return consensusDescriptor, msgSlot, err - } - - if !mv.validQBFTMsgType(signedMsg.Message.MsgType) { - return consensusDescriptor, msgSlot, ErrUnknownQBFTMessageType - } - - if err := mv.validConsensusSigners(share, signedMsg); err != nil { - return consensusDescriptor, msgSlot, err - } - - role := messageID.GetRoleType() - - if err := mv.validateSlotTime(msgSlot, role, receivedAt); err != nil { - return consensusDescriptor, msgSlot, err - } - - maxRound, err := mv.maxRound(role) - if err != nil { - return consensusDescriptor, msgSlot, fmt.Errorf("failed to get max round: %w", err) - } - if msgRound > maxRound { - err := ErrRoundTooHigh - err.got = fmt.Sprintf("%v (%v role)", msgRound, role) - err.want = fmt.Sprintf("%v (%v role)", maxRound, role) - return consensusDescriptor, msgSlot, err - } - - slotStartTime := mv.netCfg.Beacon.GetSlotStartTime(msgSlot) /*. - Add(mv.waitAfterSlotStart(role))*/ // TODO: not supported yet because first round is non-deterministic now - - sinceSlotStart := time.Duration(0) - estimatedRound := genesisspecqbft.FirstRound - if receivedAt.After(slotStartTime) { - sinceSlotStart = receivedAt.Sub(slotStartTime) - estimatedRound, err = mv.currentEstimatedRound(sinceSlotStart) - if err != nil { - return consensusDescriptor, msgSlot, err - } - } - - // TODO: lowestAllowed is not supported yet because first round is non-deterministic now - lowestAllowed := /*estimatedRound - allowedRoundsInPast*/ genesisspecqbft.FirstRound - highestAllowed := estimatedRound + allowedRoundsInFuture - - if msgRound < lowestAllowed || msgRound > highestAllowed { - err := ErrEstimatedRoundTooFar - err.got = fmt.Sprintf("%v (%v role)", msgRound, role) - err.want = fmt.Sprintf("between %v and %v (%v role) / %v passed", lowestAllowed, highestAllowed, role, sinceSlotStart) - return consensusDescriptor, msgSlot, err - } - - if mv.hasFullData(signedMsg) { - hashedFullData, err := genesisspecqbft.HashDataRoot(signedMsg.FullData) - if err != nil { - return consensusDescriptor, msgSlot, fmt.Errorf("hash data root: %w", err) - } - - if hashedFullData != signedMsg.Message.Root { - return consensusDescriptor, msgSlot, ErrInvalidHash - } - } - - if err := mv.validateBeaconDuty(messageID.GetRoleType(), msgSlot, share); err != nil { - return consensusDescriptor, msgSlot, err - } - - state := mv.consensusState(messageID) - for _, signer := range signedMsg.Signers { - if err := mv.validateSignerBehaviorConsensus(state, signer, share, messageID, signedMsg); err != nil { - return consensusDescriptor, msgSlot, fmt.Errorf("bad signer behavior: %w", err) - } - } - - if signatureVerifier != nil { - if err := signatureVerifier(); err != nil { - return consensusDescriptor, msgSlot, err - } - } - - for _, signer := range signedMsg.Signers { - signerState := state.GetSignerState(signer) - if signerState == nil { - signerState = state.CreateSignerState(signer) - } - if msgSlot > signerState.Slot { - newEpoch := mv.netCfg.Beacon.EstimatedEpochAtSlot(msgSlot) > mv.netCfg.Beacon.EstimatedEpochAtSlot(signerState.Slot) - signerState.ResetSlot(msgSlot, msgRound, newEpoch) - } else if msgSlot == signerState.Slot && msgRound > signerState.Round { - signerState.Reset(msgRound) - } - - // Allow to change the state only by proposal to avoid an attack - // where any node can send an RC message that changes message validation state. - // We could allow proposal or round change quorum, but it's more complex to implement, so just proposal is fine. - if signedMsg.Message.MsgType == genesisspecqbft.ProposalMsgType { - if mv.hasFullData(signedMsg) && signerState.ProposalData == nil { - signerState.ProposalData = signedMsg.FullData - } - } - - err := signerState.MessageCounts.RecordConsensusMessage(signedMsg) - if err != nil { - return consensusDescriptor, msgSlot, fmt.Errorf("can't record consensus message: %w", err) - } - } - - return consensusDescriptor, msgSlot, nil -} - -func (mv *messageValidator) validateJustifications( - share *ssvtypes.SSVShare, - signedMsg *genesisspecqbft.SignedMessage, -) error { - pj, err := signedMsg.Message.GetPrepareJustifications() - if err != nil { - e := ErrMalformedPrepareJustifications - e.innerErr = err - return e - } - - if len(pj) != 0 && signedMsg.Message.MsgType != genesisspecqbft.ProposalMsgType { - e := ErrUnexpectedPrepareJustifications - e.got = signedMsg.Message.MsgType - return e - } - - rcj, err := signedMsg.Message.GetRoundChangeJustifications() - if err != nil { - e := ErrMalformedRoundChangeJustifications - e.innerErr = err - return e - } - - if len(rcj) != 0 && signedMsg.Message.MsgType != genesisspecqbft.ProposalMsgType && signedMsg.Message.MsgType != genesisspecqbft.RoundChangeMsgType { - e := ErrUnexpectedRoundChangeJustifications - e.got = signedMsg.Message.MsgType - return e - } - - if signedMsg.Message.MsgType == genesisspecqbft.ProposalMsgType { - // TODO: can we keep it disabled for simplicity? - - //cfg := newQBFTConfig(mv.netCfg.Domain) - // - //if err := instance.IsProposalJustification( - // cfg, - // share, - // rcj, - // pj, - // signedMsg.Message.Height, - // signedMsg.Message.Round, - // signedMsg.FullData, - //); err != nil { - // e := ErrInvalidJustifications - // e.innerErr = err - // return e - //} - } - - return nil -} - -func (mv *messageValidator) validateSignerBehaviorConsensus( - state *ConsensusState, - signer genesisspectypes.OperatorID, - share *ssvtypes.SSVShare, - msgID genesisspectypes.MessageID, - signedMsg *genesisspecqbft.SignedMessage, -) error { - signerState := state.GetSignerState(signer) - - // If signer state is nil, this is the first message for the signer and - // it's not necessary to check the next rules. - if signerState == nil { - return mv.validateJustifications(share, signedMsg) - } - - msgSlot := phase0.Slot(signedMsg.Message.Height) - msgRound := signedMsg.Message.Round - - if msgSlot < signerState.Slot { - // Signers aren't allowed to decrease their slot. - // If they've sent a future message due to clock error, - // this should be caught by the earlyMessage check. - err := ErrSlotAlreadyAdvanced - err.want = signerState.Slot - err.got = msgSlot - return err - } - - if msgSlot == signerState.Slot && msgRound < signerState.Round { - // Signers aren't allowed to decrease their round. - // If they've sent a future message due to clock error, - // they'd have to wait for the next slot/round to be accepted. - err := ErrRoundAlreadyAdvanced - err.want = signerState.Round - err.got = msgRound - return err - } - - newDutyInSameEpoch := false - if msgSlot > signerState.Slot && mv.netCfg.Beacon.EstimatedEpochAtSlot(msgSlot) == mv.netCfg.Beacon.EstimatedEpochAtSlot(signerState.Slot) { - newDutyInSameEpoch = true - } - - if err := mv.validateDutyCount(signerState, msgID, newDutyInSameEpoch); err != nil { - return err - } - - if msgSlot == signerState.Slot && msgRound == signerState.Round { - if mv.hasFullData(signedMsg) && signerState.ProposalData != nil && !bytes.Equal(signerState.ProposalData, signedMsg.FullData) { - return ErrDifferentProposalData - } - - limits := maxMessageCounts(len(share.Committee)) - if err := signerState.MessageCounts.ValidateConsensusMessage(signedMsg, limits); err != nil { - return err - } - } - - return mv.validateJustifications(share, signedMsg) -} - -func (mv *messageValidator) validateDutyCount( - state *SignerState, - msgID genesisspectypes.MessageID, - newDutyInSameEpoch bool, -) error { - switch msgID.GetRoleType() { - case genesisspectypes.BNRoleAttester, genesisspectypes.BNRoleAggregator, genesisspectypes.BNRoleValidatorRegistration, genesisspectypes.BNRoleVoluntaryExit: - limit := maxDutiesPerEpoch - - if sameSlot := !newDutyInSameEpoch; sameSlot { - limit++ - } - - if state.EpochDuties >= limit { - err := ErrTooManyDutiesPerEpoch - err.got = fmt.Sprintf("%v (role %v)", state.EpochDuties, msgID.GetRoleType()) - err.want = fmt.Sprintf("less than %v", maxDutiesPerEpoch) - return err - } - - return nil - } - - return nil -} - -func (mv *messageValidator) validateBeaconDuty( - role genesisspectypes.BeaconRole, - slot phase0.Slot, - share *ssvtypes.SSVShare, -) error { - switch role { - case genesisspectypes.BNRoleProposer: - if share.Metadata.BeaconMetadata == nil { - return ErrNoShareMetadata - } - - epoch := mv.netCfg.Beacon.EstimatedEpochAtSlot(slot) - if mv.dutyStore != nil && mv.dutyStore.Proposer.ValidatorDuty(epoch, slot, share.Metadata.BeaconMetadata.Index) == nil { - return ErrNoDuty - } - - return nil - - case genesisspectypes.BNRoleSyncCommittee, genesisspectypes.BNRoleSyncCommitteeContribution: - if share.Metadata.BeaconMetadata == nil { - return ErrNoShareMetadata - } - - period := mv.netCfg.Beacon.EstimatedSyncCommitteePeriodAtEpoch(mv.netCfg.Beacon.EstimatedEpochAtSlot(slot)) - if mv.dutyStore != nil && mv.dutyStore.SyncCommittee.Duty(period, share.Metadata.BeaconMetadata.Index) == nil { - return ErrNoDuty - } - - return nil - } - - return nil -} - -func (mv *messageValidator) hasFullData(signedMsg *genesisspecqbft.SignedMessage) bool { - return (signedMsg.Message.MsgType == genesisspecqbft.ProposalMsgType || - signedMsg.Message.MsgType == genesisspecqbft.RoundChangeMsgType || - mv.isDecidedMessage(signedMsg)) && len(signedMsg.FullData) != 0 // TODO: more complex check of FullData -} - -func (mv *messageValidator) isDecidedMessage(signedMsg *genesisspecqbft.SignedMessage) bool { - return signedMsg.Message.MsgType == genesisspecqbft.CommitMsgType && len(signedMsg.Signers) > 1 -} - -func (mv *messageValidator) maxRound(role genesisspectypes.BeaconRole) (genesisspecqbft.Round, error) { - switch role { - case genesisspectypes.BNRoleAttester, genesisspectypes.BNRoleAggregator: // TODO: check if value for aggregator is correct as there are messages on stage exceeding the limit - return 12, nil // TODO: consider calculating based on quick timeout and slow timeout - case genesisspectypes.BNRoleProposer, genesisspectypes.BNRoleSyncCommittee, genesisspectypes.BNRoleSyncCommitteeContribution: - return 6, nil - case genesisspectypes.BNRoleValidatorRegistration, genesisspectypes.BNRoleVoluntaryExit: - return 0, nil - default: - return 0, fmt.Errorf("unknown role") - } -} - -func (mv *messageValidator) currentEstimatedRound(sinceSlotStart time.Duration) (genesisspecqbft.Round, error) { - // Quick rounds (<= QuickTimeoutThreshold) - quickRounds, err := casts.DurationToUint64(sinceSlotStart / roundtimer.QuickTimeout) - if err != nil { - return 0, fmt.Errorf("failed to convert time duration to uint64: %w", err) - } - currentQuickRound := genesisspecqbft.FirstRound + genesisspecqbft.Round(quickRounds) - if currentQuickRound <= genesisspecqbft.Round(roundtimer.QuickTimeoutThreshold) { - return currentQuickRound, nil - } - - // Slow rounds (> QuickTimeoutThreshold) - sinceFirstSlowRound := sinceSlotStart - (time.Duration(genesisspecqbft.Round(roundtimer.QuickTimeoutThreshold)) * roundtimer.QuickTimeout) - slowRounds, err := casts.DurationToUint64(sinceFirstSlowRound / roundtimer.SlowTimeout) - if err != nil { - return 0, fmt.Errorf("failed to convert time duration to uint64: %w", err) - } - currentSlowRound := genesisspecqbft.Round(roundtimer.QuickTimeoutThreshold) + genesisspecqbft.FirstRound + genesisspecqbft.Round(slowRounds) - return currentSlowRound, nil -} - -func (mv *messageValidator) waitAfterSlotStart(role genesisspectypes.BeaconRole) (time.Duration, error) { - switch role { - case genesisspectypes.BNRoleAttester, genesisspectypes.BNRoleSyncCommittee: - return mv.netCfg.Beacon.SlotDurationSec() / 3, nil - case genesisspectypes.BNRoleAggregator, genesisspectypes.BNRoleSyncCommitteeContribution: - return mv.netCfg.Beacon.SlotDurationSec() / 3 * 2, nil - case genesisspectypes.BNRoleProposer, genesisspectypes.BNRoleValidatorRegistration, genesisspectypes.BNRoleVoluntaryExit: - return 0, nil - default: - return 0, fmt.Errorf("unknown role") - } -} - -func (mv *messageValidator) validRole(roleType genesisspectypes.BeaconRole) bool { - switch roleType { - case genesisspectypes.BNRoleAttester, - genesisspectypes.BNRoleAggregator, - genesisspectypes.BNRoleProposer, - genesisspectypes.BNRoleSyncCommittee, - genesisspectypes.BNRoleSyncCommitteeContribution, - genesisspectypes.BNRoleValidatorRegistration, - genesisspectypes.BNRoleVoluntaryExit: - return true - } - return false -} - -func (mv *messageValidator) validQBFTMsgType(msgType genesisspecqbft.MessageType) bool { - switch msgType { - case genesisspecqbft.ProposalMsgType, genesisspecqbft.PrepareMsgType, genesisspecqbft.CommitMsgType, genesisspecqbft.RoundChangeMsgType: - return true - } - return false -} - -func (mv *messageValidator) validConsensusSigners(share *ssvtypes.SSVShare, m *genesisspecqbft.SignedMessage) error { - switch { - case len(m.Signers) == 0: - return ErrNoSigners - - case len(m.Signers) == 1: - if m.Message.MsgType == genesisspecqbft.ProposalMsgType { - leader := mv.roundRobinProposer(m.Message.Height, m.Message.Round, share) - if m.Signers[0] != leader { - err := ErrSignerNotLeader - err.got = m.Signers[0] - err.want = leader - return err - } - } - - case m.Message.MsgType != genesisspecqbft.CommitMsgType: - e := ErrNonDecidedWithMultipleSigners - e.got = len(m.Signers) - return e - - case !share.HasQuorum(uint64(len(m.Signers))) || len(m.Signers) > len(share.Committee): - e := ErrWrongSignersLength - e.want = fmt.Sprintf("between %v and %v", share.Quorum(), len(share.Committee)) - e.got = len(m.Signers) - return e - } - - if !slices.IsSorted(m.Signers) { - return ErrSignersNotSorted - } - - var prevSigner genesisspectypes.OperatorID - for _, signer := range m.Signers { - if err := mv.commonSignerValidation(signer, share); err != nil { - return err - } - if signer == prevSigner { - return ErrDuplicatedSigner - } - prevSigner = signer - } - return nil -} - -func (mv *messageValidator) roundRobinProposer(height genesisspecqbft.Height, round genesisspecqbft.Round, share *ssvtypes.SSVShare) genesisspectypes.OperatorID { - firstRoundIndex := uint64(0) - if height != genesisspecqbft.FirstHeight { - firstRoundIndex += uint64(height) % uint64(len(share.Committee)) - } - - index := (firstRoundIndex + uint64(round) - uint64(genesisspecqbft.FirstRound)) % uint64(len(share.Committee)) - return share.Committee[index].Signer -} diff --git a/message/validation/genesis/consensus_validation_test.go b/message/validation/genesis/consensus_validation_test.go deleted file mode 100644 index c88fc8d794..0000000000 --- a/message/validation/genesis/consensus_validation_test.go +++ /dev/null @@ -1,106 +0,0 @@ -package validation - -import ( - "testing" - "time" - - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - specqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - "github.com/stretchr/testify/require" - - "github.com/ssvlabs/ssv/protocol/v2/qbft/roundtimer" -) - -func TestMessageValidator_currentEstimatedRound(t *testing.T) { - tt := []struct { - name string - sinceSlotStart time.Duration - want specqbft.Round - }{ - { - name: "0s - expected first round", - sinceSlotStart: 0, - want: specqbft.FirstRound, - }, - { - name: "QuickTimeout/2 - expected first round", - sinceSlotStart: roundtimer.QuickTimeout / 2, - want: specqbft.FirstRound, - }, - { - name: "QuickTimeout - expected first+1 round", - sinceSlotStart: roundtimer.QuickTimeout, - want: specqbft.FirstRound + 1, - }, - { - name: "QuickTimeout*2 - expected first+2 round", - sinceSlotStart: roundtimer.QuickTimeout * 2, - want: specqbft.FirstRound + 2, - }, - { - name: "QuickTimeout*3 - expected first+3 round", - sinceSlotStart: roundtimer.QuickTimeout * 3, - want: specqbft.FirstRound + 3, - }, - { - name: "QuickTimeout*4 - expected first+4 round", - sinceSlotStart: roundtimer.QuickTimeout * 4, - want: specqbft.FirstRound + 4, - }, - { - name: "QuickTimeout*5 - expected first+5 round", - sinceSlotStart: roundtimer.QuickTimeout * 5, - want: specqbft.FirstRound + 5, - }, - { - name: "QuickTimeout*6 - expected first+6 round", - sinceSlotStart: roundtimer.QuickTimeout * 6, - want: specqbft.FirstRound + 6, - }, - { - name: "QuickTimeout*7 - expected first+7 round", - sinceSlotStart: roundtimer.QuickTimeout * 7, - want: specqbft.FirstRound + 7, - }, - { - name: "QuickTimeout*8 - expected first+8 round", - sinceSlotStart: roundtimer.QuickTimeout * 8, - want: specqbft.FirstRound + 8, - }, - { - name: "QuickTimeout*9 - expected first+8 round", - sinceSlotStart: roundtimer.QuickTimeout * time.Duration(roundtimer.QuickTimeoutThreshold+1), - want: genesisspecqbft.Round(roundtimer.QuickTimeoutThreshold) + 1, - }, - { - name: "QuickTimeout*10 - expected first+8 round", - sinceSlotStart: roundtimer.QuickTimeout * time.Duration(roundtimer.QuickTimeoutThreshold+2), - want: genesisspecqbft.Round(roundtimer.QuickTimeoutThreshold) + 1, - }, - { - name: "(QuickTimeout*8 + SlowTimeout) - expected first+9 round", - sinceSlotStart: roundtimer.QuickTimeout*time.Duration(roundtimer.QuickTimeoutThreshold) + roundtimer.SlowTimeout, - want: genesisspecqbft.Round(roundtimer.QuickTimeoutThreshold) + 2, - }, - { - name: "(QuickTimeout*8 + SlowTimeout*2) - expected first+10 round", - sinceSlotStart: roundtimer.QuickTimeout*time.Duration(roundtimer.QuickTimeoutThreshold) + roundtimer.SlowTimeout*2, - want: genesisspecqbft.Round(roundtimer.QuickTimeoutThreshold) + 3, - }, - { - name: "(QuickTimeout*8 + SlowTimeout*3) - expected first+11 round", - sinceSlotStart: roundtimer.QuickTimeout*time.Duration(roundtimer.QuickTimeoutThreshold) + roundtimer.SlowTimeout*3, - want: genesisspecqbft.Round(roundtimer.QuickTimeoutThreshold) + 4, - }, - } - - for _, tc := range tt { - tc := tc - t.Run(tc.name, func(t *testing.T) { - mv := &messageValidator{} - got, err := mv.currentEstimatedRound(tc.sinceSlotStart) - require.NoError(t, err) - require.Equal(t, tc.want, got) - }) - } -} diff --git a/message/validation/genesis/errors.go b/message/validation/genesis/errors.go deleted file mode 100644 index 52a1e5dd7b..0000000000 --- a/message/validation/genesis/errors.go +++ /dev/null @@ -1,100 +0,0 @@ -package validation - -import ( - "fmt" - "strings" -) - -type Error struct { - text string - got any - want any - innerErr error - reject bool - silent bool -} - -func (e Error) Error() string { - var sb strings.Builder - sb.WriteString(e.text) - - if e.got != nil { - sb.WriteString(fmt.Sprintf(", got %v", e.got)) - } - if e.want != nil { - sb.WriteString(fmt.Sprintf(", want %v", e.want)) - } - if e.innerErr != nil { - sb.WriteString(fmt.Sprintf(": %s", e.innerErr.Error())) - } - - return sb.String() -} - -func (e Error) Reject() bool { - return e.reject -} - -func (e Error) Silent() bool { - return e.silent -} - -func (e Error) Text() string { - return e.text -} - -var ( - ErrEmptyData = Error{text: "empty data"} - ErrWrongDomain = Error{text: "wrong domain", silent: true} - ErrNoShareMetadata = Error{text: "share has no metadata"} - ErrUnknownValidator = Error{text: "unknown validator"} - ErrValidatorLiquidated = Error{text: "validator is liquidated"} - ErrValidatorNotAttesting = Error{text: "validator is not attesting"} - ErrSlotAlreadyAdvanced = Error{text: "signer has already advanced to a later slot"} - ErrRoundAlreadyAdvanced = Error{text: "signer has already advanced to a later round"} - ErrRoundTooHigh = Error{text: "round is too high for this role" /*, reject: true*/} // TODO: enable reject - ErrEarlyMessage = Error{text: "early message"} - ErrLateMessage = Error{text: "late message"} - ErrTooManySameTypeMessagesPerRound = Error{text: "too many messages of same type per round"} - ErrSignatureVerification = Error{text: "signature verification", reject: true} - ErrOperatorNotFound = Error{text: "operator not found", reject: true} - ErrPubSubMessageHasNoData = Error{text: "pub-sub message has no data", reject: true} - ErrDecodedPubSubMessageHasEmptyData = Error{text: "pub-sub decoded message has empty data", reject: true} - ErrPubSubDataTooBig = Error{text: "pub-sub message data too big", reject: true} - ErrMalformedPubSubMessage = Error{text: "pub-sub message is malformed", reject: true} - ErrEmptyPubSubMessage = Error{text: "pub-sub message is empty", reject: true} - ErrTopicNotFound = Error{text: "topic not found", reject: true} - ErrSSVDataTooBig = Error{text: "ssv message data too big", reject: true} - ErrInvalidRole = Error{text: "invalid role", reject: true} - ErrUnexpectedConsensusMessage = Error{text: "unexpected consensus message for this role", reject: true} - ErrNoSigners = Error{text: "no signers", reject: true} - ErrWrongSignatureSize = Error{text: "wrong signature size", reject: true} - ErrZeroSignature = Error{text: "zero signature", reject: true} - ErrZeroSigner = Error{text: "zero signer ID", reject: true} - ErrSignerNotInCommittee = Error{text: "signer is not in committee", reject: true} - ErrDuplicatedSigner = Error{text: "signer is duplicated", reject: true} - ErrSignerNotLeader = Error{text: "signer is not leader", reject: true} - ErrSignersNotSorted = Error{text: "signers are not sorted", reject: true} - ErrUnexpectedSigner = Error{text: "signer is not expected", reject: true} - ErrInvalidHash = Error{text: "root doesn't match full data hash", reject: true} - ErrEstimatedRoundTooFar = Error{text: "message round is too far from estimated"} - ErrMalformedMessage = Error{text: "message could not be decoded", reject: true} - ErrMalformedSignedMessage = Error{text: "signed message could not be decoded", reject: true} - ErrUnknownSSVMessageType = Error{text: "unknown SSV message type", reject: true} - ErrUnknownQBFTMessageType = Error{text: "unknown QBFT message type", reject: true} - ErrUnknownPartialMessageType = Error{text: "unknown partial signature message type", reject: true} - ErrPartialSignatureTypeRoleMismatch = Error{text: "partial signature type and role don't match", reject: true} - ErrNonDecidedWithMultipleSigners = Error{text: "non-decided with multiple signers", reject: true} - ErrWrongSignersLength = Error{text: "decided signers size is not between quorum and committee size", reject: true} - ErrDifferentProposalData = Error{text: "different proposal data", reject: true} - ErrEventMessage = Error{text: "event messages are not broadcast", reject: true} - ErrMalformedPrepareJustifications = Error{text: "malformed prepare justifications", reject: true} - ErrUnexpectedPrepareJustifications = Error{text: "prepare justifications unexpected for this message type", reject: true} - ErrMalformedRoundChangeJustifications = Error{text: "malformed round change justifications", reject: true} - ErrUnexpectedRoundChangeJustifications = Error{text: "round change justifications unexpected for this message type", reject: true} - ErrTooManyDutiesPerEpoch = Error{text: "too many duties per epoch", reject: true} - ErrNoDuty = Error{text: "no duty for this epoch", reject: true} - ErrDeserializePublicKey = Error{text: "deserialize public key", reject: true} - ErrNoPartialMessages = Error{text: "no partial messages", reject: true} - ErrDuplicatedPartialSignatureMessage = Error{text: "duplicated partial signature message", reject: true} -) diff --git a/message/validation/genesis/message_counts.go b/message/validation/genesis/message_counts.go deleted file mode 100644 index 8a51796f36..0000000000 --- a/message/validation/genesis/message_counts.go +++ /dev/null @@ -1,158 +0,0 @@ -package validation - -// message_counts.go contains code for counting and validating messages per validator-slot-round. - -import ( - "fmt" - - specqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - spectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" -) - -// MessageCounts tracks the number of various message types received for validation. -type MessageCounts struct { - PreConsensus int - Proposal int - Prepare int - Commit int - Decided int - RoundChange int - PostConsensus int -} - -// String provides a formatted representation of the MessageCounts. -func (c *MessageCounts) String() string { - return fmt.Sprintf("pre-consensus: %v, proposal: %v, prepare: %v, commit: %v, decided: %v, round change: %v, post-consensus: %v", - c.PreConsensus, - c.Proposal, - c.Prepare, - c.Commit, - c.Decided, - c.RoundChange, - c.PostConsensus, - ) -} - -// ValidateConsensusMessage checks if the provided consensus message exceeds the set limits. -// Returns an error if the message type exceeds its respective count limit. -func (c *MessageCounts) ValidateConsensusMessage(msg *specqbft.SignedMessage, limits MessageCounts) error { - switch msg.Message.MsgType { - case specqbft.ProposalMsgType: - if c.Proposal >= limits.Proposal { - err := ErrTooManySameTypeMessagesPerRound - err.got = fmt.Sprintf("proposal, having %v", c.String()) - return err - } - case specqbft.PrepareMsgType: - if c.Prepare >= limits.Prepare { - err := ErrTooManySameTypeMessagesPerRound - err.got = fmt.Sprintf("prepare, having %v", c.String()) - return err - } - case specqbft.CommitMsgType: - if len(msg.Signers) == 1 { - if c.Commit >= limits.Commit { - err := ErrTooManySameTypeMessagesPerRound - err.got = fmt.Sprintf("commit, having %v", c.String()) - return err - } - } else if len(msg.Signers) > 1 { - if c.Decided >= limits.Decided { - err := ErrTooManySameTypeMessagesPerRound - err.got = fmt.Sprintf("decided, having %v", c.String()) - return err - } - } - case specqbft.RoundChangeMsgType: - if c.RoundChange >= limits.RoundChange { - err := ErrTooManySameTypeMessagesPerRound - err.got = fmt.Sprintf("round change, having %v", c.String()) - return err - } - default: - return fmt.Errorf("unexpected signed message type") // should be checked before - } - - return nil -} - -// ValidatePartialSignatureMessage checks if the provided partial signature message exceeds the set limits. -// Returns an error if the message type exceeds its respective count limit. -func (c *MessageCounts) ValidatePartialSignatureMessage(m *spectypes.SignedPartialSignatureMessage, limits MessageCounts) error { - switch m.Message.Type { - case spectypes.RandaoPartialSig, spectypes.SelectionProofPartialSig, spectypes.ContributionProofs, spectypes.ValidatorRegistrationPartialSig, spectypes.VoluntaryExitPartialSig: - if c.PreConsensus >= limits.PreConsensus { - err := ErrTooManySameTypeMessagesPerRound - err.got = fmt.Sprintf("pre-consensus, having %v", c.String()) - return err - } - case spectypes.PostConsensusPartialSig: - if c.PostConsensus >= limits.PostConsensus { - err := ErrTooManySameTypeMessagesPerRound - err.got = fmt.Sprintf("post-consensus, having %v", c.String()) - return err - } - default: - return fmt.Errorf("unexpected partial signature message type") // should be checked before - } - - return nil -} - -// RecordConsensusMessage updates the counts based on the provided consensus message type. -func (c *MessageCounts) RecordConsensusMessage(msg *specqbft.SignedMessage) error { - switch msg.Message.MsgType { - case specqbft.ProposalMsgType: - c.Proposal++ - case specqbft.PrepareMsgType: - c.Prepare++ - case specqbft.CommitMsgType: - switch { - case len(msg.Signers) == 1: - c.Commit++ - case len(msg.Signers) > 1: - c.Decided++ - default: - return fmt.Errorf("expected signers") // 0 length should be checked before - } - case specqbft.RoundChangeMsgType: - c.RoundChange++ - default: - return fmt.Errorf("unexpected signed message type") // should be checked before - } - - return nil -} - -// RecordPartialSignatureMessage updates the counts based on the provided partial signature message type. -func (c *MessageCounts) RecordPartialSignatureMessage(msg *spectypes.SignedPartialSignatureMessage) error { - switch msg.Message.Type { - case spectypes.RandaoPartialSig, spectypes.SelectionProofPartialSig, spectypes.ContributionProofs, spectypes.ValidatorRegistrationPartialSig, spectypes.VoluntaryExitPartialSig: - c.PreConsensus++ - case spectypes.PostConsensusPartialSig: - c.PostConsensus++ - default: - return fmt.Errorf("unexpected partial signature message type") // should be checked before - } - return nil -} - -// maxMessageCounts is the maximum number of acceptable messages from a signer within a slot & round. -func maxMessageCounts(committeeSize int) MessageCounts { - maxDecided := maxDecidedCount(committeeSize) - - return MessageCounts{ - PreConsensus: 1, - Proposal: 1, - Prepare: 1, - Commit: 1, - Decided: maxDecided, - RoundChange: 1, - PostConsensus: 1, - } -} - -func maxDecidedCount(committeeSize int) int { - f := (committeeSize - 1) / 3 - return committeeSize * (f + 1) // N * (f + 1) -} diff --git a/message/validation/genesis/partial_validation.go b/message/validation/genesis/partial_validation.go deleted file mode 100644 index 584658c236..0000000000 --- a/message/validation/genesis/partial_validation.go +++ /dev/null @@ -1,198 +0,0 @@ -package validation - -import ( - "fmt" - "time" - - "github.com/attestantio/go-eth2-client/spec/phase0" - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - - ssvtypes "github.com/ssvlabs/ssv/protocol/v2/types" -) - -// partial_validation.go contains methods for validating partial signature messages - -func (mv *messageValidator) validatePartialSignatureMessage( - share *ssvtypes.SSVShare, - signedMsg *genesisspectypes.SignedPartialSignatureMessage, - msgID genesisspectypes.MessageID, - signatureVerifier func() error, - receivedAt time.Time, -) (phase0.Slot, error) { - msgSlot := signedMsg.Message.Slot - - if !mv.validPartialSigMsgType(signedMsg.Message.Type) { - e := ErrUnknownPartialMessageType - e.got = signedMsg.Message.Type - return msgSlot, e - } - - role := msgID.GetRoleType() - matchesRole, err := mv.partialSignatureTypeMatchesRole(signedMsg.Message.Type, role) - if err != nil { - return msgSlot, err - } - if !matchesRole { - return msgSlot, ErrPartialSignatureTypeRoleMismatch - } - - if err := mv.validatePartialMessages(share, signedMsg); err != nil { - return msgSlot, err - } - - if err := mv.validateSlotTime(msgSlot, role, receivedAt); err != nil { - return msgSlot, err - } - - state := mv.consensusState(msgID) - signerState := state.GetSignerState(signedMsg.Signer) - if signerState != nil { - if err := mv.validateSignerBehaviorPartial(state, signedMsg.Signer, share, msgID, signedMsg); err != nil { - return msgSlot, err - } - } - - if err := mv.validateSignatureFormat(signedMsg.Signature); err != nil { - return msgSlot, err - } - - if signatureVerifier != nil { - if err := signatureVerifier(); err != nil { - return msgSlot, err - } - } - - if signerState == nil { - signerState = state.CreateSignerState(signedMsg.Signer) - } - - if msgSlot > signerState.Slot { - newEpoch := mv.netCfg.Beacon.EstimatedEpochAtSlot(msgSlot) > mv.netCfg.Beacon.EstimatedEpochAtSlot(signerState.Slot) - signerState.ResetSlot(msgSlot, genesisspecqbft.FirstRound, newEpoch) - } - - if err := signerState.MessageCounts.RecordPartialSignatureMessage(signedMsg); err != nil { - return msgSlot, err - } - - return msgSlot, nil -} - -func (mv *messageValidator) validPartialSigMsgType(msgType genesisspectypes.PartialSigMsgType) bool { - switch msgType { - case genesisspectypes.PostConsensusPartialSig, - genesisspectypes.RandaoPartialSig, - genesisspectypes.SelectionProofPartialSig, - genesisspectypes.ContributionProofs, - genesisspectypes.ValidatorRegistrationPartialSig, - genesisspectypes.VoluntaryExitPartialSig: - return true - default: - return false - } -} - -func (mv *messageValidator) partialSignatureTypeMatchesRole(msgType genesisspectypes.PartialSigMsgType, role genesisspectypes.BeaconRole) (bool, error) { - switch role { - case genesisspectypes.BNRoleAttester: - return msgType == genesisspectypes.PostConsensusPartialSig, nil - case genesisspectypes.BNRoleAggregator: - return msgType == genesisspectypes.PostConsensusPartialSig || msgType == genesisspectypes.SelectionProofPartialSig, nil - case genesisspectypes.BNRoleProposer: - return msgType == genesisspectypes.PostConsensusPartialSig || msgType == genesisspectypes.RandaoPartialSig, nil - case genesisspectypes.BNRoleSyncCommittee: - return msgType == genesisspectypes.PostConsensusPartialSig, nil - case genesisspectypes.BNRoleSyncCommitteeContribution: - return msgType == genesisspectypes.PostConsensusPartialSig || msgType == genesisspectypes.ContributionProofs, nil - case genesisspectypes.BNRoleValidatorRegistration: - return msgType == genesisspectypes.ValidatorRegistrationPartialSig, nil - case genesisspectypes.BNRoleVoluntaryExit: - return msgType == genesisspectypes.VoluntaryExitPartialSig, nil - default: - return false, fmt.Errorf("invalid role") // role validity should be checked before - } -} - -func (mv *messageValidator) validatePartialMessages(share *ssvtypes.SSVShare, m *genesisspectypes.SignedPartialSignatureMessage) error { - if err := mv.commonSignerValidation(m.Signer, share); err != nil { - return err - } - - if len(m.Message.Messages) == 0 { - return ErrNoPartialMessages - } - - seen := map[[32]byte]struct{}{} - for _, message := range m.Message.Messages { - if _, ok := seen[message.SigningRoot]; ok { - return ErrDuplicatedPartialSignatureMessage - } - seen[message.SigningRoot] = struct{}{} - - if message.Signer != m.Signer { - err := ErrUnexpectedSigner - err.want = m.Signer - err.got = message.Signer - return err - } - - if err := mv.commonSignerValidation(message.Signer, share); err != nil { - return err - } - - if err := mv.validateSignatureFormat(message.PartialSignature); err != nil { - return err - } - } - - return nil -} - -func (mv *messageValidator) validateSignerBehaviorPartial( - state *ConsensusState, - signer genesisspectypes.OperatorID, - share *ssvtypes.SSVShare, - msgID genesisspectypes.MessageID, - signedMsg *genesisspectypes.SignedPartialSignatureMessage, -) error { - signerState := state.GetSignerState(signer) - - if signerState == nil { - return nil - } - - msgSlot := signedMsg.Message.Slot - - if msgSlot < signerState.Slot { - // Signers aren't allowed to decrease their slot. - // If they've sent a future message due to clock error, - // this should be caught by the earlyMessage check. - err := ErrSlotAlreadyAdvanced - err.want = signerState.Slot - err.got = msgSlot - return err - } - - newDutyInSameEpoch := false - if msgSlot > signerState.Slot && mv.netCfg.Beacon.EstimatedEpochAtSlot(msgSlot) == mv.netCfg.Beacon.EstimatedEpochAtSlot(signerState.Slot) { - newDutyInSameEpoch = true - } - - if err := mv.validateBeaconDuty(msgID.GetRoleType(), msgSlot, share); err != nil { - return err - } - - if err := mv.validateDutyCount(signerState, msgID, newDutyInSameEpoch); err != nil { - return err - } - - if msgSlot <= signerState.Slot { - limits := maxMessageCounts(len(share.Committee)) - if err := signerState.MessageCounts.ValidatePartialSignatureMessage(signedMsg, limits); err != nil { - return err - } - } - - return nil -} diff --git a/message/validation/genesis/rsa.go b/message/validation/genesis/rsa.go deleted file mode 100644 index 939e07c34a..0000000000 --- a/message/validation/genesis/rsa.go +++ /dev/null @@ -1,45 +0,0 @@ -package validation - -import ( - "fmt" - - spectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - - "github.com/ssvlabs/ssv/operator/keys" -) - -func (mv *messageValidator) verifySignature(msg *spectypes.SignedSSVMessage) error { - operatorID := msg.GetOperatorID() - operatorPubKey, ok := mv.operatorIDToPubkeyCache.Get(operatorID) - if !ok { - operator, found, err := mv.nodeStorage.GetOperatorData(nil, operatorID) - if err != nil { - e := ErrOperatorNotFound - e.got = operatorID - e.innerErr = err - return e - } - if !found { - e := ErrOperatorNotFound - e.got = operatorID - return e - } - - operatorPubKey, err = keys.PublicKeyFromString(string(operator.PublicKey)) - if err != nil { - e := ErrSignatureVerification - e.innerErr = fmt.Errorf("decode public key: %w", err) - return e - } - - mv.operatorIDToPubkeyCache.Set(operatorID, operatorPubKey) - } - - if err := operatorPubKey.Verify(msg.GetData(), msg.Signature[:]); err != nil { - e := ErrSignatureVerification - e.innerErr = fmt.Errorf("verify opid: %v signature: %w", operatorID, err) - return e - } - - return nil -} diff --git a/message/validation/genesis/signer_state.go b/message/validation/genesis/signer_state.go deleted file mode 100644 index d0a94e5ab5..0000000000 --- a/message/validation/genesis/signer_state.go +++ /dev/null @@ -1,45 +0,0 @@ -package validation - -// signer_state.go describes state of a signer. - -import ( - "time" - - "github.com/attestantio/go-eth2-client/spec/phase0" - specqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" -) - -// SignerState represents the state of a signer, including its start time, slot, round, -// message counts, proposal data, and the number of duties performed in the current epoch. -type SignerState struct { - Start time.Time - Slot phase0.Slot - Round specqbft.Round - MessageCounts MessageCounts - ProposalData []byte // used only to check different proposal data - EpochDuties int -} - -// ResetSlot resets the state's slot, round, message counts, and proposal data to the given values. -// It also updates the start time to the current time and increments the epoch duties count if it's a new epoch. -func (s *SignerState) ResetSlot(slot phase0.Slot, round specqbft.Round, newEpoch bool) { - s.Start = time.Now() - s.Slot = slot - s.Round = round - s.MessageCounts = MessageCounts{} - s.ProposalData = nil - if newEpoch { - s.EpochDuties = 1 - } else { - s.EpochDuties++ - } -} - -// Reset resets the state's round, message counts, and proposal data to the given values. -// It also updates the start time to the current time. -func (s *SignerState) Reset(round specqbft.Round) { - s.Start = time.Now() - s.Round = round - s.MessageCounts = MessageCounts{} - s.ProposalData = nil -} diff --git a/message/validation/genesis/utils_test.go b/message/validation/genesis/utils_test.go deleted file mode 100644 index 0931fe762e..0000000000 --- a/message/validation/genesis/utils_test.go +++ /dev/null @@ -1,197 +0,0 @@ -package validation - -import ( - "fmt" - "testing" - - specqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - spectypes "github.com/ssvlabs/ssv-spec/types" - "github.com/stretchr/testify/require" -) - -func TestRecordConsensusMessage(t *testing.T) { - tt := []struct { - name string - msg *specqbft.SignedMessage - initialCounts MessageCounts - expectedCounts MessageCounts - expectedError error - }{ - { - name: "ProposalMessage_IncrementsProposalCount", - msg: &specqbft.SignedMessage{ - Message: specqbft.Message{MsgType: specqbft.ProposalMsgType}, - }, - initialCounts: MessageCounts{}, - expectedCounts: MessageCounts{Proposal: 1}, - expectedError: nil, - }, - { - name: "PrepareMessage_IncrementsPrepareCount", - msg: &specqbft.SignedMessage{ - Message: specqbft.Message{MsgType: specqbft.PrepareMsgType}, - }, - initialCounts: MessageCounts{}, - expectedCounts: MessageCounts{Prepare: 1}, - expectedError: nil, - }, - { - name: "CommitMessageWithSingleOperator_IncrementsCommitCount", - msg: &specqbft.SignedMessage{ - Message: specqbft.Message{MsgType: specqbft.CommitMsgType}, - Signers: []spectypes.OperatorID{1}, - }, - initialCounts: MessageCounts{}, - expectedCounts: MessageCounts{Commit: 1}, - expectedError: nil, - }, - { - name: "CommitMessageWithMultipleOperators_IncrementsDecidedCount", - msg: &specqbft.SignedMessage{ - Message: specqbft.Message{MsgType: specqbft.CommitMsgType}, - Signers: []spectypes.OperatorID{1, 2}, - }, - initialCounts: MessageCounts{}, - expectedCounts: MessageCounts{Decided: 1}, - expectedError: nil, - }, - { - name: "RoundChangeMessage_IncrementsRoundChangeCount", - msg: &specqbft.SignedMessage{ - Message: specqbft.Message{MsgType: specqbft.RoundChangeMsgType}, - }, - initialCounts: MessageCounts{}, - expectedCounts: MessageCounts{RoundChange: 1}, - expectedError: nil, - }, - { - name: "UnexpectedMessageType_ReturnsError", - msg: &specqbft.SignedMessage{ - Message: specqbft.Message{MsgType: specqbft.MessageType(12345)}, - }, - initialCounts: MessageCounts{}, - expectedCounts: MessageCounts{}, - expectedError: fmt.Errorf("unexpected signed message type"), - }, - } - - for _, tc := range tt { - t.Run(tc.name, func(t *testing.T) { - counts := tc.initialCounts - err := counts.RecordConsensusMessage(tc.msg) - - if tc.expectedError != nil { - require.EqualError(t, err, tc.expectedError.Error()) - } else { - require.NoError(t, err) - } - - require.Equal(t, tc.expectedCounts, counts) - }) - } -} - -func TestValidateConsensusMessage(t *testing.T) { - type input struct { - signedSSVMessage *specqbft.SignedMessage - counts *MessageCounts - limits MessageCounts - } - - tt := []struct { - name string - input input - expectedError error - }{ - { - name: "ProposalMessage_ExceedsLimit_ReturnsError", - input: input{ - counts: &MessageCounts{Proposal: 2}, - signedSSVMessage: &specqbft.SignedMessage{ - Message: specqbft.Message{MsgType: specqbft.ProposalMsgType}, - }, - limits: MessageCounts{Proposal: 1}, - }, - expectedError: fmt.Errorf("too many messages of same type per round, got proposal, having pre-consensus: 0, proposal: 2, prepare: 0, commit: 0, decided: 0, round change: 0, post-consensus: 0"), - }, - { - name: "PrepareMessage_ExceedsLimit_ReturnsError", - input: input{ - counts: &MessageCounts{Prepare: 2}, - signedSSVMessage: &specqbft.SignedMessage{ - Message: specqbft.Message{MsgType: specqbft.PrepareMsgType}, - }, - limits: MessageCounts{Prepare: 1}, - }, - expectedError: fmt.Errorf("too many messages of same type per round, got prepare, having pre-consensus: 0, proposal: 0, prepare: 2, commit: 0, decided: 0, round change: 0, post-consensus: 0"), - }, - { - name: "CommitMessageWithSingleOperator_ExceedsLimit_ReturnsError", - input: input{ - counts: &MessageCounts{Commit: 2}, - signedSSVMessage: &specqbft.SignedMessage{ - Message: specqbft.Message{MsgType: specqbft.CommitMsgType}, - Signers: []spectypes.OperatorID{1}, - }, - limits: MessageCounts{Commit: 0}, - }, - expectedError: fmt.Errorf("too many messages of same type per round, got commit, having pre-consensus: 0, proposal: 0, prepare: 0, commit: 2, decided: 0, round change: 0, post-consensus: 0"), - }, - { - name: "CommitMessageWithManyOperators_ExceedsLimit_ReturnsError", - input: input{ - counts: &MessageCounts{Commit: 2}, - signedSSVMessage: &specqbft.SignedMessage{ - Message: specqbft.Message{MsgType: specqbft.CommitMsgType}, - Signers: []spectypes.OperatorID{1, 2, 3}, - }, - limits: MessageCounts{Commit: 1}, - }, - expectedError: fmt.Errorf("too many messages of same type per round, got decided, having pre-consensus: 0, proposal: 0, prepare: 0, commit: 2, decided: 0, round change: 0, post-consensus: 0"), - }, - { - name: "RoundChangeMessage_ExceedsLimit_ReturnsError", - input: input{ - counts: &MessageCounts{RoundChange: 2}, - signedSSVMessage: &specqbft.SignedMessage{ - Message: specqbft.Message{MsgType: specqbft.RoundChangeMsgType}, - }, - limits: MessageCounts{RoundChange: 1}, - }, - expectedError: fmt.Errorf("too many messages of same type per round, got round change, having pre-consensus: 0, proposal: 0, prepare: 0, commit: 0, decided: 0, round change: 2, post-consensus: 0"), - }, - { - name: "UnexpectedMessageType_ReturnsError", - input: input{ - counts: &MessageCounts{}, - signedSSVMessage: &specqbft.SignedMessage{Message: specqbft.Message{MsgType: specqbft.MessageType(12345)}}, - limits: MessageCounts{}, - }, - expectedError: fmt.Errorf("unexpected signed message type"), - }, - { - name: "ValidProposalMessage_HappyFlow", - input: input{ - counts: &MessageCounts{Proposal: 2}, - signedSSVMessage: &specqbft.SignedMessage{ - Message: specqbft.Message{MsgType: specqbft.ProposalMsgType}, - }, - limits: MessageCounts{Proposal: 100}, - }, - expectedError: nil, - }, - } - - for _, tc := range tt { - t.Run(tc.name, func(t *testing.T) { - - err := tc.input.counts.ValidateConsensusMessage(tc.input.signedSSVMessage, tc.input.limits) - - if tc.expectedError != nil { - require.EqualError(t, err, tc.expectedError.Error()) - } else { - require.NoError(t, err) - } - }) - } -} diff --git a/message/validation/genesis/validation.go b/message/validation/genesis/validation.go deleted file mode 100644 index b401f8c898..0000000000 --- a/message/validation/genesis/validation.go +++ /dev/null @@ -1,581 +0,0 @@ -// Package validation provides functions and structures for validating messages. -package validation - -// validator.go contains main code for validation and most of the rule checks. - -import ( - "bytes" - "context" - "encoding/hex" - "fmt" - "slices" - "strings" - "sync" - "time" - - "github.com/attestantio/go-eth2-client/spec/phase0" - pubsub "github.com/libp2p/go-libp2p-pubsub" - "github.com/libp2p/go-libp2p/core/peer" - "github.com/pkg/errors" - specqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - spectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - alanspecqbft "github.com/ssvlabs/ssv-spec/qbft" - alanspectypes "github.com/ssvlabs/ssv-spec/types" - "go.uber.org/zap" - "go.uber.org/zap/zapcore" - - "github.com/ssvlabs/ssv/utils/hashmap" - - "github.com/ssvlabs/ssv/logging/fields" - "github.com/ssvlabs/ssv/monitoring/metricsreporter" - "github.com/ssvlabs/ssv/network/commons" - "github.com/ssvlabs/ssv/networkconfig" - "github.com/ssvlabs/ssv/operator/duties/dutystore" - "github.com/ssvlabs/ssv/operator/keys" - operatorstorage "github.com/ssvlabs/ssv/operator/storage" - "github.com/ssvlabs/ssv/protocol/genesis/ssv/genesisqueue" - ssvmessage "github.com/ssvlabs/ssv/protocol/v2/message" - ssvtypes "github.com/ssvlabs/ssv/protocol/v2/types" -) - -const ( - // lateMessageMargin is the duration past a message's TTL in which it is still considered valid. - lateMessageMargin = time.Second * 3 - - // clockErrorTolerance is the maximum amount of clock error we expect to see between nodes. - clockErrorTolerance = time.Millisecond * 50 - - maxMessageSize = maxConsensusMsgSize - maxConsensusMsgSize = 6291829 - maxPartialSignatureMsgSize = 1952 - allowedRoundsInFuture = 1 - allowedRoundsInPast = 2 - lateSlotAllowance = 2 - signatureSize = 96 - maxDutiesPerEpoch = 2 -) - -// MessageValidator is an interface that combines both PubsubMessageValidator and SSVMessageValidator. -type MessageValidator interface { - ValidatorForTopic(topic string) func(ctx context.Context, p peer.ID, pmsg *pubsub.Message) pubsub.ValidationResult - Validate(ctx context.Context, p peer.ID, pmsg *pubsub.Message) pubsub.ValidationResult -} - -type messageValidator struct { - logger *zap.Logger - metrics metricsreporter.MetricsReporter - netCfg networkconfig.NetworkConfig - index sync.Map - nodeStorage operatorstorage.Storage - dutyStore *dutystore.Store - operatorIDToPubkeyCache *hashmap.Map[spectypes.OperatorID, keys.OperatorPublicKey] - - // validationLocks is a map of lock per SSV message ID to - // prevent concurrent access to the same state. - validationLocks map[spectypes.MessageID]*sync.Mutex - validationMutex sync.Mutex - - selfPID peer.ID - selfAccept bool -} - -// New returns a new MessageValidator with the given network configuration and options. -func New(netCfg networkconfig.NetworkConfig, opts ...Option) MessageValidator { - mv := &messageValidator{ - logger: zap.NewNop(), - metrics: metricsreporter.NewNop(), - netCfg: netCfg, - operatorIDToPubkeyCache: hashmap.New[spectypes.OperatorID, keys.OperatorPublicKey](), - validationLocks: make(map[spectypes.MessageID]*sync.Mutex), - } - - for _, opt := range opts { - opt(mv) - } - - return mv -} - -// Option represents a functional option for configuring a messageValidator. -type Option func(validator *messageValidator) - -// WithLogger sets the logger for the messageValidator. -func WithLogger(logger *zap.Logger) Option { - return func(mv *messageValidator) { - mv.logger = logger - } -} - -// WithMetrics sets the metrics for the messageValidator. -func WithMetrics(metrics metricsreporter.MetricsReporter) Option { - return func(mv *messageValidator) { - mv.metrics = metrics - } -} - -// WithDutyStore sets the duty store for the messageValidator. -func WithDutyStore(dutyStore *dutystore.Store) Option { - return func(mv *messageValidator) { - mv.dutyStore = dutyStore - } -} - -// WithNodeStorage sets the node storage for the messageValidator. -func WithNodeStorage(nodeStorage operatorstorage.Storage) Option { - return func(mv *messageValidator) { - mv.nodeStorage = nodeStorage - } -} - -// WithSelfAccept blindly accepts messages sent from self. Useful for testing. -func WithSelfAccept(selfPID peer.ID, selfAccept bool) Option { - return func(mv *messageValidator) { - mv.selfPID = selfPID - mv.selfAccept = selfAccept - } -} - -// ConsensusDescriptor provides details about the consensus for a message. It's used for logging and metrics. -type ConsensusDescriptor struct { - Round specqbft.Round - QBFTMessageType specqbft.MessageType - Signers []spectypes.OperatorID - Committee []*alanspectypes.ShareMember -} - -// Descriptor provides details about a message. It's used for logging and metrics. -type Descriptor struct { - ValidatorPK spectypes.ValidatorPK - Role spectypes.BeaconRole - SSVMessageType spectypes.MsgType - Slot phase0.Slot - Consensus *ConsensusDescriptor -} - -// Fields returns zap logging fields for the descriptor. -func (d Descriptor) Fields() []zapcore.Field { - result := []zapcore.Field{ - fields.Validator(d.ValidatorPK), - //fields.Role(d.Role), - //zap.String("ssv_message_type", ssvmessage.MsgTypeToString(d.SSVMessageType)), - fields.Slot(d.Slot), - } - - if d.Consensus != nil { - var committee []spectypes.OperatorID - for _, o := range d.Consensus.Committee { - committee = append(committee, o.Signer) - } - - result = append(result, - //fields.Round(d.Consensus.Round), - //zap.String("qbft_message_type", ssvmessage.QBFTMsgTypeToString(d.Consensus.QBFTMessageType)), - zap.Uint64s("signers", d.Consensus.Signers), - zap.Uint64s("committee", committee), - ) - } - - return result -} - -// String provides a string representation of the descriptor. It may be useful for logging. -func (d Descriptor) String() string { - sb := strings.Builder{} - sb.WriteString(fmt.Sprintf("validator PK: %v, role: %v, ssv message type: %v, slot: %v", - hex.EncodeToString(d.ValidatorPK), - d.Role.String(), - ssvmessage.MsgTypeToString(alanspectypes.MsgType(d.SSVMessageType)), - d.Slot, - )) - - if d.Consensus != nil { - var committee []spectypes.OperatorID - for _, o := range d.Consensus.Committee { - committee = append(committee, o.Signer) - } - - sb.WriteString(fmt.Sprintf(", round: %v, qbft message type: %v, signers: %v, committee: %v", - d.Consensus.Round, - ssvmessage.QBFTMsgTypeToString(alanspecqbft.MessageType(d.Consensus.QBFTMessageType)), - d.Consensus.Signers, - committee, - )) - } - - return sb.String() -} - -// ValidatorForTopic returns a validation function for the given topic. -// This function can be used to validate messages within the libp2p pubsub framework. -func (mv *messageValidator) ValidatorForTopic(_ string) func(ctx context.Context, p peer.ID, pmsg *pubsub.Message) pubsub.ValidationResult { - return mv.Validate -} - -// Validate validates the given pubsub message. -// Depending on the outcome, it will return one of the pubsub validation results (Accept, Ignore, or Reject). -func (mv *messageValidator) Validate(_ context.Context, peerID peer.ID, pmsg *pubsub.Message) pubsub.ValidationResult { - if mv.selfAccept && peerID == mv.selfPID { - signedSSVMsg := &spectypes.SignedSSVMessage{} - if err := signedSSVMsg.Decode(pmsg.GetData()); err != nil { - mv.logger.Error("failed to decode signed ssv message", zap.Error(err)) - return pubsub.ValidationReject - } - - msg, err := signedSSVMsg.GetSSVMessageFromData() - if err != nil { - mv.logger.Error("failed to decode network message", zap.Error(err)) - return pubsub.ValidationReject - } - - // skipping the error check for testing simplifying - decMsg, _ := genesisqueue.DecodeGenesisSSVMessage(msg) - pmsg.ValidatorData = decMsg - return pubsub.ValidationAccept - } - - start := time.Now() - var validationDurationLabels []string // TODO: implement - - defer func() { - sinceStart := time.Since(start) - mv.metrics.MessageValidationDuration(sinceStart, validationDurationLabels...) - }() - - decodedMessage, descriptor, err := mv.validateP2PMessage(pmsg, time.Now()) - round := specqbft.Round(0) - if descriptor.Consensus != nil { - round = descriptor.Consensus.Round - } - - f := append(descriptor.Fields(), fields.PeerID(peerID)) - - if err != nil { - var valErr Error - if errors.As(err, &valErr) { - if valErr.Reject() { - if !valErr.Silent() { - f = append(f, zap.Error(err)) - mv.logger.Debug("rejecting invalid message", f...) - } - - mv.metrics.GenesisMessageRejected(valErr.Text(), descriptor.Role, round) - return pubsub.ValidationReject - } - - if !valErr.Silent() { - f = append(f, zap.Error(err)) - mv.logger.Debug("ignoring invalid message", f...) - } - mv.metrics.GenesisMessageIgnored(valErr.Text(), descriptor.Role, round) - return pubsub.ValidationIgnore - } - - mv.metrics.GenesisMessageIgnored(err.Error(), descriptor.Role, round) - f = append(f, zap.Error(err)) - mv.logger.Debug("ignoring invalid message", f...) - return pubsub.ValidationIgnore - } - - pmsg.ValidatorData = decodedMessage - - mv.metrics.GenesisMessageAccepted(descriptor.Role, round) - - return pubsub.ValidationAccept -} - -// ValidateSSVMessage validates the given SSV message. -// If successful, it returns the decoded message and its descriptor. Otherwise, it returns an error. -func (mv *messageValidator) ValidateSSVMessage(ssvMessage *genesisqueue.GenesisSSVMessage) (*genesisqueue.GenesisSSVMessage, Descriptor, error) { - return mv.validateSSVMessage(ssvMessage, time.Now(), nil) -} - -func (mv *messageValidator) validateP2PMessage(pMsg *pubsub.Message, receivedAt time.Time) (*genesisqueue.GenesisSSVMessage, Descriptor, error) { - topic := pMsg.GetTopic() - - mv.metrics.ActiveMsgValidation(topic) - mv.metrics.MessagesReceivedFromPeer(pMsg.ReceivedFrom) - mv.metrics.MessagesReceivedTotal() - - defer mv.metrics.ActiveMsgValidationDone(topic) - - encMessageData := pMsg.GetData() - - if len(encMessageData) == 0 { - return nil, Descriptor{}, ErrPubSubMessageHasNoData - } - - signedSSVMsg := &spectypes.SignedSSVMessage{} - if err := signedSSVMsg.Decode(encMessageData); err != nil { - e := ErrMalformedSignedMessage - e.innerErr = err - return nil, Descriptor{}, e - } - - messageData := signedSSVMsg.GetData() - if len(messageData) == 0 { - return nil, Descriptor{}, ErrDecodedPubSubMessageHasEmptyData - } - - signatureVerifier := func() error { - mv.metrics.MessageValidationRSAVerifications() - return mv.verifySignature(signedSSVMsg) - } - - mv.metrics.MessageSize(len(messageData)) - - // Max possible MsgType + MsgID + Data plus 10% for encoding overhead - const maxMsgSize = 4 + 56 + 8388668 - const maxEncodedMsgSize = maxMsgSize + maxMsgSize/10 - if len(messageData) > maxEncodedMsgSize { - e := ErrPubSubDataTooBig - e.got = len(messageData) - return nil, Descriptor{}, e - } - - msg, err := genesisqueue.DecodeGenesisSignedSSVMessage(signedSSVMsg) - if err != nil { - if errors.Is(err, genesisqueue.ErrDecodeNetworkMsg) { - e := ErrMalformedPubSubMessage - e.innerErr = err - return nil, Descriptor{}, e - } - if errors.Is(err, genesisqueue.ErrUnknownMessageType) { - e := ErrUnknownSSVMessageType - e.innerErr = err - return nil, Descriptor{}, e - } - e := ErrMalformedMessage - e.innerErr = err - return nil, Descriptor{}, e - } - - if msg == nil { - return nil, Descriptor{}, ErrEmptyPubSubMessage - } - - // Check if the message was sent on the right topic. - currentTopic := pMsg.GetTopic() - currentTopicBaseName := commons.GetTopicBaseName(currentTopic) - topics := commons.ValidatorTopicID(msg.GetID().GetPubKey()) - - topicFound := false - for _, tp := range topics { - if tp == currentTopicBaseName { - topicFound = true - break - } - } - if !topicFound { - return nil, Descriptor{}, ErrTopicNotFound - } - - mv.metrics.GenesisSSVMessageType(msg.MsgType) - - return mv.validateSSVMessage(msg, receivedAt, signatureVerifier) -} - -func (mv *messageValidator) validateSSVMessage(msg *genesisqueue.GenesisSSVMessage, receivedAt time.Time, signatureVerifier func() error) (*genesisqueue.GenesisSSVMessage, Descriptor, error) { - var descriptor Descriptor - ssvMessage := msg.SSVMessage - - if len(ssvMessage.Data) == 0 { - return nil, descriptor, ErrEmptyData - } - - if len(ssvMessage.Data) > maxMessageSize { - err := ErrSSVDataTooBig - err.got = len(ssvMessage.Data) - err.want = maxMessageSize - return nil, descriptor, err - } - domain := mv.netCfg.DomainType() - if !bytes.Equal(ssvMessage.MsgID.GetDomain(), domain[:]) { - err := ErrWrongDomain - err.got = hex.EncodeToString(ssvMessage.MsgID.GetDomain()) - err.want = hex.EncodeToString(domain[:]) - return nil, descriptor, err - } - - validatorPK := ssvMessage.GetID().GetPubKey() - role := ssvMessage.GetID().GetRoleType() - descriptor.Role = role - descriptor.ValidatorPK = validatorPK - - if !mv.validRole(role) { - return nil, descriptor, ErrInvalidRole - } - - publicKey, err := ssvtypes.DeserializeBLSPublicKey(validatorPK) - if err != nil { - e := ErrDeserializePublicKey - e.innerErr = err - return nil, descriptor, e - } - - var share *ssvtypes.SSVShare - var exists bool - if mv.nodeStorage != nil { - shareStorage := mv.nodeStorage.Shares() - share, exists = shareStorage.Get(nil, publicKey.Serialize()) - if !exists { - e := ErrUnknownValidator - e.got = publicKey.SerializeToHexStr() - return nil, descriptor, e - } - - if share.Liquidated { - return nil, descriptor, ErrValidatorLiquidated - } - - if share.BeaconMetadata == nil { - return nil, descriptor, ErrNoShareMetadata - } - - if !share.IsAttesting(mv.netCfg.Beacon.EstimatedCurrentEpoch()) { - err := ErrValidatorNotAttesting - err.got = share.BeaconMetadata.Status.String() - return nil, descriptor, err - } - } - - // Lock this SSV message ID to prevent concurrent access to the same state. - mv.validationMutex.Lock() - mutex, ok := mv.validationLocks[msg.GetID()] - if !ok { - mutex = &sync.Mutex{} - mv.validationLocks[msg.GetID()] = mutex - } - mutex.Lock() - defer mutex.Unlock() - mv.validationMutex.Unlock() - - descriptor.SSVMessageType = ssvMessage.MsgType - - if mv.nodeStorage != nil { - switch ssvMessage.MsgType { - case spectypes.SSVConsensusMsgType: - if len(ssvMessage.Data) > maxConsensusMsgSize { - e := ErrSSVDataTooBig - e.got = len(ssvMessage.Data) - e.want = maxConsensusMsgSize - return nil, descriptor, e - } - - signedMessage := msg.Body.(*specqbft.SignedMessage) - consensusDescriptor, slot, err := mv.validateConsensusMessage(share, signedMessage, msg.GetID(), receivedAt, signatureVerifier) - descriptor.Consensus = &consensusDescriptor - descriptor.Slot = slot - if err != nil { - return nil, descriptor, err - } - - case spectypes.SSVPartialSignatureMsgType: - if len(ssvMessage.Data) > maxPartialSignatureMsgSize { - e := ErrSSVDataTooBig - e.got = len(ssvMessage.Data) - e.want = maxPartialSignatureMsgSize - return nil, descriptor, e - } - - partialSignatureMessage := msg.Body.(*spectypes.SignedPartialSignatureMessage) - slot, err := mv.validatePartialSignatureMessage(share, partialSignatureMessage, msg.GetID(), signatureVerifier, receivedAt) - descriptor.Slot = slot - if err != nil { - return nil, descriptor, err - } - - case spectypes.MsgType(ssvmessage.SSVEventMsgType): - return nil, descriptor, ErrEventMessage - - default: - return nil, descriptor, ErrUnknownSSVMessageType - } - } - - return msg, descriptor, nil -} - -func (mv *messageValidator) containsSignerFunc(signer spectypes.OperatorID) func(shareMember *alanspectypes.ShareMember) bool { - return func(shareMember *alanspectypes.ShareMember) bool { - return shareMember.Signer == signer - } -} - -func (mv *messageValidator) validateSignatureFormat(signature []byte) error { - if len(signature) != signatureSize { - e := ErrWrongSignatureSize - e.got = len(signature) - return e - } - - if [signatureSize]byte(signature) == [signatureSize]byte{} { - return ErrZeroSignature - } - return nil -} - -func (mv *messageValidator) commonSignerValidation(signer spectypes.OperatorID, share *ssvtypes.SSVShare) error { - if signer == 0 { - return ErrZeroSigner - } - - if !slices.ContainsFunc(share.Committee, mv.containsSignerFunc(signer)) { - return ErrSignerNotInCommittee - } - - return nil -} - -func (mv *messageValidator) validateSlotTime(messageSlot phase0.Slot, role spectypes.BeaconRole, receivedAt time.Time) error { - if mv.earlyMessage(messageSlot, receivedAt) { - return ErrEarlyMessage - } - - if lateness := mv.lateMessage(messageSlot, role, receivedAt); lateness > 0 { - e := ErrLateMessage - e.got = fmt.Sprintf("late by %v", lateness) - return e - } - - return nil -} - -func (mv *messageValidator) earlyMessage(slot phase0.Slot, receivedAt time.Time) bool { - return mv.netCfg.Beacon.GetSlotEndTime(mv.netCfg.Beacon.EstimatedSlotAtTime(receivedAt.Unix())). - Add(-clockErrorTolerance).Before(mv.netCfg.Beacon.GetSlotStartTime(slot)) -} - -func (mv *messageValidator) lateMessage(slot phase0.Slot, role spectypes.BeaconRole, receivedAt time.Time) time.Duration { - var ttl phase0.Slot - switch role { - case spectypes.BNRoleProposer, spectypes.BNRoleSyncCommittee, spectypes.BNRoleSyncCommitteeContribution: - ttl = 1 + lateSlotAllowance - case spectypes.BNRoleAttester, spectypes.BNRoleAggregator: - ttl = 32 + lateSlotAllowance - case spectypes.BNRoleValidatorRegistration, spectypes.BNRoleVoluntaryExit: - return 0 - } - - deadline := mv.netCfg.Beacon.GetSlotStartTime(slot + ttl). - Add(lateMessageMargin).Add(clockErrorTolerance) - - return mv.netCfg.Beacon.GetSlotStartTime(mv.netCfg.Beacon.EstimatedSlotAtTime(receivedAt.Unix())). - Sub(deadline) -} - -func (mv *messageValidator) consensusState(messageID spectypes.MessageID) *ConsensusState { - id := ConsensusID{ - PubKey: phase0.BLSPubKey(messageID.GetPubKey()), - Role: messageID.GetRoleType(), - } - - if _, ok := mv.index.Load(id); !ok { - cs := &ConsensusState{ - Signers: hashmap.New[spectypes.OperatorID, *SignerState](), - } - mv.index.Store(id, cs) - } - - cs, _ := mv.index.Load(id) - return cs.(*ConsensusState) -} diff --git a/message/validation/genesis/validation_test.go b/message/validation/genesis/validation_test.go deleted file mode 100644 index 82585fbe02..0000000000 --- a/message/validation/genesis/validation_test.go +++ /dev/null @@ -1,2477 +0,0 @@ -package validation - -import ( - "bytes" - "encoding/hex" - "math" - "testing" - "time" - - eth2apiv1 "github.com/attestantio/go-eth2-client/api/v1" - "github.com/attestantio/go-eth2-client/spec/phase0" - "github.com/ethereum/go-ethereum/common" - "github.com/herumi/bls-eth-go-binary/bls" - pubsub "github.com/libp2p/go-libp2p-pubsub" - pspb "github.com/libp2p/go-libp2p-pubsub/pb" - specqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - spectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - spectestingutils "github.com/ssvlabs/ssv-spec-pre-cc/types/testingutils" - alanspectypes "github.com/ssvlabs/ssv-spec/types" - alanspectestingutils "github.com/ssvlabs/ssv-spec/types/testingutils" - "github.com/stretchr/testify/require" - eth2types "github.com/wealdtech/go-eth2-types/v2" - "go.uber.org/zap/zaptest" - - "github.com/ssvlabs/ssv/network/commons" - "github.com/ssvlabs/ssv/networkconfig" - "github.com/ssvlabs/ssv/operator/duties/dutystore" - "github.com/ssvlabs/ssv/operator/keys" - "github.com/ssvlabs/ssv/operator/storage" - "github.com/ssvlabs/ssv/protocol/genesis/ssv/genesisqueue" - beaconprotocol "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon" - ssvmessage "github.com/ssvlabs/ssv/protocol/v2/message" - ssvtypes "github.com/ssvlabs/ssv/protocol/v2/types" - registrystorage "github.com/ssvlabs/ssv/registry/storage" - "github.com/ssvlabs/ssv/storage/basedb" - "github.com/ssvlabs/ssv/storage/kv" -) - -func Test_ValidateSSVMessage(t *testing.T) { - logger := zaptest.NewLogger(t) - db, err := kv.NewInMemory(logger, basedb.Options{}) - require.NoError(t, err) - - ns, err := storage.NewNodeStorage(logger, db) - require.NoError(t, err) - - const validatorIndex = 123 - - ks := alanspectestingutils.Testing4SharesSet() - share := &ssvtypes.SSVShare{ - Share: *alanspectestingutils.TestingShare(ks, spectestingutils.TestingValidatorIndex), - Metadata: ssvtypes.Metadata{ - BeaconMetadata: &beaconprotocol.ValidatorMetadata{ - Status: eth2apiv1.ValidatorStateActiveOngoing, - Index: validatorIndex, - }, - Liquidated: false, - }, - } - require.NoError(t, ns.Shares().Save(nil, share)) - - netCfg := networkconfig.TestNetwork - - roleAttester := spectypes.BNRoleAttester - - // Message validation happy flow, messages are not ignored or rejected and there are no errors - t.Run("happy flow", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - slot := netCfg.Beacon.FirstSlotAtEpoch(1) - height := specqbft.Height(slot) - - validSignedMessage := spectestingutils.TestingProposalMessageWithHeight(ks.Shares[1], 1, height) - encodedValidSignedMessage, err := validSignedMessage.Encode() - require.NoError(t, err) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester), - Data: encodedValidSignedMessage, - } - - message, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg) - require.NoError(t, err) - - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait) - _, _, err = validator.validateSSVMessage(message, receivedAt, nil) - require.NoError(t, err) - }) - - // Make sure messages are incremented and throw an ignore message if more than 1 for a commit - t.Run("message counts", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - slot := netCfg.Beacon.FirstSlotAtEpoch(1) - height := specqbft.Height(slot) - - msgID := spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester) - state := validator.consensusState(msgID) - for i := spectypes.OperatorID(1); i <= 4; i++ { - signerState := state.GetSignerState(i) - require.Nil(t, signerState) - } - - signedMsg := spectestingutils.TestingProposalMessageWithHeight(ks.Shares[1], 1, height) - encodedMsg, err := signedMsg.Encode() - require.NoError(t, err) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: msgID, - Data: encodedMsg, - } - - message, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg) - require.NoError(t, err) - - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait) - _, _, err = validator.validateSSVMessage(message, receivedAt, nil) - require.NoError(t, err) - - _, _, err = validator.validateSSVMessage(message, receivedAt, nil) - require.ErrorContains(t, err, ErrTooManySameTypeMessagesPerRound.Error()) - - state1 := state.GetSignerState(1) - require.NotNil(t, state1) - require.EqualValues(t, height, state1.Slot) - require.EqualValues(t, 1, state1.Round) - require.EqualValues(t, MessageCounts{Proposal: 1}, state1.MessageCounts) - for i := spectypes.OperatorID(2); i <= 4; i++ { - signerState := state.GetSignerState(i) - require.Nil(t, signerState) - } - - signedMsg = spectestingutils.TestingPrepareMessageWithParams(ks.Shares[1], 1, 2, height, spectestingutils.TestingIdentifier, spectestingutils.TestingQBFTRootData) - encodedMsg, err = signedMsg.Encode() - require.NoError(t, err) - - ssvMsg2 := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: msgID, - Data: encodedMsg, - } - - message2, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg2) - require.NoError(t, err) - - _, _, err = validator.validateSSVMessage(message2, receivedAt, nil) - require.NoError(t, err) - - require.NotNil(t, state1) - require.EqualValues(t, height, state1.Slot) - require.EqualValues(t, 2, state1.Round) - require.EqualValues(t, MessageCounts{Prepare: 1}, state1.MessageCounts) - - _, _, err = validator.validateSSVMessage(message2, receivedAt, nil) - require.ErrorContains(t, err, ErrTooManySameTypeMessagesPerRound.Error()) - - signedMsg = spectestingutils.TestingCommitMessageWithHeight(ks.Shares[1], 1, height+1) - encodedMsg, err = signedMsg.Encode() - require.NoError(t, err) - - ssvMsg3 := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: msgID, - Data: encodedMsg, - } - - message3, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg3) - require.NoError(t, err) - - _, _, err = validator.validateSSVMessage(message3, receivedAt.Add(netCfg.Beacon.SlotDurationSec()), nil) - require.NoError(t, err) - require.NotNil(t, state1) - require.EqualValues(t, height+1, state1.Slot) - require.EqualValues(t, 1, state1.Round) - require.EqualValues(t, MessageCounts{Commit: 1}, state1.MessageCounts) - - _, _, err = validator.validateSSVMessage(message3, receivedAt.Add(netCfg.Beacon.SlotDurationSec()), nil) - require.ErrorContains(t, err, ErrTooManySameTypeMessagesPerRound.Error()) - - signedMsg = spectestingutils.TestingCommitMultiSignerMessageWithHeight([]*bls.SecretKey{ks.Shares[1], ks.Shares[2], ks.Shares[3]}, []spectypes.OperatorID{1, 2, 3}, height+1) - encodedMsg, err = signedMsg.Encode() - require.NoError(t, err) - - ssvMsg4 := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: msgID, - Data: encodedMsg, - } - - message4, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg4) - require.NoError(t, err) - - _, _, err = validator.validateSSVMessage(message4, receivedAt.Add(netCfg.Beacon.SlotDurationSec()), nil) - require.NoError(t, err) - require.NotNil(t, state1) - require.EqualValues(t, height+1, state1.Slot) - require.EqualValues(t, 1, state1.Round) - require.EqualValues(t, MessageCounts{Commit: 1, Decided: 1}, state1.MessageCounts) - }) - - // Send a pubsub message with no data should cause an error - t.Run("pubsub message has no data", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - slot := netCfg.Beacon.FirstSlotAtEpoch(1) - - pmsg := &pubsub.Message{} - - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait) - _, _, err = validator.validateP2PMessage(pmsg, receivedAt) - - require.ErrorIs(t, err, ErrPubSubMessageHasNoData) - }) - - // Send a pubsub message where there is too much data should cause an error - t.Run("pubsub data too big", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - slot := netCfg.Beacon.FirstSlotAtEpoch(1) - - topic := commons.GetTopicFullName(commons.ValidatorTopicID(share.ValidatorPubKey[:])[0]) - pmsg := &pubsub.Message{ - Message: &pspb.Message{ - Data: bytes.Repeat([]byte{1}, 10_000_000+commons.MessageOffset), - Topic: &topic, - From: []byte("16Uiu2HAkyWQyCb6reWXGQeBUt9EXArk6h3aq3PsFMwLNq3pPGH1r"), - }, - } - - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait) - _, _, err = validator.validateP2PMessage(pmsg, receivedAt) - - e := ErrPubSubDataTooBig - e.got = 10_000_000 - require.ErrorIs(t, err, e) - }) - - // Send a malformed pubsub message (empty message) should return an error - t.Run("empty pubsub message", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - slot := netCfg.Beacon.FirstSlotAtEpoch(1) - - topic := commons.GetTopicFullName(commons.ValidatorTopicID(share.ValidatorPubKey[:])[0]) - pmsg := &pubsub.Message{ - Message: &pspb.Message{ - Data: bytes.Repeat([]byte{1}, 1+commons.MessageOffset), - Topic: &topic, - From: []byte("16Uiu2HAkyWQyCb6reWXGQeBUt9EXArk6h3aq3PsFMwLNq3pPGH1r"), - }, - } - - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait) - _, _, err = validator.validateP2PMessage(pmsg, receivedAt) - - require.ErrorContains(t, err, ErrMalformedPubSubMessage.Error()) - }) - - // Send a message with incorrect data (unable to decode incorrect message type) - t.Run("bad data format", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester), - Data: bytes.Repeat([]byte{1}, 500), - } - - encodedMsg, err := ssvMsg.Encode() - require.NoError(t, err) - - encodedSignedMsg := spectypes.EncodeSignedSSVMessage(encodedMsg, 1, make([]byte, 256)) - - topic := commons.GetTopicFullName(commons.ValidatorTopicID(share.ValidatorPubKey[:])[0]) - pmsg := &pubsub.Message{ - Message: &pspb.Message{ - Data: encodedSignedMsg, - Topic: &topic, - }, - } - - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait) - _, _, err = validator.validateP2PMessage(pmsg, receivedAt) - - require.ErrorContains(t, err, ErrMalformedMessage.Error()) - }) - - // Send exact allowed data size amount but with invalid data (fails to decode) - t.Run("data size borderline / malformed message", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester), - Data: bytes.Repeat([]byte{0x1}, maxMessageSize), - } - - encodedMsg, err := ssvMsg.Encode() - require.NoError(t, err) - - encodedSignedMsg := spectypes.EncodeSignedSSVMessage(encodedMsg, 1, make([]byte, 256)) - - topic := commons.GetTopicFullName(commons.ValidatorTopicID(share.ValidatorPubKey[:])[0]) - pmsg := &pubsub.Message{ - Message: &pspb.Message{ - Data: encodedSignedMsg, - Topic: &topic, - }, - } - - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait) - _, _, err = validator.validateP2PMessage(pmsg, receivedAt) - require.ErrorContains(t, err, ErrMalformedMessage.Error()) - }) - - // Send a message with no data should return an error - t.Run("no data", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - message := &genesisqueue.GenesisSSVMessage{ - SSVMessage: &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester), - Data: []byte{}, - }, - } - - _, _, err = validator.validateSSVMessage(message, time.Now(), nil) - require.ErrorIs(t, err, ErrEmptyData) - - message.SSVMessage.Data = nil - - _, _, err = validator.validateSSVMessage(message, time.Now(), nil) - require.ErrorIs(t, err, ErrEmptyData) - }) - - // Send a message where there is too much data should cause an error - t.Run("data too big", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - const tooBigMsgSize = maxMessageSize * 2 - - message := &genesisqueue.GenesisSSVMessage{ - SSVMessage: &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester), - Data: bytes.Repeat([]byte{0x1}, tooBigMsgSize), - }, - } - - _, _, err = validator.validateSSVMessage(message, time.Now(), nil) - expectedErr := ErrSSVDataTooBig - expectedErr.got = tooBigMsgSize - expectedErr.want = maxMessageSize - require.ErrorIs(t, err, expectedErr) - }) - - // Send an invalid SSV message type returns an error - t.Run("invalid SSV message type", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) - height := specqbft.Height(slot) - - validSignedMessage := spectestingutils.TestingProposalMessageWithHeight(ks.Shares[1], 1, height) - encodedValidSignedMessage, err := validSignedMessage.Encode() - require.NoError(t, err) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: math.MaxUint64, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester), - Data: encodedValidSignedMessage, - } - - encodedMsg, err := ssvMsg.Encode() - require.NoError(t, err) - - encodedSignedMsg := spectypes.EncodeSignedSSVMessage(encodedMsg, 1, make([]byte, 256)) - - topic := commons.GetTopicFullName(commons.ValidatorTopicID(share.ValidatorPubKey[:])[0]) - pmsg := &pubsub.Message{ - Message: &pspb.Message{ - Data: encodedSignedMsg, - Topic: &topic, - }, - } - - _, _, err = validator.validateP2PMessage(pmsg, time.Now()) - require.ErrorContains(t, err, ErrUnknownSSVMessageType.Error()) - }) - - // Empty validator public key returns an error - t.Run("empty validator public key", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - validSignedMessage := spectestingutils.TestingProposalMessage(ks.Shares[1], 1) - encodedValidSignedMessage, err := validSignedMessage.Encode() - require.NoError(t, err) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), spectypes.ValidatorPK{}, roleAttester), - Data: encodedValidSignedMessage, - } - - message, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg) - require.NoError(t, err) - - _, _, err = validator.validateSSVMessage(message, time.Now(), nil) - require.ErrorContains(t, err, ErrDeserializePublicKey.Error()) - }) - - // Generate random validator and validate it is unknown to the network - t.Run("unknown validator", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - sk, err := eth2types.GenerateBLSPrivateKey() - require.NoError(t, err) - - validSignedMessage := spectestingutils.TestingProposalMessage(ks.Shares[1], 1) - encodedValidSignedMessage, err := validSignedMessage.Encode() - require.NoError(t, err) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), sk.PublicKey().Marshal(), roleAttester), - Data: encodedValidSignedMessage, - } - - message, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg) - require.NoError(t, err) - - _, _, err = validator.validateSSVMessage(message, time.Now(), nil) - expectedErr := ErrUnknownValidator - expectedErr.got = hex.EncodeToString(sk.PublicKey().Marshal()) - require.ErrorIs(t, err, expectedErr) - }) - - // Make sure messages are dropped if on the incorrect network - t.Run("wrong domain", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - slot := netCfg.Beacon.FirstSlotAtEpoch(1) - height := specqbft.Height(slot) - - validSignedMessage := spectestingutils.TestingProposalMessageWithHeight(ks.Shares[1], 1, height) - encodedValidSignedMessage, err := validSignedMessage.Encode() - require.NoError(t, err) - - wrongDomain := spectypes.DomainType{math.MaxUint8, math.MaxUint8, math.MaxUint8, math.MaxUint8} - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: spectypes.NewMsgID(wrongDomain, share.ValidatorPubKey[:], roleAttester), - Data: encodedValidSignedMessage, - } - - message, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg) - require.NoError(t, err) - - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait) - _, _, err = validator.validateSSVMessage(message, receivedAt, nil) - expectedErr := ErrWrongDomain - expectedErr.got = hex.EncodeToString(wrongDomain[:]) - domain := netCfg.DomainType() - expectedErr.want = hex.EncodeToString(domain[:]) - require.ErrorIs(t, err, expectedErr) - }) - - // Send message with a value that refers to a non-existent role - t.Run("invalid role", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - slot := netCfg.Beacon.FirstSlotAtEpoch(1) - height := specqbft.Height(slot) - - validSignedMessage := spectestingutils.TestingProposalMessageWithHeight(ks.Shares[1], 1, height) - encodedValidSignedMessage, err := validSignedMessage.Encode() - require.NoError(t, err) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], math.MaxUint64), - Data: encodedValidSignedMessage, - } - - message, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg) - require.NoError(t, err) - - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait) - _, _, err = validator.validateSSVMessage(message, receivedAt, nil) - require.ErrorIs(t, err, ErrInvalidRole) - }) - - // Perform validator registration or voluntary exit with a consensus type message will give an error - t.Run("unexpected consensus message", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - slot := netCfg.Beacon.FirstSlotAtEpoch(1) - height := specqbft.Height(slot) - - validSignedMessage := spectestingutils.TestingProposalMessageWithHeight(ks.Shares[1], 1, height) - encodedValidSignedMessage, err := validSignedMessage.Encode() - require.NoError(t, err) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], spectypes.BNRoleValidatorRegistration), - Data: encodedValidSignedMessage, - } - - message, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg) - require.NoError(t, err) - - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait) - _, _, err = validator.validateSSVMessage(message, receivedAt, nil) - require.ErrorContains(t, err, ErrUnexpectedConsensusMessage.Error()) - - ssvMsg = &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], spectypes.BNRoleVoluntaryExit), - Data: encodedValidSignedMessage, - } - - message, err = genesisqueue.DecodeGenesisSSVMessage(ssvMsg) - require.NoError(t, err) - - _, _, err = validator.validateSSVMessage(message, receivedAt, nil) - require.ErrorContains(t, err, ErrUnexpectedConsensusMessage.Error()) - }) - - // Ignore messages related to a validator that is liquidated - t.Run("liquidated validator", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - liquidatedSK, err := eth2types.GenerateBLSPrivateKey() - require.NoError(t, err) - - liquidatedShare := &ssvtypes.SSVShare{ - Share: *alanspectestingutils.TestingShare(ks, spectestingutils.TestingValidatorIndex), - Metadata: ssvtypes.Metadata{ - BeaconMetadata: &beaconprotocol.ValidatorMetadata{ - Status: eth2apiv1.ValidatorStateActiveOngoing, - }, - Liquidated: true, - }, - } - liquidatedShare.ValidatorPubKey = alanspectypes.ValidatorPK(liquidatedSK.PublicKey().Marshal()) - - require.NoError(t, ns.Shares().Save(nil, liquidatedShare)) - - validSignedMessage := spectestingutils.TestingProposalMessage(ks.Shares[1], 1) - encodedValidSignedMessage, err := validSignedMessage.Encode() - require.NoError(t, err) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), liquidatedShare.ValidatorPubKey[:], roleAttester), - Data: encodedValidSignedMessage, - } - - message, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg) - require.NoError(t, err) - - _, _, err = validator.validateSSVMessage(message, time.Now(), nil) - expectedErr := ErrValidatorLiquidated - require.ErrorIs(t, err, expectedErr) - - require.NoError(t, ns.Shares().Delete(nil, liquidatedShare.ValidatorPubKey[:])) - }) - - // Ignore messages related to a validator with unknown state - t.Run("unknown state validator", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - inactiveSK, err := eth2types.GenerateBLSPrivateKey() - require.NoError(t, err) - - inactiveShare := &ssvtypes.SSVShare{ - Share: *alanspectestingutils.TestingShare(ks, spectestingutils.TestingValidatorIndex), - Metadata: ssvtypes.Metadata{ - BeaconMetadata: &beaconprotocol.ValidatorMetadata{ - Status: eth2apiv1.ValidatorStateUnknown, - }, - Liquidated: false, - }, - } - inactiveShare.ValidatorPubKey = alanspectypes.ValidatorPK(inactiveSK.PublicKey().Marshal()) - - require.NoError(t, ns.Shares().Save(nil, inactiveShare)) - - validSignedMessage := spectestingutils.TestingProposalMessage(ks.Shares[1], 1) - encodedValidSignedMessage, err := validSignedMessage.Encode() - require.NoError(t, err) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), inactiveShare.ValidatorPubKey[:], roleAttester), - Data: encodedValidSignedMessage, - } - - message, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg) - require.NoError(t, err) - - slot := netCfg.Beacon.FirstSlotAtEpoch(1) - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait) - - _, _, err = validator.validateSSVMessage(message, receivedAt, nil) - expectedErr := ErrValidatorNotAttesting - expectedErr.got = eth2apiv1.ValidatorStateUnknown.String() - require.ErrorIs(t, err, expectedErr) - - require.NoError(t, ns.Shares().Delete(nil, inactiveShare.ValidatorPubKey[:])) - }) - - // Ignore messages related to a validator that in pending queued state - t.Run("pending queued state validator", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - nonUpdatedMetadataSK, err := eth2types.GenerateBLSPrivateKey() - require.NoError(t, err) - - slot := netCfg.Beacon.EstimatedCurrentSlot() - epoch := netCfg.Beacon.EstimatedEpochAtSlot(slot) - height := specqbft.Height(slot) - - nonUpdatedMetadataShare := &ssvtypes.SSVShare{ - Share: *alanspectestingutils.TestingShare(ks, spectestingutils.TestingValidatorIndex), - Metadata: ssvtypes.Metadata{ - BeaconMetadata: &beaconprotocol.ValidatorMetadata{ - Status: eth2apiv1.ValidatorStatePendingQueued, - ActivationEpoch: epoch + 1, - }, - Liquidated: false, - }, - } - nonUpdatedMetadataShare.ValidatorPubKey = alanspectypes.ValidatorPK(nonUpdatedMetadataSK.PublicKey().Marshal()) - - require.NoError(t, ns.Shares().Save(nil, nonUpdatedMetadataShare)) - - leader := validator.roundRobinProposer(height, specqbft.FirstRound, nonUpdatedMetadataShare) - - validSignedMessage := spectestingutils.TestingProposalMessageWithHeight(ks.Shares[leader], leader, height) - encodedValidSignedMessage, err := validSignedMessage.Encode() - require.NoError(t, err) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), nonUpdatedMetadataShare.ValidatorPubKey[:], roleAttester), - Data: encodedValidSignedMessage, - } - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait) - - message, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg) - require.NoError(t, err) - - _, _, err = validator.validateSSVMessage(message, receivedAt, nil) - expectedErr := ErrValidatorNotAttesting - expectedErr.got = eth2apiv1.ValidatorStatePendingQueued.String() - require.ErrorIs(t, err, expectedErr) - - require.NoError(t, ns.Shares().Delete(nil, nonUpdatedMetadataShare.ValidatorPubKey[:])) - }) - - // Don't ignore messages related to a validator that in pending queued state (in case metadata is not updated), - // but he is active (activation epoch <= current epoch) - t.Run("active validator with pending queued state", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - nonUpdatedMetadataSK, err := eth2types.GenerateBLSPrivateKey() - require.NoError(t, err) - - slot := netCfg.Beacon.EstimatedCurrentSlot() - epoch := netCfg.Beacon.EstimatedEpochAtSlot(slot) - height := specqbft.Height(slot) - - nonUpdatedMetadataShare := &ssvtypes.SSVShare{ - Share: *alanspectestingutils.TestingShare(ks, spectestingutils.TestingValidatorIndex), - Metadata: ssvtypes.Metadata{ - BeaconMetadata: &beaconprotocol.ValidatorMetadata{ - Status: eth2apiv1.ValidatorStatePendingQueued, - ActivationEpoch: epoch, - }, - Liquidated: false, - }, - } - nonUpdatedMetadataShare.ValidatorPubKey = alanspectypes.ValidatorPK(nonUpdatedMetadataSK.PublicKey().Marshal()) - - require.NoError(t, ns.Shares().Save(nil, nonUpdatedMetadataShare)) - - leader := validator.roundRobinProposer(height, specqbft.FirstRound, nonUpdatedMetadataShare) - - validSignedMessage := spectestingutils.TestingProposalMessageWithHeight(ks.Shares[leader], leader, height) - encodedValidSignedMessage, err := validSignedMessage.Encode() - require.NoError(t, err) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), nonUpdatedMetadataShare.ValidatorPubKey[:], roleAttester), - Data: encodedValidSignedMessage, - } - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait) - - message, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg) - require.NoError(t, err) - - _, _, err = validator.validateSSVMessage(message, receivedAt, nil) - require.NoError(t, err) - - require.NoError(t, ns.Shares().Delete(nil, nonUpdatedMetadataShare.ValidatorPubKey[:])) - }) - - // Unable to process a message with a validator that is not on the network - t.Run("no share metadata", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - noMetadataSK, err := eth2types.GenerateBLSPrivateKey() - require.NoError(t, err) - - noMetadataShare := &ssvtypes.SSVShare{ - Share: *alanspectestingutils.TestingShare(ks, spectestingutils.TestingValidatorIndex), - Metadata: ssvtypes.Metadata{ - BeaconMetadata: nil, - Liquidated: false, - }, - } - noMetadataShare.ValidatorPubKey = alanspectypes.ValidatorPK(noMetadataSK.PublicKey().Marshal()) - - require.NoError(t, ns.Shares().Save(nil, noMetadataShare)) - - validSignedMessage := spectestingutils.TestingProposalMessage(ks.Shares[1], 1) - encodedValidSignedMessage, err := validSignedMessage.Encode() - require.NoError(t, err) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), noMetadataShare.ValidatorPubKey[:], roleAttester), - Data: encodedValidSignedMessage, - } - - message, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg) - require.NoError(t, err) - - slot := netCfg.Beacon.FirstSlotAtEpoch(1) - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait) - - _, _, err = validator.validateSSVMessage(message, receivedAt, nil) - require.ErrorIs(t, err, ErrNoShareMetadata) - - require.NoError(t, ns.Shares().Delete(nil, noMetadataShare.ValidatorPubKey[:])) - }) - - // Receive error if more than 2 attestation duties in an epoch - t.Run("too many duties", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - slot := netCfg.Beacon.FirstSlotAtEpoch(1) - height := specqbft.Height(slot) - - validSignedMessage := spectestingutils.TestingProposalMessageWithHeight(ks.Shares[1], 1, height) - encodedValidSignedMessage, err := validSignedMessage.Encode() - require.NoError(t, err) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester), - Data: encodedValidSignedMessage, - } - - message, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg) - require.NoError(t, err) - - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - _, _, err = validator.validateSSVMessage(message, netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait), nil) - require.NoError(t, err) - - validSignedMessage = spectestingutils.TestingProposalMessageWithHeight(ks.Shares[1], 1, height+4) - encodedValidSignedMessage, err = validSignedMessage.Encode() - require.NoError(t, err) - - ssvMsg2 := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester), - Data: encodedValidSignedMessage, - } - - message2, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg2) - require.NoError(t, err) - - timeToWait, err = validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - - _, _, err = validator.validateSSVMessage(message2, netCfg.Beacon.GetSlotStartTime(slot+4).Add(timeToWait), nil) - require.NoError(t, err) - - validSignedMessage = spectestingutils.TestingProposalMessageWithHeight(ks.Shares[1], 1, height+8) - encodedValidSignedMessage, err = validSignedMessage.Encode() - require.NoError(t, err) - - ssvMsg3 := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester), - Data: encodedValidSignedMessage, - } - - message3, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg3) - require.NoError(t, err) - - timeToWait, err = validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - _, _, err = validator.validateSSVMessage(message3, netCfg.Beacon.GetSlotStartTime(slot+8).Add(timeToWait), nil) - require.ErrorContains(t, err, ErrTooManyDutiesPerEpoch.Error()) - }) - - // Throw error if getting a message for proposal and see there is no message from beacon - t.Run("no proposal duties", func(t *testing.T) { - const epoch = 1 - slot := netCfg.Beacon.FirstSlotAtEpoch(epoch) - height := specqbft.Height(slot) - - dutyStore := dutystore.New() - dutyStore.Proposer.Set(epoch, []dutystore.StoreDuty[eth2apiv1.ProposerDuty]{ - {Slot: slot, ValidatorIndex: validatorIndex + 1, Duty: ð2apiv1.ProposerDuty{}, InCommittee: true}, - }) - validator := New(netCfg, WithNodeStorage(ns), WithDutyStore(dutyStore)).(*messageValidator) - - validSignedMessage := spectestingutils.TestingProposalMessageWithHeight(ks.Shares[1], 1, height) - encodedValidSignedMessage, err := validSignedMessage.Encode() - require.NoError(t, err) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], spectypes.BNRoleProposer), - Data: encodedValidSignedMessage, - } - - message, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg) - require.NoError(t, err) - - timeToWait, err := validator.waitAfterSlotStart(spectypes.BNRoleProposer) - require.NoError(t, err) - _, _, err = validator.validateSSVMessage(message, netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait), nil) - require.ErrorContains(t, err, ErrNoDuty.Error()) - - dutyStore = dutystore.New() - dutyStore.Proposer.Set(epoch, []dutystore.StoreDuty[eth2apiv1.ProposerDuty]{ - {Slot: slot, ValidatorIndex: validatorIndex, Duty: ð2apiv1.ProposerDuty{}, InCommittee: true}, - }) - validator = New(netCfg, WithNodeStorage(ns), WithDutyStore(dutyStore)).(*messageValidator) - timeToWait, err = validator.waitAfterSlotStart(spectypes.BNRoleProposer) - require.NoError(t, err) - _, _, err = validator.validateSSVMessage(message, netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait), nil) - require.NoError(t, err) - }) - - // Get error when receiving a message with over 13 partial signatures - t.Run("partial message too big", func(t *testing.T) { - slot := netCfg.Beacon.FirstSlotAtEpoch(1) - - msg := spectestingutils.PostConsensusAttestationMsg(ks.Shares[1], 1, specqbft.Height(slot)) - for i := 0; i < 13; i++ { - msg.Message.Messages = append(msg.Message.Messages, msg.Message.Messages[0]) - } - - _, err := msg.Encode() - require.ErrorContains(t, err, "max expected 13 and 14 found") - }) - - // Get error when receiving message from operator who is not affiliated with the validator - t.Run("signer ID not in committee", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - slot := netCfg.Beacon.FirstSlotAtEpoch(1) - - msg := spectestingutils.PostConsensusAttestationMsg(ks.Shares[1], 5, specqbft.Height(slot)) - - encoded, err := msg.Encode() - require.NoError(t, err) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVPartialSignatureMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester), - Data: encoded, - } - - message, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg) - require.NoError(t, err) - - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait) - _, _, err = validator.validateSSVMessage(message, receivedAt, nil) - require.ErrorIs(t, err, ErrSignerNotInCommittee) - }) - - // Get error when receiving message from operator who is non-existent (operator id 0) - t.Run("partial zero signer ID", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - slot := netCfg.Beacon.FirstSlotAtEpoch(1) - - msg := spectestingutils.PostConsensusAttestationMsg(ks.Shares[1], 0, specqbft.Height(slot)) - - encoded, err := msg.Encode() - require.NoError(t, err) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVPartialSignatureMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester), - Data: encoded, - } - - message, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg) - require.NoError(t, err) - - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait) - _, _, err = validator.validateSSVMessage(message, receivedAt, nil) - require.ErrorIs(t, err, ErrZeroSigner) - }) - - // Get error when receiving partial signature message from operator who is the incorrect signer - t.Run("partial inconsistent signer ID", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - slot := netCfg.Beacon.FirstSlotAtEpoch(1) - - msg := spectestingutils.PostConsensusAttestationMsg(ks.Shares[1], 1, specqbft.Height(slot)) - msg.Message.Messages[0].Signer = 2 - - encoded, err := msg.Encode() - require.NoError(t, err) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVPartialSignatureMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester), - Data: encoded, - } - - message, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg) - require.NoError(t, err) - - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait) - _, _, err = validator.validateSSVMessage(message, receivedAt, nil) - expectedErr := ErrUnexpectedSigner - expectedErr.got = spectypes.OperatorID(2) - expectedErr.want = spectypes.OperatorID(1) - require.ErrorIs(t, err, expectedErr) - }) - - // Receive error when receiving a duplicated partial signature message - t.Run("partial duplicated message", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - slot := netCfg.Beacon.FirstSlotAtEpoch(1) - - msg := spectestingutils.PostConsensusAttestationMsg(ks.Shares[1], 1, specqbft.Height(slot)) - msg.Message.Messages = append(msg.Message.Messages, msg.Message.Messages[0]) - - encoded, err := msg.Encode() - require.NoError(t, err) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVPartialSignatureMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester), - Data: encoded, - } - - message, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg) - require.NoError(t, err) - - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait) - _, _, err = validator.validateSSVMessage(message, receivedAt, nil) - require.ErrorIs(t, err, ErrDuplicatedPartialSignatureMessage) - }) - - // Receive error when "partialSignatureMessages" does not contain any "partialSignatureMessage" - t.Run("no partial signature messages", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - slot := netCfg.Beacon.FirstSlotAtEpoch(1) - - msg := spectestingutils.PostConsensusAttestationMsg(ks.Shares[1], 1, specqbft.Height(slot)) - msg.Message.Messages = []*spectypes.PartialSignatureMessage{} - - encoded, err := msg.Encode() - require.NoError(t, err) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVPartialSignatureMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester), - Data: encoded, - } - - message, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg) - require.NoError(t, err) - - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait) - _, _, err = validator.validateSSVMessage(message, receivedAt, nil) - require.ErrorIs(t, err, ErrNoPartialMessages) - }) - - // Receive error when the partial signature message is not enough bytes - t.Run("partial wrong signature size", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - slot := netCfg.Beacon.FirstSlotAtEpoch(1) - - msg := spectestingutils.PostConsensusAttestationMsg(ks.Shares[1], 1, specqbft.Height(slot)) - msg.Signature = []byte{1} - - encoded, err := msg.Encode() - require.ErrorContains(t, err, "bytes array does not have the correct length") - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVPartialSignatureMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester), - Data: encoded, - } - - encodedMsg, err := ssvMsg.Encode() - require.NoError(t, err) - - topic := commons.GetTopicFullName(commons.ValidatorTopicID(share.ValidatorPubKey[:])[0]) - pmsg := &pubsub.Message{ - Message: &pspb.Message{ - Data: encodedMsg, - Topic: &topic, - }, - } - - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait) - _, _, err = validator.validateP2PMessage(pmsg, receivedAt) - require.ErrorContains(t, err, ErrMalformedMessage.Error()) - }) - - // Run partial message type validation tests - t.Run("partial message type validation", func(t *testing.T) { - slot := netCfg.Beacon.FirstSlotAtEpoch(162304) - - // Check happy flow of a duty for each role - t.Run("valid", func(t *testing.T) { - tests := map[spectypes.BeaconRole][]spectypes.PartialSigMsgType{ - spectypes.BNRoleAttester: {spectypes.PostConsensusPartialSig}, - spectypes.BNRoleAggregator: {spectypes.PostConsensusPartialSig, spectypes.SelectionProofPartialSig}, - spectypes.BNRoleProposer: {spectypes.PostConsensusPartialSig, spectypes.RandaoPartialSig}, - spectypes.BNRoleSyncCommittee: {spectypes.PostConsensusPartialSig}, - spectypes.BNRoleSyncCommitteeContribution: {spectypes.PostConsensusPartialSig, spectypes.ContributionProofs}, - spectypes.BNRoleValidatorRegistration: {spectypes.ValidatorRegistrationPartialSig}, - spectypes.BNRoleVoluntaryExit: {spectypes.VoluntaryExitPartialSig}, - } - - for role, msgTypes := range tests { - for _, msgType := range msgTypes { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - msgID := spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], role) - - innerSig, r, err := spectestingutils.NewTestingKeyManager().SignBeaconObject(spectypes.SSZUint64(spectestingutils.TestingDutyEpoch), phase0.Domain{}, ks.Shares[1].GetPublicKey().Serialize(), phase0.DomainType{}) - require.NoError(t, err) - - innerMsg := spectypes.PartialSignatureMessages{ - Type: msgType, - Messages: []*spectypes.PartialSignatureMessage{ - { - PartialSignature: innerSig, - SigningRoot: r, - Signer: 1, - }, - }, - Slot: slot, - } - - sig, err := spectestingutils.NewTestingKeyManager().SignRoot(innerMsg, spectypes.PartialSignatureType, ks.Shares[1].GetPublicKey().Serialize()) - require.NoError(t, err) - - msg := &spectypes.SignedPartialSignatureMessage{ - Message: innerMsg, - Signature: sig, - Signer: 1, - } - - encoded, err := msg.Encode() - require.NoError(t, err) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVPartialSignatureMsgType, - MsgID: msgID, - Data: encoded, - } - - message, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg) - require.NoError(t, err) - - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) - _, _, err = validator.validateSSVMessage(message, receivedAt, nil) - require.NoError(t, err) - } - } - }) - - // Get error when receiving a message with an incorrect message type - t.Run("invalid message type", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - msgID := spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester) - - msg := &spectypes.SignedPartialSignatureMessage{ - Message: spectypes.PartialSignatureMessages{ - Type: math.MaxUint64, - }, - Signature: make([]byte, 96), - Signer: 1, - } - - encoded, err := msg.Encode() - require.NoError(t, err) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVPartialSignatureMsgType, - MsgID: msgID, - Data: encoded, - } - - message, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg) - require.NoError(t, err) - - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) - _, _, err = validator.validateSSVMessage(message, receivedAt, nil) - require.ErrorContains(t, err, ErrUnknownPartialMessageType.Error()) - }) - - // Get error when sending an unexpected message type for the required duty (sending randao for attestor duty) - t.Run("mismatch", func(t *testing.T) { - tests := map[spectypes.BeaconRole][]spectypes.PartialSigMsgType{ - spectypes.BNRoleAttester: {spectypes.RandaoPartialSig, spectypes.SelectionProofPartialSig, spectypes.ContributionProofs, spectypes.ValidatorRegistrationPartialSig}, - spectypes.BNRoleAggregator: {spectypes.RandaoPartialSig, spectypes.ContributionProofs, spectypes.ValidatorRegistrationPartialSig}, - spectypes.BNRoleProposer: {spectypes.SelectionProofPartialSig, spectypes.ContributionProofs, spectypes.ValidatorRegistrationPartialSig}, - spectypes.BNRoleSyncCommittee: {spectypes.RandaoPartialSig, spectypes.SelectionProofPartialSig, spectypes.ContributionProofs, spectypes.ValidatorRegistrationPartialSig}, - spectypes.BNRoleSyncCommitteeContribution: {spectypes.RandaoPartialSig, spectypes.SelectionProofPartialSig, spectypes.ValidatorRegistrationPartialSig}, - spectypes.BNRoleValidatorRegistration: {spectypes.PostConsensusPartialSig, spectypes.RandaoPartialSig, spectypes.SelectionProofPartialSig, spectypes.ContributionProofs}, - spectypes.BNRoleVoluntaryExit: {spectypes.PostConsensusPartialSig, spectypes.RandaoPartialSig, spectypes.SelectionProofPartialSig, spectypes.ContributionProofs}, - } - - for role, msgTypes := range tests { - for _, msgType := range msgTypes { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - msgID := spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], role) - - msg := &spectypes.SignedPartialSignatureMessage{ - Message: spectypes.PartialSignatureMessages{ - Type: msgType, - }, - Signature: make([]byte, 96), - Signer: 1, - } - - encoded, err := msg.Encode() - require.NoError(t, err) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVPartialSignatureMsgType, - MsgID: msgID, - Data: encoded, - } - - message, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg) - require.NoError(t, err) - - receivedAt := netCfg.Beacon.GetSlotStartTime(slot) - _, _, err = validator.validateSSVMessage(message, receivedAt, nil) - require.ErrorContains(t, err, ErrPartialSignatureTypeRoleMismatch.Error()) - } - } - }) - }) - - // Get error when receiving QBFT message with an invalid type - t.Run("invalid QBFT message type", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - slot := netCfg.Beacon.FirstSlotAtEpoch(1) - height := specqbft.Height(slot) - - msg := &specqbft.Message{ - MsgType: math.MaxUint64, - Height: height, - Round: specqbft.FirstRound, - Identifier: spectestingutils.TestingIdentifier, - Root: spectestingutils.TestingQBFTRootData, - } - signedMsg := spectestingutils.SignQBFTMsg(ks.Shares[1], 1, msg) - - encodedValidSignedMessage, err := signedMsg.Encode() - require.NoError(t, err) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester), - Data: encodedValidSignedMessage, - } - - message, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg) - require.NoError(t, err) - - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait) - _, _, err = validator.validateSSVMessage(message, receivedAt, nil) - expectedErr := ErrUnknownQBFTMessageType - require.ErrorIs(t, err, expectedErr) - }) - - // Get error when receiving an incorrect signature size (too small) - t.Run("wrong signature size", func(t *testing.T) { - slot := netCfg.Beacon.FirstSlotAtEpoch(1) - height := specqbft.Height(slot) - - validSignedMessage := spectestingutils.TestingProposalMessageWithHeight(ks.Shares[1], 1, height) - validSignedMessage.Signature = []byte{0x1} - - _, err := validSignedMessage.Encode() - require.Error(t, err) - }) - - // Initialize signature tests - t.Run("zero signature", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - slot := netCfg.Beacon.FirstSlotAtEpoch(1) - height := specqbft.Height(slot) - - // Get error when receiving a consensus message with a zero signature - t.Run("consensus message", func(t *testing.T) { - validSignedMessage := spectestingutils.TestingProposalMessageWithHeight(ks.Shares[1], 1, height) - zeroSignature := [signatureSize]byte{} - validSignedMessage.Signature = zeroSignature[:] - - encoded, err := validSignedMessage.Encode() - require.NoError(t, err) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester), - Data: encoded, - } - - message, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg) - require.NoError(t, err) - - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait) - _, _, err = validator.validateSSVMessage(message, receivedAt, nil) - require.ErrorIs(t, err, ErrZeroSignature) - }) - - // Get error when receiving a consensus message with a zero signature - t.Run("partial signature message", func(t *testing.T) { - partialSigMessage := spectestingutils.PostConsensusAttestationMsg(ks.Shares[1], 1, height) - zeroSignature := [signatureSize]byte{} - partialSigMessage.Signature = zeroSignature[:] - - encoded, err := partialSigMessage.Encode() - require.NoError(t, err) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVPartialSignatureMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester), - Data: encoded, - } - - message, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg) - require.NoError(t, err) - - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait) - _, _, err = validator.validateSSVMessage(message, receivedAt, nil) - require.ErrorIs(t, err, ErrZeroSignature) - }) - }) - - // Get error when receiving a message with an empty list of signers - t.Run("no signers", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - slot := netCfg.Beacon.FirstSlotAtEpoch(1) - height := specqbft.Height(slot) - - validSignedMessage := spectestingutils.TestingProposalMessageWithHeight(ks.Shares[1], 1, height) - validSignedMessage.Signers = []spectypes.OperatorID{} - - encoded, err := validSignedMessage.Encode() - require.NoError(t, err) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester), - Data: encoded, - } - - message, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg) - require.NoError(t, err) - - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait) - _, _, err = validator.validateSSVMessage(message, receivedAt, nil) - require.ErrorIs(t, err, ErrNoSigners) - }) - - // Initialize no signer tests - t.Run("zero signer", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - slot := netCfg.Beacon.FirstSlotAtEpoch(1) - - inactiveSK, err := eth2types.GenerateBLSPrivateKey() - require.NoError(t, err) - - zeroSignerKS := alanspectestingutils.Testing7SharesSet() - zeroSignerShare := &ssvtypes.SSVShare{ - Share: *alanspectestingutils.TestingShare(zeroSignerKS, alanspectestingutils.TestingValidatorIndex), - Metadata: ssvtypes.Metadata{ - BeaconMetadata: &beaconprotocol.ValidatorMetadata{ - Status: eth2apiv1.ValidatorStateActiveOngoing, - }, - Liquidated: false, - }, - } - zeroSignerShare.Committee[0].Signer = 0 - zeroSignerShare.ValidatorPubKey = alanspectypes.ValidatorPK(inactiveSK.PublicKey().Marshal()) - - require.NoError(t, ns.Shares().Save(nil, zeroSignerShare)) - - // Get error when receiving a consensus message with a zero signer - t.Run("consensus message", func(t *testing.T) { - validSignedMessage := spectestingutils.TestingProposalMessage(zeroSignerKS.Shares[1], 1) - validSignedMessage.Signers = []spectypes.OperatorID{0} - - encodedValidSignedMessage, err := validSignedMessage.Encode() - require.NoError(t, err) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), zeroSignerShare.ValidatorPubKey[:], roleAttester), - Data: encodedValidSignedMessage, - } - - message, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg) - require.NoError(t, err) - - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait) - _, _, err = validator.validateSSVMessage(message, receivedAt, nil) - require.ErrorIs(t, err, ErrZeroSigner) - }) - - // Get error when receiving a partial message with a zero signer - t.Run("partial signature message", func(t *testing.T) { - partialSignatureMessage := spectestingutils.PostConsensusAttestationMsg(zeroSignerKS.Shares[1], 1, specqbft.Height(slot)) - partialSignatureMessage.Signer = 0 - - encoded, err := partialSignatureMessage.Encode() - require.NoError(t, err) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVPartialSignatureMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), zeroSignerShare.ValidatorPubKey[:], roleAttester), - Data: encoded, - } - - message, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg) - require.NoError(t, err) - - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait) - _, _, err = validator.validateSSVMessage(message, receivedAt, nil) - require.ErrorIs(t, err, ErrZeroSigner) - }) - - require.NoError(t, ns.Shares().Delete(nil, zeroSignerShare.ValidatorPubKey[:])) - }) - - // Get error when receiving a message with duplicated signers - t.Run("non unique signer", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - slot := netCfg.Beacon.FirstSlotAtEpoch(1) - - validSignedMessage := spectestingutils.TestingCommitMultiSignerMessage( - []*bls.SecretKey{ks.Shares[1], ks.Shares[2], ks.Shares[3]}, []spectypes.OperatorID{1, 2, 3}) - - validSignedMessage.Signers = []spectypes.OperatorID{1, 2, 2} - - encoded, err := validSignedMessage.Encode() - require.NoError(t, err) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester), - Data: encoded, - } - - message, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg) - require.NoError(t, err) - - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait) - _, _, err = validator.validateSSVMessage(message, receivedAt, nil) - require.ErrorIs(t, err, ErrDuplicatedSigner) - }) - - // Get error when receiving a message with non-sorted signers - t.Run("signers not sorted", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - slot := netCfg.Beacon.FirstSlotAtEpoch(1) - - validSignedMessage := spectestingutils.TestingCommitMultiSignerMessage( - []*bls.SecretKey{ks.Shares[1], ks.Shares[2], ks.Shares[3]}, []spectypes.OperatorID{1, 2, 3}) - - validSignedMessage.Signers = []spectypes.OperatorID{3, 2, 1} - - encoded, err := validSignedMessage.Encode() - require.NoError(t, err) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester), - Data: encoded, - } - - message, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg) - require.NoError(t, err) - - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait) - _, _, err = validator.validateSSVMessage(message, receivedAt, nil) - require.ErrorIs(t, err, ErrSignersNotSorted) - }) - - // Get error when receiving message from non quorum size amount of signers - t.Run("wrong signers length", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - slot := netCfg.Beacon.FirstSlotAtEpoch(1) - - validSignedMessage := spectestingutils.TestingCommitMultiSignerMessage( - []*bls.SecretKey{ks.Shares[1], ks.Shares[2], ks.Shares[3]}, []spectypes.OperatorID{1, 2, 3}) - - validSignedMessage.Signers = []spectypes.OperatorID{1, 2} - - encoded, err := validSignedMessage.Encode() - require.NoError(t, err) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester), - Data: encoded, - } - - message, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg) - require.NoError(t, err) - - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait) - _, _, err = validator.validateSSVMessage(message, receivedAt, nil) - - expectedErr := ErrWrongSignersLength - expectedErr.got = 2 - expectedErr.want = "between 3 and 4" - require.ErrorIs(t, err, expectedErr) - }) - - // Get error when receiving a non decided message with multiple signers - t.Run("non decided with multiple signers", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - slot := netCfg.Beacon.FirstSlotAtEpoch(1) - - validSignedMessage := spectestingutils.TestingMultiSignerProposalMessage( - []*bls.SecretKey{ks.Shares[1], ks.Shares[2], ks.Shares[3]}, []spectypes.OperatorID{1, 2, 3}) - - encoded, err := validSignedMessage.Encode() - require.NoError(t, err) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester), - Data: encoded, - } - - message, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg) - require.NoError(t, err) - - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait) - _, _, err = validator.validateSSVMessage(message, receivedAt, nil) - - expectedErr := ErrNonDecidedWithMultipleSigners - expectedErr.got = 3 - require.ErrorIs(t, err, expectedErr) - }) - - // Send late message for all roles and receive late message error - t.Run("late message", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - slot := netCfg.Beacon.FirstSlotAtEpoch(1) - height := specqbft.Height(slot) - - validSignedMessage := spectestingutils.TestingProposalMessageWithHeight(ks.Shares[1], 1, height) - encodedValidSignedMessage, err := validSignedMessage.Encode() - require.NoError(t, err) - - attesterTimeToWait, err := validator.waitAfterSlotStart(spectypes.BNRoleAttester) - require.NoError(t, err) - aggregatorTimeToWait, err := validator.waitAfterSlotStart(spectypes.BNRoleAggregator) - require.NoError(t, err) - proposerTimeToWait, err := validator.waitAfterSlotStart(spectypes.BNRoleProposer) - require.NoError(t, err) - scTimeToWait, err := validator.waitAfterSlotStart(spectypes.BNRoleSyncCommittee) - require.NoError(t, err) - sccTimeToWait, err := validator.waitAfterSlotStart(spectypes.BNRoleSyncCommitteeContribution) - require.NoError(t, err) - - tests := map[spectypes.BeaconRole]time.Time{ - spectypes.BNRoleAttester: netCfg.Beacon.GetSlotStartTime(slot + 35).Add(attesterTimeToWait), - spectypes.BNRoleAggregator: netCfg.Beacon.GetSlotStartTime(slot + 35).Add(aggregatorTimeToWait), - spectypes.BNRoleProposer: netCfg.Beacon.GetSlotStartTime(slot + 4).Add(proposerTimeToWait), - spectypes.BNRoleSyncCommittee: netCfg.Beacon.GetSlotStartTime(slot + 4).Add(scTimeToWait), - spectypes.BNRoleSyncCommitteeContribution: netCfg.Beacon.GetSlotStartTime(slot + 4).Add(sccTimeToWait), - } - - for role, receivedAt := range tests { - role, receivedAt := role, receivedAt - t.Run(role.String(), func(t *testing.T) { - msgID := spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], role) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: msgID, - Data: encodedValidSignedMessage, - } - - message, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg) - require.NoError(t, err) - - _, _, err = validator.validateSSVMessage(message, receivedAt, nil) - require.ErrorContains(t, err, ErrLateMessage.Error()) - }) - } - }) - - // Send early message for all roles before the duty start and receive early message error - t.Run("early message", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - slot := netCfg.Beacon.FirstSlotAtEpoch(1) - height := specqbft.Height(slot) - - validSignedMessage := spectestingutils.TestingProposalMessageWithHeight(ks.Shares[1], 1, height) - encodedValidSignedMessage, err := validSignedMessage.Encode() - require.NoError(t, err) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester), - Data: encodedValidSignedMessage, - } - - message, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg) - require.NoError(t, err) - - receivedAt := netCfg.Beacon.GetSlotStartTime(slot - 1) - _, _, err = validator.validateSSVMessage(message, receivedAt, nil) - require.ErrorIs(t, err, ErrEarlyMessage) - }) - - // Send message from non-leader acting as a leader should receive an error - t.Run("not a leader", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - slot := netCfg.Beacon.FirstSlotAtEpoch(1) - height := specqbft.Height(slot) - - validSignedMessage := spectestingutils.TestingProposalMessageWithHeight(ks.Shares[2], 2, height) - encodedValidSignedMessage, err := validSignedMessage.Encode() - require.NoError(t, err) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester), - Data: encodedValidSignedMessage, - } - - message, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg) - require.NoError(t, err) - - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait) - _, _, err = validator.validateSSVMessage(message, receivedAt, nil) - expectedErr := ErrSignerNotLeader - expectedErr.got = spectypes.OperatorID(2) - expectedErr.want = spectypes.OperatorID(1) - require.ErrorIs(t, err, expectedErr) - }) - - // Send wrong size of data (8 bytes) for a prepare justification message should receive an error - t.Run("malformed prepare justification", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - slot := netCfg.Beacon.FirstSlotAtEpoch(1) - height := specqbft.Height(slot) - - validSignedMessage := spectestingutils.TestingProposalMessageWithHeight(ks.Shares[1], 1, height) - validSignedMessage.Message.PrepareJustification = [][]byte{{1}} - - encodedValidSignedMessage, err := validSignedMessage.Encode() - require.NoError(t, err) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester), - Data: encodedValidSignedMessage, - } - - message, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg) - require.NoError(t, err) - - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait) - _, _, err = validator.validateSSVMessage(message, receivedAt, nil) - - require.ErrorContains(t, err, ErrMalformedPrepareJustifications.Error()) - }) - - // Send prepare justification message without a proposal message should receive an error - t.Run("non-proposal with prepare justification", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - slot := netCfg.Beacon.FirstSlotAtEpoch(1) - - msg := spectestingutils.TestingProposalMessageWithParams( - ks.Shares[1], spectypes.OperatorID(1), specqbft.FirstRound, specqbft.FirstHeight, spectestingutils.TestingQBFTRootData, - nil, - spectestingutils.MarshalJustifications([]*specqbft.SignedMessage{ - spectestingutils.TestingRoundChangeMessage(ks.Shares[1], spectypes.OperatorID(1)), - })) - msg.Message.MsgType = specqbft.PrepareMsgType - - encodedValidSignedMessage, err := msg.Encode() - require.NoError(t, err) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester), - Data: encodedValidSignedMessage, - } - - message, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg) - require.NoError(t, err) - - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait) - _, _, err = validator.validateSSVMessage(message, receivedAt, nil) - - expectedErr := ErrUnexpectedPrepareJustifications - expectedErr.got = specqbft.PrepareMsgType - require.ErrorIs(t, err, expectedErr) - }) - - // Send round change justification message without a proposal message should receive an error - t.Run("non-proposal with round change justification", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - slot := netCfg.Beacon.FirstSlotAtEpoch(1) - - msg := spectestingutils.TestingProposalMessageWithParams( - ks.Shares[1], spectypes.OperatorID(1), specqbft.FirstRound, specqbft.FirstHeight, spectestingutils.TestingQBFTRootData, - spectestingutils.MarshalJustifications([]*specqbft.SignedMessage{ - spectestingutils.TestingPrepareMessage(ks.Shares[1], spectypes.OperatorID(1)), - }), - nil, - ) - msg.Message.MsgType = specqbft.PrepareMsgType - - encodedValidSignedMessage, err := msg.Encode() - require.NoError(t, err) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester), - Data: encodedValidSignedMessage, - } - - message, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg) - require.NoError(t, err) - - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait) - _, _, err = validator.validateSSVMessage(message, receivedAt, nil) - - expectedErr := ErrUnexpectedRoundChangeJustifications - expectedErr.got = specqbft.PrepareMsgType - require.ErrorIs(t, err, expectedErr) - }) - - // Send round change justification message with a malformed message (1 byte) should receive an error - t.Run("malformed round change justification", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - slot := netCfg.Beacon.FirstSlotAtEpoch(1) - height := specqbft.Height(slot) - - validSignedMessage := spectestingutils.TestingProposalMessageWithHeight(ks.Shares[1], 1, height) - validSignedMessage.Message.RoundChangeJustification = [][]byte{{1}} - - encodedValidSignedMessage, err := validSignedMessage.Encode() - require.NoError(t, err) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester), - Data: encodedValidSignedMessage, - } - - message, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg) - require.NoError(t, err) - - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait) - _, _, err = validator.validateSSVMessage(message, receivedAt, nil) - - require.ErrorContains(t, err, ErrMalformedRoundChangeJustifications.Error()) - }) - - // Send message root hash that doesnt match the expected root hash should receive an error - t.Run("wrong root hash", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - slot := netCfg.Beacon.FirstSlotAtEpoch(1) - height := specqbft.Height(slot) - - validSignedMessage := spectestingutils.TestingProposalMessageWithHeight(ks.Shares[1], 1, height) - validSignedMessage.FullData = []byte{1} - - encodedValidSignedMessage, err := validSignedMessage.Encode() - require.NoError(t, err) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester), - Data: encodedValidSignedMessage, - } - - message, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg) - require.NoError(t, err) - - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait) - _, _, err = validator.validateSSVMessage(message, receivedAt, nil) - - expectedErr := ErrInvalidHash - require.ErrorIs(t, err, expectedErr) - }) - - // Receive proposal from same operator twice with different messages (same round) should receive an error - t.Run("double proposal with different data", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - slot := netCfg.Beacon.FirstSlotAtEpoch(1) - - signed1 := spectestingutils.TestingProposalMessageWithRound(ks.Shares[1], 1, 1) - encodedSigned1, err := signed1.Encode() - require.NoError(t, err) - - ssvMsg1 := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester), - Data: encodedSigned1, - } - - message1, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg1) - require.NoError(t, err) - - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait) - _, _, err = validator.validateSSVMessage(message1, receivedAt, nil) - require.NoError(t, err) - - signed2 := spectestingutils.TestingProposalMessageWithRound(ks.Shares[1], 1, 1) - signed2.FullData = []byte{1} - signed2.Message.Root, err = specqbft.HashDataRoot(signed2.FullData) - require.NoError(t, err) - - encodedSigned2, err := signed2.Encode() - require.NoError(t, err) - - ssvMsg2 := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester), - Data: encodedSigned2, - } - - message2, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg2) - require.NoError(t, err) - - _, _, err = validator.validateSSVMessage(message2, receivedAt, nil) - expectedErr := ErrDifferentProposalData - require.ErrorIs(t, err, expectedErr) - }) - - // Receive prepare from same operator twice with different messages (same round) should receive an error - t.Run("double prepare", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - slot := netCfg.Beacon.FirstSlotAtEpoch(1) - - signed1 := spectestingutils.TestingPrepareMessage(ks.Shares[1], 1) - encodedSigned1, err := signed1.Encode() - require.NoError(t, err) - - ssvMsg1 := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester), - Data: encodedSigned1, - } - - message1, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg1) - require.NoError(t, err) - - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait) - _, _, err = validator.validateSSVMessage(message1, receivedAt, nil) - require.NoError(t, err) - - signed2 := spectestingutils.TestingPrepareMessage(ks.Shares[1], 1) - require.NoError(t, err) - - encodedSigned2, err := signed2.Encode() - require.NoError(t, err) - - ssvMsg2 := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester), - Data: encodedSigned2, - } - - message2, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg2) - require.NoError(t, err) - - _, _, err = validator.validateSSVMessage(message2, receivedAt, nil) - expectedErr := ErrTooManySameTypeMessagesPerRound - expectedErr.got = "prepare, having pre-consensus: 0, proposal: 0, prepare: 1, commit: 0, decided: 0, round change: 0, post-consensus: 0" - require.ErrorIs(t, err, expectedErr) - }) - - // Receive commit from same operator twice with different messages (same round) should receive an error - t.Run("double commit", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - slot := netCfg.Beacon.FirstSlotAtEpoch(1) - - signed1 := spectestingutils.TestingCommitMessage(ks.Shares[1], 1) - encodedSigned1, err := signed1.Encode() - require.NoError(t, err) - - ssvMsg1 := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester), - Data: encodedSigned1, - } - - message1, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg1) - require.NoError(t, err) - - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait) - _, _, err = validator.validateSSVMessage(message1, receivedAt, nil) - require.NoError(t, err) - - signed2 := spectestingutils.TestingCommitMessage(ks.Shares[1], 1) - encodedSigned2, err := signed2.Encode() - require.NoError(t, err) - - ssvMsg2 := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester), - Data: encodedSigned2, - } - - message2, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg2) - require.NoError(t, err) - - _, _, err = validator.validateSSVMessage(message2, receivedAt, nil) - expectedErr := ErrTooManySameTypeMessagesPerRound - expectedErr.got = "commit, having pre-consensus: 0, proposal: 0, prepare: 0, commit: 1, decided: 0, round change: 0, post-consensus: 0" - require.ErrorIs(t, err, expectedErr) - }) - - // Receive round change from same operator twice with different messages (same round) should receive an error - t.Run("double round change", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - slot := netCfg.Beacon.FirstSlotAtEpoch(1) - - signed1 := spectestingutils.TestingRoundChangeMessage(ks.Shares[1], 1) - encodedSigned1, err := signed1.Encode() - require.NoError(t, err) - - ssvMsg1 := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester), - Data: encodedSigned1, - } - - message1, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg1) - require.NoError(t, err) - - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait) - _, _, err = validator.validateSSVMessage(message1, receivedAt, nil) - require.NoError(t, err) - - signed2 := spectestingutils.TestingRoundChangeMessage(ks.Shares[1], 1) - encodedSigned2, err := signed2.Encode() - require.NoError(t, err) - - ssvMsg2 := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester), - Data: encodedSigned2, - } - - message2, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg2) - require.NoError(t, err) - - _, _, err = validator.validateSSVMessage(message2, receivedAt, nil) - expectedErr := ErrTooManySameTypeMessagesPerRound - expectedErr.got = "round change, having pre-consensus: 0, proposal: 0, prepare: 0, commit: 0, decided: 0, round change: 1, post-consensus: 0" - require.ErrorIs(t, err, expectedErr) - }) - - // Receive too many decided messages should receive an error - t.Run("too many decided", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - slot := netCfg.Beacon.FirstSlotAtEpoch(1) - - msgID := spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester) - - signed := spectestingutils.TestingCommitMultiSignerMessageWithRound( - []*bls.SecretKey{ks.Shares[1], ks.Shares[2], ks.Shares[3]}, []spectypes.OperatorID{1, 2, 3}, 1) - encodedSigned, err := signed.Encode() - require.NoError(t, err) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: msgID, - Data: encodedSigned, - } - - message, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg) - require.NoError(t, err) - - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait) - - for i := 0; i < maxDecidedCount(len(share.Committee)); i++ { - _, _, err = validator.validateSSVMessage(message, receivedAt, nil) - require.NoError(t, err) - } - - _, _, err = validator.validateSSVMessage(message, receivedAt, nil) - expectedErr := ErrTooManySameTypeMessagesPerRound - expectedErr.got = "decided, having pre-consensus: 0, proposal: 0, prepare: 0, commit: 0, decided: 8, round change: 0, post-consensus: 0" - require.ErrorIs(t, err, expectedErr) - }) - - // Receive message from a round that is too high for that epoch should receive an error - t.Run("round too high", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - tests := map[spectypes.BeaconRole]specqbft.Round{ - spectypes.BNRoleAttester: 13, - spectypes.BNRoleAggregator: 13, - spectypes.BNRoleProposer: 7, - spectypes.BNRoleSyncCommittee: 7, - spectypes.BNRoleSyncCommitteeContribution: 7, - } - - for role, round := range tests { - role, round := role, round - t.Run(role.String(), func(t *testing.T) { - msgID := spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], role) - - signedMessage := spectestingutils.TestingPrepareMessageWithRound(ks.Shares[1], 1, round) - encodedMessage, err := signedMessage.Encode() - require.NoError(t, err) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: msgID, - Data: encodedMessage, - } - - message, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg) - require.NoError(t, err) - - timeToWait, err := validator.waitAfterSlotStart(role) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(0).Add(timeToWait) - _, _, err = validator.validateSSVMessage(message, receivedAt, nil) - require.ErrorContains(t, err, ErrRoundTooHigh.Error()) - }) - } - }) - - // Receive message from a round that is incorrect for current epoch should receive an error - t.Run("round already advanced", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - msgID := spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) - - signedMessage := spectestingutils.TestingPrepareMessageWithRound(ks.Shares[1], 1, 2) - encodedMessage, err := signedMessage.Encode() - require.NoError(t, err) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: msgID, - Data: encodedMessage, - } - - message, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg) - require.NoError(t, err) - - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait) - _, _, err = validator.validateSSVMessage(message, receivedAt, nil) - require.NoError(t, err) - - signedMessage = spectestingutils.TestingPrepareMessageWithRound(ks.Shares[1], 1, 1) - encodedMessage, err = signedMessage.Encode() - require.NoError(t, err) - - ssvMsg2 := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: msgID, - Data: encodedMessage, - } - - message2, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg2) - require.NoError(t, err) - - _, _, err = validator.validateSSVMessage(message2, receivedAt, nil) - require.ErrorContains(t, err, ErrRoundAlreadyAdvanced.Error()) - }) - - // Initialize tests for testing when sending a message with a slot before the current one - t.Run("slot already advanced", func(t *testing.T) { - msgID := spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) - height := specqbft.Height(slot) - - // Send a consensus message with a slot before the current one should cause an error - t.Run("consensus message", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - signedMessage := spectestingutils.TestingPrepareMessageWithHeight(ks.Shares[1], 1, height+1) - encodedMessage, err := signedMessage.Encode() - require.NoError(t, err) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: msgID, - Data: encodedMessage, - } - - message, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg) - require.NoError(t, err) - - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - _, _, err = validator.validateSSVMessage(message, netCfg.Beacon.GetSlotStartTime(slot+1).Add(timeToWait), nil) - require.NoError(t, err) - - signedMessage = spectestingutils.TestingPrepareMessageWithHeight(ks.Shares[1], 1, height) - encodedMessage, err = signedMessage.Encode() - require.NoError(t, err) - - ssvMsg2 := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: msgID, - Data: encodedMessage, - } - - message2, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg2) - require.NoError(t, err) - - timeToWait, err = validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - _, _, err = validator.validateSSVMessage(message2, netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait), nil) - require.ErrorContains(t, err, ErrSlotAlreadyAdvanced.Error()) - }) - - // Send a partial signature message with a slot before the current one should cause an error - t.Run("partial signature message", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - message := spectestingutils.PostConsensusAttestationMsg(ks.Shares[2], 2, height+1) - message.Message.Slot = phase0.Slot(height) + 1 - sig, err := spectestingutils.NewTestingKeyManager().SignRoot(message.Message, spectypes.PartialSignatureType, ks.Shares[2].GetPublicKey().Serialize()) - require.NoError(t, err) - message.Signature = sig - - encodedMessage, err := message.Encode() - require.NoError(t, err) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVPartialSignatureMsgType, - MsgID: msgID, - Data: encodedMessage, - } - - decodedMsg, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg) - require.NoError(t, err) - - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - _, _, err = validator.validateSSVMessage(decodedMsg, netCfg.Beacon.GetSlotStartTime(slot+1).Add(timeToWait), nil) - require.NoError(t, err) - - message = spectestingutils.PostConsensusAttestationMsg(ks.Shares[2], 2, height) - message.Message.Slot = phase0.Slot(height) - sig, err = spectestingutils.NewTestingKeyManager().SignRoot(message.Message, spectypes.PartialSignatureType, ks.Shares[2].GetPublicKey().Serialize()) - require.NoError(t, err) - message.Signature = sig - - encodedMessage, err = message.Encode() - require.NoError(t, err) - - ssvMsg2 := &spectypes.SSVMessage{ - MsgType: spectypes.SSVPartialSignatureMsgType, - MsgID: msgID, - Data: encodedMessage, - } - - decodedMsg2, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg2) - require.NoError(t, err) - - timeToWait, err = validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - _, _, err = validator.validateSSVMessage(decodedMsg2, netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait), nil) - require.ErrorContains(t, err, ErrSlotAlreadyAdvanced.Error()) - }) - }) - - // Receive an event message from an operator that is not myself should receive an error - t.Run("event message", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - msgID := spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester) - slot := netCfg.Beacon.FirstSlotAtEpoch(1) - - eventMsg := &ssvtypes.EventMsg{} - encoded, err := eventMsg.Encode() - require.NoError(t, err) - - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.MsgType(ssvmessage.SSVEventMsgType), - MsgID: msgID, - Data: encoded, - } - - message, err := genesisqueue.DecodeGenesisSSVMessage(ssvMsg) - require.NoError(t, err) - - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait) - _, _, err = validator.validateSSVMessage(message, receivedAt, nil) - require.ErrorIs(t, err, ErrEventMessage) - }) - - // Get error when receiving an SSV message with an invalid signature. - t.Run("signature verification", func(t *testing.T) { - epoch := phase0.Epoch(123456789) - - t.Run("unsigned message", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - validSignedMessage := spectestingutils.TestingProposalMessageWithHeight(ks.Shares[4], 4, specqbft.Height(epoch)) - - encoded, err := validSignedMessage.Encode() - require.NoError(t, err) - - message := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester), - Data: encoded, - } - - encodedMsg, err := commons.EncodeGenesisNetworkMsg(message) - require.NoError(t, err) - - topicID := commons.ValidatorTopicID(message.GetID().GetPubKey()) - pMsg := &pubsub.Message{ - Message: &pspb.Message{ - Topic: &topicID[0], - Data: encodedMsg, - }, - } - - slot := netCfg.Beacon.FirstSlotAtEpoch(epoch) - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait) - _, _, err = validator.validateP2PMessage(pMsg, receivedAt) - require.ErrorContains(t, err, ErrMalformedPubSubMessage.Error()) - }) - - t.Run("signed message", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - slot := netCfg.Beacon.FirstSlotAtEpoch(epoch) - - validSignedMessage := spectestingutils.TestingProposalMessageWithHeight(ks.Shares[1], 1, specqbft.Height(slot)) - - encoded, err := validSignedMessage.Encode() - require.NoError(t, err) - - message := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester), - Data: encoded, - } - - encodedMsg, err := commons.EncodeGenesisNetworkMsg(message) - require.NoError(t, err) - - privKey, err := keys.GeneratePrivateKey() - require.NoError(t, err) - - pubKey, err := privKey.Public().Base64() - require.NoError(t, err) - - const operatorID = spectypes.OperatorID(1) - - od := ®istrystorage.OperatorData{ - ID: operatorID, - PublicKey: pubKey, - OwnerAddress: common.Address{}, - } - - found, err := ns.SaveOperatorData(nil, od) - require.NoError(t, err) - require.False(t, found) - - signature, err := privKey.Sign(encodedMsg) - require.NoError(t, err) - - signedSSVMsg := &spectypes.SignedSSVMessage{ - Signature: signature, - OperatorID: operatorID, - Data: encodedMsg, - } - encodedMsg, err = signedSSVMsg.Encode() - require.NoError(t, err) - - topicID := commons.ValidatorTopicID(message.GetID().GetPubKey()) - pMsg := &pubsub.Message{ - Message: &pspb.Message{ - Topic: &topicID[0], - Data: encodedMsg, - }, - } - - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait) - _, _, err = validator.validateP2PMessage(pMsg, receivedAt) - require.NoError(t, err) - - require.NoError(t, ns.DeleteOperatorData(nil, operatorID)) - }) - - t.Run("unexpected operator ID", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - slot := netCfg.Beacon.FirstSlotAtEpoch(epoch) - - validSignedMessage := spectestingutils.TestingProposalMessageWithHeight(ks.Shares[1], 1, specqbft.Height(slot)) - - encoded, err := validSignedMessage.Encode() - require.NoError(t, err) - - message := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester), - Data: encoded, - } - - encodedMsg, err := commons.EncodeGenesisNetworkMsg(message) - require.NoError(t, err) - - privKey, err := keys.GeneratePrivateKey() - require.NoError(t, err) - - pubKey, err := privKey.Public().Base64() - require.NoError(t, err) - - const operatorID = spectypes.OperatorID(1) - - od := ®istrystorage.OperatorData{ - ID: operatorID, - PublicKey: pubKey, - OwnerAddress: common.Address{}, - } - - found, err := ns.SaveOperatorData(nil, od) - require.NoError(t, err) - require.False(t, found) - - signature, err := privKey.Sign(encodedMsg) - require.NoError(t, err) - - const unexpectedOperatorID = 2 - signedSSVMsg := &spectypes.SignedSSVMessage{ - Signature: signature, - OperatorID: unexpectedOperatorID, - Data: encodedMsg, - } - encodedMsg, err = signedSSVMsg.Encode() - require.NoError(t, err) - - topicID := commons.ValidatorTopicID(message.GetID().GetPubKey()) - pMsg := &pubsub.Message{ - Message: &pspb.Message{ - Topic: &topicID[0], - Data: encodedMsg, - }, - } - - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait) - _, _, err = validator.validateP2PMessage(pMsg, receivedAt) - require.ErrorContains(t, err, ErrOperatorNotFound.Error()) - - require.NoError(t, ns.DeleteOperatorData(nil, operatorID)) - }) - - t.Run("malformed signature", func(t *testing.T) { - validator := New(netCfg, WithNodeStorage(ns)).(*messageValidator) - - slot := netCfg.Beacon.FirstSlotAtEpoch(epoch) - - validSignedMessage := spectestingutils.TestingProposalMessageWithHeight(ks.Shares[1], 1, specqbft.Height(slot)) - - encoded, err := validSignedMessage.Encode() - require.NoError(t, err) - - message := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: spectypes.NewMsgID(spectypes.DomainType(netCfg.DomainType()), share.ValidatorPubKey[:], roleAttester), - Data: encoded, - } - - encodedMsg, err := commons.EncodeGenesisNetworkMsg(message) - require.NoError(t, err) - - privKey, err := keys.GeneratePrivateKey() - require.NoError(t, err) - - pubKey, err := privKey.Public().Base64() - require.NoError(t, err) - - const operatorID = spectypes.OperatorID(1) - - od := ®istrystorage.OperatorData{ - ID: operatorID, - PublicKey: pubKey, - OwnerAddress: common.Address{}, - } - - found, err := ns.SaveOperatorData(nil, od) - require.NoError(t, err) - require.False(t, found) - - signature := bytes.Repeat([]byte{1}, 256) - signedSSVMsg := &spectypes.SignedSSVMessage{ - Signature: signature, - OperatorID: operatorID, - Data: encodedMsg, - } - encodedMsg, err = signedSSVMsg.Encode() - require.NoError(t, err) - - topicID := commons.ValidatorTopicID(message.GetID().GetPubKey()) - pMsg := &pubsub.Message{ - Message: &pspb.Message{ - Topic: &topicID[0], - Data: encodedMsg, - }, - } - - timeToWait, err := validator.waitAfterSlotStart(roleAttester) - require.NoError(t, err) - receivedAt := netCfg.Beacon.GetSlotStartTime(slot).Add(timeToWait) - _, _, err = validator.validateP2PMessage(pMsg, receivedAt) - require.ErrorContains(t, err, ErrSignatureVerification.Error()) - - require.NoError(t, ns.DeleteOperatorData(nil, operatorID)) - }) - }) -} diff --git a/message/validation/logger_fields.go b/message/validation/logger_fields.go index a94306d813..5e63b5ad55 100644 --- a/message/validation/logger_fields.go +++ b/message/validation/logger_fields.go @@ -2,7 +2,6 @@ package validation import ( "github.com/attestantio/go-eth2-client/spec/phase0" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" specqbft "github.com/ssvlabs/ssv-spec/qbft" spectypes "github.com/ssvlabs/ssv-spec/types" "go.uber.org/zap" @@ -103,31 +102,3 @@ func (mv *messageValidator) addDutyIDField(lf *LoggerFields) { } } } - -// LoggerFields provides details about a message. It's used for logging and metrics. -type GenesisLoggerFields struct { - DutyExecutorID []byte - Role genesisspectypes.BeaconRole - SSVMessageType genesisspectypes.MsgType - Slot phase0.Slot - Consensus *ConsensusFields -} - -// AsZapFields returns zap logging fields for the descriptor. -func (d GenesisLoggerFields) AsZapFields() []zapcore.Field { - result := []zapcore.Field{ - fields.DutyExecutorID(d.DutyExecutorID), - fields.BeaconRole(spectypes.BeaconRole(d.Role)), - zap.String("ssv_message_type", ssvmessage.MsgTypeToString(spectypes.MsgType(d.SSVMessageType))), - fields.Slot(d.Slot), - } - - if d.Consensus != nil { - result = append(result, - fields.Round(d.Consensus.Round), - zap.String("qbft_message_type", ssvmessage.QBFTMsgTypeToString(d.Consensus.QBFTMessageType)), - ) - } - - return result -} diff --git a/message/validation/signed_ssv_message.go b/message/validation/signed_ssv_message.go index 39d61a61e9..1c4b3aca74 100644 --- a/message/validation/signed_ssv_message.go +++ b/message/validation/signed_ssv_message.go @@ -18,12 +18,6 @@ func (mv *messageValidator) decodeSignedSSVMessage(pMsg *pubsub.Message) (*spect if err := signedSSVMessage.Decode(pMsg.GetData()); err != nil { e := ErrMalformedPubSubMessage e.innerErr = err - - // Ignore genesis messages in the first slot of the fork epoch - if mv.netCfg.Beacon.EstimatedCurrentSlot() == mv.netCfg.Beacon.FirstSlotAtEpoch(mv.netCfg.AlanForkEpoch) { - e.reject = false - } - return nil, e } @@ -121,7 +115,7 @@ func (mv *messageValidator) validateSSVMessage(ssvMessage *spectypes.SSVMessage) } // Rule: If domain is different then self domain - domain := mv.netCfg.DomainType() + domain := mv.netCfg.DomainType if !bytes.Equal(ssvMessage.GetID().GetDomain(), domain[:]) { err := ErrWrongDomain err.got = hex.EncodeToString(ssvMessage.MsgID.GetDomain()) diff --git a/message/validation/validation_test.go b/message/validation/validation_test.go index 4cc937ea85..1a483570ae 100644 --- a/message/validation/validation_test.go +++ b/message/validation/validation_test.go @@ -53,7 +53,6 @@ func Test_ValidateSSVMessage(t *testing.T) { require.NoError(t, err) netCfg := networkconfig.TestNetwork - netCfg.AlanForkEpoch = math.MaxUint64 // use genesis domain to be able to use message templates from spec ks := spectestingutils.Testing4SharesSet() shares := generateShares(t, ks, ns, netCfg) @@ -128,8 +127,8 @@ func Test_ValidateSSVMessage(t *testing.T) { nonCommitteeRole := spectypes.RoleAggregator encodedCommitteeID := append(bytes.Repeat([]byte{0}, 16), committeeID[:]...) - committeeIdentifier := spectypes.NewMsgID(netCfg.DomainType(), encodedCommitteeID, committeeRole) - nonCommitteeIdentifier := spectypes.NewMsgID(netCfg.DomainType(), ks.ValidatorPK.Serialize(), nonCommitteeRole) + committeeIdentifier := spectypes.NewMsgID(netCfg.DomainType, encodedCommitteeID, committeeRole) + nonCommitteeIdentifier := spectypes.NewMsgID(netCfg.DomainType, ks.ValidatorPK.Serialize(), nonCommitteeRole) // Message validation happy flow, messages are not ignored or rejected and there are no errors t.Run("happy flow", func(t *testing.T) { @@ -377,10 +376,13 @@ func Test_ValidateSSVMessage(t *testing.T) { sk, err := eth2types.GenerateBLSPrivateKey() require.NoError(t, err) - unknown := spectypes.NewMsgID(netCfg.DomainType(), sk.PublicKey().Marshal(), nonCommitteeRole) + unknown := spectypes.NewMsgID(netCfg.DomainType, sk.PublicKey().Marshal(), nonCommitteeRole) signedSSVMessage := generateSignedMessage(ks, unknown, slot) - topicID := commons.ValidatorTopicID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID())[0] + _, exists := validatorStore.Validator(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()) + require.False(t, exists) + + topicID := commons.CommitteeTopicID(shares.active.CommitteeID())[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, time.Now()) expectedErr := ErrUnknownValidator expectedErr.got = hex.EncodeToString(sk.PublicKey().Marshal()) @@ -394,7 +396,7 @@ func Test_ValidateSSVMessage(t *testing.T) { slot := netCfg.Beacon.FirstSlotAtEpoch(1) unknownCommitteeID := bytes.Repeat([]byte{1}, 48) - unknownIdentifier := spectypes.NewMsgID(netCfg.DomainType(), unknownCommitteeID, committeeRole) + unknownIdentifier := spectypes.NewMsgID(netCfg.DomainType, unknownCommitteeID, committeeRole) signedSSVMessage := generateSignedMessage(ks, unknownIdentifier, slot) topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] @@ -414,12 +416,12 @@ func Test_ValidateSSVMessage(t *testing.T) { badIdentifier := spectypes.NewMsgID(wrongDomain, encodedCommitteeID, committeeRole) signedSSVMessage := generateSignedMessage(ks, badIdentifier, slot) - topicID := commons.ValidatorTopicID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID())[0] + topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] receivedAt := netCfg.Beacon.GetSlotStartTime(slot) _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, receivedAt) expectedErr := ErrWrongDomain expectedErr.got = hex.EncodeToString(wrongDomain[:]) - domain := netCfg.DomainType() + domain := netCfg.DomainType expectedErr.want = hex.EncodeToString(domain[:]) require.ErrorIs(t, err, expectedErr) }) @@ -430,10 +432,10 @@ func Test_ValidateSSVMessage(t *testing.T) { slot := netCfg.Beacon.FirstSlotAtEpoch(1) - badIdentifier := spectypes.NewMsgID(netCfg.DomainType(), encodedCommitteeID, math.MaxInt32) + badIdentifier := spectypes.NewMsgID(netCfg.DomainType, encodedCommitteeID, math.MaxInt32) signedSSVMessage := generateSignedMessage(ks, badIdentifier, slot) - topicID := commons.ValidatorTopicID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID())[0] + topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] receivedAt := netCfg.Beacon.GetSlotStartTime(slot) _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, receivedAt) require.ErrorIs(t, err, ErrInvalidRole) @@ -445,7 +447,7 @@ func Test_ValidateSSVMessage(t *testing.T) { slot := netCfg.Beacon.FirstSlotAtEpoch(1) - badIdentifier := spectypes.NewMsgID(netCfg.DomainType(), shares.active.ValidatorPubKey[:], spectypes.RoleValidatorRegistration) + badIdentifier := spectypes.NewMsgID(netCfg.DomainType, shares.active.ValidatorPubKey[:], spectypes.RoleValidatorRegistration) signedSSVMessage := generateSignedMessage(ks, badIdentifier, slot) topicID := commons.CommitteeTopicID(committeeID)[0] @@ -455,7 +457,7 @@ func Test_ValidateSSVMessage(t *testing.T) { expectedErr.got = spectypes.RoleValidatorRegistration require.ErrorIs(t, err, expectedErr) - badIdentifier = spectypes.NewMsgID(netCfg.DomainType(), shares.active.ValidatorPubKey[:], spectypes.RoleVoluntaryExit) + badIdentifier = spectypes.NewMsgID(netCfg.DomainType, shares.active.ValidatorPubKey[:], spectypes.RoleVoluntaryExit) signedSSVMessage = generateSignedMessage(ks, badIdentifier, slot) _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, receivedAt) @@ -469,10 +471,10 @@ func Test_ValidateSSVMessage(t *testing.T) { slot := netCfg.Beacon.FirstSlotAtEpoch(1) - liquidatedIdentifier := spectypes.NewMsgID(netCfg.DomainType(), shares.liquidated.ValidatorPubKey[:], nonCommitteeRole) + liquidatedIdentifier := spectypes.NewMsgID(netCfg.DomainType, shares.liquidated.ValidatorPubKey[:], nonCommitteeRole) signedSSVMessage := generateSignedMessage(ks, liquidatedIdentifier, slot) - topicID := commons.ValidatorTopicID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID())[0] + topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] receivedAt := netCfg.Beacon.GetSlotStartTime(slot) _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, receivedAt) expectedErr := ErrValidatorLiquidated @@ -485,10 +487,10 @@ func Test_ValidateSSVMessage(t *testing.T) { slot := netCfg.Beacon.FirstSlotAtEpoch(1) - inactiveIdentifier := spectypes.NewMsgID(netCfg.DomainType(), shares.inactive.ValidatorPubKey[:], nonCommitteeRole) + inactiveIdentifier := spectypes.NewMsgID(netCfg.DomainType, shares.inactive.ValidatorPubKey[:], nonCommitteeRole) signedSSVMessage := generateSignedMessage(ks, inactiveIdentifier, slot) - topicID := commons.ValidatorTopicID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID())[0] + topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] receivedAt := netCfg.Beacon.GetSlotStartTime(slot) _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, receivedAt) expectedErr := ErrValidatorNotAttesting @@ -502,11 +504,11 @@ func Test_ValidateSSVMessage(t *testing.T) { slot := netCfg.Beacon.FirstSlotAtEpoch(1) - nonUpdatedMetadataNextEpochIdentifier := spectypes.NewMsgID(netCfg.DomainType(), shares.nonUpdatedMetadataNextEpoch.ValidatorPubKey[:], nonCommitteeRole) + nonUpdatedMetadataNextEpochIdentifier := spectypes.NewMsgID(netCfg.DomainType, shares.nonUpdatedMetadataNextEpoch.ValidatorPubKey[:], nonCommitteeRole) signedSSVMessage := generateSignedMessage(ks, nonUpdatedMetadataNextEpochIdentifier, slot) receivedAt := netCfg.Beacon.GetSlotStartTime(slot) - topicID := commons.ValidatorTopicID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID())[0] + topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, receivedAt) expectedErr := ErrValidatorNotAttesting expectedErr.got = eth2apiv1.ValidatorStatePendingQueued.String() @@ -520,7 +522,7 @@ func Test_ValidateSSVMessage(t *testing.T) { slot := netCfg.Beacon.EstimatedCurrentSlot() - nonUpdatedMetadataIdentifier := spectypes.NewMsgID(netCfg.DomainType(), shares.nonUpdatedMetadata.ValidatorPubKey[:], nonCommitteeRole) + nonUpdatedMetadataIdentifier := spectypes.NewMsgID(netCfg.DomainType, shares.nonUpdatedMetadata.ValidatorPubKey[:], nonCommitteeRole) qbftMessage := &specqbft.Message{ MsgType: specqbft.ProposalMsgType, Height: specqbft.Height(slot), @@ -548,11 +550,11 @@ func Test_ValidateSSVMessage(t *testing.T) { slot := netCfg.Beacon.FirstSlotAtEpoch(1) - noMetadataIdentifier := spectypes.NewMsgID(netCfg.DomainType(), shares.noMetadata.ValidatorPubKey[:], nonCommitteeRole) + noMetadataIdentifier := spectypes.NewMsgID(netCfg.DomainType, shares.noMetadata.ValidatorPubKey[:], nonCommitteeRole) signedSSVMessage := generateSignedMessage(ks, noMetadataIdentifier, slot) receivedAt := netCfg.Beacon.GetSlotStartTime(slot) - topicID := commons.ValidatorTopicID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID())[0] + topicID := commons.CommitteeTopicID(spectypes.CommitteeID(signedSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, receivedAt) require.ErrorIs(t, err, ErrNoShareMetadata) }) @@ -571,7 +573,7 @@ func Test_ValidateSSVMessage(t *testing.T) { }) role := spectypes.RoleAggregator - identifier := spectypes.NewMsgID(netCfg.DomainType(), ks.ValidatorPK.Serialize(), role) + identifier := spectypes.NewMsgID(netCfg.DomainType, ks.ValidatorPK.Serialize(), role) signedSSVMessage := generateSignedMessage(ks, identifier, slot) // First duty. @@ -616,7 +618,7 @@ func Test_ValidateSSVMessage(t *testing.T) { }) validator := New(netCfg, validatorStore, ds, signatureVerifier).(*messageValidator) - identifier := spectypes.NewMsgID(netCfg.DomainType(), ks.ValidatorPK.Serialize(), spectypes.RoleProposer) + identifier := spectypes.NewMsgID(netCfg.DomainType, ks.ValidatorPK.Serialize(), spectypes.RoleProposer) signedSSVMessage := generateSignedMessage(ks, identifier, slot) topicID := commons.CommitteeTopicID(committeeID)[0] @@ -652,7 +654,7 @@ func Test_ValidateSSVMessage(t *testing.T) { dutyExecutorID := shares.active.ValidatorPubKey[:] ssvMessage := &spectypes.SSVMessage{ MsgType: spectypes.SSVPartialSignatureMsgType, - MsgID: spectypes.NewMsgID(mockNetworkConfig.DomainType(), dutyExecutorID, spectypes.RoleProposer), + MsgID: spectypes.NewMsgID(mockNetworkConfig.DomainType, dutyExecutorID, spectypes.RoleProposer), Data: encodedMessages, } @@ -688,7 +690,7 @@ func Test_ValidateSSVMessage(t *testing.T) { dutyExecutorID := shares.active.ValidatorPubKey[:] ssvMessage := &spectypes.SSVMessage{ MsgType: spectypes.SSVPartialSignatureMsgType, - MsgID: spectypes.NewMsgID(mockNetworkConfig.DomainType(), dutyExecutorID, spectypes.RoleProposer), + MsgID: spectypes.NewMsgID(mockNetworkConfig.DomainType, dutyExecutorID, spectypes.RoleProposer), Data: encodedMessages, } @@ -752,7 +754,7 @@ func Test_ValidateSSVMessage(t *testing.T) { msg.OperatorIDs = []spectypes.OperatorID{0} receivedAt := netCfg.Beacon.GetSlotStartTime(slot) - topicID := commons.ValidatorTopicID(msg.SSVMessage.GetID().GetDutyExecutorID())[0] + topicID := commons.CommitteeTopicID(spectypes.CommitteeID(msg.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] _, err = validator.handleSignedSSVMessage(msg, topicID, receivedAt) require.ErrorIs(t, err, ErrZeroSigner) }) @@ -763,7 +765,9 @@ func Test_ValidateSSVMessage(t *testing.T) { slot := netCfg.Beacon.FirstSlotAtEpoch(1) - partialSigSSVMessage := spectestingutils.SignPartialSigSSVMessage(ks, spectestingutils.SSVMsgAggregator(nil, spectestingutils.PostConsensusAggregatorMsg(ks.Shares[1], 1))) + ssvMessage := spectestingutils.SSVMsgAggregator(nil, spectestingutils.PostConsensusAggregatorMsg(ks.Shares[1], 1)) + ssvMessage.MsgID = committeeIdentifier + partialSigSSVMessage := spectestingutils.SignPartialSigSSVMessage(ks, ssvMessage) partialSigSSVMessage.OperatorIDs = []spectypes.OperatorID{2} receivedAt := netCfg.Beacon.GetSlotStartTime(slot) @@ -783,7 +787,9 @@ func Test_ValidateSSVMessage(t *testing.T) { messages := spectestingutils.PostConsensusAggregatorMsg(ks.Shares[1], 1) messages.Messages = nil - signedSSVMessage := spectestingutils.SignedSSVMessageWithSigner(1, ks.OperatorKeys[1], spectestingutils.SSVMsgAggregator(nil, messages)) + ssvMessage := spectestingutils.SSVMsgAggregator(nil, messages) + ssvMessage.MsgID = committeeIdentifier + signedSSVMessage := spectestingutils.SignedSSVMessageWithSigner(1, ks.OperatorKeys[1], ssvMessage) receivedAt := netCfg.Beacon.GetSlotStartTime(slot) topicID := commons.CommitteeTopicID(committeeID)[0] @@ -801,7 +807,7 @@ func Test_ValidateSSVMessage(t *testing.T) { partialSigSSVMessage.Signatures = [][]byte{{1}} receivedAt := netCfg.Beacon.GetSlotStartTime(slot) - topicID := commons.ValidatorTopicID(partialSigSSVMessage.SSVMessage.GetID().GetDutyExecutorID())[0] + topicID := commons.CommitteeTopicID(spectypes.CommitteeID(partialSigSSVMessage.SSVMessage.GetID().GetDutyExecutorID()[16:]))[0] _, err = validator.handleSignedSSVMessage(partialSigSSVMessage, topicID, receivedAt) require.ErrorContains(t, err, ErrWrongRSASignatureSize.Error()) }) @@ -847,7 +853,7 @@ func Test_ValidateSSVMessage(t *testing.T) { } ssvMessage := &spectypes.SSVMessage{ MsgType: spectypes.SSVPartialSignatureMsgType, - MsgID: spectypes.NewMsgID(spectestingutils.TestingSSVDomainType, dutyExecutorID, role), + MsgID: spectypes.NewMsgID(netCfg.DomainType, dutyExecutorID, role), Data: encodedMessages, } @@ -874,10 +880,9 @@ func Test_ValidateSSVMessage(t *testing.T) { encodedMessages, err := messages.Encode() require.NoError(t, err) - pk := shares.active.ValidatorPubKey[:] ssvMessage := &spectypes.SSVMessage{ MsgType: spectypes.SSVPartialSignatureMsgType, - MsgID: spectypes.NewMsgID(spectestingutils.TestingSSVDomainType, pk, nonCommitteeRole), + MsgID: committeeIdentifier, Data: encodedMessages, } @@ -921,13 +926,13 @@ func Test_ValidateSSVMessage(t *testing.T) { encodedMessages, err := messages.Encode() require.NoError(t, err) - pk := shares.active.ValidatorPubKey[:] + dutyExecutorID := shares.active.ValidatorPubKey[:] if validator.committeeRole(role) { - pk = encodedCommitteeID + dutyExecutorID = encodedCommitteeID } ssvMessage := &spectypes.SSVMessage{ MsgType: spectypes.SSVPartialSignatureMsgType, - MsgID: spectypes.NewMsgID(spectestingutils.TestingSSVDomainType, pk, role), + MsgID: spectypes.NewMsgID(netCfg.DomainType, dutyExecutorID, role), Data: encodedMessages, } @@ -935,6 +940,7 @@ func Test_ValidateSSVMessage(t *testing.T) { receivedAt := netCfg.Beacon.GetSlotStartTime(spectestingutils.TestingDutySlot) topicID := commons.CommitteeTopicID(committeeID)[0] + t.Log(signedSSVMessage.SSVMessage.MsgID.GetDomain()) _, err = validator.handleSignedSSVMessage(signedSSVMessage, topicID, receivedAt) require.ErrorContains(t, err, ErrPartialSignatureTypeRoleMismatch.Error()) }) @@ -1130,7 +1136,7 @@ func Test_ValidateSSVMessage(t *testing.T) { dutyExecutorID = encodedCommitteeID } - msgID := spectypes.NewMsgID(netCfg.DomainType(), dutyExecutorID, role) + msgID := spectypes.NewMsgID(netCfg.DomainType, dutyExecutorID, role) signedSSVMessage := generateSignedMessage(ks, msgID, slot) topicID := commons.CommitteeTopicID(committeeID)[0] @@ -1297,7 +1303,7 @@ func Test_ValidateSSVMessage(t *testing.T) { slot := netCfg.Beacon.FirstSlotAtEpoch(1) - identifier := spectypes.NewMsgID(netCfg.DomainType(), ks.ValidatorPK.Serialize(), spectypes.RoleProposer) + identifier := spectypes.NewMsgID(netCfg.DomainType, ks.ValidatorPK.Serialize(), spectypes.RoleProposer) signedSSVMessage := generateSignedMessage(ks, identifier, slot, func(message *specqbft.Message) { message.MsgType = specqbft.PrepareMsgType }) @@ -1455,7 +1461,7 @@ func Test_ValidateSSVMessage(t *testing.T) { dutyExecutorID = encodedCommitteeID } - msgID := spectypes.NewMsgID(netCfg.DomainType(), dutyExecutorID, role) + msgID := spectypes.NewMsgID(netCfg.DomainType, dutyExecutorID, role) signedSSVMessage := generateSignedMessage(ks, msgID, slot, func(message *specqbft.Message) { message.MsgType = specqbft.PrepareMsgType message.Round = round @@ -1609,7 +1615,7 @@ func Test_ValidateSSVMessage(t *testing.T) { slot := netCfg.Beacon.FirstSlotAtEpoch(1) signedSSVMessage := generateSignedMessage(ks, committeeIdentifier, slot, func(message *specqbft.Message) { - wrongID := spectypes.NewMsgID(netCfg.DomainType(), encodedCommitteeID[:], nonCommitteeRole) + wrongID := spectypes.NewMsgID(netCfg.DomainType, encodedCommitteeID[:], nonCommitteeRole) message.Identifier = wrongID[:] }) signedSSVMessage.SSVMessage.MsgID = committeeIdentifier @@ -1648,7 +1654,9 @@ func Test_ValidateSSVMessage(t *testing.T) { slot := netCfg.Beacon.FirstSlotAtEpoch(1) - signedSSVMessage := spectestingutils.SignPartialSigSSVMessage(ks, spectestingutils.SSVMsgAggregator(nil, spectestingutils.PostConsensusAggregatorMsg(ks.Shares[1], 1))) + ssvMessage := spectestingutils.SSVMsgAggregator(nil, spectestingutils.PostConsensusAggregatorMsg(ks.Shares[1], 1)) + ssvMessage.MsgID = committeeIdentifier + signedSSVMessage := spectestingutils.SignPartialSigSSVMessage(ks, ssvMessage) signedSSVMessage.FullData = []byte{1} receivedAt := netCfg.Beacon.GetSlotStartTime(slot) @@ -1663,7 +1671,9 @@ func Test_ValidateSSVMessage(t *testing.T) { slot := netCfg.Beacon.FirstSlotAtEpoch(1) - signedSSVMessage := spectestingutils.SignPartialSigSSVMessage(ks, spectestingutils.SSVMsgAggregator(nil, spectestingutils.PostConsensusAggregatorMsg(ks.Shares[1], 1))) + ssvMessage := spectestingutils.SSVMsgAggregator(nil, spectestingutils.PostConsensusAggregatorMsg(ks.Shares[1], 1)) + ssvMessage.MsgID = committeeIdentifier + signedSSVMessage := spectestingutils.SignPartialSigSSVMessage(ks, ssvMessage) signedSSVMessage.OperatorIDs = []spectypes.OperatorID{1, 2} signedSSVMessage.Signatures = append(signedSSVMessage.Signatures, signedSSVMessage.Signatures[0]) @@ -1687,10 +1697,9 @@ func Test_ValidateSSVMessage(t *testing.T) { data, err := messages.Encode() require.NoError(t, err) - msgID := spectypes.NewMsgID(spectestingutils.TestingSSVDomainType, encodedCommitteeID, committeeRole) ssvMessage := &spectypes.SSVMessage{ MsgType: spectypes.SSVPartialSignatureMsgType, - MsgID: msgID, + MsgID: committeeIdentifier, Data: data, } @@ -1716,10 +1725,9 @@ func Test_ValidateSSVMessage(t *testing.T) { data, err := messages.Encode() require.NoError(t, err) - msgID := spectypes.NewMsgID(spectestingutils.TestingSSVDomainType, encodedCommitteeID, committeeRole) ssvMessage := &spectypes.SSVMessage{ MsgType: spectypes.SSVPartialSignatureMsgType, - MsgID: msgID, + MsgID: committeeIdentifier, Data: data, } diff --git a/migrations/migration_4_configlock_add_alan_fork_to_network_name.go b/migrations/migration_4_configlock_add_alan_fork_to_network_name.go index 844f91a05d..6a49712c01 100644 --- a/migrations/migration_4_configlock_add_alan_fork_to_network_name.go +++ b/migrations/migration_4_configlock_add_alan_fork_to_network_name.go @@ -32,7 +32,7 @@ var migration_4_configlock_add_alan_fork_to_network_name = Migration{ return fmt.Errorf("failed to get network config by name: %w", err) } - config.NetworkName = networkConfig.AlanForkNetworkName() + config.NetworkName = networkConfig.NetworkName() if err := nodeStorage.SaveConfig(txn, config); err != nil { return fmt.Errorf("failed to save config: %w", err) } diff --git a/monitoring/metricsreporter/genesis.go b/monitoring/metricsreporter/genesis.go deleted file mode 100644 index 269a9e42ef..0000000000 --- a/monitoring/metricsreporter/genesis.go +++ /dev/null @@ -1,57 +0,0 @@ -// genesis.go is DEPRECATED - -package metricsreporter - -import ( - "strconv" - - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" -) - -type Genesis interface { - GenesisMessageAccepted(role genesisspectypes.BeaconRole, round genesisspecqbft.Round) // DEPRECATED - GenesisMessageIgnored(reason string, role genesisspectypes.BeaconRole, round genesisspecqbft.Round) // DEPRECATED - GenesisMessageRejected(reason string, role genesisspectypes.BeaconRole, round genesisspecqbft.Round) // DEPRECATED -} - -// DEPRECATED -func (m *metricsReporter) GenesisMessageAccepted( - role genesisspectypes.BeaconRole, - round genesisspecqbft.Round, -) { - messageValidationResult.WithLabelValues( - messageAccepted, - "", - role.String(), - strconv.FormatUint(uint64(round), 10), - ).Inc() -} - -// DEPRECATED -func (m *metricsReporter) GenesisMessageIgnored( - reason string, - role genesisspectypes.BeaconRole, - round genesisspecqbft.Round, -) { - messageValidationResult.WithLabelValues( - messageIgnored, - reason, - role.String(), - strconv.FormatUint(uint64(round), 10), - ).Inc() -} - -// DEPRECATED -func (m *metricsReporter) GenesisMessageRejected( - reason string, - role genesisspectypes.BeaconRole, - round genesisspecqbft.Round, -) { - messageValidationResult.WithLabelValues( - messageRejected, - reason, - role.String(), - strconv.FormatUint(uint64(round), 10), - ).Inc() -} diff --git a/monitoring/metricsreporter/metrics_reporter.go b/monitoring/metricsreporter/metrics_reporter.go index efcc02a7dc..73c59911ca 100644 --- a/monitoring/metricsreporter/metrics_reporter.go +++ b/monitoring/metricsreporter/metrics_reporter.go @@ -10,12 +10,10 @@ import ( "github.com/libp2p/go-libp2p/core/peer" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" specqbft "github.com/ssvlabs/ssv-spec/qbft" spectypes "github.com/ssvlabs/ssv-spec/types" "go.uber.org/zap" - genesisssvmessage "github.com/ssvlabs/ssv/protocol/genesis/message" ssvmessage "github.com/ssvlabs/ssv/protocol/v2/message" ) @@ -161,8 +159,6 @@ var ( ) type MetricsReporter interface { - Genesis // DEPRECATED - SSVNodeHealthy() SSVNodeNotHealthy() ExecutionClientReady() @@ -192,7 +188,6 @@ type MetricsReporter interface { MessageIgnored(reason string, role spectypes.RunnerRole, round specqbft.Round) MessageRejected(reason string, role spectypes.RunnerRole, round specqbft.Round) SSVMessageType(msgType spectypes.MsgType) - GenesisSSVMessageType(msgType genesisspectypes.MsgType) ConsensusMsgType(msgType specqbft.MessageType, signers int) MessageValidationDuration(duration time.Duration, labels ...string) SignatureValidationDuration(duration time.Duration, labels ...string) @@ -388,9 +383,6 @@ func (m *metricsReporter) SSVMessageType(msgType spectypes.MsgType) { messageValidationSSVType.WithLabelValues(ssvmessage.MsgTypeToString(msgType)).Inc() } -func (m *metricsReporter) GenesisSSVMessageType(msgType genesisspectypes.MsgType) { - messageValidationSSVType.WithLabelValues(genesisssvmessage.MsgTypeToString(msgType)).Inc() -} func (m *metricsReporter) ConsensusMsgType(msgType specqbft.MessageType, signers int) { messageValidationConsensusType.WithLabelValues(ssvmessage.QBFTMsgTypeToString(msgType), strconv.Itoa(signers)).Inc() } diff --git a/monitoring/metricsreporter/nop_metrics_reporter.go b/monitoring/metricsreporter/nop_metrics_reporter.go index a17ed5b36d..37070819d7 100644 --- a/monitoring/metricsreporter/nop_metrics_reporter.go +++ b/monitoring/metricsreporter/nop_metrics_reporter.go @@ -4,8 +4,6 @@ import ( "time" "github.com/libp2p/go-libp2p/core/peer" - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" specqbft "github.com/ssvlabs/ssv-spec/qbft" spectypes "github.com/ssvlabs/ssv-spec/types" ) @@ -16,43 +14,36 @@ func NewNop() MetricsReporter { return &nopMetrics{} } -func (n *nopMetrics) SSVNodeHealthy() {} -func (n *nopMetrics) SSVNodeNotHealthy() {} -func (n *nopMetrics) ExecutionClientReady() {} -func (n *nopMetrics) ExecutionClientSyncing() {} -func (n *nopMetrics) ExecutionClientFailure() {} -func (n *nopMetrics) ExecutionClientLastFetchedBlock(block uint64) {} -func (n *nopMetrics) OperatorPublicKey(operatorID spectypes.OperatorID, publicKey []byte) {} -func (n *nopMetrics) ValidatorInactive(publicKey []byte) {} -func (n *nopMetrics) ValidatorNoIndex(publicKey []byte) {} -func (n *nopMetrics) ValidatorError(publicKey []byte) {} -func (n *nopMetrics) ValidatorReady(publicKey []byte) {} -func (n *nopMetrics) ValidatorNotActivated(publicKey []byte) {} -func (n *nopMetrics) ValidatorExiting(publicKey []byte) {} -func (n *nopMetrics) ValidatorSlashed(publicKey []byte) {} -func (n *nopMetrics) ValidatorNotFound(publicKey []byte) {} -func (n *nopMetrics) ValidatorPending(publicKey []byte) {} -func (n *nopMetrics) ValidatorRemoved(publicKey []byte) {} -func (n *nopMetrics) ValidatorUnknown(publicKey []byte) {} -func (n *nopMetrics) EventProcessed(eventName string) {} -func (n *nopMetrics) EventProcessingFailed(eventName string) {} -func (n *nopMetrics) MessagesReceivedFromPeer(peerId peer.ID) {} -func (n *nopMetrics) MessagesReceivedTotal() {} -func (n *nopMetrics) MessageValidationRSAVerifications() {} -func (n *nopMetrics) LastBlockProcessed(block uint64) {} -func (n *nopMetrics) LogsProcessingError(err error) {} -func (n *nopMetrics) GenesisMessageAccepted(role genesisspectypes.BeaconRole, round genesisspecqbft.Round) { -} -func (n *nopMetrics) GenesisMessageIgnored(reason string, role genesisspectypes.BeaconRole, round genesisspecqbft.Round) { -} -func (n *nopMetrics) GenesisMessageRejected(reason string, role genesisspectypes.BeaconRole, round genesisspecqbft.Round) { -} +func (n *nopMetrics) SSVNodeHealthy() {} +func (n *nopMetrics) SSVNodeNotHealthy() {} +func (n *nopMetrics) ExecutionClientReady() {} +func (n *nopMetrics) ExecutionClientSyncing() {} +func (n *nopMetrics) ExecutionClientFailure() {} +func (n *nopMetrics) ExecutionClientLastFetchedBlock(block uint64) {} +func (n *nopMetrics) OperatorPublicKey(operatorID spectypes.OperatorID, publicKey []byte) {} +func (n *nopMetrics) ValidatorInactive(publicKey []byte) {} +func (n *nopMetrics) ValidatorNoIndex(publicKey []byte) {} +func (n *nopMetrics) ValidatorError(publicKey []byte) {} +func (n *nopMetrics) ValidatorReady(publicKey []byte) {} +func (n *nopMetrics) ValidatorNotActivated(publicKey []byte) {} +func (n *nopMetrics) ValidatorExiting(publicKey []byte) {} +func (n *nopMetrics) ValidatorSlashed(publicKey []byte) {} +func (n *nopMetrics) ValidatorNotFound(publicKey []byte) {} +func (n *nopMetrics) ValidatorPending(publicKey []byte) {} +func (n *nopMetrics) ValidatorRemoved(publicKey []byte) {} +func (n *nopMetrics) ValidatorUnknown(publicKey []byte) {} +func (n *nopMetrics) EventProcessed(eventName string) {} +func (n *nopMetrics) EventProcessingFailed(eventName string) {} +func (n *nopMetrics) MessagesReceivedFromPeer(peerId peer.ID) {} +func (n *nopMetrics) MessagesReceivedTotal() {} +func (n *nopMetrics) MessageValidationRSAVerifications() {} +func (n *nopMetrics) LastBlockProcessed(block uint64) {} +func (n *nopMetrics) LogsProcessingError(err error) {} func (n *nopMetrics) MessageAccepted(role spectypes.RunnerRole, round specqbft.Round) {} func (n *nopMetrics) MessageIgnored(reason string, role spectypes.RunnerRole, round specqbft.Round) {} func (n *nopMetrics) MessageRejected(reason string, role spectypes.RunnerRole, round specqbft.Round) { } func (n *nopMetrics) SSVMessageType(msgType spectypes.MsgType) {} -func (n *nopMetrics) GenesisSSVMessageType(msgType genesisspectypes.MsgType) {} func (n *nopMetrics) ConsensusMsgType(msgType specqbft.MessageType, signers int) {} func (n *nopMetrics) MessageValidationDuration(duration time.Duration, labels ...string) {} func (n *nopMetrics) SignatureValidationDuration(duration time.Duration, labels ...string) {} diff --git a/network/commons/common.go b/network/commons/common.go index 2a9036e9bf..2d71d84890 100644 --- a/network/commons/common.go +++ b/network/commons/common.go @@ -2,29 +2,18 @@ package commons import ( "encoding/binary" - "encoding/hex" "fmt" "math" "math/big" - "strconv" "strings" "time" "github.com/cespare/xxhash/v2" "github.com/libp2p/go-libp2p" - "github.com/libp2p/go-libp2p/core/protocol" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" spectypes "github.com/ssvlabs/ssv-spec/types" - - p2pprotocol "github.com/ssvlabs/ssv/protocol/v2/p2p" ) const ( - lastDecidedProtocol = "/ssv/sync/decided/last/0.0.1" - historyProtocol = "/ssv/sync/decided/history/0.0.1" - - peersForSync = 10 - // SubnetsCount returns the subnet count for genesis SubnetsCount uint64 = 128 @@ -44,29 +33,6 @@ const ( MessageOffset = operatorIDOffset + operatorIDSize ) -// EncodeGenesisSignedSSVMessage serializes the message, op id and signature into bytes -// DEPRECATED, TODO: remove post-fork -func EncodeGenesisSignedSSVMessage(message []byte, operatorID genesisspectypes.OperatorID, signature []byte) []byte { - b := make([]byte, signatureSize+operatorIDSize+len(message)) - copy(b[signatureOffset:], signature) - binary.LittleEndian.PutUint64(b[operatorIDOffset:], operatorID) - copy(b[MessageOffset:], message) - return b -} - -// DecodeGenesisSignedSSVMessage deserializes signed message bytes messsage, op id and a signature -// DEPRECATED, TODO: remove post-fork -func DecodeGenesisSignedSSVMessage(encoded []byte) ([]byte, genesisspectypes.OperatorID, []byte, error) { - if len(encoded) < MessageOffset { - return nil, 0, nil, fmt.Errorf("unexpected encoded message size of %d", len(encoded)) - } - - message := encoded[MessageOffset:] - operatorID := binary.LittleEndian.Uint64(encoded[operatorIDOffset : operatorIDOffset+operatorIDSize]) - signature := encoded[signatureOffset : signatureOffset+signatureSize] - return message, operatorID, signature, nil -} - // SubnetTopicID returns the topic to use for the given subnet func SubnetTopicID(subnet uint64) string { if subnet == UnknownSubnetId { @@ -75,13 +41,6 @@ func SubnetTopicID(subnet uint64) string { return fmt.Sprintf("%d", subnet) } -// ValidatorTopicID returns the topic to use for the given validator -func ValidatorTopicID(pkByts []byte) []string { - pkHex := hex.EncodeToString(pkByts) - subnet := ValidatorSubnet(pkHex) - return []string{SubnetTopicID(subnet)} -} - func CommitteeTopicID(cid spectypes.CommitteeID) []string { return []string{fmt.Sprintf("%d", CommitteeSubnet(cid))} } @@ -96,16 +55,6 @@ func GetTopicBaseName(topicName string) string { return strings.Replace(topicName, fmt.Sprintf("%s.", topicPrefix), "", 1) } -// ValidatorSubnet returns the subnet for the given validator -func ValidatorSubnet(validatorPKHex string) uint64 { - if len(validatorPKHex) < 10 { - return UnknownSubnetId - } - val := hexToUint64(validatorPKHex[:10]) - - return val % SubnetsCount -} - // CommitteeSubnet returns the subnet for the given committee func CommitteeSubnet(cid spectypes.CommitteeID) uint64 { subnet := new(big.Int).Mod(new(big.Int).SetBytes(cid[:]), new(big.Int).SetUint64(SubnetsCount)) @@ -151,23 +100,6 @@ func AddOptions(opts []libp2p.Option) []libp2p.Option { return opts } -// EncodeGenesisNetworkMsg encodes network message -// TODO: DEPRECATED, remove post-fork -func EncodeGenesisNetworkMsg(msg *genesisspectypes.SSVMessage) ([]byte, error) { - return msg.Encode() -} - -// DecodeGenesisNetworkMsg decodes network message -// TODO: DEPRECATED, remove post-fork -func DecodeGenesisNetworkMsg(data []byte) (*genesisspectypes.SSVMessage, error) { - msg := genesisspectypes.SSVMessage{} - err := msg.Decode(data) - if err != nil { - return nil, err - } - return &msg, nil -} - // EncodeNetworkMsg encodes network message func EncodeNetworkMsg(msg *spectypes.SSVMessage) ([]byte, error) { return msg.Encode() @@ -181,23 +113,3 @@ func DecodeNetworkMsg(data []byte) (*spectypes.SSVMessage, error) { } return &msg, nil } - -// ProtocolID returns the protocol id of the given protocol, -// and the amount of peers for distribution -func ProtocolID(prot p2pprotocol.SyncProtocol) (protocol.ID, int) { - switch prot { - case p2pprotocol.LastDecidedProtocol: - return lastDecidedProtocol, peersForSync - case p2pprotocol.DecidedHistoryProtocol: - return historyProtocol, peersForSync - } - return "", 0 -} - -func hexToUint64(hexStr string) uint64 { - result, err := strconv.ParseUint(hexStr, 16, 64) - if err != nil { - return uint64(0) - } - return result -} diff --git a/network/discovery/dv5_service.go b/network/discovery/dv5_service.go index 4897f85f56..2be0af99aa 100644 --- a/network/discovery/dv5_service.go +++ b/network/discovery/dv5_service.go @@ -11,13 +11,14 @@ import ( "github.com/ethereum/go-ethereum/p2p/enode" "github.com/libp2p/go-libp2p/core/peer" "github.com/pkg/errors" + "go.uber.org/zap" + "github.com/ssvlabs/ssv/logging" "github.com/ssvlabs/ssv/logging/fields" "github.com/ssvlabs/ssv/network/commons" "github.com/ssvlabs/ssv/network/peers" "github.com/ssvlabs/ssv/network/records" "github.com/ssvlabs/ssv/networkconfig" - "go.uber.org/zap" ) var ( @@ -57,8 +58,7 @@ type DiscV5Service struct { conns peers.ConnectionIndex subnetsIdx peers.SubnetsIndex - conn *net.UDPConn - sharedConn *SharedUDPConn + conn *net.UDPConn networkConfig networkconfig.NetworkConfig subnets []byte @@ -100,9 +100,6 @@ func (dvs *DiscV5Service) Close() error { return err } } - if dvs.sharedConn != nil { - close(dvs.sharedConn.Unhandled) - } if dvs.dv5Listener != nil { dvs.dv5Listener.Close() } @@ -174,14 +171,8 @@ func (dvs *DiscV5Service) checkPeer(logger *zap.Logger, e PeerEvent) error { if err != nil { return errors.Wrap(err, "could not read domain type") } - nodeNextDomainType, err := records.GetDomainTypeEntry(e.Node.Record(), records.KeyNextDomainType) - if err != nil && !errors.Is(err, records.ErrEntryNotFound) { - return errors.Wrap(err, "could not read domain type") - } - if dvs.networkConfig.DomainType() != nodeDomainType && - dvs.networkConfig.DomainType() != nodeNextDomainType { - return fmt.Errorf("mismatched domain type: neither %x nor %x match %x", - nodeDomainType, nodeNextDomainType, dvs.networkConfig.DomainType()) + if dvs.networkConfig.DomainType != nodeDomainType { + return fmt.Errorf("domain type %x doesn't match %x", nodeDomainType, dvs.networkConfig.DomainType) } // Get the peer's subnets, skipping if it has none. @@ -236,49 +227,26 @@ func (dvs *DiscV5Service) initDiscV5Listener(logger *zap.Logger, discOpts *Optio protocolID = DefaultSSVProtocolID } - // New discovery, with ProtocolID restriction, to be kept post-fork - unhandled := make(chan discover.ReadPacket, 100) // size taken from https://github.com/ethereum/go-ethereum/blob/v1.13.5/p2p/server.go#L551 - sharedConn := &SharedUDPConn{udpConn, unhandled} - dvs.sharedConn = sharedConn - - dv5PostForkCfg, err := opts.DiscV5Cfg(logger, WithProtocolID(protocolID), WithUnhandled(unhandled)) + dv5Cfg, err := opts.DiscV5Cfg(logger, WithProtocolID(protocolID)) if err != nil { return err } - dv5PostForkListener, err := discover.ListenV5(udpConn, localNode, *dv5PostForkCfg) + dv5Listener, err := discover.ListenV5(udpConn, localNode, *dv5Cfg) if err != nil { return errors.Wrap(err, "could not create discV5 listener") } - logger.Debug("started discv5 post-fork listener (UDP)", + logger.Debug("started discv5 listener (UDP)", fields.BindIP(bindIP), zap.Uint16("UdpPort", opts.Port), fields.ENRLocalNode(localNode), - fields.Domain(discOpts.NetworkConfig.NextDomainType()), + fields.Domain(discOpts.NetworkConfig.DomainType), fields.ProtocolID(protocolID), ) - // Previous discovery, without ProtocolID restriction, to be discontinued after the fork - dv5PreForkCfg, err := opts.DiscV5Cfg(logger) - if err != nil { - return err - } - - dv5PreForkListener, err := discover.ListenV5(sharedConn, localNode, *dv5PreForkCfg) - if err != nil { - return errors.Wrap(err, "could not create discV5 pre-fork listener") - } - - logger.Debug("started discv5 pre-fork listener (UDP)", - fields.BindIP(bindIP), - zap.Uint16("UdpPort", opts.Port), - fields.ENRLocalNode(localNode), - fields.Domain(discOpts.NetworkConfig.DomainType()), - ) - - dvs.dv5Listener = NewForkingDV5Listener(logger, dv5PreForkListener, dv5PostForkListener, 5*time.Second, dvs.networkConfig) - dvs.bootnodes = dv5PreForkCfg.Bootnodes // Just take bootnodes from one of the config since they're equal + dvs.dv5Listener = dv5Listener + dvs.bootnodes = dv5Cfg.Bootnodes return nil } @@ -367,16 +335,11 @@ func (dvs *DiscV5Service) DeregisterSubnets(logger *zap.Logger, subnets ...uint6 // PublishENR publishes the ENR with the current domain type across the network func (dvs *DiscV5Service) PublishENR(logger *zap.Logger) { // Update own node record. - err := records.SetDomainTypeEntry(dvs.dv5Listener.LocalNode(), records.KeyDomainType, dvs.networkConfig.DomainType()) + err := records.SetDomainTypeEntry(dvs.dv5Listener.LocalNode(), records.KeyDomainType, dvs.networkConfig.DomainType) if err != nil { logger.Error("could not set domain type", zap.Error(err)) return } - err = records.SetDomainTypeEntry(dvs.dv5Listener.LocalNode(), records.KeyNextDomainType, dvs.networkConfig.NextDomainType()) - if err != nil { - logger.Error("could not set next domain type", zap.Error(err)) - return - } // Acquire publish lock to prevent parallel publishing. // If there's an ongoing goroutine, it would now start publishing the record updated above, @@ -439,15 +402,14 @@ func (dvs *DiscV5Service) createLocalNode(logger *zap.Logger, discOpts *Options, localNode, // Satisfy decorations of forks supported by this node. - DecorateWithDomainType(records.KeyDomainType, dvs.networkConfig.DomainType()), - DecorateWithDomainType(records.KeyNextDomainType, dvs.networkConfig.NextDomainType()), + DecorateWithDomainType(records.KeyDomainType, dvs.networkConfig.DomainType), DecorateWithSubnets(opts.Subnets), ) if err != nil { return nil, errors.Wrap(err, "could not decorate local node") } - logger.Debug("node record is ready", fields.ENRLocalNode(localNode), fields.Domain(dvs.networkConfig.DomainType()), fields.Subnets(opts.Subnets)) + logger.Debug("node record is ready", fields.ENRLocalNode(localNode), fields.Domain(dvs.networkConfig.DomainType), fields.Subnets(opts.Subnets)) return localNode, nil } diff --git a/network/discovery/dv5_service_test.go b/network/discovery/dv5_service_test.go index e02a8991d6..4ed5c989b0 100644 --- a/network/discovery/dv5_service_test.go +++ b/network/discovery/dv5_service_test.go @@ -2,7 +2,6 @@ package discovery import ( "context" - "math" "net" "os" "testing" @@ -23,10 +22,8 @@ import ( ) var TestNetwork = networkconfig.NetworkConfig{ - Beacon: beacon.NewNetwork(spectypes.BeaconTestNetwork), - GenesisDomainType: spectypes.DomainType{0x1, 0x2, 0x3, 0x4}, - AlanDomainType: spectypes.DomainType{0x1, 0x2, 0x3, 0x5}, - AlanForkEpoch: math.MaxUint64, + Beacon: beacon.NewNetwork(spectypes.BeaconTestNetwork), + DomainType: spectypes.DomainType{0x1, 0x2, 0x3, 0x4}, } func TestCheckPeer(t *testing.T) { @@ -49,38 +46,10 @@ func TestCheckPeer(t *testing.T) { expectedError: errors.New("could not read domain type: not found"), }, { - name: "missing domain type but has next domain type", - domainType: nil, - nextDomainType: &spectypes.DomainType{0x1, 0x2, 0x3, 0x5}, - subnets: mySubnets, - expectedError: errors.New("could not read domain type: not found"), - }, - { - name: "domain type mismatch", - domainType: &spectypes.DomainType{0x1, 0x2, 0x3, 0x5}, - nextDomainType: &spectypes.DomainType{0x1, 0x2, 0x3, 0x6}, - subnets: mySubnets, - expectedError: errors.New("mismatched domain type: neither 01020305 nor 01020306 match 01020304"), - }, - { - name: "domain type mismatch (missing next domain type)", + name: "domain type mismatch", domainType: &spectypes.DomainType{0x1, 0x2, 0x3, 0x5}, subnets: mySubnets, - expectedError: errors.New("mismatched domain type: neither 01020305 nor 00000000 match 01020304"), - }, - { - name: "only next domain type matches", - domainType: &spectypes.DomainType{0x1, 0x2, 0x3, 0x3}, - nextDomainType: &spectypes.DomainType{0x1, 0x2, 0x3, 0x4}, - subnets: mySubnets, - expectedError: nil, - }, - { - name: "both domain types match", - domainType: &spectypes.DomainType{0x1, 0x2, 0x3, 0x4}, - nextDomainType: &spectypes.DomainType{0x1, 0x2, 0x3, 0x4}, - subnets: mySubnets, - expectedError: nil, + expectedError: errors.New("domain type 01020305 doesn't match 01020304"), }, { name: "missing subnets", @@ -134,10 +103,6 @@ func TestCheckPeer(t *testing.T) { err := records.SetDomainTypeEntry(localNode, records.KeyDomainType, *test.domainType) require.NoError(t, err) } - if test.nextDomainType != nil { - err := records.SetDomainTypeEntry(localNode, records.KeyNextDomainType, *test.nextDomainType) - require.NoError(t, err) - } if test.subnets != nil { err := records.SetSubnetsEntry(localNode, test.subnets) require.NoError(t, err) @@ -173,12 +138,11 @@ func TestCheckPeer(t *testing.T) { } type checkPeerTest struct { - name string - domainType *spectypes.DomainType - nextDomainType *spectypes.DomainType - subnets []byte - localNode *enode.LocalNode - expectedError error + name string + domainType *spectypes.DomainType + subnets []byte + localNode *enode.LocalNode + expectedError error } func mockSubnets(active ...int) []byte { diff --git a/network/discovery/forking_dv5_listener.go b/network/discovery/forking_dv5_listener.go deleted file mode 100644 index 5f0826178d..0000000000 --- a/network/discovery/forking_dv5_listener.go +++ /dev/null @@ -1,103 +0,0 @@ -package discovery - -import ( - "time" - - "github.com/ethereum/go-ethereum/p2p/enode" - "github.com/ssvlabs/ssv/networkconfig" - "go.uber.org/zap" -) - -const ( - defaultIteratorTimeout = 5 * time.Second -) - -// forkingDV5Listener wraps a pre-fork and a post-fork listener. -// Before the fork, it performs operations on both services. -// Aftet the fork, it performs operations only on the post-fork service. -type forkingDV5Listener struct { - logger *zap.Logger - preForkListener Listener - postForkListener Listener - iteratorTimeout time.Duration - netCfg networkconfig.NetworkConfig -} - -func NewForkingDV5Listener(logger *zap.Logger, preFork, postFork Listener, iteratorTimeout time.Duration, netConfig networkconfig.NetworkConfig) *forkingDV5Listener { - if iteratorTimeout == 0 { - iteratorTimeout = defaultIteratorTimeout - } - return &forkingDV5Listener{ - logger: logger, - preForkListener: preFork, - postForkListener: postFork, - iteratorTimeout: iteratorTimeout, - netCfg: netConfig, - } -} - -// Before the fork, returns the result of a Lookup in both pre and post-fork services. -// After the fork, returns only the result from the post-fork service. -func (l *forkingDV5Listener) Lookup(id enode.ID) []*enode.Node { - nodes := l.postForkListener.Lookup(id) - nodes = append(nodes, l.preForkListener.Lookup(id)...) - return nodes -} - -// Before the fork, returns an iterator for both pre and post-fork services. -// After the fork, returns only the iterator from the post-fork service. -func (l *forkingDV5Listener) RandomNodes() enode.Iterator { - fairMix := enode.NewFairMix(l.iteratorTimeout) - fairMix.AddSource(&annotatedIterator{l.postForkListener.RandomNodes(), "post"}) - fairMix.AddSource(&annotatedIterator{l.preForkListener.RandomNodes(), "pre"}) - return fairMix -} - -// Before the fork, returns all nodes from the pre and post-fork listeners. -// After the fork, returns only the result from the post-fork service. -func (l *forkingDV5Listener) AllNodes() []*enode.Node { - enodes := l.postForkListener.AllNodes() - enodes = append(enodes, l.preForkListener.AllNodes()...) - return enodes -} - -// Sends a ping in the post-fork service. -// Before the fork, it also tries to ping with the pre-fork service in case of error. -func (l *forkingDV5Listener) Ping(node *enode.Node) error { - err := l.postForkListener.Ping(node) - if err != nil { - return l.preForkListener.Ping(node) - } - return nil -} - -// Returns the LocalNode using the post-fork listener. -// Both pre and post-fork listeners should have the same LocalNode. -func (l *forkingDV5Listener) LocalNode() *enode.LocalNode { - return l.postForkListener.LocalNode() -} - -// Closes both listeners -func (l *forkingDV5Listener) Close() { - l.closePreForkListener() - l.postForkListener.Close() -} - -// closePreForkListener ensures preForkListener is closed once -func (l *forkingDV5Listener) closePreForkListener() { - l.preForkListener.Close() -} - -// annotatedIterator wraps an enode.Iterator with metrics collection. -type annotatedIterator struct { - enode.Iterator - fork string -} - -func (i *annotatedIterator) Next() bool { - if !i.Iterator.Next() { - return false - } - metricIterations.WithLabelValues(i.fork).Inc() - return true -} diff --git a/network/discovery/forking_dv5_listener_test.go b/network/discovery/forking_dv5_listener_test.go deleted file mode 100644 index c5db74b8cc..0000000000 --- a/network/discovery/forking_dv5_listener_test.go +++ /dev/null @@ -1,267 +0,0 @@ -package discovery - -import ( - "testing" - "time" - - "github.com/ethereum/go-ethereum/p2p/enode" - "github.com/ssvlabs/ssv/networkconfig" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "go.uber.org/zap" -) - -const iteratorTimeout = 5 * time.Millisecond - -func TestForkListener_Create(t *testing.T) { - localNode := NewLocalNode(t) - - preForkListener := NewMockListener(localNode, []*enode.Node{}) - postForkListener := NewMockListener(localNode, []*enode.Node{}) - - t.Run("Pre-Fork", func(t *testing.T) { - netCfg := PreForkNetworkConfig() - _ = NewForkingDV5Listener(zap.NewNop(), preForkListener, postForkListener, iteratorTimeout, netCfg) - - assert.False(t, preForkListener.closed) - assert.False(t, postForkListener.closed) - }) - - t.Run("Post-Fork", func(t *testing.T) { - netCfg := PostForkNetworkConfig() - _ = NewForkingDV5Listener(zap.NewNop(), preForkListener, postForkListener, iteratorTimeout, netCfg) - - assert.False(t, preForkListener.closed) - assert.False(t, postForkListener.closed) - }) -} - -func TestForkListener_Lookup(t *testing.T) { - nodeFromPreForkListener := NewTestingNode(t) // pre-fork node - nodeFromPostForkListener := NewTestingNode(t) // post-fork node - localNode := NewLocalNode(t) - - preForkListener := NewMockListener(localNode, []*enode.Node{nodeFromPreForkListener}) - postForkListener := NewMockListener(localNode, []*enode.Node{nodeFromPostForkListener}) - - t.Run("Pre-Fork", func(t *testing.T) { - netCfg := PreForkNetworkConfig() - forkListener := NewForkingDV5Listener(zap.NewNop(), preForkListener, postForkListener, iteratorTimeout, netCfg) - - nodes := forkListener.Lookup(enode.ID{}) - assert.Len(t, nodes, 2) - // post-fork nodes are set first - assert.Equal(t, nodes[0], nodeFromPostForkListener) - assert.Equal(t, nodes[1], nodeFromPreForkListener) - }) - - t.Run("Post-Fork", func(t *testing.T) { - netCfg := PostForkNetworkConfig() - forkListener := NewForkingDV5Listener(zap.NewNop(), preForkListener, postForkListener, iteratorTimeout, netCfg) - - nodes := forkListener.Lookup(enode.ID{}) - assert.Len(t, nodes, 2) - // post-fork nodes are set first - assert.Equal(t, nodes[0], nodeFromPostForkListener) - assert.Equal(t, nodes[1], nodeFromPreForkListener) - }) -} - -func TestForkListener_RandomNodes(t *testing.T) { - nodeFromPreForkListener := NewTestingNode(t) // pre-fork node - nodeFromPostForkListener := NewTestingNode(t) // post-fork node - localNode := NewLocalNode(t) - - t.Run("Pre-Fork", func(t *testing.T) { - preForkListener := NewMockListener(localNode, []*enode.Node{nodeFromPreForkListener}) - postForkListener := NewMockListener(localNode, []*enode.Node{nodeFromPostForkListener}) - - netCfg := PreForkNetworkConfig() - forkListener := NewForkingDV5Listener(zap.NewNop(), preForkListener, postForkListener, iteratorTimeout, netCfg) - - iter := forkListener.RandomNodes() - defer iter.Close() - var nodes []*enode.Node - for i := 0; i < 2; i++ { - require.True(t, iter.Next()) - nodes = append(nodes, iter.Node()) - } - - assert.Len(t, nodes, 2) - // post-fork nodes are set first - assert.Equal(t, nodes[0], nodeFromPreForkListener) - assert.Equal(t, nodes[1], nodeFromPostForkListener) - - // No more next - requireNextTimeout(t, false, iter, 10*time.Millisecond) - }) - - t.Run("Post-Fork", func(t *testing.T) { - preForkListener := NewMockListener(localNode, []*enode.Node{nodeFromPreForkListener}) - postForkListener := NewMockListener(localNode, []*enode.Node{nodeFromPostForkListener}) - - netCfg := PostForkNetworkConfig() - forkListener := NewForkingDV5Listener(zap.NewNop(), preForkListener, postForkListener, iteratorTimeout, netCfg) - - iter := forkListener.RandomNodes() - defer iter.Close() - var nodes []*enode.Node - for i := 0; i < 2; i++ { - require.True(t, iter.Next()) - nodes = append(nodes, iter.Node()) - } - - // there should be no difference between pre-fork and post-fork - assert.Equal(t, nodes[0], nodeFromPreForkListener) - assert.Equal(t, nodes[1], nodeFromPostForkListener) - - // No more next - requireNextTimeout(t, false, iter, 10*time.Millisecond) - }) -} - -func TestForkListener_AllNodes(t *testing.T) { - nodeFromPreForkListener := NewTestingNode(t) // pre-fork node - nodeFromPostForkListener := NewTestingNode(t) // post-fork node - localNode := NewLocalNode(t) - - preForkListener := NewMockListener(localNode, []*enode.Node{nodeFromPreForkListener}) - postForkListener := NewMockListener(localNode, []*enode.Node{nodeFromPostForkListener}) - - t.Run("Pre-Fork", func(t *testing.T) { - netCfg := PreForkNetworkConfig() - forkListener := NewForkingDV5Listener(zap.NewNop(), preForkListener, postForkListener, iteratorTimeout, netCfg) - - nodes := forkListener.AllNodes() - assert.Len(t, nodes, 2) - // post-fork nodes are set first - assert.Equal(t, nodes[0], nodeFromPostForkListener) - assert.Equal(t, nodes[1], nodeFromPreForkListener) - }) - - t.Run("Post-Fork", func(t *testing.T) { - netCfg := PostForkNetworkConfig() - forkListener := NewForkingDV5Listener(zap.NewNop(), preForkListener, postForkListener, iteratorTimeout, netCfg) - - nodes := forkListener.AllNodes() - assert.Len(t, nodes, 2) - // there should be no difference between pre-fork and post-fork - assert.Equal(t, nodes[0], nodeFromPostForkListener) - assert.Equal(t, nodes[1], nodeFromPreForkListener) - }) -} - -func TestForkListener_PingPreFork(t *testing.T) { - for _, netCfg := range []networkconfig.NetworkConfig{PreForkNetworkConfig(), PostForkNetworkConfig()} { - pingPeer := NewTestingNode(t) // any peer to ping - localNode := NewLocalNode(t) - - preForkListener := NewMockListener(localNode, []*enode.Node{}) - postForkListener := NewMockListener(localNode, []*enode.Node{}) - - forkListener := NewForkingDV5Listener(zap.NewNop(), preForkListener, postForkListener, iteratorTimeout, netCfg) - - t.Run("Post-Fork succeeds", func(t *testing.T) { - postForkListener.SetNodesForPingError([]*enode.Node{}) - preForkListener.SetNodesForPingError([]*enode.Node{pingPeer}) - err := forkListener.Ping(pingPeer) - assert.NoError(t, err) - }) - - t.Run("Post-Fork fails and Pre-Fork succeeds", func(t *testing.T) { - postForkListener.SetNodesForPingError([]*enode.Node{pingPeer}) - preForkListener.SetNodesForPingError([]*enode.Node{}) - err := forkListener.Ping(pingPeer) - assert.NoError(t, err) - }) - - t.Run("Post-Fork and Pre-Fork fails", func(t *testing.T) { - postForkListener.SetNodesForPingError([]*enode.Node{pingPeer}) - preForkListener.SetNodesForPingError([]*enode.Node{pingPeer}) - err := forkListener.Ping(pingPeer) - assert.ErrorContains(t, err, "failed ping") - }) - } -} - -func TestForkListener_LocalNode(t *testing.T) { - localNode := NewLocalNode(t) - - preForkListener := NewMockListener(localNode, []*enode.Node{}) - postForkListener := NewMockListener(localNode, []*enode.Node{}) - - t.Run("Pre-Fork", func(t *testing.T) { - netCfg := PreForkNetworkConfig() - forkListener := NewForkingDV5Listener(zap.NewNop(), preForkListener, postForkListener, iteratorTimeout, netCfg) - - assert.Equal(t, localNode, forkListener.LocalNode()) - }) - - t.Run("Post-Fork", func(t *testing.T) { - netCfg := PostForkNetworkConfig() - forkListener := NewForkingDV5Listener(zap.NewNop(), preForkListener, postForkListener, iteratorTimeout, netCfg) - - assert.Equal(t, localNode, forkListener.LocalNode()) - }) -} - -func TestForkListener_Close(t *testing.T) { - for name, netCfg := range map[string]networkconfig.NetworkConfig{ - "Pre-Fork": PreForkNetworkConfig(), - "Post-Fork": PostForkNetworkConfig(), - } { - t.Run(name, func(t *testing.T) { - preForkListener := NewMockListener(&enode.LocalNode{}, []*enode.Node{}) - postForkListener := NewMockListener(&enode.LocalNode{}, []*enode.Node{}) - - forkListener := NewForkingDV5Listener(zap.NewNop(), preForkListener, postForkListener, iteratorTimeout, netCfg) - - // Call any method so that it will check whether to close the pre-fork listener - _ = forkListener.AllNodes() - - assert.False(t, preForkListener.closed) - assert.False(t, postForkListener.closed) - - // Close - forkListener.Close() - - assert.True(t, preForkListener.closed) - assert.True(t, postForkListener.closed) - }) - } -} - -func requireNextTimeout(t *testing.T, expected bool, iter enode.Iterator, timeout time.Duration) { - const maxTries = 10 - var deadline = time.After(timeout) - next := make(chan bool) - go func() { - defer close(next) - for { - ok := iter.Next() - select { - case next <- ok: - case <-deadline: - return - } - if ok { - return - } - time.Sleep(timeout / maxTries) - } - }() - for { - select { - case ok := <-next: - require.Equal(t, expected, ok, "expected next to be %v", expected) - if ok { - return - } - case <-deadline: - if expected { - require.Fail(t, "expected next to be %v", expected) - } - return - } - } -} diff --git a/network/discovery/iterator_test.go b/network/discovery/iterator_test.go deleted file mode 100644 index e89dc1ddcd..0000000000 --- a/network/discovery/iterator_test.go +++ /dev/null @@ -1,123 +0,0 @@ -package discovery - -import ( - "testing" - "time" - - "github.com/ethereum/go-ethereum/p2p/enode" - "github.com/stretchr/testify/require" -) - -func TestFairMixIterator_Next(t *testing.T) { - // Mock iterators - preforkNodes := NewTestingNodes(t, 2) - postForkNodes := NewTestingNodes(t, 2) - - iterator := enode.NewFairMix(5 * time.Millisecond) - defer iterator.Close() - iterator.AddSource(NewMockIterator(preforkNodes)) - iterator.AddSource(NewMockIterator(postForkNodes)) - - expectedNodes := append(preforkNodes, postForkNodes...) - actualNodes := make([]*enode.Node, 0, len(expectedNodes)) - for i := 0; i < 4; i++ { - require.True(t, iterator.Next()) - actualNodes = append(actualNodes, iterator.Node()) - } - require.ElementsMatch(t, expectedNodes, actualNodes) - - // No more elements - requireNextTimeout(t, false, iterator, 15*time.Millisecond) -} - -func TestFairMixIterator_Next_False(t *testing.T) { - // Mock iterators - // Nil means return false on Next() - preforkNodes := []*enode.Node{NewTestingNode(t), nil, NewTestingNode(t)} - postForkNodes := []*enode.Node{NewTestingNode(t), NewTestingNode(t), nil} - preFork := NewMockIterator(preforkNodes) - postFork := NewMockIterator(postForkNodes) - - iterator := enode.NewFairMix(5 * time.Millisecond) - defer iterator.Close() - iterator.AddSource(preFork) - iterator.AddSource(postFork) - - var expectedNodes []*enode.Node - for _, node := range preforkNodes { - if node == nil { - break - } - expectedNodes = append(expectedNodes, node) - } - for _, node := range postForkNodes { - if node == nil { - break - } - expectedNodes = append(expectedNodes, node) - } - - var actualNodes []*enode.Node - for i := 0; i < len(expectedNodes); i++ { - requireNextTimeout(t, true, iterator, 10*time.Millisecond) - actualNodes = append(actualNodes, iterator.Node()) - } - require.ElementsMatch(t, expectedNodes, actualNodes) - - // No more elements - requireNextTimeout(t, false, iterator, 15*time.Millisecond) -} - -func TestFairMixIterator_PostForkEmpty(t *testing.T) { - // Mock nodes - node1 := NewTestingNode(t) - node2 := NewTestingNode(t) - - // Mock iterators - preFork := NewMockIterator([]*enode.Node{node2, node1}) // preFork has 2 nodes - postFork := NewMockIterator([]*enode.Node{}) // postFork has no node - - iterator := enode.NewFairMix(5 * time.Millisecond) - defer iterator.Close() - iterator.AddSource(preFork) - iterator.AddSource(postFork) - - require.False(t, postFork.closed) // postFork iterator must start openened - - // First check: preFork first node after switch - requireNextTimeout(t, true, iterator, 10*time.Millisecond) - require.Equal(t, node2, iterator.Node()) - - // Second check: preFork second node - requireNextTimeout(t, true, iterator, 10*time.Millisecond) - require.Equal(t, node1, iterator.Node()) - - // No more elements - requireNextTimeout(t, false, iterator, 15*time.Millisecond) -} - -func TestFairMixIterator_PreForkEmpty(t *testing.T) { - // Mock nodes - node1 := NewTestingNode(t) - node2 := NewTestingNode(t) - - // Mock iterators - preFork := NewMockIterator([]*enode.Node{}) // preFork has no node - postFork := NewMockIterator([]*enode.Node{node1, node2}) // postFork has 2 nodes - - iterator := enode.NewFairMix(5 * time.Millisecond) - defer iterator.Close() - iterator.AddSource(preFork) - iterator.AddSource(postFork) - - // First check: postFork first node - requireNextTimeout(t, true, iterator, 10*time.Millisecond) - require.Equal(t, node1, iterator.Node()) - - // Second check: postFork second node - requireNextTimeout(t, true, iterator, 10*time.Millisecond) - require.Equal(t, node2, iterator.Node()) - - // No more elements even after switch - requireNextTimeout(t, false, iterator, 15*time.Millisecond) -} diff --git a/network/discovery/service_test.go b/network/discovery/service_test.go index 54c503ac32..46725ee3c1 100644 --- a/network/discovery/service_test.go +++ b/network/discovery/service_test.go @@ -5,13 +5,13 @@ import ( "testing" "time" - "github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap" spectypes "github.com/ssvlabs/ssv-spec/types" + "github.com/ssvlabs/ssv/network/records" "github.com/ssvlabs/ssv/networkconfig" ) @@ -127,16 +127,7 @@ func checkLocalNodeDomainTypeAlignment(t *testing.T, localNode *enode.LocalNode, } err := localNode.Node().Record().Load(&domainEntry) require.NoError(t, err) - require.Equal(t, netConfig.DomainType(), domainEntry.DomainType) - - // Check next domain entry - nextDomainEntry := records.DomainTypeEntry{ - Key: records.KeyNextDomainType, - DomainType: spectypes.DomainType{}, - } - err = localNode.Node().Record().Load(&nextDomainEntry) - require.NoError(t, err) - require.Equal(t, netConfig.NextDomainType(), nextDomainEntry.DomainType) + require.Equal(t, netConfig.DomainType, domainEntry.DomainType) } func TestDiscV5Service_PublishENR(t *testing.T) { @@ -250,21 +241,13 @@ func TestDiscV5Service_checkPeer(t *testing.T) { err = dvs.checkPeer(testLogger, ToPeerEvent(NodeWithoutDomain(t))) require.ErrorContains(t, err, "could not read domain type: not found") - // No next domain. No error since it's not enforced - err = dvs.checkPeer(testLogger, ToPeerEvent(NodeWithoutNextDomain(t))) - require.NoError(t, err) - // Matching main domain - err = dvs.checkPeer(testLogger, ToPeerEvent(NodeWithCustomDomains(t, testNetConfig.DomainType(), spectypes.DomainType{}))) - require.NoError(t, err) - - // Matching next domain - err = dvs.checkPeer(testLogger, ToPeerEvent(NodeWithCustomDomains(t, spectypes.DomainType{}, testNetConfig.DomainType()))) + err = dvs.checkPeer(testLogger, ToPeerEvent(NodeWithCustomDomain(t, testNetConfig.DomainType))) require.NoError(t, err) // Mismatching domains - err = dvs.checkPeer(testLogger, ToPeerEvent(NodeWithCustomDomains(t, spectypes.DomainType{}, spectypes.DomainType{}))) - require.ErrorContains(t, err, "mismatched domain type: neither 00000000 nor 00000000 match 00000302") + err = dvs.checkPeer(testLogger, ToPeerEvent(NodeWithCustomDomain(t, spectypes.DomainType{}))) + require.ErrorContains(t, err, "domain type 00000000 doesn't match 00000302") // No subnets err = dvs.checkPeer(testLogger, ToPeerEvent(NodeWithoutSubnets(t))) @@ -286,45 +269,3 @@ func TestDiscV5Service_checkPeer(t *testing.T) { err = dvs.checkPeer(testLogger, ToPeerEvent(NodeWithCustomSubnets(t, subnets))) require.ErrorContains(t, err, "no shared subnets") } - -func TestDiscV5ServiceListenerType(t *testing.T) { - - t.Run("Post-Fork", func(t *testing.T) { - netConfig := PostForkNetworkConfig() - dvs := testingDiscoveryWithNetworkConfig(t, netConfig) - - // Check listener type - _, ok := dvs.dv5Listener.(*forkingDV5Listener) - require.True(t, ok) - - _, ok = dvs.dv5Listener.(*discover.UDPv5) - require.False(t, ok) - - // Check bootnodes - CheckBootnodes(t, dvs, netConfig) - - // Close - err := dvs.Close() - require.NoError(t, err) - }) - - t.Run("Pre-Fork", func(t *testing.T) { - - netConfig := PreForkNetworkConfig() - dvs := testingDiscoveryWithNetworkConfig(t, netConfig) - - // Check listener type - _, ok := dvs.dv5Listener.(*discover.UDPv5) - require.False(t, ok) - - _, ok = dvs.dv5Listener.(*forkingDV5Listener) - require.True(t, ok) - - // Check bootnodes - CheckBootnodes(t, dvs, netConfig) - - // Close - err := dvs.Close() - require.NoError(t, err) - }) -} diff --git a/network/discovery/util_test.go b/network/discovery/util_test.go index 183d222f5a..145908b5f8 100644 --- a/network/discovery/util_test.go +++ b/network/discovery/util_test.go @@ -10,7 +10,6 @@ import ( "sync" "testing" - "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/p2p/enr" @@ -19,11 +18,12 @@ import ( "github.com/pkg/errors" "github.com/prysmaticlabs/go-bitfield" spectypes "github.com/ssvlabs/ssv-spec/types" + "github.com/stretchr/testify/require" + "go.uber.org/zap" + "github.com/ssvlabs/ssv/network/peers" "github.com/ssvlabs/ssv/network/records" "github.com/ssvlabs/ssv/networkconfig" - "github.com/stretchr/testify/require" - "go.uber.org/zap" ) var ( @@ -88,59 +88,9 @@ func testingDiscovery(t *testing.T) *DiscV5Service { return testingDiscoveryWithNetworkConfig(t, testNetConfig) } -// NetworkConfig with fork epoch -func testingNetConfigWithForkEpoch(forkEpoch phase0.Epoch) networkconfig.NetworkConfig { - n := networkconfig.HoleskyStage - return networkconfig.NetworkConfig{ - Name: n.Name, - Beacon: n.Beacon, - GenesisDomainType: n.GenesisDomainType, - AlanDomainType: n.AlanDomainType, - GenesisEpoch: n.GenesisEpoch, - RegistrySyncOffset: n.RegistrySyncOffset, - RegistryContractAddr: n.RegistryContractAddr, - Bootnodes: n.Bootnodes, - // Fork epoch - AlanForkEpoch: forkEpoch, - } -} - -// NetworkConfig for staying in pre-fork -func PreForkNetworkConfig() networkconfig.NetworkConfig { - forkEpoch := networkconfig.HoleskyStage.Beacon.EstimatedCurrentEpoch() + 1000 - return testingNetConfigWithForkEpoch(forkEpoch) -} - -// NetworkConfig for staying in post-fork -func PostForkNetworkConfig() networkconfig.NetworkConfig { - forkEpoch := networkconfig.HoleskyStage.Beacon.EstimatedCurrentEpoch() - 1000 - return testingNetConfigWithForkEpoch(forkEpoch) -} - -// Testing LocalNode -func NewLocalNode(t *testing.T) *enode.LocalNode { - // Generate key - nodeKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - require.NoError(t, err) - - // Create local node - localNode, err := records.CreateLocalNode(nodeKey, t.TempDir(), net.IP(testIP), testPort, testTCPPort) - require.NoError(t, err) - - // Set entries - err = records.SetDomainTypeEntry(localNode, records.KeyDomainType, testNetConfig.DomainType()) - require.NoError(t, err) - err = records.SetDomainTypeEntry(localNode, records.KeyNextDomainType, testNetConfig.NextDomainType()) - require.NoError(t, err) - err = records.SetSubnetsEntry(localNode, mockSubnets(1)) - require.NoError(t, err) - - return localNode -} - // Testing node func NewTestingNode(t *testing.T) *enode.Node { - return CustomNode(t, true, testNetConfig.DomainType(), true, testNetConfig.NextDomainType(), true, mockSubnets(1)) + return CustomNode(t, true, testNetConfig.DomainType, true, mockSubnets(1)) } func NewTestingNodes(t *testing.T, count int) []*enode.Node { @@ -152,33 +102,32 @@ func NewTestingNodes(t *testing.T, count int) []*enode.Node { } func NodeWithoutDomain(t *testing.T) *enode.Node { - return CustomNode(t, false, spectypes.DomainType{}, true, testNetConfig.NextDomainType(), true, mockSubnets(1)) -} - -func NodeWithoutNextDomain(t *testing.T) *enode.Node { - return CustomNode(t, true, testNetConfig.DomainType(), false, spectypes.DomainType{}, true, mockSubnets(1)) + return CustomNode(t, false, spectypes.DomainType{}, true, mockSubnets(1)) } func NodeWithoutSubnets(t *testing.T) *enode.Node { - return CustomNode(t, true, testNetConfig.DomainType(), true, testNetConfig.NextDomainType(), false, nil) + return CustomNode(t, true, testNetConfig.DomainType, false, nil) } -func NodeWithCustomDomains(t *testing.T, domainType spectypes.DomainType, nextDomainType spectypes.DomainType) *enode.Node { - return CustomNode(t, true, domainType, true, nextDomainType, true, mockSubnets(1)) +func NodeWithCustomDomain(t *testing.T, domainType spectypes.DomainType) *enode.Node { + return CustomNode(t, true, domainType, true, mockSubnets(1)) } func NodeWithZeroSubnets(t *testing.T) *enode.Node { - return CustomNode(t, true, testNetConfig.DomainType(), true, testNetConfig.NextDomainType(), true, zeroSubnets) + return CustomNode(t, true, testNetConfig.DomainType, true, zeroSubnets) } func NodeWithCustomSubnets(t *testing.T, subnets []byte) *enode.Node { - return CustomNode(t, true, testNetConfig.DomainType(), true, testNetConfig.NextDomainType(), true, subnets) + return CustomNode(t, true, testNetConfig.DomainType, true, subnets) } -func CustomNode(t *testing.T, - setDomainType bool, domainType spectypes.DomainType, - setNextDomainType bool, nextDomainType spectypes.DomainType, - setSubnets bool, subnets []byte) *enode.Node { +func CustomNode( + t *testing.T, + setDomainType bool, + domainType spectypes.DomainType, + setSubnets bool, + subnets []byte, +) *enode.Node { // Generate key nodeKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) @@ -202,12 +151,6 @@ func CustomNode(t *testing.T, DomainType: domainType, }) } - if setNextDomainType { - record.Set(records.DomainTypeEntry{ - Key: records.KeyNextDomainType, - DomainType: nextDomainType, - }) - } if setSubnets { subnetsVec := bitfield.NewBitvector128() for i, subnet := range subnets { diff --git a/network/p2p/p2p.go b/network/p2p/p2p.go index 674ab65c94..a516204d10 100644 --- a/network/p2p/p2p.go +++ b/network/p2p/p2p.go @@ -90,7 +90,6 @@ type p2pNetwork struct { state int32 - activeValidators *hashmap.Map[string, validatorStatus] activeCommittees *hashmap.Map[string, validatorStatus] backoffConnector *libp2pdiscbackoff.BackoffConnector @@ -121,7 +120,6 @@ func New(logger *zap.Logger, cfg *Config, mr Metrics) (*p2pNetwork, error) { msgRouter: cfg.Router, msgValidator: cfg.MessageValidator, state: stateClosed, - activeValidators: hashmap.New[string, validatorStatus](), activeCommittees: hashmap.New[string, validatorStatus](), nodeStorage: cfg.NodeStorage, operatorPKHashToPKCache: hashmap.New[string, []byte](), @@ -367,13 +365,6 @@ func (n *p2pNetwork) UpdateSubnets(logger *zap.Logger) { return true }) - if !n.cfg.Network.PastAlanFork() { - n.activeValidators.Range(func(pkHex string, status validatorStatus) bool { - subnet := commons.ValidatorSubnet(pkHex) - updatedSubnets[subnet] = byte(1) - return true - }) - } n.activeSubnets = updatedSubnets // Compute the not yet registered subnets. diff --git a/network/p2p/p2p_genesis.go b/network/p2p/p2p_genesis.go deleted file mode 100644 index eb042fd388..0000000000 --- a/network/p2p/p2p_genesis.go +++ /dev/null @@ -1,66 +0,0 @@ -package p2pv1 - -import ( - "encoding/hex" - "fmt" - - "github.com/pkg/errors" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - spectypes "github.com/ssvlabs/ssv-spec/types" - "go.uber.org/zap" - - "github.com/ssvlabs/ssv/logging/fields" - "github.com/ssvlabs/ssv/network/commons" - p2pprotocol "github.com/ssvlabs/ssv/protocol/v2/p2p" -) - -type GenesisP2P struct { - Network *p2pNetwork -} - -func (p *GenesisP2P) Broadcast(message *genesisspectypes.SSVMessage) error { - - zap.L().Debug("broadcasting genesis msg", fields.PubKey(message.MsgID.GetPubKey()), zap.Uint64("msg_type", uint64(message.MsgType))) - - if !p.Network.isReady() { - return p2pprotocol.ErrNetworkIsNotReady - } - - if !p.Network.operatorDataStore.OperatorIDReady() { - return fmt.Errorf("operator ID is not ready") - } - - encodedMsg, err := commons.EncodeGenesisNetworkMsg(message) - if err != nil { - return errors.Wrap(err, "could not decode msg") - } - signature, err := p.Network.operatorSigner.Sign(encodedMsg) - if err != nil { - return err - } - encodedMsg = commons.EncodeGenesisSignedSSVMessage(encodedMsg, p.Network.operatorDataStore.GetOperatorID(), signature) - - _, exists := p.Network.nodeStorage.ValidatorStore().Validator(message.MsgID.GetPubKey()) - if !exists { - return fmt.Errorf("could not find share for validator %s", hex.EncodeToString(message.MsgID.GetPubKey())) - } - - topics := commons.ValidatorTopicID(message.MsgID.GetPubKey()) - - for _, topic := range topics { - p.Network.interfaceLogger.Debug("broadcasting msg", - fields.PubKey(message.MsgID.GetPubKey()), - zap.Uint64("msg_type", uint64(message.MsgType)), - fields.Topic(topic)) - if err := p.Network.topicsCtrl.Broadcast(topic, encodedMsg, p.Network.cfg.RequestTimeout); err != nil { - p.Network.interfaceLogger.Debug("could not broadcast msg", fields.PubKey(message.MsgID.GetPubKey()), zap.Error(err)) - return fmt.Errorf("could not broadcast msg: %w", err) - } - } - return nil -} - -// Subscribe subscribes to validator subnet -func (n *GenesisP2P) Subscribe(pk genesisspectypes.ValidatorPK) error { - return n.Network.Subscribe(spectypes.ValidatorPK(pk)) -} diff --git a/network/p2p/p2p_pubsub.go b/network/p2p/p2p_pubsub.go index 68baf5126c..2488fdfd67 100644 --- a/network/p2p/p2p_pubsub.go +++ b/network/p2p/p2p_pubsub.go @@ -16,8 +16,6 @@ import ( "github.com/ssvlabs/ssv/network" "github.com/ssvlabs/ssv/network/commons" "github.com/ssvlabs/ssv/network/records" - genesismessage "github.com/ssvlabs/ssv/protocol/genesis/message" - "github.com/ssvlabs/ssv/protocol/genesis/ssv/genesisqueue" "github.com/ssvlabs/ssv/protocol/v2/message" p2pprotocol "github.com/ssvlabs/ssv/protocol/v2/p2p" "github.com/ssvlabs/ssv/protocol/v2/ssv/queue" @@ -133,9 +131,7 @@ func (n *p2pNetwork) Subscribe(pk spectypes.ValidatorPK) error { if err != nil { return fmt.Errorf("could not subscribe to committee: %w", err) } - if !n.cfg.Network.PastAlanFork() { - return n.subscribeValidator(pk) - } + return nil } @@ -156,23 +152,6 @@ func (n *p2pNetwork) subscribeCommittee(cid spectypes.CommitteeID) error { return nil } -// subscribeValidator handles the subscription logic for validator subnets -func (n *p2pNetwork) subscribeValidator(pk spectypes.ValidatorPK) error { - pkHex := hex.EncodeToString(pk[:]) - n.interfaceLogger.Debug("subscribing to validator", zap.String("validator", pkHex)) - status, found := n.activeValidators.GetOrSet(pkHex, validatorStatusSubscribing) - if found && status != validatorStatusInactive { - return nil - } - for _, topic := range commons.ValidatorTopicID(pk[:]) { - if err := n.topicsCtrl.Subscribe(n.interfaceLogger, topic); err != nil { - return fmt.Errorf("could not subscribe to topic %s: %w", topic, err) - } - } - n.activeValidators.Set(pkHex, validatorStatusSubscribed) - return nil -} - func (n *p2pNetwork) unsubscribeSubnet(logger *zap.Logger, subnet uint64) error { if !n.isReady() { return p2pprotocol.ErrNetworkIsNotReady @@ -192,14 +171,6 @@ func (n *p2pNetwork) Unsubscribe(logger *zap.Logger, pk spectypes.ValidatorPK) e return p2pprotocol.ErrNetworkIsNotReady } - if !n.cfg.Network.PastAlanFork() { - pkHex := hex.EncodeToString(pk[:]) - if status, _ := n.activeValidators.Get(pkHex); status != validatorStatusSubscribed { - return nil - } - n.activeValidators.Delete(pkHex) - } - share, exists := n.nodeStorage.ValidatorStore().Validator(pk[:]) if !exists { return fmt.Errorf("could not find share for validator %s", hex.EncodeToString(pk[:])) @@ -232,9 +203,6 @@ func (n *p2pNetwork) handlePubsubMessages(logger *zap.Logger) func(ctx context.C case *queue.SSVMessage: decodedMsg = m metricsRouterIncoming.WithLabelValues(message.MsgTypeToString(m.MsgType)).Inc() - case *genesisqueue.GenesisSSVMessage: - decodedMsg = m - metricsRouterIncoming.WithLabelValues(genesismessage.MsgTypeToString(m.MsgType)).Inc() case nil: return errors.New("message was not decoded") default: diff --git a/network/p2p/p2p_setup.go b/network/p2p/p2p_setup.go index 6225b0f0e3..fe9738e498 100644 --- a/network/p2p/p2p_setup.go +++ b/network/p2p/p2p_setup.go @@ -176,7 +176,7 @@ func (n *p2pNetwork) setupPeerServices(logger *zap.Logger) error { if err != nil { return err } - d := n.cfg.Network.DomainType() + d := n.cfg.Network.DomainType domain := "0x" + hex.EncodeToString(d[:]) self := records.NewNodeInfo(domain) self.Metadata = &records.NodeMetadata{ @@ -207,7 +207,7 @@ func (n *p2pNetwork) setupPeerServices(logger *zap.Logger) error { // Handshake filters filters := func() []connections.HandshakeFilter { - newDomain := n.cfg.Network.DomainType() + newDomain := n.cfg.Network.DomainType newDomainString := "0x" + hex.EncodeToString(newDomain[:]) return []connections.HandshakeFilter{ connections.NetworkIDFilter(newDomainString), @@ -216,15 +216,15 @@ func (n *p2pNetwork) setupPeerServices(logger *zap.Logger) error { } handshaker := connections.NewHandshaker(n.ctx, &connections.HandshakerCfg{ - Streams: n.streamCtrl, - NodeInfos: n.idx, - PeerInfos: n.idx, - ConnIdx: n.idx, - SubnetsIdx: n.idx, - IDService: ids, - Network: n.host.Network(), - DomainTypeProvider: n.cfg.Network, - SubnetsProvider: subnetsProvider, + Streams: n.streamCtrl, + NodeInfos: n.idx, + PeerInfos: n.idx, + ConnIdx: n.idx, + SubnetsIdx: n.idx, + IDService: ids, + Network: n.host.Network(), + DomainType: n.cfg.Network.DomainType, + SubnetsProvider: subnetsProvider, }, filters) n.host.SetStreamHandler(peers.NodeInfoProtocol, handshaker.Handler(logger)) diff --git a/network/p2p/p2p_test.go b/network/p2p/p2p_test.go index 0ab9bc1154..11a5ff4cc6 100644 --- a/network/p2p/p2p_test.go +++ b/network/p2p/p2p_test.go @@ -12,7 +12,6 @@ import ( eth2apiv1 "github.com/attestantio/go-eth2-client/api/v1" "github.com/pkg/errors" - "github.com/ssvlabs/ssv-spec-pre-cc/types" specqbft "github.com/ssvlabs/ssv-spec/qbft" spectypes "github.com/ssvlabs/ssv-spec/types" spectestingutils "github.com/ssvlabs/ssv-spec/types/testingutils" @@ -152,7 +151,7 @@ func generateValidatorMsg(ks *spectestingutils.TestKeySet, round specqbft.Round, fullData := spectestingutils.TestingQBFTFullData - nonCommitteeIdentifier := spectypes.NewMsgID(netCfg.DomainType(), ks.ValidatorPK.Serialize(), nonCommitteeRole) + nonCommitteeIdentifier := spectypes.NewMsgID(netCfg.DomainType, ks.ValidatorPK.Serialize(), nonCommitteeRole) qbftMessage := &specqbft.Message{ MsgType: specqbft.ProposalMsgType, @@ -191,7 +190,7 @@ func generateCommitteeMsg(ks *spectestingutils.TestKeySet, round specqbft.Round) fullData := spectestingutils.TestingQBFTFullData encodedCommitteeID := append(bytes.Repeat([]byte{0}, 16), committeeID[:]...) - committeeIdentifier := spectypes.NewMsgID(netCfg.DomainType(), encodedCommitteeID, spectypes.RoleCommittee) + committeeIdentifier := spectypes.NewMsgID(netCfg.DomainType, encodedCommitteeID, spectypes.RoleCommittee) qbftMessage := &specqbft.Message{ MsgType: specqbft.ProposalMsgType, @@ -211,7 +210,7 @@ func generateCommitteeMsg(ks *spectestingutils.TestKeySet, round specqbft.Round) return signedSSVMessage } -func roundLeader(ks *spectestingutils.TestKeySet, height specqbft.Height, round specqbft.Round) types.OperatorID { +func roundLeader(ks *spectestingutils.TestKeySet, height specqbft.Height, round specqbft.Round) spectypes.OperatorID { share := spectestingutils.TestingShare(ks, 1) firstRoundIndex := 0 @@ -228,10 +227,10 @@ func dummyMsg(t *testing.T, pkHex string, height int, role spectypes.RunnerRole) require.NoError(t, err) dutyExecutorID := pk if role == spectypes.RoleCommittee { - committeeID := ssvtypes.ComputeCommitteeID([]types.OperatorID{1, 2, 3, 4}) + committeeID := ssvtypes.ComputeCommitteeID([]spectypes.OperatorID{1, 2, 3, 4}) dutyExecutorID = append(bytes.Repeat([]byte{0}, 16), committeeID[:]...) } - id := spectypes.NewMsgID(networkconfig.TestNetwork.DomainType(), dutyExecutorID, role) + id := spectypes.NewMsgID(networkconfig.TestNetwork.DomainType, dutyExecutorID, role) qbftMessage := &specqbft.Message{ MsgType: specqbft.CommitMsgType, diff --git a/network/peers/connections/conn_gater.go b/network/peers/connections/conn_gater.go index ffa000a593..5f9274d5b4 100644 --- a/network/peers/connections/conn_gater.go +++ b/network/peers/connections/conn_gater.go @@ -12,8 +12,9 @@ import ( ma "github.com/multiformats/go-multiaddr" manet "github.com/multiformats/go-multiaddr/net" leakybucket "github.com/prysmaticlabs/prysm/v4/container/leaky-bucket" - "github.com/ssvlabs/ssv/logging/fields" "go.uber.org/zap" + + "github.com/ssvlabs/ssv/logging/fields" ) const ( diff --git a/network/peers/connections/handshaker.go b/network/peers/connections/handshaker.go index bdb62ad9a2..13698b4089 100644 --- a/network/peers/connections/handshaker.go +++ b/network/peers/connections/handshaker.go @@ -9,13 +9,13 @@ import ( "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/p2p/protocol/identify" "github.com/pkg/errors" + spectypes "github.com/ssvlabs/ssv-spec/types" "go.uber.org/zap" "github.com/ssvlabs/ssv/logging/fields" "github.com/ssvlabs/ssv/network/peers" "github.com/ssvlabs/ssv/network/records" "github.com/ssvlabs/ssv/network/streams" - "github.com/ssvlabs/ssv/networkconfig" "github.com/ssvlabs/ssv/operator/keys" ) @@ -54,38 +54,38 @@ type handshaker struct { ids identify.IDService net libp2pnetwork.Network - domainTypeProvider networkconfig.DomainTypeProvider - subnetsProvider SubnetsProvider + domainType spectypes.DomainType + subnetsProvider SubnetsProvider } // HandshakerCfg is the configuration for creating an handshaker instance type HandshakerCfg struct { - Network libp2pnetwork.Network - Streams streams.StreamController - NodeInfos peers.NodeInfoIndex - PeerInfos peers.PeerInfoIndex - ConnIdx peers.ConnectionIndex - SubnetsIdx peers.SubnetsIndex - IDService identify.IDService - OperatorSigner keys.OperatorSigner - DomainTypeProvider networkconfig.DomainTypeProvider - SubnetsProvider SubnetsProvider + Network libp2pnetwork.Network + Streams streams.StreamController + NodeInfos peers.NodeInfoIndex + PeerInfos peers.PeerInfoIndex + ConnIdx peers.ConnectionIndex + SubnetsIdx peers.SubnetsIndex + IDService identify.IDService + OperatorSigner keys.OperatorSigner + DomainType spectypes.DomainType + SubnetsProvider SubnetsProvider } // NewHandshaker creates a new instance of handshaker func NewHandshaker(ctx context.Context, cfg *HandshakerCfg, filters func() []HandshakeFilter) Handshaker { h := &handshaker{ - ctx: ctx, - streams: cfg.Streams, - nodeInfos: cfg.NodeInfos, - connIdx: cfg.ConnIdx, - subnetsIdx: cfg.SubnetsIdx, - ids: cfg.IDService, - filters: filters, - peerInfos: cfg.PeerInfos, - subnetsProvider: cfg.SubnetsProvider, - domainTypeProvider: cfg.DomainTypeProvider, - net: cfg.Network, + ctx: ctx, + streams: cfg.Streams, + nodeInfos: cfg.NodeInfos, + connIdx: cfg.ConnIdx, + subnetsIdx: cfg.SubnetsIdx, + ids: cfg.IDService, + filters: filters, + peerInfos: cfg.PeerInfos, + subnetsProvider: cfg.SubnetsProvider, + domainType: cfg.DomainType, + net: cfg.Network, } return h } @@ -243,8 +243,7 @@ func (h *handshaker) applyFilters(sender peer.ID, ni *records.NodeInfo) error { func (h *handshaker) sealedNodeRecord() ([]byte, error) { // Update DomainType. h.nodeInfos.UpdateSelfRecord(func(self *records.NodeInfo) *records.NodeInfo { - dt := h.domainTypeProvider.DomainType() - self.NetworkID = "0x" + hex.EncodeToString(dt[:]) + self.NetworkID = "0x" + hex.EncodeToString(h.domainType[:]) return self }) diff --git a/network/peers/connections/helpers_test.go b/network/peers/connections/helpers_test.go index 5eb52ba88a..c3509e7f22 100644 --- a/network/peers/connections/helpers_test.go +++ b/network/peers/connections/helpers_test.go @@ -90,15 +90,15 @@ func getTestingData(t *testing.T) TestData { } mockHandshaker := handshaker{ - ctx: context.Background(), - nodeInfos: nii, - peerInfos: ns, - subnetsIdx: peers.NewSubnetsIndex(commons.Subnets()), - ids: ids, - net: net, - streams: sc, - filters: func() []HandshakeFilter { return []HandshakeFilter{} }, - domainTypeProvider: networkconfig.TestNetwork, + ctx: context.Background(), + nodeInfos: nii, + peerInfos: ns, + subnetsIdx: peers.NewSubnetsIndex(commons.Subnets()), + ids: ids, + net: net, + streams: sc, + filters: func() []HandshakeFilter { return []HandshakeFilter{} }, + domainType: networkconfig.TestNetwork.DomainType, } mockConn := mock.Conn{ diff --git a/network/records/entries.go b/network/records/entries.go index 846e7d938d..44e4464222 100644 --- a/network/records/entries.go +++ b/network/records/entries.go @@ -15,8 +15,7 @@ import ( type ENRKey string const ( - KeyDomainType = "domaintype" - KeyNextDomainType = "next_domaintype" + KeyDomainType = "domaintype" ) var ErrEntryNotFound = errors.New("not found") diff --git a/network/topics/controller.go b/network/topics/controller.go index 71f37414ab..55b5e0f9f6 100644 --- a/network/topics/controller.go +++ b/network/topics/controller.go @@ -13,7 +13,6 @@ import ( "go.uber.org/zap" "github.com/ssvlabs/ssv/network/commons" - "github.com/ssvlabs/ssv/protocol/genesis/ssv/genesisqueue" "github.com/ssvlabs/ssv/protocol/v2/ssv/queue" ) @@ -279,11 +278,6 @@ func (ctrl *topicsCtrl) listen(logger *zap.Logger, sub *pubsub.Subscription) err commons.GetTopicBaseName(topicName), strconv.FormatUint(uint64(m.MsgType), 10), ).Inc() - case *genesisqueue.GenesisSSVMessage: - metricPubsubInbound.WithLabelValues( - commons.GetTopicBaseName(topicName), - strconv.FormatUint(uint64(m.MsgType), 10), - ).Inc() default: logger.Warn("unknown message type", zap.Any("message", m)) } diff --git a/network/topics/controller_test.go b/network/topics/controller_test.go index 5c4beba80b..728e9a908a 100644 --- a/network/topics/controller_test.go +++ b/network/topics/controller_test.go @@ -17,23 +17,27 @@ import ( "github.com/libp2p/go-libp2p/core/host" libp2pnetwork "github.com/libp2p/go-libp2p/core/network" "github.com/libp2p/go-libp2p/core/peer" - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" + specqbft "github.com/ssvlabs/ssv-spec/qbft" spectypes "github.com/ssvlabs/ssv-spec/types" "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" "go.uber.org/zap" "github.com/ssvlabs/ssv/logging" + "github.com/ssvlabs/ssv/message/signatureverifier" "github.com/ssvlabs/ssv/message/validation" - genesisvalidation "github.com/ssvlabs/ssv/message/validation/genesis" "github.com/ssvlabs/ssv/monitoring/metricsreporter" "github.com/ssvlabs/ssv/network/commons" "github.com/ssvlabs/ssv/network/discovery" "github.com/ssvlabs/ssv/networkconfig" + "github.com/ssvlabs/ssv/operator/duties/dutystore" registrystorage "github.com/ssvlabs/ssv/registry/storage" + "github.com/ssvlabs/ssv/registry/storage/mocks" "github.com/ssvlabs/ssv/storage/basedb" "github.com/ssvlabs/ssv/storage/kv" ) +// TODO: fix this test to run post-fork func TestTopicManager(t *testing.T) { // TODO: reduce running time of this test, use channels instead of long timeouts logger := logging.TestLogger(t) @@ -42,7 +46,7 @@ func TestTopicManager(t *testing.T) { t.Run("happy flow", func(t *testing.T) { nPeers := 4 - pks := []string{"b768cdc2b2e0a859052bf04d1cd66383c96d95096a5287d08151494ce709556ba39c1300fbb902a0e2ebb7c31dc4e400", + cids := []string{"b768cdc2b2e0a859052bf04d1cd66383c96d95096a5287d08151494ce709556ba39c1300fbb902a0e2ebb7c31dc4e400", "824b9024767a01b56790a72afb5f18bb0f97d5bddb946a7bd8dd35cc607c35a4d76be21f24f484d0d478b99dc63ed170", "9340b7b80983a412bbb42cad6f992e06983d53deb41166ed5978dcbfa3761f347b237ad446d7cb4a4d0a5cca78c2ce8a", "a5abb232568fc869765da01688387738153f3ad6cc4e635ab282c5d5cfce2bba2351f03367103090804c5243dc8e229b", @@ -57,13 +61,13 @@ func TestTopicManager(t *testing.T) { validator := &DummyMessageValidator{} peers := newPeers(ctx, logger, t, nPeers, validator, true, nil) - baseTest(t, ctx, logger, peers, pks, 1, 2) + baseTest(t, ctx, logger, peers, cids, 1) }) t.Run("banning peer", func(t *testing.T) { t.Skip() // TODO: finish the test - pks := []string{ + cids := []string{ "b768cdc2b2e0a859052bf04d1cd66383c96d95096a5287d08151494ce709556ba39c1300fbb902a0e2ebb7c31dc4e400", "824b9024767a01b56790a72afb5f18bb0f97d5bddb946a7bd8dd35cc607c35a4d76be21f24f484d0d478b99dc63ed170", "9340b7b80983a412bbb42cad6f992e06983d53deb41166ed5978dcbfa3761f347b237ad446d7cb4a4d0a5cca78c2ce8a", @@ -73,7 +77,13 @@ func TestTopicManager(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - validator := genesisvalidation.New(networkconfig.TestNetwork) + ctrl := gomock.NewController(t) + + dutyStore := dutystore.New() + validatorStore := mocks.NewMockValidatorStore(ctrl) + signatureVerifier := signatureverifier.NewMockSignatureVerifier(ctrl) + + validator := validation.New(networkconfig.TestNetwork, validatorStore, dutyStore, signatureVerifier) scoreMap := map[peer.ID]*pubsub.PeerScoreSnapshot{} var scoreMapMu sync.Mutex @@ -90,27 +100,27 @@ func TestTopicManager(t *testing.T) { const nPeers = 4 peers := newPeers(ctx, logger, t, nPeers, validator, true, scoreInspector) - banningTest(t, ctx, logger, peers, pks, scoreMap, &scoreMapMu) + banningTest(t, logger, peers, cids, scoreMap, &scoreMapMu) }) } -func baseTest(t *testing.T, ctx context.Context, logger *zap.Logger, peers []*P, pks []string, minMsgCount, maxMsgCount int) { - nValidators := len(pks) +func baseTest(t *testing.T, ctx context.Context, logger *zap.Logger, peers []*P, cids []string, minMsgCount int) { + nValidators := len(cids) // nPeers := len(peers) t.Log("subscribing to topics") // listen to topics - for _, pk := range pks { + for _, cid := range cids { for _, p := range peers { - require.NoError(t, p.tm.Subscribe(logger, validatorTopic(pk))) + require.NoError(t, p.tm.Subscribe(logger, committeeTopic(cid))) // simulate concurrency, by trying to subscribe multiple times - go func(tm Controller, pk string) { - require.NoError(t, tm.Subscribe(logger, validatorTopic(pk))) - }(p.tm, pk) - go func(tm Controller, pk string) { + go func(tm Controller, cid string) { + require.NoError(t, tm.Subscribe(logger, committeeTopic(cid))) + }(p.tm, cid) + go func(tm Controller, cid string) { <-time.After(100 * time.Millisecond) - require.NoError(t, tm.Subscribe(logger, validatorTopic(pk))) - }(p.tm, pk) + require.NoError(t, tm.Subscribe(logger, committeeTopic(cid))) + }(p.tm, cid) } } @@ -124,15 +134,15 @@ func baseTest(t *testing.T, ctx context.Context, logger *zap.Logger, peers []*P, for i := 0; i < nValidators; i++ { for j, p := range peers { wg.Add(1) - go func(p *P, pk string, pi int) { + go func(p *P, cid string, pi int) { defer wg.Done() - msg, err := dummyMsg(pk, pi%4, false) + msg, err := dummyMsg(cid, pi%4, false) require.NoError(t, err) raw, err := msg.Encode() require.NoError(t, err) - require.NoError(t, p.tm.Broadcast(validatorTopic(pk), raw, time.Second*10)) + require.NoError(t, p.tm.Broadcast(committeeTopic(cid), raw, time.Second*10)) <-time.After(time.Second * 5) - }(p, pks[i], j) + }(p, cids[i], j) } } wg.Wait() @@ -144,21 +154,21 @@ func baseTest(t *testing.T, ctx context.Context, logger *zap.Logger, peers []*P, // check number of peers and messages for i := 0; i < nValidators; i++ { wg.Add(1) - go func(pk string) { + go func(cid string) { ctxReadMessages, cancel := context.WithTimeout(ctx, time.Second*5) defer cancel() defer wg.Done() for _, p := range peers { // wait for messages - for ctxReadMessages.Err() == nil && p.getCount(commons.GetTopicFullName(validatorTopic(pk))) < minMsgCount { + for ctxReadMessages.Err() == nil && p.getCount(commons.GetTopicFullName(committeeTopic(cid))) < minMsgCount { time.Sleep(time.Millisecond * 100) } require.NoError(t, ctxReadMessages.Err()) - c := p.getCount(commons.GetTopicFullName(validatorTopic(pk))) + c := p.getCount(commons.GetTopicFullName(committeeTopic(cid))) require.GreaterOrEqual(t, c, minMsgCount) // require.LessOrEqual(t, c, maxMsgCount) } - }(pks[i]) + }(cids[i]) } }() wg.Wait() @@ -168,10 +178,10 @@ func baseTest(t *testing.T, ctx context.Context, logger *zap.Logger, peers []*P, for i := 0; i < nValidators; i++ { for _, p := range peers { wg.Add(1) - go func(p *P, pk string) { + go func(p *P, cid string) { defer wg.Done() - topic := validatorTopic(pk) + topic := committeeTopic(cid) topicFullName := commons.GetTopicFullName(topic) err := p.tm.Unsubscribe(logger, topic, false) @@ -192,18 +202,18 @@ func baseTest(t *testing.T, ctx context.Context, logger *zap.Logger, peers []*P, err := p.tm.Unsubscribe(logger, topic, false) require.ErrorContains(t, err, fmt.Sprintf("failed to unsubscribe from topic %s: not subscribed", topicFullName)) }(p) - }(p, pks[i]) + }(p, cids[i]) } } wg.Wait() } -func banningTest(t *testing.T, ctx context.Context, logger *zap.Logger, peers []*P, pks []string, scoreMap map[peer.ID]*pubsub.PeerScoreSnapshot, scoreMapMu *sync.Mutex) { +func banningTest(t *testing.T, logger *zap.Logger, peers []*P, cids []string, scoreMap map[peer.ID]*pubsub.PeerScoreSnapshot, scoreMapMu *sync.Mutex) { t.Log("subscribing to topics") - for _, pk := range pks { + for _, cid := range cids { for _, p := range peers { - require.NoError(t, p.tm.Subscribe(logger, validatorTopic(pk))) + require.NoError(t, p.tm.Subscribe(logger, committeeTopic(cid))) } } @@ -211,7 +221,7 @@ func banningTest(t *testing.T, ctx context.Context, logger *zap.Logger, peers [] <-time.After(3 * time.Second) t.Log("checking initial scores") - for _, pk := range pks { + for _, pk := range cids { for _, p := range peers { peerList, err := p.tm.Peers(pk) require.NoError(t, err) @@ -233,30 +243,30 @@ func banningTest(t *testing.T, ctx context.Context, logger *zap.Logger, peers [] // TODO: get current default score, send an invalid rejected message, check the score; then run 10 of them and check the score; then check valid message - invalidMessages, err := msgSequence(pks[0], invalidMessagesCount, len(pks), true) + invalidMessages, err := msgSequence(cids[0], invalidMessagesCount, len(cids), true) require.NoError(t, err) var wg sync.WaitGroup // publish some messages for i, msg := range invalidMessages { wg.Add(1) - go func(p *P, pk string, msg *spectypes.SSVMessage) { + go func(p *P, cid string, msg *spectypes.SignedSSVMessage) { defer wg.Done() raw, err := msg.Encode() require.NoError(t, err) - require.NoError(t, p.tm.Broadcast(validatorTopic(pk), raw, time.Second*10)) + require.NoError(t, p.tm.Broadcast(committeeTopic(cid), raw, time.Second*10)) <-time.After(time.Second * 5) - }(peers[0], pks[i%len(pks)], msg) + }(peers[0], cids[i%len(cids)], msg) } wg.Wait() <-time.After(5 * time.Second) t.Log("checking final scores") - for _, pk := range pks { + for _, pk := range cids { for _, p := range peers { peerList, err := p.tm.Peers(pk) require.NoError(t, err) @@ -275,30 +285,31 @@ func banningTest(t *testing.T, ctx context.Context, logger *zap.Logger, peers [] //t.Log("unsubscribing") //// unsubscribing multiple times for each topic //wg.Add(1) - //go func(p *P, pk string) { + //go func(p *P, cid string) { // defer wg.Done() - // require.NoError(t, p.tm.Unsubscribe(logger, validatorTopic(pk), false)) + // require.NoError(t, p.tm.Unsubscribe(logger, committeeTopic(cid), false)) // go func(p *P) { // <-time.After(time.Millisecond) - // require.NoError(t, p.tm.Unsubscribe(logger, validatorTopic(pk), false)) + // require.NoError(t, p.tm.Unsubscribe(logger, committeeTopic(cid), false)) // }(p) // wg.Add(1) // go func(p *P) { // defer wg.Done() // <-time.After(time.Millisecond * 50) - // require.NoError(t, p.tm.Unsubscribe(logger, validatorTopic(pk), false)) + // require.NoError(t, p.tm.Unsubscribe(logger, committeeTopic(cid), false)) // }(p) - //}(peer, pk) + //}(peer, cid) // //wg.Wait() } -func validatorTopic(pkhex string) string { - pk, err := hex.DecodeString(pkhex) +func committeeTopic(cidHex string) string { + cid, err := hex.DecodeString(cidHex) if err != nil { return "invalid" } - return commons.ValidatorTopicID(pk)[0] + + return commons.CommitteeTopicID(spectypes.CommitteeID(cid))[0] } type P struct { @@ -418,8 +429,8 @@ func newPeer(ctx context.Context, logger *zap.Logger, t *testing.T, msgValidator return p } -func msgSequence(pkHex string, n, committeeSize int, malformed bool) ([]*spectypes.SSVMessage, error) { - var messages []*spectypes.SSVMessage +func msgSequence(pkHex string, n, committeeSize int, malformed bool) ([]*spectypes.SignedSSVMessage, error) { + var messages []*spectypes.SignedSSVMessage for i := 0; i < n; i++ { height := i * committeeSize @@ -434,47 +445,47 @@ func msgSequence(pkHex string, n, committeeSize int, malformed bool) ([]*spectyp return messages, nil } -func dummyMsg(pkHex string, height int, malformed bool) (*spectypes.SSVMessage, error) { +func dummyMsg(pkHex string, height int, malformed bool) (*spectypes.SignedSSVMessage, error) { pk, err := hex.DecodeString(pkHex) if err != nil { return nil, err } - id := spectypes.NewMsgID(networkconfig.TestNetwork.DomainType(), pk, spectypes.RoleCommittee) + id := spectypes.NewMsgID(networkconfig.TestNetwork.DomainType, pk, spectypes.RoleCommittee) signature, err := base64.StdEncoding.DecodeString("sVV0fsvqQlqliKv/ussGIatxpe8LDWhc9uoaM5WpjbiYvvxUr1eCpz0ja7UT1PGNDdmoGi6xbMC1g/ozhAt4uCdpy0Xdfqbv2hMf2iRL5ZPKOSmMifHbd8yg4PeeceyN") if err != nil { return nil, err } - signedMessage := genesisspecqbft.SignedMessage{ - Signature: signature, - Signers: []spectypes.OperatorID{1, 3, 4}, - Message: genesisspecqbft.Message{ - MsgType: genesisspecqbft.RoundChangeMsgType, - Height: genesisspecqbft.Height(height), - Round: 2, - Identifier: id[:], - Root: [32]byte{}, - }, - FullData: nil, + qbftMessage := &specqbft.Message{ + MsgType: specqbft.RoundChangeMsgType, + Height: specqbft.Height(height), + Round: 2, + Identifier: id[:], + Root: [32]byte{}, } - msgData, err := signedMessage.Encode() + msgData, err := qbftMessage.Encode() if err != nil { return nil, err } - ssvMsg := &spectypes.SSVMessage{ - MsgType: spectypes.SSVConsensusMsgType, - MsgID: id, - Data: msgData, + signedMessage := &spectypes.SignedSSVMessage{ + Signatures: [][]byte{signature}, + OperatorIDs: []spectypes.OperatorID{1, 3, 4}, + SSVMessage: &spectypes.SSVMessage{ + MsgType: spectypes.SSVConsensusMsgType, + MsgID: id, + Data: msgData, + }, + FullData: nil, } if malformed { - ssvMsg.MsgType = math.MaxUint64 + signedMessage.SSVMessage.MsgType = math.MaxUint64 } - return ssvMsg, nil + return signedMessage, nil } type DummyMessageValidator struct { diff --git a/network/topics/msg_id.go b/network/topics/msg_id.go index ac0f8e7ecb..d19005310d 100644 --- a/network/topics/msg_id.go +++ b/network/topics/msg_id.go @@ -10,7 +10,6 @@ import ( "github.com/libp2p/go-libp2p/core/peer" "go.uber.org/zap" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" "github.com/ssvlabs/ssv/logging/fields" "github.com/ssvlabs/ssv/network/commons" "github.com/ssvlabs/ssv/networkconfig" @@ -128,17 +127,6 @@ func (handler *msgIDHandler) MsgID(logger *zap.Logger) func(pmsg *ps_pb.Message) func (handler *msgIDHandler) pubsubMsgToMsgID(msg []byte) string { // TODO: (Alan) should we hash only the message body or what? @GalRogozinski @MatheusFranco99 - // pre-fork hashing scheme - - if !handler.networkConfig.PastAlanFork() { - decodedMsg, _, _, err := genesisspectypes.DecodeSignedSSVMessage(msg) - if err != nil { - // todo: should err here or just log and let the decode function err? - } else { - return commons.MsgID()(decodedMsg) - } - } - // In Alan message structure the message body can be identical for all 4 operators // whereas before it included a BLS signature which made it unique // so we hash full message (including signer) to make it unique diff --git a/network/topics/msg_validator_test.go b/network/topics/msg_validator_test.go index 712cb7eb5d..30e48fd7ad 100644 --- a/network/topics/msg_validator_test.go +++ b/network/topics/msg_validator_test.go @@ -9,7 +9,6 @@ import ( pubsub "github.com/libp2p/go-libp2p-pubsub" ps_pb "github.com/libp2p/go-libp2p-pubsub/pb" pspb "github.com/libp2p/go-libp2p-pubsub/pb" - "github.com/ssvlabs/ssv-spec-pre-cc/types" "github.com/ssvlabs/ssv-spec/qbft" spectypes "github.com/ssvlabs/ssv-spec/types" spectestingutils "github.com/ssvlabs/ssv-spec/types/testingutils" @@ -87,7 +86,7 @@ func TestMsgValidator(t *testing.T) { signedSSVMessage := &spectypes.SignedSSVMessage{ Signatures: [][]byte{sig}, - OperatorIDs: []types.OperatorID{operatorID}, + OperatorIDs: []spectypes.OperatorID{operatorID}, SSVMessage: ssvMsg, } @@ -115,7 +114,7 @@ func TestMsgValidator(t *testing.T) { signedSSVMessage := &spectypes.SignedSSVMessage{ Signatures: [][]byte{sig}, - OperatorIDs: []types.OperatorID{operatorID}, + OperatorIDs: []spectypes.OperatorID{operatorID}, SSVMessage: ssvMsg, } @@ -149,7 +148,7 @@ func TestMsgValidator(t *testing.T) { signedSSVMessage := &spectypes.SignedSSVMessage{ Signatures: [][]byte{sig}, - OperatorIDs: []types.OperatorID{operatorID}, + OperatorIDs: []spectypes.OperatorID{operatorID}, SSVMessage: ssvMsg, } @@ -180,7 +179,7 @@ func newPBMsg(data []byte, topic string, from []byte) *pubsub.Message { } func dummySSVConsensusMsg(dutyExecutorID []byte, height qbft.Height) (*spectypes.SSVMessage, error) { - id := spectypes.NewMsgID(networkconfig.TestNetwork.DomainType(), dutyExecutorID, spectypes.RoleCommittee) + id := spectypes.NewMsgID(networkconfig.TestNetwork.DomainType, dutyExecutorID, spectypes.RoleCommittee) qbftMsg := &qbft.Message{ MsgType: qbft.RoundChangeMsgType, Height: height, diff --git a/network/topics/pubsub.go b/network/topics/pubsub.go index 6d4781970f..e981a83b94 100644 --- a/network/topics/pubsub.go +++ b/network/topics/pubsub.go @@ -163,10 +163,7 @@ func NewPubSub(ctx context.Context, logger *zap.Logger, cfg *PubSubConfig, metri } topicScoreFactory = func(t string) *pubsub.TopicScoreParams { - if cfg.NetworkConfig.PastAlanFork() { - return topicScoreParams(logger, cfg, committeesProvider)(t) - } - return validatorTopicScoreParams(logger, cfg)(t) + return topicScoreParams(logger, cfg, committeesProvider)(t) } // Get overall score params diff --git a/network/topics/scoring.go b/network/topics/scoring.go index a15c4a9f1c..fbd3736aea 100644 --- a/network/topics/scoring.go +++ b/network/topics/scoring.go @@ -228,27 +228,6 @@ func topicScoreParams(logger *zap.Logger, cfg *PubSubConfig, committeesProvider } } -// topicScoreParams factory for creating scoring params for topics -func validatorTopicScoreParams(logger *zap.Logger, cfg *PubSubConfig) func(string) *pubsub.TopicScoreParams { - return func(t string) *pubsub.TopicScoreParams { - totalValidators, activeValidators, myValidators, err := cfg.GetValidatorStats() - if err != nil { - logger.Debug("could not read stats: active validators") - return nil - } - logger := logger.With(zap.String("topic", t), zap.Uint64("totalValidators", totalValidators), - zap.Uint64("activeValidators", activeValidators), zap.Uint64("myValidators", myValidators)) - logger.Debug("got validator stats for score params") - opts := params.NewSubnetTopicOptsValidators(totalValidators, commons.Subnets()) - tp, err := params.TopicParams(opts) - if err != nil { - logger.Debug("ignoring topic score params", zap.Error(err)) - return nil - } - return tp - } -} - // Returns a new committee list with only the committees that belong to the given topic func filterCommitteesForTopic(topic string, committees []*storage.Committee) []*storage.Committee { diff --git a/networkconfig/config.go b/networkconfig/config.go index 2438fd1eba..58a1faba7b 100644 --- a/networkconfig/config.go +++ b/networkconfig/config.go @@ -20,7 +20,7 @@ var SupportedConfigs = map[string]NetworkConfig{ HoleskyE2E.Name: HoleskyE2E, } -const alanForkName = "alan" +const forkName = "alan" func GetNetworkConfigByName(name string) (NetworkConfig, error) { if network, ok := SupportedConfigs[name]; ok { @@ -30,25 +30,15 @@ func GetNetworkConfigByName(name string) (NetworkConfig, error) { return NetworkConfig{}, fmt.Errorf("network not supported: %v", name) } -// DomainTypeProvider is an interface for getting the domain type based on the current or given epoch. -type DomainTypeProvider interface { - DomainType() spectypes.DomainType - NextDomainType() spectypes.DomainType - DomainTypeAtEpoch(epoch phase0.Epoch) spectypes.DomainType -} - type NetworkConfig struct { Name string Beacon beacon.BeaconNetwork - GenesisDomainType spectypes.DomainType - AlanDomainType spectypes.DomainType + DomainType spectypes.DomainType GenesisEpoch phase0.Epoch RegistrySyncOffset *big.Int RegistryContractAddr string // TODO: ethcommon.Address Bootnodes []string DiscoveryProtocolID [6]byte - - AlanForkEpoch phase0.Epoch } func (n NetworkConfig) String() string { @@ -60,16 +50,8 @@ func (n NetworkConfig) String() string { return string(b) } -func (n NetworkConfig) AlanForkNetworkName() string { - return fmt.Sprintf("%s:%s", n.Name, alanForkName) -} - -func (n NetworkConfig) PastAlanFork() bool { - return n.Beacon.EstimatedCurrentEpoch() >= n.AlanForkEpoch -} - -func (n NetworkConfig) PastAlanForkAtEpoch(epoch phase0.Epoch) bool { - return epoch >= n.AlanForkEpoch +func (n NetworkConfig) NetworkName() string { + return fmt.Sprintf("%s:%s", n.Name, forkName) } // ForkVersion returns the fork version of the network. @@ -91,20 +73,3 @@ func (n NetworkConfig) SlotsPerEpoch() uint64 { func (n NetworkConfig) GetGenesisTime() time.Time { return time.Unix(n.Beacon.MinGenesisTime(), 0) } - -// DomainType returns current domain type based on the current fork. -func (n NetworkConfig) DomainType() spectypes.DomainType { - return n.DomainTypeAtEpoch(n.Beacon.EstimatedCurrentEpoch()) -} - -// DomainTypeAtEpoch returns domain type based on the fork at the given epoch. -func (n NetworkConfig) DomainTypeAtEpoch(epoch phase0.Epoch) spectypes.DomainType { - if n.PastAlanForkAtEpoch(epoch) { - return n.AlanDomainType - } - return n.GenesisDomainType -} - -func (n NetworkConfig) NextDomainType() spectypes.DomainType { - return n.AlanDomainType -} diff --git a/networkconfig/holesky-e2e.go b/networkconfig/holesky-e2e.go index 933475d6a9..0f962b83ff 100644 --- a/networkconfig/holesky-e2e.go +++ b/networkconfig/holesky-e2e.go @@ -4,14 +4,14 @@ import ( "math/big" spectypes "github.com/ssvlabs/ssv-spec/types" + "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon" ) var HoleskyE2E = NetworkConfig{ Name: "holesky-e2e", Beacon: beacon.NewNetwork(spectypes.HoleskyNetwork), - GenesisDomainType: spectypes.DomainType{0x0, 0x0, 0xee, 0x0}, - AlanDomainType: spectypes.DomainType{0x0, 0x0, 0xee, 0x1}, + DomainType: spectypes.DomainType{0x0, 0x0, 0xee, 0x1}, GenesisEpoch: 1, RegistryContractAddr: "0x58410bef803ecd7e63b23664c586a6db72daf59c", RegistrySyncOffset: big.NewInt(405579), diff --git a/networkconfig/holesky-stage.go b/networkconfig/holesky-stage.go index 55cd6671f9..a8a8c90d31 100644 --- a/networkconfig/holesky-stage.go +++ b/networkconfig/holesky-stage.go @@ -11,12 +11,10 @@ import ( var HoleskyStage = NetworkConfig{ Name: "holesky-stage", Beacon: beacon.NewNetwork(spectypes.HoleskyNetwork), - GenesisDomainType: [4]byte{0x00, 0x00, 0x31, 0x12}, - AlanDomainType: [4]byte{0x00, 0x00, 0x31, 0x13}, + DomainType: [4]byte{0x00, 0x00, 0x31, 0x13}, GenesisEpoch: 1, RegistrySyncOffset: new(big.Int).SetInt64(84599), RegistryContractAddr: "0x0d33801785340072C452b994496B19f196b7eE15", - AlanForkEpoch: 0, DiscoveryProtocolID: [6]byte{'s', 's', 'v', 'd', 'v', '5'}, Bootnodes: []string{ // Public bootnode: diff --git a/networkconfig/holesky.go b/networkconfig/holesky.go index 32eec0ec32..f91cd78e4d 100644 --- a/networkconfig/holesky.go +++ b/networkconfig/holesky.go @@ -11,10 +11,8 @@ import ( var Holesky = NetworkConfig{ Name: "holesky", Beacon: beacon.NewNetwork(spectypes.HoleskyNetwork), - GenesisDomainType: spectypes.DomainType{0x0, 0x0, 0x5, 0x1}, - AlanDomainType: spectypes.DomainType{0x0, 0x0, 0x5, 0x2}, + DomainType: spectypes.DomainType{0x0, 0x0, 0x5, 0x2}, GenesisEpoch: 1, - AlanForkEpoch: 84600, // Oct-08-2024 12:00:00 PM UTC RegistrySyncOffset: new(big.Int).SetInt64(181612), RegistryContractAddr: "0x38A4794cCEd47d3baf7370CcC43B560D3a1beEFA", DiscoveryProtocolID: [6]byte{'s', 's', 'v', 'd', 'v', '5'}, diff --git a/networkconfig/local-testnet.go b/networkconfig/local-testnet.go index 8b0ce16940..e25a7ff2d2 100644 --- a/networkconfig/local-testnet.go +++ b/networkconfig/local-testnet.go @@ -9,8 +9,7 @@ import ( var LocalTestnet = NetworkConfig{ Name: "local-testnet", Beacon: beacon.NewLocalTestNetwork(spectypes.PraterNetwork), - GenesisDomainType: spectypes.DomainType{0x0, 0x0, spectypes.JatoV2NetworkID.Byte(), 0x1}, - AlanDomainType: spectypes.DomainType{0x0, 0x0, spectypes.JatoV2NetworkID.Byte(), 0x2}, + DomainType: spectypes.DomainType{0x0, 0x0, spectypes.JatoV2NetworkID.Byte(), 0x2}, GenesisEpoch: 1, RegistryContractAddr: "0xC3CD9A0aE89Fff83b71b58b6512D43F8a41f363D", Bootnodes: []string{ diff --git a/networkconfig/mainnet.go b/networkconfig/mainnet.go index a6b6eddbfb..b5886394c7 100644 --- a/networkconfig/mainnet.go +++ b/networkconfig/mainnet.go @@ -11,10 +11,8 @@ import ( var Mainnet = NetworkConfig{ Name: "mainnet", Beacon: beacon.NewNetwork(spectypes.MainNetwork), - GenesisDomainType: spectypes.GenesisMainnet, - AlanDomainType: spectypes.AlanMainnet, + DomainType: spectypes.AlanMainnet, GenesisEpoch: 218450, - AlanForkEpoch: 327375, // Nov-25-2024 12:00:23 PM UTC RegistrySyncOffset: new(big.Int).SetInt64(17507487), RegistryContractAddr: "0xDD9BC35aE942eF0cFa76930954a156B3fF30a4E1", DiscoveryProtocolID: [6]byte{'s', 's', 'v', 'd', 'v', '5'}, diff --git a/networkconfig/test-network.go b/networkconfig/test-network.go index 02002fd5c7..09a9486773 100644 --- a/networkconfig/test-network.go +++ b/networkconfig/test-network.go @@ -11,8 +11,7 @@ import ( var TestNetwork = NetworkConfig{ Name: "testnet", Beacon: beacon.NewNetwork(spectypes.BeaconTestNetwork), - GenesisDomainType: spectypes.DomainType{0x0, 0x0, spectypes.JatoNetworkID.Byte(), 0x1}, - AlanDomainType: spectypes.DomainType{0x0, 0x0, spectypes.JatoNetworkID.Byte(), 0x2}, + DomainType: spectypes.DomainType{0x0, 0x0, spectypes.JatoNetworkID.Byte(), 0x2}, GenesisEpoch: 152834, RegistrySyncOffset: new(big.Int).SetInt64(9015219), RegistryContractAddr: "0x4B133c68A084B8A88f72eDCd7944B69c8D545f03", diff --git a/operator/duties/attester.go b/operator/duties/attester.go index 5f93ee79f5..64d1c4f38c 100644 --- a/operator/duties/attester.go +++ b/operator/duties/attester.go @@ -7,10 +7,9 @@ import ( eth2apiv1 "github.com/attestantio/go-eth2-client/api/v1" "github.com/attestantio/go-eth2-client/spec/phase0" + spectypes "github.com/ssvlabs/ssv-spec/types" "go.uber.org/zap" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - spectypes "github.com/ssvlabs/ssv-spec/types" "github.com/ssvlabs/ssv/logging/fields" "github.com/ssvlabs/ssv/operator/duties/dutystore" ) @@ -172,19 +171,6 @@ func (h *AttesterHandler) processExecution(ctx context.Context, epoch phase0.Epo return } - if !h.network.PastAlanForkAtEpoch(h.network.Beacon.EstimatedEpochAtSlot(slot)) { - toExecute := make([]*genesisspectypes.Duty, 0, len(duties)*2) - for _, d := range duties { - if h.shouldExecute(d) { - toExecute = append(toExecute, h.toGenesisSpecDuty(d, genesisspectypes.BNRoleAttester)) - toExecute = append(toExecute, h.toGenesisSpecDuty(d, genesisspectypes.BNRoleAggregator)) - } - } - - h.dutiesExecutor.ExecuteGenesisDuties(h.logger, toExecute) - return - } - toExecute := make([]*spectypes.ValidatorDuty, 0, len(duties)) for _, d := range duties { if h.shouldExecute(d) { @@ -261,19 +247,6 @@ func (h *AttesterHandler) toSpecDuty(duty *eth2apiv1.AttesterDuty, role spectype } } -func (h *AttesterHandler) toGenesisSpecDuty(duty *eth2apiv1.AttesterDuty, role genesisspectypes.BeaconRole) *genesisspectypes.Duty { - return &genesisspectypes.Duty{ - Type: role, - PubKey: duty.PubKey, - Slot: duty.Slot, - ValidatorIndex: duty.ValidatorIndex, - CommitteeIndex: duty.CommitteeIndex, - CommitteeLength: duty.CommitteeLength, - CommitteesAtSlot: duty.CommitteesAtSlot, - ValidatorCommitteeIndex: duty.ValidatorCommitteeIndex, - } -} - func (h *AttesterHandler) shouldExecute(duty *eth2apiv1.AttesterDuty) bool { currentSlot := h.network.Beacon.EstimatedCurrentSlot() currentEpoch := h.network.Beacon.EstimatedEpochAtSlot(currentSlot) diff --git a/operator/duties/attester_genesis_test.go b/operator/duties/attester_genesis_test.go deleted file mode 100644 index 735e61ea2e..0000000000 --- a/operator/duties/attester_genesis_test.go +++ /dev/null @@ -1,1003 +0,0 @@ -package duties - -import ( - "bytes" - "context" - "testing" - "time" - - eth2apiv1 "github.com/attestantio/go-eth2-client/api/v1" - "github.com/attestantio/go-eth2-client/spec/phase0" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - spectypes "github.com/ssvlabs/ssv-spec/types" - "github.com/stretchr/testify/require" - "go.uber.org/mock/gomock" - - "github.com/ssvlabs/ssv/utils/hashmap" - - "github.com/ssvlabs/ssv/beacon/goclient" - "github.com/ssvlabs/ssv/operator/duties/dutystore" - "github.com/ssvlabs/ssv/protocol/v2/types" -) - -func setupAttesterGenesisDutiesMock( - s *Scheduler, - dutiesMap *hashmap.Map[phase0.Epoch, []*eth2apiv1.AttesterDuty], - waitForDuties *SafeValue[bool], -) (chan struct{}, chan []*genesisspectypes.Duty) { - fetchDutiesCall := make(chan struct{}) - executeDutiesCall := make(chan []*genesisspectypes.Duty) - - s.beaconNode.(*MockBeaconNode).EXPECT().AttesterDuties(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( - func(ctx context.Context, epoch phase0.Epoch, indices []phase0.ValidatorIndex) ([]*eth2apiv1.AttesterDuty, error) { - if waitForDuties.Get() { - fetchDutiesCall <- struct{}{} - } - duties, _ := dutiesMap.Get(epoch) - return duties, nil - }).AnyTimes() - - getShares := func(epoch phase0.Epoch) []*types.SSVShare { - uniqueIndices := make(map[phase0.ValidatorIndex]bool) - - duties, _ := dutiesMap.Get(epoch) - for _, d := range duties { - uniqueIndices[d.ValidatorIndex] = true - } - - shares := make([]*types.SSVShare, 0, len(uniqueIndices)) - for index := range uniqueIndices { - share := &types.SSVShare{ - Share: spectypes.Share{ - ValidatorIndex: index, - }, - } - shares = append(shares, share) - } - - return shares - } - s.validatorProvider.(*MockValidatorProvider).EXPECT().SelfParticipatingValidators(gomock.Any()).DoAndReturn(getShares).AnyTimes() - s.validatorProvider.(*MockValidatorProvider).EXPECT().ParticipatingValidators(gomock.Any()).DoAndReturn(getShares).AnyTimes() - s.validatorProvider.(*MockValidatorProvider).EXPECT().Validator(gomock.Any()).DoAndReturn( - func(pubKey []byte) (*types.SSVShare, bool) { - var ssvShare *types.SSVShare - var minEpoch phase0.Epoch - dutiesMap.Range(func(epoch phase0.Epoch, duties []*eth2apiv1.AttesterDuty) bool { - for _, duty := range duties { - if bytes.Equal(duty.PubKey[:], pubKey) { - ssvShare = &types.SSVShare{ - Share: spectypes.Share{ - ValidatorIndex: duty.ValidatorIndex, - }, - } - if epoch < minEpoch { - minEpoch = epoch - ssvShare.SetMinParticipationEpoch(epoch) - } - return true - } - } - return true - }) - - if ssvShare != nil { - return ssvShare, true - } - - return nil, false - }, - ).AnyTimes() - - s.beaconNode.(*MockBeaconNode).EXPECT().SubmitBeaconCommitteeSubscriptions(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() - - return fetchDutiesCall, executeDutiesCall -} - -func expectedExecutedGenesisAttesterDuties(handler *AttesterHandler, duties []*eth2apiv1.AttesterDuty) []*genesisspectypes.Duty { - expectedDuties := make([]*genesisspectypes.Duty, 0) - for _, d := range duties { - expectedDuties = append(expectedDuties, handler.toGenesisSpecDuty(d, genesisspectypes.BNRoleAttester)) - expectedDuties = append(expectedDuties, handler.toGenesisSpecDuty(d, genesisspectypes.BNRoleAggregator)) - } - return expectedDuties -} - -func TestScheduler_Attester_Genesis_Same_Slot(t *testing.T) { - var ( - handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) - currentSlot = &SafeValue[phase0.Slot]{} - dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() - waitForDuties = &SafeValue[bool]{} - forkEpoch = goclient.FarFutureEpoch - ) - dutiesMap.Set(phase0.Epoch(0), []*eth2apiv1.AttesterDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(1), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - currentSlot.Set(phase0.Slot(1)) - - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupAttesterGenesisDutiesMock(scheduler, dutiesMap, waitForDuties) - startFn() - - duties, _ := dutiesMap.Get(phase0.Epoch(0)) - expected := expectedExecutedGenesisAttesterDuties(handler, duties) - setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - - startTime := time.Now() - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - require.Less(t, scheduler.network.Beacon.SlotDurationSec()/3, time.Since(startTime)) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} - -func TestScheduler_Attester_Genesis_Diff_Slots(t *testing.T) { - var ( - handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) - currentSlot = &SafeValue[phase0.Slot]{} - dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() - waitForDuties = &SafeValue[bool]{} - forkEpoch = goclient.FarFutureEpoch - ) - dutiesMap.Set(phase0.Epoch(0), []*eth2apiv1.AttesterDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(2), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - currentSlot.Set(phase0.Slot(0)) - - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupAttesterGenesisDutiesMock(scheduler, dutiesMap, waitForDuties) - startFn() - - ticker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - currentSlot.Set(phase0.Slot(1)) - ticker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - currentSlot.Set(phase0.Slot(2)) - duties, _ := dutiesMap.Get(phase0.Epoch(0)) - expected := expectedExecutedGenesisAttesterDuties(handler, duties) - setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} - -func TestScheduler_Attester_Genesis_Indices_Changed(t *testing.T) { - var ( - handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) - currentSlot = &SafeValue[phase0.Slot]{} - dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() - waitForDuties = &SafeValue[bool]{} - forkEpoch = goclient.FarFutureEpoch - ) - currentSlot.Set(phase0.Slot(0)) - scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupAttesterGenesisDutiesMock(scheduler, dutiesMap, waitForDuties) - startFn() - - // STEP 1: wait for no action to be taken - mockTicker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 2: trigger a change in active indices - scheduler.indicesChg <- struct{}{} - // no execution should happen in slot 0 - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - dutiesMap.Set(phase0.Epoch(0), []*eth2apiv1.AttesterDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(0), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - { - PubKey: phase0.BLSPubKey{1, 2, 4}, - Slot: phase0.Slot(1), - ValidatorIndex: phase0.ValidatorIndex(2), - }, - { - PubKey: phase0.BLSPubKey{1, 2, 5}, - Slot: phase0.Slot(2), - ValidatorIndex: phase0.ValidatorIndex(3), - }, - }) - - // STEP 3: wait for attester duties to be fetched again - currentSlot.Set(phase0.Slot(1)) - waitForDuties.Set(true) - mockTicker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - // no execution should happen in slot 1 - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 4: wait for attester duties to be executed - currentSlot.Set(phase0.Slot(2)) - duties, _ := dutiesMap.Get(phase0.Epoch(0)) - expected := expectedExecutedGenesisAttesterDuties(handler, []*eth2apiv1.AttesterDuty{duties[2]}) - setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - - mockTicker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} - -func TestScheduler_Attester_Genesis_Multiple_Indices_Changed_Same_Slot(t *testing.T) { - var ( - handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) - currentSlot = &SafeValue[phase0.Slot]{} - dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() - waitForDuties = &SafeValue[bool]{} - forkEpoch = goclient.FarFutureEpoch - ) - currentSlot.Set(phase0.Slot(0)) - scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupAttesterGenesisDutiesMock(scheduler, dutiesMap, waitForDuties) - startFn() - - // STEP 1: wait for no action to be taken - mockTicker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 2: wait for no action to be taken - currentSlot.Set(phase0.Slot(1)) - mockTicker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 3: trigger a change in active indices - scheduler.indicesChg <- struct{}{} - duties, _ := dutiesMap.Get(phase0.Epoch(0)) - dutiesMap.Set(phase0.Epoch(0), append(duties, ð2apiv1.AttesterDuty{ - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(3), - ValidatorIndex: phase0.ValidatorIndex(1), - })) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 4: trigger a change in active indices in the same slot - scheduler.indicesChg <- struct{}{} - duties, _ = dutiesMap.Get(phase0.Epoch(0)) - dutiesMap.Set(phase0.Epoch(0), append(duties, ð2apiv1.AttesterDuty{ - PubKey: phase0.BLSPubKey{1, 2, 4}, - Slot: phase0.Slot(4), - ValidatorIndex: phase0.ValidatorIndex(2), - })) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 5: wait for attester duties to be fetched - currentSlot.Set(phase0.Slot(2)) - waitForDuties.Set(true) - mockTicker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 6: wait for attester duties to be executed - currentSlot.Set(phase0.Slot(3)) - duties, _ = dutiesMap.Get(phase0.Epoch(0)) - expected := expectedExecutedGenesisAttesterDuties(handler, []*eth2apiv1.AttesterDuty{duties[0]}) - setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - - mockTicker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // STEP 7: wait for attester duties to be executed - currentSlot.Set(phase0.Slot(4)) - duties, _ = dutiesMap.Get(phase0.Epoch(0)) - expected = expectedExecutedGenesisAttesterDuties(handler, []*eth2apiv1.AttesterDuty{duties[1]}) - setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - - mockTicker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} - -// reorg previous dependent root changed -func TestScheduler_Attester_Genesis_Reorg_Previous_Epoch_Transition(t *testing.T) { - var ( - handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) - currentSlot = &SafeValue[phase0.Slot]{} - dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() - waitForDuties = &SafeValue[bool]{} - forkEpoch = goclient.FarFutureEpoch - ) - currentSlot.Set(phase0.Slot(63)) - scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupAttesterGenesisDutiesMock(scheduler, dutiesMap, waitForDuties) - startFn() - - dutiesMap.Set(phase0.Epoch(2), []*eth2apiv1.AttesterDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(66), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - - // STEP 1: wait for attester duties to be fetched for next epoch - waitForDuties.Set(true) - mockTicker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 2: trigger head event - e := ð2apiv1.Event{ - Data: ð2apiv1.HeadEvent{ - Slot: currentSlot.Get(), - CurrentDutyDependentRoot: phase0.Root{0x01}, - PreviousDutyDependentRoot: phase0.Root{0x01}, - }, - } - scheduler.HandleHeadEvent(logger)(e) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 3: Ticker with no action - currentSlot.Set(phase0.Slot(64)) - mockTicker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 4: trigger reorg on epoch transition - e = ð2apiv1.Event{ - Data: ð2apiv1.HeadEvent{ - Slot: currentSlot.Get(), - PreviousDutyDependentRoot: phase0.Root{0x02}, - }, - } - dutiesMap.Set(phase0.Epoch(2), []*eth2apiv1.AttesterDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(67), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - scheduler.HandleHeadEvent(logger)(e) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 5: wait for attester duties to be fetched again for the current epoch - currentSlot.Set(phase0.Slot(65)) - mockTicker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 6: The first assigned duty should not be executed - currentSlot.Set(phase0.Slot(66)) - mockTicker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 7: The second assigned duty should be executed - currentSlot.Set(phase0.Slot(67)) - duties, _ := dutiesMap.Get(phase0.Epoch(2)) - expected := expectedExecutedGenesisAttesterDuties(handler, duties) - setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - - mockTicker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} - -// reorg previous dependent root changed and the indices changed as well -func TestScheduler_Attester_Genesis_Reorg_Previous_Epoch_Transition_Indices_Changed(t *testing.T) { - var ( - handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) - currentSlot = &SafeValue[phase0.Slot]{} - dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() - waitForDuties = &SafeValue[bool]{} - forkEpoch = goclient.FarFutureEpoch - ) - currentSlot.Set(phase0.Slot(63)) - scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupAttesterGenesisDutiesMock(scheduler, dutiesMap, waitForDuties) - startFn() - - dutiesMap.Set(phase0.Epoch(2), []*eth2apiv1.AttesterDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(66), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - - // STEP 1: wait for attester duties to be fetched for next epoch - waitForDuties.Set(true) - mockTicker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 2: trigger head event - e := ð2apiv1.Event{ - Data: ð2apiv1.HeadEvent{ - Slot: currentSlot.Get(), - CurrentDutyDependentRoot: phase0.Root{0x01}, - PreviousDutyDependentRoot: phase0.Root{0x01}, - }, - } - scheduler.HandleHeadEvent(logger)(e) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 3: Ticker with no action - currentSlot.Set(phase0.Slot(64)) - mockTicker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 4: trigger reorg on epoch transition - e = ð2apiv1.Event{ - Data: ð2apiv1.HeadEvent{ - Slot: currentSlot.Get(), - PreviousDutyDependentRoot: phase0.Root{0x02}, - }, - } - dutiesMap.Set(phase0.Epoch(2), []*eth2apiv1.AttesterDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(67), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - scheduler.HandleHeadEvent(logger)(e) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 5: trigger indices change - scheduler.indicesChg <- struct{}{} - duties, _ := dutiesMap.Get(phase0.Epoch(2)) - dutiesMap.Set(phase0.Epoch(2), append(duties, ð2apiv1.AttesterDuty{ - PubKey: phase0.BLSPubKey{1, 2, 4}, - Slot: phase0.Slot(67), - ValidatorIndex: phase0.ValidatorIndex(2), - })) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 6: wait for attester duties to be fetched again for the current epoch - currentSlot.Set(phase0.Slot(65)) - mockTicker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 7: The first assigned duty should not be executed - currentSlot.Set(phase0.Slot(66)) - mockTicker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 8: The second assigned duty should be executed - currentSlot.Set(phase0.Slot(67)) - duties, _ = dutiesMap.Get(phase0.Epoch(2)) - expected := expectedExecutedGenesisAttesterDuties(handler, duties) - setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - - mockTicker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} - -// reorg previous dependent root changed -func TestScheduler_Attester_Genesis_Reorg_Previous(t *testing.T) { - var ( - handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) - currentSlot = &SafeValue[phase0.Slot]{} - dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() - waitForDuties = &SafeValue[bool]{} - forkEpoch = goclient.FarFutureEpoch - ) - dutiesMap.Set(phase0.Epoch(1), []*eth2apiv1.AttesterDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(35), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - currentSlot.Set(phase0.Slot(32)) - - // STEP 1: wait for attester duties to be fetched (handle initial duties) - scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupAttesterGenesisDutiesMock(scheduler, dutiesMap, waitForDuties) - startFn() - - mockTicker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 2: trigger head event - e := ð2apiv1.Event{ - Data: ð2apiv1.HeadEvent{ - Slot: currentSlot.Get(), - PreviousDutyDependentRoot: phase0.Root{0x01}, - }, - } - scheduler.HandleHeadEvent(logger)(e) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 3: Ticker with no action - waitForDuties.Set(true) - currentSlot.Set(phase0.Slot(33)) - mockTicker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 4: trigger reorg - e = ð2apiv1.Event{ - Data: ð2apiv1.HeadEvent{ - Slot: currentSlot.Get(), - PreviousDutyDependentRoot: phase0.Root{0x02}, - }, - } - dutiesMap.Set(phase0.Epoch(1), []*eth2apiv1.AttesterDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(36), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - scheduler.HandleHeadEvent(logger)(e) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 5: wait for no action to be taken - currentSlot.Set(phase0.Slot(34)) - mockTicker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 6: The first assigned duty should not be executed - currentSlot.Set(phase0.Slot(35)) - mockTicker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 7: The second assigned duty should be executed - currentSlot.Set(phase0.Slot(36)) - duties, _ := dutiesMap.Get(phase0.Epoch(1)) - expected := expectedExecutedGenesisAttesterDuties(handler, duties) - setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - - mockTicker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} - -// reorg previous dependent root changed and the indices changed the same slot -func TestScheduler_Attester_Genesis_Reorg_Previous_Indices_Change_Same_Slot(t *testing.T) { - var ( - handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) - currentSlot = &SafeValue[phase0.Slot]{} - dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() - waitForDuties = &SafeValue[bool]{} - forkEpoch = goclient.FarFutureEpoch - ) - dutiesMap.Set(phase0.Epoch(1), []*eth2apiv1.AttesterDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(35), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - currentSlot.Set(phase0.Slot(32)) - - // STEP 1: wait for attester duties to be fetched (handle initial duties) - scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupAttesterGenesisDutiesMock(scheduler, dutiesMap, waitForDuties) - startFn() - - mockTicker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 2: trigger head event - e := ð2apiv1.Event{ - Data: ð2apiv1.HeadEvent{ - Slot: currentSlot.Get(), - PreviousDutyDependentRoot: phase0.Root{0x01}, - }, - } - scheduler.HandleHeadEvent(logger)(e) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 3: Ticker with no action - currentSlot.Set(phase0.Slot(33)) - waitForDuties.Set(true) - mockTicker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 4: trigger reorg - e = ð2apiv1.Event{ - Data: ð2apiv1.HeadEvent{ - Slot: currentSlot.Get(), - PreviousDutyDependentRoot: phase0.Root{0x02}, - }, - } - dutiesMap.Set(phase0.Epoch(1), []*eth2apiv1.AttesterDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(36), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - scheduler.HandleHeadEvent(logger)(e) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 5: trigger indices change - scheduler.indicesChg <- struct{}{} - duties, _ := dutiesMap.Get(phase0.Epoch(1)) - dutiesMap.Set(phase0.Epoch(1), append(duties, ð2apiv1.AttesterDuty{ - PubKey: phase0.BLSPubKey{1, 2, 4}, - Slot: phase0.Slot(36), - ValidatorIndex: phase0.ValidatorIndex(2), - })) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 6: wait for attester duties to be fetched again for the current epoch - currentSlot.Set(phase0.Slot(34)) - mockTicker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 7: The first assigned duty should not be executed - currentSlot.Set(phase0.Slot(35)) - mockTicker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 8: The second and new from indices change assigned duties should be executed - currentSlot.Set(phase0.Slot(36)) - duties, _ = dutiesMap.Get(phase0.Epoch(1)) - expected := expectedExecutedGenesisAttesterDuties(handler, duties) - setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - - mockTicker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} - -// reorg current dependent root changed -func TestScheduler_Attester_Genesis_Reorg_Current(t *testing.T) { - var ( - handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) - currentSlot = &SafeValue[phase0.Slot]{} - dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() - waitForDuties = &SafeValue[bool]{} - forkEpoch = goclient.FarFutureEpoch - ) - currentSlot.Set(phase0.Slot(48)) - scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupAttesterGenesisDutiesMock(scheduler, dutiesMap, waitForDuties) - startFn() - - dutiesMap.Set(phase0.Epoch(2), []*eth2apiv1.AttesterDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(64), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - - // STEP 1: wait for attester duties to be fetched for next epoch - waitForDuties.Set(true) - mockTicker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 2: trigger head event - e := ð2apiv1.Event{ - Data: ð2apiv1.HeadEvent{ - Slot: currentSlot.Get(), - CurrentDutyDependentRoot: phase0.Root{0x01}, - }, - } - scheduler.HandleHeadEvent(logger)(e) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 3: Ticker with no action - currentSlot.Set(phase0.Slot(49)) - mockTicker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 4: trigger reorg - e = ð2apiv1.Event{ - Data: ð2apiv1.HeadEvent{ - Slot: currentSlot.Get(), - CurrentDutyDependentRoot: phase0.Root{0x02}, - }, - } - dutiesMap.Set(phase0.Epoch(2), []*eth2apiv1.AttesterDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(65), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - scheduler.HandleHeadEvent(logger)(e) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 5: wait for attester duties to be fetched again for the current epoch - currentSlot.Set(phase0.Slot(50)) - mockTicker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 6: skip to the next epoch - currentSlot.Set(phase0.Slot(51)) - for slot := currentSlot.Get(); slot < 64; slot++ { - mockTicker.Send(slot) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - currentSlot.Set(slot + 1) - } - - // STEP 7: The first assigned duty should not be executed - // slot = 64 - mockTicker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 8: The second assigned duty should be executed - currentSlot.Set(phase0.Slot(65)) - duties, _ := dutiesMap.Get(phase0.Epoch(2)) - expected := expectedExecutedGenesisAttesterDuties(handler, duties) - setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - - mockTicker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} - -// reorg current dependent root changed including indices change in the same slot -func TestScheduler_Attester_Genesis_Reorg_Current_Indices_Changed(t *testing.T) { - var ( - handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) - currentSlot = &SafeValue[phase0.Slot]{} - dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() - waitForDuties = &SafeValue[bool]{} - forkEpoch = goclient.FarFutureEpoch - ) - currentSlot.Set(phase0.Slot(48)) - scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupAttesterGenesisDutiesMock(scheduler, dutiesMap, waitForDuties) - startFn() - - dutiesMap.Set(phase0.Epoch(2), []*eth2apiv1.AttesterDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(64), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - - // STEP 1: wait for attester duties to be fetched for next epoch - waitForDuties.Set(true) - mockTicker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 2: trigger head event - e := ð2apiv1.Event{ - Data: ð2apiv1.HeadEvent{ - Slot: currentSlot.Get(), - CurrentDutyDependentRoot: phase0.Root{0x01}, - }, - } - scheduler.HandleHeadEvent(logger)(e) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 3: Ticker with no action - currentSlot.Set(phase0.Slot(49)) - mockTicker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 4: trigger reorg - e = ð2apiv1.Event{ - Data: ð2apiv1.HeadEvent{ - Slot: currentSlot.Get(), - CurrentDutyDependentRoot: phase0.Root{0x02}, - }, - } - dutiesMap.Set(phase0.Epoch(2), []*eth2apiv1.AttesterDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(65), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - scheduler.HandleHeadEvent(logger)(e) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 5: trigger indices change - scheduler.indicesChg <- struct{}{} - duties, _ := dutiesMap.Get(phase0.Epoch(2)) - dutiesMap.Set(phase0.Epoch(2), append(duties, ð2apiv1.AttesterDuty{ - PubKey: phase0.BLSPubKey{1, 2, 4}, - Slot: phase0.Slot(65), - ValidatorIndex: phase0.ValidatorIndex(2), - })) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 6: wait for attester duties to be fetched again for the next epoch due to indices change - currentSlot.Set(phase0.Slot(50)) - mockTicker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 7: skip to the next epoch - currentSlot.Set(phase0.Slot(51)) - for slot := currentSlot.Get(); slot < 64; slot++ { - mockTicker.Send(slot) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - currentSlot.Set(slot + 1) - } - - // STEP 8: The first assigned duty should not be executed - // slot = 64 - mockTicker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 9: The second assigned duty should be executed - currentSlot.Set(phase0.Slot(65)) - duties, _ = dutiesMap.Get(phase0.Epoch(2)) - expected := expectedExecutedGenesisAttesterDuties(handler, duties) - setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - - mockTicker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} - -func TestScheduler_Attester_Genesis_Early_Block(t *testing.T) { - var ( - handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) - currentSlot = &SafeValue[phase0.Slot]{} - dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() - waitForDuties = &SafeValue[bool]{} - forkEpoch = goclient.FarFutureEpoch - ) - dutiesMap.Set(phase0.Epoch(0), []*eth2apiv1.AttesterDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(2), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - currentSlot.Set(phase0.Slot(0)) - - // STEP 1: wait for attester duties to be fetched (handle initial duties) - scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupAttesterGenesisDutiesMock(scheduler, dutiesMap, waitForDuties) - startFn() - - mockTicker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 2: wait for no action to be taken - currentSlot.Set(phase0.Slot(1)) - mockTicker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 3: wait for attester duties to be executed faster than 1/3 of the slot duration - startTime := time.Now() - currentSlot.Set(phase0.Slot(2)) - mockTicker.Send(currentSlot.Get()) - duties, _ := dutiesMap.Get(phase0.Epoch(0)) - expected := expectedExecutedGenesisAttesterDuties(handler, duties) - setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - - // STEP 4: trigger head event (block arrival) - e := ð2apiv1.Event{ - Data: ð2apiv1.HeadEvent{ - Slot: currentSlot.Get(), - }, - } - scheduler.HandleHeadEvent(logger)(e) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - require.Less(t, time.Since(startTime), scheduler.network.Beacon.SlotDurationSec()/3) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} - -func TestScheduler_Attester_Genesis_Start_In_The_End_Of_The_Epoch(t *testing.T) { - var ( - handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) - currentSlot = &SafeValue[phase0.Slot]{} - dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() - waitForDuties = &SafeValue[bool]{} - forkEpoch = goclient.FarFutureEpoch - ) - currentSlot.Set(phase0.Slot(31)) - scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupAttesterGenesisDutiesMock(scheduler, dutiesMap, waitForDuties) - startFn() - - dutiesMap.Set(phase0.Epoch(1), []*eth2apiv1.AttesterDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(32), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - - // STEP 1: wait for attester duties to be fetched for the next epoch - waitForDuties.Set(true) - mockTicker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 2: wait for attester duties to be executed - currentSlot.Set(phase0.Slot(32)) - duties, _ := dutiesMap.Get(phase0.Epoch(1)) - expected := expectedExecutedGenesisAttesterDuties(handler, duties) - setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - - mockTicker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} - -func TestScheduler_Attester_Genesis_Fetch_Execute_Next_Epoch_Duty(t *testing.T) { - var ( - handler = NewAttesterHandler(dutystore.NewDuties[eth2apiv1.AttesterDuty]()) - currentSlot = &SafeValue[phase0.Slot]{} - dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() - waitForDuties = &SafeValue[bool]{} - forkEpoch = goclient.FarFutureEpoch - ) - currentSlot.Set(phase0.Slot(13)) - scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupAttesterGenesisDutiesMock(scheduler, dutiesMap, waitForDuties) - startFn() - - dutiesMap.Set(phase0.Epoch(1), []*eth2apiv1.AttesterDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(32), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - - // STEP 1: wait for no action to be taken - mockTicker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 2: wait for no action to be taken - currentSlot.Set(phase0.Slot(14)) - mockTicker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 2: wait for duties to be fetched for the next epoch - currentSlot.Set(phase0.Slot(15)) - waitForDuties.Set(true) - mockTicker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 3: wait for attester duties to be executed - currentSlot.Set(phase0.Slot(32)) - duties, _ := dutiesMap.Get(phase0.Epoch(1)) - expected := expectedExecutedGenesisAttesterDuties(handler, duties) - setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - - mockTicker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} diff --git a/operator/duties/attester_test.go b/operator/duties/attester_test.go index 2b9125ae80..d09097f0f3 100644 --- a/operator/duties/attester_test.go +++ b/operator/duties/attester_test.go @@ -106,7 +106,6 @@ func TestScheduler_Attester_Same_Slot(t *testing.T) { currentSlot = &SafeValue[phase0.Slot]{} dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() waitForDuties = &SafeValue[bool]{} - forkEpoch = phase0.Epoch(0) ) dutiesMap.Set(phase0.Epoch(0), []*eth2apiv1.AttesterDuty{ { @@ -117,7 +116,7 @@ func TestScheduler_Attester_Same_Slot(t *testing.T) { }) currentSlot.Set(phase0.Slot(1)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot) fetchDutiesCall, executeDutiesCall := setupAttesterDutiesMock(scheduler, dutiesMap, waitForDuties) startFn() @@ -139,7 +138,6 @@ func TestScheduler_Attester_Diff_Slots(t *testing.T) { currentSlot = &SafeValue[phase0.Slot]{} dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() waitForDuties = &SafeValue[bool]{} - forkEpoch = phase0.Epoch(0) ) dutiesMap.Set(phase0.Epoch(0), []*eth2apiv1.AttesterDuty{ { @@ -150,7 +148,7 @@ func TestScheduler_Attester_Diff_Slots(t *testing.T) { }) currentSlot.Set(phase0.Slot(0)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot) fetchDutiesCall, executeDutiesCall := setupAttesterDutiesMock(scheduler, dutiesMap, waitForDuties) startFn() @@ -180,10 +178,9 @@ func TestScheduler_Attester_Indices_Changed(t *testing.T) { currentSlot = &SafeValue[phase0.Slot]{} dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() waitForDuties = &SafeValue[bool]{} - forkEpoch = phase0.Epoch(0) ) currentSlot.Set(phase0.Slot(0)) - scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) + scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot) fetchDutiesCall, executeDutiesCall := setupAttesterDutiesMock(scheduler, dutiesMap, waitForDuties) startFn() @@ -241,10 +238,9 @@ func TestScheduler_Attester_Multiple_Indices_Changed_Same_Slot(t *testing.T) { currentSlot = &SafeValue[phase0.Slot]{} dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() waitForDuties = &SafeValue[bool]{} - forkEpoch = phase0.Epoch(0) ) currentSlot.Set(phase0.Slot(0)) - scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) + scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot) fetchDutiesCall, executeDutiesCall := setupAttesterDutiesMock(scheduler, dutiesMap, waitForDuties) startFn() @@ -313,10 +309,9 @@ func TestScheduler_Attester_Reorg_Previous_Epoch_Transition(t *testing.T) { currentSlot = &SafeValue[phase0.Slot]{} dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() waitForDuties = &SafeValue[bool]{} - forkEpoch = phase0.Epoch(0) ) currentSlot.Set(phase0.Slot(63)) - scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) + scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot) fetchDutiesCall, executeDutiesCall := setupAttesterDutiesMock(scheduler, dutiesMap, waitForDuties) startFn() @@ -397,10 +392,9 @@ func TestScheduler_Attester_Reorg_Previous_Epoch_Transition_Indices_Changed(t *t currentSlot = &SafeValue[phase0.Slot]{} dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() waitForDuties = &SafeValue[bool]{} - forkEpoch = phase0.Epoch(0) ) currentSlot.Set(phase0.Slot(63)) - scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) + scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot) fetchDutiesCall, executeDutiesCall := setupAttesterDutiesMock(scheduler, dutiesMap, waitForDuties) startFn() @@ -492,7 +486,6 @@ func TestScheduler_Attester_Reorg_Previous(t *testing.T) { currentSlot = &SafeValue[phase0.Slot]{} dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() waitForDuties = &SafeValue[bool]{} - forkEpoch = phase0.Epoch(0) ) dutiesMap.Set(phase0.Epoch(1), []*eth2apiv1.AttesterDuty{ { @@ -504,7 +497,7 @@ func TestScheduler_Attester_Reorg_Previous(t *testing.T) { currentSlot.Set(phase0.Slot(32)) // STEP 1: wait for attester duties to be fetched (handle initial duties) - scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) + scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot) fetchDutiesCall, executeDutiesCall := setupAttesterDutiesMock(scheduler, dutiesMap, waitForDuties) startFn() @@ -575,7 +568,6 @@ func TestScheduler_Attester_Reorg_Previous_Indices_Change_Same_Slot(t *testing.T currentSlot = &SafeValue[phase0.Slot]{} dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() waitForDuties = &SafeValue[bool]{} - forkEpoch = phase0.Epoch(0) ) dutiesMap.Set(phase0.Epoch(1), []*eth2apiv1.AttesterDuty{ { @@ -587,7 +579,7 @@ func TestScheduler_Attester_Reorg_Previous_Indices_Change_Same_Slot(t *testing.T currentSlot.Set(phase0.Slot(32)) // STEP 1: wait for attester duties to be fetched (handle initial duties) - scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) + scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot) fetchDutiesCall, executeDutiesCall := setupAttesterDutiesMock(scheduler, dutiesMap, waitForDuties) startFn() @@ -668,10 +660,9 @@ func TestScheduler_Attester_Reorg_Current(t *testing.T) { currentSlot = &SafeValue[phase0.Slot]{} dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() waitForDuties = &SafeValue[bool]{} - forkEpoch = phase0.Epoch(0) ) currentSlot.Set(phase0.Slot(48)) - scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) + scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot) fetchDutiesCall, executeDutiesCall := setupAttesterDutiesMock(scheduler, dutiesMap, waitForDuties) startFn() @@ -759,10 +750,9 @@ func TestScheduler_Attester_Reorg_Current_Indices_Changed(t *testing.T) { currentSlot = &SafeValue[phase0.Slot]{} dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() waitForDuties = &SafeValue[bool]{} - forkEpoch = phase0.Epoch(0) ) currentSlot.Set(phase0.Slot(48)) - scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) + scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot) fetchDutiesCall, executeDutiesCall := setupAttesterDutiesMock(scheduler, dutiesMap, waitForDuties) startFn() @@ -859,7 +849,6 @@ func TestScheduler_Attester_Early_Block(t *testing.T) { currentSlot = &SafeValue[phase0.Slot]{} dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() waitForDuties = &SafeValue[bool]{} - forkEpoch = phase0.Epoch(0) ) dutiesMap.Set(phase0.Epoch(0), []*eth2apiv1.AttesterDuty{ { @@ -871,7 +860,7 @@ func TestScheduler_Attester_Early_Block(t *testing.T) { currentSlot.Set(phase0.Slot(0)) // STEP 1: wait for attester duties to be fetched (handle initial duties) - scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) + scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot) fetchDutiesCall, executeDutiesCall := setupAttesterDutiesMock(scheduler, dutiesMap, waitForDuties) startFn() @@ -912,10 +901,9 @@ func TestScheduler_Attester_Start_In_The_End_Of_The_Epoch(t *testing.T) { currentSlot = &SafeValue[phase0.Slot]{} dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() waitForDuties = &SafeValue[bool]{} - forkEpoch = phase0.Epoch(0) ) currentSlot.Set(phase0.Slot(31)) - scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) + scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot) fetchDutiesCall, executeDutiesCall := setupAttesterDutiesMock(scheduler, dutiesMap, waitForDuties) startFn() @@ -952,10 +940,9 @@ func TestScheduler_Attester_Fetch_Execute_Next_Epoch_Duty(t *testing.T) { currentSlot = &SafeValue[phase0.Slot]{} dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() waitForDuties = &SafeValue[bool]{} - forkEpoch = phase0.Epoch(0) ) currentSlot.Set(phase0.Slot(13)) - scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) + scheduler, logger, mockTicker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot) fetchDutiesCall, executeDutiesCall := setupAttesterDutiesMock(scheduler, dutiesMap, waitForDuties) startFn() diff --git a/operator/duties/committee.go b/operator/duties/committee.go index 630844814e..29bb028ca0 100644 --- a/operator/duties/committee.go +++ b/operator/duties/committee.go @@ -59,14 +59,6 @@ func (h *CommitteeHandler) HandleDuties(ctx context.Context) { period := h.network.Beacon.EstimatedSyncCommitteePeriodAtEpoch(epoch) buildStr := fmt.Sprintf("p%v-e%v-s%v-#%v", period, epoch, slot, slot%32+1) - if !h.network.PastAlanForkAtEpoch(epoch) { - h.logger.Debug("🛠 ticker event", - zap.String("period_epoch_slot_pos", buildStr), - zap.String("status", "alan not forked yet"), - ) - continue - } - h.logger.Debug("🛠 ticker event", zap.String("period_epoch_slot_pos", buildStr)) h.processExecution(ctx, period, epoch, slot) diff --git a/operator/duties/committee_test.go b/operator/duties/committee_test.go index 527aa1922b..1d1e31e3e3 100644 --- a/operator/duties/committee_test.go +++ b/operator/duties/committee_test.go @@ -146,7 +146,6 @@ func TestScheduler_Committee_Same_Slot_Attester_Only(t *testing.T) { attHandler = NewAttesterHandler(dutyStore.Attester) syncHandler = NewSyncCommitteeHandler(dutyStore.SyncCommittee) commHandler = NewCommitteeHandler(dutyStore.Attester, dutyStore.SyncCommittee) - alanForkEpoch = phase0.Epoch(0) currentSlot = &SafeValue[phase0.Slot]{} waitForDuties = &SafeValue[bool]{} attDuties = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() @@ -169,7 +168,7 @@ func TestScheduler_Committee_Same_Slot_Attester_Only(t *testing.T) { }) currentSlot.Set(phase0.Slot(1)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot) fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) startFn() @@ -198,7 +197,6 @@ func TestScheduler_Committee_Same_Slot_SyncCommittee_Only(t *testing.T) { attHandler = NewAttesterHandler(dutyStore.Attester) syncHandler = NewSyncCommitteeHandler(dutyStore.SyncCommittee) commHandler = NewCommitteeHandler(dutyStore.Attester, dutyStore.SyncCommittee) - alanForkEpoch = phase0.Epoch(0) currentSlot = &SafeValue[phase0.Slot]{} waitForDuties = &SafeValue[bool]{} attDuties = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() @@ -220,7 +218,7 @@ func TestScheduler_Committee_Same_Slot_SyncCommittee_Only(t *testing.T) { }) currentSlot.Set(phase0.Slot(1)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot) fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) startFn() @@ -249,7 +247,6 @@ func TestScheduler_Committee_Same_Slot(t *testing.T) { attHandler = NewAttesterHandler(dutyStore.Attester) syncHandler = NewSyncCommitteeHandler(dutyStore.SyncCommittee) commHandler = NewCommitteeHandler(dutyStore.Attester, dutyStore.SyncCommittee) - alanForkEpoch = phase0.Epoch(0) currentSlot = &SafeValue[phase0.Slot]{} waitForDuties = &SafeValue[bool]{} attDuties = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() @@ -278,7 +275,7 @@ func TestScheduler_Committee_Same_Slot(t *testing.T) { }) currentSlot.Set(phase0.Slot(1)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot) fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) startFn() @@ -308,7 +305,6 @@ func TestScheduler_Committee_Diff_Slot_Attester_Only(t *testing.T) { attHandler = NewAttesterHandler(dutyStore.Attester) syncHandler = NewSyncCommitteeHandler(dutyStore.SyncCommittee) commHandler = NewCommitteeHandler(dutyStore.Attester, dutyStore.SyncCommittee) - alanForkEpoch = phase0.Epoch(0) currentSlot = &SafeValue[phase0.Slot]{} waitForDuties = &SafeValue[bool]{} attDuties = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() @@ -331,7 +327,7 @@ func TestScheduler_Committee_Diff_Slot_Attester_Only(t *testing.T) { }) // STEP 1: wait for attester duties to be fetched using handle initial duties - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot) fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) startFn() @@ -365,7 +361,6 @@ func TestScheduler_Committee_Indices_Changed_Attester_Only(t *testing.T) { attHandler = NewAttesterHandler(dutyStore.Attester) syncHandler = NewSyncCommitteeHandler(dutyStore.SyncCommittee) commHandler = NewCommitteeHandler(dutyStore.Attester, dutyStore.SyncCommittee) - alanForkEpoch = phase0.Epoch(0) currentSlot = &SafeValue[phase0.Slot]{} waitForDuties = &SafeValue[bool]{} attDuties = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() @@ -398,7 +393,7 @@ func TestScheduler_Committee_Indices_Changed_Attester_Only(t *testing.T) { } ) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot) fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) startFn() @@ -468,7 +463,6 @@ func TestScheduler_Committee_Indices_Changed_Attester_Only_2(t *testing.T) { attHandler = NewAttesterHandler(dutyStore.Attester) syncHandler = NewSyncCommitteeHandler(dutyStore.SyncCommittee) commHandler = NewCommitteeHandler(dutyStore.Attester, dutyStore.SyncCommittee) - alanForkEpoch = phase0.Epoch(0) currentSlot = &SafeValue[phase0.Slot]{} waitForDuties = &SafeValue[bool]{} attDuties = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() @@ -501,7 +495,7 @@ func TestScheduler_Committee_Indices_Changed_Attester_Only_2(t *testing.T) { } ) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot) fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) startFn() @@ -571,7 +565,6 @@ func TestScheduler_Committee_Indices_Changed_Attester_Only_3(t *testing.T) { attHandler = NewAttesterHandler(dutyStore.Attester) syncHandler = NewSyncCommitteeHandler(dutyStore.SyncCommittee) commHandler = NewCommitteeHandler(dutyStore.Attester, dutyStore.SyncCommittee) - alanForkEpoch = phase0.Epoch(0) currentSlot = &SafeValue[phase0.Slot]{} waitForDuties = &SafeValue[bool]{} attDuties = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() @@ -604,7 +597,7 @@ func TestScheduler_Committee_Indices_Changed_Attester_Only_3(t *testing.T) { }) // STEP 1: wait for attester duties to be fetched using handle initial duties - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot) fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) startFn() @@ -665,7 +658,6 @@ func TestScheduler_Committee_Reorg_Previous_Epoch_Transition_Attester_only(t *te attHandler = NewAttesterHandler(dutyStore.Attester) syncHandler = NewSyncCommitteeHandler(dutyStore.SyncCommittee) commHandler = NewCommitteeHandler(dutyStore.Attester, dutyStore.SyncCommittee) - alanForkEpoch = phase0.Epoch(0) currentSlot = &SafeValue[phase0.Slot]{} waitForDuties = &SafeValue[bool]{} attDuties = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() @@ -683,7 +675,7 @@ func TestScheduler_Committee_Reorg_Previous_Epoch_Transition_Attester_only(t *te ) currentSlot.Set(phase0.Slot(63)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot) fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) startFn() @@ -763,7 +755,6 @@ func TestScheduler_Committee_Reorg_Previous_Epoch_Transition_Indices_Changed_Att attHandler = NewAttesterHandler(dutyStore.Attester) syncHandler = NewSyncCommitteeHandler(dutyStore.SyncCommittee) commHandler = NewCommitteeHandler(dutyStore.Attester, dutyStore.SyncCommittee) - alanForkEpoch = phase0.Epoch(0) currentSlot = &SafeValue[phase0.Slot]{} waitForDuties = &SafeValue[bool]{} attDuties = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() @@ -789,7 +780,7 @@ func TestScheduler_Committee_Reorg_Previous_Epoch_Transition_Indices_Changed_Att ) currentSlot.Set(phase0.Slot(63)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot) fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) startFn() @@ -883,7 +874,6 @@ func TestScheduler_Committee_Reorg_Previous_Attester_only(t *testing.T) { attHandler = NewAttesterHandler(dutyStore.Attester) syncHandler = NewSyncCommitteeHandler(dutyStore.SyncCommittee) commHandler = NewCommitteeHandler(dutyStore.Attester, dutyStore.SyncCommittee) - alanForkEpoch = phase0.Epoch(0) currentSlot = &SafeValue[phase0.Slot]{} waitForDuties = &SafeValue[bool]{} attDuties = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() @@ -910,7 +900,7 @@ func TestScheduler_Committee_Reorg_Previous_Attester_only(t *testing.T) { // STEP 1: wait for attester duties to be fetched using handle initial duties currentSlot.Set(phase0.Slot(32)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot) fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) startFn() @@ -980,7 +970,6 @@ func TestScheduler_Committee_Early_Block_Attester_Only(t *testing.T) { attHandler = NewAttesterHandler(dutyStore.Attester) syncHandler = NewSyncCommitteeHandler(dutyStore.SyncCommittee) commHandler = NewCommitteeHandler(dutyStore.Attester, dutyStore.SyncCommittee) - alanForkEpoch = phase0.Epoch(0) currentSlot = &SafeValue[phase0.Slot]{} waitForDuties = &SafeValue[bool]{} attDuties = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() @@ -1003,7 +992,7 @@ func TestScheduler_Committee_Early_Block_Attester_Only(t *testing.T) { }) // STEP 1: wait for attester duties to be fetched (handle initial duties) currentSlot.Set(phase0.Slot(0)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot) fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) startFn() @@ -1045,7 +1034,6 @@ func TestScheduler_Committee_Early_Block(t *testing.T) { attHandler = NewAttesterHandler(dutyStore.Attester) syncHandler = NewSyncCommitteeHandler(dutyStore.SyncCommittee) commHandler = NewCommitteeHandler(dutyStore.Attester, dutyStore.SyncCommittee) - alanForkEpoch = phase0.Epoch(0) currentSlot = &SafeValue[phase0.Slot]{} waitForDuties = &SafeValue[bool]{} attDuties = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() @@ -1074,7 +1062,7 @@ func TestScheduler_Committee_Early_Block(t *testing.T) { }) // STEP 1: wait for attester & sync committee duties to be fetched (handle initial duties) currentSlot.Set(phase0.Slot(1)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot) fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) startFn() @@ -1113,176 +1101,3 @@ func TestScheduler_Committee_Early_Block(t *testing.T) { cancel() require.NoError(t, schedulerPool.Wait()) } - -func TestScheduler_Committee_On_Fork_Attester_only(t *testing.T) { - var ( - dutyStore = dutystore.New() - attHandler = NewAttesterHandler(dutyStore.Attester) - syncHandler = NewSyncCommitteeHandler(dutyStore.SyncCommittee) - commHandler = NewCommitteeHandler(dutyStore.Attester, dutyStore.SyncCommittee) - alanForkEpoch = phase0.Epoch(2) - currentSlot = &SafeValue[phase0.Slot]{} - waitForDuties = &SafeValue[bool]{} - attDuties = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() - syncDuties = hashmap.New[uint64, []*eth2apiv1.SyncCommitteeDuty]() - activeShares = []*ssvtypes.SSVShare{{ - Share: spectypes.Share{ - Committee: []*spectypes.ShareMember{ - {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, - }, - ValidatorIndex: 1, - }, - }} - ) - attDuties.Set(phase0.Epoch(0), []*eth2apiv1.AttesterDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(1), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - attDuties.Set(phase0.Epoch(2), []*eth2apiv1.AttesterDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(64), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - - currentSlot.Set(phase0.Slot(1)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) - fetchAttesterDutiesCall, executeAttesterDutiesCall := setupAttesterGenesisDutiesMock(scheduler, attDuties, waitForDuties) - _, _ = setupSyncCommitteeDutiesMock(scheduler, activeShares, syncDuties, waitForDuties) - fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) - startFn() - - aDuties, _ := attDuties.Get(0) - aExpected := expectedExecutedGenesisAttesterDuties(attHandler, aDuties) - setExecuteGenesisDutyFunc(scheduler, executeAttesterDutiesCall, len(aExpected)) - - startTime := time.Now() - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchAttesterDutiesCall, executeAttesterDutiesCall, timeout, aExpected) - waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // validate the 1/3 of the slot waiting time - require.Less(t, scheduler.network.Beacon.SlotDurationSec()/3, time.Since(startTime)) - - // skip to the next epoch - currentSlot.Set(phase0.Slot(2)) - for slot := currentSlot.Get(); slot < 48; slot++ { - ticker.Send(slot) - waitForNoActionGenesis(t, logger, fetchAttesterDutiesCall, executeAttesterDutiesCall, timeout) - waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - currentSlot.Set(slot + 1) - } - - // wait for duties to be fetched for the next fork epoch - currentSlot.Set(phase0.Slot(48)) - waitForDuties.Set(true) - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchAttesterDutiesCall, executeAttesterDutiesCall, timeout) - - currentSlot.Set(phase0.Slot(64)) - aDuties, _ = attDuties.Get(2) - committeeMap := commHandler.buildCommitteeDuties(aDuties, nil, 2, currentSlot.Get()) - setExecuteDutyFuncs(scheduler, executeDutiesCall, len(committeeMap)) - - startTime = time.Now() - ticker.Send(currentSlot.Get()) - waitForDutiesExecutionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout, committeeMap) - waitForNoActionGenesis(t, logger, fetchAttesterDutiesCall, executeAttesterDutiesCall, timeout) - - // validate the 1/3 of the slot waiting time - require.Less(t, scheduler.network.Beacon.SlotDurationSec()/3, time.Since(startTime)) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} - -func TestScheduler_Committee_On_Fork(t *testing.T) { - var ( - dutyStore = dutystore.New() - attHandler = NewAttesterHandler(dutyStore.Attester) - syncHandler = NewSyncCommitteeHandler(dutyStore.SyncCommittee) - commHandler = NewCommitteeHandler(dutyStore.Attester, dutyStore.SyncCommittee) - alanForkEpoch = phase0.Epoch(256) - currentSlot = &SafeValue[phase0.Slot]{} - waitForDuties = &SafeValue[bool]{} - attDuties = hashmap.New[phase0.Epoch, []*eth2apiv1.AttesterDuty]() - syncDuties = hashmap.New[uint64, []*eth2apiv1.SyncCommitteeDuty]() - activeShares = []*ssvtypes.SSVShare{{ - Share: spectypes.Share{ - Committee: []*spectypes.ShareMember{ - {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, - }, - ValidatorIndex: 1, - }, - }} - ) - - lastPeriodEpoch := phase0.Epoch(256 - 1) - attDuties.Set(lastPeriodEpoch, []*eth2apiv1.AttesterDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(lastPeriodEpoch*32 + 1), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - attDuties.Set(lastPeriodEpoch+1, []*eth2apiv1.AttesterDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot((lastPeriodEpoch + 1) * 32), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - syncDuties.Set(1, []*eth2apiv1.SyncCommitteeDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - - currentSlot.Set(phase0.Slot(lastPeriodEpoch * 32)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{attHandler, syncHandler, commHandler}, currentSlot, alanForkEpoch) - fetchAttesterDutiesCall, executeAttesterDutiesCall := setupAttesterGenesisDutiesMock(scheduler, attDuties, waitForDuties) - _, _ = setupSyncCommitteeDutiesMock(scheduler, activeShares, syncDuties, waitForDuties) - fetchDutiesCall, executeDutiesCall := setupCommitteeDutiesMock(scheduler, activeShares, attDuties, syncDuties, waitForDuties) - startFn() - - aDuties, _ := attDuties.Get(lastPeriodEpoch) - aExpected := expectedExecutedGenesisAttesterDuties(attHandler, aDuties) - setExecuteGenesisDutyFunc(scheduler, executeAttesterDutiesCall, len(aExpected)) - - ticker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, fetchAttesterDutiesCall, executeAttesterDutiesCall, timeout) - waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - currentSlot.Set(phase0.Slot(lastPeriodEpoch*32 + 1)) - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchAttesterDutiesCall, executeAttesterDutiesCall, timeout, aExpected) - waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - currentSlot.Set(phase0.Slot(lastPeriodEpoch*32 + 2)) - for slot := currentSlot.Get(); slot < 256*32; slot++ { - ticker.Send(slot) - waitForNoActionGenesis(t, logger, fetchAttesterDutiesCall, executeAttesterDutiesCall, timeout) - waitForNoActionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - currentSlot.Set(slot + 1) - } - - currentSlot.Set(phase0.Slot(256 * 32)) - aDuties, _ = attDuties.Get(lastPeriodEpoch + 1) - sDuties, _ := syncDuties.Get(1) - committeeMap := commHandler.buildCommitteeDuties(aDuties, sDuties, 256, currentSlot.Get()) - setExecuteDutyFuncs(scheduler, executeDutiesCall, len(committeeMap)) - - ticker.Send(currentSlot.Get()) - waitForDutiesExecutionCommittee(t, logger, fetchDutiesCall, executeDutiesCall, timeout, committeeMap) - waitForNoActionGenesis(t, logger, fetchAttesterDutiesCall, executeAttesterDutiesCall, timeout) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} diff --git a/operator/duties/proposer.go b/operator/duties/proposer.go index f78217e0dd..c57488adde 100644 --- a/operator/duties/proposer.go +++ b/operator/duties/proposer.go @@ -7,10 +7,9 @@ import ( eth2apiv1 "github.com/attestantio/go-eth2-client/api/v1" "github.com/attestantio/go-eth2-client/spec/phase0" + spectypes "github.com/ssvlabs/ssv-spec/types" "go.uber.org/zap" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - spectypes "github.com/ssvlabs/ssv-spec/types" "github.com/ssvlabs/ssv/logging/fields" "github.com/ssvlabs/ssv/operator/duties/dutystore" ) @@ -138,18 +137,6 @@ func (h *ProposerHandler) processExecution(ctx context.Context, epoch phase0.Epo return } - if !h.network.PastAlanForkAtEpoch(epoch) { - toExecute := make([]*genesisspectypes.Duty, 0, len(duties)) - for _, d := range duties { - if h.shouldExecute(d) { - toExecute = append(toExecute, h.toGenesisSpecDuty(d, genesisspectypes.BNRoleProposer)) - } - } - - h.dutiesExecutor.ExecuteGenesisDuties(h.logger, toExecute) - return - } - // range over duties and execute toExecute := make([]*spectypes.ValidatorDuty, 0, len(duties)) for _, d := range duties { @@ -212,15 +199,6 @@ func (h *ProposerHandler) toSpecDuty(duty *eth2apiv1.ProposerDuty, role spectype } } -func (h *ProposerHandler) toGenesisSpecDuty(duty *eth2apiv1.ProposerDuty, role genesisspectypes.BeaconRole) *genesisspectypes.Duty { - return &genesisspectypes.Duty{ - Type: role, - PubKey: duty.PubKey, - Slot: duty.Slot, - ValidatorIndex: duty.ValidatorIndex, - } -} - func (h *ProposerHandler) shouldExecute(duty *eth2apiv1.ProposerDuty) bool { currentSlot := h.network.Beacon.EstimatedCurrentSlot() // execute task if slot already began and not pass 1 slot diff --git a/operator/duties/proposer_genesis_test.go b/operator/duties/proposer_genesis_test.go deleted file mode 100644 index c9125fafec..0000000000 --- a/operator/duties/proposer_genesis_test.go +++ /dev/null @@ -1,456 +0,0 @@ -package duties - -import ( - "context" - "testing" - - eth2apiv1 "github.com/attestantio/go-eth2-client/api/v1" - "github.com/attestantio/go-eth2-client/spec/phase0" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - "github.com/stretchr/testify/require" - "go.uber.org/mock/gomock" - - "github.com/ssvlabs/ssv/utils/hashmap" - - spectypes "github.com/ssvlabs/ssv-spec/types" - - "github.com/ssvlabs/ssv/beacon/goclient" - "github.com/ssvlabs/ssv/operator/duties/dutystore" - "github.com/ssvlabs/ssv/protocol/v2/types" -) - -func setupProposerGenesisDutiesMock(s *Scheduler, dutiesMap *hashmap.Map[phase0.Epoch, []*eth2apiv1.ProposerDuty]) (chan struct{}, chan []*genesisspectypes.Duty) { - fetchDutiesCall := make(chan struct{}) - executeDutiesCall := make(chan []*genesisspectypes.Duty) - - s.beaconNode.(*MockBeaconNode).EXPECT().ProposerDuties(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( - func(ctx context.Context, epoch phase0.Epoch, indices []phase0.ValidatorIndex) ([]*eth2apiv1.ProposerDuty, error) { - fetchDutiesCall <- struct{}{} - duties, _ := dutiesMap.Get(epoch) - return duties, nil - }).AnyTimes() - - getShares := func(epoch phase0.Epoch) []*types.SSVShare { - uniqueIndices := make(map[phase0.ValidatorIndex]bool) - - duties, _ := dutiesMap.Get(epoch) - for _, d := range duties { - uniqueIndices[d.ValidatorIndex] = true - } - - shares := make([]*types.SSVShare, 0, len(uniqueIndices)) - for index := range uniqueIndices { - share := &types.SSVShare{ - Share: spectypes.Share{ - ValidatorIndex: index, - }, - } - shares = append(shares, share) - } - - return shares - } - - s.validatorProvider.(*MockValidatorProvider).EXPECT().SelfParticipatingValidators(gomock.Any()).DoAndReturn(getShares).AnyTimes() - s.validatorProvider.(*MockValidatorProvider).EXPECT().ParticipatingValidators(gomock.Any()).DoAndReturn(getShares).AnyTimes() - - return fetchDutiesCall, executeDutiesCall -} - -func expectedExecutedGenesisProposerDuties(handler *ProposerHandler, duties []*eth2apiv1.ProposerDuty) []*genesisspectypes.Duty { - expectedDuties := make([]*genesisspectypes.Duty, 0) - for _, d := range duties { - expectedDuties = append(expectedDuties, handler.toGenesisSpecDuty(d, genesisspectypes.BNRoleProposer)) - } - return expectedDuties -} - -func TestScheduler_Proposer_Genesis_Same_Slot(t *testing.T) { - var ( - handler = NewProposerHandler(dutystore.NewDuties[eth2apiv1.ProposerDuty]()) - currentSlot = &SafeValue[phase0.Slot]{} - dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.ProposerDuty]() - ) - currentSlot.Set(phase0.Slot(0)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, goclient.FarFutureEpoch) - fetchDutiesCall, executeDutiesCall := setupProposerGenesisDutiesMock(scheduler, dutiesMap) - startFn() - - dutiesMap.Set(phase0.Epoch(0), []*eth2apiv1.ProposerDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(0), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - - // STEP 1: wait for proposer duties to be fetched and executed at the same slot - duties, _ := dutiesMap.Get(phase0.Epoch(0)) - expected := expectedExecutedGenesisProposerDuties(handler, duties) - setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} - -func TestScheduler_Proposer_Genesis_Diff_Slots(t *testing.T) { - var ( - handler = NewProposerHandler(dutystore.NewDuties[eth2apiv1.ProposerDuty]()) - currentSlot = &SafeValue[phase0.Slot]{} - dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.ProposerDuty]() - ) - currentSlot.Set(phase0.Slot(0)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, goclient.FarFutureEpoch) - fetchDutiesCall, executeDutiesCall := setupProposerGenesisDutiesMock(scheduler, dutiesMap) - startFn() - - dutiesMap.Set(phase0.Epoch(0), []*eth2apiv1.ProposerDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(2), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - - // STEP 1: wait for proposer duties to be fetched - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 2: wait for no action to be taken - currentSlot.Set(phase0.Slot(1)) - ticker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 3: wait for proposer duties to be executed - currentSlot.Set(phase0.Slot(2)) - duties, _ := dutiesMap.Get(phase0.Epoch(0)) - expected := expectedExecutedGenesisProposerDuties(handler, duties) - setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} - -// execute duty after two slots after the indices changed -func TestScheduler_Proposer_Genesis_Indices_Changed(t *testing.T) { - var ( - handler = NewProposerHandler(dutystore.NewDuties[eth2apiv1.ProposerDuty]()) - currentSlot = &SafeValue[phase0.Slot]{} - dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.ProposerDuty]() - ) - currentSlot.Set(phase0.Slot(0)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, goclient.FarFutureEpoch) - fetchDutiesCall, executeDutiesCall := setupProposerGenesisDutiesMock(scheduler, dutiesMap) - startFn() - - // STEP 1: wait for no action to be taken - ticker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 2: wait for no action to be taken - currentSlot.Set(phase0.Slot(1)) - ticker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 3: trigger a change in active indices - scheduler.indicesChg <- struct{}{} - dutiesMap.Set(phase0.Epoch(0), []*eth2apiv1.ProposerDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(1), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - { - PubKey: phase0.BLSPubKey{1, 2, 4}, - Slot: phase0.Slot(2), - ValidatorIndex: phase0.ValidatorIndex(2), - }, - { - PubKey: phase0.BLSPubKey{1, 2, 5}, - Slot: phase0.Slot(3), - ValidatorIndex: phase0.ValidatorIndex(3), - }, - }) - // no execution should happen in slot 1 - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 4: wait for proposer duties to be fetched again - currentSlot.Set(phase0.Slot(2)) - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - // no execution should happen in slot 2 - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 4: wait for proposer duties to be executed - currentSlot.Set(phase0.Slot(3)) - duties, _ := dutiesMap.Get(phase0.Epoch(0)) - expected := expectedExecutedGenesisProposerDuties(handler, []*eth2apiv1.ProposerDuty{duties[2]}) - setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} - -func TestScheduler_Proposer_Genesis_Multiple_Indices_Changed_Same_Slot(t *testing.T) { - var ( - handler = NewProposerHandler(dutystore.NewDuties[eth2apiv1.ProposerDuty]()) - currentSlot = &SafeValue[phase0.Slot]{} - dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.ProposerDuty]() - ) - currentSlot.Set(phase0.Slot(0)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, goclient.FarFutureEpoch) - fetchDutiesCall, executeDutiesCall := setupProposerGenesisDutiesMock(scheduler, dutiesMap) - startFn() - - dutiesMap.Set(phase0.Epoch(0), []*eth2apiv1.ProposerDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(2), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - - // STEP 1: wait for proposer duties to be fetched - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 2: trigger a change in active indices - scheduler.indicesChg <- struct{}{} - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - duties, _ := dutiesMap.Get(phase0.Epoch(0)) - dutiesMap.Set(phase0.Epoch(0), append(duties, ð2apiv1.ProposerDuty{ - PubKey: phase0.BLSPubKey{1, 2, 4}, - Slot: phase0.Slot(3), - ValidatorIndex: phase0.ValidatorIndex(2), - })) - - // STEP 3: trigger a change in active indices in the same slot - scheduler.indicesChg <- struct{}{} - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - duties, _ = dutiesMap.Get(phase0.Epoch(0)) - dutiesMap.Set(phase0.Epoch(0), append(duties, ð2apiv1.ProposerDuty{ - PubKey: phase0.BLSPubKey{1, 2, 5}, - Slot: phase0.Slot(4), - ValidatorIndex: phase0.ValidatorIndex(3), - })) - - // STEP 4: wait for proposer duties to be fetched again - currentSlot.Set(phase0.Slot(1)) - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 5: wait for proposer duties to be executed - currentSlot.Set(phase0.Slot(2)) - duties, _ = dutiesMap.Get(phase0.Epoch(0)) - expected := expectedExecutedGenesisProposerDuties(handler, []*eth2apiv1.ProposerDuty{duties[0]}) - setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // STEP 6: wait for proposer duties to be executed - currentSlot.Set(phase0.Slot(3)) - duties, _ = dutiesMap.Get(phase0.Epoch(0)) - expected = expectedExecutedGenesisProposerDuties(handler, []*eth2apiv1.ProposerDuty{duties[1]}) - setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // STEP 7: wait for proposer duties to be executed - currentSlot.Set(phase0.Slot(4)) - duties, _ = dutiesMap.Get(phase0.Epoch(0)) - expected = expectedExecutedGenesisProposerDuties(handler, []*eth2apiv1.ProposerDuty{duties[2]}) - setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} - -// reorg current dependent root changed -func TestScheduler_Proposer_Genesis_Reorg_Current(t *testing.T) { - var ( - handler = NewProposerHandler(dutystore.NewDuties[eth2apiv1.ProposerDuty]()) - currentSlot = &SafeValue[phase0.Slot]{} - dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.ProposerDuty]() - ) - currentSlot.Set(phase0.Slot(34)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, goclient.FarFutureEpoch) - fetchDutiesCall, executeDutiesCall := setupProposerGenesisDutiesMock(scheduler, dutiesMap) - startFn() - - dutiesMap.Set(phase0.Epoch(1), []*eth2apiv1.ProposerDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(36), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - - // STEP 1: wait for proposer duties to be fetched - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 2: trigger head event - e := ð2apiv1.Event{ - Data: ð2apiv1.HeadEvent{ - Slot: currentSlot.Get(), - CurrentDutyDependentRoot: phase0.Root{0x01}, - }, - } - scheduler.HandleHeadEvent(logger)(e) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 3: Ticker with no action - currentSlot.Set(phase0.Slot(35)) - ticker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 4: trigger reorg - e = ð2apiv1.Event{ - Data: ð2apiv1.HeadEvent{ - Slot: currentSlot.Get(), - CurrentDutyDependentRoot: phase0.Root{0x02}, - }, - } - dutiesMap.Set(phase0.Epoch(1), []*eth2apiv1.ProposerDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(37), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - scheduler.HandleHeadEvent(logger)(e) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 5: wait for proposer duties to be fetched again for the current epoch. - // The first assigned duty should not be executed - currentSlot.Set(phase0.Slot(36)) - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 7: The second assigned duty should be executed - currentSlot.Set(phase0.Slot(37)) - duties, _ := dutiesMap.Get(phase0.Epoch(1)) - expected := expectedExecutedGenesisProposerDuties(handler, duties) - setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} - -// reorg current dependent root changed -func TestScheduler_Proposer_Genesis_Reorg_Current_Indices_Changed(t *testing.T) { - var ( - handler = NewProposerHandler(dutystore.NewDuties[eth2apiv1.ProposerDuty]()) - currentSlot = &SafeValue[phase0.Slot]{} - dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.ProposerDuty]() - ) - currentSlot.Set(phase0.Slot(34)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, goclient.FarFutureEpoch) - fetchDutiesCall, executeDutiesCall := setupProposerGenesisDutiesMock(scheduler, dutiesMap) - startFn() - - dutiesMap.Set(phase0.Epoch(1), []*eth2apiv1.ProposerDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(36), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - - // STEP 1: wait for proposer duties to be fetched - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 2: trigger head event - e := ð2apiv1.Event{ - Data: ð2apiv1.HeadEvent{ - Slot: currentSlot.Get(), - CurrentDutyDependentRoot: phase0.Root{0x01}, - }, - } - scheduler.HandleHeadEvent(logger)(e) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 3: Ticker with no action - currentSlot.Set(phase0.Slot(35)) - ticker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 4: trigger reorg - e = ð2apiv1.Event{ - Data: ð2apiv1.HeadEvent{ - Slot: currentSlot.Get(), - CurrentDutyDependentRoot: phase0.Root{0x02}, - }, - } - dutiesMap.Set(phase0.Epoch(1), []*eth2apiv1.ProposerDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(37), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - scheduler.HandleHeadEvent(logger)(e) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 5: trigger a change in active indices in the same slot - scheduler.indicesChg <- struct{}{} - duties, _ := dutiesMap.Get(phase0.Epoch(1)) - dutiesMap.Set(phase0.Epoch(1), append(duties, ð2apiv1.ProposerDuty{ - PubKey: phase0.BLSPubKey{1, 2, 4}, - Slot: phase0.Slot(38), - ValidatorIndex: phase0.ValidatorIndex(2), - })) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 6: wait for proposer duties to be fetched again for the current epoch. - // The first assigned duty should not be executed - currentSlot.Set(phase0.Slot(36)) - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 7: The second assigned duty should be executed - currentSlot.Set(phase0.Slot(37)) - duties, _ = dutiesMap.Get(phase0.Epoch(1)) - expected := expectedExecutedGenesisProposerDuties(handler, []*eth2apiv1.ProposerDuty{duties[0]}) - setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // STEP 8: The second assigned duty should be executed - currentSlot.Set(phase0.Slot(38)) - duties, _ = dutiesMap.Get(phase0.Epoch(1)) - expected = expectedExecutedGenesisProposerDuties(handler, []*eth2apiv1.ProposerDuty{duties[1]}) - setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} diff --git a/operator/duties/proposer_test.go b/operator/duties/proposer_test.go index 8a13a3ce77..e9a8b5d2a7 100644 --- a/operator/duties/proposer_test.go +++ b/operator/duties/proposer_test.go @@ -70,7 +70,7 @@ func TestScheduler_Proposer_Same_Slot(t *testing.T) { dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.ProposerDuty]() ) currentSlot.Set(phase0.Slot(0)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, 0) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot) fetchDutiesCall, executeDutiesCall := setupProposerDutiesMock(scheduler, dutiesMap) startFn() @@ -103,7 +103,7 @@ func TestScheduler_Proposer_Diff_Slots(t *testing.T) { dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.ProposerDuty]() ) currentSlot.Set(phase0.Slot(0)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, 0) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot) fetchDutiesCall, executeDutiesCall := setupProposerDutiesMock(scheduler, dutiesMap) startFn() @@ -146,7 +146,7 @@ func TestScheduler_Proposer_Indices_Changed(t *testing.T) { dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.ProposerDuty]() ) currentSlot.Set(phase0.Slot(0)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, 0) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot) fetchDutiesCall, executeDutiesCall := setupProposerDutiesMock(scheduler, dutiesMap) startFn() @@ -209,7 +209,7 @@ func TestScheduler_Proposer_Multiple_Indices_Changed_Same_Slot(t *testing.T) { dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.ProposerDuty]() ) currentSlot.Set(phase0.Slot(0)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, 0) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot) fetchDutiesCall, executeDutiesCall := setupProposerDutiesMock(scheduler, dutiesMap) startFn() @@ -290,7 +290,7 @@ func TestScheduler_Proposer_Reorg_Current(t *testing.T) { dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.ProposerDuty]() ) currentSlot.Set(phase0.Slot(34)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, 0) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot) fetchDutiesCall, executeDutiesCall := setupProposerDutiesMock(scheduler, dutiesMap) startFn() @@ -366,7 +366,7 @@ func TestScheduler_Proposer_Reorg_Current_Indices_Changed(t *testing.T) { dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.ProposerDuty]() ) currentSlot.Set(phase0.Slot(34)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, 0) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot) fetchDutiesCall, executeDutiesCall := setupProposerDutiesMock(scheduler, dutiesMap) startFn() @@ -452,74 +452,3 @@ func TestScheduler_Proposer_Reorg_Current_Indices_Changed(t *testing.T) { cancel() require.NoError(t, schedulerPool.Wait()) } - -func TestScheduler_Proposer_On_Fork(t *testing.T) { - var ( - handler = NewProposerHandler(dutystore.NewDuties[eth2apiv1.ProposerDuty]()) - currentSlot = &SafeValue[phase0.Slot]{} - dutiesMap = hashmap.New[phase0.Epoch, []*eth2apiv1.ProposerDuty]() - alanForkEpoch = phase0.Epoch(1) - ) - currentSlot.Set(phase0.Slot(0)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, alanForkEpoch) - fetchDutiesCallGenesis, executeDutiesCallGenesis := setupProposerGenesisDutiesMock(scheduler, dutiesMap) - _, executeDutiesCall := setupProposerDutiesMock(scheduler, dutiesMap) - startFn() - - dutiesMap.Set(phase0.Epoch(0), []*eth2apiv1.ProposerDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(2), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - dutiesMap.Set(phase0.Epoch(1), []*eth2apiv1.ProposerDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - Slot: phase0.Slot(32), - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - - // STEP 1: wait for proposer genesis duties to be fetched - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCallGenesis, executeDutiesCallGenesis, timeout) - - // STEP 2: wait for no action to be taken - currentSlot.Set(phase0.Slot(1)) - ticker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, fetchDutiesCallGenesis, executeDutiesCallGenesis, timeout) - - // STEP 3: wait for proposer duties to be executed - currentSlot.Set(phase0.Slot(2)) - duties, _ := dutiesMap.Get(phase0.Epoch(0)) - expectedGenesis := expectedExecutedGenesisProposerDuties(handler, duties) - setExecuteGenesisDutyFunc(scheduler, executeDutiesCallGenesis, len(expectedGenesis)) - - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCallGenesis, executeDutiesCallGenesis, timeout, expectedGenesis) - waitForNoAction(t, logger, fetchDutiesCallGenesis, executeDutiesCall, timeout) - - // skip to the next epoch - currentSlot.Set(phase0.Slot(3)) - for slot := currentSlot.Get(); slot < 32; slot++ { - ticker.Send(slot) - waitForNoActionGenesis(t, logger, fetchDutiesCallGenesis, executeDutiesCallGenesis, timeout) - currentSlot.Set(slot + 1) - } - - // fork epoch - currentSlot.Set(phase0.Slot(32)) - duties, _ = dutiesMap.Get(phase0.Epoch(1)) - expected := expectedExecutedProposerDuties(handler, duties) - setExecuteDutyFunc(scheduler, executeDutiesCall, len(expected)) - - ticker.Send(currentSlot.Get()) - waitForDutiesFetch(t, logger, fetchDutiesCallGenesis, executeDutiesCall, timeout) - waitForDutiesExecution(t, logger, fetchDutiesCallGenesis, executeDutiesCall, timeout, expected) - waitForNoActionGenesis(t, logger, fetchDutiesCallGenesis, executeDutiesCallGenesis, timeout) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} diff --git a/operator/duties/scheduler.go b/operator/duties/scheduler.go index 25d2a43e85..1cd979dbc8 100644 --- a/operator/duties/scheduler.go +++ b/operator/duties/scheduler.go @@ -16,10 +16,9 @@ import ( "github.com/prometheus/client_golang/prometheus/promauto" "github.com/prysmaticlabs/prysm/v4/async/event" "github.com/sourcegraph/conc/pool" + spectypes "github.com/ssvlabs/ssv-spec/types" "go.uber.org/zap" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - spectypes "github.com/ssvlabs/ssv-spec/types" "github.com/ssvlabs/ssv/beacon/goclient" "github.com/ssvlabs/ssv/logging" "github.com/ssvlabs/ssv/logging/fields" @@ -54,14 +53,12 @@ const ( // DutiesExecutor is an interface for executing duties. type DutiesExecutor interface { - ExecuteGenesisDuties(logger *zap.Logger, duties []*genesisspectypes.Duty) ExecuteDuties(ctx context.Context, logger *zap.Logger, duties []*spectypes.ValidatorDuty) ExecuteCommitteeDuties(ctx context.Context, logger *zap.Logger, duties committeeDutiesMap) } // DutyExecutor is an interface for executing duty. type DutyExecutor interface { - ExecuteGenesisDuty(logger *zap.Logger, duty *genesisspectypes.Duty) ExecuteDuty(ctx context.Context, logger *zap.Logger, duty *spectypes.ValidatorDuty) ExecuteCommitteeDuty(ctx context.Context, logger *zap.Logger, committeeID spectypes.CommitteeID, duty *spectypes.CommitteeDuty) } @@ -375,24 +372,6 @@ func (s *Scheduler) HandleHeadEvent(logger *zap.Logger) func(event *eth2apiv1.Ev } } -func (s *Scheduler) ExecuteGenesisDuties(logger *zap.Logger, duties []*genesisspectypes.Duty) { - for _, duty := range duties { - duty := duty - logger := s.loggerWithGenesisDutyContext(logger, duty) - slotDelay := time.Since(s.network.Beacon.GetSlotStartTime(duty.Slot)) - if slotDelay >= 100*time.Millisecond { - logger.Debug("⚠️ late duty execution", zap.Int64("slot_delay", slotDelay.Milliseconds())) - } - slotDelayHistogram.Observe(float64(slotDelay.Milliseconds())) - go func() { - if duty.Type == genesisspectypes.BNRoleAttester || duty.Type == genesisspectypes.BNRoleSyncCommittee { - s.waitOneThirdOrValidBlock(duty.Slot) - } - s.dutyExecutor.ExecuteGenesisDuty(logger, duty) - }() - } -} - // ExecuteDuties tries to execute the given duties func (s *Scheduler) ExecuteDuties(ctx context.Context, logger *zap.Logger, duties []*spectypes.ValidatorDuty) { for _, duty := range duties { @@ -432,18 +411,6 @@ func (s *Scheduler) ExecuteCommitteeDuties(ctx context.Context, logger *zap.Logg } } -// loggerWithGenesisDutyContext returns an instance of logger with the given genesis duty's information -func (s *Scheduler) loggerWithGenesisDutyContext(logger *zap.Logger, duty *genesisspectypes.Duty) *zap.Logger { - return logger. - With(zap.Stringer(fields.FieldRole, duty.Type)). - With(zap.Uint64("committee_index", uint64(duty.CommitteeIndex))). - With(fields.CurrentSlot(s.network.Beacon.EstimatedCurrentSlot())). - With(fields.Slot(duty.Slot)). - With(fields.Epoch(s.network.Beacon.EstimatedEpochAtSlot(duty.Slot))). - With(fields.PubKey(duty.PubKey[:])). - With(fields.StartTimeUnixMilli(s.network.Beacon.GetSlotStartTime(duty.Slot))) -} - // loggerWithDutyContext returns an instance of logger with the given duty's information func (s *Scheduler) loggerWithDutyContext(logger *zap.Logger, duty *spectypes.ValidatorDuty) *zap.Logger { return logger. diff --git a/operator/duties/scheduler_mock.go b/operator/duties/scheduler_mock.go index b6671530d2..c43f216504 100644 --- a/operator/duties/scheduler_mock.go +++ b/operator/duties/scheduler_mock.go @@ -18,7 +18,6 @@ import ( v1 "github.com/attestantio/go-eth2-client/api/v1" phase0 "github.com/attestantio/go-eth2-client/spec/phase0" types "github.com/ethereum/go-ethereum/core/types" - types0 "github.com/ssvlabs/ssv-spec-pre-cc/types" types1 "github.com/ssvlabs/ssv-spec/types" types2 "github.com/ssvlabs/ssv/protocol/v2/types" gomock "go.uber.org/mock/gomock" @@ -72,18 +71,6 @@ func (mr *MockDutiesExecutorMockRecorder) ExecuteDuties(ctx, logger, duties any) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecuteDuties", reflect.TypeOf((*MockDutiesExecutor)(nil).ExecuteDuties), ctx, logger, duties) } -// ExecuteGenesisDuties mocks base method. -func (m *MockDutiesExecutor) ExecuteGenesisDuties(logger *zap.Logger, duties []*types0.Duty) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "ExecuteGenesisDuties", logger, duties) -} - -// ExecuteGenesisDuties indicates an expected call of ExecuteGenesisDuties. -func (mr *MockDutiesExecutorMockRecorder) ExecuteGenesisDuties(logger, duties any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecuteGenesisDuties", reflect.TypeOf((*MockDutiesExecutor)(nil).ExecuteGenesisDuties), logger, duties) -} - // MockDutyExecutor is a mock of DutyExecutor interface. type MockDutyExecutor struct { ctrl *gomock.Controller @@ -131,18 +118,6 @@ func (mr *MockDutyExecutorMockRecorder) ExecuteDuty(ctx, logger, duty any) *gomo return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecuteDuty", reflect.TypeOf((*MockDutyExecutor)(nil).ExecuteDuty), ctx, logger, duty) } -// ExecuteGenesisDuty mocks base method. -func (m *MockDutyExecutor) ExecuteGenesisDuty(logger *zap.Logger, duty *types0.Duty) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "ExecuteGenesisDuty", logger, duty) -} - -// ExecuteGenesisDuty indicates an expected call of ExecuteGenesisDuty. -func (mr *MockDutyExecutorMockRecorder) ExecuteGenesisDuty(logger, duty any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecuteGenesisDuty", reflect.TypeOf((*MockDutyExecutor)(nil).ExecuteGenesisDuty), logger, duty) -} - // MockBeaconNode is a mock of BeaconNode interface. type MockBeaconNode struct { ctrl *gomock.Controller diff --git a/operator/duties/scheduler_test.go b/operator/duties/scheduler_test.go index 6bea01f565..685187f2e3 100644 --- a/operator/duties/scheduler_test.go +++ b/operator/duties/scheduler_test.go @@ -9,7 +9,6 @@ import ( "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/prysmaticlabs/prysm/v4/async/event" "github.com/sourcegraph/conc/pool" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" spectypes "github.com/ssvlabs/ssv-spec/types" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" @@ -73,15 +72,7 @@ type mockSlotTickerService struct { event.Feed } -func setupSchedulerAndMocks(t *testing.T, handlers []dutyHandler, currentSlot *SafeValue[phase0.Slot], alanForkEpoch phase0.Epoch) ( - *Scheduler, - *zap.Logger, - *mockSlotTickerService, - time.Duration, - context.CancelFunc, - *pool.ContextPool, - func(), -) { +func setupSchedulerAndMocks(t *testing.T, handlers []dutyHandler, currentSlot *SafeValue[phase0.Slot]) (*Scheduler, *zap.Logger, *mockSlotTickerService, time.Duration, context.CancelFunc, *pool.ContextPool, func()) { ctrl := gomock.NewController(t) // A 200ms timeout ensures the test passes, even with mockSlotTicker overhead. timeout := 200 * time.Millisecond @@ -96,8 +87,7 @@ func setupSchedulerAndMocks(t *testing.T, handlers []dutyHandler, currentSlot *S mockDutyExecutor := NewMockDutyExecutor(ctrl) mockSlotService := &mockSlotTickerService{} mockNetworkConfig := networkconfig.NetworkConfig{ - Beacon: mocknetwork.NewMockBeaconNetwork(ctrl), - AlanForkEpoch: alanForkEpoch, + Beacon: mocknetwork.NewMockBeaconNetwork(ctrl), } opts := &SchedulerOptions{ @@ -190,31 +180,6 @@ func setExecuteDutyFunc(s *Scheduler, executeDutiesCall chan []*spectypes.Valida ).AnyTimes() } -func setExecuteGenesisDutyFunc(s *Scheduler, executeDutiesCall chan []*genesisspectypes.Duty, executeDutiesCallSize int) { - executeDutiesBuffer := make(chan *genesisspectypes.Duty, executeDutiesCallSize) - - s.dutyExecutor.(*MockDutyExecutor).EXPECT().ExecuteGenesisDuty(gomock.Any(), gomock.Any()).AnyTimes().DoAndReturn( - func(logger *zap.Logger, duty *genesisspectypes.Duty) error { - logger.Debug("🏃 Executing duty", zap.Any("duty", duty)) - executeDutiesBuffer <- duty - - // Check if all expected duties have been received - if len(executeDutiesBuffer) == executeDutiesCallSize { - // Build the array of duties - var duties []*genesisspectypes.Duty - for i := 0; i < executeDutiesCallSize; i++ { - d := <-executeDutiesBuffer - duties = append(duties, d) - } - - // Send the array of duties to executeDutiesCall - executeDutiesCall <- duties - } - return nil - }, - ).AnyTimes() -} - func setExecuteDutyFuncs(s *Scheduler, executeDutiesCall chan committeeDutiesMap, executeDutiesCallSize int) { executeDutiesBuffer := make(chan *committeeDuty, executeDutiesCallSize) @@ -260,17 +225,6 @@ func waitForDutiesFetch(t *testing.T, logger *zap.Logger, fetchDutiesCall chan s } } -func waitForGenesisDutiesFetch(t *testing.T, logger *zap.Logger, fetchDutiesCall chan struct{}, executeDutiesCall chan []*genesisspectypes.Duty, timeout time.Duration) { - select { - case <-fetchDutiesCall: - logger.Debug("duties fetched") - case <-executeDutiesCall: - require.FailNow(t, "unexpected execute duty call") - case <-time.After(timeout): - require.FailNow(t, "timed out waiting for duties to be fetched") - } -} - func waitForNoAction(t *testing.T, logger *zap.Logger, fetchDutiesCall chan struct{}, executeDutiesCall chan []*spectypes.ValidatorDuty, timeout time.Duration) { select { case <-fetchDutiesCall: @@ -282,17 +236,6 @@ func waitForNoAction(t *testing.T, logger *zap.Logger, fetchDutiesCall chan stru } } -func waitForNoActionGenesis(t *testing.T, logger *zap.Logger, fetchDutiesCall chan struct{}, executeDutiesCall chan []*genesisspectypes.Duty, timeout time.Duration) { - select { - case <-fetchDutiesCall: - require.FailNow(t, "unexpected duties call") - case <-executeDutiesCall: - require.FailNow(t, "unexpected execute duty call") - case <-time.After(timeout): - // No action as expected. - } -} - func waitForDutiesExecution(t *testing.T, logger *zap.Logger, fetchDutiesCall chan struct{}, executeDutiesCall chan []*spectypes.ValidatorDuty, timeout time.Duration, expectedDuties []*spectypes.ValidatorDuty) { select { case <-fetchDutiesCall: @@ -317,30 +260,6 @@ func waitForDutiesExecution(t *testing.T, logger *zap.Logger, fetchDutiesCall ch } } -func waitForGenesisDutiesExecution(t *testing.T, logger *zap.Logger, fetchDutiesCall chan struct{}, executeDutiesCall chan []*genesisspectypes.Duty, timeout time.Duration, expectedDuties []*genesisspectypes.Duty) { - select { - case <-fetchDutiesCall: - require.FailNow(t, "unexpected duties call") - case duties := <-executeDutiesCall: - logger.Debug("duties executed", zap.Any("duties", duties)) - logger.Debug("expected duties", zap.Any("duties", expectedDuties)) - require.Len(t, duties, len(expectedDuties)) - for _, e := range expectedDuties { - found := false - for _, d := range duties { - if e.Type == d.Type && e.PubKey == d.PubKey && e.ValidatorIndex == d.ValidatorIndex && e.Slot == d.Slot { - found = true - break - } - } - require.True(t, found) - } - - case <-time.After(timeout): - require.FailNow(t, "timed out waiting for duty to be executed") - } -} - func waitForDutiesFetchCommittee(t *testing.T, logger *zap.Logger, fetchDutiesCall chan struct{}, executeDutiesCall chan committeeDutiesMap, timeout time.Duration) { select { case <-fetchDutiesCall: diff --git a/operator/duties/sync_committee.go b/operator/duties/sync_committee.go index 4fa4edb528..75e7bace63 100644 --- a/operator/duties/sync_committee.go +++ b/operator/duties/sync_committee.go @@ -8,10 +8,9 @@ import ( eth2apiv1 "github.com/attestantio/go-eth2-client/api/v1" "github.com/attestantio/go-eth2-client/spec/phase0" + spectypes "github.com/ssvlabs/ssv-spec/types" "go.uber.org/zap" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - spectypes "github.com/ssvlabs/ssv-spec/types" "github.com/ssvlabs/ssv/logging/fields" "github.com/ssvlabs/ssv/operator/duties/dutystore" ) @@ -169,19 +168,6 @@ func (h *SyncCommitteeHandler) processExecution(ctx context.Context, period uint return } - if !h.network.PastAlanForkAtEpoch(h.network.Beacon.EstimatedEpochAtSlot(slot)) { - toExecute := make([]*genesisspectypes.Duty, 0, len(duties)*2) - for _, d := range duties { - if h.shouldExecute(d, slot) { - toExecute = append(toExecute, h.toGenesisSpecDuty(d, slot, genesisspectypes.BNRoleSyncCommittee)) - toExecute = append(toExecute, h.toGenesisSpecDuty(d, slot, genesisspectypes.BNRoleSyncCommitteeContribution)) - } - } - - h.dutiesExecutor.ExecuteGenesisDuties(h.logger, toExecute) - return - } - toExecute := make([]*spectypes.ValidatorDuty, 0, len(duties)) for _, d := range duties { if h.shouldExecute(d, slot) { @@ -267,20 +253,6 @@ func (h *SyncCommitteeHandler) prepareDutiesResultLog(period uint64, duties []*e fields.Duration(start)) } -func (h *SyncCommitteeHandler) toGenesisSpecDuty(duty *eth2apiv1.SyncCommitteeDuty, slot phase0.Slot, role genesisspectypes.BeaconRole) *genesisspectypes.Duty { - indices := make([]uint64, len(duty.ValidatorSyncCommitteeIndices)) - for i, index := range duty.ValidatorSyncCommitteeIndices { - indices[i] = uint64(index) - } - return &genesisspectypes.Duty{ - Type: role, - PubKey: duty.PubKey, - Slot: slot, // in order for the duty scheduler to execute - ValidatorIndex: duty.ValidatorIndex, - ValidatorSyncCommitteeIndices: indices, - } -} - func (h *SyncCommitteeHandler) toSpecDuty(duty *eth2apiv1.SyncCommitteeDuty, slot phase0.Slot, role spectypes.BeaconRole) *spectypes.ValidatorDuty { indices := make([]uint64, len(duty.ValidatorSyncCommitteeIndices)) for i, index := range duty.ValidatorSyncCommitteeIndices { diff --git a/operator/duties/sync_committee_genesis_test.go b/operator/duties/sync_committee_genesis_test.go deleted file mode 100644 index 0b62fd1597..0000000000 --- a/operator/duties/sync_committee_genesis_test.go +++ /dev/null @@ -1,705 +0,0 @@ -package duties - -import ( - "bytes" - "context" - "testing" - "time" - - v1 "github.com/attestantio/go-eth2-client/api/v1" - "github.com/attestantio/go-eth2-client/spec/phase0" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - "github.com/stretchr/testify/require" - "go.uber.org/mock/gomock" - - "github.com/ssvlabs/ssv/utils/hashmap" - - spectypes "github.com/ssvlabs/ssv-spec/types" - - "github.com/ssvlabs/ssv/beacon/goclient" - "github.com/ssvlabs/ssv/operator/duties/dutystore" - mocknetwork "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon/mocks" - ssvtypes "github.com/ssvlabs/ssv/protocol/v2/types" -) - -func setupSyncCommitteeGenesisDutiesMock( - s *Scheduler, - activeShares []*ssvtypes.SSVShare, - dutiesMap *hashmap.Map[uint64, []*v1.SyncCommitteeDuty], - waitForDuties *SafeValue[bool], -) (chan struct{}, chan []*genesisspectypes.Duty) { - fetchDutiesCall := make(chan struct{}) - executeDutiesCall := make(chan []*genesisspectypes.Duty) - - s.network.Beacon.(*mocknetwork.MockBeaconNetwork).EXPECT().EstimatedSyncCommitteePeriodAtEpoch(gomock.Any()).DoAndReturn( - func(epoch phase0.Epoch) uint64 { - return uint64(epoch) / s.network.Beacon.EpochsPerSyncCommitteePeriod() - }, - ).AnyTimes() - - s.network.Beacon.(*mocknetwork.MockBeaconNetwork).EXPECT().FirstEpochOfSyncPeriod(gomock.Any()).DoAndReturn( - func(period uint64) phase0.Epoch { - return phase0.Epoch(period * s.network.Beacon.EpochsPerSyncCommitteePeriod()) - }, - ).AnyTimes() - - s.network.Beacon.(*mocknetwork.MockBeaconNetwork).EXPECT().LastSlotOfSyncPeriod(gomock.Any()).DoAndReturn( - func(period uint64) phase0.Slot { - lastEpoch := s.network.Beacon.FirstEpochOfSyncPeriod(period+1) - 1 - // If we are in the sync committee that ends at slot x we do not generate a message during slot x-1 - // as it will never be included, hence -1. - return s.network.Beacon.GetEpochFirstSlot(lastEpoch+1) - 2 - }, - ).AnyTimes() - - s.network.Beacon.(*mocknetwork.MockBeaconNetwork).EXPECT().GetEpochFirstSlot(gomock.Any()).DoAndReturn( - func(epoch phase0.Epoch) phase0.Slot { - return phase0.Slot(uint64(epoch) * s.network.Beacon.SlotsPerEpoch()) - }, - ).AnyTimes() - - s.beaconNode.(*MockBeaconNode).EXPECT().SyncCommitteeDuties(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn( - func(ctx context.Context, epoch phase0.Epoch, indices []phase0.ValidatorIndex) ([]*v1.SyncCommitteeDuty, error) { - if waitForDuties.Get() { - fetchDutiesCall <- struct{}{} - } - period := s.network.Beacon.EstimatedSyncCommitteePeriodAtEpoch(epoch) - duties, _ := dutiesMap.Get(period) - return duties, nil - }).AnyTimes() - - s.validatorProvider.(*MockValidatorProvider).EXPECT().SelfParticipatingValidators(gomock.Any()).Return(activeShares).AnyTimes() - s.validatorProvider.(*MockValidatorProvider).EXPECT().ParticipatingValidators(gomock.Any()).Return(activeShares).AnyTimes() - s.validatorProvider.(*MockValidatorProvider).EXPECT().Validator(gomock.Any()).DoAndReturn( - func(pubKey []byte) (*ssvtypes.SSVShare, bool) { - var ssvShare *ssvtypes.SSVShare - var minEpoch phase0.Epoch - dutiesMap.Range(func(period uint64, duties []*v1.SyncCommitteeDuty) bool { - for _, duty := range duties { - if bytes.Equal(duty.PubKey[:], pubKey) { - ssvShare = &ssvtypes.SSVShare{ - Share: spectypes.Share{ - ValidatorIndex: duty.ValidatorIndex, - }, - } - firstEpoch := s.network.Beacon.FirstEpochOfSyncPeriod(period) - if firstEpoch < minEpoch { - minEpoch = firstEpoch - ssvShare.SetMinParticipationEpoch(firstEpoch) - } - return true - } - } - return true - }) - - if ssvShare != nil { - return ssvShare, true - } - - return nil, false - }, - ).AnyTimes() - - s.validatorController.(*MockValidatorController).EXPECT().AllActiveIndices(gomock.Any(), gomock.Any()).DoAndReturn( - func(epoch phase0.Epoch, afterInit bool) []phase0.ValidatorIndex { - return indicesFromShares(activeShares) - }).AnyTimes() - - s.beaconNode.(*MockBeaconNode).EXPECT().SubmitSyncCommitteeSubscriptions(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() - - return fetchDutiesCall, executeDutiesCall -} - -func expectedExecutedGenesisSyncCommitteeDuties(handler *SyncCommitteeHandler, duties []*v1.SyncCommitteeDuty, slot phase0.Slot) []*genesisspectypes.Duty { - expectedDuties := make([]*genesisspectypes.Duty, 0) - for _, d := range duties { - expectedDuties = append(expectedDuties, handler.toGenesisSpecDuty(d, slot, genesisspectypes.BNRoleSyncCommittee)) - expectedDuties = append(expectedDuties, handler.toGenesisSpecDuty(d, slot, genesisspectypes.BNRoleSyncCommitteeContribution)) - } - return expectedDuties -} - -func TestScheduler_SyncCommittee_Genesis_Same_Period(t *testing.T) { - var ( - handler = NewSyncCommitteeHandler(dutystore.NewSyncCommitteeDuties()) - currentSlot = &SafeValue[phase0.Slot]{} - waitForDuties = &SafeValue[bool]{} - forkEpoch = goclient.FarFutureEpoch - dutiesMap = hashmap.New[uint64, []*v1.SyncCommitteeDuty]() - activeShares = []*ssvtypes.SSVShare{{ - Share: spectypes.Share{ - Committee: []*spectypes.ShareMember{ - {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, - }, - ValidatorIndex: 1, - }, - }} - ) - dutiesMap.Set(0, []*v1.SyncCommitteeDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - - // STEP 1: wait for sync committee duties to be fetched (handle initial duties) - currentSlot.Set(phase0.Slot(1)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupSyncCommitteeGenesisDutiesMock(scheduler, activeShares, dutiesMap, waitForDuties) - startFn() - - // STEP 1: wait for sync committee duties to be fetched and executed at the same slot - duties, _ := dutiesMap.Get(0) - expected := expectedExecutedGenesisSyncCommitteeDuties(handler, duties, currentSlot.Get()) - setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // STEP 2: expect sync committee duties to be executed at the same period - currentSlot.Set(phase0.Slot(2)) - duties, _ = dutiesMap.Get(0) - expected = expectedExecutedGenesisSyncCommitteeDuties(handler, duties, currentSlot.Get()) - setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // STEP 3: expect sync committee duties to be executed at the last slot of the period - currentSlot.Set(scheduler.network.Beacon.LastSlotOfSyncPeriod(0)) - duties, _ = dutiesMap.Get(0) - expected = expectedExecutedGenesisSyncCommitteeDuties(handler, duties, currentSlot.Get()) - setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // STEP 4: expect no action to be taken as we are in the next period - firstSlotOfNextPeriod := scheduler.network.Beacon.GetEpochFirstSlot(scheduler.network.Beacon.FirstEpochOfSyncPeriod(1)) - currentSlot.Set(firstSlotOfNextPeriod) - ticker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} - -func TestScheduler_SyncCommittee_Genesis_Current_Next_Periods(t *testing.T) { - var ( - handler = NewSyncCommitteeHandler(dutystore.NewSyncCommitteeDuties()) - currentSlot = &SafeValue[phase0.Slot]{} - waitForDuties = &SafeValue[bool]{} - forkEpoch = goclient.FarFutureEpoch - dutiesMap = hashmap.New[uint64, []*v1.SyncCommitteeDuty]() - activeShares = []*ssvtypes.SSVShare{ - { - Share: spectypes.Share{ - Committee: []*spectypes.ShareMember{ - {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, - }, - ValidatorIndex: 1, - }, - }, - { - Share: spectypes.Share{ - Committee: []*spectypes.ShareMember{ - {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, - }, - ValidatorIndex: 2, - }, - }, - } - ) - dutiesMap.Set(0, []*v1.SyncCommitteeDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - dutiesMap.Set(1, []*v1.SyncCommitteeDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 4}, - ValidatorIndex: phase0.ValidatorIndex(2), - }, - }) - - // STEP 1: wait for sync committee duties to be fetched (handle initial duties) - currentSlot.Set(phase0.Slot(256*32 - 49)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupSyncCommitteeGenesisDutiesMock(scheduler, activeShares, dutiesMap, waitForDuties) - startFn() - - duties, _ := dutiesMap.Get(0) - expected := expectedExecutedGenesisSyncCommitteeDuties(handler, duties, currentSlot.Get()) - setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // STEP 2: wait for sync committee duties to be executed - currentSlot.Set(phase0.Slot(256*32 - 48)) - duties, _ = dutiesMap.Get(0) - expected = expectedExecutedGenesisSyncCommitteeDuties(handler, duties, currentSlot.Get()) - setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // STEP 3: wait for sync committee duties to be executed - currentSlot.Set(phase0.Slot(256*32 - 47)) - duties, _ = dutiesMap.Get(0) - expected = expectedExecutedGenesisSyncCommitteeDuties(handler, duties, currentSlot.Get()) - setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // ... - - // STEP 4: new period, wait for sync committee duties to be executed - currentSlot.Set(phase0.Slot(256 * 32)) - duties, _ = dutiesMap.Get(1) - expected = expectedExecutedGenesisSyncCommitteeDuties(handler, duties, currentSlot.Get()) - setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} - -func TestScheduler_SyncCommittee_Genesis_Indices_Changed(t *testing.T) { - var ( - handler = NewSyncCommitteeHandler(dutystore.NewSyncCommitteeDuties()) - currentSlot = &SafeValue[phase0.Slot]{} - waitForDuties = &SafeValue[bool]{} - forkEpoch = goclient.FarFutureEpoch - dutiesMap = hashmap.New[uint64, []*v1.SyncCommitteeDuty]() - activeShares = []*ssvtypes.SSVShare{ - { - Share: spectypes.Share{ - Committee: []*spectypes.ShareMember{ - {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, - }, - ValidatorIndex: 1, - }, - }, - { - Share: spectypes.Share{ - Committee: []*spectypes.ShareMember{ - {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, - }, - ValidatorIndex: 2, - }, - }, - } - ) - currentSlot.Set(phase0.Slot(256*32 - 3)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupSyncCommitteeGenesisDutiesMock(scheduler, activeShares, dutiesMap, waitForDuties) - startFn() - - dutiesMap.Set(1, []*v1.SyncCommitteeDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - - // STEP 1: wait for sync committee duties to be fetched for next period - waitForDuties.Set(true) - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 2: trigger a change in active indices - scheduler.indicesChg <- struct{}{} - duties, _ := dutiesMap.Get(1) - dutiesMap.Set(1, append(duties, &v1.SyncCommitteeDuty{ - PubKey: phase0.BLSPubKey{1, 2, 4}, - ValidatorIndex: phase0.ValidatorIndex(2), - })) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 3: wait for sync committee duties to be fetched again - currentSlot.Set(phase0.Slot(256*32 - 2)) - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 4: no action should be taken - currentSlot.Set(phase0.Slot(256*32 - 1)) - ticker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 5: execute duties - currentSlot.Set(phase0.Slot(256 * 32)) - duties, _ = dutiesMap.Get(1) - expected := expectedExecutedGenesisSyncCommitteeDuties(handler, duties, currentSlot.Get()) - setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} - -func TestScheduler_SyncCommittee_Genesis_Multiple_Indices_Changed_Same_Slot(t *testing.T) { - var ( - handler = NewSyncCommitteeHandler(dutystore.NewSyncCommitteeDuties()) - currentSlot = &SafeValue[phase0.Slot]{} - waitForDuties = &SafeValue[bool]{} - forkEpoch = goclient.FarFutureEpoch - dutiesMap = hashmap.New[uint64, []*v1.SyncCommitteeDuty]() - activeShares = []*ssvtypes.SSVShare{ - { - Share: spectypes.Share{ - Committee: []*spectypes.ShareMember{ - {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, - }, - ValidatorIndex: 1, - }, - }, - { - Share: spectypes.Share{ - Committee: []*spectypes.ShareMember{ - {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, - }, - ValidatorIndex: 2, - }, - }, - } - ) - currentSlot.Set(phase0.Slot(256*32 - 3)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupSyncCommitteeGenesisDutiesMock(scheduler, activeShares, dutiesMap, waitForDuties) - startFn() - - // STEP 1: wait for no action to be taken - ticker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 2: trigger a change in active indices - scheduler.indicesChg <- struct{}{} - dutiesMap.Set(1, []*v1.SyncCommitteeDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 3: trigger a change in active indices - scheduler.indicesChg <- struct{}{} - duties, _ := dutiesMap.Get(1) - dutiesMap.Set(1, append(duties, &v1.SyncCommitteeDuty{ - PubKey: phase0.BLSPubKey{1, 2, 4}, - ValidatorIndex: phase0.ValidatorIndex(2), - })) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 4: wait for sync committee duties to be fetched again - currentSlot.Set(phase0.Slot(256*32 - 2)) - waitForDuties.Set(true) - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 5: no action should be taken - currentSlot.Set(phase0.Slot(256*32 - 1)) - ticker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 6: The first assigned duty should not be executed, but the second one should - currentSlot.Set(phase0.Slot(256 * 32)) - duties, _ = dutiesMap.Get(1) - expected := expectedExecutedGenesisSyncCommitteeDuties(handler, duties, currentSlot.Get()) - setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} - -// reorg current dependent root changed -func TestScheduler_SyncCommittee_Genesis_Reorg_Current(t *testing.T) { - var ( - handler = NewSyncCommitteeHandler(dutystore.NewSyncCommitteeDuties()) - currentSlot = &SafeValue[phase0.Slot]{} - waitForDuties = &SafeValue[bool]{} - forkEpoch = goclient.FarFutureEpoch - dutiesMap = hashmap.New[uint64, []*v1.SyncCommitteeDuty]() - activeShares = []*ssvtypes.SSVShare{ - { - Share: spectypes.Share{ - Committee: []*spectypes.ShareMember{ - {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, - }, - ValidatorIndex: 1, - }, - }, - { - Share: spectypes.Share{ - Committee: []*spectypes.ShareMember{ - {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, - }, - ValidatorIndex: 2, - }, - }, - } - ) - currentSlot.Set(phase0.Slot(256*32 - 3)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupSyncCommitteeGenesisDutiesMock(scheduler, activeShares, dutiesMap, waitForDuties) - startFn() - - dutiesMap.Set(1, []*v1.SyncCommitteeDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - - // STEP 1: wait for sync committee duties to be fetched and executed at the same slot - waitForDuties.Set(true) - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 2: trigger head event - e := &v1.Event{ - Data: &v1.HeadEvent{ - Slot: currentSlot.Get(), - CurrentDutyDependentRoot: phase0.Root{0x01}, - }, - } - scheduler.HandleHeadEvent(logger)(e) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 3: Ticker with no action - currentSlot.Set(phase0.Slot(256*32 - 2)) - ticker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 4: trigger reorg - e = &v1.Event{ - Data: &v1.HeadEvent{ - Slot: currentSlot.Get(), - CurrentDutyDependentRoot: phase0.Root{0x02}, - }, - } - dutiesMap.Set(1, []*v1.SyncCommitteeDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 4}, - ValidatorIndex: phase0.ValidatorIndex(2), - }, - }) - scheduler.HandleHeadEvent(logger)(e) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 5: wait for sync committee duties to be fetched again for the current epoch - currentSlot.Set(phase0.Slot(256*32 - 1)) - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 6: The first assigned duty should not be executed, but the second one should - currentSlot.Set(phase0.Slot(256 * 32)) - duties, _ := dutiesMap.Get(1) - expected := expectedExecutedGenesisSyncCommitteeDuties(handler, duties, currentSlot.Get()) - setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} - -// reorg current dependent root changed including indices change in the same slot -func TestScheduler_SyncCommittee_Genesis_Reorg_Current_Indices_Changed(t *testing.T) { - var ( - handler = NewSyncCommitteeHandler(dutystore.NewSyncCommitteeDuties()) - currentSlot = &SafeValue[phase0.Slot]{} - waitForDuties = &SafeValue[bool]{} - forkEpoch = goclient.FarFutureEpoch - dutiesMap = hashmap.New[uint64, []*v1.SyncCommitteeDuty]() - activeShares = []*ssvtypes.SSVShare{ - { - Share: spectypes.Share{ - Committee: []*spectypes.ShareMember{ - {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, - }, - ValidatorIndex: 1, - }, - }, - { - Share: spectypes.Share{ - Committee: []*spectypes.ShareMember{ - {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, - }, - ValidatorIndex: 2, - }, - }, - { - Share: spectypes.Share{ - Committee: []*spectypes.ShareMember{ - {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, - }, - ValidatorIndex: 3, - }, - }, - } - ) - currentSlot.Set(phase0.Slot(256*32 - 3)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupSyncCommitteeGenesisDutiesMock(scheduler, activeShares, dutiesMap, waitForDuties) - startFn() - - dutiesMap.Set(1, []*v1.SyncCommitteeDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - - // STEP 1: wait for sync committee duties to be fetched and executed at the same slot - waitForDuties.Set(true) - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 2: trigger head event - e := &v1.Event{ - Data: &v1.HeadEvent{ - Slot: currentSlot.Get(), - CurrentDutyDependentRoot: phase0.Root{0x01}, - }, - } - scheduler.HandleHeadEvent(logger)(e) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 3: Ticker with no action - currentSlot.Set(phase0.Slot(256*32 - 2)) - ticker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 4: trigger reorg - e = &v1.Event{ - Data: &v1.HeadEvent{ - Slot: currentSlot.Get(), - CurrentDutyDependentRoot: phase0.Root{0x02}, - }, - } - dutiesMap.Set(1, []*v1.SyncCommitteeDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 4}, - ValidatorIndex: phase0.ValidatorIndex(2), - }, - }) - scheduler.HandleHeadEvent(logger)(e) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 3: trigger a change in active indices - scheduler.indicesChg <- struct{}{} - duties, _ := dutiesMap.Get(1) - dutiesMap.Set(1, append(duties, &v1.SyncCommitteeDuty{ - PubKey: phase0.BLSPubKey{1, 2, 5}, - ValidatorIndex: phase0.ValidatorIndex(3), - })) - waitForNoActionGenesis(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 5: wait for sync committee duties to be fetched again for the current epoch - currentSlot.Set(phase0.Slot(256*32 - 1)) - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - waitForGenesisDutiesFetch(t, logger, fetchDutiesCall, executeDutiesCall, timeout) - - // STEP 6: The first assigned duty should not be executed, but the second and the new from indices change should - currentSlot.Set(phase0.Slot(256 * 32)) - duties, _ = dutiesMap.Get(1) - expected := expectedExecutedGenesisSyncCommitteeDuties(handler, duties, currentSlot.Get()) - setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} - -func TestScheduler_SyncCommittee_Genesis_Early_Block(t *testing.T) { - var ( - handler = NewSyncCommitteeHandler(dutystore.NewSyncCommitteeDuties()) - currentSlot = &SafeValue[phase0.Slot]{} - waitForDuties = &SafeValue[bool]{} - forkEpoch = goclient.FarFutureEpoch - dutiesMap = hashmap.New[uint64, []*v1.SyncCommitteeDuty]() - activeShares = []*ssvtypes.SSVShare{{ - Share: spectypes.Share{ - Committee: []*spectypes.ShareMember{ - {Signer: 1}, {Signer: 2}, {Signer: 3}, {Signer: 4}, - }, - ValidatorIndex: 1, - }, - }} - ) - dutiesMap.Set(0, []*v1.SyncCommitteeDuty{ - { - PubKey: phase0.BLSPubKey{1, 2, 3}, - ValidatorIndex: phase0.ValidatorIndex(1), - }, - }) - - currentSlot.Set(phase0.Slot(0)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) - fetchDutiesCall, executeDutiesCall := setupSyncCommitteeGenesisDutiesMock(scheduler, activeShares, dutiesMap, waitForDuties) - startFn() - - duties, _ := dutiesMap.Get(0) - expected := expectedExecutedGenesisSyncCommitteeDuties(handler, duties, currentSlot.Get()) - setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - - // STEP 1: wait for sync committee duties to be fetched and executed at the same slot - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // STEP 2: expect sync committee duties to be executed at the same period - currentSlot.Set(phase0.Slot(1)) - duties, _ = dutiesMap.Get(0) - expected = expectedExecutedGenesisSyncCommitteeDuties(handler, duties, currentSlot.Get()) - setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - - // STEP 3: wait for sync committee duties to be executed faster than 1/3 of the slot duration - startTime := time.Now() - currentSlot.Set(phase0.Slot(2)) - duties, _ = dutiesMap.Get(0) - expected = expectedExecutedGenesisSyncCommitteeDuties(handler, duties, currentSlot.Get()) - setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, len(expected)) - - ticker.Send(currentSlot.Get()) - - // STEP 4: trigger head event (block arrival) - e := &v1.Event{ - Data: &v1.HeadEvent{ - Slot: currentSlot.Get(), - }, - } - scheduler.HandleHeadEvent(logger)(e) - waitForGenesisDutiesExecution(t, logger, fetchDutiesCall, executeDutiesCall, timeout, expected) - require.Less(t, time.Since(startTime), scheduler.network.Beacon.SlotDurationSec()/3) - - // Stop scheduler & wait for graceful exit. - cancel() - require.NoError(t, schedulerPool.Wait()) -} diff --git a/operator/duties/sync_committee_test.go b/operator/duties/sync_committee_test.go index 99529380c4..230cf4ded4 100644 --- a/operator/duties/sync_committee_test.go +++ b/operator/duties/sync_committee_test.go @@ -112,9 +112,6 @@ func setupSyncCommitteeDutiesMock( func expectedExecutedSyncCommitteeDuties(handler *SyncCommitteeHandler, duties []*v1.SyncCommitteeDuty, slot phase0.Slot) []*spectypes.ValidatorDuty { expectedDuties := make([]*spectypes.ValidatorDuty, 0) for _, d := range duties { - if !handler.network.PastAlanForkAtEpoch(handler.network.Beacon.EstimatedEpochAtSlot(slot)) { - expectedDuties = append(expectedDuties, handler.toSpecDuty(d, slot, spectypes.BNRoleSyncCommittee)) - } expectedDuties = append(expectedDuties, handler.toSpecDuty(d, slot, spectypes.BNRoleSyncCommitteeContribution)) } return expectedDuties @@ -125,7 +122,6 @@ func TestScheduler_SyncCommittee_Same_Period(t *testing.T) { handler = NewSyncCommitteeHandler(dutystore.NewSyncCommitteeDuties()) currentSlot = &SafeValue[phase0.Slot]{} waitForDuties = &SafeValue[bool]{} - forkEpoch = phase0.Epoch(0) dutiesMap = hashmap.New[uint64, []*v1.SyncCommitteeDuty]() activeShares = []*ssvtypes.SSVShare{{ Share: spectypes.Share{ @@ -145,7 +141,7 @@ func TestScheduler_SyncCommittee_Same_Period(t *testing.T) { // STEP 1: wait for sync committee duties to be fetched (handle initial duties) currentSlot.Set(phase0.Slot(1)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot) fetchDutiesCall, executeDutiesCall := setupSyncCommitteeDutiesMock(scheduler, activeShares, dutiesMap, waitForDuties) startFn() @@ -191,7 +187,6 @@ func TestScheduler_SyncCommittee_Current_Next_Periods(t *testing.T) { handler = NewSyncCommitteeHandler(dutystore.NewSyncCommitteeDuties()) currentSlot = &SafeValue[phase0.Slot]{} waitForDuties = &SafeValue[bool]{} - forkEpoch = phase0.Epoch(0) dutiesMap = hashmap.New[uint64, []*v1.SyncCommitteeDuty]() activeShares = []*ssvtypes.SSVShare{ { @@ -227,7 +222,7 @@ func TestScheduler_SyncCommittee_Current_Next_Periods(t *testing.T) { // STEP 1: wait for sync committee duties to be fetched (handle initial duties) currentSlot.Set(phase0.Slot(256*32 - 49)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot) fetchDutiesCall, executeDutiesCall := setupSyncCommitteeDutiesMock(scheduler, activeShares, dutiesMap, waitForDuties) startFn() @@ -277,7 +272,6 @@ func TestScheduler_SyncCommittee_Indices_Changed(t *testing.T) { handler = NewSyncCommitteeHandler(dutystore.NewSyncCommitteeDuties()) currentSlot = &SafeValue[phase0.Slot]{} waitForDuties = &SafeValue[bool]{} - forkEpoch = phase0.Epoch(0) dutiesMap = hashmap.New[uint64, []*v1.SyncCommitteeDuty]() activeShares = []*ssvtypes.SSVShare{ { @@ -299,7 +293,7 @@ func TestScheduler_SyncCommittee_Indices_Changed(t *testing.T) { } ) currentSlot.Set(phase0.Slot(256*32 - 3)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot) fetchDutiesCall, executeDutiesCall := setupSyncCommitteeDutiesMock(scheduler, activeShares, dutiesMap, waitForDuties) startFn() @@ -354,7 +348,6 @@ func TestScheduler_SyncCommittee_Multiple_Indices_Changed_Same_Slot(t *testing.T handler = NewSyncCommitteeHandler(dutystore.NewSyncCommitteeDuties()) currentSlot = &SafeValue[phase0.Slot]{} waitForDuties = &SafeValue[bool]{} - forkEpoch = phase0.Epoch(0) dutiesMap = hashmap.New[uint64, []*v1.SyncCommitteeDuty]() activeShares = []*ssvtypes.SSVShare{ { @@ -376,7 +369,7 @@ func TestScheduler_SyncCommittee_Multiple_Indices_Changed_Same_Slot(t *testing.T } ) currentSlot.Set(phase0.Slot(256*32 - 3)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot) fetchDutiesCall, executeDutiesCall := setupSyncCommitteeDutiesMock(scheduler, activeShares, dutiesMap, waitForDuties) startFn() @@ -435,7 +428,6 @@ func TestScheduler_SyncCommittee_Reorg_Current(t *testing.T) { handler = NewSyncCommitteeHandler(dutystore.NewSyncCommitteeDuties()) currentSlot = &SafeValue[phase0.Slot]{} waitForDuties = &SafeValue[bool]{} - forkEpoch = phase0.Epoch(0) dutiesMap = hashmap.New[uint64, []*v1.SyncCommitteeDuty]() activeShares = []*ssvtypes.SSVShare{ { @@ -457,7 +449,7 @@ func TestScheduler_SyncCommittee_Reorg_Current(t *testing.T) { } ) currentSlot.Set(phase0.Slot(256*32 - 3)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot) fetchDutiesCall, executeDutiesCall := setupSyncCommitteeDutiesMock(scheduler, activeShares, dutiesMap, waitForDuties) startFn() @@ -529,7 +521,6 @@ func TestScheduler_SyncCommittee_Reorg_Current_Indices_Changed(t *testing.T) { handler = NewSyncCommitteeHandler(dutystore.NewSyncCommitteeDuties()) currentSlot = &SafeValue[phase0.Slot]{} waitForDuties = &SafeValue[bool]{} - forkEpoch = phase0.Epoch(0) dutiesMap = hashmap.New[uint64, []*v1.SyncCommitteeDuty]() activeShares = []*ssvtypes.SSVShare{ { @@ -559,7 +550,7 @@ func TestScheduler_SyncCommittee_Reorg_Current_Indices_Changed(t *testing.T) { } ) currentSlot.Set(phase0.Slot(256*32 - 3)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot) fetchDutiesCall, executeDutiesCall := setupSyncCommitteeDutiesMock(scheduler, activeShares, dutiesMap, waitForDuties) startFn() @@ -640,7 +631,6 @@ func TestScheduler_SyncCommittee_Early_Block(t *testing.T) { handler = NewSyncCommitteeHandler(dutystore.NewSyncCommitteeDuties()) currentSlot = &SafeValue[phase0.Slot]{} waitForDuties = &SafeValue[bool]{} - forkEpoch = phase0.Epoch(0) dutiesMap = hashmap.New[uint64, []*v1.SyncCommitteeDuty]() activeShares = []*ssvtypes.SSVShare{{ Share: spectypes.Share{ @@ -659,7 +649,7 @@ func TestScheduler_SyncCommittee_Early_Block(t *testing.T) { }) currentSlot.Set(phase0.Slot(0)) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, forkEpoch) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot) fetchDutiesCall, executeDutiesCall := setupSyncCommitteeDutiesMock(scheduler, activeShares, dutiesMap, waitForDuties) startFn() diff --git a/operator/duties/validatorregistration.go b/operator/duties/validatorregistration.go index 9c0ffc826c..ac8f3e5131 100644 --- a/operator/duties/validatorregistration.go +++ b/operator/duties/validatorregistration.go @@ -5,10 +5,8 @@ import ( "encoding/hex" "github.com/attestantio/go-eth2-client/spec/phase0" - "go.uber.org/zap" - - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" spectypes "github.com/ssvlabs/ssv-spec/types" + "go.uber.org/zap" ) const validatorRegistrationEpochInterval = uint64(10) @@ -57,23 +55,13 @@ func (h *ValidatorRegistrationHandler) HandleDuties(ctx context.Context) { pk := phase0.BLSPubKey{} copy(pk[:], share.ValidatorPubKey[:]) - if !h.network.PastAlanForkAtEpoch(epoch) { - h.dutiesExecutor.ExecuteGenesisDuties(h.logger, []*genesisspectypes.Duty{{ - Type: genesisspectypes.BNRoleValidatorRegistration, - ValidatorIndex: share.ValidatorIndex, - PubKey: pk, - Slot: slot, - // no need for other params - }}) - } else { - h.dutiesExecutor.ExecuteDuties(ctx, h.logger, []*spectypes.ValidatorDuty{{ - Type: spectypes.BNRoleValidatorRegistration, - ValidatorIndex: share.ValidatorIndex, - PubKey: pk, - Slot: slot, - // no need for other params - }}) - } + h.dutiesExecutor.ExecuteDuties(ctx, h.logger, []*spectypes.ValidatorDuty{{ + Type: spectypes.BNRoleValidatorRegistration, + ValidatorIndex: share.ValidatorIndex, + PubKey: pk, + Slot: slot, + // no need for other params + }}) vrs = append(vrs, ValidatorRegistration{ ValidatorIndex: share.BeaconMetadata.Index, diff --git a/operator/duties/voluntary_exit.go b/operator/duties/voluntary_exit.go index cee619ccce..470e5718bc 100644 --- a/operator/duties/voluntary_exit.go +++ b/operator/duties/voluntary_exit.go @@ -5,10 +5,9 @@ import ( "math/big" "github.com/attestantio/go-eth2-client/spec/phase0" + spectypes "github.com/ssvlabs/ssv-spec/types" "go.uber.org/zap" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - spectypes "github.com/ssvlabs/ssv-spec/types" "github.com/ssvlabs/ssv/logging/fields" "github.com/ssvlabs/ssv/operator/duties/dutystore" ) @@ -117,21 +116,6 @@ func (h *VoluntaryExitHandler) processExecution(ctx context.Context, slot phase0 h.dutyQueue = pendingDuties h.duties.RemoveSlot(slot - phase0.Slot(h.network.SlotsPerEpoch())) - if !h.network.PastAlanForkAtEpoch(h.network.Beacon.EstimatedEpochAtSlot(slot)) { - toExecute := make([]*genesisspectypes.Duty, 0, len(dutiesForExecution)) - for _, d := range dutiesForExecution { - toExecute = append(toExecute, h.toGenesisSpecDuty(d, genesisspectypes.BNRoleVoluntaryExit)) - } - - if dutyCount := len(dutiesForExecution); dutyCount != 0 { - h.dutiesExecutor.ExecuteGenesisDuties(h.logger, toExecute) - h.logger.Debug("executed voluntary exit duties", - fields.Slot(slot), - fields.Count(dutyCount)) - } - return - } - if dutyCount := len(dutiesForExecution); dutyCount != 0 { h.dutiesExecutor.ExecuteDuties(ctx, h.logger, dutiesForExecution) h.logger.Debug("executed voluntary exit duties", @@ -140,15 +124,6 @@ func (h *VoluntaryExitHandler) processExecution(ctx context.Context, slot phase0 } } -func (h *VoluntaryExitHandler) toGenesisSpecDuty(duty *spectypes.ValidatorDuty, role genesisspectypes.BeaconRole) *genesisspectypes.Duty { - return &genesisspectypes.Duty{ - Type: role, - PubKey: duty.PubKey, - Slot: duty.Slot, - ValidatorIndex: duty.ValidatorIndex, - } -} - // blockSlot gets slots happened at the same time as block, // it prevents calling execution client multiple times if there are several validator exit events on the same block func (h *VoluntaryExitHandler) blockSlot(ctx context.Context, blockNumber uint64) (phase0.Slot, error) { diff --git a/operator/duties/voluntary_exit_genesis_test.go b/operator/duties/voluntary_exit_genesis_test.go deleted file mode 100644 index e8719d8922..0000000000 --- a/operator/duties/voluntary_exit_genesis_test.go +++ /dev/null @@ -1,148 +0,0 @@ -package duties - -import ( - "testing" - - "github.com/attestantio/go-eth2-client/spec/phase0" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - "github.com/stretchr/testify/require" - - "github.com/ssvlabs/ssv/beacon/goclient" - "github.com/ssvlabs/ssv/operator/duties/dutystore" -) - -func TestVoluntaryExitHandler_HandleGenesisDuties(t *testing.T) { - exitCh := make(chan ExitDescriptor) - handler := NewVoluntaryExitHandler(dutystore.NewVoluntaryExit(), exitCh) - - currentSlot := &SafeValue[phase0.Slot]{} - currentSlot.Set(0) - - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, goclient.FarFutureEpoch) - startFn() - - blockByNumberCalls := create1to1BlockSlotMapping(scheduler) - assert1to1BlockSlotMapping(t, scheduler) - require.EqualValues(t, 1, blockByNumberCalls.Load()) - - executeDutiesCall := make(chan []*genesisspectypes.Duty) - setExecuteGenesisDutyFunc(scheduler, executeDutiesCall, 1) - - const blockNumber = uint64(1) - - normalExit := ExitDescriptor{ - OwnValidator: true, - PubKey: phase0.BLSPubKey{1, 2, 3}, - ValidatorIndex: phase0.ValidatorIndex(1), - BlockNumber: blockNumber, - } - sameBlockExit := ExitDescriptor{ - OwnValidator: true, - PubKey: phase0.BLSPubKey{4, 5, 6}, - ValidatorIndex: phase0.ValidatorIndex(2), - BlockNumber: normalExit.BlockNumber, - } - newBlockExit := ExitDescriptor{ - OwnValidator: true, - PubKey: phase0.BLSPubKey{1, 2, 3}, - ValidatorIndex: phase0.ValidatorIndex(1), - BlockNumber: normalExit.BlockNumber + 1, - } - pastBlockExit := ExitDescriptor{ - OwnValidator: true, - PubKey: phase0.BLSPubKey{1, 2, 3}, - ValidatorIndex: phase0.ValidatorIndex(1), - BlockNumber: normalExit.BlockNumber + 4, - } - - allDescriptors := []ExitDescriptor{ - normalExit, - sameBlockExit, - newBlockExit, - pastBlockExit, - } - - expectedDuties := expectedExecutedVoluntaryExitGenesisDuties(allDescriptors) - - require.EqualValues(t, 1, blockByNumberCalls.Load()) - exitCh <- normalExit - - t.Run("slot = 0, block = 1 - no execution", func(t *testing.T) { - currentSlot.Set(0) - ticker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, nil, executeDutiesCall, timeout) - require.EqualValues(t, 2, blockByNumberCalls.Load()) - }) - - t.Run("slot = 1, block = 1 - no execution", func(t *testing.T) { - currentSlot.Set(phase0.Slot(normalExit.BlockNumber)) - ticker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, nil, executeDutiesCall, timeout) - require.EqualValues(t, 2, blockByNumberCalls.Load()) - }) - - t.Run("slot = 4, block = 1 - no execution", func(t *testing.T) { - currentSlot.Set(phase0.Slot(normalExit.BlockNumber) + voluntaryExitSlotsToPostpone - 1) - ticker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, nil, executeDutiesCall, timeout) - require.EqualValues(t, 2, blockByNumberCalls.Load()) - }) - - t.Run("slot = 5, block = 1 - executing duty, fetching block number", func(t *testing.T) { - currentSlot.Set(phase0.Slot(normalExit.BlockNumber) + voluntaryExitSlotsToPostpone) - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, nil, executeDutiesCall, timeout, expectedDuties[:1]) - require.EqualValues(t, 2, blockByNumberCalls.Load()) - }) - - exitCh <- sameBlockExit - - t.Run("slot = 5, block = 1 - executing another duty, no block number fetch", func(t *testing.T) { - currentSlot.Set(phase0.Slot(sameBlockExit.BlockNumber) + voluntaryExitSlotsToPostpone) - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, nil, executeDutiesCall, timeout, expectedDuties[1:2]) - require.EqualValues(t, 2, blockByNumberCalls.Load()) - }) - - exitCh <- newBlockExit - - t.Run("slot = 5, block = 2 - no execution", func(t *testing.T) { - currentSlot.Set(phase0.Slot(normalExit.BlockNumber) + voluntaryExitSlotsToPostpone) - ticker.Send(currentSlot.Get()) - waitForNoActionGenesis(t, logger, nil, executeDutiesCall, timeout) - require.EqualValues(t, 3, blockByNumberCalls.Load()) - }) - - t.Run("slot = 6, block = 1 - executing new duty, fetching block number", func(t *testing.T) { - currentSlot.Set(phase0.Slot(newBlockExit.BlockNumber) + voluntaryExitSlotsToPostpone) - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, nil, executeDutiesCall, timeout, expectedDuties[2:3]) - require.EqualValues(t, 3, blockByNumberCalls.Load()) - }) - - exitCh <- pastBlockExit - - t.Run("slot = 10, block = 5 - executing past duty, fetching block number", func(t *testing.T) { - currentSlot.Set(phase0.Slot(pastBlockExit.BlockNumber) + voluntaryExitSlotsToPostpone + 1) - ticker.Send(currentSlot.Get()) - waitForGenesisDutiesExecution(t, logger, nil, executeDutiesCall, timeout, expectedDuties[3:4]) - require.EqualValues(t, 4, blockByNumberCalls.Load()) - }) - - cancel() - close(exitCh) - require.NoError(t, schedulerPool.Wait()) -} - -func expectedExecutedVoluntaryExitGenesisDuties(descriptors []ExitDescriptor) []*genesisspectypes.Duty { - expectedDuties := make([]*genesisspectypes.Duty, 0) - for _, d := range descriptors { - expectedDuties = append(expectedDuties, &genesisspectypes.Duty{ - Type: genesisspectypes.BNRoleVoluntaryExit, - PubKey: d.PubKey, - Slot: phase0.Slot(d.BlockNumber) + voluntaryExitSlotsToPostpone, - ValidatorIndex: d.ValidatorIndex, - }) - } - return expectedDuties -} diff --git a/operator/duties/voluntary_exit_test.go b/operator/duties/voluntary_exit_test.go index 1bd0460d2d..bfab7212c2 100644 --- a/operator/duties/voluntary_exit_test.go +++ b/operator/duties/voluntary_exit_test.go @@ -25,7 +25,7 @@ func TestVoluntaryExitHandler_HandleDuties(t *testing.T) { currentSlot := &SafeValue[phase0.Slot]{} currentSlot.Set(0) - scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot, 0) + scheduler, logger, ticker, timeout, cancel, schedulerPool, startFn := setupSchedulerAndMocks(t, []dutyHandler{handler}, currentSlot) startFn() blockByNumberCalls := create1to1BlockSlotMapping(scheduler) diff --git a/operator/node.go b/operator/node.go index 70823a0bbd..9b3aed88ed 100644 --- a/operator/node.go +++ b/operator/node.go @@ -149,7 +149,6 @@ func (n *operatorNode) Start(logger *zap.Logger) error { } go n.net.UpdateSubnets(logger) go n.net.UpdateScoreParams(logger) - n.validatorsCtrl.ForkListener(logger) n.validatorsCtrl.StartValidators() go n.reportOperators(logger) @@ -180,7 +179,7 @@ func (n *operatorNode) handleQueryRequests(logger *zap.Logger, nm *api.NetworkMe zap.String("type", string(nm.Msg.Type))) switch nm.Msg.Type { case api.TypeDecided: - api.HandleParticipantsQuery(logger, n.qbftStorage, nm, n.network.DomainType()) + api.HandleParticipantsQuery(logger, n.qbftStorage, nm, n.network.DomainType) case api.TypeError: api.HandleErrorQuery(logger, nm) default: diff --git a/operator/validator/mocks/controller.go b/operator/validator/mocks/controller.go index 2ec55ecd50..107d6428c4 100644 --- a/operator/validator/mocks/controller.go +++ b/operator/validator/mocks/controller.go @@ -14,15 +14,14 @@ import ( "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/ethereum/go-ethereum/common" - "github.com/ssvlabs/ssv-spec-pre-cc/types" types0 "github.com/ssvlabs/ssv-spec/types" "go.uber.org/mock/gomock" "go.uber.org/zap" "github.com/ssvlabs/ssv/network" "github.com/ssvlabs/ssv/operator/duties" - "github.com/ssvlabs/ssv/operator/validators" "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon" + "github.com/ssvlabs/ssv/protocol/v2/ssv/validator" types1 "github.com/ssvlabs/ssv/protocol/v2/types" "github.com/ssvlabs/ssv/registry/storage" "github.com/ssvlabs/ssv/storage/basedb" @@ -89,18 +88,6 @@ func (mr *MockControllerMockRecorder) ExecuteDuty(logger, duty any) *gomock.Call return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecuteDuty", reflect.TypeOf((*MockController)(nil).ExecuteDuty), logger, duty) } -// ExecuteGenesisDuty mocks base method. -func (m *MockController) ExecuteGenesisDuty(logger *zap.Logger, duty *types.Duty) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "ExecuteGenesisDuty", logger, duty) -} - -// ExecuteGenesisDuty indicates an expected call of ExecuteGenesisDuty. -func (mr *MockControllerMockRecorder) ExecuteGenesisDuty(logger, duty any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecuteGenesisDuty", reflect.TypeOf((*MockController)(nil).ExecuteGenesisDuty), logger, duty) -} - // ExitValidator mocks base method. func (m *MockController) ExitValidator(pubKey phase0.BLSPubKey, blockNumber uint64, validatorIndex phase0.ValidatorIndex, ownValidator bool) error { m.ctrl.T.Helper() @@ -115,18 +102,6 @@ func (mr *MockControllerMockRecorder) ExitValidator(pubKey, blockNumber, validat return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExitValidator", reflect.TypeOf((*MockController)(nil).ExitValidator), pubKey, blockNumber, validatorIndex, ownValidator) } -// ForkListener mocks base method. -func (m *MockController) ForkListener(logger *zap.Logger) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "ForkListener", logger) -} - -// ForkListener indicates an expected call of ForkListener. -func (mr *MockControllerMockRecorder) ForkListener(logger any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ForkListener", reflect.TypeOf((*MockController)(nil).ForkListener), logger) -} - // GetOperatorShares mocks base method. func (m *MockController) GetOperatorShares() []*types1.SSVShare { m.ctrl.T.Helper() @@ -142,10 +117,10 @@ func (mr *MockControllerMockRecorder) GetOperatorShares() *gomock.Call { } // GetValidator mocks base method. -func (m *MockController) GetValidator(pubKey types0.ValidatorPK) (*validators.ValidatorContainer, bool) { +func (m *MockController) GetValidator(pubKey types0.ValidatorPK) (*validator.Validator, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetValidator", pubKey) - ret0, _ := ret[0].(*validators.ValidatorContainer) + ret0, _ := ret[0].(*validator.Validator) ret1, _ := ret[1].(bool) return ret0, ret1 } diff --git a/operator/validator/router_test.go b/operator/validator/router_test.go index 0b494b6cf1..a358ae5397 100644 --- a/operator/validator/router_test.go +++ b/operator/validator/router_test.go @@ -44,7 +44,7 @@ func TestRouter(t *testing.T) { msg := &queue.SSVMessage{ SSVMessage: &spectypes.SSVMessage{ MsgType: spectypes.MsgType(i % 3), - MsgID: spectypes.NewMsgID(networkconfig.TestNetwork.DomainType(), []byte{1, 1, 1, 1, 1}, spectypes.RoleCommittee), + MsgID: spectypes.NewMsgID(networkconfig.TestNetwork.DomainType, []byte{1, 1, 1, 1, 1}, spectypes.RoleCommittee), Data: []byte(fmt.Sprintf("data-%d", i)), }, } diff --git a/operator/validator/task_executor.go b/operator/validator/task_executor.go index dd2a5db437..68146d14de 100644 --- a/operator/validator/task_executor.go +++ b/operator/validator/task_executor.go @@ -11,8 +11,7 @@ import ( "github.com/ssvlabs/ssv/logging/fields" "github.com/ssvlabs/ssv/operator/duties" - "github.com/ssvlabs/ssv/operator/validators" - genesistypes "github.com/ssvlabs/ssv/protocol/genesis/types" + "github.com/ssvlabs/ssv/protocol/v2/ssv/validator" "github.com/ssvlabs/ssv/protocol/v2/types" ) @@ -80,15 +79,9 @@ func (c *controller) UpdateFeeRecipient(owner, recipient common.Address) error { zap.String("owner", owner.String()), zap.String("fee_recipient", recipient.String())) - c.validatorsMap.ForEachValidator(func(v *validators.ValidatorContainer) bool { - if v.Share().OwnerAddress == owner { - v.UpdateShare( - func(s *types.SSVShare) { - s.FeeRecipientAddress = recipient - }, func(s *genesistypes.SSVShare) { - s.FeeRecipientAddress = recipient - }, - ) + c.validatorsMap.ForEachValidator(func(v *validator.Validator) bool { + if v.Share.OwnerAddress == owner { + v.Share.FeeRecipientAddress = recipient logger.Debug("updated recipient address") } diff --git a/operator/validators/validator_container.go b/operator/validators/validator_container.go deleted file mode 100644 index 5aa214d552..0000000000 --- a/operator/validators/validator_container.go +++ /dev/null @@ -1,81 +0,0 @@ -package validators - -import ( - "errors" - "sync" - "sync/atomic" - - genesisvalidator "github.com/ssvlabs/ssv/protocol/genesis/ssv/validator" - genesistypes "github.com/ssvlabs/ssv/protocol/genesis/types" - "github.com/ssvlabs/ssv/protocol/v2/ssv/validator" - "github.com/ssvlabs/ssv/protocol/v2/types" - "go.uber.org/zap" -) - -type ValidatorContainer struct { - validator atomic.Pointer[validator.Validator] - genesisValidator atomic.Pointer[genesisvalidator.Validator] - mtx sync.Mutex -} - -func NewValidatorContainer(validator *validator.Validator, genesisValidator *genesisvalidator.Validator) (*ValidatorContainer, error) { - if validator == nil { - return nil, errors.New("validator must not be nil") - } - vc := &ValidatorContainer{} - vc.validator.Store(validator) - vc.genesisValidator.Store(genesisValidator) - return vc, nil -} - -func (vc *ValidatorContainer) Validator() *validator.Validator { - return vc.validator.Load() -} - -func (vc *ValidatorContainer) GenesisValidator() (*genesisvalidator.Validator, bool) { - v := vc.genesisValidator.Load() - if v == nil { - return nil, false - } - return v, true -} - -func (vc *ValidatorContainer) UnsetGenesisValidator() { - vc.genesisValidator.Store(nil) -} - -func (vc *ValidatorContainer) Start(logger *zap.Logger) (started bool, err error) { - started, err = vc.Validator().Start(logger) - if !started || err != nil { - return - } - if v, ok := vc.GenesisValidator(); ok { - started, err = v.Start(logger) - } - return -} - -func (vc *ValidatorContainer) Stop() { - vc.Validator().Stop() - if v, ok := vc.GenesisValidator(); ok { - v.Stop() - } -} - -func (vc *ValidatorContainer) Share() *types.SSVShare { - return vc.Validator().Share -} - -func (vc *ValidatorContainer) UpdateShare(updateAlan func(*types.SSVShare), updateGenesis func(*genesistypes.SSVShare)) { - if updateAlan == nil || updateGenesis == nil { - panic("both updateAlan and updateGenesis must be provided") - } - - vc.mtx.Lock() - defer vc.mtx.Unlock() - - updateAlan(vc.Validator().Share) - if v, ok := vc.GenesisValidator(); ok { - updateGenesis(v.Share) - } -} diff --git a/operator/validators/validators_map.go b/operator/validators/validators_map.go index ac4115e833..905748f1e7 100644 --- a/operator/validators/validators_map.go +++ b/operator/validators/validators_map.go @@ -13,7 +13,7 @@ import ( // TODO: use queues // validatorIterator is the function used to iterate over existing validators -type validatorIterator func(validator *ValidatorContainer) bool +type validatorIterator func(validator *validator.Validator) bool type committeeIterator func(validator *validator.Committee) bool // ValidatorsMap manages a collection of running validators @@ -21,7 +21,7 @@ type ValidatorsMap struct { ctx context.Context vlock sync.RWMutex mlock sync.RWMutex - validators map[spectypes.ValidatorPK]*ValidatorContainer + validators map[spectypes.ValidatorPK]*validator.Validator committees map[spectypes.CommitteeID]*validator.Committee } @@ -30,7 +30,7 @@ func New(ctx context.Context, opts ...Option) *ValidatorsMap { ctx: ctx, vlock: sync.RWMutex{}, mlock: sync.RWMutex{}, - validators: make(map[spectypes.ValidatorPK]*ValidatorContainer), + validators: make(map[spectypes.ValidatorPK]*validator.Validator), committees: make(map[spectypes.CommitteeID]*validator.Committee), } @@ -45,7 +45,7 @@ func New(ctx context.Context, opts ...Option) *ValidatorsMap { type Option func(*ValidatorsMap) // WithInitialState sets initial state -func WithInitialState(vstate map[spectypes.ValidatorPK]*ValidatorContainer, mstate map[spectypes.CommitteeID]*validator.Committee) Option { +func WithInitialState(vstate map[spectypes.ValidatorPK]*validator.Validator, mstate map[spectypes.CommitteeID]*validator.Committee) Option { return func(vm *ValidatorsMap) { vm.validators = vstate vm.committees = mstate @@ -66,7 +66,7 @@ func (vm *ValidatorsMap) ForEachValidator(iterator validatorIterator) bool { } // GetValidator returns a validator -func (vm *ValidatorsMap) GetValidator(pubKey spectypes.ValidatorPK) (*ValidatorContainer, bool) { +func (vm *ValidatorsMap) GetValidator(pubKey spectypes.ValidatorPK) (*validator.Validator, bool) { vm.vlock.RLock() defer vm.vlock.RUnlock() @@ -76,7 +76,7 @@ func (vm *ValidatorsMap) GetValidator(pubKey spectypes.ValidatorPK) (*ValidatorC } // PutValidator creates a new validator instance -func (vm *ValidatorsMap) PutValidator(pubKey spectypes.ValidatorPK, v *ValidatorContainer) { +func (vm *ValidatorsMap) PutValidator(pubKey spectypes.ValidatorPK, v *validator.Validator) { vm.vlock.Lock() defer vm.vlock.Unlock() @@ -85,7 +85,7 @@ func (vm *ValidatorsMap) PutValidator(pubKey spectypes.ValidatorPK, v *Validator // Remove removes a validator instance from the map // TODO: pass spectypes.ValidatorPK instead of string -func (vm *ValidatorsMap) RemoveValidator(pubKey spectypes.ValidatorPK) *ValidatorContainer { +func (vm *ValidatorsMap) RemoveValidator(pubKey spectypes.ValidatorPK) *validator.Validator { if v, found := vm.GetValidator(pubKey); found { vm.vlock.Lock() defer vm.vlock.Unlock() diff --git a/protocol/genesis/blockchain/beacon/beacon.go b/protocol/genesis/blockchain/beacon/beacon.go deleted file mode 100644 index 4e05dca21b..0000000000 --- a/protocol/genesis/blockchain/beacon/beacon.go +++ /dev/null @@ -1,55 +0,0 @@ -package genesisbeacon - -import ( - "context" - - eth2client "github.com/attestantio/go-eth2-client" - eth2apiv1 "github.com/attestantio/go-eth2-client/api/v1" - "github.com/attestantio/go-eth2-client/spec/bellatrix" - "github.com/attestantio/go-eth2-client/spec/phase0" - genesisspecssv "github.com/ssvlabs/ssv-spec-pre-cc/ssv" -) - -// TODO: add missing tests - -//go:generate mockgen -package=genesisbeacon -destination=./mock_client.go -source=./beacon.go - -// beaconDuties interface serves all duty related calls -type beaconDuties interface { - AttesterDuties(ctx context.Context, epoch phase0.Epoch, validatorIndices []phase0.ValidatorIndex) ([]*eth2apiv1.AttesterDuty, error) - ProposerDuties(ctx context.Context, epoch phase0.Epoch, validatorIndices []phase0.ValidatorIndex) ([]*eth2apiv1.ProposerDuty, error) - SyncCommitteeDuties(ctx context.Context, epoch phase0.Epoch, indices []phase0.ValidatorIndex) ([]*eth2apiv1.SyncCommitteeDuty, error) - eth2client.EventsProvider -} - -// beaconSubscriber interface serves all committee subscribe to subnet (p2p topic) -type beaconSubscriber interface { - // SubmitBeaconCommitteeSubscriptions subscribe committee to subnet - SubmitBeaconCommitteeSubscriptions(ctx context.Context, subscription []*eth2apiv1.BeaconCommitteeSubscription) error - // SubmitSyncCommitteeSubscriptions subscribe to sync committee subnet - SubmitSyncCommitteeSubscriptions(ctx context.Context, subscription []*eth2apiv1.SyncCommitteeSubscription) error -} - -type beaconValidator interface { - // GetValidatorData returns metadata (balance, index, status, more) for each pubkey from the node - GetValidatorData(validatorPubKeys []phase0.BLSPubKey) (map[phase0.ValidatorIndex]*eth2apiv1.Validator, error) -} - -type proposer interface { - // SubmitProposalPreparation with fee recipients - SubmitProposalPreparation(feeRecipients map[phase0.ValidatorIndex]bellatrix.ExecutionAddress) error -} - -// TODO need to handle differently (by spec) -type signer interface { - ComputeSigningRoot(object interface{}, domain phase0.Domain) ([32]byte, error) -} - -type BeaconNode interface { - genesisspecssv.BeaconNode // spec beacon interface - beaconDuties - beaconSubscriber - beaconValidator - signer // TODO need to handle differently - proposer -} diff --git a/protocol/genesis/blockchain/beacon/mock_client.go b/protocol/genesis/blockchain/beacon/mock_client.go deleted file mode 100644 index f141bf3bf9..0000000000 --- a/protocol/genesis/blockchain/beacon/mock_client.go +++ /dev/null @@ -1,693 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: ./beacon.go -// -// Generated by this command: -// -// mockgen -package=genesisbeacon -destination=./mock_client.go -source=./beacon.go -// - -// Package genesisbeacon is a generated GoMock package. -package genesisbeacon - -import ( - context "context" - reflect "reflect" - - client "github.com/attestantio/go-eth2-client" - api "github.com/attestantio/go-eth2-client/api" - v1 "github.com/attestantio/go-eth2-client/api/v1" - spec "github.com/attestantio/go-eth2-client/spec" - altair "github.com/attestantio/go-eth2-client/spec/altair" - bellatrix "github.com/attestantio/go-eth2-client/spec/bellatrix" - phase0 "github.com/attestantio/go-eth2-client/spec/phase0" - ssz "github.com/ferranbt/fastssz" - types "github.com/ssvlabs/ssv-spec-pre-cc/types" - gomock "go.uber.org/mock/gomock" -) - -// MockbeaconDuties is a mock of beaconDuties interface. -type MockbeaconDuties struct { - ctrl *gomock.Controller - recorder *MockbeaconDutiesMockRecorder -} - -// MockbeaconDutiesMockRecorder is the mock recorder for MockbeaconDuties. -type MockbeaconDutiesMockRecorder struct { - mock *MockbeaconDuties -} - -// NewMockbeaconDuties creates a new mock instance. -func NewMockbeaconDuties(ctrl *gomock.Controller) *MockbeaconDuties { - mock := &MockbeaconDuties{ctrl: ctrl} - mock.recorder = &MockbeaconDutiesMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockbeaconDuties) EXPECT() *MockbeaconDutiesMockRecorder { - return m.recorder -} - -// AttesterDuties mocks base method. -func (m *MockbeaconDuties) AttesterDuties(ctx context.Context, epoch phase0.Epoch, validatorIndices []phase0.ValidatorIndex) ([]*v1.AttesterDuty, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AttesterDuties", ctx, epoch, validatorIndices) - ret0, _ := ret[0].([]*v1.AttesterDuty) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// AttesterDuties indicates an expected call of AttesterDuties. -func (mr *MockbeaconDutiesMockRecorder) AttesterDuties(ctx, epoch, validatorIndices any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AttesterDuties", reflect.TypeOf((*MockbeaconDuties)(nil).AttesterDuties), ctx, epoch, validatorIndices) -} - -// Events mocks base method. -func (m *MockbeaconDuties) Events(ctx context.Context, topics []string, handler client.EventHandlerFunc) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Events", ctx, topics, handler) - ret0, _ := ret[0].(error) - return ret0 -} - -// Events indicates an expected call of Events. -func (mr *MockbeaconDutiesMockRecorder) Events(ctx, topics, handler any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Events", reflect.TypeOf((*MockbeaconDuties)(nil).Events), ctx, topics, handler) -} - -// ProposerDuties mocks base method. -func (m *MockbeaconDuties) ProposerDuties(ctx context.Context, epoch phase0.Epoch, validatorIndices []phase0.ValidatorIndex) ([]*v1.ProposerDuty, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ProposerDuties", ctx, epoch, validatorIndices) - ret0, _ := ret[0].([]*v1.ProposerDuty) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ProposerDuties indicates an expected call of ProposerDuties. -func (mr *MockbeaconDutiesMockRecorder) ProposerDuties(ctx, epoch, validatorIndices any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProposerDuties", reflect.TypeOf((*MockbeaconDuties)(nil).ProposerDuties), ctx, epoch, validatorIndices) -} - -// SyncCommitteeDuties mocks base method. -func (m *MockbeaconDuties) SyncCommitteeDuties(ctx context.Context, epoch phase0.Epoch, indices []phase0.ValidatorIndex) ([]*v1.SyncCommitteeDuty, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SyncCommitteeDuties", ctx, epoch, indices) - ret0, _ := ret[0].([]*v1.SyncCommitteeDuty) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// SyncCommitteeDuties indicates an expected call of SyncCommitteeDuties. -func (mr *MockbeaconDutiesMockRecorder) SyncCommitteeDuties(ctx, epoch, indices any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SyncCommitteeDuties", reflect.TypeOf((*MockbeaconDuties)(nil).SyncCommitteeDuties), ctx, epoch, indices) -} - -// MockbeaconSubscriber is a mock of beaconSubscriber interface. -type MockbeaconSubscriber struct { - ctrl *gomock.Controller - recorder *MockbeaconSubscriberMockRecorder -} - -// MockbeaconSubscriberMockRecorder is the mock recorder for MockbeaconSubscriber. -type MockbeaconSubscriberMockRecorder struct { - mock *MockbeaconSubscriber -} - -// NewMockbeaconSubscriber creates a new mock instance. -func NewMockbeaconSubscriber(ctrl *gomock.Controller) *MockbeaconSubscriber { - mock := &MockbeaconSubscriber{ctrl: ctrl} - mock.recorder = &MockbeaconSubscriberMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockbeaconSubscriber) EXPECT() *MockbeaconSubscriberMockRecorder { - return m.recorder -} - -// SubmitBeaconCommitteeSubscriptions mocks base method. -func (m *MockbeaconSubscriber) SubmitBeaconCommitteeSubscriptions(ctx context.Context, subscription []*v1.BeaconCommitteeSubscription) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SubmitBeaconCommitteeSubscriptions", ctx, subscription) - ret0, _ := ret[0].(error) - return ret0 -} - -// SubmitBeaconCommitteeSubscriptions indicates an expected call of SubmitBeaconCommitteeSubscriptions. -func (mr *MockbeaconSubscriberMockRecorder) SubmitBeaconCommitteeSubscriptions(ctx, subscription any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitBeaconCommitteeSubscriptions", reflect.TypeOf((*MockbeaconSubscriber)(nil).SubmitBeaconCommitteeSubscriptions), ctx, subscription) -} - -// SubmitSyncCommitteeSubscriptions mocks base method. -func (m *MockbeaconSubscriber) SubmitSyncCommitteeSubscriptions(ctx context.Context, subscription []*v1.SyncCommitteeSubscription) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SubmitSyncCommitteeSubscriptions", ctx, subscription) - ret0, _ := ret[0].(error) - return ret0 -} - -// SubmitSyncCommitteeSubscriptions indicates an expected call of SubmitSyncCommitteeSubscriptions. -func (mr *MockbeaconSubscriberMockRecorder) SubmitSyncCommitteeSubscriptions(ctx, subscription any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitSyncCommitteeSubscriptions", reflect.TypeOf((*MockbeaconSubscriber)(nil).SubmitSyncCommitteeSubscriptions), ctx, subscription) -} - -// MockbeaconValidator is a mock of beaconValidator interface. -type MockbeaconValidator struct { - ctrl *gomock.Controller - recorder *MockbeaconValidatorMockRecorder -} - -// MockbeaconValidatorMockRecorder is the mock recorder for MockbeaconValidator. -type MockbeaconValidatorMockRecorder struct { - mock *MockbeaconValidator -} - -// NewMockbeaconValidator creates a new mock instance. -func NewMockbeaconValidator(ctrl *gomock.Controller) *MockbeaconValidator { - mock := &MockbeaconValidator{ctrl: ctrl} - mock.recorder = &MockbeaconValidatorMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockbeaconValidator) EXPECT() *MockbeaconValidatorMockRecorder { - return m.recorder -} - -// GetValidatorData mocks base method. -func (m *MockbeaconValidator) GetValidatorData(validatorPubKeys []phase0.BLSPubKey) (map[phase0.ValidatorIndex]*v1.Validator, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetValidatorData", validatorPubKeys) - ret0, _ := ret[0].(map[phase0.ValidatorIndex]*v1.Validator) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetValidatorData indicates an expected call of GetValidatorData. -func (mr *MockbeaconValidatorMockRecorder) GetValidatorData(validatorPubKeys any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetValidatorData", reflect.TypeOf((*MockbeaconValidator)(nil).GetValidatorData), validatorPubKeys) -} - -// Mockproposer is a mock of proposer interface. -type Mockproposer struct { - ctrl *gomock.Controller - recorder *MockproposerMockRecorder -} - -// MockproposerMockRecorder is the mock recorder for Mockproposer. -type MockproposerMockRecorder struct { - mock *Mockproposer -} - -// NewMockproposer creates a new mock instance. -func NewMockproposer(ctrl *gomock.Controller) *Mockproposer { - mock := &Mockproposer{ctrl: ctrl} - mock.recorder = &MockproposerMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *Mockproposer) EXPECT() *MockproposerMockRecorder { - return m.recorder -} - -// SubmitProposalPreparation mocks base method. -func (m *Mockproposer) SubmitProposalPreparation(feeRecipients map[phase0.ValidatorIndex]bellatrix.ExecutionAddress) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SubmitProposalPreparation", feeRecipients) - ret0, _ := ret[0].(error) - return ret0 -} - -// SubmitProposalPreparation indicates an expected call of SubmitProposalPreparation. -func (mr *MockproposerMockRecorder) SubmitProposalPreparation(feeRecipients any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitProposalPreparation", reflect.TypeOf((*Mockproposer)(nil).SubmitProposalPreparation), feeRecipients) -} - -// Mocksigner is a mock of signer interface. -type Mocksigner struct { - ctrl *gomock.Controller - recorder *MocksignerMockRecorder -} - -// MocksignerMockRecorder is the mock recorder for Mocksigner. -type MocksignerMockRecorder struct { - mock *Mocksigner -} - -// NewMocksigner creates a new mock instance. -func NewMocksigner(ctrl *gomock.Controller) *Mocksigner { - mock := &Mocksigner{ctrl: ctrl} - mock.recorder = &MocksignerMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *Mocksigner) EXPECT() *MocksignerMockRecorder { - return m.recorder -} - -// ComputeSigningRoot mocks base method. -func (m *Mocksigner) ComputeSigningRoot(object any, domain phase0.Domain) ([32]byte, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ComputeSigningRoot", object, domain) - ret0, _ := ret[0].([32]byte) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ComputeSigningRoot indicates an expected call of ComputeSigningRoot. -func (mr *MocksignerMockRecorder) ComputeSigningRoot(object, domain any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ComputeSigningRoot", reflect.TypeOf((*Mocksigner)(nil).ComputeSigningRoot), object, domain) -} - -// MockBeaconNode is a mock of BeaconNode interface. -type MockBeaconNode struct { - ctrl *gomock.Controller - recorder *MockBeaconNodeMockRecorder -} - -// MockBeaconNodeMockRecorder is the mock recorder for MockBeaconNode. -type MockBeaconNodeMockRecorder struct { - mock *MockBeaconNode -} - -// NewMockBeaconNode creates a new mock instance. -func NewMockBeaconNode(ctrl *gomock.Controller) *MockBeaconNode { - mock := &MockBeaconNode{ctrl: ctrl} - mock.recorder = &MockBeaconNodeMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockBeaconNode) EXPECT() *MockBeaconNodeMockRecorder { - return m.recorder -} - -// AttesterDuties mocks base method. -func (m *MockBeaconNode) AttesterDuties(ctx context.Context, epoch phase0.Epoch, validatorIndices []phase0.ValidatorIndex) ([]*v1.AttesterDuty, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AttesterDuties", ctx, epoch, validatorIndices) - ret0, _ := ret[0].([]*v1.AttesterDuty) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// AttesterDuties indicates an expected call of AttesterDuties. -func (mr *MockBeaconNodeMockRecorder) AttesterDuties(ctx, epoch, validatorIndices any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AttesterDuties", reflect.TypeOf((*MockBeaconNode)(nil).AttesterDuties), ctx, epoch, validatorIndices) -} - -// ComputeSigningRoot mocks base method. -func (m *MockBeaconNode) ComputeSigningRoot(object any, domain phase0.Domain) ([32]byte, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ComputeSigningRoot", object, domain) - ret0, _ := ret[0].([32]byte) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ComputeSigningRoot indicates an expected call of ComputeSigningRoot. -func (mr *MockBeaconNodeMockRecorder) ComputeSigningRoot(object, domain any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ComputeSigningRoot", reflect.TypeOf((*MockBeaconNode)(nil).ComputeSigningRoot), object, domain) -} - -// DomainData mocks base method. -func (m *MockBeaconNode) DomainData(epoch phase0.Epoch, domain phase0.DomainType) (phase0.Domain, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DomainData", epoch, domain) - ret0, _ := ret[0].(phase0.Domain) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// DomainData indicates an expected call of DomainData. -func (mr *MockBeaconNodeMockRecorder) DomainData(epoch, domain any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DomainData", reflect.TypeOf((*MockBeaconNode)(nil).DomainData), epoch, domain) -} - -// Events mocks base method. -func (m *MockBeaconNode) Events(ctx context.Context, topics []string, handler client.EventHandlerFunc) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Events", ctx, topics, handler) - ret0, _ := ret[0].(error) - return ret0 -} - -// Events indicates an expected call of Events. -func (mr *MockBeaconNodeMockRecorder) Events(ctx, topics, handler any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Events", reflect.TypeOf((*MockBeaconNode)(nil).Events), ctx, topics, handler) -} - -// GetAttestationData mocks base method. -func (m *MockBeaconNode) GetAttestationData(slot phase0.Slot, committeeIndex phase0.CommitteeIndex) (ssz.Marshaler, spec.DataVersion, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAttestationData", slot, committeeIndex) - ret0, _ := ret[0].(ssz.Marshaler) - ret1, _ := ret[1].(spec.DataVersion) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// GetAttestationData indicates an expected call of GetAttestationData. -func (mr *MockBeaconNodeMockRecorder) GetAttestationData(slot, committeeIndex any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAttestationData", reflect.TypeOf((*MockBeaconNode)(nil).GetAttestationData), slot, committeeIndex) -} - -// GetBeaconBlock mocks base method. -func (m *MockBeaconNode) GetBeaconBlock(slot phase0.Slot, graffiti, randao []byte) (ssz.Marshaler, spec.DataVersion, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetBeaconBlock", slot, graffiti, randao) - ret0, _ := ret[0].(ssz.Marshaler) - ret1, _ := ret[1].(spec.DataVersion) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// GetBeaconBlock indicates an expected call of GetBeaconBlock. -func (mr *MockBeaconNodeMockRecorder) GetBeaconBlock(slot, graffiti, randao any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBeaconBlock", reflect.TypeOf((*MockBeaconNode)(nil).GetBeaconBlock), slot, graffiti, randao) -} - -// GetBeaconNetwork mocks base method. -func (m *MockBeaconNode) GetBeaconNetwork() types.BeaconNetwork { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetBeaconNetwork") - ret0, _ := ret[0].(types.BeaconNetwork) - return ret0 -} - -// GetBeaconNetwork indicates an expected call of GetBeaconNetwork. -func (mr *MockBeaconNodeMockRecorder) GetBeaconNetwork() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBeaconNetwork", reflect.TypeOf((*MockBeaconNode)(nil).GetBeaconNetwork)) -} - -// GetBlindedBeaconBlock mocks base method. -func (m *MockBeaconNode) GetBlindedBeaconBlock(slot phase0.Slot, graffiti, randao []byte) (ssz.Marshaler, spec.DataVersion, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetBlindedBeaconBlock", slot, graffiti, randao) - ret0, _ := ret[0].(ssz.Marshaler) - ret1, _ := ret[1].(spec.DataVersion) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// GetBlindedBeaconBlock indicates an expected call of GetBlindedBeaconBlock. -func (mr *MockBeaconNodeMockRecorder) GetBlindedBeaconBlock(slot, graffiti, randao any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBlindedBeaconBlock", reflect.TypeOf((*MockBeaconNode)(nil).GetBlindedBeaconBlock), slot, graffiti, randao) -} - -// GetSyncCommitteeContribution mocks base method. -func (m *MockBeaconNode) GetSyncCommitteeContribution(slot phase0.Slot, selectionProofs []phase0.BLSSignature, subnetIDs []uint64) (ssz.Marshaler, spec.DataVersion, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetSyncCommitteeContribution", slot, selectionProofs, subnetIDs) - ret0, _ := ret[0].(ssz.Marshaler) - ret1, _ := ret[1].(spec.DataVersion) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// GetSyncCommitteeContribution indicates an expected call of GetSyncCommitteeContribution. -func (mr *MockBeaconNodeMockRecorder) GetSyncCommitteeContribution(slot, selectionProofs, subnetIDs any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSyncCommitteeContribution", reflect.TypeOf((*MockBeaconNode)(nil).GetSyncCommitteeContribution), slot, selectionProofs, subnetIDs) -} - -// GetSyncMessageBlockRoot mocks base method. -func (m *MockBeaconNode) GetSyncMessageBlockRoot(slot phase0.Slot) (phase0.Root, spec.DataVersion, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetSyncMessageBlockRoot", slot) - ret0, _ := ret[0].(phase0.Root) - ret1, _ := ret[1].(spec.DataVersion) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// GetSyncMessageBlockRoot indicates an expected call of GetSyncMessageBlockRoot. -func (mr *MockBeaconNodeMockRecorder) GetSyncMessageBlockRoot(slot any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSyncMessageBlockRoot", reflect.TypeOf((*MockBeaconNode)(nil).GetSyncMessageBlockRoot), slot) -} - -// GetValidatorData mocks base method. -func (m *MockBeaconNode) GetValidatorData(validatorPubKeys []phase0.BLSPubKey) (map[phase0.ValidatorIndex]*v1.Validator, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetValidatorData", validatorPubKeys) - ret0, _ := ret[0].(map[phase0.ValidatorIndex]*v1.Validator) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetValidatorData indicates an expected call of GetValidatorData. -func (mr *MockBeaconNodeMockRecorder) GetValidatorData(validatorPubKeys any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetValidatorData", reflect.TypeOf((*MockBeaconNode)(nil).GetValidatorData), validatorPubKeys) -} - -// IsSyncCommitteeAggregator mocks base method. -func (m *MockBeaconNode) IsSyncCommitteeAggregator(proof []byte) (bool, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "IsSyncCommitteeAggregator", proof) - ret0, _ := ret[0].(bool) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// IsSyncCommitteeAggregator indicates an expected call of IsSyncCommitteeAggregator. -func (mr *MockBeaconNodeMockRecorder) IsSyncCommitteeAggregator(proof any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsSyncCommitteeAggregator", reflect.TypeOf((*MockBeaconNode)(nil).IsSyncCommitteeAggregator), proof) -} - -// ProposerDuties mocks base method. -func (m *MockBeaconNode) ProposerDuties(ctx context.Context, epoch phase0.Epoch, validatorIndices []phase0.ValidatorIndex) ([]*v1.ProposerDuty, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ProposerDuties", ctx, epoch, validatorIndices) - ret0, _ := ret[0].([]*v1.ProposerDuty) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ProposerDuties indicates an expected call of ProposerDuties. -func (mr *MockBeaconNodeMockRecorder) ProposerDuties(ctx, epoch, validatorIndices any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProposerDuties", reflect.TypeOf((*MockBeaconNode)(nil).ProposerDuties), ctx, epoch, validatorIndices) -} - -// SubmitAggregateSelectionProof mocks base method. -func (m *MockBeaconNode) SubmitAggregateSelectionProof(slot phase0.Slot, committeeIndex phase0.CommitteeIndex, committeeLength uint64, index phase0.ValidatorIndex, slotSig []byte) (ssz.Marshaler, spec.DataVersion, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SubmitAggregateSelectionProof", slot, committeeIndex, committeeLength, index, slotSig) - ret0, _ := ret[0].(ssz.Marshaler) - ret1, _ := ret[1].(spec.DataVersion) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// SubmitAggregateSelectionProof indicates an expected call of SubmitAggregateSelectionProof. -func (mr *MockBeaconNodeMockRecorder) SubmitAggregateSelectionProof(slot, committeeIndex, committeeLength, index, slotSig any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitAggregateSelectionProof", reflect.TypeOf((*MockBeaconNode)(nil).SubmitAggregateSelectionProof), slot, committeeIndex, committeeLength, index, slotSig) -} - -// SubmitAttestation mocks base method. -func (m *MockBeaconNode) SubmitAttestation(attestation *phase0.Attestation) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SubmitAttestation", attestation) - ret0, _ := ret[0].(error) - return ret0 -} - -// SubmitAttestation indicates an expected call of SubmitAttestation. -func (mr *MockBeaconNodeMockRecorder) SubmitAttestation(attestation any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitAttestation", reflect.TypeOf((*MockBeaconNode)(nil).SubmitAttestation), attestation) -} - -// SubmitBeaconBlock mocks base method. -func (m *MockBeaconNode) SubmitBeaconBlock(block *api.VersionedProposal, sig phase0.BLSSignature) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SubmitBeaconBlock", block, sig) - ret0, _ := ret[0].(error) - return ret0 -} - -// SubmitBeaconBlock indicates an expected call of SubmitBeaconBlock. -func (mr *MockBeaconNodeMockRecorder) SubmitBeaconBlock(block, sig any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitBeaconBlock", reflect.TypeOf((*MockBeaconNode)(nil).SubmitBeaconBlock), block, sig) -} - -// SubmitBeaconCommitteeSubscriptions mocks base method. -func (m *MockBeaconNode) SubmitBeaconCommitteeSubscriptions(ctx context.Context, subscription []*v1.BeaconCommitteeSubscription) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SubmitBeaconCommitteeSubscriptions", ctx, subscription) - ret0, _ := ret[0].(error) - return ret0 -} - -// SubmitBeaconCommitteeSubscriptions indicates an expected call of SubmitBeaconCommitteeSubscriptions. -func (mr *MockBeaconNodeMockRecorder) SubmitBeaconCommitteeSubscriptions(ctx, subscription any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitBeaconCommitteeSubscriptions", reflect.TypeOf((*MockBeaconNode)(nil).SubmitBeaconCommitteeSubscriptions), ctx, subscription) -} - -// SubmitBlindedBeaconBlock mocks base method. -func (m *MockBeaconNode) SubmitBlindedBeaconBlock(block *api.VersionedBlindedProposal, sig phase0.BLSSignature) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SubmitBlindedBeaconBlock", block, sig) - ret0, _ := ret[0].(error) - return ret0 -} - -// SubmitBlindedBeaconBlock indicates an expected call of SubmitBlindedBeaconBlock. -func (mr *MockBeaconNodeMockRecorder) SubmitBlindedBeaconBlock(block, sig any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitBlindedBeaconBlock", reflect.TypeOf((*MockBeaconNode)(nil).SubmitBlindedBeaconBlock), block, sig) -} - -// SubmitProposalPreparation mocks base method. -func (m *MockBeaconNode) SubmitProposalPreparation(feeRecipients map[phase0.ValidatorIndex]bellatrix.ExecutionAddress) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SubmitProposalPreparation", feeRecipients) - ret0, _ := ret[0].(error) - return ret0 -} - -// SubmitProposalPreparation indicates an expected call of SubmitProposalPreparation. -func (mr *MockBeaconNodeMockRecorder) SubmitProposalPreparation(feeRecipients any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitProposalPreparation", reflect.TypeOf((*MockBeaconNode)(nil).SubmitProposalPreparation), feeRecipients) -} - -// SubmitSignedAggregateSelectionProof mocks base method. -func (m *MockBeaconNode) SubmitSignedAggregateSelectionProof(msg *phase0.SignedAggregateAndProof) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SubmitSignedAggregateSelectionProof", msg) - ret0, _ := ret[0].(error) - return ret0 -} - -// SubmitSignedAggregateSelectionProof indicates an expected call of SubmitSignedAggregateSelectionProof. -func (mr *MockBeaconNodeMockRecorder) SubmitSignedAggregateSelectionProof(msg any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitSignedAggregateSelectionProof", reflect.TypeOf((*MockBeaconNode)(nil).SubmitSignedAggregateSelectionProof), msg) -} - -// SubmitSignedContributionAndProof mocks base method. -func (m *MockBeaconNode) SubmitSignedContributionAndProof(contribution *altair.SignedContributionAndProof) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SubmitSignedContributionAndProof", contribution) - ret0, _ := ret[0].(error) - return ret0 -} - -// SubmitSignedContributionAndProof indicates an expected call of SubmitSignedContributionAndProof. -func (mr *MockBeaconNodeMockRecorder) SubmitSignedContributionAndProof(contribution any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitSignedContributionAndProof", reflect.TypeOf((*MockBeaconNode)(nil).SubmitSignedContributionAndProof), contribution) -} - -// SubmitSyncCommitteeSubscriptions mocks base method. -func (m *MockBeaconNode) SubmitSyncCommitteeSubscriptions(ctx context.Context, subscription []*v1.SyncCommitteeSubscription) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SubmitSyncCommitteeSubscriptions", ctx, subscription) - ret0, _ := ret[0].(error) - return ret0 -} - -// SubmitSyncCommitteeSubscriptions indicates an expected call of SubmitSyncCommitteeSubscriptions. -func (mr *MockBeaconNodeMockRecorder) SubmitSyncCommitteeSubscriptions(ctx, subscription any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitSyncCommitteeSubscriptions", reflect.TypeOf((*MockBeaconNode)(nil).SubmitSyncCommitteeSubscriptions), ctx, subscription) -} - -// SubmitSyncMessage mocks base method. -func (m *MockBeaconNode) SubmitSyncMessage(msg *altair.SyncCommitteeMessage) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SubmitSyncMessage", msg) - ret0, _ := ret[0].(error) - return ret0 -} - -// SubmitSyncMessage indicates an expected call of SubmitSyncMessage. -func (mr *MockBeaconNodeMockRecorder) SubmitSyncMessage(msg any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitSyncMessage", reflect.TypeOf((*MockBeaconNode)(nil).SubmitSyncMessage), msg) -} - -// SubmitValidatorRegistration mocks base method. -func (m *MockBeaconNode) SubmitValidatorRegistration(pubkey []byte, feeRecipient bellatrix.ExecutionAddress, sig phase0.BLSSignature) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SubmitValidatorRegistration", pubkey, feeRecipient, sig) - ret0, _ := ret[0].(error) - return ret0 -} - -// SubmitValidatorRegistration indicates an expected call of SubmitValidatorRegistration. -func (mr *MockBeaconNodeMockRecorder) SubmitValidatorRegistration(pubkey, feeRecipient, sig any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitValidatorRegistration", reflect.TypeOf((*MockBeaconNode)(nil).SubmitValidatorRegistration), pubkey, feeRecipient, sig) -} - -// SubmitVoluntaryExit mocks base method. -func (m *MockBeaconNode) SubmitVoluntaryExit(voluntaryExit *phase0.SignedVoluntaryExit) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SubmitVoluntaryExit", voluntaryExit) - ret0, _ := ret[0].(error) - return ret0 -} - -// SubmitVoluntaryExit indicates an expected call of SubmitVoluntaryExit. -func (mr *MockBeaconNodeMockRecorder) SubmitVoluntaryExit(voluntaryExit any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitVoluntaryExit", reflect.TypeOf((*MockBeaconNode)(nil).SubmitVoluntaryExit), voluntaryExit) -} - -// SyncCommitteeDuties mocks base method. -func (m *MockBeaconNode) SyncCommitteeDuties(ctx context.Context, epoch phase0.Epoch, indices []phase0.ValidatorIndex) ([]*v1.SyncCommitteeDuty, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SyncCommitteeDuties", ctx, epoch, indices) - ret0, _ := ret[0].([]*v1.SyncCommitteeDuty) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// SyncCommitteeDuties indicates an expected call of SyncCommitteeDuties. -func (mr *MockBeaconNodeMockRecorder) SyncCommitteeDuties(ctx, epoch, indices any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SyncCommitteeDuties", reflect.TypeOf((*MockBeaconNode)(nil).SyncCommitteeDuties), ctx, epoch, indices) -} - -// SyncCommitteeSubnetID mocks base method. -func (m *MockBeaconNode) SyncCommitteeSubnetID(index phase0.CommitteeIndex) (uint64, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SyncCommitteeSubnetID", index) - ret0, _ := ret[0].(uint64) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// SyncCommitteeSubnetID indicates an expected call of SyncCommitteeSubnetID. -func (mr *MockBeaconNodeMockRecorder) SyncCommitteeSubnetID(index any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SyncCommitteeSubnetID", reflect.TypeOf((*MockBeaconNode)(nil).SyncCommitteeSubnetID), index) -} diff --git a/protocol/genesis/blockchain/eth1/registry_storage.go b/protocol/genesis/blockchain/eth1/registry_storage.go deleted file mode 100644 index ee869ee8f4..0000000000 --- a/protocol/genesis/blockchain/eth1/registry_storage.go +++ /dev/null @@ -1,7 +0,0 @@ -package eth1 - -// RegistryStore interface for registry store -// TODO: extend this interface and re-think storage refactoring -type RegistryStore interface { - DropRegistryData() error -} diff --git a/protocol/genesis/message/consensus.go b/protocol/genesis/message/consensus.go deleted file mode 100644 index a497662e71..0000000000 --- a/protocol/genesis/message/consensus.go +++ /dev/null @@ -1,19 +0,0 @@ -package message - -import ( - "sort" - - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" -) - -// Aggregate is a utility that helps to ensure sorted signers -func Aggregate(signedMsg *genesisspecqbft.SignedMessage, s genesisspectypes.MessageSignature) error { - if err := signedMsg.Aggregate(s); err != nil { - return err - } - sort.Slice(signedMsg.Signers, func(i, j int) bool { - return signedMsg.Signers[i] < signedMsg.Signers[j] - }) - return nil -} diff --git a/protocol/genesis/message/consensus_test.go b/protocol/genesis/message/consensus_test.go deleted file mode 100644 index 6750af5b76..0000000000 --- a/protocol/genesis/message/consensus_test.go +++ /dev/null @@ -1,40 +0,0 @@ -package message_test - -import ( - "sort" - "testing" - - "github.com/ssvlabs/ssv/protocol/genesis/message" - protocoltesting "github.com/ssvlabs/ssv/protocol/genesis/testing" - - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - "github.com/stretchr/testify/require" -) - -func TestAggregateSorting(t *testing.T) { - uids := []genesisspectypes.OperatorID{genesisspectypes.OperatorID(1), genesisspectypes.OperatorID(2), genesisspectypes.OperatorID(3), genesisspectypes.OperatorID(4)} - secretKeys, _ := protocoltesting.GenerateBLSKeys(uids...) - - identifier := []byte("pk") - - generateSignedMsg := func(operatorId genesisspectypes.OperatorID) *genesisspecqbft.SignedMessage { - return protocoltesting.SignMsg(t, secretKeys, []genesisspectypes.OperatorID{operatorId}, &genesisspecqbft.Message{ - MsgType: genesisspecqbft.CommitMsgType, - Height: 0, - Round: 1, - Identifier: identifier, - }) - } - - signedMessage := generateSignedMsg(1) - for i := 2; i <= 4; i++ { - sig := generateSignedMsg(genesisspectypes.OperatorID(i)) - require.NoError(t, message.Aggregate(signedMessage, sig)) - } - - sorted := sort.SliceIsSorted(signedMessage.Signers, func(i, j int) bool { - return signedMessage.Signers[i] < signedMessage.Signers[j] - }) - require.True(t, sorted) -} diff --git a/protocol/genesis/message/encoding.go b/protocol/genesis/message/encoding.go deleted file mode 100644 index 6171088c87..0000000000 --- a/protocol/genesis/message/encoding.go +++ /dev/null @@ -1,9 +0,0 @@ -package message - -// Encoder encodes or decodes the message -type Encoder interface { - // Encode encodes the message - Encode() ([]byte, error) - // Decode decodes the message - Decode(data []byte) error -} diff --git a/protocol/genesis/message/msg.go b/protocol/genesis/message/msg.go deleted file mode 100644 index 0ff34e9a09..0000000000 --- a/protocol/genesis/message/msg.go +++ /dev/null @@ -1,68 +0,0 @@ -package message - -import ( - "fmt" - - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" -) - -const ( - // SSVSyncMsgType extends spec msg type - SSVSyncMsgType genesisspectypes.MsgType = 100 - // SSVEventMsgType extends spec msg type - SSVEventMsgType genesisspectypes.MsgType = 200 -) - -// MsgTypeToString extension for spec msg type. convert spec msg type to string -func MsgTypeToString(mt genesisspectypes.MsgType) string { - switch mt { - case genesisspectypes.SSVConsensusMsgType: - return "consensus" - case genesisspectypes.SSVPartialSignatureMsgType: - return "partial_signature" - case SSVSyncMsgType: - return "sync" - case SSVEventMsgType: - return "event" - default: - return fmt.Sprintf("unknown(%d)", mt) - } -} - -func QBFTMsgTypeToString(mt genesisspecqbft.MessageType) string { - switch mt { - case genesisspecqbft.ProposalMsgType: - return "proposal" - case genesisspecqbft.PrepareMsgType: - return "prepare" - case genesisspecqbft.CommitMsgType: - return "commit" - case genesisspecqbft.RoundChangeMsgType: - return "round_change" - default: - return fmt.Sprintf("unknown(%d)", mt) - } -} - -// BeaconRoleFromString returns BeaconRole from string -func BeaconRoleFromString(s string) (genesisspectypes.BeaconRole, error) { - switch s { - case "ATTESTER": - return genesisspectypes.BNRoleAttester, nil - case "AGGREGATOR": - return genesisspectypes.BNRoleAggregator, nil - case "PROPOSER": - return genesisspectypes.BNRoleProposer, nil - case "SYNC_COMMITTEE": - return genesisspectypes.BNRoleSyncCommittee, nil - case "SYNC_COMMITTEE_CONTRIBUTION": - return genesisspectypes.BNRoleSyncCommitteeContribution, nil - case "VALIDATOR_REGISTRATION": - return genesisspectypes.BNRoleValidatorRegistration, nil - case "VOLUNTARY_EXIT": - return genesisspectypes.BNRoleVoluntaryExit, nil - default: - return 0, fmt.Errorf("unknown role: %s", s) - } -} diff --git a/protocol/genesis/qbft/config.go b/protocol/genesis/qbft/config.go deleted file mode 100644 index 942c5853ad..0000000000 --- a/protocol/genesis/qbft/config.go +++ /dev/null @@ -1,81 +0,0 @@ -package qbft - -import ( - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - - "github.com/ssvlabs/ssv/protocol/genesis/qbft/roundtimer" - qbftstorage "github.com/ssvlabs/ssv/protocol/genesis/qbft/storage" -) - -type signing interface { - // GetSigner returns a Signer instance - GetSigner() genesisspectypes.SSVSigner - // GetSignatureDomainType returns the Domain type used for signatures - GetSignatureDomainType() genesisspectypes.DomainType -} - -type IConfig interface { - signing - // GetValueCheckF returns value check function - GetValueCheckF() genesisspecqbft.ProposedValueCheckF - // GetProposerF returns func used to calculate proposer - GetProposerF() genesisspecqbft.ProposerF - // GetNetwork returns a p2p Network instance - GetNetwork() genesisspecqbft.Network - // GetStorage returns a storage instance - GetStorage() qbftstorage.QBFTStore - // GetTimer returns round timer - GetTimer() roundtimer.Timer -} - -type Config struct { - Signer genesisspectypes.SSVSigner - SigningPK []byte - Domain genesisspectypes.DomainType - ValueCheckF genesisspecqbft.ProposedValueCheckF - ProposerF genesisspecqbft.ProposerF - Storage qbftstorage.QBFTStore - Network genesisspecqbft.Network - Timer roundtimer.Timer -} - -// GetSigner returns a Signer instance -func (c *Config) GetSigner() genesisspectypes.SSVSigner { - return c.Signer -} - -// GetSigningPubKey returns the public key used to sign all QBFT messages -func (c *Config) GetSigningPubKey() []byte { - return c.SigningPK -} - -// GetSignatureDomainType returns the Domain type used for signatures -func (c *Config) GetSignatureDomainType() genesisspectypes.DomainType { - return c.Domain -} - -// GetValueCheckF returns value check instance -func (c *Config) GetValueCheckF() genesisspecqbft.ProposedValueCheckF { - return c.ValueCheckF -} - -// GetProposerF returns func used to calculate proposer -func (c *Config) GetProposerF() genesisspecqbft.ProposerF { - return c.ProposerF -} - -// GetNetwork returns a p2p Network instance -func (c *Config) GetNetwork() genesisspecqbft.Network { - return c.Network -} - -// GetStorage returns a storage instance -func (c *Config) GetStorage() qbftstorage.QBFTStore { - return c.Storage -} - -// GetTimer returns round timer -func (c *Config) GetTimer() roundtimer.Timer { - return c.Timer -} diff --git a/protocol/genesis/qbft/controller/controller.go b/protocol/genesis/qbft/controller/controller.go deleted file mode 100644 index 7af60c67e7..0000000000 --- a/protocol/genesis/qbft/controller/controller.go +++ /dev/null @@ -1,250 +0,0 @@ -package controller - -import ( - "bytes" - "crypto/sha256" - "encoding/json" - "fmt" - - "github.com/pkg/errors" - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - specqbft "github.com/ssvlabs/ssv-spec/qbft" - "go.uber.org/zap" - - "github.com/ssvlabs/ssv/logging/fields" - "github.com/ssvlabs/ssv/protocol/genesis/qbft" - "github.com/ssvlabs/ssv/protocol/genesis/qbft/instance" -) - -// NewDecidedHandler handles newly saved decided messages. -// it will be called in a new goroutine to avoid concurrency issues -type NewDecidedHandler func(msg *genesisspecqbft.SignedMessage) - -// Controller is a QBFT coordinator responsible for starting and following the entire life cycle of multiple QBFT InstanceContainer -type Controller struct { - Identifier []byte - Height genesisspecqbft.Height // incremental Height for InstanceContainer - // StoredInstances stores the last HistoricalInstanceCapacity in an array for message processing purposes. - StoredInstances InstanceContainer - Share *genesisspectypes.Share - NewDecidedHandler NewDecidedHandler `json:"-"` - config qbft.IConfig - fullNode bool -} - -func NewController( - identifier []byte, - share *genesisspectypes.Share, - config qbft.IConfig, - fullNode bool, -) *Controller { - return &Controller{ - Identifier: identifier, - Height: genesisspecqbft.FirstHeight, - Share: share, - StoredInstances: make(InstanceContainer, 0, InstanceContainerDefaultCapacity), - config: config, - fullNode: fullNode, - } -} - -// StartNewInstance will start a new QBFT instance, if can't will return error -func (c *Controller) StartNewInstance(logger *zap.Logger, height genesisspecqbft.Height, value []byte) error { - - if err := c.GetConfig().GetValueCheckF()(value); err != nil { - return errors.Wrap(err, "value invalid") - } - - if height < c.Height { - return errors.New("attempting to start an instance with a past height") - } - - if c.StoredInstances.FindInstance(height) != nil { - return errors.New("instance already running") - } - - c.Height = height - - newInstance := c.addAndStoreNewInstance() - newInstance.Start(logger, value, height) - c.forceStopAllInstanceExceptCurrent() - return nil -} - -func (c *Controller) forceStopAllInstanceExceptCurrent() { - for _, i := range c.StoredInstances { - if i.State.Height != c.Height { - i.ForceStop() - } - } -} - -// ProcessMsg processes a new msg, returns decided message or error -func (c *Controller) ProcessMsg(logger *zap.Logger, msg *genesisspecqbft.SignedMessage) (*genesisspecqbft.SignedMessage, error) { - if err := c.BaseMsgValidation(msg); err != nil { - return nil, errors.Wrap(err, "invalid msg") - } - - /** - Main controller processing flow - _______________________________ - All decided msgs are processed the same, out of instance - All valid future msgs are saved in a container and can trigger highest decided futuremsg - All other msgs (not future or decided) are processed normally by an existing instance (if found) - */ - if IsDecidedMsg(c.Share, msg) { - return c.UponDecided(logger, msg) - } else if c.isFutureMessage(msg) { - return nil, fmt.Errorf("future msg from height, could not process") - } - return c.UponExistingInstanceMsg(logger, msg) -} - -func (c *Controller) UponExistingInstanceMsg(logger *zap.Logger, msg *genesisspecqbft.SignedMessage) (*genesisspecqbft.SignedMessage, error) { - inst := c.InstanceForHeight(logger, msg.Message.Height) - if inst == nil { - return nil, errors.New("instance not found") - } - - prevDecided, _ := inst.IsDecided() - - decided, _, decidedMsg, err := inst.ProcessMsg(logger, msg) - if err != nil { - return nil, errors.Wrap(err, "could not process msg") - } - - // save the highest Decided - if !decided { - return nil, nil - } - - // ProcessMsg returns a nil decidedMsg when given a non-commit message - // while the instance is decided. In this case, we have nothing new to broadcast. - if decidedMsg == nil { - return nil, nil - } - - if err := c.broadcastDecided(decidedMsg); err != nil { - // no need to fail processing instance deciding if failed to save/ broadcast - logger.Debug("❌ failed to broadcast decided message", zap.Error(err)) - } - - if prevDecided { - return nil, err - } - - return decidedMsg, nil -} - -// BaseMsgValidation returns error if msg is invalid (base validation) -func (c *Controller) BaseMsgValidation(msg *genesisspecqbft.SignedMessage) error { - // verify msg belongs to controller - if !bytes.Equal(c.Identifier, msg.Message.Identifier) { - return errors.New("message doesn't belong to Identifier") - } - - return nil -} - -func (c *Controller) InstanceForHeight(logger *zap.Logger, height genesisspecqbft.Height) *instance.Instance { - // Search in memory. - if inst := c.StoredInstances.FindInstance(height); inst != nil { - return inst - } - - // Search in storage, if full node. - if !c.fullNode { - return nil - } - storedInst, err := c.config.GetStorage().GetInstance(c.Identifier, height) - if err != nil { - logger.Debug("❗ could not load instance from storage", - fields.Height(specqbft.Height(height)), - zap.Uint64("ctrl_height", uint64(c.Height)), - zap.Error(err)) - return nil - } - if storedInst == nil { - return nil - } - inst := instance.NewInstance(c.config, c.Share, c.Identifier, storedInst.State.Height) - inst.State = storedInst.State - return inst -} - -// GetIdentifier returns QBFT Identifier, used to identify messages -func (c *Controller) GetIdentifier() []byte { - return c.Identifier -} - -// isFutureMessage returns true if message height is from a future instance. -// It takes into consideration a special case where FirstHeight didn't start but c.Height == FirstHeight (since we bump height on start instance) -func (c *Controller) isFutureMessage(msg *genesisspecqbft.SignedMessage) bool { - if c.Height == genesisspecqbft.FirstHeight && c.StoredInstances.FindInstance(c.Height) == nil { - return true - } - return msg.Message.Height > c.Height -} - -// addAndStoreNewInstance returns creates a new QBFT instance, stores it in an array and returns it -func (c *Controller) addAndStoreNewInstance() *instance.Instance { - i := instance.NewInstance(c.GetConfig(), c.Share, c.Identifier, c.Height) - c.StoredInstances.addNewInstance(i) - return i -} - -// GetRoot returns the state's deterministic root -func (c *Controller) GetRoot() ([32]byte, error) { - marshaledRoot, err := json.Marshal(c) - if err != nil { - return [32]byte{}, errors.Wrap(err, "could not encode state") - } - ret := sha256.Sum256(marshaledRoot) - return ret, nil -} - -// Encode implementation -func (c *Controller) Encode() ([]byte, error) { - return json.Marshal(c) -} - -// Decode implementation -func (c *Controller) Decode(data []byte) error { - err := json.Unmarshal(data, &c) - if err != nil { - return errors.Wrap(err, "could not decode controller") - } - - config := c.GetConfig() - for _, i := range c.StoredInstances { - if i != nil { - // TODO-spec-align changed due to instance and controller are not in same package as in spec, do we still need it for test? - i.SetConfig(config) - } - } - return nil -} - -func (c *Controller) broadcastDecided(aggregatedCommit *genesisspecqbft.SignedMessage) error { - // Broadcast Decided msg - byts, err := aggregatedCommit.Encode() - if err != nil { - return errors.Wrap(err, "could not encode decided message") - } - - msgToBroadcast := &genesisspectypes.SSVMessage{ - MsgType: genesisspectypes.SSVConsensusMsgType, - MsgID: genesisspecqbft.ControllerIdToMessageID(c.Identifier), - Data: byts, - } - if err := c.GetConfig().GetNetwork().Broadcast(msgToBroadcast); err != nil { - // We do not return error here, just Log broadcasting error. - return errors.Wrap(err, "could not broadcast decided") - } - return nil -} - -func (c *Controller) GetConfig() qbft.IConfig { - return c.config -} diff --git a/protocol/genesis/qbft/controller/controller_test.go b/protocol/genesis/qbft/controller/controller_test.go deleted file mode 100644 index c03d8514fa..0000000000 --- a/protocol/genesis/qbft/controller/controller_test.go +++ /dev/null @@ -1,91 +0,0 @@ -package controller - -import ( - "encoding/json" - "testing" - - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - spectestingutils "github.com/ssvlabs/ssv-spec-pre-cc/types/testingutils" - "github.com/stretchr/testify/require" - - "github.com/ssvlabs/ssv/logging" - "github.com/ssvlabs/ssv/protocol/genesis/qbft" - "github.com/ssvlabs/ssv/protocol/genesis/qbft/instance" - "github.com/ssvlabs/ssv/protocol/genesis/qbft/roundtimer" - "github.com/ssvlabs/ssv/protocol/genesis/types" -) - -func TestController_Marshaling(t *testing.T) { - c := qbft.TestingControllerStruct - - byts, err := c.Encode() - require.NoError(t, err) - - decoded := &Controller{ - // Since StoredInstances is an interface, it wouldn't be decoded properly. - // Therefore, we set it to NewInstanceContainer which implements json.Unmarshaler - StoredInstances: make(InstanceContainer, 0, InstanceContainerTestCapacity), - } - require.NoError(t, decoded.Decode(byts)) - - bytsDecoded, err := decoded.Encode() - require.NoError(t, err) - require.EqualValues(t, byts, bytsDecoded) -} - -func TestController_OnTimeoutWithRoundCheck(t *testing.T) { - // Initialize logger - logger := logging.TestLogger(t) - - testConfig := &qbft.Config{ - Signer: spectestingutils.NewTestingKeyManager(), - Network: spectestingutils.NewTestingNetwork(), - Timer: roundtimer.NewTestingTimer(), - } - - share := spectestingutils.TestingShare(spectestingutils.Testing4SharesSet()) - inst := instance.NewInstance( - testConfig, - share, - []byte{1, 2, 3, 4}, - genesisspecqbft.FirstHeight, - ) - - // Initialize Controller - contr := &Controller{} - - // Initialize EventMsg for the test - timeoutData := types.TimeoutData{ - Height: genesisspecqbft.FirstHeight, - Round: genesisspecqbft.FirstRound, - } - - data, err := json.Marshal(timeoutData) - require.NoError(t, err) - - msg := &types.EventMsg{ - Type: types.Timeout, - Data: data, - } - - // Simulate a scenario where the instance is at a higher round - inst.State.Round = genesisspecqbft.Round(2) - contr.StoredInstances.addNewInstance(inst) - - // Call OnTimeout and capture the error - err = contr.OnTimeout(logger, *msg) - - // Assert that the error is nil and the round did not bump - require.NoError(t, err) - require.Equal(t, genesisspecqbft.Round(2), inst.State.Round, "Round should not bump") - - // Simulate a scenario where the instance is at the same or lower round - inst.State.Round = genesisspecqbft.FirstRound - - // Call OnTimeout and capture the error - err = contr.OnTimeout(logger, *msg) - - // Assert that the error is nil and the round did bump - require.NoError(t, err) - require.Equal(t, genesisspecqbft.Round(2), inst.State.Round, "Round should bump") -} diff --git a/protocol/genesis/qbft/controller/decided.go b/protocol/genesis/qbft/controller/decided.go deleted file mode 100644 index dfc54c49a9..0000000000 --- a/protocol/genesis/qbft/controller/decided.go +++ /dev/null @@ -1,117 +0,0 @@ -package controller - -import ( - "bytes" - - "github.com/pkg/errors" - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - "go.uber.org/zap" - - "github.com/ssvlabs/ssv/protocol/genesis/qbft" - "github.com/ssvlabs/ssv/protocol/genesis/qbft/instance" -) - -// UponDecided returns decided msg if decided, nil otherwise -func (c *Controller) UponDecided(logger *zap.Logger, msg *genesisspecqbft.SignedMessage) (*genesisspecqbft.SignedMessage, error) { - if err := ValidateDecided( - c.config, - msg, - c.Share, - ); err != nil { - return nil, errors.Wrap(err, "invalid decided msg") - } - - // try to find instance - inst := c.InstanceForHeight(logger, msg.Message.Height) - prevDecided := inst != nil && inst.State.Decided - isFutureDecided := msg.Message.Height > c.Height - save := true - - if inst == nil { - i := instance.NewInstance(c.GetConfig(), c.Share, c.Identifier, msg.Message.Height) - i.State.Round = msg.Message.Round - i.State.Decided = true - i.State.DecidedValue = msg.FullData - i.State.CommitContainer.AddMsg(msg) - c.StoredInstances.addNewInstance(i) - } else if decided, _ := inst.IsDecided(); !decided { - inst.State.Decided = true - inst.State.Round = msg.Message.Round - inst.State.DecidedValue = msg.FullData - inst.State.CommitContainer.AddMsg(msg) - } else { // decide previously, add if has more signers - signers, _ := inst.State.CommitContainer.LongestUniqueSignersForRoundAndRoot(msg.Message.Round, msg.Message.Root) - if len(msg.Signers) > len(signers) { - inst.State.CommitContainer.AddMsg(msg) - } else { - save = false - } - } - - if save { - // Retrieve instance from StoredInstances (in case it was created above) - // and save it together with the decided message. - if inst := c.StoredInstances.FindInstance(msg.Message.Height); inst != nil { - logger := logger.With( - zap.Uint64("msg_height", uint64(msg.Message.Height)), - zap.Uint64("ctrl_height", uint64(c.Height)), - zap.Any("signers", msg.Signers), - ) - if err := c.SaveInstance(inst, msg); err != nil { - logger.Debug("❗failed to save instance", zap.Error(err)) - } else { - logger.Debug("💾 saved instance upon decided", zap.Error(err)) - } - } - } - - if isFutureDecided { - // bump height - c.Height = msg.Message.Height - } - if c.NewDecidedHandler != nil { - c.NewDecidedHandler(msg) - } - if !prevDecided { - return msg, nil - } - return nil, nil -} - -func ValidateDecided( - config qbft.IConfig, - signedDecided *genesisspecqbft.SignedMessage, - share *genesisspectypes.Share, -) error { - if !IsDecidedMsg(share, signedDecided) { - return errors.New("not a decided msg") - } - - if err := signedDecided.Validate(); err != nil { - return errors.Wrap(err, "invalid decided msg") - } - - if err := instance.BaseCommitValidation(config, signedDecided, signedDecided.Message.Height, share.Committee); err != nil { - return errors.Wrap(err, "invalid decided msg") - } - - if err := signedDecided.Validate(); err != nil { - return errors.Wrap(err, "invalid decided") - } - - r, err := genesisspecqbft.HashDataRoot(signedDecided.FullData) - if err != nil { - return errors.Wrap(err, "could not hash input data") - } - if !bytes.Equal(r[:], signedDecided.Message.Root[:]) { - return errors.New("H(data) != root") - } - - return nil -} - -// IsDecidedMsg returns true if signed commit has all quorum sigs -func IsDecidedMsg(share *genesisspectypes.Share, signedDecided *genesisspecqbft.SignedMessage) bool { - return share.HasQuorum(len(signedDecided.Signers)) && signedDecided.Message.MsgType == genesisspecqbft.CommitMsgType -} diff --git a/protocol/genesis/qbft/controller/highest_instance.go b/protocol/genesis/qbft/controller/highest_instance.go deleted file mode 100644 index a99a4774ed..0000000000 --- a/protocol/genesis/qbft/controller/highest_instance.go +++ /dev/null @@ -1,69 +0,0 @@ -package controller - -import ( - "github.com/pkg/errors" - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - - "github.com/ssvlabs/ssv/protocol/genesis/qbft/instance" - qbftstorage "github.com/ssvlabs/ssv/protocol/genesis/qbft/storage" -) - -func (c *Controller) LoadHighestInstance(identifier []byte) (*instance.Instance, error) { - highestInstance, err := c.getHighestInstance(identifier[:]) - if err != nil { - return nil, err - } - if highestInstance == nil { - return nil, nil - } - c.Height = highestInstance.GetHeight() - c.StoredInstances.reset() - c.StoredInstances.addNewInstance(highestInstance) - return highestInstance, nil -} - -func (c *Controller) getHighestInstance(identifier []byte) (*instance.Instance, error) { - highestInstance, err := c.config.GetStorage().GetHighestInstance(identifier) - if err != nil { - return nil, errors.Wrap(err, "could not fetch highest instance") - } - if highestInstance == nil { - return nil, nil - } - - // Compact the instance to reduce its memory footprint. - instance.Compact(highestInstance.State, highestInstance.DecidedMessage) - - i := instance.NewInstance( - c.config, - highestInstance.State.Share, - identifier, - highestInstance.State.Height, - ) - i.State = highestInstance.State - return i, nil -} - -// SaveInstance saves the given instance to the storage. -func (c *Controller) SaveInstance(i *instance.Instance, msg *genesisspecqbft.SignedMessage) error { - storedInstance := &qbftstorage.StoredInstance{ - State: i.State, - DecidedMessage: msg, - } - isHighest := msg.Message.Height >= c.Height - - // Full nodes save both highest and historical instances. - if c.fullNode { - if isHighest { - return c.config.GetStorage().SaveHighestAndHistoricalInstance(storedInstance) - } - return c.config.GetStorage().SaveInstance(storedInstance) - } - - // Light nodes only save highest instances. - if isHighest { - return c.config.GetStorage().SaveHighestInstance(storedInstance) - } - - return nil -} diff --git a/protocol/genesis/qbft/controller/timer.go b/protocol/genesis/qbft/controller/timer.go deleted file mode 100644 index 84d96c302a..0000000000 --- a/protocol/genesis/qbft/controller/timer.go +++ /dev/null @@ -1,32 +0,0 @@ -package controller - -import ( - "github.com/pkg/errors" - "go.uber.org/zap" - - "github.com/ssvlabs/ssv/protocol/genesis/types" -) - -// OnTimeout is trigger upon timeout for the given height -func (c *Controller) OnTimeout(logger *zap.Logger, msg types.EventMsg) error { - // TODO add validation - - timeoutData, err := msg.GetTimeoutData() - if err != nil { - return errors.Wrap(err, "failed to get timeout data") - } - instance := c.StoredInstances.FindInstance(timeoutData.Height) - if instance == nil { - return errors.New("instance is nil") - } - - if timeoutData.Round < instance.State.Round { - logger.Debug("timeout for old round", zap.Uint64("timeout round", uint64(timeoutData.Round)), zap.Uint64("instance round", uint64(instance.State.Round))) - return nil - } - - if decided, _ := instance.IsDecided(); decided { - return nil - } - return instance.UponRoundTimeout(logger) -} diff --git a/protocol/genesis/qbft/controller/types.go b/protocol/genesis/qbft/controller/types.go deleted file mode 100644 index 41b1cc991a..0000000000 --- a/protocol/genesis/qbft/controller/types.go +++ /dev/null @@ -1,98 +0,0 @@ -package controller - -import ( - "encoding/json" - "fmt" - "strings" - - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - - "github.com/ssvlabs/ssv/protocol/genesis/qbft/instance" -) - -const ( - // InstanceContainerDefaultCapacity is the default capacity for InstanceContainer. - InstanceContainerDefaultCapacity int = 2 - - // InstanceContainerTestCapacity is the capacity for InstanceContainer used in tests. - InstanceContainerTestCapacity int = 1024 -) - -// InstanceContainer is a fixed-capacity container for instances. -type InstanceContainer []*instance.Instance - -func (i InstanceContainer) FindInstance(height genesisspecqbft.Height) *instance.Instance { - for _, inst := range i { - if inst != nil { - if inst.GetHeight() == height { - return inst - } - } - } - return nil -} - -// addNewInstance will add the new instance at index 0, pushing all other stored InstanceContainer one index up (ejecting last one if existing) -func (i *InstanceContainer) addNewInstance(instance *instance.Instance) { - if cap(*i) == 0 { - *i = make(InstanceContainer, 0, InstanceContainerDefaultCapacity) - } - - indexToInsert := len(*i) - for index, existingInstance := range *i { - if existingInstance.GetHeight() < instance.GetHeight() { - indexToInsert = index - break - } - } - - if indexToInsert == len(*i) { - // Append the new instance, only if there's free space for it. - if len(*i) < cap(*i) { - *i = append(*i, instance) - } - } else { - if len(*i) == cap(*i) { - // Shift the instances to the right and insert the new instance. - copy((*i)[indexToInsert+1:], (*i)[indexToInsert:]) - (*i)[indexToInsert] = instance - } else { - // Shift the instances to the right and append the new instance. - *i = append(*i, nil) - copy((*i)[indexToInsert+1:], (*i)[indexToInsert:]) - (*i)[indexToInsert] = instance - } - } -} - -// reset will remove all instances from the container, perserving the underlying slice's capacity. -func (i *InstanceContainer) reset() { - *i = (*i)[:0] -} - -// String returns a human-readable representation of the instances. Useful for debugging. -func (i *InstanceContainer) String() string { - heights := make([]string, len(*i)) - for index, inst := range *i { - heights[index] = fmt.Sprint(inst.GetHeight()) - } - return fmt.Sprintf("Instances(len=%d, cap=%d, heights=(%s))", len(*i), cap(*i), strings.Join(heights, ", ")) -} - -// UnmarshalJSON implements the json.Unmarshaler interface for InstanceContainer -func (c *InstanceContainer) UnmarshalJSON(data []byte) error { - // InstanceContainer must always have correct capacity on initialization - // because addition to instance container doesn't grow beyond cap removing values that don't fit. - // Therefore, we need to initialize it properly on unmarshalling - // to allow spec tests grow StoredInstances as much as they need to. - instances := make([]*instance.Instance, 0, InstanceContainerTestCapacity) - if cap(*c) != 0 { - instances = *c - } - - if err := json.Unmarshal(data, &instances); err != nil { - return err - } - *c = instances - return nil -} diff --git a/protocol/genesis/qbft/controller/types_test.go b/protocol/genesis/qbft/controller/types_test.go deleted file mode 100644 index 79e3405842..0000000000 --- a/protocol/genesis/qbft/controller/types_test.go +++ /dev/null @@ -1,163 +0,0 @@ -package controller - -import ( - "math/rand" - "sort" - "testing" - - "github.com/ssvlabs/ssv/protocol/genesis/qbft/instance" - - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - "github.com/stretchr/testify/require" -) - -func TestInstances_FindInstance(t *testing.T) { - i := InstanceContainer{ - &instance.Instance{State: &genesisspecqbft.State{Height: 1}}, - &instance.Instance{State: &genesisspecqbft.State{Height: 2}}, - &instance.Instance{State: &genesisspecqbft.State{Height: 3}}, - } - - t.Run("find 1", func(t *testing.T) { - require.NotNil(t, i.FindInstance(1)) - }) - t.Run("find 2", func(t *testing.T) { - require.NotNil(t, i.FindInstance(2)) - }) - t.Run("find 5", func(t *testing.T) { - require.Nil(t, i.FindInstance(5)) - }) -} - -func TestInstances_AddNewInstance(t *testing.T) { - t.Run("add to full", func(t *testing.T) { - i := append( - make(InstanceContainer, 0, 5), - &instance.Instance{State: &genesisspecqbft.State{Height: 1}}, - &instance.Instance{State: &genesisspecqbft.State{Height: 2}}, - &instance.Instance{State: &genesisspecqbft.State{Height: 3}}, - &instance.Instance{State: &genesisspecqbft.State{Height: 4}}, - &instance.Instance{State: &genesisspecqbft.State{Height: 5}}, - ) - i.addNewInstance(&instance.Instance{State: &genesisspecqbft.State{Height: 6}}) - - requireHeights(t, i, 6, 1, 2, 3, 4) - }) - - t.Run("add to empty", func(t *testing.T) { - i := make(InstanceContainer, 0, 5) - i.addNewInstance(&instance.Instance{State: &genesisspecqbft.State{Height: 1}}) - - require.EqualValues(t, 1, i[0].State.Height) - require.Len(t, i, 1) - }) - - t.Run("add to semi full", func(t *testing.T) { - i := append( - make(InstanceContainer, 0, 5), - &instance.Instance{State: &genesisspecqbft.State{Height: 1}}, - &instance.Instance{State: &genesisspecqbft.State{Height: 2}}, - &instance.Instance{State: &genesisspecqbft.State{Height: 3}}, - ) - i.addNewInstance(&instance.Instance{State: &genesisspecqbft.State{Height: 4}}) - - requireHeights(t, i, 4, 1, 2, 3) - }) - - t.Run("add to full with lower height", func(t *testing.T) { - i := append( - make(InstanceContainer, 0, 5), - &instance.Instance{State: &genesisspecqbft.State{Height: 1}}, - &instance.Instance{State: &genesisspecqbft.State{Height: 2}}, - &instance.Instance{State: &genesisspecqbft.State{Height: 3}}, - &instance.Instance{State: &genesisspecqbft.State{Height: 4}}, - &instance.Instance{State: &genesisspecqbft.State{Height: 5}}, - ) - i.addNewInstance(&instance.Instance{State: &genesisspecqbft.State{Height: 0}}) - - requireHeights(t, i, 1, 2, 3, 4, 5) - }) - - t.Run("add to full with higher height", func(t *testing.T) { - i := append( - make(InstanceContainer, 0, 5), - &instance.Instance{State: &genesisspecqbft.State{Height: 1}}, - &instance.Instance{State: &genesisspecqbft.State{Height: 2}}, - &instance.Instance{State: &genesisspecqbft.State{Height: 3}}, - &instance.Instance{State: &genesisspecqbft.State{Height: 4}}, - &instance.Instance{State: &genesisspecqbft.State{Height: 5}}, - ) - i.addNewInstance(&instance.Instance{State: &genesisspecqbft.State{Height: 6}}) - - requireHeights(t, i, 6, 1, 2, 3, 4) - }) - - t.Run("add to semi-full with higher height", func(t *testing.T) { - i := append( - make(InstanceContainer, 0, 5), - &instance.Instance{State: &genesisspecqbft.State{Height: 9}}, - &instance.Instance{State: &genesisspecqbft.State{Height: 7}}, - &instance.Instance{State: &genesisspecqbft.State{Height: 5}}, - &instance.Instance{State: &genesisspecqbft.State{Height: 1}}, - ) - i.addNewInstance(&instance.Instance{State: &genesisspecqbft.State{Height: 4}}) - i.addNewInstance(&instance.Instance{State: &genesisspecqbft.State{Height: 3}}) - i.addNewInstance(&instance.Instance{State: &genesisspecqbft.State{Height: 6}}) - i.addNewInstance(&instance.Instance{State: &genesisspecqbft.State{Height: 8}}) - i.addNewInstance(&instance.Instance{State: &genesisspecqbft.State{Height: 8}}) - - requireHeights(t, i, 9, 8, 8, 7, 6) - }) - - t.Run("randoms", func(t *testing.T) { - minCap := InstanceContainerDefaultCapacity/2 + 1 - maxCap := InstanceContainerDefaultCapacity * 2 - for _, capacity := range []int{minCap, InstanceContainerDefaultCapacity, maxCap} { - // mirror is a slice of heights we've inserted so far. - // We use it to compare with the InstanceContainer. - mirror := make([]genesisspecqbft.Height, 0, capacity) - - i := make(InstanceContainer, 0, capacity) - numberOfRandomHeights := 100 - for _, height := range rand.Perm(numberOfRandomHeights) { - // Add height to InstanceContainer. - i.addNewInstance(&instance.Instance{State: &genesisspecqbft.State{Height: genesisspecqbft.Height(height)}}) - - // Add height to mirror. - mirror = append(mirror, genesisspecqbft.Height(height)) - - // Sort mirror in descending order. - sort.Slice(mirror, func(i, j int) bool { - return mirror[i] > mirror[j] - }) - - // Compare with mirror only the first (capacity) elements. - addedSoFarCap := capacity - if len(mirror) < capacity { - addedSoFarCap = len(mirror) - } - requireHeights(t, i, mirror[:addedSoFarCap]...) - } - - // Finally, a sanity check. We expect the InstanceContainer to contain exactly - // the numbers from numberOfRandomHeights-1 to numberOfRandomHeights-capacity. - expectedHeights := make([]genesisspecqbft.Height, 0, capacity) - minHeight := 0 - if capacity < numberOfRandomHeights { - minHeight = numberOfRandomHeights - capacity - } - for height := numberOfRandomHeights - 1; height >= minHeight; height-- { - expectedHeights = append(expectedHeights, genesisspecqbft.Height(height)) - } - requireHeights(t, i, expectedHeights...) - } - }) -} - -func requireHeights(t *testing.T, i []*instance.Instance, heights ...genesisspecqbft.Height) { - actualHeights := make([]genesisspecqbft.Height, 0, len(heights)) - for _, v := range i { - actualHeights = append(actualHeights, v.State.Height) - } - require.EqualValues(t, heights, actualHeights, "expected heights to match") -} diff --git a/protocol/genesis/qbft/instance/commit.go b/protocol/genesis/qbft/instance/commit.go deleted file mode 100644 index abecd21069..0000000000 --- a/protocol/genesis/qbft/instance/commit.go +++ /dev/null @@ -1,176 +0,0 @@ -package instance - -import ( - "bytes" - "sort" - - "github.com/pkg/errors" - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - specqbft "github.com/ssvlabs/ssv-spec/qbft" - "go.uber.org/zap" - - "github.com/ssvlabs/ssv/logging/fields" - "github.com/ssvlabs/ssv/protocol/genesis/qbft" - "github.com/ssvlabs/ssv/protocol/genesis/types" -) - -// UponCommit returns true if a quorum of commit messages was received. -// Assumes commit message is valid! -func (i *Instance) UponCommit(logger *zap.Logger, signedCommit *genesisspecqbft.SignedMessage, commitMsgContainer *genesisspecqbft.MsgContainer) (bool, []byte, *genesisspecqbft.SignedMessage, error) { - addMsg, err := commitMsgContainer.AddFirstMsgForSignerAndRound(signedCommit) - if err != nil { - return false, nil, nil, errors.Wrap(err, "could not add commit msg to container") - } - if !addMsg { - return false, nil, nil, nil // UponCommit was already called - } - - logger.Debug("📬 got commit message", - fields.Round(specqbft.Round(i.State.Round)), - zap.Any("commit_signers", signedCommit.Signers), - fields.Root(signedCommit.Message.Root)) - - // calculate commit quorum and act upon it - quorum, commitMsgs, err := commitQuorumForRoundRoot(i.State, commitMsgContainer, signedCommit.Message.Root, signedCommit.Message.Round) - if err != nil { - return false, nil, nil, errors.Wrap(err, "could not calculate commit quorum") - } - - if quorum { - fullData := i.State.ProposalAcceptedForCurrentRound.FullData /* must have value there, checked on validateCommit */ - - agg, err := aggregateCommitMsgs(commitMsgs, fullData) - if err != nil { - return false, nil, nil, errors.Wrap(err, "could not aggregate commit msgs") - } - - logger.Debug("🎯 got commit quorum", - fields.Round(specqbft.Round(i.State.Round)), - zap.Any("agg_signers", agg.Signers), - fields.Root(signedCommit.Message.Root)) - - i.metrics.EndStageCommit() - - return true, fullData, agg, nil - } - - return false, nil, nil, nil -} - -// returns true if there is a quorum for the current round for this provided value -func commitQuorumForRoundRoot(state *genesisspecqbft.State, commitMsgContainer *genesisspecqbft.MsgContainer, root [32]byte, round genesisspecqbft.Round) (bool, []*genesisspecqbft.SignedMessage, error) { - signers, msgs := commitMsgContainer.LongestUniqueSignersForRoundAndRoot(round, root) - return state.Share.HasQuorum(len(signers)), msgs, nil -} - -func aggregateCommitMsgs(msgs []*genesisspecqbft.SignedMessage, fullData []byte) (*genesisspecqbft.SignedMessage, error) { - if len(msgs) == 0 { - return nil, errors.New("can't aggregate zero commit msgs") - } - - var ret *genesisspecqbft.SignedMessage - for _, m := range msgs { - if ret == nil { - ret = m.DeepCopy() - } else { - if err := ret.Aggregate(m); err != nil { - return nil, errors.Wrap(err, "could not aggregate commit msg") - } - } - } - ret.FullData = fullData - - // TODO: REWRITE THIS! - sort.Slice(ret.Signers, func(i, j int) bool { - return ret.Signers[i] < ret.Signers[j] - }) - - return ret, nil -} - -// CreateCommit -/** -Commit( - signCommit( - UnsignedCommit( - |current.blockchain|, - current.round, - signHash(hashBlockForCommitSeal(proposedBlock), current.id), - digest(proposedBlock)), - current.id - ) - ); -*/ -func CreateCommit(state *genesisspecqbft.State, config qbft.IConfig, root [32]byte) (*genesisspecqbft.SignedMessage, error) { - msg := &genesisspecqbft.Message{ - MsgType: genesisspecqbft.CommitMsgType, - Height: state.Height, - Round: state.Round, - Identifier: state.ID, - - Root: root, - } - sig, err := config.GetSigner().SignRoot(msg, genesisspectypes.QBFTSignatureType, state.Share.SharePubKey) - if err != nil { - return nil, errors.Wrap(err, "failed signing commit msg") - } - - signedMsg := &genesisspecqbft.SignedMessage{ - Signature: sig, - Signers: []genesisspectypes.OperatorID{state.Share.OperatorID}, - Message: *msg, - } - return signedMsg, nil -} - -func BaseCommitValidation( - config qbft.IConfig, - signedCommit *genesisspecqbft.SignedMessage, - height genesisspecqbft.Height, - operators []*genesisspectypes.Operator, -) error { - if signedCommit.Message.MsgType != genesisspecqbft.CommitMsgType { - return errors.New("commit msg type is wrong") - } - if signedCommit.Message.Height != height { - return errors.New("wrong msg height") - } - - if err := signedCommit.Validate(); err != nil { - return errors.Wrap(err, "signed commit invalid") - } - - if err := types.VerifyByOperators(signedCommit.Signature, signedCommit, config.GetSignatureDomainType(), genesisspectypes.QBFTSignatureType, operators); err != nil { - return errors.Wrap(err, "msg signature invalid") - } - - return nil -} - -func validateCommit( - config qbft.IConfig, - signedCommit *genesisspecqbft.SignedMessage, - height genesisspecqbft.Height, - round genesisspecqbft.Round, - proposedMsg *genesisspecqbft.SignedMessage, - operators []*genesisspectypes.Operator, -) error { - if err := BaseCommitValidation(config, signedCommit, height, operators); err != nil { - return err - } - - if len(signedCommit.Signers) != 1 { - return errors.New("msg allows 1 signer") - } - - if signedCommit.Message.Round != round { - return errors.New("wrong msg round") - } - - if !bytes.Equal(proposedMsg.Message.Root[:], signedCommit.Message.Root[:]) { - return errors.New("proposed data mistmatch") - } - - return nil -} diff --git a/protocol/genesis/qbft/instance/compact.go b/protocol/genesis/qbft/instance/compact.go deleted file mode 100644 index 60a1fe5c7d..0000000000 --- a/protocol/genesis/qbft/instance/compact.go +++ /dev/null @@ -1,99 +0,0 @@ -package instance - -import ( - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" -) - -// Compact trims the given qbft.State down to the minimum required -// for consensus to proceed. -// -// Compact always discards message from previous rounds. -// Compact discards all non-commit messages, only if the given state is decided. -// -// This helps reduce the state's memory footprint. -func Compact(state *genesisspecqbft.State, decidedMessage *genesisspecqbft.SignedMessage) { - compact(state, decidedMessage, compactContainerEdit) -} - -// CompactCopy returns a compacted copy of the given qbft.State. -// CompactCopy is guaranteed to not modify the given state, but the returned state may be modified -// when the given state is modified. -// -// TODO: this is a temporary solution to not break spec-tests. Revert this once spec is aligned. -// -// See Compact for more details. -func CompactCopy(state *genesisspecqbft.State, decidedMessage *genesisspecqbft.SignedMessage) *genesisspecqbft.State { - stateCopy := *state - compact(&stateCopy, decidedMessage, compactContainerCopy) - return &stateCopy -} - -func compact(state *genesisspecqbft.State, decidedMessage *genesisspecqbft.SignedMessage, compactContainer compactContainerFunc) { - state.ProposeContainer = compactContainer(state.ProposeContainer, state.Round, state.Decided) - state.PrepareContainer = compactContainer(state.PrepareContainer, state.LastPreparedRound, state.Decided) - state.RoundChangeContainer = compactContainer(state.RoundChangeContainer, state.Round, state.Decided) - state.CommitContainer = compactContainer(state.CommitContainer, state.Round, false) - - // TODO: disabled for now as we depend on the commit messages to check for - // whether we need to save an incoming decided message or not (see UponDecided). - // - // // Only discard commit messages if the whole committee has signed, - // // otherwise just trim down to the current round and future rounds. - // var wholeCommitteeDecided bool - // if state.Share == nil { - // // Share may be missing in tests. - // } else { - // var signers []genesisspectypes.OperatorID - // if decidedMessage != nil { - // signers = decidedMessage.Signers - // } else if state.Decided && len(state.CommitContainer.Msgs) >= len(state.Share.Committee) { - // signers, _ = state.CommitContainer.LongestUniqueSignersForRoundAndValue(state.Round, state.DecidedValue) - // } - // wholeCommitteeDecided = len(signers) == len(state.Share.Committee) - // } - // state.CommitContainer = compactContainer(state.CommitContainer, state.Round, wholeCommitteeDecided) -} - -type compactContainerFunc func(container *genesisspecqbft.MsgContainer, currentRound genesisspecqbft.Round, clear bool) *genesisspecqbft.MsgContainer - -func compactContainerEdit(container *genesisspecqbft.MsgContainer, currentRound genesisspecqbft.Round, clear bool) *genesisspecqbft.MsgContainer { - switch { - case container == nil || len(container.Msgs) == 0: - // Empty already. - case clear: - // Discard all messages. - container.Msgs = map[genesisspecqbft.Round][]*genesisspecqbft.SignedMessage{} - default: - // Trim down to the current and future rounds. - for r := range container.Msgs { - if r < currentRound { - delete(container.Msgs, r) - } - } - } - return container -} - -func compactContainerCopy(container *genesisspecqbft.MsgContainer, currentRound genesisspecqbft.Round, clear bool) *genesisspecqbft.MsgContainer { - switch { - case container == nil || len(container.Msgs) == 0: - // Empty already. - return container - case clear: - // Discard all messages. - return &genesisspecqbft.MsgContainer{ - Msgs: map[genesisspecqbft.Round][]*genesisspecqbft.SignedMessage{}, - } - default: - // Trim down to the current and future rounds. - compact := genesisspecqbft.MsgContainer{ - Msgs: map[genesisspecqbft.Round][]*genesisspecqbft.SignedMessage{}, - } - for r, msgs := range container.Msgs { - if r >= currentRound { - compact.Msgs[r] = msgs - } - } - return &compact - } -} diff --git a/protocol/genesis/qbft/instance/compact_test.go b/protocol/genesis/qbft/instance/compact_test.go deleted file mode 100644 index 683205a9b5..0000000000 --- a/protocol/genesis/qbft/instance/compact_test.go +++ /dev/null @@ -1,181 +0,0 @@ -package instance - -import ( - "testing" - - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - "github.com/stretchr/testify/require" -) - -var compactTests = []struct { - name string - inputState *genesisspecqbft.State - inputMsg *genesisspecqbft.SignedMessage - expected *genesisspecqbft.State // if nil, expected to be equal to input -}{ - { - name: "empty", - inputState: &genesisspecqbft.State{}, - expected: nil, - }, - { - name: "empty but not nil", - inputState: &genesisspecqbft.State{ - Round: 1, - ProposeContainer: &genesisspecqbft.MsgContainer{}, - PrepareContainer: &genesisspecqbft.MsgContainer{}, - CommitContainer: &genesisspecqbft.MsgContainer{}, - RoundChangeContainer: &genesisspecqbft.MsgContainer{}, - }, - expected: nil, - }, - { - name: "nothing to compact", - inputState: &genesisspecqbft.State{ - Round: 1, - ProposeContainer: mockContainer(1, 2), - PrepareContainer: mockContainer(1, 2), - CommitContainer: mockContainer(1, 2), - RoundChangeContainer: mockContainer(1, 2), - }, - expected: nil, - }, - { - name: "compact non-decided with previous rounds", - inputState: &genesisspecqbft.State{ - Round: 2, - LastPreparedRound: 2, - ProposeContainer: mockContainer(1, 2), - PrepareContainer: mockContainer(1, 2), - CommitContainer: mockContainer(1, 2), - RoundChangeContainer: mockContainer(1, 2), - }, - expected: &genesisspecqbft.State{ - Round: 2, - LastPreparedRound: 2, - ProposeContainer: mockContainer(2), - PrepareContainer: mockContainer(2), - CommitContainer: mockContainer(2), - RoundChangeContainer: mockContainer(2), - }, - }, - { - name: "compact non-decided with previous rounds except for prepared", - inputState: &genesisspecqbft.State{ - Round: 2, - LastPreparedRound: 1, - ProposeContainer: mockContainer(1, 2), - PrepareContainer: mockContainer(1, 2), - CommitContainer: mockContainer(1, 2), - RoundChangeContainer: mockContainer(1, 2), - }, - expected: &genesisspecqbft.State{ - Round: 2, - LastPreparedRound: 1, - ProposeContainer: mockContainer(2), - PrepareContainer: mockContainer(1, 2), - CommitContainer: mockContainer(2), - RoundChangeContainer: mockContainer(2), - }, - }, - { - name: "compact quorum decided with previous rounds", - inputState: &genesisspecqbft.State{ - Round: 3, - LastPreparedRound: 3, - Decided: true, - Share: &genesisspectypes.Share{ - Committee: make([]*genesisspectypes.Operator, 4), - }, - ProposeContainer: mockContainer(1, 2, 3, 4), - PrepareContainer: mockContainer(1, 2, 3, 4), - CommitContainer: mockContainer(1, 2, 3, 4), - RoundChangeContainer: mockContainer(1, 2, 3, 4), - }, - inputMsg: &genesisspecqbft.SignedMessage{ - Signers: []genesisspectypes.OperatorID{1, 2, 3}, - }, - expected: &genesisspecqbft.State{ - Round: 3, - LastPreparedRound: 3, - Decided: true, - Share: &genesisspectypes.Share{ - Committee: make([]*genesisspectypes.Operator, 4), - }, - ProposeContainer: mockContainer(), - PrepareContainer: mockContainer(), - CommitContainer: mockContainer(3, 4), - RoundChangeContainer: mockContainer(), - }, - }, - { - name: "compact whole committee decided with previous rounds", - inputState: &genesisspecqbft.State{ - Round: 2, - LastPreparedRound: 2, - Decided: true, - Share: &genesisspectypes.Share{ - Committee: make([]*genesisspectypes.Operator, 4), - }, - ProposeContainer: mockContainer(1, 2, 3, 4), - PrepareContainer: mockContainer(1, 2, 3, 4), - CommitContainer: mockContainer(1, 2, 3, 4), - RoundChangeContainer: mockContainer(1, 2, 3, 4), - }, - inputMsg: &genesisspecqbft.SignedMessage{ - Signers: []genesisspectypes.OperatorID{1, 2, 3, 4}, - }, - expected: &genesisspecqbft.State{ - Round: 2, - LastPreparedRound: 2, - Decided: true, - Share: &genesisspectypes.Share{ - Committee: make([]*genesisspectypes.Operator, 4), - }, - ProposeContainer: mockContainer(), - PrepareContainer: mockContainer(), - CommitContainer: mockContainer(2, 3, 4), - RoundChangeContainer: mockContainer(), - }, - }, -} - -func TestCompact(t *testing.T) { - for _, tt := range compactTests { - t.Run(tt.name, func(t *testing.T) { - inputStateBefore, err := tt.inputState.Encode() - require.NoError(t, err) - - if tt.expected == nil { - tt.expected = &genesisspecqbft.State{} - require.NoError(t, tt.expected.Decode(inputStateBefore)) - } - - // Test CompactCopy. - stateCopy := CompactCopy(tt.inputState, tt.inputMsg) - require.Equal(t, tt.expected, stateCopy) - - // Verify that input state was not modified by CompactCopy. - inputStateAfter, err := tt.inputState.Encode() - require.NoError(t, err) - require.Equal(t, inputStateBefore, inputStateAfter) - - // Test Compact. - Compact(tt.inputState, tt.inputMsg) - require.Equal(t, tt.expected, tt.inputState) - }) - } -} - -func mockContainer(rounds ...genesisspecqbft.Round) *genesisspecqbft.MsgContainer { - container := genesisspecqbft.NewMsgContainer() - for _, round := range rounds { - container.AddMsg(&genesisspecqbft.SignedMessage{ - Message: genesisspecqbft.Message{ - Round: round, - }, - }) - } - return container -} diff --git a/protocol/genesis/qbft/instance/instance.go b/protocol/genesis/qbft/instance/instance.go deleted file mode 100644 index 18c0f5917f..0000000000 --- a/protocol/genesis/qbft/instance/instance.go +++ /dev/null @@ -1,259 +0,0 @@ -package instance - -import ( - "encoding/json" - "sync" - - "github.com/ssvlabs/ssv/logging/fields" - - "github.com/pkg/errors" - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - specqbft "github.com/ssvlabs/ssv-spec/qbft" - "go.uber.org/zap" - - "github.com/ssvlabs/ssv/protocol/genesis/qbft" -) - -// Instance is a single QBFT instance that starts with a Start call (including a value). -// Every new msg the ProcessMsg function needs to be called -type Instance struct { - State *genesisspecqbft.State - config qbft.IConfig - - processMsgF *genesisspectypes.ThreadSafeF - startOnce sync.Once - - forceStop bool - StartValue []byte - - metrics *metrics -} - -func NewInstance( - config qbft.IConfig, - share *genesisspectypes.Share, - identifier []byte, - height genesisspecqbft.Height, -) *Instance { - msgId := genesisspectypes.MessageIDFromBytes(identifier) - return &Instance{ - State: &genesisspecqbft.State{ - Share: share, - ID: identifier, - Round: genesisspecqbft.FirstRound, - Height: height, - LastPreparedRound: genesisspecqbft.NoRound, - ProposeContainer: genesisspecqbft.NewMsgContainer(), - PrepareContainer: genesisspecqbft.NewMsgContainer(), - CommitContainer: genesisspecqbft.NewMsgContainer(), - RoundChangeContainer: genesisspecqbft.NewMsgContainer(), - }, - config: config, - processMsgF: genesisspectypes.NewThreadSafeF(), - metrics: newMetrics(msgId), - } -} - -func (i *Instance) ForceStop() { - i.forceStop = true -} - -// Start is an interface implementation -func (i *Instance) Start(logger *zap.Logger, value []byte, height genesisspecqbft.Height) { - i.startOnce.Do(func() { - i.StartValue = value - i.bumpToRound(genesisspecqbft.FirstRound) - i.State.Height = height - i.metrics.StartStage() - - i.config.GetTimer().TimeoutForRound(height, genesisspecqbft.FirstRound) - - logger = logger.With( - fields.Round(specqbft.Round(i.State.Round)), - fields.Height(specqbft.Height(i.State.Height))) - - proposerID := proposer(i.State, i.GetConfig(), genesisspecqbft.FirstRound) - logger.Debug("ℹ️ starting QBFT instance", zap.Uint64("leader", proposerID)) - - // propose if this node is the proposer - if proposerID == i.State.Share.OperatorID { - proposal, err := CreateProposal(i.State, i.config, i.StartValue, nil, nil) - // nolint - if err != nil { - logger.Warn("❗ failed to create proposal", zap.Error(err)) - // TODO align spec to add else to avoid broadcast errored proposal - } else { - // nolint - logger = logger.With(fields.Root(proposal.Message.Root)) - logger.Debug("📢 leader broadcasting proposal message") - if err := i.Broadcast(logger, proposal); err != nil { - logger.Warn("❌ failed to broadcast proposal", zap.Error(err)) - } - } - } - }) -} - -func (i *Instance) Broadcast(logger *zap.Logger, msg *genesisspecqbft.SignedMessage) error { - if !i.CanProcessMessages() { - return errors.New("instance stopped processing messages") - } - byts, err := msg.Encode() - if err != nil { - return errors.Wrap(err, "could not encode message") - } - - msgID := genesisspectypes.MessageID{} - copy(msgID[:], msg.Message.Identifier) - - msgToBroadcast := &genesisspectypes.SSVMessage{ - MsgType: genesisspectypes.SSVConsensusMsgType, - MsgID: msgID, - Data: byts, - } - return i.config.GetNetwork().Broadcast(msgToBroadcast) -} - -func allSigners(all []*genesisspecqbft.SignedMessage) []genesisspectypes.OperatorID { - signers := make([]genesisspectypes.OperatorID, 0, len(all)) - for _, m := range all { - signers = append(signers, m.Signers...) - } - return signers -} - -// ProcessMsg processes a new QBFT msg, returns non nil error on msg processing error -func (i *Instance) ProcessMsg(logger *zap.Logger, msg *genesisspecqbft.SignedMessage) (decided bool, decidedValue []byte, aggregatedCommit *genesisspecqbft.SignedMessage, err error) { - if !i.CanProcessMessages() { - return false, nil, nil, errors.New("instance stopped processing messages") - } - - if err := i.BaseMsgValidation(msg); err != nil { - return false, nil, nil, errors.Wrap(err, "invalid signed message") - } - - res := i.processMsgF.Run(func() interface{} { - switch msg.Message.MsgType { - case genesisspecqbft.ProposalMsgType: - return i.uponProposal(logger, msg, i.State.ProposeContainer) - case genesisspecqbft.PrepareMsgType: - return i.uponPrepare(logger, msg, i.State.PrepareContainer) - case genesisspecqbft.CommitMsgType: - decided, decidedValue, aggregatedCommit, err = i.UponCommit(logger, msg, i.State.CommitContainer) - if decided { - i.State.Decided = decided - i.State.DecidedValue = decidedValue - } - return err - case genesisspecqbft.RoundChangeMsgType: - return i.uponRoundChange(logger, i.StartValue, msg, i.State.RoundChangeContainer, i.config.GetValueCheckF()) - default: - return errors.New("signed message type not supported") - } - }) - if res != nil { - return false, nil, nil, res.(error) - } - return i.State.Decided, i.State.DecidedValue, aggregatedCommit, nil -} - -func (i *Instance) BaseMsgValidation(msg *genesisspecqbft.SignedMessage) error { - if err := msg.Validate(); err != nil { - return errors.Wrap(err, "invalid signed message") - } - - if msg.Message.Round < i.State.Round { - return errors.New("past round") - } - - switch msg.Message.MsgType { - case genesisspecqbft.ProposalMsgType: - return isValidProposal( - i.State, - i.config, - msg, - i.config.GetValueCheckF(), - i.State.Share.Committee, - ) - case genesisspecqbft.PrepareMsgType: - proposedMsg := i.State.ProposalAcceptedForCurrentRound - if proposedMsg == nil { - return errors.New("did not receive proposal for this round") - } - return validSignedPrepareForHeightRoundAndRoot( - i.config, - msg, - i.State.Height, - i.State.Round, - proposedMsg.Message.Root, - i.State.Share.Committee, - ) - case genesisspecqbft.CommitMsgType: - proposedMsg := i.State.ProposalAcceptedForCurrentRound - if proposedMsg == nil { - return errors.New("did not receive proposal for this round") - } - return validateCommit( - i.config, - msg, - i.State.Height, - i.State.Round, - i.State.ProposalAcceptedForCurrentRound, - i.State.Share.Committee, - ) - case genesisspecqbft.RoundChangeMsgType: - return validRoundChangeForData(i.State, i.config, msg, i.State.Height, msg.Message.Round, msg.FullData) - default: - return errors.New("signed message type not supported") - } -} - -// IsDecided interface implementation -func (i *Instance) IsDecided() (bool, []byte) { - if state := i.State; state != nil { - return state.Decided, state.DecidedValue - } - return false, nil -} - -// GetConfig returns the instance config -func (i *Instance) GetConfig() qbft.IConfig { - return i.config -} - -// SetConfig returns the instance config -func (i *Instance) SetConfig(config qbft.IConfig) { - i.config = config -} - -// GetHeight interface implementation -func (i *Instance) GetHeight() genesisspecqbft.Height { - return i.State.Height -} - -// GetRoot returns the state's deterministic root -func (i *Instance) GetRoot() ([32]byte, error) { - return i.State.GetRoot() -} - -// Encode implementation -func (i *Instance) Encode() ([]byte, error) { - return json.Marshal(i) -} - -// Decode implementation -func (i *Instance) Decode(data []byte) error { - return json.Unmarshal(data, &i) -} - -// bumpToRound sets round and sends current round metrics. -func (i *Instance) bumpToRound(round genesisspecqbft.Round) { - i.State.Round = round - i.metrics.SetRound(round) -} - -// CanProcessMessages will return true if instance can process messages -func (i *Instance) CanProcessMessages() bool { - return !i.forceStop && uint64(i.State.Round) < CutoffRound -} diff --git a/protocol/genesis/qbft/instance/instance_test.go b/protocol/genesis/qbft/instance/instance_test.go deleted file mode 100644 index 922585d4d6..0000000000 --- a/protocol/genesis/qbft/instance/instance_test.go +++ /dev/null @@ -1,98 +0,0 @@ -package instance - -import ( - "testing" - - spec "github.com/attestantio/go-eth2-client/spec/phase0" - "github.com/herumi/bls-eth-go-binary/bls" - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - "github.com/ssvlabs/ssv-spec-pre-cc/types/testingutils" - "github.com/stretchr/testify/require" -) - -func TestInstance_Marshaling(t *testing.T) { - var TestingMessage = &genesisspecqbft.Message{ - MsgType: genesisspecqbft.ProposalMsgType, - Height: genesisspecqbft.FirstHeight, - Round: genesisspecqbft.FirstRound, - Identifier: []byte{1, 2, 3, 4}, - Root: testingutils.TestingQBFTRootData, - } - TestingSK := func() *bls.SecretKey { - genesisspectypes.InitBLS() - ret := &bls.SecretKey{} - ret.SetByCSPRNG() - return ret - }() - testingSignedMsg := func() *genesisspecqbft.SignedMessage { - return testingutils.SignQBFTMsg(TestingSK, 1, TestingMessage) - }() - testingValidatorPK := spec.BLSPubKey{1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4} - testingShare := &genesisspectypes.Share{ - OperatorID: 1, - ValidatorPubKey: testingValidatorPK[:], - SharePubKey: TestingSK.GetPublicKey().Serialize(), - DomainType: genesisspectypes.PrimusTestnet, - Quorum: 3, - PartialQuorum: 2, - Committee: []*genesisspectypes.Operator{ - { - OperatorID: 1, - PubKey: TestingSK.GetPublicKey().Serialize(), - }, - }, - } - i := &genesisspecqbft.Instance{ - State: &genesisspecqbft.State{ - Share: testingShare, - ID: []byte{1, 2, 3, 4}, - Round: 1, - Height: 1, - LastPreparedRound: 1, - LastPreparedValue: []byte{1, 2, 3, 4}, - ProposalAcceptedForCurrentRound: testingSignedMsg, - Decided: false, - DecidedValue: []byte{1, 2, 3, 4}, - - ProposeContainer: &genesisspecqbft.MsgContainer{ - Msgs: map[genesisspecqbft.Round][]*genesisspecqbft.SignedMessage{ - 1: { - testingSignedMsg, - }, - }, - }, - PrepareContainer: &genesisspecqbft.MsgContainer{ - Msgs: map[genesisspecqbft.Round][]*genesisspecqbft.SignedMessage{ - 1: { - testingSignedMsg, - }, - }, - }, - CommitContainer: &genesisspecqbft.MsgContainer{ - Msgs: map[genesisspecqbft.Round][]*genesisspecqbft.SignedMessage{ - 1: { - testingSignedMsg, - }, - }, - }, - RoundChangeContainer: &genesisspecqbft.MsgContainer{ - Msgs: map[genesisspecqbft.Round][]*genesisspecqbft.SignedMessage{ - 1: { - testingSignedMsg, - }, - }, - }, - }, - } - - byts, err := i.Encode() - require.NoError(t, err) - - decoded := &genesisspecqbft.Instance{} - require.NoError(t, decoded.Decode(byts)) - - bytsDecoded, err := decoded.Encode() - require.NoError(t, err) - require.EqualValues(t, byts, bytsDecoded) -} diff --git a/protocol/genesis/qbft/instance/marshalutils.go b/protocol/genesis/qbft/instance/marshalutils.go deleted file mode 100644 index ba76e75453..0000000000 --- a/protocol/genesis/qbft/instance/marshalutils.go +++ /dev/null @@ -1,47 +0,0 @@ -package instance - -import "encoding/json" - -/////////////////////// JSON Marshalling for Tests /////////////////////// - -// region: JSON Marshalling for Instance - -// MarshalJSON is a custom JSON marshaller for Instance -func (i *Instance) MarshalJSON() ([]byte, error) { - type Alias Instance - if i.forceStop { - return json.Marshal(&struct { - ForceStop bool `json:"forceStop"` - *Alias - }{ - ForceStop: i.forceStop, - Alias: (*Alias)(i), - }) - } else { - return json.Marshal(&struct { - *Alias - }{ - Alias: (*Alias)(i), - }) - } -} - -// UnmarshalJSON is a custom JSON unmarshaller for Instance -func (i *Instance) UnmarshalJSON(data []byte) error { - type Alias Instance - aux := &struct { - ForceStop *bool `json:"forceStop,omitempty"` - *Alias - }{ - Alias: (*Alias)(i), - } - if err := json.Unmarshal(data, &aux); err != nil { - return err - } - if aux.ForceStop != nil { - i.forceStop = *aux.ForceStop - } - return nil -} - -// endregion: JSON Marshalling for Instance diff --git a/protocol/genesis/qbft/instance/metrics.go b/protocol/genesis/qbft/instance/metrics.go deleted file mode 100644 index ba090d094f..0000000000 --- a/protocol/genesis/qbft/instance/metrics.go +++ /dev/null @@ -1,76 +0,0 @@ -package instance - -import ( - "time" - - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - "go.uber.org/zap" -) - -var ( - metricsStageDuration = promauto.NewHistogramVec(prometheus.HistogramOpts{ - Name: "genesis::ssv_validator_instance_stage_duration_seconds", - Help: "Instance stage duration (seconds)", - Buckets: []float64{0.02, 0.05, 0.1, 0.2, 0.5, 1, 1.5, 2, 5}, - }, []string{"stage"}) - metricsRound = promauto.NewGaugeVec(prometheus.GaugeOpts{ - Name: "genesis::ssv_qbft_instance_round", - Help: "QBFT instance round", - }, []string{"roleType"}) -) - -func init() { - allMetrics := []prometheus.Collector{ - metricsStageDuration, - metricsRound, - } - logger := zap.L() - for _, c := range allMetrics { - if err := prometheus.Register(c); err != nil { - logger.Debug("could not register prometheus collector") - } - } -} - -type metrics struct { - StageStart time.Time - proposalDuration prometheus.Observer - prepareDuration prometheus.Observer - commitDuration prometheus.Observer - round prometheus.Gauge -} - -func newMetrics(msgID genesisspectypes.MessageID) *metrics { - return &metrics{ - proposalDuration: metricsStageDuration.WithLabelValues("proposal"), - prepareDuration: metricsStageDuration.WithLabelValues("prepare"), - commitDuration: metricsStageDuration.WithLabelValues("commit"), - round: metricsRound.WithLabelValues(msgID.GetRoleType().String()), - } -} - -func (m *metrics) StartStage() { - m.StageStart = time.Now() -} - -func (m *metrics) EndStageProposal() { - m.proposalDuration.Observe(time.Since(m.StageStart).Seconds()) - m.StageStart = time.Now() -} - -func (m *metrics) EndStagePrepare() { - m.prepareDuration.Observe(time.Since(m.StageStart).Seconds()) - m.StageStart = time.Now() -} - -func (m *metrics) EndStageCommit() { - m.commitDuration.Observe(time.Since(m.StageStart).Seconds()) - m.StageStart = time.Now() -} - -func (m *metrics) SetRound(round genesisspecqbft.Round) { - m.round.Set(float64(round)) -} diff --git a/protocol/genesis/qbft/instance/prepare.go b/protocol/genesis/qbft/instance/prepare.go deleted file mode 100644 index 54329a63f9..0000000000 --- a/protocol/genesis/qbft/instance/prepare.go +++ /dev/null @@ -1,199 +0,0 @@ -package instance - -import ( - "bytes" - - "github.com/pkg/errors" - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - specqbft "github.com/ssvlabs/ssv-spec/qbft" - "go.uber.org/zap" - - "github.com/ssvlabs/ssv/logging/fields" - "github.com/ssvlabs/ssv/protocol/genesis/qbft" - "github.com/ssvlabs/ssv/protocol/genesis/types" -) - -// uponPrepare process prepare message -// Assumes prepare message is valid! -func (i *Instance) uponPrepare(logger *zap.Logger, signedPrepare *genesisspecqbft.SignedMessage, prepareMsgContainer *genesisspecqbft.MsgContainer) error { - hasQuorumBefore := genesisspecqbft.HasQuorum(i.State.Share, prepareMsgContainer.MessagesForRound(i.State.Round)) - - addedMsg, err := prepareMsgContainer.AddFirstMsgForSignerAndRound(signedPrepare) - if err != nil { - return errors.Wrap(err, "could not add prepare msg to container") - } - if !addedMsg { - return nil // uponPrepare was already called - } - - logger.Debug("📬 got prepare message", - fields.Round(specqbft.Round(i.State.Round)), - zap.Any("prepare_signers", signedPrepare.Signers), - fields.Root(signedPrepare.Message.Root)) - - if hasQuorumBefore { - return nil // already moved to commit stage - } - - if !genesisspecqbft.HasQuorum(i.State.Share, prepareMsgContainer.MessagesForRound(i.State.Round)) { - return nil // no quorum yet - } - - proposedRoot := i.State.ProposalAcceptedForCurrentRound.Message.Root - - i.State.LastPreparedValue = i.State.ProposalAcceptedForCurrentRound.FullData - i.State.LastPreparedRound = i.State.Round - - i.metrics.EndStagePrepare() - - logger.Debug("🎯 got prepare quorum", - fields.Round(specqbft.Round(i.State.Round)), - zap.Any("prepare_signers", allSigners(prepareMsgContainer.MessagesForRound(i.State.Round))), - fields.Root(proposedRoot)) - - commitMsg, err := CreateCommit(i.State, i.config, proposedRoot) - if err != nil { - return errors.Wrap(err, "could not create commit msg") - } - - logger.Debug("📢 broadcasting commit message", - fields.Round(specqbft.Round(i.State.Round)), - zap.Any("commit_signers", commitMsg.Signers), - fields.Root(commitMsg.Message.Root)) - - if err := i.Broadcast(logger, commitMsg); err != nil { - return errors.Wrap(err, "failed to broadcast commit message") - } - - return nil -} - -// getRoundChangeJustification returns the round change justification for the current round. -// The justification is a quorum of signed prepare messages that agree on state.LastPreparedValue -func getRoundChangeJustification(state *genesisspecqbft.State, config qbft.IConfig, prepareMsgContainer *genesisspecqbft.MsgContainer) ([]*genesisspecqbft.SignedMessage, error) { - if state.LastPreparedValue == nil { - return nil, nil - } - - r, err := genesisspecqbft.HashDataRoot(state.LastPreparedValue) - if err != nil { - return nil, errors.Wrap(err, "could not hash input data") - } - - prepareMsgs := prepareMsgContainer.MessagesForRound(state.LastPreparedRound) - ret := make([]*genesisspecqbft.SignedMessage, 0) - for _, msg := range prepareMsgs { - if err := validSignedPrepareForHeightRoundAndRoot( - config, - msg, - state.Height, - state.LastPreparedRound, - r, - state.Share.Committee, - ); err == nil { - ret = append(ret, msg) - } - } - - if !genesisspecqbft.HasQuorum(state.Share, ret) { - return nil, nil - } - - return ret, nil -} - -// validPreparesForHeightRoundAndValue returns an aggregated prepare msg for a specific Height and round -// func validPreparesForHeightRoundAndValue( -// config IConfig, -// prepareMessages []*SignedMessage, -// height Height, -// round Round, -// value []byte, -// operators []*types.Operator) *SignedMessage { -// var aggregatedPrepareMsg *SignedMessage -// for _, signedMsg := range prepareMessages { -// if err := validSignedPrepareForHeightRoundAndValue(config, signedMsg, height, round, value, operators); err == nil { -// if aggregatedPrepareMsg == nil { -// aggregatedPrepareMsg = signedMsg -// } else { -// // TODO: check error -// // nolint -// aggregatedPrepareMsg.Aggregate(signedMsg) -// } -// } -// } -// return aggregatedPrepareMsg -// } - -// validSignedPrepareForHeightRoundAndValue known in dafny spec as validSignedPrepareForHeightRoundAndDigest -// https://entethalliance.github.io/client-spec/qbft_spec.html#dfn-qbftspecification -func validSignedPrepareForHeightRoundAndRoot( - config qbft.IConfig, - signedPrepare *genesisspecqbft.SignedMessage, - height genesisspecqbft.Height, - round genesisspecqbft.Round, - root [32]byte, - operators []*genesisspectypes.Operator) error { - if signedPrepare.Message.MsgType != genesisspecqbft.PrepareMsgType { - return errors.New("prepare msg type is wrong") - } - if signedPrepare.Message.Height != height { - return errors.New("wrong msg height") - } - if signedPrepare.Message.Round != round { - return errors.New("wrong msg round") - } - - if err := signedPrepare.Validate(); err != nil { - return errors.Wrap(err, "prepareData invalid") - } - - if !bytes.Equal(signedPrepare.Message.Root[:], root[:]) { - return errors.New("proposed data mistmatch") - } - - if len(signedPrepare.GetSigners()) != 1 { - return errors.New("msg allows 1 signer") - } - - if err := types.VerifyByOperators(signedPrepare.Signature, signedPrepare, config.GetSignatureDomainType(), genesisspectypes.QBFTSignatureType, operators); err != nil { - return errors.Wrap(err, "msg signature invalid") - } - - return nil -} - -// CreatePrepare -/** -Prepare( - signPrepare( - UnsignedPrepare( - |current.blockchain|, - newRound, - digest(m.proposedBlock)), - current.id - ) - ); -*/ -func CreatePrepare(state *genesisspecqbft.State, config qbft.IConfig, newRound genesisspecqbft.Round, root [32]byte) (*genesisspecqbft.SignedMessage, error) { - msg := &genesisspecqbft.Message{ - MsgType: genesisspecqbft.PrepareMsgType, - Height: state.Height, - Round: newRound, - Identifier: state.ID, - - Root: root, - } - sig, err := config.GetSigner().SignRoot(msg, genesisspectypes.QBFTSignatureType, state.Share.SharePubKey) - if err != nil { - return nil, errors.Wrap(err, "failed signing prepare msg") - } - - signedMsg := &genesisspecqbft.SignedMessage{ - Signature: sig, - Signers: []genesisspectypes.OperatorID{state.Share.OperatorID}, - Message: *msg, - } - return signedMsg, nil -} diff --git a/protocol/genesis/qbft/instance/proposal.go b/protocol/genesis/qbft/instance/proposal.go deleted file mode 100644 index 29ed1b5401..0000000000 --- a/protocol/genesis/qbft/instance/proposal.go +++ /dev/null @@ -1,295 +0,0 @@ -package instance - -import ( - "bytes" - - "github.com/pkg/errors" - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - specqbft "github.com/ssvlabs/ssv-spec/qbft" - "go.uber.org/zap" - - "github.com/ssvlabs/ssv/logging/fields" - "github.com/ssvlabs/ssv/protocol/genesis/qbft" - genesisssvtypes "github.com/ssvlabs/ssv/protocol/genesis/types" -) - -// uponProposal process proposal message -// Assumes proposal message is valid! -func (i *Instance) uponProposal(logger *zap.Logger, signedProposal *genesisspecqbft.SignedMessage, proposeMsgContainer *genesisspecqbft.MsgContainer) error { - addedMsg, err := proposeMsgContainer.AddFirstMsgForSignerAndRound(signedProposal) - if err != nil { - return errors.Wrap(err, "could not add proposal msg to container") - } - if !addedMsg { - return nil // uponProposal was already called - } - - logger.Debug("📬 got proposal message", - fields.Round(specqbft.Round(i.State.Round)), - zap.Any("proposal_signers", signedProposal.Signers)) - - newRound := signedProposal.Message.Round - i.State.ProposalAcceptedForCurrentRound = signedProposal - - // A future justified proposal should bump us into future round and reset timer - if signedProposal.Message.Round > i.State.Round { - i.config.GetTimer().TimeoutForRound(signedProposal.Message.Height, signedProposal.Message.Round) - } - i.bumpToRound(newRound) - - i.metrics.EndStageProposal() - - // value root - r, err := genesisspecqbft.HashDataRoot(signedProposal.FullData) - if err != nil { - return errors.Wrap(err, "could not hash input data") - } - - prepare, err := CreatePrepare(i.State, i.config, newRound, r) - if err != nil { - return errors.Wrap(err, "could not create prepare msg") - } - - logger.Debug("📢 got proposal, broadcasting prepare message", - fields.Round(specqbft.Round(i.State.Round)), - zap.Any("proposal_signers", signedProposal.Signers), - zap.Any("prepare_signers", prepare.Signers)) - - if err := i.Broadcast(logger, prepare); err != nil { - return errors.Wrap(err, "failed to broadcast prepare message") - } - return nil -} - -func isValidProposal( - state *genesisspecqbft.State, - config qbft.IConfig, - signedProposal *genesisspecqbft.SignedMessage, - valCheck genesisspecqbft.ProposedValueCheckF, - operators []*genesisspectypes.Operator, -) error { - if signedProposal.Message.MsgType != genesisspecqbft.ProposalMsgType { - return errors.New("msg type is not proposal") - } - if signedProposal.Message.Height != state.Height { - return errors.New("wrong msg height") - } - if len(signedProposal.GetSigners()) != 1 { - return errors.New("msg allows 1 signer") - } - if err := genesisssvtypes.VerifyByOperators(signedProposal.Signature, signedProposal, config.GetSignatureDomainType(), genesisspectypes.QBFTSignatureType, operators); err != nil { - return errors.Wrap(err, "msg signature invalid") - } - if !signedProposal.MatchedSigners([]genesisspectypes.OperatorID{proposer(state, config, signedProposal.Message.Round)}) { - return errors.New("proposal leader invalid") - } - - if err := signedProposal.Validate(); err != nil { - return errors.Wrap(err, "proposal invalid") - } - - // verify full data integrity - r, err := genesisspecqbft.HashDataRoot(signedProposal.FullData) - if err != nil { - return errors.Wrap(err, "could not hash input data") - } - if !bytes.Equal(signedProposal.Message.Root[:], r[:]) { - return errors.New("H(data) != root") - } - - // get justifications - roundChangeJustification, _ := signedProposal.Message.GetRoundChangeJustifications() // no need to check error, checked on signedProposal.Validate() - prepareJustification, _ := signedProposal.Message.GetPrepareJustifications() // no need to check error, checked on signedProposal.Validate() - - if err := isProposalJustification( - state, - config, - roundChangeJustification, - prepareJustification, - state.Height, - signedProposal.Message.Round, - signedProposal.FullData, - valCheck, - ); err != nil { - return errors.Wrap(err, "proposal not justified") - } - - if (state.ProposalAcceptedForCurrentRound == nil && signedProposal.Message.Round == state.Round) || - signedProposal.Message.Round > state.Round { - return nil - } - return errors.New("proposal is not valid with current state") -} - -func IsProposalJustification( - config qbft.IConfig, - share *genesisspectypes.Share, - roundChangeMsgs []*genesisspecqbft.SignedMessage, - prepareMsgs []*genesisspecqbft.SignedMessage, - height genesisspecqbft.Height, - round genesisspecqbft.Round, - fullData []byte, -) error { - return isProposalJustification( - &genesisspecqbft.State{ - Share: share, - Height: height, - }, - config, - roundChangeMsgs, - prepareMsgs, - height, - round, - fullData, - func(data []byte) error { return nil }, - ) -} - -// isProposalJustification returns nil if the proposal and round change messages are valid and justify a proposal message for the provided round, value and leader -func isProposalJustification( - state *genesisspecqbft.State, - config qbft.IConfig, - roundChangeMsgs []*genesisspecqbft.SignedMessage, - prepareMsgs []*genesisspecqbft.SignedMessage, - height genesisspecqbft.Height, - round genesisspecqbft.Round, - fullData []byte, - valCheck genesisspecqbft.ProposedValueCheckF, -) error { - if err := valCheck(fullData); err != nil { - return errors.Wrap(err, "proposal fullData invalid") - } - - if round == genesisspecqbft.FirstRound { - return nil - } else { - // check all round changes are valid for height and round - // no quorum, duplicate signers, invalid still has quorum, invalid no quorum - // prepared - for _, rc := range roundChangeMsgs { - if err := validRoundChangeForData(state, config, rc, height, round, fullData); err != nil { - return errors.Wrap(err, "change round msg not valid") - } - } - - // check there is a quorum - if !genesisspecqbft.HasQuorum(state.Share, roundChangeMsgs) { - return errors.New("change round has no quorum") - } - - // previouslyPreparedF returns true if any on the round change messages have a prepared round and fullData - previouslyPrepared, err := func(rcMsgs []*genesisspecqbft.SignedMessage) (bool, error) { - for _, rc := range rcMsgs { - if rc.Message.RoundChangePrepared() { - return true, nil - } - } - return false, nil - }(roundChangeMsgs) - if err != nil { - return errors.Wrap(err, "could not calculate if previously prepared") - } - - if !previouslyPrepared { - return nil - } else { - - // check prepare quorum - if !genesisspecqbft.HasQuorum(state.Share, prepareMsgs) { - return errors.New("prepares has no quorum") - } - - // get a round change data for which there is a justification for the highest previously prepared round - rcm, err := highestPrepared(roundChangeMsgs) - if err != nil { - return errors.Wrap(err, "could not get highest prepared") - } - if rcm == nil { - return errors.New("no highest prepared") - } - - // proposed fullData must equal highest prepared fullData - r, err := genesisspecqbft.HashDataRoot(fullData) - if err != nil { - return errors.Wrap(err, "could not hash input data") - } - if !bytes.Equal(r[:], rcm.Message.Root[:]) { - return errors.New("proposed data doesn't match highest prepared") - } - - // validate each prepare message against the highest previously prepared fullData and round - for _, pm := range prepareMsgs { - if err := validSignedPrepareForHeightRoundAndRoot( - config, - pm, - height, - rcm.Message.DataRound, - rcm.Message.Root, - state.Share.Committee, - ); err != nil { - return errors.New("signed prepare not valid") - } - } - return nil - } - } -} - -func proposer(state *genesisspecqbft.State, config qbft.IConfig, round genesisspecqbft.Round) genesisspectypes.OperatorID { - // TODO - https://github.com/ConsenSys/qbft-formal-spec-and-verification/blob/29ae5a44551466453a84d4d17b9e083ecf189d97/dafny/spec/L1/node_auxiliary_functions.dfy#L304-L323 - return config.GetProposerF()(state, round) -} - -// CreateProposal -/** - Proposal( - signProposal( - UnsignedProposal( - |current.blockchain|, - newRound, - digest(block)), - current.id), - block, - extractSignedRoundChanges(roundChanges), - extractSignedPrepares(prepares)); -*/ -func CreateProposal(state *genesisspecqbft.State, config qbft.IConfig, fullData []byte, roundChanges, prepares []*genesisspecqbft.SignedMessage) (*genesisspecqbft.SignedMessage, error) { - r, err := genesisspecqbft.HashDataRoot(fullData) - if err != nil { - return nil, errors.Wrap(err, "could not hash input data") - } - - roundChangesData, err := genesisspecqbft.MarshalJustifications(roundChanges) - if err != nil { - return nil, errors.Wrap(err, "could not marshal justifications") - } - preparesData, err := genesisspecqbft.MarshalJustifications(prepares) - if err != nil { - return nil, errors.Wrap(err, "could not marshal justifications") - } - - msg := &genesisspecqbft.Message{ - MsgType: genesisspecqbft.ProposalMsgType, - Height: state.Height, - Round: state.Round, - Identifier: state.ID, - - Root: r, - RoundChangeJustification: roundChangesData, - PrepareJustification: preparesData, - } - sig, err := config.GetSigner().SignRoot(msg, genesisspectypes.QBFTSignatureType, state.Share.SharePubKey) - if err != nil { - return nil, errors.Wrap(err, "failed signing proposal msg") - } - - signedMsg := &genesisspecqbft.SignedMessage{ - Signature: sig, - Signers: []genesisspectypes.OperatorID{state.Share.OperatorID}, - Message: *msg, - - FullData: fullData, - } - return signedMsg, nil -} diff --git a/protocol/genesis/qbft/instance/round_change.go b/protocol/genesis/qbft/instance/round_change.go deleted file mode 100644 index e65f4bb434..0000000000 --- a/protocol/genesis/qbft/instance/round_change.go +++ /dev/null @@ -1,404 +0,0 @@ -package instance - -import ( - "bytes" - - "github.com/pkg/errors" - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - specqbft "github.com/ssvlabs/ssv-spec/qbft" - "go.uber.org/zap" - - "github.com/ssvlabs/ssv/logging/fields" - "github.com/ssvlabs/ssv/protocol/genesis/qbft" - "github.com/ssvlabs/ssv/protocol/genesis/types" -) - -// uponRoundChange process round change messages. -// Assumes round change message is valid! -func (i *Instance) uponRoundChange( - logger *zap.Logger, - instanceStartValue []byte, - signedRoundChange *genesisspecqbft.SignedMessage, - roundChangeMsgContainer *genesisspecqbft.MsgContainer, - valCheck genesisspecqbft.ProposedValueCheckF, -) error { - hasQuorumBefore := genesisspecqbft.HasQuorum(i.State.Share, roundChangeMsgContainer.MessagesForRound(signedRoundChange.Message. - Round)) - // Currently, even if we have a quorum of round change messages, we update the container - addedMsg, err := roundChangeMsgContainer.AddFirstMsgForSignerAndRound(signedRoundChange) - if err != nil { - return errors.Wrap(err, "could not add round change msg to container") - } - if !addedMsg { - return nil // message was already added from signer - } - - if hasQuorumBefore { - return nil // already changed round - } - - logger = logger.With( - fields.Round(specqbft.Round(i.State.Round)), - fields.Height(specqbft.Height(i.State.Height)), - zap.Uint64("msg_round", uint64(signedRoundChange.Message.Round)), - ) - - logger.Debug("🔄 got round change", - fields.Root(signedRoundChange.Message.Root), - zap.Any("round_change_signers", signedRoundChange.Signers)) - - justifiedRoundChangeMsg, valueToPropose, err := hasReceivedProposalJustificationForLeadingRound( - i.State, - i.config, - instanceStartValue, - signedRoundChange, - roundChangeMsgContainer, - valCheck) - if err != nil { - return errors.Wrap(err, "could not get proposal justification for leading round") - } - if justifiedRoundChangeMsg != nil { - roundChangeJustification, _ := justifiedRoundChangeMsg.Message.GetRoundChangeJustifications() // no need to check error, check on isValidRoundChange - - proposal, err := CreateProposal( - i.State, - i.config, - valueToPropose, - roundChangeMsgContainer.MessagesForRound(i.State.Round), // TODO - might be optimized to include only necessary quorum - roundChangeJustification, - ) - if err != nil { - return errors.Wrap(err, "failed to create proposal") - } - - logger.Debug("🔄 got justified round change, broadcasting proposal message", - fields.Round(specqbft.Round(i.State.Round)), - zap.Any("round_change_signers", allSigners(roundChangeMsgContainer.MessagesForRound(i.State.Round))), - fields.Root(proposal.Message.Root)) - - if err := i.Broadcast(logger, proposal); err != nil { - return errors.Wrap(err, "failed to broadcast proposal message") - } - } else if partialQuorum, rcs := hasReceivedPartialQuorum(i.State, roundChangeMsgContainer); partialQuorum { - newRound := minRound(rcs) - if newRound <= i.State.Round { - return nil // no need to advance round - } - err := i.uponChangeRoundPartialQuorum(logger, newRound, instanceStartValue) - if err != nil { - return err - } - } - return nil -} - -func (i *Instance) uponChangeRoundPartialQuorum(logger *zap.Logger, newRound genesisspecqbft.Round, instanceStartValue []byte) error { - i.bumpToRound(newRound) - i.State.ProposalAcceptedForCurrentRound = nil - - i.config.GetTimer().TimeoutForRound(i.State.Height, i.State.Round) - - roundChange, err := CreateRoundChange(i.State, i.config, newRound, instanceStartValue) - if err != nil { - return errors.Wrap(err, "failed to create round change message") - } - - logger.Debug("📢 got partial quorum, broadcasting round change message", - fields.Round(specqbft.Round(i.State.Round)), - fields.Root(roundChange.Message.Root), - zap.Any("round_change_signers", roundChange.Signers), - fields.Height(specqbft.Height(i.State.Height)), - zap.String("reason", "partial-quorum")) - - if err := i.Broadcast(logger, roundChange); err != nil { - return errors.Wrap(err, "failed to broadcast round change message") - } - - return nil -} - -func hasReceivedPartialQuorum(state *genesisspecqbft.State, roundChangeMsgContainer *genesisspecqbft.MsgContainer) (bool, []*genesisspecqbft.SignedMessage) { - all := roundChangeMsgContainer.AllMessaged() - - rc := make([]*genesisspecqbft.SignedMessage, 0) - for _, msg := range all { - if msg.Message.Round > state.Round { - rc = append(rc, msg) - } - } - - return genesisspecqbft.HasPartialQuorum(state.Share, rc), rc -} - -// hasReceivedProposalJustificationForLeadingRound returns -// if first round or not received round change msgs with prepare justification - returns first rc msg in container and value to propose -// if received round change msgs with prepare justification - returns the highest prepare justification round change msg and value to propose -// (all the above considering the operator is a leader for the round -func hasReceivedProposalJustificationForLeadingRound( - state *genesisspecqbft.State, - config qbft.IConfig, - instanceStartValue []byte, - signedRoundChange *genesisspecqbft.SignedMessage, - roundChangeMsgContainer *genesisspecqbft.MsgContainer, - valCheck genesisspecqbft.ProposedValueCheckF, -) (*genesisspecqbft.SignedMessage, []byte, error) { - roundChanges := roundChangeMsgContainer.MessagesForRound(signedRoundChange.Message.Round) - // optimization, if no round change quorum can return false - if !genesisspecqbft.HasQuorum(state.Share, roundChanges) { - return nil, nil, nil - } - - // Important! - // We iterate on all round chance msgs for liveliness in case the last round change msg is malicious. - for _, msg := range roundChanges { - - // Chose proposal value. - // If justifiedRoundChangeMsg has no prepare justification chose state value - // If justifiedRoundChangeMsg has prepare justification chose prepared value - valueToPropose := instanceStartValue - if msg.Message.RoundChangePrepared() { - valueToPropose = signedRoundChange.FullData - } - - roundChangeJustification, _ := msg.Message.GetRoundChangeJustifications() // no need to check error, checked on isValidRoundChange - if isProposalJustificationForLeadingRound( - state, - config, - msg, - roundChanges, - roundChangeJustification, - valueToPropose, - valCheck, - signedRoundChange.Message.Round, - ) == nil { - // not returning error, no need to - return msg, valueToPropose, nil - } - } - return nil, nil, nil -} - -// isProposalJustificationForLeadingRound - returns nil if we have a quorum of round change msgs and highest justified value for leading round -func isProposalJustificationForLeadingRound( - state *genesisspecqbft.State, - config qbft.IConfig, - roundChangeMsg *genesisspecqbft.SignedMessage, - roundChanges []*genesisspecqbft.SignedMessage, - roundChangeJustifications []*genesisspecqbft.SignedMessage, - value []byte, - valCheck genesisspecqbft.ProposedValueCheckF, - newRound genesisspecqbft.Round, -) error { - if err := isReceivedProposalJustification( - state, - config, - roundChanges, - roundChangeJustifications, - roundChangeMsg.Message.Round, - value, - valCheck); err != nil { - return err - } - - if proposer(state, config, roundChangeMsg.Message.Round) != state.Share.OperatorID { - return errors.New("not proposer") - } - - currentRoundProposal := state.ProposalAcceptedForCurrentRound == nil && state.Round == newRound - futureRoundProposal := newRound > state.Round - - if !currentRoundProposal && !futureRoundProposal { - return errors.New("proposal round mismatch") - } - - return nil -} - -// isReceivedProposalJustification - returns nil if we have a quorum of round change msgs and highest justified value -func isReceivedProposalJustification( - state *genesisspecqbft.State, - config qbft.IConfig, - roundChanges, prepares []*genesisspecqbft.SignedMessage, - newRound genesisspecqbft.Round, - value []byte, - valCheck genesisspecqbft.ProposedValueCheckF, -) error { - if err := isProposalJustification( - state, - config, - roundChanges, - prepares, - state.Height, - newRound, - value, - valCheck, - ); err != nil { - return errors.Wrap(err, "proposal not justified") - } - return nil -} - -func validRoundChangeForData( - state *genesisspecqbft.State, - config qbft.IConfig, - signedMsg *genesisspecqbft.SignedMessage, - height genesisspecqbft.Height, - round genesisspecqbft.Round, - fullData []byte, -) error { - if signedMsg.Message.MsgType != genesisspecqbft.RoundChangeMsgType { - return errors.New("round change msg type is wrong") - } - if signedMsg.Message.Height != height { - return errors.New("wrong msg height") - } - if signedMsg.Message.Round != round { - return errors.New("wrong msg round") - } - if len(signedMsg.GetSigners()) != 1 { - return errors.New("msg allows 1 signer") - } - - if err := types.VerifyByOperators(signedMsg.Signature, signedMsg, config.GetSignatureDomainType(), genesisspectypes.QBFTSignatureType, state.Share.Committee); err != nil { - return errors.Wrap(err, "msg signature invalid") - } - - if err := signedMsg.Message.Validate(); err != nil { - return errors.Wrap(err, "roundChange invalid") - } - - // Addition to formal spec - // We add this extra tests on the msg itself to filter round change msgs with invalid justifications, before they are inserted into msg containers - if signedMsg.Message.RoundChangePrepared() { - r, err := genesisspecqbft.HashDataRoot(fullData) - if err != nil { - return errors.Wrap(err, "could not hash input data") - } - - // validate prepare message justifications - prepareMsgs, _ := signedMsg.Message.GetRoundChangeJustifications() // no need to check error, checked on signedMsg.Message.Validate() - for _, pm := range prepareMsgs { - if err := validSignedPrepareForHeightRoundAndRoot( - config, - pm, - state.Height, - signedMsg.Message.DataRound, - signedMsg.Message.Root, - state.Share.Committee); err != nil { - return errors.Wrap(err, "round change justification invalid") - } - } - - if !bytes.Equal(r[:], signedMsg.Message.Root[:]) { - return errors.New("H(data) != root") - } - - if !genesisspecqbft.HasQuorum(state.Share, prepareMsgs) { - return errors.New("no justifications quorum") - } - - if signedMsg.Message.DataRound > round { - return errors.New("prepared round > round") - } - - return nil - } - return nil -} - -// highestPrepared returns a round change message with the highest prepared round, returns nil if none found -func highestPrepared(roundChanges []*genesisspecqbft.SignedMessage) (*genesisspecqbft.SignedMessage, error) { - var ret *genesisspecqbft.SignedMessage - for _, rc := range roundChanges { - if !rc.Message.RoundChangePrepared() { - continue - } - - if ret == nil { - ret = rc - } else { - if ret.Message.DataRound < rc.Message.DataRound { - ret = rc - } - } - } - return ret, nil -} - -// returns the min round number out of the signed round change messages and the current round -func minRound(roundChangeMsgs []*genesisspecqbft.SignedMessage) genesisspecqbft.Round { - ret := genesisspecqbft.NoRound - for _, msg := range roundChangeMsgs { - if ret == genesisspecqbft.NoRound || ret > msg.Message.Round { - ret = msg.Message.Round - } - } - return ret -} - -func getRoundChangeData(state *genesisspecqbft.State, config qbft.IConfig, instanceStartValue []byte) (genesisspecqbft.Round, [32]byte, []byte, []*genesisspecqbft.SignedMessage, error) { - if state.LastPreparedRound != genesisspecqbft.NoRound && state.LastPreparedValue != nil { - justifications, err := getRoundChangeJustification(state, config, state.PrepareContainer) - if err != nil { - return genesisspecqbft.NoRound, [32]byte{}, nil, nil, errors.Wrap(err, "could not get round change justification") - } - - r, err := genesisspecqbft.HashDataRoot(state.LastPreparedValue) - if err != nil { - return genesisspecqbft.NoRound, [32]byte{}, nil, nil, errors.Wrap(err, "could not hash input data") - } - - return state.LastPreparedRound, r, state.LastPreparedValue, justifications, nil - } - return genesisspecqbft.NoRound, [32]byte{}, nil, nil, nil -} - -// CreateRoundChange -/** -RoundChange( - signRoundChange( - UnsignedRoundChange( - |current.blockchain|, - newRound, - digestOptionalBlock(current.lastPreparedBlock), - current.lastPreparedRound), - current.id), - current.lastPreparedBlock, - getRoundChangeJustification(current) - ) -*/ -func CreateRoundChange(state *genesisspecqbft.State, config qbft.IConfig, newRound genesisspecqbft.Round, instanceStartValue []byte) (*genesisspecqbft.SignedMessage, error) { - round, root, fullData, justifications, err := getRoundChangeData(state, config, instanceStartValue) - if err != nil { - return nil, errors.Wrap(err, "could not generate round change data") - } - - justificationsData, err := genesisspecqbft.MarshalJustifications(justifications) - if err != nil { - return nil, errors.Wrap(err, "could not marshal justifications") - } - msg := &genesisspecqbft.Message{ - MsgType: genesisspecqbft.RoundChangeMsgType, - Height: state.Height, - Round: newRound, - Identifier: state.ID, - - Root: root, - DataRound: round, - RoundChangeJustification: justificationsData, - } - sig, err := config.GetSigner().SignRoot(msg, genesisspectypes.QBFTSignatureType, state.Share.SharePubKey) - if err != nil { - return nil, errors.Wrap(err, "failed signing round change msg") - } - - signedMsg := &genesisspecqbft.SignedMessage{ - Signature: sig, - Signers: []genesisspectypes.OperatorID{state.Share.OperatorID}, - Message: *msg, - - FullData: fullData, - } - return signedMsg, nil -} diff --git a/protocol/genesis/qbft/instance/timeout.go b/protocol/genesis/qbft/instance/timeout.go deleted file mode 100644 index 7232c627b4..0000000000 --- a/protocol/genesis/qbft/instance/timeout.go +++ /dev/null @@ -1,48 +0,0 @@ -package instance - -import ( - "github.com/pkg/errors" - "go.uber.org/zap" - - specqbft "github.com/ssvlabs/ssv-spec/qbft" - - "github.com/ssvlabs/ssv/logging/fields" -) - -var CutoffRound = uint64(15) // stop processing instances after 8*2+120*6 = 14.2 min (~ 2 epochs) - -func (i *Instance) UponRoundTimeout(logger *zap.Logger) error { - if !i.CanProcessMessages() { - return errors.New("instance stopped processing timeouts") - } - - newRound := i.State.Round + 1 - logger.Debug("⌛ round timed out", fields.Round(specqbft.Round(newRound))) - - // TODO: previously this was done outside of a defer, which caused the - // round to be bumped before the round change message was created & broadcasted. - // Remember to track the impact of this change and revert/modify if necessary. - defer func() { - i.bumpToRound(newRound) - i.State.ProposalAcceptedForCurrentRound = nil - i.config.GetTimer().TimeoutForRound(i.State.Height, i.State.Round) - }() - - roundChange, err := CreateRoundChange(i.State, i.config, newRound, i.StartValue) - if err != nil { - return errors.Wrap(err, "could not generate round change msg") - } - - logger.Debug("📢 broadcasting round change message", - fields.Round(specqbft.Round(i.State.Round)), - fields.Root(roundChange.Message.Root), - zap.Any("round_change_signers", roundChange.Signers), - fields.Height(specqbft.Height(i.State.Height)), - zap.String("reason", "timeout")) - - if err := i.Broadcast(logger, roundChange); err != nil { - return errors.Wrap(err, "failed to broadcast round change message") - } - - return nil -} diff --git a/protocol/genesis/qbft/roundtimer/mocks/timer.go b/protocol/genesis/qbft/roundtimer/mocks/timer.go deleted file mode 100644 index ea0776bb22..0000000000 --- a/protocol/genesis/qbft/roundtimer/mocks/timer.go +++ /dev/null @@ -1,105 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: ./timer.go -// -// Generated by this command: -// -// mockgen -package=mocks -destination=./mocks/timer.go -source=./timer.go -// - -// Package mocks is a generated GoMock package. -package mocks - -import ( - reflect "reflect" - time "time" - - phase0 "github.com/attestantio/go-eth2-client/spec/phase0" - qbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - gomock "go.uber.org/mock/gomock" -) - -// MockTimer is a mock of Timer interface. -type MockTimer struct { - ctrl *gomock.Controller - recorder *MockTimerMockRecorder -} - -// MockTimerMockRecorder is the mock recorder for MockTimer. -type MockTimerMockRecorder struct { - mock *MockTimer -} - -// NewMockTimer creates a new mock instance. -func NewMockTimer(ctrl *gomock.Controller) *MockTimer { - mock := &MockTimer{ctrl: ctrl} - mock.recorder = &MockTimerMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockTimer) EXPECT() *MockTimerMockRecorder { - return m.recorder -} - -// TimeoutForRound mocks base method. -func (m *MockTimer) TimeoutForRound(height qbft.Height, round qbft.Round) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "TimeoutForRound", height, round) -} - -// TimeoutForRound indicates an expected call of TimeoutForRound. -func (mr *MockTimerMockRecorder) TimeoutForRound(height, round any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TimeoutForRound", reflect.TypeOf((*MockTimer)(nil).TimeoutForRound), height, round) -} - -// MockBeaconNetwork is a mock of BeaconNetwork interface. -type MockBeaconNetwork struct { - ctrl *gomock.Controller - recorder *MockBeaconNetworkMockRecorder -} - -// MockBeaconNetworkMockRecorder is the mock recorder for MockBeaconNetwork. -type MockBeaconNetworkMockRecorder struct { - mock *MockBeaconNetwork -} - -// NewMockBeaconNetwork creates a new mock instance. -func NewMockBeaconNetwork(ctrl *gomock.Controller) *MockBeaconNetwork { - mock := &MockBeaconNetwork{ctrl: ctrl} - mock.recorder = &MockBeaconNetworkMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockBeaconNetwork) EXPECT() *MockBeaconNetworkMockRecorder { - return m.recorder -} - -// GetSlotStartTime mocks base method. -func (m *MockBeaconNetwork) GetSlotStartTime(slot phase0.Slot) time.Time { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetSlotStartTime", slot) - ret0, _ := ret[0].(time.Time) - return ret0 -} - -// GetSlotStartTime indicates an expected call of GetSlotStartTime. -func (mr *MockBeaconNetworkMockRecorder) GetSlotStartTime(slot any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSlotStartTime", reflect.TypeOf((*MockBeaconNetwork)(nil).GetSlotStartTime), slot) -} - -// SlotDurationSec mocks base method. -func (m *MockBeaconNetwork) SlotDurationSec() time.Duration { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SlotDurationSec") - ret0, _ := ret[0].(time.Duration) - return ret0 -} - -// SlotDurationSec indicates an expected call of SlotDurationSec. -func (mr *MockBeaconNetworkMockRecorder) SlotDurationSec() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SlotDurationSec", reflect.TypeOf((*MockBeaconNetwork)(nil).SlotDurationSec)) -} diff --git a/protocol/genesis/qbft/roundtimer/testing_timer.go b/protocol/genesis/qbft/roundtimer/testing_timer.go deleted file mode 100644 index c7c5ad7ce4..0000000000 --- a/protocol/genesis/qbft/roundtimer/testing_timer.go +++ /dev/null @@ -1,23 +0,0 @@ -package roundtimer - -import genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - -type TimerState struct { - Timeouts int - Round genesisspecqbft.Round -} - -type TestQBFTTimer struct { - State TimerState -} - -func NewTestingTimer() Timer { - return &TestQBFTTimer{ - State: TimerState{}, - } -} - -func (t *TestQBFTTimer) TimeoutForRound(height genesisspecqbft.Height, round genesisspecqbft.Round) { - t.State.Timeouts++ - t.State.Round = round -} diff --git a/protocol/genesis/qbft/roundtimer/timer.go b/protocol/genesis/qbft/roundtimer/timer.go deleted file mode 100644 index 43854dd3ae..0000000000 --- a/protocol/genesis/qbft/roundtimer/timer.go +++ /dev/null @@ -1,192 +0,0 @@ -package roundtimer - -import ( - "context" - "sync" - "sync/atomic" - "time" - - "github.com/attestantio/go-eth2-client/spec/phase0" - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - "github.com/ssvlabs/ssv/utils/casts" -) - -//go:generate mockgen -package=mocks -destination=./mocks/timer.go -source=./timer.go - -type OnRoundTimeoutF func(round genesisspecqbft.Round) - -const ( - QuickTimeoutThreshold = genesisspecqbft.Round(8) - QuickTimeout = 2 * time.Second - SlowTimeout = 2 * time.Minute -) - -// Timer is an interface for a round timer, calling the UponRoundTimeout when times out -type Timer interface { - // TimeoutForRound will reset running timer if exists and will start a new timer for a specific round - TimeoutForRound(height genesisspecqbft.Height, round genesisspecqbft.Round) -} - -type BeaconNetwork interface { - GetSlotStartTime(slot phase0.Slot) time.Time - SlotDurationSec() time.Duration -} - -type TimeoutOptions struct { - quickThreshold genesisspecqbft.Round - quick time.Duration - slow time.Duration -} - -// RoundTimer helps to manage current instance rounds. -type RoundTimer struct { - mtx *sync.RWMutex - ctx context.Context - // cancelCtx cancels the current context, will be called from Kill() - cancelCtx context.CancelFunc - // timer is the underlying time.Timer - timer *time.Timer - // result holds the result of the timer - done OnRoundTimeoutF - // round is the current round of the timer - round int64 - // timeoutOptions holds the timeoutOptions for the timer - timeoutOptions TimeoutOptions - // role is the role of the instance - role genesisspectypes.BeaconRole - // beaconNetwork is the beacon network - beaconNetwork BeaconNetwork -} - -// New creates a new instance of RoundTimer. -func New(pctx context.Context, beaconNetwork BeaconNetwork, role genesisspectypes.BeaconRole, done OnRoundTimeoutF) *RoundTimer { - ctx, cancelCtx := context.WithCancel(pctx) - return &RoundTimer{ - mtx: &sync.RWMutex{}, - ctx: ctx, - cancelCtx: cancelCtx, - timer: nil, - done: done, - role: role, - beaconNetwork: beaconNetwork, - timeoutOptions: TimeoutOptions{ - quickThreshold: QuickTimeoutThreshold, - quick: QuickTimeout, - slow: SlowTimeout, - }, - } -} - -// RoundTimeout calculates the timeout duration for a specific role, height, and round. -// -// Timeout Rules: -// - For roles BNRoleAttester and BNRoleSyncCommittee, the base timeout is 1/3 of the slot duration. -// - For roles BNRoleAggregator and BNRoleSyncCommitteeContribution, the base timeout is 2/3 of the slot duration. -// - For role BNRoleProposer, the timeout is either quickTimeout or slowTimeout, depending on the round. -// -// Additional Timeout: -// - For rounds less than or equal to quickThreshold, the additional timeout is 'quick' seconds. -// - For rounds greater than quickThreshold, the additional timeout is 'slow' seconds. -// -// SIP Reference: -// For more details, see SIP at https://github.com/ssvlabs/SIPs/pull/22 -// -// TODO: Update SIP for Deterministic Round Timeout -// TODO: Decide if to make the proposer timeout deterministic -// -// Synchronization Note: -// To ensure synchronized timeouts across instances, the timeout is based on the duty start time, -// which is calculated from the slot height. The base timeout is set based on the role, -// and the additional timeout is added based on the round number. -func (t *RoundTimer) RoundTimeout(height genesisspecqbft.Height, round genesisspecqbft.Round) time.Duration { - // Initialize duration to zero - var baseDuration time.Duration - - // Set base duration based on role - switch t.role { - case genesisspectypes.BNRoleAttester, genesisspectypes.BNRoleSyncCommittee: - // third of the slot time - baseDuration = t.beaconNetwork.SlotDurationSec() / 3 - case genesisspectypes.BNRoleAggregator, genesisspectypes.BNRoleSyncCommitteeContribution: - // two-third of the slot time - baseDuration = t.beaconNetwork.SlotDurationSec() / 3 * 2 - default: - if round <= t.timeoutOptions.quickThreshold { - return t.timeoutOptions.quick - } - return t.timeoutOptions.slow - } - - // Calculate additional timeout based on round - var additionalTimeout time.Duration - if round <= t.timeoutOptions.quickThreshold { - additionalTimeout = casts.DurationFromUint64(uint64(round)) * t.timeoutOptions.quick - } else { - quickPortion := casts.DurationFromUint64(uint64(t.timeoutOptions.quickThreshold)) * t.timeoutOptions.quick - slowPortion := casts.DurationFromUint64(uint64(round-t.timeoutOptions.quickThreshold)) * t.timeoutOptions.slow - additionalTimeout = quickPortion + slowPortion - } - - // Combine base duration and additional timeout - timeoutDuration := baseDuration + additionalTimeout - - // Get the start time of the duty - dutyStartTime := t.beaconNetwork.GetSlotStartTime(phase0.Slot(height)) - - // Calculate the time until the duty should start plus the timeout duration - return time.Until(dutyStartTime.Add(timeoutDuration)) -} - -// OnTimeout sets a function called on timeout. -func (t *RoundTimer) OnTimeout(done OnRoundTimeoutF) { - t.mtx.Lock() // write to t.done - defer t.mtx.Unlock() - - t.done = done -} - -// Round returns a round. -func (t *RoundTimer) Round() genesisspecqbft.Round { - return genesisspecqbft.Round(atomic.LoadInt64(&t.round)) // #nosec G115 -} - -// TimeoutForRound times out for a given round. -func (t *RoundTimer) TimeoutForRound(height genesisspecqbft.Height, round genesisspecqbft.Round) { - atomic.StoreInt64(&t.round, int64(round)) // #nosec G115 - timeout := t.RoundTimeout(height, round) - - // preparing the underlying timer - timer := t.timer - if timer == nil { - timer = time.NewTimer(timeout) - } else { - timer.Stop() - // draining the channel of existing timer - select { - case <-timer.C: - default: - } - } - timer.Reset(timeout) - // spawns a new goroutine to listen to the timer - go t.waitForRound(round, timer.C) -} - -func (t *RoundTimer) waitForRound(round genesisspecqbft.Round, timeout <-chan time.Time) { - ctx, cancel := context.WithCancel(t.ctx) - defer cancel() - select { - case <-ctx.Done(): - case <-timeout: - if t.Round() == round { - func() { - t.mtx.RLock() // read t.done - defer t.mtx.RUnlock() - if done := t.done; done != nil { - done(round) - } - }() - } - } -} diff --git a/protocol/genesis/qbft/roundtimer/timer_test.go b/protocol/genesis/qbft/roundtimer/timer_test.go deleted file mode 100644 index d4607fa3e0..0000000000 --- a/protocol/genesis/qbft/roundtimer/timer_test.go +++ /dev/null @@ -1,168 +0,0 @@ -package roundtimer - -import ( - "context" - "fmt" - "sync" - "sync/atomic" - "testing" - "time" - - "github.com/attestantio/go-eth2-client/spec/phase0" - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - "github.com/stretchr/testify/require" - "go.uber.org/mock/gomock" - - "github.com/ssvlabs/ssv/protocol/genesis/qbft/roundtimer/mocks" -) - -func TestTimeoutForRound(t *testing.T) { - roles := []genesisspectypes.BeaconRole{ - genesisspectypes.BNRoleAttester, - genesisspectypes.BNRoleAggregator, - genesisspectypes.BNRoleProposer, - genesisspectypes.BNRoleSyncCommittee, - genesisspectypes.BNRoleSyncCommitteeContribution, - } - - for _, role := range roles { - t.Run(fmt.Sprintf("TimeoutForRound - %s: <= quickTimeoutThreshold", role), func(t *testing.T) { - testTimeoutForRound(t, role, genesisspecqbft.Round(1)) - }) - - t.Run(fmt.Sprintf("TimeoutForRound - %s: > quickTimeoutThreshold", role), func(t *testing.T) { - testTimeoutForRound(t, role, genesisspecqbft.Round(2)) - }) - - t.Run(fmt.Sprintf("TimeoutForRound - %s: before elapsed", role), func(t *testing.T) { - testTimeoutForRoundElapsed(t, role, genesisspecqbft.Round(2)) - }) - - // TODO: Decide if to make the proposer timeout deterministic - // Proposer role is not tested for multiple synchronized timers since it's not deterministic - if role == genesisspectypes.BNRoleProposer { - continue - } - - t.Run(fmt.Sprintf("TimeoutForRound - %s: multiple synchronized timers", role), func(t *testing.T) { - testTimeoutForRoundMulti(t, role, genesisspecqbft.Round(1)) - }) - } -} - -func setupMockBeaconNetwork(t *testing.T) *mocks.MockBeaconNetwork { - ctrl := gomock.NewController(t) - mockBeaconNetwork := mocks.NewMockBeaconNetwork(ctrl) - - mockBeaconNetwork.EXPECT().SlotDurationSec().Return(120 * time.Millisecond).AnyTimes() - mockBeaconNetwork.EXPECT().GetSlotStartTime(gomock.Any()).DoAndReturn( - func(slot phase0.Slot) time.Time { - return time.Now() - }, - ).AnyTimes() - return mockBeaconNetwork -} - -func setupTimer(mockBeaconNetwork *mocks.MockBeaconNetwork, onTimeout OnRoundTimeoutF, role genesisspectypes.BeaconRole, round genesisspecqbft.Round) *RoundTimer { - timer := New(context.Background(), mockBeaconNetwork, role, onTimeout) - timer.timeoutOptions = TimeoutOptions{ - quickThreshold: round, - quick: 100 * time.Millisecond, - slow: 200 * time.Millisecond, - } - - return timer -} - -func testTimeoutForRound(t *testing.T, role genesisspectypes.BeaconRole, threshold genesisspecqbft.Round) { - mockBeaconNetwork := setupMockBeaconNetwork(t) - - count := int32(0) - onTimeout := func(round genesisspecqbft.Round) { - atomic.AddInt32(&count, 1) - } - - timer := setupTimer(mockBeaconNetwork, onTimeout, role, threshold) - - timer.TimeoutForRound(genesisspecqbft.FirstHeight, threshold) - require.Equal(t, int32(0), atomic.LoadInt32(&count)) - <-time.After(timer.RoundTimeout(genesisspecqbft.FirstHeight, threshold) + time.Millisecond*10) - require.Equal(t, int32(1), atomic.LoadInt32(&count)) -} - -func testTimeoutForRoundElapsed(t *testing.T, role genesisspectypes.BeaconRole, threshold genesisspecqbft.Round) { - mockBeaconNetwork := setupMockBeaconNetwork(t) - - count := int32(0) - onTimeout := func(round genesisspecqbft.Round) { - atomic.AddInt32(&count, 1) - } - - timer := setupTimer(mockBeaconNetwork, onTimeout, role, threshold) - - timer.TimeoutForRound(genesisspecqbft.FirstHeight, genesisspecqbft.FirstRound) - <-time.After(timer.RoundTimeout(genesisspecqbft.FirstHeight, genesisspecqbft.FirstRound) / 2) - timer.TimeoutForRound(genesisspecqbft.FirstHeight, genesisspecqbft.Round(2)) // reset before elapsed - require.Equal(t, int32(0), atomic.LoadInt32(&count)) - <-time.After(timer.RoundTimeout(genesisspecqbft.FirstHeight, genesisspecqbft.Round(2)) + time.Millisecond*10) - require.Equal(t, int32(1), atomic.LoadInt32(&count)) -} - -func testTimeoutForRoundMulti(t *testing.T, role genesisspectypes.BeaconRole, threshold genesisspecqbft.Round) { - ctrl := gomock.NewController(t) - mockBeaconNetwork := mocks.NewMockBeaconNetwork(ctrl) - - var count int32 - var timestamps = make([]int64, 4) - var mu sync.Mutex - - onTimeout := func(index int) { - atomic.AddInt32(&count, 1) - mu.Lock() - timestamps[index] = time.Now().UnixNano() - mu.Unlock() - } - - timeNow := time.Now() - mockBeaconNetwork.EXPECT().SlotDurationSec().Return(100 * time.Millisecond).AnyTimes() - mockBeaconNetwork.EXPECT().GetSlotStartTime(gomock.Any()).DoAndReturn( - func(slot phase0.Slot) time.Time { - return timeNow - }, - ).AnyTimes() - - var wg sync.WaitGroup - for i := 0; i < 4; i++ { - wg.Add(1) - go func(index int) { - timer := New(context.Background(), mockBeaconNetwork, role, func(round genesisspecqbft.Round) { onTimeout(index) }) - timer.timeoutOptions = TimeoutOptions{ - quickThreshold: threshold, - quick: 100 * time.Millisecond, - } - timer.TimeoutForRound(genesisspecqbft.FirstHeight, genesisspecqbft.FirstRound) - wg.Done() - }(i) - time.Sleep(time.Millisecond * 10) // Introduce a sleep between creating timers - } - - wg.Wait() // Wait for all go-routines to finish - - timer := New(context.Background(), mockBeaconNetwork, role, nil) - timer.timeoutOptions = TimeoutOptions{ - quickThreshold: genesisspecqbft.Round(1), - quick: 100 * time.Millisecond, - } - - // Wait a bit more than the expected timeout to ensure all timers have triggered - <-time.After(timer.RoundTimeout(genesisspecqbft.FirstHeight, genesisspecqbft.FirstRound) + time.Millisecond*100) - - require.Equal(t, int32(4), atomic.LoadInt32(&count), "All four timers should have triggered") - - mu.Lock() - for i := 1; i < 4; i++ { - require.InDelta(t, timestamps[0], timestamps[i], float64(time.Millisecond*10), "All four timers should expire nearly at the same time") - } - mu.Unlock() -} diff --git a/protocol/genesis/qbft/spectest/controller_type.go b/protocol/genesis/qbft/spectest/controller_type.go deleted file mode 100644 index 1b4c84f0cf..0000000000 --- a/protocol/genesis/qbft/spectest/controller_type.go +++ /dev/null @@ -1,190 +0,0 @@ -package qbft - -import ( - "bytes" - "encoding/hex" - "encoding/json" - "fmt" - "os" - "path/filepath" - "reflect" - "testing" - - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - spectests "github.com/ssvlabs/ssv-spec-pre-cc/qbft/spectest/tests" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - spectestingutils "github.com/ssvlabs/ssv-spec-pre-cc/types/testingutils" - typescomparable "github.com/ssvlabs/ssv-spec-pre-cc/types/testingutils/comparable" - "github.com/stretchr/testify/require" - "go.uber.org/zap" - - "github.com/ssvlabs/ssv/logging" - "github.com/ssvlabs/ssv/protocol/genesis/qbft" - "github.com/ssvlabs/ssv/protocol/genesis/qbft/controller" - "github.com/ssvlabs/ssv/protocol/genesis/qbft/roundtimer" - qbfttesting "github.com/ssvlabs/ssv/protocol/genesis/qbft/testing" - protocoltesting "github.com/ssvlabs/ssv/protocol/genesis/testing" -) - -func RunControllerSpecTest(t *testing.T, test *spectests.ControllerSpecTest) { - //temporary to override state comparisons from file not inputted one - overrideStateComparisonForControllerSpecTest(t, test) - - logger := logging.TestLogger(t) - contr := generateController(logger) - - if test.StartHeight != nil { - contr.Height = *test.StartHeight - } - - var lastErr error - height := genesisspecqbft.Height(0) - for _, runData := range test.RunInstanceData { - if runData.Height != nil { - height = *runData.Height - } - if err := runInstanceWithData(t, logger, height, contr, runData); err != nil { - lastErr = err - } - height++ - } - - if len(test.ExpectedError) != 0 { - require.EqualError(t, lastErr, test.ExpectedError) - } else { - require.NoError(t, lastErr) - } -} - -func generateController(logger *zap.Logger) *controller.Controller { - identifier := []byte{1, 2, 3, 4} - config := qbfttesting.TestingConfig(logger, spectestingutils.Testing4SharesSet(), genesisspectypes.BNRoleAttester) - return qbfttesting.NewTestingQBFTController( - identifier[:], - spectestingutils.TestingShare(spectestingutils.Testing4SharesSet()), - config, - false, - ) -} - -func testTimer( - t *testing.T, - config *qbft.Config, - runData *spectests.RunInstanceData, -) { - if runData.ExpectedTimerState != nil { - if timer, ok := config.GetTimer().(*roundtimer.TestQBFTTimer); ok { - require.Equal(t, runData.ExpectedTimerState.Timeouts, timer.State.Timeouts) - require.Equal(t, runData.ExpectedTimerState.Round, timer.State.Round) - } - } -} - -func testProcessMsg( - t *testing.T, - logger *zap.Logger, - contr *controller.Controller, - config *qbft.Config, - runData *spectests.RunInstanceData, -) error { - decidedCnt := uint(0) - var lastErr error - for _, msg := range runData.InputMessages { - decided, err := contr.ProcessMsg(logger, msg) - if err != nil { - lastErr = err - } - if decided != nil { - decidedCnt++ - - require.EqualValues(t, runData.ExpectedDecidedState.DecidedVal, decided.FullData) - } - } - require.EqualValues(t, runData.ExpectedDecidedState.DecidedCnt, decidedCnt, lastErr) - - return lastErr -} - -func testBroadcastedDecided( - t *testing.T, - config *qbft.Config, - identifier []byte, - runData *spectests.RunInstanceData, -) { - if runData.ExpectedDecidedState.BroadcastedDecided != nil { - // test broadcasted - broadcastedMsgs := config.GetNetwork().(*spectestingutils.TestingNetwork).BroadcastedMsgs - require.Greater(t, len(broadcastedMsgs), 0) - found := false - for _, msg := range broadcastedMsgs { - - // a hack for testing non standard messageID identifiers since we copy them into a MessageID this fixes it - msgID := genesisspectypes.MessageID{} - copy(msgID[:], identifier) - - if !bytes.Equal(msgID[:], msg.MsgID[:]) { - continue - } - - msg1 := &genesisspecqbft.SignedMessage{} - require.NoError(t, msg1.Decode(msg.Data)) - r1, err := msg1.GetRoot() - require.NoError(t, err) - - r2, err := runData.ExpectedDecidedState.BroadcastedDecided.GetRoot() - require.NoError(t, err) - - if r1 == r2 && - reflect.DeepEqual(runData.ExpectedDecidedState.BroadcastedDecided.Signers, msg1.Signers) && - reflect.DeepEqual(runData.ExpectedDecidedState.BroadcastedDecided.Signature, msg1.Signature) { - require.False(t, found) - found = true - } - } - require.True(t, found) - } -} - -func runInstanceWithData(t *testing.T, logger *zap.Logger, height genesisspecqbft.Height, contr *controller.Controller, runData *spectests.RunInstanceData) error { - err := contr.StartNewInstance(logger, height, runData.InputValue) - var lastErr error - if err != nil { - lastErr = err - } - - testTimer(t, contr.GetConfig().(*qbft.Config), runData) - - if err := testProcessMsg(t, logger, contr, contr.GetConfig().(*qbft.Config), runData); err != nil { - lastErr = err - } - - testBroadcastedDecided(t, contr.GetConfig().(*qbft.Config), contr.Identifier, runData) - - // test root - r, err := contr.GetRoot() - require.NoError(t, err) - require.EqualValues(t, runData.ControllerPostRoot, hex.EncodeToString(r[:])) - - return lastErr -} - -func overrideStateComparisonForControllerSpecTest(t *testing.T, test *spectests.ControllerSpecTest) { - specDir, err := protocoltesting.GetSpecDir("", filepath.Join("qbft", "spectest")) - require.NoError(t, err) - specDir = filepath.Join(specDir, "generate") - dir := typescomparable.GetSCDir(specDir, reflect.TypeOf(test).String()) - path := filepath.Join(dir, fmt.Sprintf("%s.json", test.TestName())) - byteValue, err := os.ReadFile(filepath.Clean(path)) - require.NoError(t, err) - sc := make([]*controller.Controller, len(test.RunInstanceData)) - require.NoError(t, json.Unmarshal(byteValue, &sc)) - - for i, runData := range test.RunInstanceData { - runData.ControllerPostState = sc[i] - - r, err := sc[i].GetRoot() - require.NoError(t, err) - - runData.ControllerPostRoot = hex.EncodeToString(r[:]) - } -} diff --git a/protocol/genesis/qbft/spectest/create_msg_type.go b/protocol/genesis/qbft/spectest/create_msg_type.go deleted file mode 100644 index 471008a3d2..0000000000 --- a/protocol/genesis/qbft/spectest/create_msg_type.go +++ /dev/null @@ -1,100 +0,0 @@ -package qbft - -import ( - "encoding/hex" - "testing" - - "github.com/pkg/errors" - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - spectests "github.com/ssvlabs/ssv-spec-pre-cc/qbft/spectest/tests" - "github.com/ssvlabs/ssv-spec-pre-cc/types/testingutils" - "github.com/stretchr/testify/require" -) - -func RunCreateMsg(t *testing.T, test *spectests.CreateMsgSpecTest) { - var msg *genesisspecqbft.SignedMessage - var err error - switch test.CreateType { - case spectests.CreateProposal: - msg, err = createProposal(test) - case spectests.CreatePrepare: - msg, err = createPrepare(test) - case spectests.CreateCommit: - msg, err = createCommit(test) - case spectests.CreateRoundChange: - msg, err = createRoundChange(test) - default: - t.Fail() - } - - if err != nil && len(test.ExpectedError) != 0 { - require.EqualError(t, err, test.ExpectedError) - return - } - require.NoError(t, err) - - r, err2 := msg.GetRoot() - if len(test.ExpectedError) != 0 { - require.EqualError(t, err2, test.ExpectedError) - return - } - require.NoError(t, err2) - require.EqualValues(t, test.ExpectedRoot, hex.EncodeToString(r[:])) -} - -func createCommit(test *spectests.CreateMsgSpecTest) (*genesisspecqbft.SignedMessage, error) { - ks := testingutils.Testing4SharesSet() - state := &genesisspecqbft.State{ - Share: testingutils.TestingShare(ks), - ID: []byte{1, 2, 3, 4}, - } - config := testingutils.TestingConfig(ks) - - return genesisspecqbft.CreateCommit(state, config, test.Value) -} - -func createPrepare(test *spectests.CreateMsgSpecTest) (*genesisspecqbft.SignedMessage, error) { - ks := testingutils.Testing4SharesSet() - state := &genesisspecqbft.State{ - Share: testingutils.TestingShare(ks), - ID: []byte{1, 2, 3, 4}, - } - config := testingutils.TestingConfig(ks) - - return genesisspecqbft.CreatePrepare(state, config, test.Round, test.Value) -} - -func createProposal(test *spectests.CreateMsgSpecTest) (*genesisspecqbft.SignedMessage, error) { - ks := testingutils.Testing4SharesSet() - state := &genesisspecqbft.State{ - Share: testingutils.TestingShare(ks), - ID: []byte{1, 2, 3, 4}, - } - config := testingutils.TestingConfig(ks) - - return genesisspecqbft.CreateProposal(state, config, test.Value[:], test.RoundChangeJustifications, test.PrepareJustifications) -} - -func createRoundChange(test *spectests.CreateMsgSpecTest) (*genesisspecqbft.SignedMessage, error) { - ks := testingutils.Testing4SharesSet() - state := &genesisspecqbft.State{ - Share: testingutils.TestingShare(ks), - ID: []byte{1, 2, 3, 4}, - PrepareContainer: genesisspecqbft.NewMsgContainer(), - } - config := testingutils.TestingConfig(ks) - - if len(test.PrepareJustifications) > 0 { - state.LastPreparedRound = test.PrepareJustifications[0].Message.Round - state.LastPreparedValue = test.StateValue - - for _, msg := range test.PrepareJustifications { - _, err := state.PrepareContainer.AddFirstMsgForSignerAndRound(msg) - if err != nil { - return nil, errors.Wrap(err, "could not add first message for signer") - } - } - } - - return genesisspecqbft.CreateRoundChange(state, config, 1, test.Value[:]) -} diff --git a/protocol/genesis/qbft/spectest/msg_processing_type.go b/protocol/genesis/qbft/spectest/msg_processing_type.go deleted file mode 100644 index 7dfccbd8e1..0000000000 --- a/protocol/genesis/qbft/spectest/msg_processing_type.go +++ /dev/null @@ -1,105 +0,0 @@ -package qbft - -import ( - "encoding/hex" - "fmt" - "path/filepath" - "reflect" - "testing" - "time" - - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - spectests "github.com/ssvlabs/ssv-spec-pre-cc/qbft/spectest/tests" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - spectestingutils "github.com/ssvlabs/ssv-spec-pre-cc/types/testingutils" - typescomparable "github.com/ssvlabs/ssv-spec-pre-cc/types/testingutils/comparable" - "github.com/ssvlabs/ssv/logging" - "github.com/ssvlabs/ssv/protocol/genesis/qbft" - "github.com/ssvlabs/ssv/protocol/genesis/qbft/instance" - qbfttesting "github.com/ssvlabs/ssv/protocol/genesis/qbft/testing" - protocoltesting "github.com/ssvlabs/ssv/protocol/genesis/testing" - "github.com/stretchr/testify/require" -) - -// RunMsgProcessing processes MsgProcessingSpecTest. It probably may be removed. -func RunMsgProcessing(t *testing.T, test *spectests.MsgProcessingSpecTest) { - overrideStateComparisonForMsgProcessingSpecTest(t, test) - - // a little trick we do to instantiate all the internal instance params - preByts, _ := test.Pre.Encode() - msgId := genesisspecqbft.ControllerIdToMessageID(test.Pre.State.ID) - logger := logging.TestLogger(t) - pre := instance.NewInstance( - qbfttesting.TestingConfig(logger, spectestingutils.KeySetForShare(test.Pre.State.Share), msgId.GetRoleType()), - test.Pre.State.Share, - test.Pre.State.ID, - test.Pre.State.Height, - ) - require.NoError(t, pre.Decode(preByts)) - - preInstance := pre - - // a simple hack to change the proposer func - if preInstance.State.Height == spectests.ChangeProposerFuncInstanceHeight { - preInstance.GetConfig().(*qbft.Config).ProposerF = func(state *genesisspecqbft.State, round genesisspecqbft.Round) genesisspectypes.OperatorID { - return 2 - } - } - - var lastErr error - for _, msg := range test.InputMessages { - _, _, _, err := preInstance.ProcessMsg(logger, msg) - if err != nil { - lastErr = err - } - } - - if len(test.ExpectedError) != 0 { - require.EqualError(t, lastErr, test.ExpectedError, "expected %v, but got %v", test.ExpectedError, lastErr) - } else { - require.NoError(t, lastErr) - } - - postRoot, err := preInstance.State.GetRoot() - require.NoError(t, err) - - // broadcasting is asynchronic, so need to wait a bit before checking - time.Sleep(time.Millisecond * 5) - - // test output message - broadcastedMsgs := preInstance.GetConfig().GetNetwork().(*spectestingutils.TestingNetwork).BroadcastedMsgs - if len(test.OutputMessages) > 0 || len(broadcastedMsgs) > 0 { - require.Len(t, broadcastedMsgs, len(test.OutputMessages)) - - for i, msg := range test.OutputMessages { - r1, _ := msg.GetRoot() - - msg2 := &genesisspecqbft.SignedMessage{} - require.NoError(t, msg2.Decode(broadcastedMsgs[i].Data)) - r2, _ := msg2.GetRoot() - - require.EqualValues(t, r1, r2, fmt.Sprintf("output msg %d roots not equal", i)) - } - } - - require.EqualValues(t, test.PostRoot, hex.EncodeToString(postRoot[:]), "post root not valid") -} - -func overrideStateComparisonForMsgProcessingSpecTest(t *testing.T, test *spectests.MsgProcessingSpecTest) { - specDir, err := protocoltesting.GetSpecDir("", filepath.Join("qbft", "spectest")) - require.NoError(t, err) - test.PostState, err = typescomparable.UnmarshalStateComparison(specDir, test.TestName(), - reflect.TypeOf(test).String(), - &genesisspecqbft.State{}) - require.NoError(t, err) - - r, err := test.PostState.GetRoot() - require.NoError(t, err) - - // backwards compatability test, hard coded post root must be equal to the one loaded from file - if len(test.PostRoot) > 0 { - require.EqualValues(t, test.PostRoot, hex.EncodeToString(r[:])) - } - - test.PostRoot = hex.EncodeToString(r[:]) -} diff --git a/protocol/genesis/qbft/spectest/msg_type.go b/protocol/genesis/qbft/spectest/msg_type.go deleted file mode 100644 index 668c778053..0000000000 --- a/protocol/genesis/qbft/spectest/msg_type.go +++ /dev/null @@ -1,43 +0,0 @@ -package qbft - -import ( - "errors" - "testing" - - spectests "github.com/ssvlabs/ssv-spec-pre-cc/qbft/spectest/tests" - "github.com/stretchr/testify/require" -) - -func RunMsg(t *testing.T, test *spectests.MsgSpecTest) { // using only spec struct so this test can be imported - var lastErr error - - for i, msg := range test.Messages { - if err := msg.Validate(); err != nil { - lastErr = err - continue - } - - if msg.Message.RoundChangePrepared() && len(msg.Message.RoundChangeJustification) == 0 { - lastErr = errors.New("round change justification invalid") - } - - if len(test.EncodedMessages) > 0 { - byts, err := msg.Encode() - require.NoError(t, err) - require.EqualValues(t, test.EncodedMessages[i], byts) - } - - if len(test.ExpectedRoots) > 0 { - r, err := msg.GetRoot() - require.NoError(t, err) - require.EqualValues(t, test.ExpectedRoots[i], r) - } - } - - // check error - if len(test.ExpectedError) != 0 { - require.EqualError(t, lastErr, test.ExpectedError) - } else { - require.NoError(t, lastErr) - } -} diff --git a/protocol/genesis/qbft/spectest/qbft_mapping_test.go b/protocol/genesis/qbft/spectest/qbft_mapping_test.go deleted file mode 100644 index 697b87a52f..0000000000 --- a/protocol/genesis/qbft/spectest/qbft_mapping_test.go +++ /dev/null @@ -1,120 +0,0 @@ -package qbft - -import ( - "encoding/json" - "os" - "reflect" - "strings" - "testing" - - spectests "github.com/ssvlabs/ssv-spec-pre-cc/qbft/spectest/tests" - "github.com/ssvlabs/ssv-spec-pre-cc/qbft/spectest/tests/timeout" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - "github.com/ssvlabs/ssv-spec-pre-cc/types/testingutils" - "github.com/stretchr/testify/require" - - "github.com/ssvlabs/ssv/logging" - testing2 "github.com/ssvlabs/ssv/protocol/genesis/qbft/testing" - - "github.com/ssvlabs/ssv/protocol/genesis/qbft/instance" - protocoltesting "github.com/ssvlabs/ssv/protocol/genesis/testing" - "github.com/ssvlabs/ssv/protocol/genesis/types" -) - -func TestQBFTMapping(t *testing.T) { - path, _ := os.Getwd() - jsonTests, err := protocoltesting.GetSpecTestJSON(path, "qbft") - require.NoError(t, err) - - untypedTests := map[string]interface{}{} - if err := json.Unmarshal(jsonTests, &untypedTests); err != nil { - panic(err.Error()) - } - - types.SetDefaultDomain(testingutils.TestingSSVDomainType) - - for name, test := range untypedTests { - name, test := name, test - testName := strings.Split(name, "_")[1] - testType := strings.Split(name, "_")[0] - switch testType { - case reflect.TypeOf(&spectests.MsgProcessingSpecTest{}).String(): - byts, err := json.Marshal(test) - require.NoError(t, err) - typedTest := &spectests.MsgProcessingSpecTest{} - require.NoError(t, json.Unmarshal(byts, &typedTest)) - - t.Run(typedTest.TestName(), func(t *testing.T) { - t.Parallel() - RunMsgProcessing(t, typedTest) - }) - case reflect.TypeOf(&spectests.MsgSpecTest{}).String(): - byts, err := json.Marshal(test) - require.NoError(t, err) - typedTest := &spectests.MsgSpecTest{} - require.NoError(t, json.Unmarshal(byts, &typedTest)) - - t.Run(typedTest.TestName(), func(t *testing.T) { - t.Parallel() - RunMsg(t, typedTest) - }) - case reflect.TypeOf(&spectests.ControllerSpecTest{}).String(): - byts, err := json.Marshal(test) - require.NoError(t, err) - typedTest := &spectests.ControllerSpecTest{} - require.NoError(t, json.Unmarshal(byts, &typedTest)) - - t.Run(typedTest.TestName(), func(t *testing.T) { - t.Parallel() - RunControllerSpecTest(t, typedTest) - }) - case reflect.TypeOf(&spectests.CreateMsgSpecTest{}).String(): - byts, err := json.Marshal(test) - require.NoError(t, err) - typedTest := &spectests.CreateMsgSpecTest{} - require.NoError(t, json.Unmarshal(byts, &typedTest)) - - t.Run(typedTest.TestName(), func(t *testing.T) { - t.Parallel() - RunCreateMsg(t, typedTest) - }) - case reflect.TypeOf(&spectests.RoundRobinSpecTest{}).String(): - byts, err := json.Marshal(test) - require.NoError(t, err) - typedTest := &spectests.RoundRobinSpecTest{} - require.NoError(t, json.Unmarshal(byts, &typedTest)) - - t.Run(typedTest.TestName(), func(t *testing.T) { // using only spec struct so no need to run our version (TODO: check how we choose leader) - t.Parallel() - typedTest.Run(t) - }) - /*t.Run(typedTest.TestName(), func(t *testing.T) { - RunMsg(t, typedTest) - })*/ - case reflect.TypeOf(&timeout.SpecTest{}).String(): - byts, err := json.Marshal(test) - require.NoError(t, err) - typedTest := &SpecTest{} - require.NoError(t, json.Unmarshal(byts, &typedTest)) - - // a little trick we do to instantiate all the internal instance params - - identifier := genesisspectypes.MessageIDFromBytes(typedTest.Pre.State.ID) - preByts, _ := typedTest.Pre.Encode() - logger := logging.TestLogger(t) - pre := instance.NewInstance( - testing2.TestingConfig(logger, testingutils.KeySetForShare(typedTest.Pre.State.Share), identifier.GetRoleType()), - typedTest.Pre.State.Share, - typedTest.Pre.State.ID, - typedTest.Pre.State.Height, - ) - err = pre.Decode(preByts) - require.NoError(t, err) - typedTest.Pre = pre - - RunTimeout(t, typedTest) - default: - t.Fatalf("unsupported test type %s [%s]", testType, testName) - } - } -} diff --git a/protocol/genesis/qbft/spectest/tests.json b/protocol/genesis/qbft/spectest/tests.json deleted file mode 100644 index 2059ea2793..0000000000 --- a/protocol/genesis/qbft/spectest/tests.json +++ /dev/null @@ -1 +0,0 @@ -{"*futuremsg.ControllerSyncSpecTest_qbft controller sync f+1 future msgs":{"Name":"f+1 future msgs","InputMessages":[{"Signature":"qdQ+ADWgukmDciZHW+6Sd88YaY0nnNcWBZiC64kTCWtmfwZIezDAuXOyI4xvB7IoCzHDKbAYrE2DnVGtAQXw1ruxwz2yOe1Hozf9Oa6/JCDxJSaMJVCxna5898mAsBbF","Signers":[4],"Message":{"MsgType":2,"Height":5,"Round":1,"Identifier":"AAADAY6ABmVRqBsxglhwntr33R9jzWhqDk24spu7es/mVghnevWlJ9lEjuR4NUheArULwAAAAAA=","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"tCRX3kv0EBf8PQZeppWj96Iir6gB4C7TxKbSn9IW3Mudy5RqDEK5C+a7/rXwbN1nEWm2dtDf3iRj30NNVBjlv65UbEi088s9Ck+Apz8Nlp7YPQ4UCKS0OdCxvlCVrXce","Signers":[3],"Message":{"MsgType":1,"Height":10,"Round":3,"Identifier":"AAADAY6ABmVRqBsxglhwntr33R9jzWhqDk24spu7es/mVghnevWlJ9lEjuR4NUheArULwAAAAAA=","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"SyncDecidedCalledCnt":1,"ControllerPostRoot":"98c0625374dc64e6350eb704812d9222f9a6121a87c2a55a8b1a3f8790e87c77","ExpectedError":""},"*futuremsg.ControllerSyncSpecTest_qbft controller sync future invalid msg":{"Name":"future invalid msg","InputMessages":[{"Signature":"tCRX3kv0EBf8PQZeppWj96Iir6gB4C7TxKbSn9IW3Mudy5RqDEK5C+a7/rXwbN1nEWm2dtDf3iRj30NNVBjlv65UbEi088s9Ck+Apz8Nlp7YPQ4UCKS0OdCxvlCVrXce","Signers":null,"Message":{"MsgType":1,"Height":10,"Round":3,"Identifier":"AAADAY6ABmVRqBsxglhwntr33R9jzWhqDk24spu7es/mVghnevWlJ9lEjuR4NUheArULwAAAAAA=","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"SyncDecidedCalledCnt":0,"ControllerPostRoot":"3b9cd21ca426a4e9e3188e0c8d931861a8f263636c4c0369da84fe9a99fb2fa5","ExpectedError":"invalid future msg: invalid decided msg: message signers is empty"},"*futuremsg.ControllerSyncSpecTest_qbft controller sync future msg duplicate signer":{"Name":"future msg duplicate signer","InputMessages":[{"Signature":"qdQ+ADWgukmDciZHW+6Sd88YaY0nnNcWBZiC64kTCWtmfwZIezDAuXOyI4xvB7IoCzHDKbAYrE2DnVGtAQXw1ruxwz2yOe1Hozf9Oa6/JCDxJSaMJVCxna5898mAsBbF","Signers":[4],"Message":{"MsgType":2,"Height":5,"Round":1,"Identifier":"AAADAY6ABmVRqBsxglhwntr33R9jzWhqDk24spu7es/mVghnevWlJ9lEjuR4NUheArULwAAAAAA=","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"tCRX3kv0EBf8PQZeppWj96Iir6gB4C7TxKbSn9IW3Mudy5RqDEK5C+a7/rXwbN1nEWm2dtDf3iRj30NNVBjlv65UbEi088s9Ck+Apz8Nlp7YPQ4UCKS0OdCxvlCVrXce","Signers":[3],"Message":{"MsgType":1,"Height":10,"Round":3,"Identifier":"AAADAY6ABmVRqBsxglhwntr33R9jzWhqDk24spu7es/mVghnevWlJ9lEjuR4NUheArULwAAAAAA=","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gdam9rNZcT+A8Ml5kU60o9u0hMMkoGlevF4QO3xybve5VqA4lyzwAzXM2Hs5AsfyDSt5i2M/jsIXpVt9t5vTJ/svH8rtuUMHYGQ1Dlhc25nSIC3WqSXcsOJPgV0aLq64","Signers":[4],"Message":{"MsgType":1,"Height":6,"Round":1,"Identifier":"AAADAY6ABmVRqBsxglhwntr33R9jzWhqDk24spu7es/mVghnevWlJ9lEjuR4NUheArULwAAAAAA=","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"tNtqCygW/kk8Kqi9j9kYCmf8oTjs/T+EUgenesis7XQ+4Vslju4KbJpM95KRloopdGwmDAOLG41wDJYyxki49u9sNFhV0auKDnyY53Z3n/uZVkfe3r74mVqyLwpb8LlwH27wW","Signers":[4],"Message":{"MsgType":3,"Height":2,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"iUhHrZHstt7z6rmN4x1AEMsPM7pNqkILKCvp/y80iWfvKSwsP60rlzuT/GDsy0y3FFqdrMjLFjMNpu/+Bg5vwljyBwsu3yzXs6pnej1uVegh1MvE5nU9hqwwhrHmrA3e","Signers":[4],"Message":{"MsgType":2,"Height":50,"Round":1,"Identifier":"AAADAY6ABmVRqBsxglhwntr33R9jzWhqDk24spu7es/mVghnevWlJ9lEjuR4NUheArULwAAAAAA=","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"SyncDecidedCalledCnt":1,"ControllerPostRoot":"98c0625374dc64e6350eb704812d9222f9a6121a87c2a55a8b1a3f8790e87c77","ExpectedError":"discarded future msg"},"*futuremsg.ControllerSyncSpecTest_qbft controller sync future msg unknown signer":{"Name":"future msg unknown signer","InputMessages":[{"Signature":"tCRX3kv0EBf8PQZeppWj96Iir6gB4C7TxKbSn9IW3Mudy5RqDEK5C+a7/rXwbN1nEWm2dtDf3iRj30NNVBjlv65UbEi088s9Ck+Apz8Nlp7YPQ4UCKS0OdCxvlCVrXce","Signers":[10],"Message":{"MsgType":1,"Height":10,"Round":3,"Identifier":"AAADAY6ABmVRqBsxglhwntr33R9jzWhqDk24spu7es/mVghnevWlJ9lEjuR4NUheArULwAAAAAA=","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"SyncDecidedCalledCnt":0,"ControllerPostRoot":"3b9cd21ca426a4e9e3188e0c8d931861a8f263636c4c0369da84fe9a99fb2fa5","ExpectedError":"invalid future msg: msg signature invalid: unknown signer"},"*futuremsg.ControllerSyncSpecTest_qbft controller sync future msg wrong sig":{"Name":"future msg wrong sig","InputMessages":[{"Signature":"tCRX3kv0EBf8PQZeppWj96Iir6gB4C7TxKbSn9IW3Mudy5RqDEK5C+a7/rXwbN1nEWm2dtDf3iRj30NNVBjlv65UbEi088s9Ck+Apz8Nlp7YPQ4UCKS0OdCxvlCVrXce","Signers":[2],"Message":{"MsgType":1,"Height":10,"Round":3,"Identifier":"AAADAY6ABmVRqBsxglhwntr33R9jzWhqDk24spu7es/mVghnevWlJ9lEjuR4NUheArULwAAAAAA=","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"SyncDecidedCalledCnt":0,"ControllerPostRoot":"3b9cd21ca426a4e9e3188e0c8d931861a8f263636c4c0369da84fe9a99fb2fa5","ExpectedError":"invalid future msg: msg signature invalid: failed to verify signature"},"*futuremsg.ControllerSyncSpecTest_qbft controller sync future msgs cleanup":{"Name":"future msgs cleanup","InputMessages":[{"Signature":"qdQ+ADWgukmDciZHW+6Sd88YaY0nnNcWBZiC64kTCWtmfwZIezDAuXOyI4xvB7IoCzHDKbAYrE2DnVGtAQXw1ruxwz2yOe1Hozf9Oa6/JCDxJSaMJVCxna5898mAsBbF","Signers":[4],"Message":{"MsgType":2,"Height":5,"Round":1,"Identifier":"AAADAY6ABmVRqBsxglhwntr33R9jzWhqDk24spu7es/mVghnevWlJ9lEjuR4NUheArULwAAAAAA=","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"tCRX3kv0EBf8PQZeppWj96Iir6gB4C7TxKbSn9IW3Mudy5RqDEK5C+a7/rXwbN1nEWm2dtDf3iRj30NNVBjlv65UbEi088s9Ck+Apz8Nlp7YPQ4UCKS0OdCxvlCVrXce","Signers":[3],"Message":{"MsgType":1,"Height":10,"Round":3,"Identifier":"AAADAY6ABmVRqBsxglhwntr33R9jzWhqDk24spu7es/mVghnevWlJ9lEjuR4NUheArULwAAAAAA=","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"oHukGZD+I5e2rkfehhlzyoo8Oz9UdWT6QTimHbwOBoAD/+7KZdeNmvq/08hqxPHWAWjoF7FGsi4lyOI7C5yYIxmSufvdMA9h40YGXAdbgPlPhgD2cBFfWbzaTvUrs9Oq","Signers":[1,2,3],"Message":{"MsgType":2,"Height":10,"Round":1,"Identifier":"AAADAY6ABmVRqBsxglhwntr33R9jzWhqDk24spu7es/mVghnevWlJ9lEjuR4NUheArULwAAAAAA=","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"k3RWxGX/xvWBE9J2aRapYewQJOUnACR2ljEKkEaw5fT5ubLqrW8/3/GNTADdkHB6GHtEU2+zYYkM3aHW+uotZYSYyH5PAc0TpBxWMmcS9t1H0KI5WgBYkf2UtgPj5eKb","Signers":[2],"Message":{"MsgType":1,"Height":11,"Round":3,"Identifier":"AAADAY6ABmVRqBsxglhwntr33R9jzWhqDk24spu7es/mVghnevWlJ9lEjuR4NUheArULwAAAAAA=","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"SyncDecidedCalledCnt":1,"ControllerPostRoot":"cd0e7827cc4f55c6972925aa38b79050ae7f99d6083032127b9ee1fbd28caae0","ExpectedError":""},"*futuremsg.ControllerSyncSpecTest_qbft controller sync future msgs multiple signers":{"Name":"future msgs multiple signers","InputMessages":[{"Signature":"tLalCvw1/Ewt25LP9AZBn52X2kYiN9+xygBp/U+XHjctnP70ZAx/DElZlWIToEvAGCJ+2DH/JHw/EwyIc0mNJEftt9/xuEG7pz5GywSvhIjES3wZwXOdkdKIokf/buk9","Signers":[1,2,3],"Message":{"MsgType":1,"Height":2,"Round":1,"Identifier":"AAADAY6ABmVRqBsxglhwntr33R9jzWhqDk24spu7es/mVghnevWlJ9lEjuR4NUheArULwAAAAAA=","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"SyncDecidedCalledCnt":0,"ControllerPostRoot":"3b9cd21ca426a4e9e3188e0c8d931861a8f263636c4c0369da84fe9a99fb2fa5","ExpectedError":"invalid future msg: allows 1 signer"},"*futuremsg.ControllerSyncSpecTest_qbft controller sync future msgs no signer":{"Name":"future msgs no signer","InputMessages":[{"Signature":"tCRX3kv0EBf8PQZeppWj96Iir6gB4C7TxKbSn9IW3Mudy5RqDEK5C+a7/rXwbN1nEWm2dtDf3iRj30NNVBjlv65UbEi088s9Ck+Apz8Nlp7YPQ4UCKS0OdCxvlCVrXce","Signers":[],"Message":{"MsgType":1,"Height":10,"Round":3,"Identifier":"AAADAY6ABmVRqBsxglhwntr33R9jzWhqDk24spu7es/mVghnevWlJ9lEjuR4NUheArULwAAAAAA=","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"SyncDecidedCalledCnt":0,"ControllerPostRoot":"3b9cd21ca426a4e9e3188e0c8d931861a8f263636c4c0369da84fe9a99fb2fa5","ExpectedError":"invalid future msg: invalid decided msg: message signers is empty"},"*tests.ControllerSpecTest_qbft controller broadcast decided":{"Name":"broadcast decided","RunInstanceData":[{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooE","Signers":[2],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRM","Signers":[3],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"kFGy1aK4Kychuv0xFbXRVjFgDU6hCAaltJ+Nbjsie+2yEmuFSwss4IiyekCVHu+1F9TN31CUv+uZ4FwzPQSkR5oM709TN/61iEcY4j44Zm/OXL8pnEhSipyXSSngprJ6","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"q+s2ftJ53lt+iNxpg6bfNxGrjGkY19VA5zRn92qJueuJgKCVyLQkrpNymxHC5bQADhtbiD8/LNzJ9bB0w14T3M8xooKaUCt6IX4qh5wqkfAicOB+KZ4+wGTfq/XQBGaj","Signers":[2],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"sbN4I4KmsP1QwuIS5hztulBMjvjyZsJIxc7VnPfzdBMPLq+BImXyrU2wPwzSyjPJDXsE9EQFb2D7NPnCA/gIutwGbRK/kwdRYUKH6VL5L5qPmw7Be2nDhly1J9iELyIq","Signers":[3],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"ControllerPostRoot":"24cf697092529cfab3ab06b969d8696692c8bcbb9f41a954f71dc74c3b1d7e97","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":{"Signature":"ok+cfOp6t9/h8xvePHOpDCnIif+wMjLeYWhHv5/Csldj3S/G2PgaYaZhxpSKKURCFXRMTSP1OhfbMinxQrnxZYg7SP2C6NDOa/9vV6NLaX/eusH+W91JkAgPd+seFNhr","Signers":[1,2,3],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}}],"OutputMessages":null,"ExpectedError":""},"*tests.ControllerSpecTest_qbft controller decide current instance":{"Name":"decide current instance","RunInstanceData":[{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooE","Signers":[2],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRM","Signers":[3],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"kFGy1aK4Kychuv0xFbXRVjFgDU6hCAaltJ+Nbjsie+2yEmuFSwss4IiyekCVHu+1F9TN31CUv+uZ4FwzPQSkR5oM709TN/61iEcY4j44Zm/OXL8pnEhSipyXSSngprJ6","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"q+s2ftJ53lt+iNxpg6bfNxGrjGkY19VA5zRn92qJueuJgKCVyLQkrpNymxHC5bQADhtbiD8/LNzJ9bB0w14T3M8xooKaUCt6IX4qh5wqkfAicOB+KZ4+wGTfq/XQBGaj","Signers":[2],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"ok+cfOp6t9/h8xvePHOpDCnIif+wMjLeYWhHv5/Csldj3S/G2PgaYaZhxpSKKURCFXRMTSP1OhfbMinxQrnxZYg7SP2C6NDOa/9vV6NLaX/eusH+W91JkAgPd+seFNhr","Signers":[1,2,3],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ControllerPostRoot":"9a5519c1584d326e7c20cb1a824af5dc7139bac2f4d1ced2ff13b5aacfe9ab33","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":null,"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}}],"OutputMessages":null,"ExpectedError":""},"*tests.ControllerSpecTest_qbft controller decide current instance future round":{"Name":"decide current instance future round","RunInstanceData":[{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooE","Signers":[2],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRM","Signers":[3],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"kFGy1aK4Kychuv0xFbXRVjFgDU6hCAaltJ+Nbjsie+2yEmuFSwss4IiyekCVHu+1F9TN31CUv+uZ4FwzPQSkR5oM709TN/61iEcY4j44Zm/OXL8pnEhSipyXSSngprJ6","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"q+s2ftJ53lt+iNxpg6bfNxGrjGkY19VA5zRn92qJueuJgKCVyLQkrpNymxHC5bQADhtbiD8/LNzJ9bB0w14T3M8xooKaUCt6IX4qh5wqkfAicOB+KZ4+wGTfq/XQBGaj","Signers":[2],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"paIoE41obXeitOeomDBwSAIQibDADOZT3ZRkjXlwUMOy9+6zF0u87JYbwMl4O/CoBYcAb0BxvVNeozrjJppHQr42t+++8QegpTQ81HRxd7nEIoLwMZ8fAV8w90YaVV+C","Signers":[1,2,3],"Message":{"MsgType":2,"Height":0,"Round":50,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ControllerPostRoot":"22a652c6cf6e1243c2a82a72c31ae733e614e5f886e700acaa81210f04659664","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":null,"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}}],"OutputMessages":null,"ExpectedError":""},"*tests.ControllerSpecTest_qbft controller decide current instance past round":{"Name":"decide current instance past round","RunInstanceData":[{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooE","Signers":[2],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"t3wDXB0dnGx8wigQxQqFyeVgrXkVCQVQYd/gfUA+2qEwQWFGbUn2SES25c9LCXCfBp3r/ZFDj5fUFKT2TNy0+M8elwP42hQb6fdFCQh+L4RJIxSwNBlmxLj8Ftkx9roI","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"mMq307rvpww3BxdPm0KCuat1CPoddAl9w/tnc524RivFpWby1PNqd9ymZl6/aFkOGOkeJoqoIZJgn22ff+c9UplIJTKxcIGm5d70ocze3Sx7TCq+A2xixwEsOb/A0831","Signers":[2],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"lLT5TkZ3/7MmNTrG07fwT6CwDZ4una1ok5q6S7EHMBfoBJSNN5XOl3X1Hir/GbWLDs+i2Xrwlu+WjIqNYsaICol14dSQYu1LErxRH0dre4CUBLD7uPMIRUej/0GNt2S8","Signers":[3],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"qV5LdutK2zk8Xbqu+xKdm1T33abJK26BV1I8cmor0GIZQdtFEJh4KP/6KMA1IZRSAH7cSAX9fvXO4/r4cVjuCRIFcdRL/cpnvfcAoTu0T8FBW1XqybwpwJcqglpkSvPk","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":["t3wDXB0dnGx8wigQxQqFyeVgrXkVCQVQYd/gfUA+2qEwQWFGbUn2SES25c9LCXCfBp3r/ZFDj5fUFKT2TNy0+M8elwP42hQb6fdFCQh+L4RJIxSwNBlmxLj8Ftkx9roIbAAAAHQAAADEAAAAAQAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","mMq307rvpww3BxdPm0KCuat1CPoddAl9w/tnc524RivFpWby1PNqd9ymZl6/aFkOGOkeJoqoIZJgn22ff+c9UplIJTKxcIGm5d70ocze3Sx7TCq+A2xixwEsOb/A0831bAAAAHQAAADEAAAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","lLT5TkZ3/7MmNTrG07fwT6CwDZ4una1ok5q6S7EHMBfoBJSNN5XOl3X1Hir/GbWLDs+i2Xrwlu+WjIqNYsaICol14dSQYu1LErxRH0dre4CUBLD7uPMIRUej/0GNt2S8bAAAAHQAAADEAAAAAwAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gAnu0h9b+GhMBWRD5uGwXQLZU+IkR7T5Han28nJNGHmWkWOOmJloo2EsNkho6+tEDcKeEKScBqDMt0Ms+GE7aUfTkeP8K8UDOm/lIlv6SEzFBlTgSaJTRmuybOX1x+Ep","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"g/mKHqmW47ekvXEY62N5fKTQ9oHBU5QKSZxjehyH9MZ8X7t4egnx/1F3SU39doGDA+eJlA2/r0rw2QzdjPbXyBY16RrkoAqqOgItSNzsCEvJl3pEsE9MVp8Ry8r3vs9T","Signers":[2],"Message":{"MsgType":1,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"j7thZ1r0Gs6AEJKBXe2IpYOkIQrN1ggSWQjb6Nz7KLazl+4Iikq9xEPjgs/oiNbYFB5BWknHZR1t3QMWWu/45saLm+WS289AZ503i9GVNM1Rcs+O845PvoFYjWstIUOB","Signers":[3],"Message":{"MsgType":1,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"g4KG9ysNs6oCilSUUjIeaLCVxNkOb04OhT3OxuqzozSCU7YyIwKguDcTNel9Dw0tCpaulMPrIm31VD5eE9OH5DklvF2hNjeVT/3YWtX2brkMuF9zag142guBiAnML6In","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"gsr1DCLhAStwnKO6zt7HBr7lFNjRQuInb4Ny/Ty+EyPV/fpB3txoI1NwJYHdRsl1E4EKSwxAPeyxJBD+Uqj9Yjq8wBYjWz+9xWgNIH73kln1XYVmWCZ35mhzUPaF0jWW","Signers":[2],"Message":{"MsgType":2,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"ok+cfOp6t9/h8xvePHOpDCnIif+wMjLeYWhHv5/Csldj3S/G2PgaYaZhxpSKKURCFXRMTSP1OhfbMinxQrnxZYg7SP2C6NDOa/9vV6NLaX/eusH+W91JkAgPd+seFNhr","Signers":[1,2,3],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ControllerPostRoot":"a96bbf6034a156f6a7196c255e60b5f6a592ab30130cab0d3b4ac2cbc1dd9ac4","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":null,"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}}],"OutputMessages":null,"ExpectedError":""},"*tests.ControllerSpecTest_qbft controller decide duplicate msg":{"Name":"decide duplicate msg","RunInstanceData":[{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"tDaFqXgayP7v782nniCBFQ0egTOpUlUM8p1LlTZAsnn1TYNWCI4qcYmlo3brXmO4F9207XiuHXeeq8XflMaQKg/cyb90ZOSzZK5c9XoSRf3gSirLDwZGRYWs3CWKgjzf","Signers":[1,2,3],"Message":{"MsgType":2,"Height":10,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"tDaFqXgayP7v782nniCBFQ0egTOpUlUM8p1LlTZAsnn1TYNWCI4qcYmlo3brXmO4F9207XiuHXeeq8XflMaQKg/cyb90ZOSzZK5c9XoSRf3gSirLDwZGRYWs3CWKgjzf","Signers":[1,2,3],"Message":{"MsgType":2,"Height":10,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ControllerPostRoot":"589b0c0352f1c22875246f2e66530d5fda62f646434b250ade128c61c16f47bd","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":null,"CalledSyncDecidedByRange":true,"DecidedByRangeValues":[0,10]}}],"OutputMessages":null,"ExpectedError":""},"*tests.ControllerSpecTest_qbft controller decide duplicate signer":{"Name":"decide duplicate signer","RunInstanceData":[{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"tDaFqXgayP7v782nniCBFQ0egTOpUlUM8p1LlTZAsnn1TYNWCI4qcYmlo3brXmO4F9207XiuHXeeq8XflMaQKg/cyb90ZOSzZK5c9XoSRf3gSirLDwZGRYWs3CWKgjzf","Signers":[1,2,2],"Message":{"MsgType":2,"Height":10,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ControllerPostRoot":"47713c38fe74ce55959980781287886c603c2117a14dc8abce24dcb9be0093af","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":null,"DecidedCnt":0,"BroadcastedDecided":null,"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}}],"OutputMessages":null,"ExpectedError":"invalid decided msg: invalid decided msg: non unique signer"},"*tests.ControllerSpecTest_qbft controller decide future instance":{"Name":"decide future instance","RunInstanceData":[{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"tDaFqXgayP7v782nniCBFQ0egTOpUlUM8p1LlTZAsnn1TYNWCI4qcYmlo3brXmO4F9207XiuHXeeq8XflMaQKg/cyb90ZOSzZK5c9XoSRf3gSirLDwZGRYWs3CWKgjzf","Signers":[1,2,3],"Message":{"MsgType":2,"Height":10,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ControllerPostRoot":"589b0c0352f1c22875246f2e66530d5fda62f646434b250ade128c61c16f47bd","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":null,"CalledSyncDecidedByRange":true,"DecidedByRangeValues":[0,10]}}],"OutputMessages":null,"ExpectedError":""},"*tests.ControllerSpecTest_qbft controller decide has quorum":{"Name":"decide has quorum","RunInstanceData":[{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"tDaFqXgayP7v782nniCBFQ0egTOpUlUM8p1LlTZAsnn1TYNWCI4qcYmlo3brXmO4F9207XiuHXeeq8XflMaQKg/cyb90ZOSzZK5c9XoSRf3gSirLDwZGRYWs3CWKgjzf","Signers":[1,2,3],"Message":{"MsgType":2,"Height":10,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ControllerPostRoot":"589b0c0352f1c22875246f2e66530d5fda62f646434b250ade128c61c16f47bd","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":null,"CalledSyncDecidedByRange":true,"DecidedByRangeValues":[0,10]}}],"OutputMessages":null,"ExpectedError":""},"*tests.ControllerSpecTest_qbft controller decide invalid msg":{"Name":"decide invalid msg","RunInstanceData":[{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"tDaFqXgayP7v782nniCBFQ0egTOpUlUM8p1LlTZAsnn1TYNWCI4qcYmlo3brXmO4F9207XiuHXeeq8XflMaQKg/cyb90ZOSzZK5c9XoSRf3gSirLDwZGRYWs3CWKgjzf","Signers":[],"Message":{"MsgType":2,"Height":10,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ControllerPostRoot":"47713c38fe74ce55959980781287886c603c2117a14dc8abce24dcb9be0093af","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":null,"DecidedCnt":0,"BroadcastedDecided":null,"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}}],"OutputMessages":null,"ExpectedError":"invalid future msg: invalid decided msg: message signers is empty"},"*tests.ControllerSpecTest_qbft controller decide invalid value (should pass)":{"Name":"decide invalid value (should pass)","RunInstanceData":[{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"lvAUf9IwsF59HHfQHKKW25goi3OaT3zpWeRYiBi2hbKx9405qkq14hGxnERfJOgZFbwFmwd6+qjIr5+goUnUgBEfSAhaVP89Kh77G3AGeBF3AnhaDly2ENQ7cdseSuNE","Signers":[1,2,3],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[39,236,208,165,152,231,111,138,47,210,100,212,39,223,10,17,153,3,232,234,227,132,228,120,144,37,65,117,111,8,157,209],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQEBAQ=="}],"ControllerPostRoot":"c7420429b97ed92ad8f21bdef11421c74d86c5d93131a8942d825d1c0aab969c","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQEBAQ==","DecidedCnt":1,"BroadcastedDecided":null,"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}}],"OutputMessages":null,"ExpectedError":""},"*tests.ControllerSpecTest_qbft controller decide late decided":{"Name":"decide late decided","RunInstanceData":[{"InputValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","InputMessages":[{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooE","Signers":[2],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRM","Signers":[3],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"kFGy1aK4Kychuv0xFbXRVjFgDU6hCAaltJ+Nbjsie+2yEmuFSwss4IiyekCVHu+1F9TN31CUv+uZ4FwzPQSkR5oM709TN/61iEcY4j44Zm/OXL8pnEhSipyXSSngprJ6","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"q+s2ftJ53lt+iNxpg6bfNxGrjGkY19VA5zRn92qJueuJgKCVyLQkrpNymxHC5bQADhtbiD8/LNzJ9bB0w14T3M8xooKaUCt6IX4qh5wqkfAicOB+KZ4+wGTfq/XQBGaj","Signers":[2],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"sbN4I4KmsP1QwuIS5hztulBMjvjyZsJIxc7VnPfzdBMPLq+BImXyrU2wPwzSyjPJDXsE9EQFb2D7NPnCA/gIutwGbRK/kwdRYUKH6VL5L5qPmw7Be2nDhly1J9iELyIq","Signers":[3],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"iXRusUDDnhYVyxBMh6EVwET3rMRhjDOIEyp3XEJyeOkeNm7CRQ2YA7HZlgjEixksFSzI0EBCUrlbHN3WMBleFcDPZk7Gee/8KWJomWQWn1genesisvw6bFY/alyaadyPArGiZ","Signers":[1,2,4],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ControllerPostRoot":"4c96913e87aa17c9f0d5c1b6b220cbc7a66b7b40ef55f1059f1e1fa9f59c94d9","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":{"Signature":"ok+cfOp6t9/h8xvePHOpDCnIif+wMjLeYWhHv5/Csldj3S/G2PgaYaZhxpSKKURCFXRMTSP1OhfbMinxQrnxZYg7SP2C6NDOa/9vV6NLaX/eusH+W91JkAgPd+seFNhr","Signers":[1,2,3],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}}],"OutputMessages":null,"ExpectedError":""},"*tests.ControllerSpecTest_qbft controller decide late decided bigger quorum":{"Name":"decide late decided bigger quorum","RunInstanceData":[{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooE","Signers":[2],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRM","Signers":[3],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"kFGy1aK4Kychuv0xFbXRVjFgDU6hCAaltJ+Nbjsie+2yEmuFSwss4IiyekCVHu+1F9TN31CUv+uZ4FwzPQSkR5oM709TN/61iEcY4j44Zm/OXL8pnEhSipyXSSngprJ6","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"q+s2ftJ53lt+iNxpg6bfNxGrjGkY19VA5zRn92qJueuJgKCVyLQkrpNymxHC5bQADhtbiD8/LNzJ9bB0w14T3M8xooKaUCt6IX4qh5wqkfAicOB+KZ4+wGTfq/XQBGaj","Signers":[2],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"sbN4I4KmsP1QwuIS5hztulBMjvjyZsJIxc7VnPfzdBMPLq+BImXyrU2wPwzSyjPJDXsE9EQFb2D7NPnCA/gIutwGbRK/kwdRYUKH6VL5L5qPmw7Be2nDhly1J9iELyIq","Signers":[3],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"oHWjKW3atb2+we1iUxg6l04aGid3hz9KZwWQ42TsN5pWJHMvMDWtFSiZO0Aky/t7ElJ4KHD7+uGrgYPzSL/uNESPG5UlMr4VpvDPu0q5X3BwymfRvFJpCngPsARrIq3g","Signers":[1,2,3,4],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ControllerPostRoot":"b61697f2ff067f353262fec6946aaf5c0ee94fd56e98128757fe737af83b754d","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":{"Signature":"ok+cfOp6t9/h8xvePHOpDCnIif+wMjLeYWhHv5/Csldj3S/G2PgaYaZhxpSKKURCFXRMTSP1OhfbMinxQrnxZYg7SP2C6NDOa/9vV6NLaX/eusH+W91JkAgPd+seFNhr","Signers":[1,2,3],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}}],"OutputMessages":null,"ExpectedError":""},"*tests.ControllerSpecTest_qbft controller decide late decided smaller quorum":{"Name":"decide late decided smaller quorum","RunInstanceData":[{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"oHWjKW3atb2+we1iUxg6l04aGid3hz9KZwWQ42TsN5pWJHMvMDWtFSiZO0Aky/t7ElJ4KHD7+uGrgYPzSL/uNESPG5UlMr4VpvDPu0q5X3BwymfRvFJpCngPsARrIq3g","Signers":[1,2,3,4],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"ok+cfOp6t9/h8xvePHOpDCnIif+wMjLeYWhHv5/Csldj3S/G2PgaYaZhxpSKKURCFXRMTSP1OhfbMinxQrnxZYg7SP2C6NDOa/9vV6NLaX/eusH+W91JkAgPd+seFNhr","Signers":[1,2,3],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ControllerPostRoot":"c8ea9638aa8aa332763f5b79fe26d88bbcb173837b64545d871eec89df6b7ee5","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":null,"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}}],"OutputMessages":null,"ExpectedError":""},"*tests.ControllerSpecTest_qbft controller decide no quorum":{"Name":"decide no quorum","RunInstanceData":[{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"tI7cs4tt4ryjPbVqnjDIFO5IkD+RVPWbrCTsH/4NhRY7Eq2HHAt+mMQ0w+20EVM/CDOSYr7CHYyPxX+oQbAeiX4diH7n7H0AxD1SYxsTJFWmp6vrPM4NlPBvdzWqow9i","Signers":[1,2],"Message":{"MsgType":2,"Height":10,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ControllerPostRoot":"47713c38fe74ce55959980781287886c603c2117a14dc8abce24dcb9be0093af","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":null,"DecidedCnt":0,"BroadcastedDecided":null,"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}}],"OutputMessages":null,"ExpectedError":"invalid future msg: allows 1 signer"},"*tests.ControllerSpecTest_qbft controller decide past instance":{"Name":"decide past instance","RunInstanceData":[{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"mJrQ0dudEveNvHqOqocVdsVQLJmfbfHkYt3CWuKxPPssZutcF2YoYTWDr9DUhu54CqHCBuO/iSqxDL2bAWiGQYMLk1+Av8C4tQPhgenesis9wXnIGq1VXQ2vz+2Dr0GxIVTR0","Signers":[1,2,3],"Message":{"MsgType":2,"Height":100,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"h7qPASSCMkmBEpFPbh/p0BafeFsK/P6r37QXjbJ0ff/uRIO2jRejNKvvznbnVKPzCbhVkdwBbxZ0hPTD36GcOpTJmFefJfuc1YAnsi6CPnINt71MG5HeVxx8aXt4uVW/","Signers":[1,2,3],"Message":{"MsgType":2,"Height":80,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"k0pjSdx3UhkesZ5tJDZYxzyTG08qiUHzFWbjmX4QMpvknwCgbhRZMz2JCKapQRW7CIJ90Q2cfHa6lak/SjDsIOCaP8DZu3KiFR9+X2yEBQisVtWqlk5pvphN4KE/kL3s","Signers":[1,2,3],"Message":{"MsgType":2,"Height":90,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ControllerPostRoot":"d2f7f4bfc09d8695021a3c10657907e6196adda7ff8f06c9b48a368539a2e7bf","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":3,"BroadcastedDecided":null,"CalledSyncDecidedByRange":true,"DecidedByRangeValues":[0,100]}}],"OutputMessages":null,"ExpectedError":""},"*tests.ControllerSpecTest_qbft controller decide unknown signer":{"Name":"decide unknown signer","RunInstanceData":[{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"ok+cfOp6t9/h8xvePHOpDCnIif+wMjLeYWhHv5/Csldj3S/G2PgaYaZhxpSKKURCFXRMTSP1OhfbMinxQrnxZYg7SP2C6NDOa/9vV6NLaX/eusH+W91JkAgPd+seFNhr","Signers":[1,2,5],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ControllerPostRoot":"47713c38fe74ce55959980781287886c603c2117a14dc8abce24dcb9be0093af","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":null,"DecidedCnt":0,"BroadcastedDecided":null,"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}}],"OutputMessages":null,"ExpectedError":"invalid decided msg: invalid decided msg: msg signature invalid: unknown signer"},"*tests.ControllerSpecTest_qbft controller decide wrong msg type":{"Name":"decide wrong msg type","RunInstanceData":[{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"kps+lNVfulVPNKYcsgBRh9Qhh6IedNTkRM83AxDI6fGCtPN/TatW7G7xtQFWNrTKECeH7SBad09OOxrpbWh/l92Q0Rz+HZYfByH46qUYQwEFXSey3fRUF02vP5UVYJMm","Signers":[1,2,3],"Message":{"MsgType":0,"Height":10,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ControllerPostRoot":"47713c38fe74ce55959980781287886c603c2117a14dc8abce24dcb9be0093af","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":null,"DecidedCnt":0,"BroadcastedDecided":null,"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}}],"OutputMessages":null,"ExpectedError":"invalid future msg: allows 1 signer"},"*tests.ControllerSpecTest_qbft controller decide wrong sig":{"Name":"decide wrong sig","RunInstanceData":[{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"iXRusUDDnhYVyxBMh6EVwET3rMRhjDOIEyp3XEJyeOkeNm7CRQ2YA7HZlgjEixksFSzI0EBCUrlbHN3WMBleFcDPZk7Gee/8KWJomWQWn1genesisvw6bFY/alyaadyPArGiZ","Signers":[1,2,3],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ControllerPostRoot":"47713c38fe74ce55959980781287886c603c2117a14dc8abce24dcb9be0093af","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":null,"DecidedCnt":0,"BroadcastedDecided":null,"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}}],"OutputMessages":null,"ExpectedError":"invalid decided msg: invalid decided msg: msg signature invalid: failed to verify signature"},"*tests.ControllerSpecTest_qbft controller full decided":{"Name":"full decided","RunInstanceData":[{"InputValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","InputMessages":[{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooE","Signers":[2],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRM","Signers":[3],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"kFGy1aK4Kychuv0xFbXRVjFgDU6hCAaltJ+Nbjsie+2yEmuFSwss4IiyekCVHu+1F9TN31CUv+uZ4FwzPQSkR5oM709TN/61iEcY4j44Zm/OXL8pnEhSipyXSSngprJ6","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"q+s2ftJ53lt+iNxpg6bfNxGrjGkY19VA5zRn92qJueuJgKCVyLQkrpNymxHC5bQADhtbiD8/LNzJ9bB0w14T3M8xooKaUCt6IX4qh5wqkfAicOB+KZ4+wGTfq/XQBGaj","Signers":[2],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"sbN4I4KmsP1QwuIS5hztulBMjvjyZsJIxc7VnPfzdBMPLq+BImXyrU2wPwzSyjPJDXsE9EQFb2D7NPnCA/gIutwGbRK/kwdRYUKH6VL5L5qPmw7Be2nDhly1J9iELyIq","Signers":[3],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"ControllerPostRoot":"4c96913e87aa17c9f0d5c1b6b220cbc7a66b7b40ef55f1059f1e1fa9f59c94d9","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":{"Signature":"ok+cfOp6t9/h8xvePHOpDCnIif+wMjLeYWhHv5/Csldj3S/G2PgaYaZhxpSKKURCFXRMTSP1OhfbMinxQrnxZYg7SP2C6NDOa/9vV6NLaX/eusH+W91JkAgPd+seFNhr","Signers":[1,2,3],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}}],"OutputMessages":null,"ExpectedError":""},"*tests.ControllerSpecTest_qbft controller full flow after decided":{"Name":"full flow after decided","RunInstanceData":[{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"ok+cfOp6t9/h8xvePHOpDCnIif+wMjLeYWhHv5/Csldj3S/G2PgaYaZhxpSKKURCFXRMTSP1OhfbMinxQrnxZYg7SP2C6NDOa/9vV6NLaX/eusH+W91JkAgPd+seFNhr","Signers":[1,2,3],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"qV5LdutK2zk8Xbqu+xKdm1T33abJK26BV1I8cmor0GIZQdtFEJh4KP/6KMA1IZRSAH7cSAX9fvXO4/r4cVjuCRIFcdRL/cpnvfcAoTu0T8FBW1XqybwpwJcqglpkSvPk","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":["t3wDXB0dnGx8wigQxQqFyeVgrXkVCQVQYd/gfUA+2qEwQWFGbUn2SES25c9LCXCfBp3r/ZFDj5fUFKT2TNy0+M8elwP42hQb6fdFCQh+L4RJIxSwNBlmxLj8Ftkx9roIbAAAAHQAAADEAAAAAQAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","mMq307rvpww3BxdPm0KCuat1CPoddAl9w/tnc524RivFpWby1PNqd9ymZl6/aFkOGOkeJoqoIZJgn22ff+c9UplIJTKxcIGm5d70ocze3Sx7TCq+A2xixwEsOb/A0831bAAAAHQAAADEAAAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","lLT5TkZ3/7MmNTrG07fwT6CwDZ4una1ok5q6S7EHMBfoBJSNN5XOl3X1Hir/GbWLDs+i2Xrwlu+WjIqNYsaICol14dSQYu1LErxRH0dre4CUBLD7uPMIRUej/0GNt2S8bAAAAHQAAADEAAAAAwAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gAnu0h9b+GhMBWRD5uGwXQLZU+IkR7T5Han28nJNGHmWkWOOmJloo2EsNkho6+tEDcKeEKScBqDMt0Ms+GE7aUfTkeP8K8UDOm/lIlv6SEzFBlTgSaJTRmuybOX1x+Ep","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"g/mKHqmW47ekvXEY62N5fKTQ9oHBU5QKSZxjehyH9MZ8X7t4egnx/1F3SU39doGDA+eJlA2/r0rw2QzdjPbXyBY16RrkoAqqOgItSNzsCEvJl3pEsE9MVp8Ry8r3vs9T","Signers":[2],"Message":{"MsgType":1,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"j7thZ1r0Gs6AEJKBXe2IpYOkIQrN1ggSWQjb6Nz7KLazl+4Iikq9xEPjgs/oiNbYFB5BWknHZR1t3QMWWu/45saLm+WS289AZ503i9GVNM1Rcs+O845PvoFYjWstIUOB","Signers":[3],"Message":{"MsgType":1,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"g4KG9ysNs6oCilSUUjIeaLCVxNkOb04OhT3OxuqzozSCU7YyIwKguDcTNel9Dw0tCpaulMPrIm31VD5eE9OH5DklvF2hNjeVT/3YWtX2brkMuF9zag142guBiAnML6In","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"gsr1DCLhAStwnKO6zt7HBr7lFNjRQuInb4Ny/Ty+EyPV/fpB3txoI1NwJYHdRsl1E4EKSwxAPeyxJBD+Uqj9Yjq8wBYjWz+9xWgNIH73kln1XYVmWCZ35mhzUPaF0jWW","Signers":[2],"Message":{"MsgType":2,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"s9I25QV+qrQFaaYiRguHT6C002/u43TMJN28P3K/T9b/lzlEkQ7mRfd/vdGqWk2FFyADjrpfwq+qZNqXWbZobGdAQxN0JM4hStqMfMLYGzMOqC77FSJciWYq8mGgaRLe","Signers":[4],"Message":{"MsgType":2,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"ControllerPostRoot":"ac8cbc7f074702dd643312cf277d894b35079706116237e3eb41a82914ee69e9","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":null,"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}}],"OutputMessages":null,"ExpectedError":""},"*tests.ControllerSpecTest_qbft controller invalid identifier":{"Name":"invalid identifier","RunInstanceData":[{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"l3y3v/O16uz7zhdtMstsdMbamy1bfeFo9j3RW3+N3nVUiNWIPQRj8/1gdDd5fqA+DkYLxPePnMrk/3oP9AStOh2eVcl2hNKiGSzCmPEdB9PEH2Kd+h1/ckSvw7CMpWUm","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AAADAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"ControllerPostRoot":"47713c38fe74ce55959980781287886c603c2117a14dc8abce24dcb9be0093af","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":null,"DecidedCnt":0,"BroadcastedDecided":null,"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}}],"OutputMessages":null,"ExpectedError":"invalid msg: message doesn't belong to Identifier"},"*tests.ControllerSpecTest_qbft controller late commit":{"Name":"late commit","RunInstanceData":[{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooE","Signers":[2],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRM","Signers":[3],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"kFGy1aK4Kychuv0xFbXRVjFgDU6hCAaltJ+Nbjsie+2yEmuFSwss4IiyekCVHu+1F9TN31CUv+uZ4FwzPQSkR5oM709TN/61iEcY4j44Zm/OXL8pnEhSipyXSSngprJ6","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"q+s2ftJ53lt+iNxpg6bfNxGrjGkY19VA5zRn92qJueuJgKCVyLQkrpNymxHC5bQADhtbiD8/LNzJ9bB0w14T3M8xooKaUCt6IX4qh5wqkfAicOB+KZ4+wGTfq/XQBGaj","Signers":[2],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"sbN4I4KmsP1QwuIS5hztulBMjvjyZsJIxc7VnPfzdBMPLq+BImXyrU2wPwzSyjPJDXsE9EQFb2D7NPnCA/gIutwGbRK/kwdRYUKH6VL5L5qPmw7Be2nDhly1J9iELyIq","Signers":[3],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"otJRiw6BkIF2XRWOwVx+pfKbZlP6RyaRFX0klsywbIYGiMOfwBW5hVPWylklu2nGAWOaUKClM1RH3csiez6KGCex2qe57+32EiNFcHeJzoVqLXjLL74YyE7fCXuXuPGU","Signers":[4],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"ControllerPostRoot":"0a04b3b92d44f929df35bf88c6f4eeb91862db2af65319cd679015fc08158341","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":{"Signature":"ok+cfOp6t9/h8xvePHOpDCnIif+wMjLeYWhHv5/Csldj3S/G2PgaYaZhxpSKKURCFXRMTSP1OhfbMinxQrnxZYg7SP2C6NDOa/9vV6NLaX/eusH+W91JkAgPd+seFNhr","Signers":[1,2,3],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}}],"OutputMessages":null,"ExpectedError":""},"*tests.ControllerSpecTest_qbft controller late commit past instance":{"Name":"late commit past instance","RunInstanceData":[{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooE","Signers":[2],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRM","Signers":[3],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"kFGy1aK4Kychuv0xFbXRVjFgDU6hCAaltJ+Nbjsie+2yEmuFSwss4IiyekCVHu+1F9TN31CUv+uZ4FwzPQSkR5oM709TN/61iEcY4j44Zm/OXL8pnEhSipyXSSngprJ6","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"q+s2ftJ53lt+iNxpg6bfNxGrjGkY19VA5zRn92qJueuJgKCVyLQkrpNymxHC5bQADhtbiD8/LNzJ9bB0w14T3M8xooKaUCt6IX4qh5wqkfAicOB+KZ4+wGTfq/XQBGaj","Signers":[2],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"sbN4I4KmsP1QwuIS5hztulBMjvjyZsJIxc7VnPfzdBMPLq+BImXyrU2wPwzSyjPJDXsE9EQFb2D7NPnCA/gIutwGbRK/kwdRYUKH6VL5L5qPmw7Be2nDhly1J9iELyIq","Signers":[3],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"ControllerPostRoot":"24cf697092529cfab3ab06b969d8696692c8bcbb9f41a954f71dc74c3b1d7e97","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":{"Signature":"ok+cfOp6t9/h8xvePHOpDCnIif+wMjLeYWhHv5/Csldj3S/G2PgaYaZhxpSKKURCFXRMTSP1OhfbMinxQrnxZYg7SP2C6NDOa/9vV6NLaX/eusH+W91JkAgPd+seFNhr","Signers":[1,2,3],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}},{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"qWjI/XXkiDZRmT8FTuyaO8nURadLRx6Nd1DPr6xO+Z4z/200Ee9XlmliwGxjzLbdCam3kbDDaQBuhMOxR+gce88BXz6m85d7FMGrKpdxwsrY2pCmbSwXB989mvorwr2z","Signers":[1],"Message":{"MsgType":0,"Height":1,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"qJH6qx0VYJ9iwAFgh2h0HtIHx3OweoMzCyoEeacp7QD5HVTWH8nYVLfnbSBOmt15BjSCdhv/5+UBWw2kY02k/DnOr2H8tgGAtTGFj/LcHiDjKBKacGuJAIn8iPzHXezT","Signers":[1],"Message":{"MsgType":1,"Height":1,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"s5d41fPqm/E2OTHbxMt5wGzC8eKwYGsmxvwDtj0Jowb37DlTOYT2wj6HDq2YcPrSGRqbaAI9zih1PYlsay/XQ6+nWLTdZpQDy7vzQHS+y5Jqu8WRDM3pOmmTiQXOQL1T","Signers":[2],"Message":{"MsgType":1,"Height":1,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"jZ4zNOmrZLWYsjxIMJMNL+z7Lquow9jxNPZMgQKFueEBKnQIiGdTy2DA20fjuBMaCx4ZS7Or4tjQmuJZSTgrqL0rYvWdINd3Kh15MoePPm3LWNjxdPLUUIu6lPX58VbD","Signers":[3],"Message":{"MsgType":1,"Height":1,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"sPhLOQokU1UWjkjbRZg0jeD/MfYBxpL0Y/v5GLkA5dlWWFs+R59R4zpc4EmJOt/VEQ0mwAyU9DYHxs995xLhNe1Jy79I3U47eNAwqwfd7tBlcYX/7i5PvQ/x6gb7uo0H","Signers":[1],"Message":{"MsgType":2,"Height":1,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"g8sz9aemTuiggvswqFra5VFZdw27zRtXt+dNb7d6FLJxwNaZdfiL31sI7X9hli92A9ybVg0zItMJcH1WoyZQbIRDSR5tyaJsdO6Jv9ExX9bhZQsYgEJ14g3u5PX2Bh50","Signers":[2],"Message":{"MsgType":2,"Height":1,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"hUAk4Jpa5ORTNV4PzV37EomDoX+/R8t82MWZE1J2AqBtIh0LkJuQBn+CYB6CzTcIEoQ36pn/uXvMPzqmrKhUld6tBvDp7di9K1/eh7lCTw4RXVSJ6c82lEuAFpQHJC+S","Signers":[3],"Message":{"MsgType":2,"Height":1,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"ControllerPostRoot":"8aa5464b119518f178d81edf4cea1f4c918f9e084e5262a0e276d3afb00ba620","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":{"Signature":"o1/7b5O3Y9AdTgg8gKYwNB3vJKwiC8jP9skkFHz713rqR+g6bYZ3ooAUTI5k2vdsEeZkHKlP5X6bdpqBoGOVzw9Gv+n5DG8O+WUaSsehH3u4LhQdxTOwZVOlqzX2za1I","Signers":[1,2,3],"Message":{"MsgType":2,"Height":1,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}},{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"k/fLjlW3b3EomFbpA/r8cP9s9b1tmqUPe97nxML9+8YsCZcr5yQktY/YQjDGaHi7ENfMQuViMNTJ+IKLQ4j/wcbRSLuPKH1ciptzdXi3ynYJmcO3ATxlVT9BOcaSNSp+","Signers":[1],"Message":{"MsgType":0,"Height":2,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"iKWgpKtY9lB1+ipfY+/h/uixAx5TFmUYdfwwFZS9Sejy1fBKBvDvzpvD8xFg1UdIFNVtEOqJypfDko8sgF7roVcV8kFlPl4/U81oz+jqcDUgALj9X1peMm3jUL3oNmle","Signers":[1],"Message":{"MsgType":1,"Height":2,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"lhVcNwY0/yiPgjQr3G+ZrSRKEQncZ/gWY0utTEwCUCiETK7i3bQ1szjw5cTsaoIIAaywyI5Zx9605hjDd0flmHz9/5AbWJv+6QkFwxNo3dkCTybOTKVC2dDzMMO6uy3n","Signers":[2],"Message":{"MsgType":1,"Height":2,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"ogA+fxj2nN8zrEtSbH9zFWEcpt4WOIXuJFJ1I3/mHaJbyHSH7JKOjVC3Ius/h7yEFaVFZwLQPyN5OUPPNqV0E4FTCST3ITKw3YQ5//nyGlXGF3zrJ/ySLrYRy9PXvjAy","Signers":[3],"Message":{"MsgType":1,"Height":2,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"mV+sJOquRaZQ8ynNDRklvEBCRieEt6kUSqHYKZFM4KpZVmfPORUdUrwhhTT+w2QEGMYHuwTX/CezN9ihvS0Mss40PTv4BUls61OG2APnepBEQc5yo1usLP4aaTK4b9aP","Signers":[1],"Message":{"MsgType":2,"Height":2,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"kmcpBKH9XVgvyQjvywB68g/F4nUQc3Os20SmDOIosRSpuVBM5zJBbMSKw9FYu872F6UCTCbMXjfT4L3EwGExhbgWwq4quNT2dg/w6V/YFyjjqTs1juo11UqB3Dvonnuf","Signers":[2],"Message":{"MsgType":2,"Height":2,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"sDwwOg4hMKSjHaKPqzurxgeo/sYvrioEgG6EqyvWgZZ8WEs4d30POuvNwGc+QpFzDgWVfCKD03NOsOysslBU5EnJTHKBJgVR0/GoHtYLBqtLiuhIalERW6QT30zMzhcX","Signers":[3],"Message":{"MsgType":2,"Height":2,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"ControllerPostRoot":"1799fe0981ae08bde1eae9fef88ef8035f5952974647786287a1a8c36544a5da","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":{"Signature":"kiu6YzcRCchksadatQmjgnBumdhbYYXzdrh26X8tUJ8sNSnF5vzCdKA5JU33VbhDBhgGZNS1UKzPBHI1VdZqTwGG/XXA6FKAp1PdXZe+g+VnFe04HUT3VbGEgdRCbicm","Signers":[1,2,3],"Message":{"MsgType":2,"Height":2,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}},{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"kTj4HjnR0sDlmIG9U1nXeLtK3LKlLK05639bhIx2gR8SXms9uO9sy35AbrPjMQM1Fid6S4KQLD35/8xgGvhe2l032AEz+PhQXhI/1KS7DkwmcloOsWhAKJ6zQ20AQB6+","Signers":[1],"Message":{"MsgType":0,"Height":3,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"lAtzc70hH4MpcG9YZr7FU+Fx9Qq8MeWwVp3pkmywHXtYId/kdtTC1PBG3X7qvOnwCcNQtOWOWUHW8Ynr05tma5RkXZYlyA192bxLkiCKng03nxfYnPIBdQA4IjzPfs+U","Signers":[1],"Message":{"MsgType":1,"Height":3,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"gEMHw6ScIvun+ky1Y7eJhjPyCG+Yz6IvqS00rKpT3hdNQA+XGYofWWJQsVOZl0Q9AWYBJE0N4jvNQgGl+/qwHiLeHVw3x4SsIcawyuR/3G/wWZHTqIr6+7C9Gf2y9zEv","Signers":[2],"Message":{"MsgType":1,"Height":3,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"hJODESaoYhHCpEPxDrqf+fyJ+NyUXy7QPUy40VAnJlEZ7kbp1YAqapC/Hf/gBmsKFZIRee0/w1aLp/isFWihINIQZs4t22dVqILDG8W3AI9bP+7kjXKnTuX+bA/nzKZ7","Signers":[3],"Message":{"MsgType":1,"Height":3,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"ksBzbjXX+ZYOwyfXRWBDRd8dSq5FTYDoQkW1qOnMbBVFZ0TL6RWNelv6dYkXp7lRBF95n3cA21NmZzmxEeVXNxETAOHXWqmybb2HTMgMjo2mjnTe/hMBRqykIm1Yt4yI","Signers":[1],"Message":{"MsgType":2,"Height":3,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"or/kk3kwQHkLOOPHaNRHEkdgD+vkIxFQ+lDr/k1zn6UB089vIMgWrHnFE5E6ux2JBBnimcoLG7M6iYF9uihibD/BzZFeON4rptDg4Sh5i5NZUPmmNA0S/dD7kuAXjhRw","Signers":[2],"Message":{"MsgType":2,"Height":3,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"lMRgzMy0GWzxa94CDMXcfLYPzDV3ANmSTt2MlPqP/gpQ/EwXOo2Jy4O9oEgvNHSqAeeZd1Q5DuFbXTrYKdRw75QGLMom1usKys3GozGUFsNtyeEdYUZi4Adk+cdc7w2w","Signers":[3],"Message":{"MsgType":2,"Height":3,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"ControllerPostRoot":"2ddf2b8c2f35c8115ddd68120c71e64809bfea6b023ed13e177c2474a95d137d","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":{"Signature":"qNpKmg1ip+H7Jzj6pp18FG041HaXH94TiIWsXxnL3xpSg5fr7oNKiw6Egot5em7FDRFANP5Mx9MUVVq+qQPeokslW4XcOmkBUtTaa/QCLUhFrzzcd+9DeYRYNn9OSA+q","Signers":[1,2,3],"Message":{"MsgType":2,"Height":3,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}},{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"hcdipfqDq5AX9IBtqgHww0JVhVZFpykwWFpwSBwBgIIc9GOu3h9fCYdeIyptfDVQDp86Zo8uX+I/d816Jwl+6a0TAel5C9MLILWyLEMTLcA/lysgLfLMET2Dco4iuVFs","Signers":[1],"Message":{"MsgType":0,"Height":4,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gxQRj17O0YXXmHaXi8aL9+mguAbZTCIDBz2lNaCpZ0JAXWPfcFRxNUlNymzD+1LyGFdhAGcaY5Owvh3q9Lah1HLL+3JIQpsoHHKsQ388V98BK/dKult88R88nu9B3fcQ","Signers":[1],"Message":{"MsgType":1,"Height":4,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"uHzDg/4u8t9z2FmUw9w+xK93iVCyf+QPMvS1NK/iqYyCE4vMYsweb+mcMVAcdqvmBdkygE5PPG0t60tnEnFo01pzrgFmtO9cECrcBh/+wTX8dsE8onWJpu54uYOeo+eK","Signers":[2],"Message":{"MsgType":1,"Height":4,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"rPln7mrnIjDPMw4wJZRlpftVPs6YsdXsyskFbyxFPu0ZTxPzyqRWg5d1yP8Nw+GTGFAd9BWv8P74/S4tdah1msTtQWHqqQZ0HhCI78TwicZ4N8ucae79e7uT7HYQl0vJ","Signers":[3],"Message":{"MsgType":1,"Height":4,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"mYst1nptBrvgH2eWuvFDLu+suut/kYrvR2fXXCvJGiVLK25HVQRmaCIAiDWB9j90DWekNnSWfX4hISabbP/B/Yp7obr0eFgnm6tWstv63GvFTNUr7XcCmEwcVkrDYqJa","Signers":[1],"Message":{"MsgType":2,"Height":4,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"qG5iQ0+Rct8lqdlyLj2a9vgieF/uvukWA2I7wT/XDRNPFK1/5Oun+SyRZp6XcHjXEDGD8ABMe4nfpHMqbdQGlx9u01frfNYgcZd9yGc1vg6bZgY7vEZhN6A1EXtKqyDv","Signers":[2],"Message":{"MsgType":2,"Height":4,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"kft+uPE9qN/dF0GVUR1wWuz8hj0rZuUVneSktZ0gnxGd7VBktkP8oe163JsuJey2EjvD5VllCMYUNbEPHK2pWURMLZNIf/zZ8CdItZDDWPX5+kLm/cMEMfXJ0zKtkQ/U","Signers":[3],"Message":{"MsgType":2,"Height":4,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"ControllerPostRoot":"318816cc8819ad062996704fb4b9990b8088ade0cb4c26816ea0965783bff12e","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":{"Signature":"hSo4/P21GVMqCZfvI07YjkMipilwxPNXEeB1pKHZBLuNHGrsDg3KCFLPFhCFFk9rAWiumy41OftD+FzmvrqxUtpuo6Wi3EZt0sJr+WpQBrWbFdMmvgy6mZFvyE7E97Xv","Signers":[1,2,3],"Message":{"MsgType":2,"Height":4,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}},{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"hoP18tJ+XZ2+zYo34ZA/g2wf4aaDF65TgC/ZPsfu4pdf5X7YwO1QlKCKw8h8e5VvBRcpPUBJ9j05I7jdOdH2Bk3TK7EjQYM7UIAjHxiw4oU38fkQ78tmgZjEskef8FWO","Signers":[1],"Message":{"MsgType":0,"Height":5,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"i270PoqsRzpwLYHruXQSoIaW2SKwVncetTTn/BEixmFs0sGEPP5uNy+85j5DjZFGBda3cMHufY0H7/FEjzNipryvDuKuGxY3vrJaIMG9Pxfq9SasyCzPSZCH9KKxuhZ3","Signers":[1],"Message":{"MsgType":1,"Height":5,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"lWCXktxKou+d3J4kyoZtWRIhaRfOyPBaxm9V4svlWdyU4HUl7rZ4xoJ4kMe218x2Dzmoh7oiT+I2uH4/f7M/yzW0Uu0rwBtig069T3OZ2ajF2EBz6ZbL4txWg2CoRksk","Signers":[2],"Message":{"MsgType":1,"Height":5,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"tbzB+QhVk0WP6p4CnX7FMMqa7QywKPUlJMki75CRcO+4+IZLGO7ek4Vn/cRRk2jIBudfyP44quRdxTHjumC7tBmful+/AvitkVODV/+Tb/nK72Jx0ChiAXqYsHJ/RG12","Signers":[3],"Message":{"MsgType":1,"Height":5,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"gz0UCft0fOWZWrrDX8L/xh1k2PdugJNn/vKdjQxsmBKyIVbyPZXE2Hq0q9IvT9mcCyZ9gXu6xhVRJrxEcRUxev33Np+QjHJX7CQ0rjGTJyq9hAOTBgt674KyBWYx0Wnt","Signers":[1],"Message":{"MsgType":2,"Height":5,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"jG1Ht3WUVMXTE341wug2Wt30D0cYQZKCeaODvGUfZCkihMdsPDGXtCYwCFueV3jjBqbnafe7ynlvsRuCRVGt2c1GWmdNFcdWw4r64ElXGhci0ckQd/xo5RasDHh8er6e","Signers":[2],"Message":{"MsgType":2,"Height":5,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"iFDqJViRHiOhN/et4Ft+wUTmdDC0+X7MbHi47rqeVQpY9YVqNeuEg229LSz8d6z+CGsVQ7dA0gwKRbRGll7X2WKfQtOmiY2NMzWAL6QQqH5+RiT9krbbuQo4mp0wpp85","Signers":[3],"Message":{"MsgType":2,"Height":5,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"ControllerPostRoot":"fd83cdee705de628cf4a9baf9e662c424bf0942c63a680266d7db872d16e9f0a","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":{"Signature":"mKoLfkCFmGgVV7kLQe8HaD99G/pgxraQua44HTz9vaIgVqc7SBrHz/pw4EUgSrcpGJhsEi5+AastbfilekdVi0I+HZPafBEZ+MwJABzSmio1lwPf7Xgtnb6Q0RiEOkxZ","Signers":[1,2,3],"Message":{"MsgType":2,"Height":5,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}},{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"pDtnCh6AEmJfrfCNqNlpR6pomAeqjrChE4eeReiojr06u5+SRotvH0jiqzb+zG3aA60D8I76q/OtfjMRA0Kwetj2bR55HV/6Mk12bp+iVwiz/zRJZV9Kt1T2gpsTfxfX","Signers":[4],"Message":{"MsgType":2,"Height":4,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ControllerPostRoot":"691ac09194b0f6c67e85de702293f19863b0f5ee19e17c3764e411cee253f8b7","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":null,"DecidedCnt":0,"BroadcastedDecided":null,"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}}],"OutputMessages":null,"ExpectedError":""},"*tests.ControllerSpecTest_qbft controller late commit past round":{"Name":"late commit past round","RunInstanceData":[{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooE","Signers":[2],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"t3wDXB0dnGx8wigQxQqFyeVgrXkVCQVQYd/gfUA+2qEwQWFGbUn2SES25c9LCXCfBp3r/ZFDj5fUFKT2TNy0+M8elwP42hQb6fdFCQh+L4RJIxSwNBlmxLj8Ftkx9roI","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"mMq307rvpww3BxdPm0KCuat1CPoddAl9w/tnc524RivFpWby1PNqd9ymZl6/aFkOGOkeJoqoIZJgn22ff+c9UplIJTKxcIGm5d70ocze3Sx7TCq+A2xixwEsOb/A0831","Signers":[2],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"lLT5TkZ3/7MmNTrG07fwT6CwDZ4una1ok5q6S7EHMBfoBJSNN5XOl3X1Hir/GbWLDs+i2Xrwlu+WjIqNYsaICol14dSQYu1LErxRH0dre4CUBLD7uPMIRUej/0GNt2S8","Signers":[3],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"qV5LdutK2zk8Xbqu+xKdm1T33abJK26BV1I8cmor0GIZQdtFEJh4KP/6KMA1IZRSAH7cSAX9fvXO4/r4cVjuCRIFcdRL/cpnvfcAoTu0T8FBW1XqybwpwJcqglpkSvPk","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":["t3wDXB0dnGx8wigQxQqFyeVgrXkVCQVQYd/gfUA+2qEwQWFGbUn2SES25c9LCXCfBp3r/ZFDj5fUFKT2TNy0+M8elwP42hQb6fdFCQh+L4RJIxSwNBlmxLj8Ftkx9roIbAAAAHQAAADEAAAAAQAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","mMq307rvpww3BxdPm0KCuat1CPoddAl9w/tnc524RivFpWby1PNqd9ymZl6/aFkOGOkeJoqoIZJgn22ff+c9UplIJTKxcIGm5d70ocze3Sx7TCq+A2xixwEsOb/A0831bAAAAHQAAADEAAAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","lLT5TkZ3/7MmNTrG07fwT6CwDZ4una1ok5q6S7EHMBfoBJSNN5XOl3X1Hir/GbWLDs+i2Xrwlu+WjIqNYsaICol14dSQYu1LErxRH0dre4CUBLD7uPMIRUej/0GNt2S8bAAAAHQAAADEAAAAAwAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gAnu0h9b+GhMBWRD5uGwXQLZU+IkR7T5Han28nJNGHmWkWOOmJloo2EsNkho6+tEDcKeEKScBqDMt0Ms+GE7aUfTkeP8K8UDOm/lIlv6SEzFBlTgSaJTRmuybOX1x+Ep","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"g/mKHqmW47ekvXEY62N5fKTQ9oHBU5QKSZxjehyH9MZ8X7t4egnx/1F3SU39doGDA+eJlA2/r0rw2QzdjPbXyBY16RrkoAqqOgItSNzsCEvJl3pEsE9MVp8Ry8r3vs9T","Signers":[2],"Message":{"MsgType":1,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"j7thZ1r0Gs6AEJKBXe2IpYOkIQrN1ggSWQjb6Nz7KLazl+4Iikq9xEPjgs/oiNbYFB5BWknHZR1t3QMWWu/45saLm+WS289AZ503i9GVNM1Rcs+O845PvoFYjWstIUOB","Signers":[3],"Message":{"MsgType":1,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"g4KG9ysNs6oCilSUUjIeaLCVxNkOb04OhT3OxuqzozSCU7YyIwKguDcTNel9Dw0tCpaulMPrIm31VD5eE9OH5DklvF2hNjeVT/3YWtX2brkMuF9zag142guBiAnML6In","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"gsr1DCLhAStwnKO6zt7HBr7lFNjRQuInb4Ny/Ty+EyPV/fpB3txoI1NwJYHdRsl1E4EKSwxAPeyxJBD+Uqj9Yjq8wBYjWz+9xWgNIH73kln1XYVmWCZ35mhzUPaF0jWW","Signers":[2],"Message":{"MsgType":2,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"uaJFjWkFWOVdIW0w26l2oMCm68+vHetAT9JNBPyKKZmgH4y0GH1vEA1rB3+wF/0nCgrh+fuWHa7uAYrQ3Gk7p9h8kS8ZaIOZc+aZxQ9LOO1IKJoRTXmOdjWs9iV+gAra","Signers":[3],"Message":{"MsgType":2,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"otJRiw6BkIF2XRWOwVx+pfKbZlP6RyaRFX0klsywbIYGiMOfwBW5hVPWylklu2nGAWOaUKClM1RH3csiez6KGCex2qe57+32EiNFcHeJzoVqLXjLL74YyE7fCXuXuPGU","Signers":[4],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"ControllerPostRoot":"04eba8412e564e9b000f61035cfbc663216671d98d39d74119612614916542ad","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":{"Signature":"rtVJAyIqaGQFYSFpI6zJ2KB3rbXKU4UlipSFLg1PKczSS/cV9TbHUowZ5ClDmD0mB14+RIJ1r6BZbjuhtNPzydJP9UhYoba6P7ur2QjpVfKU/nEQ+KDE96pb83UQad4r","Signers":[1,2,3],"Message":{"MsgType":2,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}}],"OutputMessages":null,"ExpectedError":"could not process msg: invalid signed message: past round"},"*tests.ControllerSpecTest_qbft controller late prepare":{"Name":"late prepare","RunInstanceData":[{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooE","Signers":[2],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRM","Signers":[3],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"kFGy1aK4Kychuv0xFbXRVjFgDU6hCAaltJ+Nbjsie+2yEmuFSwss4IiyekCVHu+1F9TN31CUv+uZ4FwzPQSkR5oM709TN/61iEcY4j44Zm/OXL8pnEhSipyXSSngprJ6","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"q+s2ftJ53lt+iNxpg6bfNxGrjGkY19VA5zRn92qJueuJgKCVyLQkrpNymxHC5bQADhtbiD8/LNzJ9bB0w14T3M8xooKaUCt6IX4qh5wqkfAicOB+KZ4+wGTfq/XQBGaj","Signers":[2],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"sbN4I4KmsP1QwuIS5hztulBMjvjyZsJIxc7VnPfzdBMPLq+BImXyrU2wPwzSyjPJDXsE9EQFb2D7NPnCA/gIutwGbRK/kwdRYUKH6VL5L5qPmw7Be2nDhly1J9iELyIq","Signers":[3],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"oBCGUs0KByzDk/HXikTym24T9pSNbwTwNUB6D13bcfec+gBWx5PHkGtJ3tqvDwD6DWINpQ+L4WazwDXLLLIV9QipQzlKZfFirNC/abOufNz5NIjqxYNwQoS5OWsycScV","Signers":[4],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ControllerPostRoot":"bdef629f3a8234faa1111cba866b04c907b822003410ca0b067111fa0c048841","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":{"Signature":"ok+cfOp6t9/h8xvePHOpDCnIif+wMjLeYWhHv5/Csldj3S/G2PgaYaZhxpSKKURCFXRMTSP1OhfbMinxQrnxZYg7SP2C6NDOa/9vV6NLaX/eusH+W91JkAgPd+seFNhr","Signers":[1,2,3],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}}],"OutputMessages":null,"ExpectedError":""},"*tests.ControllerSpecTest_qbft controller late prepare past instance":{"Name":"late prepare past instance","RunInstanceData":[{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooE","Signers":[2],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRM","Signers":[3],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"kFGy1aK4Kychuv0xFbXRVjFgDU6hCAaltJ+Nbjsie+2yEmuFSwss4IiyekCVHu+1F9TN31CUv+uZ4FwzPQSkR5oM709TN/61iEcY4j44Zm/OXL8pnEhSipyXSSngprJ6","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"q+s2ftJ53lt+iNxpg6bfNxGrjGkY19VA5zRn92qJueuJgKCVyLQkrpNymxHC5bQADhtbiD8/LNzJ9bB0w14T3M8xooKaUCt6IX4qh5wqkfAicOB+KZ4+wGTfq/XQBGaj","Signers":[2],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"sbN4I4KmsP1QwuIS5hztulBMjvjyZsJIxc7VnPfzdBMPLq+BImXyrU2wPwzSyjPJDXsE9EQFb2D7NPnCA/gIutwGbRK/kwdRYUKH6VL5L5qPmw7Be2nDhly1J9iELyIq","Signers":[3],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"ControllerPostRoot":"24cf697092529cfab3ab06b969d8696692c8bcbb9f41a954f71dc74c3b1d7e97","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":{"Signature":"ok+cfOp6t9/h8xvePHOpDCnIif+wMjLeYWhHv5/Csldj3S/G2PgaYaZhxpSKKURCFXRMTSP1OhfbMinxQrnxZYg7SP2C6NDOa/9vV6NLaX/eusH+W91JkAgPd+seFNhr","Signers":[1,2,3],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}},{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"qWjI/XXkiDZRmT8FTuyaO8nURadLRx6Nd1DPr6xO+Z4z/200Ee9XlmliwGxjzLbdCam3kbDDaQBuhMOxR+gce88BXz6m85d7FMGrKpdxwsrY2pCmbSwXB989mvorwr2z","Signers":[1],"Message":{"MsgType":0,"Height":1,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"qJH6qx0VYJ9iwAFgh2h0HtIHx3OweoMzCyoEeacp7QD5HVTWH8nYVLfnbSBOmt15BjSCdhv/5+UBWw2kY02k/DnOr2H8tgGAtTGFj/LcHiDjKBKacGuJAIn8iPzHXezT","Signers":[1],"Message":{"MsgType":1,"Height":1,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"s5d41fPqm/E2OTHbxMt5wGzC8eKwYGsmxvwDtj0Jowb37DlTOYT2wj6HDq2YcPrSGRqbaAI9zih1PYlsay/XQ6+nWLTdZpQDy7vzQHS+y5Jqu8WRDM3pOmmTiQXOQL1T","Signers":[2],"Message":{"MsgType":1,"Height":1,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"jZ4zNOmrZLWYsjxIMJMNL+z7Lquow9jxNPZMgQKFueEBKnQIiGdTy2DA20fjuBMaCx4ZS7Or4tjQmuJZSTgrqL0rYvWdINd3Kh15MoePPm3LWNjxdPLUUIu6lPX58VbD","Signers":[3],"Message":{"MsgType":1,"Height":1,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"sPhLOQokU1UWjkjbRZg0jeD/MfYBxpL0Y/v5GLkA5dlWWFs+R59R4zpc4EmJOt/VEQ0mwAyU9DYHxs995xLhNe1Jy79I3U47eNAwqwfd7tBlcYX/7i5PvQ/x6gb7uo0H","Signers":[1],"Message":{"MsgType":2,"Height":1,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"g8sz9aemTuiggvswqFra5VFZdw27zRtXt+dNb7d6FLJxwNaZdfiL31sI7X9hli92A9ybVg0zItMJcH1WoyZQbIRDSR5tyaJsdO6Jv9ExX9bhZQsYgEJ14g3u5PX2Bh50","Signers":[2],"Message":{"MsgType":2,"Height":1,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"hUAk4Jpa5ORTNV4PzV37EomDoX+/R8t82MWZE1J2AqBtIh0LkJuQBn+CYB6CzTcIEoQ36pn/uXvMPzqmrKhUld6tBvDp7di9K1/eh7lCTw4RXVSJ6c82lEuAFpQHJC+S","Signers":[3],"Message":{"MsgType":2,"Height":1,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"ControllerPostRoot":"8aa5464b119518f178d81edf4cea1f4c918f9e084e5262a0e276d3afb00ba620","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":{"Signature":"o1/7b5O3Y9AdTgg8gKYwNB3vJKwiC8jP9skkFHz713rqR+g6bYZ3ooAUTI5k2vdsEeZkHKlP5X6bdpqBoGOVzw9Gv+n5DG8O+WUaSsehH3u4LhQdxTOwZVOlqzX2za1I","Signers":[1,2,3],"Message":{"MsgType":2,"Height":1,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}},{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"k/fLjlW3b3EomFbpA/r8cP9s9b1tmqUPe97nxML9+8YsCZcr5yQktY/YQjDGaHi7ENfMQuViMNTJ+IKLQ4j/wcbRSLuPKH1ciptzdXi3ynYJmcO3ATxlVT9BOcaSNSp+","Signers":[1],"Message":{"MsgType":0,"Height":2,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"iKWgpKtY9lB1+ipfY+/h/uixAx5TFmUYdfwwFZS9Sejy1fBKBvDvzpvD8xFg1UdIFNVtEOqJypfDko8sgF7roVcV8kFlPl4/U81oz+jqcDUgALj9X1peMm3jUL3oNmle","Signers":[1],"Message":{"MsgType":1,"Height":2,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"lhVcNwY0/yiPgjQr3G+ZrSRKEQncZ/gWY0utTEwCUCiETK7i3bQ1szjw5cTsaoIIAaywyI5Zx9605hjDd0flmHz9/5AbWJv+6QkFwxNo3dkCTybOTKVC2dDzMMO6uy3n","Signers":[2],"Message":{"MsgType":1,"Height":2,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"ogA+fxj2nN8zrEtSbH9zFWEcpt4WOIXuJFJ1I3/mHaJbyHSH7JKOjVC3Ius/h7yEFaVFZwLQPyN5OUPPNqV0E4FTCST3ITKw3YQ5//nyGlXGF3zrJ/ySLrYRy9PXvjAy","Signers":[3],"Message":{"MsgType":1,"Height":2,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"mV+sJOquRaZQ8ynNDRklvEBCRieEt6kUSqHYKZFM4KpZVmfPORUdUrwhhTT+w2QEGMYHuwTX/CezN9ihvS0Mss40PTv4BUls61OG2APnepBEQc5yo1usLP4aaTK4b9aP","Signers":[1],"Message":{"MsgType":2,"Height":2,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"kmcpBKH9XVgvyQjvywB68g/F4nUQc3Os20SmDOIosRSpuVBM5zJBbMSKw9FYu872F6UCTCbMXjfT4L3EwGExhbgWwq4quNT2dg/w6V/YFyjjqTs1juo11UqB3Dvonnuf","Signers":[2],"Message":{"MsgType":2,"Height":2,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"sDwwOg4hMKSjHaKPqzurxgeo/sYvrioEgG6EqyvWgZZ8WEs4d30POuvNwGc+QpFzDgWVfCKD03NOsOysslBU5EnJTHKBJgVR0/GoHtYLBqtLiuhIalERW6QT30zMzhcX","Signers":[3],"Message":{"MsgType":2,"Height":2,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"ControllerPostRoot":"1799fe0981ae08bde1eae9fef88ef8035f5952974647786287a1a8c36544a5da","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":{"Signature":"kiu6YzcRCchksadatQmjgnBumdhbYYXzdrh26X8tUJ8sNSnF5vzCdKA5JU33VbhDBhgGZNS1UKzPBHI1VdZqTwGG/XXA6FKAp1PdXZe+g+VnFe04HUT3VbGEgdRCbicm","Signers":[1,2,3],"Message":{"MsgType":2,"Height":2,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}},{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"kTj4HjnR0sDlmIG9U1nXeLtK3LKlLK05639bhIx2gR8SXms9uO9sy35AbrPjMQM1Fid6S4KQLD35/8xgGvhe2l032AEz+PhQXhI/1KS7DkwmcloOsWhAKJ6zQ20AQB6+","Signers":[1],"Message":{"MsgType":0,"Height":3,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"lAtzc70hH4MpcG9YZr7FU+Fx9Qq8MeWwVp3pkmywHXtYId/kdtTC1PBG3X7qvOnwCcNQtOWOWUHW8Ynr05tma5RkXZYlyA192bxLkiCKng03nxfYnPIBdQA4IjzPfs+U","Signers":[1],"Message":{"MsgType":1,"Height":3,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"gEMHw6ScIvun+ky1Y7eJhjPyCG+Yz6IvqS00rKpT3hdNQA+XGYofWWJQsVOZl0Q9AWYBJE0N4jvNQgGl+/qwHiLeHVw3x4SsIcawyuR/3G/wWZHTqIr6+7C9Gf2y9zEv","Signers":[2],"Message":{"MsgType":1,"Height":3,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"hJODESaoYhHCpEPxDrqf+fyJ+NyUXy7QPUy40VAnJlEZ7kbp1YAqapC/Hf/gBmsKFZIRee0/w1aLp/isFWihINIQZs4t22dVqILDG8W3AI9bP+7kjXKnTuX+bA/nzKZ7","Signers":[3],"Message":{"MsgType":1,"Height":3,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"ksBzbjXX+ZYOwyfXRWBDRd8dSq5FTYDoQkW1qOnMbBVFZ0TL6RWNelv6dYkXp7lRBF95n3cA21NmZzmxEeVXNxETAOHXWqmybb2HTMgMjo2mjnTe/hMBRqykIm1Yt4yI","Signers":[1],"Message":{"MsgType":2,"Height":3,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"or/kk3kwQHkLOOPHaNRHEkdgD+vkIxFQ+lDr/k1zn6UB089vIMgWrHnFE5E6ux2JBBnimcoLG7M6iYF9uihibD/BzZFeON4rptDg4Sh5i5NZUPmmNA0S/dD7kuAXjhRw","Signers":[2],"Message":{"MsgType":2,"Height":3,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"lMRgzMy0GWzxa94CDMXcfLYPzDV3ANmSTt2MlPqP/gpQ/EwXOo2Jy4O9oEgvNHSqAeeZd1Q5DuFbXTrYKdRw75QGLMom1usKys3GozGUFsNtyeEdYUZi4Adk+cdc7w2w","Signers":[3],"Message":{"MsgType":2,"Height":3,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"ControllerPostRoot":"2ddf2b8c2f35c8115ddd68120c71e64809bfea6b023ed13e177c2474a95d137d","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":{"Signature":"qNpKmg1ip+H7Jzj6pp18FG041HaXH94TiIWsXxnL3xpSg5fr7oNKiw6Egot5em7FDRFANP5Mx9MUVVq+qQPeokslW4XcOmkBUtTaa/QCLUhFrzzcd+9DeYRYNn9OSA+q","Signers":[1,2,3],"Message":{"MsgType":2,"Height":3,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}},{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"hcdipfqDq5AX9IBtqgHww0JVhVZFpykwWFpwSBwBgIIc9GOu3h9fCYdeIyptfDVQDp86Zo8uX+I/d816Jwl+6a0TAel5C9MLILWyLEMTLcA/lysgLfLMET2Dco4iuVFs","Signers":[1],"Message":{"MsgType":0,"Height":4,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gxQRj17O0YXXmHaXi8aL9+mguAbZTCIDBz2lNaCpZ0JAXWPfcFRxNUlNymzD+1LyGFdhAGcaY5Owvh3q9Lah1HLL+3JIQpsoHHKsQ388V98BK/dKult88R88nu9B3fcQ","Signers":[1],"Message":{"MsgType":1,"Height":4,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"uHzDg/4u8t9z2FmUw9w+xK93iVCyf+QPMvS1NK/iqYyCE4vMYsweb+mcMVAcdqvmBdkygE5PPG0t60tnEnFo01pzrgFmtO9cECrcBh/+wTX8dsE8onWJpu54uYOeo+eK","Signers":[2],"Message":{"MsgType":1,"Height":4,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"rPln7mrnIjDPMw4wJZRlpftVPs6YsdXsyskFbyxFPu0ZTxPzyqRWg5d1yP8Nw+GTGFAd9BWv8P74/S4tdah1msTtQWHqqQZ0HhCI78TwicZ4N8ucae79e7uT7HYQl0vJ","Signers":[3],"Message":{"MsgType":1,"Height":4,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"mYst1nptBrvgH2eWuvFDLu+suut/kYrvR2fXXCvJGiVLK25HVQRmaCIAiDWB9j90DWekNnSWfX4hISabbP/B/Yp7obr0eFgnm6tWstv63GvFTNUr7XcCmEwcVkrDYqJa","Signers":[1],"Message":{"MsgType":2,"Height":4,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"qG5iQ0+Rct8lqdlyLj2a9vgieF/uvukWA2I7wT/XDRNPFK1/5Oun+SyRZp6XcHjXEDGD8ABMe4nfpHMqbdQGlx9u01frfNYgcZd9yGc1vg6bZgY7vEZhN6A1EXtKqyDv","Signers":[2],"Message":{"MsgType":2,"Height":4,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"kft+uPE9qN/dF0GVUR1wWuz8hj0rZuUVneSktZ0gnxGd7VBktkP8oe163JsuJey2EjvD5VllCMYUNbEPHK2pWURMLZNIf/zZ8CdItZDDWPX5+kLm/cMEMfXJ0zKtkQ/U","Signers":[3],"Message":{"MsgType":2,"Height":4,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"ControllerPostRoot":"318816cc8819ad062996704fb4b9990b8088ade0cb4c26816ea0965783bff12e","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":{"Signature":"hSo4/P21GVMqCZfvI07YjkMipilwxPNXEeB1pKHZBLuNHGrsDg3KCFLPFhCFFk9rAWiumy41OftD+FzmvrqxUtpuo6Wi3EZt0sJr+WpQBrWbFdMmvgy6mZFvyE7E97Xv","Signers":[1,2,3],"Message":{"MsgType":2,"Height":4,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}},{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"hoP18tJ+XZ2+zYo34ZA/g2wf4aaDF65TgC/ZPsfu4pdf5X7YwO1QlKCKw8h8e5VvBRcpPUBJ9j05I7jdOdH2Bk3TK7EjQYM7UIAjHxiw4oU38fkQ78tmgZjEskef8FWO","Signers":[1],"Message":{"MsgType":0,"Height":5,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"i270PoqsRzpwLYHruXQSoIaW2SKwVncetTTn/BEixmFs0sGEPP5uNy+85j5DjZFGBda3cMHufY0H7/FEjzNipryvDuKuGxY3vrJaIMG9Pxfq9SasyCzPSZCH9KKxuhZ3","Signers":[1],"Message":{"MsgType":1,"Height":5,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"lWCXktxKou+d3J4kyoZtWRIhaRfOyPBaxm9V4svlWdyU4HUl7rZ4xoJ4kMe218x2Dzmoh7oiT+I2uH4/f7M/yzW0Uu0rwBtig069T3OZ2ajF2EBz6ZbL4txWg2CoRksk","Signers":[2],"Message":{"MsgType":1,"Height":5,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"tbzB+QhVk0WP6p4CnX7FMMqa7QywKPUlJMki75CRcO+4+IZLGO7ek4Vn/cRRk2jIBudfyP44quRdxTHjumC7tBmful+/AvitkVODV/+Tb/nK72Jx0ChiAXqYsHJ/RG12","Signers":[3],"Message":{"MsgType":1,"Height":5,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"gz0UCft0fOWZWrrDX8L/xh1k2PdugJNn/vKdjQxsmBKyIVbyPZXE2Hq0q9IvT9mcCyZ9gXu6xhVRJrxEcRUxev33Np+QjHJX7CQ0rjGTJyq9hAOTBgt674KyBWYx0Wnt","Signers":[1],"Message":{"MsgType":2,"Height":5,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"jG1Ht3WUVMXTE341wug2Wt30D0cYQZKCeaODvGUfZCkihMdsPDGXtCYwCFueV3jjBqbnafe7ynlvsRuCRVGt2c1GWmdNFcdWw4r64ElXGhci0ckQd/xo5RasDHh8er6e","Signers":[2],"Message":{"MsgType":2,"Height":5,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"iFDqJViRHiOhN/et4Ft+wUTmdDC0+X7MbHi47rqeVQpY9YVqNeuEg229LSz8d6z+CGsVQ7dA0gwKRbRGll7X2WKfQtOmiY2NMzWAL6QQqH5+RiT9krbbuQo4mp0wpp85","Signers":[3],"Message":{"MsgType":2,"Height":5,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"ControllerPostRoot":"fd83cdee705de628cf4a9baf9e662c424bf0942c63a680266d7db872d16e9f0a","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":{"Signature":"mKoLfkCFmGgVV7kLQe8HaD99G/pgxraQua44HTz9vaIgVqc7SBrHz/pw4EUgSrcpGJhsEi5+AastbfilekdVi0I+HZPafBEZ+MwJABzSmio1lwPf7Xgtnb6Q0RiEOkxZ","Signers":[1,2,3],"Message":{"MsgType":2,"Height":5,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}},{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"lkb7bw2rU0odScSYAU65BYkc8/1BzOYqz8PlBoQ5zhURob9k7/F1S4kN3O9VkAjEF+Zy2Jol35H2pO15E9Z9vUg5tiVoeRW1zJjMm4J8rhWikQrMoaDy0i884GHE0Wzm","Signers":[4],"Message":{"MsgType":1,"Height":4,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ControllerPostRoot":"f604068832f986e37b8b2725fbd426ce3f5bf7d6541d4a7296d7573f36befa27","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":null,"DecidedCnt":0,"BroadcastedDecided":null,"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}}],"OutputMessages":null,"ExpectedError":""},"*tests.ControllerSpecTest_qbft controller late prepare past round":{"Name":"late prepare past round","RunInstanceData":[{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooE","Signers":[2],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"t3wDXB0dnGx8wigQxQqFyeVgrXkVCQVQYd/gfUA+2qEwQWFGbUn2SES25c9LCXCfBp3r/ZFDj5fUFKT2TNy0+M8elwP42hQb6fdFCQh+L4RJIxSwNBlmxLj8Ftkx9roI","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"mMq307rvpww3BxdPm0KCuat1CPoddAl9w/tnc524RivFpWby1PNqd9ymZl6/aFkOGOkeJoqoIZJgn22ff+c9UplIJTKxcIGm5d70ocze3Sx7TCq+A2xixwEsOb/A0831","Signers":[2],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"lLT5TkZ3/7MmNTrG07fwT6CwDZ4una1ok5q6S7EHMBfoBJSNN5XOl3X1Hir/GbWLDs+i2Xrwlu+WjIqNYsaICol14dSQYu1LErxRH0dre4CUBLD7uPMIRUej/0GNt2S8","Signers":[3],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"qV5LdutK2zk8Xbqu+xKdm1T33abJK26BV1I8cmor0GIZQdtFEJh4KP/6KMA1IZRSAH7cSAX9fvXO4/r4cVjuCRIFcdRL/cpnvfcAoTu0T8FBW1XqybwpwJcqglpkSvPk","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":["t3wDXB0dnGx8wigQxQqFyeVgrXkVCQVQYd/gfUA+2qEwQWFGbUn2SES25c9LCXCfBp3r/ZFDj5fUFKT2TNy0+M8elwP42hQb6fdFCQh+L4RJIxSwNBlmxLj8Ftkx9roIbAAAAHQAAADEAAAAAQAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","mMq307rvpww3BxdPm0KCuat1CPoddAl9w/tnc524RivFpWby1PNqd9ymZl6/aFkOGOkeJoqoIZJgn22ff+c9UplIJTKxcIGm5d70ocze3Sx7TCq+A2xixwEsOb/A0831bAAAAHQAAADEAAAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","lLT5TkZ3/7MmNTrG07fwT6CwDZ4una1ok5q6S7EHMBfoBJSNN5XOl3X1Hir/GbWLDs+i2Xrwlu+WjIqNYsaICol14dSQYu1LErxRH0dre4CUBLD7uPMIRUej/0GNt2S8bAAAAHQAAADEAAAAAwAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gAnu0h9b+GhMBWRD5uGwXQLZU+IkR7T5Han28nJNGHmWkWOOmJloo2EsNkho6+tEDcKeEKScBqDMt0Ms+GE7aUfTkeP8K8UDOm/lIlv6SEzFBlTgSaJTRmuybOX1x+Ep","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"g/mKHqmW47ekvXEY62N5fKTQ9oHBU5QKSZxjehyH9MZ8X7t4egnx/1F3SU39doGDA+eJlA2/r0rw2QzdjPbXyBY16RrkoAqqOgItSNzsCEvJl3pEsE9MVp8Ry8r3vs9T","Signers":[2],"Message":{"MsgType":1,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"j7thZ1r0Gs6AEJKBXe2IpYOkIQrN1ggSWQjb6Nz7KLazl+4Iikq9xEPjgs/oiNbYFB5BWknHZR1t3QMWWu/45saLm+WS289AZ503i9GVNM1Rcs+O845PvoFYjWstIUOB","Signers":[3],"Message":{"MsgType":1,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"g4KG9ysNs6oCilSUUjIeaLCVxNkOb04OhT3OxuqzozSCU7YyIwKguDcTNel9Dw0tCpaulMPrIm31VD5eE9OH5DklvF2hNjeVT/3YWtX2brkMuF9zag142guBiAnML6In","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"gsr1DCLhAStwnKO6zt7HBr7lFNjRQuInb4Ny/Ty+EyPV/fpB3txoI1NwJYHdRsl1E4EKSwxAPeyxJBD+Uqj9Yjq8wBYjWz+9xWgNIH73kln1XYVmWCZ35mhzUPaF0jWW","Signers":[2],"Message":{"MsgType":2,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"uaJFjWkFWOVdIW0w26l2oMCm68+vHetAT9JNBPyKKZmgH4y0GH1vEA1rB3+wF/0nCgrh+fuWHa7uAYrQ3Gk7p9h8kS8ZaIOZc+aZxQ9LOO1IKJoRTXmOdjWs9iV+gAra","Signers":[3],"Message":{"MsgType":2,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ControllerPostRoot":"04eba8412e564e9b000f61035cfbc663216671d98d39d74119612614916542ad","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":{"Signature":"rtVJAyIqaGQFYSFpI6zJ2KB3rbXKU4UlipSFLg1PKczSS/cV9TbHUowZ5ClDmD0mB14+RIJ1r6BZbjuhtNPzydJP9UhYoba6P7ur2QjpVfKU/nEQ+KDE96pb83UQad4r","Signers":[1,2,3],"Message":{"MsgType":2,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}}],"OutputMessages":null,"ExpectedError":"could not process msg: invalid signed message: past round"},"*tests.ControllerSpecTest_qbft controller late proposal":{"Name":"late proposal","RunInstanceData":[{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooE","Signers":[2],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRM","Signers":[3],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"kFGy1aK4Kychuv0xFbXRVjFgDU6hCAaltJ+Nbjsie+2yEmuFSwss4IiyekCVHu+1F9TN31CUv+uZ4FwzPQSkR5oM709TN/61iEcY4j44Zm/OXL8pnEhSipyXSSngprJ6","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"q+s2ftJ53lt+iNxpg6bfNxGrjGkY19VA5zRn92qJueuJgKCVyLQkrpNymxHC5bQADhtbiD8/LNzJ9bB0w14T3M8xooKaUCt6IX4qh5wqkfAicOB+KZ4+wGTfq/XQBGaj","Signers":[2],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"sbN4I4KmsP1QwuIS5hztulBMjvjyZsJIxc7VnPfzdBMPLq+BImXyrU2wPwzSyjPJDXsE9EQFb2D7NPnCA/gIutwGbRK/kwdRYUKH6VL5L5qPmw7Be2nDhly1J9iELyIq","Signers":[3],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ControllerPostRoot":"24cf697092529cfab3ab06b969d8696692c8bcbb9f41a954f71dc74c3b1d7e97","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":{"Signature":"ok+cfOp6t9/h8xvePHOpDCnIif+wMjLeYWhHv5/Csldj3S/G2PgaYaZhxpSKKURCFXRMTSP1OhfbMinxQrnxZYg7SP2C6NDOa/9vV6NLaX/eusH+W91JkAgPd+seFNhr","Signers":[1,2,3],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}}],"OutputMessages":null,"ExpectedError":"could not process msg: invalid signed message: proposal is not valid with current state"},"*tests.ControllerSpecTest_qbft controller late proposal past instance":{"Name":"late proposal past instance","RunInstanceData":[{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooE","Signers":[2],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRM","Signers":[3],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"kFGy1aK4Kychuv0xFbXRVjFgDU6hCAaltJ+Nbjsie+2yEmuFSwss4IiyekCVHu+1F9TN31CUv+uZ4FwzPQSkR5oM709TN/61iEcY4j44Zm/OXL8pnEhSipyXSSngprJ6","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"q+s2ftJ53lt+iNxpg6bfNxGrjGkY19VA5zRn92qJueuJgKCVyLQkrpNymxHC5bQADhtbiD8/LNzJ9bB0w14T3M8xooKaUCt6IX4qh5wqkfAicOB+KZ4+wGTfq/XQBGaj","Signers":[2],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"sbN4I4KmsP1QwuIS5hztulBMjvjyZsJIxc7VnPfzdBMPLq+BImXyrU2wPwzSyjPJDXsE9EQFb2D7NPnCA/gIutwGbRK/kwdRYUKH6VL5L5qPmw7Be2nDhly1J9iELyIq","Signers":[3],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"ControllerPostRoot":"24cf697092529cfab3ab06b969d8696692c8bcbb9f41a954f71dc74c3b1d7e97","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":{"Signature":"ok+cfOp6t9/h8xvePHOpDCnIif+wMjLeYWhHv5/Csldj3S/G2PgaYaZhxpSKKURCFXRMTSP1OhfbMinxQrnxZYg7SP2C6NDOa/9vV6NLaX/eusH+W91JkAgPd+seFNhr","Signers":[1,2,3],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}},{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"qWjI/XXkiDZRmT8FTuyaO8nURadLRx6Nd1DPr6xO+Z4z/200Ee9XlmliwGxjzLbdCam3kbDDaQBuhMOxR+gce88BXz6m85d7FMGrKpdxwsrY2pCmbSwXB989mvorwr2z","Signers":[1],"Message":{"MsgType":0,"Height":1,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"qJH6qx0VYJ9iwAFgh2h0HtIHx3OweoMzCyoEeacp7QD5HVTWH8nYVLfnbSBOmt15BjSCdhv/5+UBWw2kY02k/DnOr2H8tgGAtTGFj/LcHiDjKBKacGuJAIn8iPzHXezT","Signers":[1],"Message":{"MsgType":1,"Height":1,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"s5d41fPqm/E2OTHbxMt5wGzC8eKwYGsmxvwDtj0Jowb37DlTOYT2wj6HDq2YcPrSGRqbaAI9zih1PYlsay/XQ6+nWLTdZpQDy7vzQHS+y5Jqu8WRDM3pOmmTiQXOQL1T","Signers":[2],"Message":{"MsgType":1,"Height":1,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"jZ4zNOmrZLWYsjxIMJMNL+z7Lquow9jxNPZMgQKFueEBKnQIiGdTy2DA20fjuBMaCx4ZS7Or4tjQmuJZSTgrqL0rYvWdINd3Kh15MoePPm3LWNjxdPLUUIu6lPX58VbD","Signers":[3],"Message":{"MsgType":1,"Height":1,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"sPhLOQokU1UWjkjbRZg0jeD/MfYBxpL0Y/v5GLkA5dlWWFs+R59R4zpc4EmJOt/VEQ0mwAyU9DYHxs995xLhNe1Jy79I3U47eNAwqwfd7tBlcYX/7i5PvQ/x6gb7uo0H","Signers":[1],"Message":{"MsgType":2,"Height":1,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"g8sz9aemTuiggvswqFra5VFZdw27zRtXt+dNb7d6FLJxwNaZdfiL31sI7X9hli92A9ybVg0zItMJcH1WoyZQbIRDSR5tyaJsdO6Jv9ExX9bhZQsYgEJ14g3u5PX2Bh50","Signers":[2],"Message":{"MsgType":2,"Height":1,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"hUAk4Jpa5ORTNV4PzV37EomDoX+/R8t82MWZE1J2AqBtIh0LkJuQBn+CYB6CzTcIEoQ36pn/uXvMPzqmrKhUld6tBvDp7di9K1/eh7lCTw4RXVSJ6c82lEuAFpQHJC+S","Signers":[3],"Message":{"MsgType":2,"Height":1,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"ControllerPostRoot":"8aa5464b119518f178d81edf4cea1f4c918f9e084e5262a0e276d3afb00ba620","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":{"Signature":"o1/7b5O3Y9AdTgg8gKYwNB3vJKwiC8jP9skkFHz713rqR+g6bYZ3ooAUTI5k2vdsEeZkHKlP5X6bdpqBoGOVzw9Gv+n5DG8O+WUaSsehH3u4LhQdxTOwZVOlqzX2za1I","Signers":[1,2,3],"Message":{"MsgType":2,"Height":1,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}},{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"k/fLjlW3b3EomFbpA/r8cP9s9b1tmqUPe97nxML9+8YsCZcr5yQktY/YQjDGaHi7ENfMQuViMNTJ+IKLQ4j/wcbRSLuPKH1ciptzdXi3ynYJmcO3ATxlVT9BOcaSNSp+","Signers":[1],"Message":{"MsgType":0,"Height":2,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"iKWgpKtY9lB1+ipfY+/h/uixAx5TFmUYdfwwFZS9Sejy1fBKBvDvzpvD8xFg1UdIFNVtEOqJypfDko8sgF7roVcV8kFlPl4/U81oz+jqcDUgALj9X1peMm3jUL3oNmle","Signers":[1],"Message":{"MsgType":1,"Height":2,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"lhVcNwY0/yiPgjQr3G+ZrSRKEQncZ/gWY0utTEwCUCiETK7i3bQ1szjw5cTsaoIIAaywyI5Zx9605hjDd0flmHz9/5AbWJv+6QkFwxNo3dkCTybOTKVC2dDzMMO6uy3n","Signers":[2],"Message":{"MsgType":1,"Height":2,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"ogA+fxj2nN8zrEtSbH9zFWEcpt4WOIXuJFJ1I3/mHaJbyHSH7JKOjVC3Ius/h7yEFaVFZwLQPyN5OUPPNqV0E4FTCST3ITKw3YQ5//nyGlXGF3zrJ/ySLrYRy9PXvjAy","Signers":[3],"Message":{"MsgType":1,"Height":2,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"mV+sJOquRaZQ8ynNDRklvEBCRieEt6kUSqHYKZFM4KpZVmfPORUdUrwhhTT+w2QEGMYHuwTX/CezN9ihvS0Mss40PTv4BUls61OG2APnepBEQc5yo1usLP4aaTK4b9aP","Signers":[1],"Message":{"MsgType":2,"Height":2,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"kmcpBKH9XVgvyQjvywB68g/F4nUQc3Os20SmDOIosRSpuVBM5zJBbMSKw9FYu872F6UCTCbMXjfT4L3EwGExhbgWwq4quNT2dg/w6V/YFyjjqTs1juo11UqB3Dvonnuf","Signers":[2],"Message":{"MsgType":2,"Height":2,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"sDwwOg4hMKSjHaKPqzurxgeo/sYvrioEgG6EqyvWgZZ8WEs4d30POuvNwGc+QpFzDgWVfCKD03NOsOysslBU5EnJTHKBJgVR0/GoHtYLBqtLiuhIalERW6QT30zMzhcX","Signers":[3],"Message":{"MsgType":2,"Height":2,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"ControllerPostRoot":"1799fe0981ae08bde1eae9fef88ef8035f5952974647786287a1a8c36544a5da","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":{"Signature":"kiu6YzcRCchksadatQmjgnBumdhbYYXzdrh26X8tUJ8sNSnF5vzCdKA5JU33VbhDBhgGZNS1UKzPBHI1VdZqTwGG/XXA6FKAp1PdXZe+g+VnFe04HUT3VbGEgdRCbicm","Signers":[1,2,3],"Message":{"MsgType":2,"Height":2,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}},{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"kTj4HjnR0sDlmIG9U1nXeLtK3LKlLK05639bhIx2gR8SXms9uO9sy35AbrPjMQM1Fid6S4KQLD35/8xgGvhe2l032AEz+PhQXhI/1KS7DkwmcloOsWhAKJ6zQ20AQB6+","Signers":[1],"Message":{"MsgType":0,"Height":3,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"lAtzc70hH4MpcG9YZr7FU+Fx9Qq8MeWwVp3pkmywHXtYId/kdtTC1PBG3X7qvOnwCcNQtOWOWUHW8Ynr05tma5RkXZYlyA192bxLkiCKng03nxfYnPIBdQA4IjzPfs+U","Signers":[1],"Message":{"MsgType":1,"Height":3,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"gEMHw6ScIvun+ky1Y7eJhjPyCG+Yz6IvqS00rKpT3hdNQA+XGYofWWJQsVOZl0Q9AWYBJE0N4jvNQgGl+/qwHiLeHVw3x4SsIcawyuR/3G/wWZHTqIr6+7C9Gf2y9zEv","Signers":[2],"Message":{"MsgType":1,"Height":3,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"hJODESaoYhHCpEPxDrqf+fyJ+NyUXy7QPUy40VAnJlEZ7kbp1YAqapC/Hf/gBmsKFZIRee0/w1aLp/isFWihINIQZs4t22dVqILDG8W3AI9bP+7kjXKnTuX+bA/nzKZ7","Signers":[3],"Message":{"MsgType":1,"Height":3,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"ksBzbjXX+ZYOwyfXRWBDRd8dSq5FTYDoQkW1qOnMbBVFZ0TL6RWNelv6dYkXp7lRBF95n3cA21NmZzmxEeVXNxETAOHXWqmybb2HTMgMjo2mjnTe/hMBRqykIm1Yt4yI","Signers":[1],"Message":{"MsgType":2,"Height":3,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"or/kk3kwQHkLOOPHaNRHEkdgD+vkIxFQ+lDr/k1zn6UB089vIMgWrHnFE5E6ux2JBBnimcoLG7M6iYF9uihibD/BzZFeON4rptDg4Sh5i5NZUPmmNA0S/dD7kuAXjhRw","Signers":[2],"Message":{"MsgType":2,"Height":3,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"lMRgzMy0GWzxa94CDMXcfLYPzDV3ANmSTt2MlPqP/gpQ/EwXOo2Jy4O9oEgvNHSqAeeZd1Q5DuFbXTrYKdRw75QGLMom1usKys3GozGUFsNtyeEdYUZi4Adk+cdc7w2w","Signers":[3],"Message":{"MsgType":2,"Height":3,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"ControllerPostRoot":"2ddf2b8c2f35c8115ddd68120c71e64809bfea6b023ed13e177c2474a95d137d","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":{"Signature":"qNpKmg1ip+H7Jzj6pp18FG041HaXH94TiIWsXxnL3xpSg5fr7oNKiw6Egot5em7FDRFANP5Mx9MUVVq+qQPeokslW4XcOmkBUtTaa/QCLUhFrzzcd+9DeYRYNn9OSA+q","Signers":[1,2,3],"Message":{"MsgType":2,"Height":3,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}},{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"hcdipfqDq5AX9IBtqgHww0JVhVZFpykwWFpwSBwBgIIc9GOu3h9fCYdeIyptfDVQDp86Zo8uX+I/d816Jwl+6a0TAel5C9MLILWyLEMTLcA/lysgLfLMET2Dco4iuVFs","Signers":[1],"Message":{"MsgType":0,"Height":4,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gxQRj17O0YXXmHaXi8aL9+mguAbZTCIDBz2lNaCpZ0JAXWPfcFRxNUlNymzD+1LyGFdhAGcaY5Owvh3q9Lah1HLL+3JIQpsoHHKsQ388V98BK/dKult88R88nu9B3fcQ","Signers":[1],"Message":{"MsgType":1,"Height":4,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"uHzDg/4u8t9z2FmUw9w+xK93iVCyf+QPMvS1NK/iqYyCE4vMYsweb+mcMVAcdqvmBdkygE5PPG0t60tnEnFo01pzrgFmtO9cECrcBh/+wTX8dsE8onWJpu54uYOeo+eK","Signers":[2],"Message":{"MsgType":1,"Height":4,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"rPln7mrnIjDPMw4wJZRlpftVPs6YsdXsyskFbyxFPu0ZTxPzyqRWg5d1yP8Nw+GTGFAd9BWv8P74/S4tdah1msTtQWHqqQZ0HhCI78TwicZ4N8ucae79e7uT7HYQl0vJ","Signers":[3],"Message":{"MsgType":1,"Height":4,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"mYst1nptBrvgH2eWuvFDLu+suut/kYrvR2fXXCvJGiVLK25HVQRmaCIAiDWB9j90DWekNnSWfX4hISabbP/B/Yp7obr0eFgnm6tWstv63GvFTNUr7XcCmEwcVkrDYqJa","Signers":[1],"Message":{"MsgType":2,"Height":4,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"qG5iQ0+Rct8lqdlyLj2a9vgieF/uvukWA2I7wT/XDRNPFK1/5Oun+SyRZp6XcHjXEDGD8ABMe4nfpHMqbdQGlx9u01frfNYgcZd9yGc1vg6bZgY7vEZhN6A1EXtKqyDv","Signers":[2],"Message":{"MsgType":2,"Height":4,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"kft+uPE9qN/dF0GVUR1wWuz8hj0rZuUVneSktZ0gnxGd7VBktkP8oe163JsuJey2EjvD5VllCMYUNbEPHK2pWURMLZNIf/zZ8CdItZDDWPX5+kLm/cMEMfXJ0zKtkQ/U","Signers":[3],"Message":{"MsgType":2,"Height":4,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"ControllerPostRoot":"318816cc8819ad062996704fb4b9990b8088ade0cb4c26816ea0965783bff12e","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":{"Signature":"hSo4/P21GVMqCZfvI07YjkMipilwxPNXEeB1pKHZBLuNHGrsDg3KCFLPFhCFFk9rAWiumy41OftD+FzmvrqxUtpuo6Wi3EZt0sJr+WpQBrWbFdMmvgy6mZFvyE7E97Xv","Signers":[1,2,3],"Message":{"MsgType":2,"Height":4,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}},{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"hoP18tJ+XZ2+zYo34ZA/g2wf4aaDF65TgC/ZPsfu4pdf5X7YwO1QlKCKw8h8e5VvBRcpPUBJ9j05I7jdOdH2Bk3TK7EjQYM7UIAjHxiw4oU38fkQ78tmgZjEskef8FWO","Signers":[1],"Message":{"MsgType":0,"Height":5,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"i270PoqsRzpwLYHruXQSoIaW2SKwVncetTTn/BEixmFs0sGEPP5uNy+85j5DjZFGBda3cMHufY0H7/FEjzNipryvDuKuGxY3vrJaIMG9Pxfq9SasyCzPSZCH9KKxuhZ3","Signers":[1],"Message":{"MsgType":1,"Height":5,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"lWCXktxKou+d3J4kyoZtWRIhaRfOyPBaxm9V4svlWdyU4HUl7rZ4xoJ4kMe218x2Dzmoh7oiT+I2uH4/f7M/yzW0Uu0rwBtig069T3OZ2ajF2EBz6ZbL4txWg2CoRksk","Signers":[2],"Message":{"MsgType":1,"Height":5,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"tbzB+QhVk0WP6p4CnX7FMMqa7QywKPUlJMki75CRcO+4+IZLGO7ek4Vn/cRRk2jIBudfyP44quRdxTHjumC7tBmful+/AvitkVODV/+Tb/nK72Jx0ChiAXqYsHJ/RG12","Signers":[3],"Message":{"MsgType":1,"Height":5,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"gz0UCft0fOWZWrrDX8L/xh1k2PdugJNn/vKdjQxsmBKyIVbyPZXE2Hq0q9IvT9mcCyZ9gXu6xhVRJrxEcRUxev33Np+QjHJX7CQ0rjGTJyq9hAOTBgt674KyBWYx0Wnt","Signers":[1],"Message":{"MsgType":2,"Height":5,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"jG1Ht3WUVMXTE341wug2Wt30D0cYQZKCeaODvGUfZCkihMdsPDGXtCYwCFueV3jjBqbnafe7ynlvsRuCRVGt2c1GWmdNFcdWw4r64ElXGhci0ckQd/xo5RasDHh8er6e","Signers":[2],"Message":{"MsgType":2,"Height":5,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"iFDqJViRHiOhN/et4Ft+wUTmdDC0+X7MbHi47rqeVQpY9YVqNeuEg229LSz8d6z+CGsVQ7dA0gwKRbRGll7X2WKfQtOmiY2NMzWAL6QQqH5+RiT9krbbuQo4mp0wpp85","Signers":[3],"Message":{"MsgType":2,"Height":5,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"ControllerPostRoot":"fd83cdee705de628cf4a9baf9e662c424bf0942c63a680266d7db872d16e9f0a","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":{"Signature":"mKoLfkCFmGgVV7kLQe8HaD99G/pgxraQua44HTz9vaIgVqc7SBrHz/pw4EUgSrcpGJhsEi5+AastbfilekdVi0I+HZPafBEZ+MwJABzSmio1lwPf7Xgtnb6Q0RiEOkxZ","Signers":[1,2,3],"Message":{"MsgType":2,"Height":5,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}},{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"inhneAUzWO8oVpJexCV1vLap/LZFYYyCJbJvmRIkT741nWpcvb7rhMpY0UWSKkY1DRcOwzf5s9U67MzefmQZWen+NcG9n3dpRitqfCwP1aPUSOOaCFOjkPZG2DK6qQz3","Signers":[1],"Message":{"MsgType":3,"Height":2,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ControllerPostRoot":"db25cef2d243616e3d344cdb10f53875b4b06aad746a6fd476cab4c7c5dc6533","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":null,"DecidedCnt":0,"BroadcastedDecided":null,"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}}],"OutputMessages":null,"ExpectedError":""},"*tests.ControllerSpecTest_qbft controller late proposal past round":{"Name":"late proposal past round","RunInstanceData":[{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooE","Signers":[2],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"t3wDXB0dnGx8wigQxQqFyeVgrXkVCQVQYd/gfUA+2qEwQWFGbUn2SES25c9LCXCfBp3r/ZFDj5fUFKT2TNy0+M8elwP42hQb6fdFCQh+L4RJIxSwNBlmxLj8Ftkx9roI","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"mMq307rvpww3BxdPm0KCuat1CPoddAl9w/tnc524RivFpWby1PNqd9ymZl6/aFkOGOkeJoqoIZJgn22ff+c9UplIJTKxcIGm5d70ocze3Sx7TCq+A2xixwEsOb/A0831","Signers":[2],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"lLT5TkZ3/7MmNTrG07fwT6CwDZ4una1ok5q6S7EHMBfoBJSNN5XOl3X1Hir/GbWLDs+i2Xrwlu+WjIqNYsaICol14dSQYu1LErxRH0dre4CUBLD7uPMIRUej/0GNt2S8","Signers":[3],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"qV5LdutK2zk8Xbqu+xKdm1T33abJK26BV1I8cmor0GIZQdtFEJh4KP/6KMA1IZRSAH7cSAX9fvXO4/r4cVjuCRIFcdRL/cpnvfcAoTu0T8FBW1XqybwpwJcqglpkSvPk","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":["t3wDXB0dnGx8wigQxQqFyeVgrXkVCQVQYd/gfUA+2qEwQWFGbUn2SES25c9LCXCfBp3r/ZFDj5fUFKT2TNy0+M8elwP42hQb6fdFCQh+L4RJIxSwNBlmxLj8Ftkx9roIbAAAAHQAAADEAAAAAQAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","mMq307rvpww3BxdPm0KCuat1CPoddAl9w/tnc524RivFpWby1PNqd9ymZl6/aFkOGOkeJoqoIZJgn22ff+c9UplIJTKxcIGm5d70ocze3Sx7TCq+A2xixwEsOb/A0831bAAAAHQAAADEAAAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","lLT5TkZ3/7MmNTrG07fwT6CwDZ4una1ok5q6S7EHMBfoBJSNN5XOl3X1Hir/GbWLDs+i2Xrwlu+WjIqNYsaICol14dSQYu1LErxRH0dre4CUBLD7uPMIRUej/0GNt2S8bAAAAHQAAADEAAAAAwAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gAnu0h9b+GhMBWRD5uGwXQLZU+IkR7T5Han28nJNGHmWkWOOmJloo2EsNkho6+tEDcKeEKScBqDMt0Ms+GE7aUfTkeP8K8UDOm/lIlv6SEzFBlTgSaJTRmuybOX1x+Ep","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"g/mKHqmW47ekvXEY62N5fKTQ9oHBU5QKSZxjehyH9MZ8X7t4egnx/1F3SU39doGDA+eJlA2/r0rw2QzdjPbXyBY16RrkoAqqOgItSNzsCEvJl3pEsE9MVp8Ry8r3vs9T","Signers":[2],"Message":{"MsgType":1,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"j7thZ1r0Gs6AEJKBXe2IpYOkIQrN1ggSWQjb6Nz7KLazl+4Iikq9xEPjgs/oiNbYFB5BWknHZR1t3QMWWu/45saLm+WS289AZ503i9GVNM1Rcs+O845PvoFYjWstIUOB","Signers":[3],"Message":{"MsgType":1,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"g4KG9ysNs6oCilSUUjIeaLCVxNkOb04OhT3OxuqzozSCU7YyIwKguDcTNel9Dw0tCpaulMPrIm31VD5eE9OH5DklvF2hNjeVT/3YWtX2brkMuF9zag142guBiAnML6In","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"gsr1DCLhAStwnKO6zt7HBr7lFNjRQuInb4Ny/Ty+EyPV/fpB3txoI1NwJYHdRsl1E4EKSwxAPeyxJBD+Uqj9Yjq8wBYjWz+9xWgNIH73kln1XYVmWCZ35mhzUPaF0jWW","Signers":[2],"Message":{"MsgType":2,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"uaJFjWkFWOVdIW0w26l2oMCm68+vHetAT9JNBPyKKZmgH4y0GH1vEA1rB3+wF/0nCgrh+fuWHa7uAYrQ3Gk7p9h8kS8ZaIOZc+aZxQ9LOO1IKJoRTXmOdjWs9iV+gAra","Signers":[3],"Message":{"MsgType":2,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ControllerPostRoot":"04eba8412e564e9b000f61035cfbc663216671d98d39d74119612614916542ad","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":{"Signature":"rtVJAyIqaGQFYSFpI6zJ2KB3rbXKU4UlipSFLg1PKczSS/cV9TbHUowZ5ClDmD0mB14+RIJ1r6BZbjuhtNPzydJP9UhYoba6P7ur2QjpVfKU/nEQ+KDE96pb83UQad4r","Signers":[1,2,3],"Message":{"MsgType":2,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}}],"OutputMessages":null,"ExpectedError":"could not process msg: invalid signed message: past round"},"*tests.ControllerSpecTest_qbft controller late round change":{"Name":"late round change","RunInstanceData":[{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooE","Signers":[2],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRM","Signers":[3],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"kFGy1aK4Kychuv0xFbXRVjFgDU6hCAaltJ+Nbjsie+2yEmuFSwss4IiyekCVHu+1F9TN31CUv+uZ4FwzPQSkR5oM709TN/61iEcY4j44Zm/OXL8pnEhSipyXSSngprJ6","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"q+s2ftJ53lt+iNxpg6bfNxGrjGkY19VA5zRn92qJueuJgKCVyLQkrpNymxHC5bQADhtbiD8/LNzJ9bB0w14T3M8xooKaUCt6IX4qh5wqkfAicOB+KZ4+wGTfq/XQBGaj","Signers":[2],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"sbN4I4KmsP1QwuIS5hztulBMjvjyZsJIxc7VnPfzdBMPLq+BImXyrU2wPwzSyjPJDXsE9EQFb2D7NPnCA/gIutwGbRK/kwdRYUKH6VL5L5qPmw7Be2nDhly1J9iELyIq","Signers":[3],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"o6otXDxvOgJvzh1Q/A5LfoZYdegkhqe6rRbZgh/NfljBP1tqgROM4ZXjwJ/E+jeiAVUzOQaMkDB20wYzPTVSdgrdkhxsMugYk+HH8LA5H3h5P0RtIMXbuqZWtJVZiC6T","Signers":[4],"Message":{"MsgType":3,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ControllerPostRoot":"748bb5748d521fca09a0636ab8764b372c3690e744cd6f1289b23f2daf5d7112","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":{"Signature":"ok+cfOp6t9/h8xvePHOpDCnIif+wMjLeYWhHv5/Csldj3S/G2PgaYaZhxpSKKURCFXRMTSP1OhfbMinxQrnxZYg7SP2C6NDOa/9vV6NLaX/eusH+W91JkAgPd+seFNhr","Signers":[1,2,3],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}}],"OutputMessages":null,"ExpectedError":""},"*tests.ControllerSpecTest_qbft controller late round change past instance":{"Name":"late round change past instance","RunInstanceData":[{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooE","Signers":[2],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRM","Signers":[3],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"kFGy1aK4Kychuv0xFbXRVjFgDU6hCAaltJ+Nbjsie+2yEmuFSwss4IiyekCVHu+1F9TN31CUv+uZ4FwzPQSkR5oM709TN/61iEcY4j44Zm/OXL8pnEhSipyXSSngprJ6","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"q+s2ftJ53lt+iNxpg6bfNxGrjGkY19VA5zRn92qJueuJgKCVyLQkrpNymxHC5bQADhtbiD8/LNzJ9bB0w14T3M8xooKaUCt6IX4qh5wqkfAicOB+KZ4+wGTfq/XQBGaj","Signers":[2],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"sbN4I4KmsP1QwuIS5hztulBMjvjyZsJIxc7VnPfzdBMPLq+BImXyrU2wPwzSyjPJDXsE9EQFb2D7NPnCA/gIutwGbRK/kwdRYUKH6VL5L5qPmw7Be2nDhly1J9iELyIq","Signers":[3],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"ControllerPostRoot":"24cf697092529cfab3ab06b969d8696692c8bcbb9f41a954f71dc74c3b1d7e97","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":{"Signature":"ok+cfOp6t9/h8xvePHOpDCnIif+wMjLeYWhHv5/Csldj3S/G2PgaYaZhxpSKKURCFXRMTSP1OhfbMinxQrnxZYg7SP2C6NDOa/9vV6NLaX/eusH+W91JkAgPd+seFNhr","Signers":[1,2,3],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}},{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"qWjI/XXkiDZRmT8FTuyaO8nURadLRx6Nd1DPr6xO+Z4z/200Ee9XlmliwGxjzLbdCam3kbDDaQBuhMOxR+gce88BXz6m85d7FMGrKpdxwsrY2pCmbSwXB989mvorwr2z","Signers":[1],"Message":{"MsgType":0,"Height":1,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"qJH6qx0VYJ9iwAFgh2h0HtIHx3OweoMzCyoEeacp7QD5HVTWH8nYVLfnbSBOmt15BjSCdhv/5+UBWw2kY02k/DnOr2H8tgGAtTGFj/LcHiDjKBKacGuJAIn8iPzHXezT","Signers":[1],"Message":{"MsgType":1,"Height":1,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"s5d41fPqm/E2OTHbxMt5wGzC8eKwYGsmxvwDtj0Jowb37DlTOYT2wj6HDq2YcPrSGRqbaAI9zih1PYlsay/XQ6+nWLTdZpQDy7vzQHS+y5Jqu8WRDM3pOmmTiQXOQL1T","Signers":[2],"Message":{"MsgType":1,"Height":1,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"jZ4zNOmrZLWYsjxIMJMNL+z7Lquow9jxNPZMgQKFueEBKnQIiGdTy2DA20fjuBMaCx4ZS7Or4tjQmuJZSTgrqL0rYvWdINd3Kh15MoePPm3LWNjxdPLUUIu6lPX58VbD","Signers":[3],"Message":{"MsgType":1,"Height":1,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"sPhLOQokU1UWjkjbRZg0jeD/MfYBxpL0Y/v5GLkA5dlWWFs+R59R4zpc4EmJOt/VEQ0mwAyU9DYHxs995xLhNe1Jy79I3U47eNAwqwfd7tBlcYX/7i5PvQ/x6gb7uo0H","Signers":[1],"Message":{"MsgType":2,"Height":1,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"g8sz9aemTuiggvswqFra5VFZdw27zRtXt+dNb7d6FLJxwNaZdfiL31sI7X9hli92A9ybVg0zItMJcH1WoyZQbIRDSR5tyaJsdO6Jv9ExX9bhZQsYgEJ14g3u5PX2Bh50","Signers":[2],"Message":{"MsgType":2,"Height":1,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"hUAk4Jpa5ORTNV4PzV37EomDoX+/R8t82MWZE1J2AqBtIh0LkJuQBn+CYB6CzTcIEoQ36pn/uXvMPzqmrKhUld6tBvDp7di9K1/eh7lCTw4RXVSJ6c82lEuAFpQHJC+S","Signers":[3],"Message":{"MsgType":2,"Height":1,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"ControllerPostRoot":"8aa5464b119518f178d81edf4cea1f4c918f9e084e5262a0e276d3afb00ba620","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":{"Signature":"o1/7b5O3Y9AdTgg8gKYwNB3vJKwiC8jP9skkFHz713rqR+g6bYZ3ooAUTI5k2vdsEeZkHKlP5X6bdpqBoGOVzw9Gv+n5DG8O+WUaSsehH3u4LhQdxTOwZVOlqzX2za1I","Signers":[1,2,3],"Message":{"MsgType":2,"Height":1,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}},{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"k/fLjlW3b3EomFbpA/r8cP9s9b1tmqUPe97nxML9+8YsCZcr5yQktY/YQjDGaHi7ENfMQuViMNTJ+IKLQ4j/wcbRSLuPKH1ciptzdXi3ynYJmcO3ATxlVT9BOcaSNSp+","Signers":[1],"Message":{"MsgType":0,"Height":2,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"iKWgpKtY9lB1+ipfY+/h/uixAx5TFmUYdfwwFZS9Sejy1fBKBvDvzpvD8xFg1UdIFNVtEOqJypfDko8sgF7roVcV8kFlPl4/U81oz+jqcDUgALj9X1peMm3jUL3oNmle","Signers":[1],"Message":{"MsgType":1,"Height":2,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"lhVcNwY0/yiPgjQr3G+ZrSRKEQncZ/gWY0utTEwCUCiETK7i3bQ1szjw5cTsaoIIAaywyI5Zx9605hjDd0flmHz9/5AbWJv+6QkFwxNo3dkCTybOTKVC2dDzMMO6uy3n","Signers":[2],"Message":{"MsgType":1,"Height":2,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"ogA+fxj2nN8zrEtSbH9zFWEcpt4WOIXuJFJ1I3/mHaJbyHSH7JKOjVC3Ius/h7yEFaVFZwLQPyN5OUPPNqV0E4FTCST3ITKw3YQ5//nyGlXGF3zrJ/ySLrYRy9PXvjAy","Signers":[3],"Message":{"MsgType":1,"Height":2,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"mV+sJOquRaZQ8ynNDRklvEBCRieEt6kUSqHYKZFM4KpZVmfPORUdUrwhhTT+w2QEGMYHuwTX/CezN9ihvS0Mss40PTv4BUls61OG2APnepBEQc5yo1usLP4aaTK4b9aP","Signers":[1],"Message":{"MsgType":2,"Height":2,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"kmcpBKH9XVgvyQjvywB68g/F4nUQc3Os20SmDOIosRSpuVBM5zJBbMSKw9FYu872F6UCTCbMXjfT4L3EwGExhbgWwq4quNT2dg/w6V/YFyjjqTs1juo11UqB3Dvonnuf","Signers":[2],"Message":{"MsgType":2,"Height":2,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"sDwwOg4hMKSjHaKPqzurxgeo/sYvrioEgG6EqyvWgZZ8WEs4d30POuvNwGc+QpFzDgWVfCKD03NOsOysslBU5EnJTHKBJgVR0/GoHtYLBqtLiuhIalERW6QT30zMzhcX","Signers":[3],"Message":{"MsgType":2,"Height":2,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"ControllerPostRoot":"1799fe0981ae08bde1eae9fef88ef8035f5952974647786287a1a8c36544a5da","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":{"Signature":"kiu6YzcRCchksadatQmjgnBumdhbYYXzdrh26X8tUJ8sNSnF5vzCdKA5JU33VbhDBhgGZNS1UKzPBHI1VdZqTwGG/XXA6FKAp1PdXZe+g+VnFe04HUT3VbGEgdRCbicm","Signers":[1,2,3],"Message":{"MsgType":2,"Height":2,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}},{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"kTj4HjnR0sDlmIG9U1nXeLtK3LKlLK05639bhIx2gR8SXms9uO9sy35AbrPjMQM1Fid6S4KQLD35/8xgGvhe2l032AEz+PhQXhI/1KS7DkwmcloOsWhAKJ6zQ20AQB6+","Signers":[1],"Message":{"MsgType":0,"Height":3,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"lAtzc70hH4MpcG9YZr7FU+Fx9Qq8MeWwVp3pkmywHXtYId/kdtTC1PBG3X7qvOnwCcNQtOWOWUHW8Ynr05tma5RkXZYlyA192bxLkiCKng03nxfYnPIBdQA4IjzPfs+U","Signers":[1],"Message":{"MsgType":1,"Height":3,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"gEMHw6ScIvun+ky1Y7eJhjPyCG+Yz6IvqS00rKpT3hdNQA+XGYofWWJQsVOZl0Q9AWYBJE0N4jvNQgGl+/qwHiLeHVw3x4SsIcawyuR/3G/wWZHTqIr6+7C9Gf2y9zEv","Signers":[2],"Message":{"MsgType":1,"Height":3,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"hJODESaoYhHCpEPxDrqf+fyJ+NyUXy7QPUy40VAnJlEZ7kbp1YAqapC/Hf/gBmsKFZIRee0/w1aLp/isFWihINIQZs4t22dVqILDG8W3AI9bP+7kjXKnTuX+bA/nzKZ7","Signers":[3],"Message":{"MsgType":1,"Height":3,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"ksBzbjXX+ZYOwyfXRWBDRd8dSq5FTYDoQkW1qOnMbBVFZ0TL6RWNelv6dYkXp7lRBF95n3cA21NmZzmxEeVXNxETAOHXWqmybb2HTMgMjo2mjnTe/hMBRqykIm1Yt4yI","Signers":[1],"Message":{"MsgType":2,"Height":3,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"or/kk3kwQHkLOOPHaNRHEkdgD+vkIxFQ+lDr/k1zn6UB089vIMgWrHnFE5E6ux2JBBnimcoLG7M6iYF9uihibD/BzZFeON4rptDg4Sh5i5NZUPmmNA0S/dD7kuAXjhRw","Signers":[2],"Message":{"MsgType":2,"Height":3,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"lMRgzMy0GWzxa94CDMXcfLYPzDV3ANmSTt2MlPqP/gpQ/EwXOo2Jy4O9oEgvNHSqAeeZd1Q5DuFbXTrYKdRw75QGLMom1usKys3GozGUFsNtyeEdYUZi4Adk+cdc7w2w","Signers":[3],"Message":{"MsgType":2,"Height":3,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"ControllerPostRoot":"2ddf2b8c2f35c8115ddd68120c71e64809bfea6b023ed13e177c2474a95d137d","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":{"Signature":"qNpKmg1ip+H7Jzj6pp18FG041HaXH94TiIWsXxnL3xpSg5fr7oNKiw6Egot5em7FDRFANP5Mx9MUVVq+qQPeokslW4XcOmkBUtTaa/QCLUhFrzzcd+9DeYRYNn9OSA+q","Signers":[1,2,3],"Message":{"MsgType":2,"Height":3,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}},{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"hcdipfqDq5AX9IBtqgHww0JVhVZFpykwWFpwSBwBgIIc9GOu3h9fCYdeIyptfDVQDp86Zo8uX+I/d816Jwl+6a0TAel5C9MLILWyLEMTLcA/lysgLfLMET2Dco4iuVFs","Signers":[1],"Message":{"MsgType":0,"Height":4,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gxQRj17O0YXXmHaXi8aL9+mguAbZTCIDBz2lNaCpZ0JAXWPfcFRxNUlNymzD+1LyGFdhAGcaY5Owvh3q9Lah1HLL+3JIQpsoHHKsQ388V98BK/dKult88R88nu9B3fcQ","Signers":[1],"Message":{"MsgType":1,"Height":4,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"uHzDg/4u8t9z2FmUw9w+xK93iVCyf+QPMvS1NK/iqYyCE4vMYsweb+mcMVAcdqvmBdkygE5PPG0t60tnEnFo01pzrgFmtO9cECrcBh/+wTX8dsE8onWJpu54uYOeo+eK","Signers":[2],"Message":{"MsgType":1,"Height":4,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"rPln7mrnIjDPMw4wJZRlpftVPs6YsdXsyskFbyxFPu0ZTxPzyqRWg5d1yP8Nw+GTGFAd9BWv8P74/S4tdah1msTtQWHqqQZ0HhCI78TwicZ4N8ucae79e7uT7HYQl0vJ","Signers":[3],"Message":{"MsgType":1,"Height":4,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"mYst1nptBrvgH2eWuvFDLu+suut/kYrvR2fXXCvJGiVLK25HVQRmaCIAiDWB9j90DWekNnSWfX4hISabbP/B/Yp7obr0eFgnm6tWstv63GvFTNUr7XcCmEwcVkrDYqJa","Signers":[1],"Message":{"MsgType":2,"Height":4,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"qG5iQ0+Rct8lqdlyLj2a9vgieF/uvukWA2I7wT/XDRNPFK1/5Oun+SyRZp6XcHjXEDGD8ABMe4nfpHMqbdQGlx9u01frfNYgcZd9yGc1vg6bZgY7vEZhN6A1EXtKqyDv","Signers":[2],"Message":{"MsgType":2,"Height":4,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"kft+uPE9qN/dF0GVUR1wWuz8hj0rZuUVneSktZ0gnxGd7VBktkP8oe163JsuJey2EjvD5VllCMYUNbEPHK2pWURMLZNIf/zZ8CdItZDDWPX5+kLm/cMEMfXJ0zKtkQ/U","Signers":[3],"Message":{"MsgType":2,"Height":4,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"ControllerPostRoot":"318816cc8819ad062996704fb4b9990b8088ade0cb4c26816ea0965783bff12e","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":{"Signature":"hSo4/P21GVMqCZfvI07YjkMipilwxPNXEeB1pKHZBLuNHGrsDg3KCFLPFhCFFk9rAWiumy41OftD+FzmvrqxUtpuo6Wi3EZt0sJr+WpQBrWbFdMmvgy6mZFvyE7E97Xv","Signers":[1,2,3],"Message":{"MsgType":2,"Height":4,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}},{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"hoP18tJ+XZ2+zYo34ZA/g2wf4aaDF65TgC/ZPsfu4pdf5X7YwO1QlKCKw8h8e5VvBRcpPUBJ9j05I7jdOdH2Bk3TK7EjQYM7UIAjHxiw4oU38fkQ78tmgZjEskef8FWO","Signers":[1],"Message":{"MsgType":0,"Height":5,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"i270PoqsRzpwLYHruXQSoIaW2SKwVncetTTn/BEixmFs0sGEPP5uNy+85j5DjZFGBda3cMHufY0H7/FEjzNipryvDuKuGxY3vrJaIMG9Pxfq9SasyCzPSZCH9KKxuhZ3","Signers":[1],"Message":{"MsgType":1,"Height":5,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"lWCXktxKou+d3J4kyoZtWRIhaRfOyPBaxm9V4svlWdyU4HUl7rZ4xoJ4kMe218x2Dzmoh7oiT+I2uH4/f7M/yzW0Uu0rwBtig069T3OZ2ajF2EBz6ZbL4txWg2CoRksk","Signers":[2],"Message":{"MsgType":1,"Height":5,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"tbzB+QhVk0WP6p4CnX7FMMqa7QywKPUlJMki75CRcO+4+IZLGO7ek4Vn/cRRk2jIBudfyP44quRdxTHjumC7tBmful+/AvitkVODV/+Tb/nK72Jx0ChiAXqYsHJ/RG12","Signers":[3],"Message":{"MsgType":1,"Height":5,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"gz0UCft0fOWZWrrDX8L/xh1k2PdugJNn/vKdjQxsmBKyIVbyPZXE2Hq0q9IvT9mcCyZ9gXu6xhVRJrxEcRUxev33Np+QjHJX7CQ0rjGTJyq9hAOTBgt674KyBWYx0Wnt","Signers":[1],"Message":{"MsgType":2,"Height":5,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"jG1Ht3WUVMXTE341wug2Wt30D0cYQZKCeaODvGUfZCkihMdsPDGXtCYwCFueV3jjBqbnafe7ynlvsRuCRVGt2c1GWmdNFcdWw4r64ElXGhci0ckQd/xo5RasDHh8er6e","Signers":[2],"Message":{"MsgType":2,"Height":5,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"iFDqJViRHiOhN/et4Ft+wUTmdDC0+X7MbHi47rqeVQpY9YVqNeuEg229LSz8d6z+CGsVQ7dA0gwKRbRGll7X2WKfQtOmiY2NMzWAL6QQqH5+RiT9krbbuQo4mp0wpp85","Signers":[3],"Message":{"MsgType":2,"Height":5,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"ControllerPostRoot":"fd83cdee705de628cf4a9baf9e662c424bf0942c63a680266d7db872d16e9f0a","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":{"Signature":"mKoLfkCFmGgVV7kLQe8HaD99G/pgxraQua44HTz9vaIgVqc7SBrHz/pw4EUgSrcpGJhsEi5+AastbfilekdVi0I+HZPafBEZ+MwJABzSmio1lwPf7Xgtnb6Q0RiEOkxZ","Signers":[1,2,3],"Message":{"MsgType":2,"Height":5,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}},{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"qzYuC0sAOrc60M6HThMGL5XyQfAtICP+D02FW4+Sa4Qgr7FkX2IkLIPggYAR16QqFdIp/VelXWZLQo/bau81uu7hLQkS77W64TF+6fpzCbkBDHvvhyVz4pZtOT6qBpoq","Signers":[4],"Message":{"MsgType":3,"Height":4,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ControllerPostRoot":"25270cd1fd958682fc7602ddcabbbd2f384a9c28fca960e214268867caa23bd7","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":null,"DecidedCnt":0,"BroadcastedDecided":null,"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}}],"OutputMessages":null,"ExpectedError":""},"*tests.ControllerSpecTest_qbft controller late round change past round":{"Name":"late round change past round","RunInstanceData":[{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooE","Signers":[2],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"t3wDXB0dnGx8wigQxQqFyeVgrXkVCQVQYd/gfUA+2qEwQWFGbUn2SES25c9LCXCfBp3r/ZFDj5fUFKT2TNy0+M8elwP42hQb6fdFCQh+L4RJIxSwNBlmxLj8Ftkx9roI","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"mMq307rvpww3BxdPm0KCuat1CPoddAl9w/tnc524RivFpWby1PNqd9ymZl6/aFkOGOkeJoqoIZJgn22ff+c9UplIJTKxcIGm5d70ocze3Sx7TCq+A2xixwEsOb/A0831","Signers":[2],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"lLT5TkZ3/7MmNTrG07fwT6CwDZ4una1ok5q6S7EHMBfoBJSNN5XOl3X1Hir/GbWLDs+i2Xrwlu+WjIqNYsaICol14dSQYu1LErxRH0dre4CUBLD7uPMIRUej/0GNt2S8","Signers":[3],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"qV5LdutK2zk8Xbqu+xKdm1T33abJK26BV1I8cmor0GIZQdtFEJh4KP/6KMA1IZRSAH7cSAX9fvXO4/r4cVjuCRIFcdRL/cpnvfcAoTu0T8FBW1XqybwpwJcqglpkSvPk","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":["t3wDXB0dnGx8wigQxQqFyeVgrXkVCQVQYd/gfUA+2qEwQWFGbUn2SES25c9LCXCfBp3r/ZFDj5fUFKT2TNy0+M8elwP42hQb6fdFCQh+L4RJIxSwNBlmxLj8Ftkx9roIbAAAAHQAAADEAAAAAQAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","mMq307rvpww3BxdPm0KCuat1CPoddAl9w/tnc524RivFpWby1PNqd9ymZl6/aFkOGOkeJoqoIZJgn22ff+c9UplIJTKxcIGm5d70ocze3Sx7TCq+A2xixwEsOb/A0831bAAAAHQAAADEAAAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","lLT5TkZ3/7MmNTrG07fwT6CwDZ4una1ok5q6S7EHMBfoBJSNN5XOl3X1Hir/GbWLDs+i2Xrwlu+WjIqNYsaICol14dSQYu1LErxRH0dre4CUBLD7uPMIRUej/0GNt2S8bAAAAHQAAADEAAAAAwAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gAnu0h9b+GhMBWRD5uGwXQLZU+IkR7T5Han28nJNGHmWkWOOmJloo2EsNkho6+tEDcKeEKScBqDMt0Ms+GE7aUfTkeP8K8UDOm/lIlv6SEzFBlTgSaJTRmuybOX1x+Ep","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"g/mKHqmW47ekvXEY62N5fKTQ9oHBU5QKSZxjehyH9MZ8X7t4egnx/1F3SU39doGDA+eJlA2/r0rw2QzdjPbXyBY16RrkoAqqOgItSNzsCEvJl3pEsE9MVp8Ry8r3vs9T","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"j7thZ1r0Gs6AEJKBXe2IpYOkIQrN1ggSWQjb6Nz7KLazl+4Iikq9xEPjgs/oiNbYFB5BWknHZR1t3QMWWu/45saLm+WS289AZ503i9GVNM1Rcs+O845PvoFYjWstIUOB","Signers":[3],"Message":{"MsgType":1,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"g4KG9ysNs6oCilSUUjIeaLCVxNkOb04OhT3OxuqzozSCU7YyIwKguDcTNel9Dw0tCpaulMPrIm31VD5eE9OH5DklvF2hNjeVT/3YWtX2brkMuF9zag142guBiAnML6In","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"gsr1DCLhAStwnKO6zt7HBr7lFNjRQuInb4Ny/Ty+EyPV/fpB3txoI1NwJYHdRsl1E4EKSwxAPeyxJBD+Uqj9Yjq8wBYjWz+9xWgNIH73kln1XYVmWCZ35mhzUPaF0jWW","Signers":[2],"Message":{"MsgType":2,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"uaJFjWkFWOVdIW0w26l2oMCm68+vHetAT9JNBPyKKZmgH4y0GH1vEA1rB3+wF/0nCgrh+fuWHa7uAYrQ3Gk7p9h8kS8ZaIOZc+aZxQ9LOO1IKJoRTXmOdjWs9iV+gAra","Signers":[3],"Message":{"MsgType":2,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"o6otXDxvOgJvzh1Q/A5LfoZYdegkhqe6rRbZgh/NfljBP1tqgROM4ZXjwJ/E+jeiAVUzOQaMkDB20wYzPTVSdgrdkhxsMugYk+HH8LA5H3h5P0RtIMXbuqZWtJVZiC6T","Signers":[4],"Message":{"MsgType":3,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ControllerPostRoot":"94ff35842a25bfb2379bb5419e83ad32f4e4cc079a2b8cfa6f6e183fd44b8f52","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":{"Signature":"rtVJAyIqaGQFYSFpI6zJ2KB3rbXKU4UlipSFLg1PKczSS/cV9TbHUowZ5ClDmD0mB14+RIJ1r6BZbjuhtNPzydJP9UhYoba6P7ur2QjpVfKU/nEQ+KDE96pb83UQad4r","Signers":[1,2,3],"Message":{"MsgType":2,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}}],"OutputMessages":null,"ExpectedError":"could not process msg: invalid signed message: past round"},"*tests.ControllerSpecTest_qbft controller multi decide instances":{"Name":"multi decide instances","RunInstanceData":[{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"ok+cfOp6t9/h8xvePHOpDCnIif+wMjLeYWhHv5/Csldj3S/G2PgaYaZhxpSKKURCFXRMTSP1OhfbMinxQrnxZYg7SP2C6NDOa/9vV6NLaX/eusH+W91JkAgPd+seFNhr","Signers":[1,2,3],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ControllerPostRoot":"f552f5aedb2e0d7933e77c4297c69e761000e88f78ae02e0afd4d053847b8d5c","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":null,"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}},{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"o1/7b5O3Y9AdTgg8gKYwNB3vJKwiC8jP9skkFHz713rqR+g6bYZ3ooAUTI5k2vdsEeZkHKlP5X6bdpqBoGOVzw9Gv+n5DG8O+WUaSsehH3u4LhQdxTOwZVOlqzX2za1I","Signers":[1,2,3],"Message":{"MsgType":2,"Height":1,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ControllerPostRoot":"e8da0b4b1afeee3b611e2c91feb6a66b48dc6287728ab6211c40bac76282ebc4","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":null,"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}},{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"kiu6YzcRCchksadatQmjgnBumdhbYYXzdrh26X8tUJ8sNSnF5vzCdKA5JU33VbhDBhgGZNS1UKzPBHI1VdZqTwGG/XXA6FKAp1PdXZe+g+VnFe04HUT3VbGEgdRCbicm","Signers":[1,2,3],"Message":{"MsgType":2,"Height":2,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ControllerPostRoot":"1fcc752b2ec7645dd4f46c50c7e0260699aceebfe03c8812cfd5aaaa059a9659","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":null,"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}},{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"qNpKmg1ip+H7Jzj6pp18FG041HaXH94TiIWsXxnL3xpSg5fr7oNKiw6Egot5em7FDRFANP5Mx9MUVVq+qQPeokslW4XcOmkBUtTaa/QCLUhFrzzcd+9DeYRYNn9OSA+q","Signers":[1,2,3],"Message":{"MsgType":2,"Height":3,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ControllerPostRoot":"64dd8825012aae061b0d2cdd36e6379f1b30c1fb8f7d8db2c800161565e9bee1","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":null,"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}},{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"hSo4/P21GVMqCZfvI07YjkMipilwxPNXEeB1pKHZBLuNHGrsDg3KCFLPFhCFFk9rAWiumy41OftD+FzmvrqxUtpuo6Wi3EZt0sJr+WpQBrWbFdMmvgy6mZFvyE7E97Xv","Signers":[1,2,3],"Message":{"MsgType":2,"Height":4,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ControllerPostRoot":"dbafdd3370a7cb493058842a2036b9a95a6aa0040300f1cf96ab74f086eb7749","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":null,"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}},{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"mKoLfkCFmGgVV7kLQe8HaD99G/pgxraQua44HTz9vaIgVqc7SBrHz/pw4EUgSrcpGJhsEi5+AastbfilekdVi0I+HZPafBEZ+MwJABzSmio1lwPf7Xgtnb6Q0RiEOkxZ","Signers":[1,2,3],"Message":{"MsgType":2,"Height":5,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ControllerPostRoot":"562a4c43ebed97b9f6f24fbb2fa5c9850f6ed921187e70c667143028250fb301","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":null,"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}},{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"kj1jRzs5FptrMVJxhoX+o5XG/kfpX9n3Crf7gNnHYewfJX2lZXSk8JzffWbWiGUkDGvR7exlKEZYPtK6nM1jEtXqIjvMa8yl3R/Z+Q/e8vM7fJlD/+Ttw7a7Kq4NCF21","Signers":[1,2,3],"Message":{"MsgType":2,"Height":6,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ControllerPostRoot":"489ca498ca6fb5e38e6b28a2bb54fb5b8ac57892647972e4c91ce48aa8d17950","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":null,"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}},{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"pQBpGAQF5bM2hK1/Q0+iJek6pc09lliw5SEZ397Yh/cZ+Pja36Oi4puTxzXIjgNfFp1IIqPNPa0xu4YaG6hlp7tTfc5zI9gg1JL+bHDOUEWzN/SBbUP5q4Y9lf2wsy/B","Signers":[1,2,3],"Message":{"MsgType":2,"Height":7,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ControllerPostRoot":"c83067c025a4222fe887fecf099f0a8a6c4372bafce05a588bbb11f640700910","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":null,"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}},{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"orKLotMH1dhKU3qYtqe7+9eYSVFGxa3BiooVDOQk3hj9dS+VXhgTrmlrFpH8d5PnGePfz7WVTW1idwIQlODtjPhlnp4xbG8xSv8UZ14sZDfcEKn8bbjLZee7bEJ6KdpE","Signers":[1,2,3],"Message":{"MsgType":2,"Height":8,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ControllerPostRoot":"fd9008ae027eff439bc2acfeeaab368ecd93d7964b1e1ddd7c760bdebfde0a92","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":null,"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}},{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"qVBmP+z8rYix+zSOuArjuKHQPKqwU89snGw8I6aP3E9Q5AnSw2PEZrs1wZ1HW9HbB+NN9buLhiID8VdazyAqqxDqZ1Qe+f6D3eDMlUtiA9bVA4s1lm0EmGeZOkmpLiNv","Signers":[1,2,3],"Message":{"MsgType":2,"Height":9,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ControllerPostRoot":"4bc1dca94e40137aa7e000ad13b9b6eca02f7a6b31f3730971dfc162f5c941f5","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":null,"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}},{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"tDaFqXgayP7v782nniCBFQ0egTOpUlUM8p1LlTZAsnn1TYNWCI4qcYmlo3brXmO4F9207XiuHXeeq8XflMaQKg/cyb90ZOSzZK5c9XoSRf3gSirLDwZGRYWs3CWKgjzf","Signers":[1,2,3],"Message":{"MsgType":2,"Height":10,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ControllerPostRoot":"a76efa101019ba414067c5918e419a9dcc31a348035c7357fa073852d46d98fc","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":null,"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}}],"OutputMessages":null,"ExpectedError":""},"*tests.ControllerSpecTest_qbft controller no instance running":{"Name":"no instance running","RunInstanceData":[{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"k0rtwqKAWABDImCqvaxz2sBx94582TYkzm3shbiwThxcrGnOuky5tYLH4JqamYGbEdRq5xNg+HQXt5ivUedY4GM2d4Ek9YtO0lHyPF7ZcrRQJ5e7auc81Fg+57N8Esm4","Signers":[1,2,3],"Message":{"MsgType":2,"Height":50,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"k/fLjlW3b3EomFbpA/r8cP9s9b1tmqUPe97nxML9+8YsCZcr5yQktY/YQjDGaHi7ENfMQuViMNTJ+IKLQ4j/wcbRSLuPKH1ciptzdXi3ynYJmcO3ATxlVT9BOcaSNSp+","Signers":[1],"Message":{"MsgType":0,"Height":2,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ControllerPostRoot":"e8da00ea07e1e5098026373c51e38a681215e12ca4bdeb1f1efbb9d4f3325a92","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":null,"CalledSyncDecidedByRange":true,"DecidedByRangeValues":[0,50]}}],"OutputMessages":null,"ExpectedError":"instance not found"},"*tests.ControllerSpecTest_qbft controller process msg error":{"Name":"process msg error","RunInstanceData":[{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"kMgJWj8wMw5wE3IwJaPniNhlh5LPrqwnCvZQ9kusNUG6kc8DckZHiWeHwJwJnAKqGYA/Ni1lZTavTHA4h8U8NVVkDKJ6LOJk4s3AIUFrsbRnPRNHKNw7zhq43Y3EyAMi","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":100,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ControllerPostRoot":"47713c38fe74ce55959980781287886c603c2117a14dc8abce24dcb9be0093af","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":null,"DecidedCnt":0,"BroadcastedDecided":null,"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}}],"OutputMessages":null,"ExpectedError":"could not process msg: invalid signed message: proposal not justified: change round has no quorum"},"*tests.ControllerSpecTest_qbft controller single consensus msg":{"Name":"single consensus msg","RunInstanceData":[{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ControllerPostRoot":"c9258515e169e330c535d38068f2dc6bf3f61e6d36a941ea41a0133435afae22","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":null,"DecidedCnt":0,"BroadcastedDecided":null,"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}}],"OutputMessages":null,"ExpectedError":""},"*tests.ControllerSpecTest_qbft controller start instance empty value":{"Name":"start instance empty value","RunInstanceData":[{"InputValue":"","InputMessages":null,"ControllerPostRoot":"baf3ccea443a6c639b76dccf2d9c4fb5e48318473797de9b55e4d8de48fccc6b","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":null,"DecidedCnt":0,"BroadcastedDecided":null,"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}}],"OutputMessages":null,"ExpectedError":"can't start new QBFT instance: value invalid: invalid value"},"*tests.ControllerSpecTest_qbft controller start instance first height":{"Name":"start instance first height","RunInstanceData":[{"InputValue":"AQIDBA==","InputMessages":null,"ControllerPostRoot":"47713c38fe74ce55959980781287886c603c2117a14dc8abce24dcb9be0093af","ExpectedTimerState":{"Timeouts":1,"Round":1},"ExpectedDecidedState":{"DecidedVal":null,"DecidedCnt":0,"BroadcastedDecided":null,"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}}],"OutputMessages":null,"ExpectedError":""},"*tests.ControllerSpecTest_qbft controller start instance invalid value":{"Name":"start instance invalid value","RunInstanceData":[{"InputValue":"AQEBAQ==","InputMessages":null,"ControllerPostRoot":"baf3ccea443a6c639b76dccf2d9c4fb5e48318473797de9b55e4d8de48fccc6b","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":null,"DecidedCnt":0,"BroadcastedDecided":null,"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}}],"OutputMessages":null,"ExpectedError":"can't start new QBFT instance: value invalid: invalid value"},"*tests.ControllerSpecTest_qbft controller start instance nil value":{"Name":"start instance nil value","RunInstanceData":[{"InputValue":null,"InputMessages":null,"ControllerPostRoot":"baf3ccea443a6c639b76dccf2d9c4fb5e48318473797de9b55e4d8de48fccc6b","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":null,"DecidedCnt":0,"BroadcastedDecided":null,"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}}],"OutputMessages":null,"ExpectedError":"can't start new QBFT instance: value invalid: invalid value"},"*tests.ControllerSpecTest_qbft controller start instance post future decided":{"Name":"start instance post future decided","RunInstanceData":[{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"tDaFqXgayP7v782nniCBFQ0egTOpUlUM8p1LlTZAsnn1TYNWCI4qcYmlo3brXmO4F9207XiuHXeeq8XflMaQKg/cyb90ZOSzZK5c9XoSRf3gSirLDwZGRYWs3CWKgjzf","Signers":[1,2,3],"Message":{"MsgType":2,"Height":10,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ControllerPostRoot":"589b0c0352f1c22875246f2e66530d5fda62f646434b250ade128c61c16f47bd","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":null,"CalledSyncDecidedByRange":true,"DecidedByRangeValues":[0,10]}},{"InputValue":"AQIDBA==","InputMessages":null,"ControllerPostRoot":"b4553bc2b2e105311bcc65dfa18efeeb8a44ff01a6e9c2a34417afed3b474b02","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":0,"BroadcastedDecided":null,"CalledSyncDecidedByRange":true,"DecidedByRangeValues":[0,10]}}],"OutputMessages":null,"ExpectedError":""},"*tests.ControllerSpecTest_qbft controller start instance prev decided":{"Name":"start instance prev decided","RunInstanceData":[{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooE","Signers":[2],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRM","Signers":[3],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"kFGy1aK4Kychuv0xFbXRVjFgDU6hCAaltJ+Nbjsie+2yEmuFSwss4IiyekCVHu+1F9TN31CUv+uZ4FwzPQSkR5oM709TN/61iEcY4j44Zm/OXL8pnEhSipyXSSngprJ6","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"q+s2ftJ53lt+iNxpg6bfNxGrjGkY19VA5zRn92qJueuJgKCVyLQkrpNymxHC5bQADhtbiD8/LNzJ9bB0w14T3M8xooKaUCt6IX4qh5wqkfAicOB+KZ4+wGTfq/XQBGaj","Signers":[2],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"sbN4I4KmsP1QwuIS5hztulBMjvjyZsJIxc7VnPfzdBMPLq+BImXyrU2wPwzSyjPJDXsE9EQFb2D7NPnCA/gIutwGbRK/kwdRYUKH6VL5L5qPmw7Be2nDhly1J9iELyIq","Signers":[3],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"ControllerPostRoot":"24cf697092529cfab3ab06b969d8696692c8bcbb9f41a954f71dc74c3b1d7e97","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":null,"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}},{"InputValue":"AQIDBA==","InputMessages":null,"ControllerPostRoot":"5c23b58681df24d3131dab8fcff377035d03bb5a8db99c16c68bd644baa2d3b3","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":null,"DecidedCnt":0,"BroadcastedDecided":null,"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}}],"OutputMessages":null,"ExpectedError":""},"*tests.ControllerSpecTest_qbft controller start instance prev not decided":{"Name":"start instance prev not decided","RunInstanceData":[{"InputValue":"AQIDBA==","InputMessages":null,"ControllerPostRoot":"47713c38fe74ce55959980781287886c603c2117a14dc8abce24dcb9be0093af","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":null,"DecidedCnt":0,"BroadcastedDecided":null,"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}},{"InputValue":"AQIDBA==","InputMessages":null,"ControllerPostRoot":"47713c38fe74ce55959980781287886c603c2117a14dc8abce24dcb9be0093af","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":null,"DecidedCnt":0,"BroadcastedDecided":null,"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}}],"OutputMessages":null,"ExpectedError":"can't start new QBFT instance: previous instance hasn't Decided"},"*tests.ControllerSpecTest_qbft controller start instance valid":{"Name":"start instance valid","RunInstanceData":[{"InputValue":"AQIDBA==","InputMessages":null,"ControllerPostRoot":"47713c38fe74ce55959980781287886c603c2117a14dc8abce24dcb9be0093af","ExpectedTimerState":{"Timeouts":1,"Round":1},"ExpectedDecidedState":{"DecidedVal":null,"DecidedCnt":0,"BroadcastedDecided":null,"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}}],"OutputMessages":null,"ExpectedError":""},"*tests.ControllerSpecTest_qbft controller valid":{"Name":"valid","RunInstanceData":[{"InputValue":"AQIDBA==","InputMessages":[{"Signature":"ok+cfOp6t9/h8xvePHOpDCnIif+wMjLeYWhHv5/Csldj3S/G2PgaYaZhxpSKKURCFXRMTSP1OhfbMinxQrnxZYg7SP2C6NDOa/9vV6NLaX/eusH+W91JkAgPd+seFNhr","Signers":[1,2,3],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ControllerPostRoot":"f552f5aedb2e0d7933e77c4297c69e761000e88f78ae02e0afd4d053847b8d5c","ExpectedTimerState":null,"ExpectedDecidedState":{"DecidedVal":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ","DecidedCnt":1,"BroadcastedDecided":null,"CalledSyncDecidedByRange":false,"DecidedByRangeValues":[0,0]}}],"OutputMessages":null,"ExpectedError":""},"*tests.CreateMsgSpecTest_qbft create message create commit":{"Name":"create commit","Value":[1,2,3,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"Round":10,"RoundChangeJustifications":null,"PrepareJustifications":null,"CreateType":"CreateCommit","ExpectedRoot":"0834b51f3c87d4aba362d7e2eeb4172d22e0ef18d4dfadd37e8c9ceb62c7719d","ExpectedError":""},"*tests.CreateMsgSpecTest_qbft create message create prepare":{"Name":"create prepare","Value":[1,2,3,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"Round":10,"RoundChangeJustifications":null,"PrepareJustifications":null,"CreateType":"CreatePrepare","ExpectedRoot":"2eba5b18818e0ec94f5c02ff7abc8ca932ed5d1f32a115197fbaa14247a39cb2","ExpectedError":""},"*tests.CreateMsgSpecTest_qbft create message create proposal":{"Name":"create proposal","Value":[1,2,3,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"Round":0,"RoundChangeJustifications":null,"PrepareJustifications":null,"CreateType":"createProposal","ExpectedRoot":"dfb0a692281b916b1d037df44f5c742f13ac3ee207ea0082cc3ca2afff34e178","ExpectedError":""},"*tests.CreateMsgSpecTest_qbft create message create proposal not previously prepared":{"Name":"create proposal not previously prepared","Value":[1,2,3,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"Round":0,"RoundChangeJustifications":[{"Signature":"gztz02gJCBBld01h2Pb/7pG5BvLmymJYG+fOGi8fCq81dVcUcoNMZmCvloEyiT+OD5Cl1b+v6OaSbkM80leaeENvDl8G377UXnKTI4wY/74ve7a7iZgSLRf/x7fNM7fB","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"jLHzMp8Y+D46RWGLJEOr49k05Q+UkqPjWgknRsFgFHRJgHGOi0u6wt10PLief4fVCUJOEXE1fbENSqUgtf03KVpQ3EaEN94xbfkY5AFfbfF7yk12EpEOP/0C49uBovdQ","Signers":[2],"Message":{"MsgType":0,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"ts0KRie2NM79t+H+9ZajE3y5g3oTw3ak/YMsEEZzWWE2G4EtrJCJevuhAwxFhnwgFbcMf8xHfxryZP5dTcRS2zA30tVz/n6XsPmHJT5dzwtVdY9QpJ8hQsE56sJSEsOz","Signers":[3],"Message":{"MsgType":0,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"PrepareJustifications":null,"CreateType":"createProposal","ExpectedRoot":"45f15e2dc3a0c1bd13aa977ead44abfabab094864db4a77dcd0cd9a30e87c3bb","ExpectedError":""},"*tests.CreateMsgSpecTest_qbft create message create proposal previously prepared":{"Name":"create proposal previously prepared","Value":[1,2,3,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"Round":0,"RoundChangeJustifications":[{"Signature":"t3wDXB0dnGx8wigQxQqFyeVgrXkVCQVQYd/gfUA+2qEwQWFGbUn2SES25c9LCXCfBp3r/ZFDj5fUFKT2TNy0+M8elwP42hQb6fdFCQh+L4RJIxSwNBlmxLj8Ftkx9roI","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"mMq307rvpww3BxdPm0KCuat1CPoddAl9w/tnc524RivFpWby1PNqd9ymZl6/aFkOGOkeJoqoIZJgn22ff+c9UplIJTKxcIGm5d70ocze3Sx7TCq+A2xixwEsOb/A0831","Signers":[2],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"lLT5TkZ3/7MmNTrG07fwT6CwDZ4una1ok5q6S7EHMBfoBJSNN5XOl3X1Hir/GbWLDs+i2Xrwlu+WjIqNYsaICol14dSQYu1LErxRH0dre4CUBLD7uPMIRUej/0GNt2S8","Signers":[3],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"PrepareJustifications":[{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooE","Signers":[2],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRM","Signers":[3],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"CreateType":"createProposal","ExpectedRoot":"282dd7899470e882fc22e9284628f4c25b2e3ba89bc0f50becb677c9a2e4708c","ExpectedError":""},"*tests.CreateMsgSpecTest_qbft create message create round change":{"Name":"create round change","Value":[1,2,3,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"Round":0,"RoundChangeJustifications":null,"PrepareJustifications":null,"CreateType":"CreateRoundChange","ExpectedRoot":"e08923100c9970e713651b4ff2ce0308b0c761e97eb009b93beefc0b923828f5","ExpectedError":""},"*tests.CreateMsgSpecTest_qbft create message create round change previously prepared":{"Name":"create round change previously prepared","Value":[1,2,3,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"Round":0,"RoundChangeJustifications":null,"PrepareJustifications":[{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooE","Signers":[2],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRM","Signers":[3],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"CreateType":"createProposal","ExpectedRoot":"a095b2f42e32e62980d9675cfcf3a58ed4da4d05b63463e182a451461de3b44e","ExpectedError":""},"*tests.MsgProcessingSpecTest_qbft message processing commit current round":{"Name":"commit current round","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"c3e7db86655d01a58ae7a54e4c181c1626014adf43c30d694785874e30c005b9","InputMessages":[{"Signature":"kFGy1aK4Kychuv0xFbXRVjFgDU6hCAaltJ+Nbjsie+2yEmuFSwss4IiyekCVHu+1F9TN31CUv+uZ4FwzPQSkR5oM709TN/61iEcY4j44Zm/OXL8pnEhSipyXSSngprJ6","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"OutputMessages":null,"ExpectedError":"","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing commit data != acceptedProposalData.Data":{"Name":"commit data != acceptedProposalData.Data","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"b61f5233721865ca43afc68f4ad5045eeb123f6e8f095ce76ecf956dabc74713","InputMessages":[{"Signature":"mDw5XJhKjNX1P6JnxNt3QdZy2PvnBwdIr6JUIgr9M7Aw4SABw1Xivaq8hNVcLzS3BGmJKYQX7BpvkbuH/M2WDtUxlBmlaOy5aDKQVwteuVfwHNQr+jScvllz9nSBKkiy","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[171,223,56,59,122,252,254,162,11,93,114,204,119,150,8,230,250,112,249,19,252,223,246,20,151,76,6,90,175,133,17,249],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"OutputMessages":null,"ExpectedError":"invalid signed message: proposed data mistmatch","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing commit data != prepared data":{"Name":"commit data != prepared data","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"976cd5cecd58bba892a38ec0ef02b3aed4656fb89fef473d8af78fedf095439d","InputMessages":[{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooE","Signers":[2],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRM","Signers":[3],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"mDw5XJhKjNX1P6JnxNt3QdZy2PvnBwdIr6JUIgr9M7Aw4SABw1Xivaq8hNVcLzS3BGmJKYQX7BpvkbuH/M2WDtUxlBmlaOy5aDKQVwteuVfwHNQr+jScvllz9nSBKkiy","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[171,223,56,59,122,252,254,162,11,93,114,204,119,150,8,230,250,112,249,19,252,223,246,20,151,76,6,90,175,133,17,249],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"OutputMessages":[{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"kFGy1aK4Kychuv0xFbXRVjFgDU6hCAaltJ+Nbjsie+2yEmuFSwss4IiyekCVHu+1F9TN31CUv+uZ4FwzPQSkR5oM709TN/61iEcY4j44Zm/OXL8pnEhSipyXSSngprJ6","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"ExpectedError":"invalid signed message: proposed data mistmatch","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing commit future round":{"Name":"commit future round","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"b61f5233721865ca43afc68f4ad5045eeb123f6e8f095ce76ecf956dabc74713","InputMessages":[{"Signature":"g4KG9ysNs6oCilSUUjIeaLCVxNkOb04OhT3OxuqzozSCU7YyIwKguDcTNel9Dw0tCpaulMPrIm31VD5eE9OH5DklvF2hNjeVT/3YWtX2brkMuF9zag142guBiAnML6In","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"OutputMessages":null,"ExpectedError":"invalid signed message: wrong msg round","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing commit happy flow":{"Name":"commit happy flow","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"a298f278a78362257e233e1db8eeb44c2bb9b45a55bb3555928d6723231ebcd2","InputMessages":[{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooE","Signers":[2],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRM","Signers":[3],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"kFGy1aK4Kychuv0xFbXRVjFgDU6hCAaltJ+Nbjsie+2yEmuFSwss4IiyekCVHu+1F9TN31CUv+uZ4FwzPQSkR5oM709TN/61iEcY4j44Zm/OXL8pnEhSipyXSSngprJ6","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"q+s2ftJ53lt+iNxpg6bfNxGrjGkY19VA5zRn92qJueuJgKCVyLQkrpNymxHC5bQADhtbiD8/LNzJ9bB0w14T3M8xooKaUCt6IX4qh5wqkfAicOB+KZ4+wGTfq/XQBGaj","Signers":[2],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"sbN4I4KmsP1QwuIS5hztulBMjvjyZsJIxc7VnPfzdBMPLq+BImXyrU2wPwzSyjPJDXsE9EQFb2D7NPnCA/gIutwGbRK/kwdRYUKH6VL5L5qPmw7Be2nDhly1J9iELyIq","Signers":[3],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"OutputMessages":[{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"kFGy1aK4Kychuv0xFbXRVjFgDU6hCAaltJ+Nbjsie+2yEmuFSwss4IiyekCVHu+1F9TN31CUv+uZ4FwzPQSkR5oM709TN/61iEcY4j44Zm/OXL8pnEhSipyXSSngprJ6","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"ExpectedError":"","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing commit invalid val check":{"Name":"commit invalid val check","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"5b18ca0b470208d8d247543306850618f02bddcbaa7c37eb6d5b36eb3accb5fb","InputMessages":[],"OutputMessages":[],"ExpectedError":"","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing commit no prepare quorum":{"Name":"commit no prepare quorum","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"254e9491d5e3167c239ec04149e7b6d29402a6f8638010c35e04f1cea7f8f7e0","InputMessages":[{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooE","Signers":[2],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"kFGy1aK4Kychuv0xFbXRVjFgDU6hCAaltJ+Nbjsie+2yEmuFSwss4IiyekCVHu+1F9TN31CUv+uZ4FwzPQSkR5oM709TN/61iEcY4j44Zm/OXL8pnEhSipyXSSngprJ6","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"q+s2ftJ53lt+iNxpg6bfNxGrjGkY19VA5zRn92qJueuJgKCVyLQkrpNymxHC5bQADhtbiD8/LNzJ9bB0w14T3M8xooKaUCt6IX4qh5wqkfAicOB+KZ4+wGTfq/XQBGaj","Signers":[2],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"sbN4I4KmsP1QwuIS5hztulBMjvjyZsJIxc7VnPfzdBMPLq+BImXyrU2wPwzSyjPJDXsE9EQFb2D7NPnCA/gIutwGbRK/kwdRYUKH6VL5L5qPmw7Be2nDhly1J9iELyIq","Signers":[3],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"OutputMessages":[{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ExpectedError":"","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing commit past round":{"Name":"commit past round","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":5,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":{"Signature":"q8R98okpfDj5G/JLg1oLEbuVamFDVoF+U2iD5c3mbxQbsq0mhCi9MjSvcEuUosYlCZrbHr1mEA2fkF5GYF9737XAGGVwOmQUKdJKYudPMyQRJwP7Gfb4GStBTiVXLR4i","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":5,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"929e208f33ae4f872976a962256ddddebfb625905ff02903315586688ec1562d","InputMessages":[{"Signature":"g4KG9ysNs6oCilSUUjIeaLCVxNkOb04OhT3OxuqzozSCU7YyIwKguDcTNel9Dw0tCpaulMPrIm31VD5eE9OH5DklvF2hNjeVT/3YWtX2brkMuF9zag142guBiAnML6In","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"OutputMessages":null,"ExpectedError":"invalid signed message: past round","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing different proposal round change justification":{"Name":"different proposal round change justification","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":3,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"0ad556ecb45cf0366e4067fad721d5674fed6f75706bfb63fd6d512742fbc46c","InputMessages":[{"Signature":"tRLBHYuyohO8YyDehFIbUX7By84zGLCia/vuD7c/lIQeUxcqJM0nwOaSoxguTc3aFfEWR46VZT47ciKy0o6EFHzTTcO5drhsniBITLBAe5GqCtyOvibjDrJs5nNvfNNu","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":3,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":["oqogtSDFcKNGwA6LazB126oPQM/gL+Sq7d3P6Sf50v4DLwe27XVfTWh6kQJ3oIMyEVDnrq17ockOMGFABvAVgP5c4xufhWI2LYDs/78Qq+nnED51ID6vAde9NCvbPt9SbAAAAHQAAAAcAwAAAQAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAwAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQBAAAAAAAAAFAAAACoAgAAAQIDBAwAAADQAAAAlAEAAIEp5oYqUSC9CF4ZNrTvtaVfx9GcDQ/aDp7FdtGKvUoXqzoDP1KWt0xf2vhct7PaMgG2P+yna4g2E+OxyhN+djo0Ljsd3bzgFvjKPLzjLIsSXdjCWnY5gZwgtTnp58bFeWwAAAB0AAAAxAAAAAEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAATAAAAL6Vb7ffTvN1MWgtWIMgCE/JFMPw/tM1Jj5bRAYubCm0AAAAAAAAAABQAAAAUAAAAAECAwSEnhPcn4cPkyDDrML3nq3WEu40fnfuTScs19kmgbZcMOuVawnZe4TlyWRb1GHLgmcMafsrGhwbwgyT27FOmo+V0J2A7PXyiJ01o8pf0aEGCq32lvqo5yPenHuXeNOqigRsAAAAdAAAAMQAAAACAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAEwAAAC+lW+3307zdTFoLViDIAhPyRTD8P7TNSY+W0QGLmwptAAAAAAAAAAAUAAAAFAAAAABAgMEr0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","rMABLDOIdpu/rzp8kHcAbR2YXojlNEpQuL/Pk1yNRijjrsAaMRp9BSSDUYjVPCsED1AbgvT60kOKIE0ADWnXny4tghNa7Z+jM7/9n4QWxQ8pui89H0BlYeraGEkiEkOSbAAAAHQAAAAcAwAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAwAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQCAAAAAAAAAFAAAACoAgAAAQIDBAwAAADQAAAAlAEAAIAJ7tIfW/hoTAVkQ+bhsF0C2VPiJEe0+R2p9vJyTRh5lpFjjpiZaKNhLDZIaOvrRA3CnhCknAagzLdDLPhhO2lH05Hj/CvFAzpv5SJb+khMxQZU4EmiU0Zrsmzl9cfhKWwAAAB0AAAAxAAAAAEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAIAAAAAAAAATAAAAL6Vb7ffTvN1MWgtWIMgCE/JFMPw/tM1Jj5bRAYubCm0AAAAAAAAAABQAAAAUAAAAAECAwSD+YoeqZbjt6S9cRjrY3l8pND2gcFTlApJnGN6HIf0xnxfu3h6CfH/UXdJTf12gYMD54mUDb+vSvDZDN2M9tfIFjXpGuSgCqo6Ai1I3OwIS8mXekSwT0xWnxHLyve+z1NsAAAAdAAAAMQAAAACAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAACAAAAAAAAAEwAAAC+lW+3307zdTFoLViDIAhPyRTD8P7TNSY+W0QGLmwptAAAAAAAAAAAUAAAAFAAAAABAgMEj7thZ1r0Gs6AEJKBXe2IpYOkIQrN1ggSWQjb6Nz7KLazl+4Iikq9xEPjgs/oiNbYFB5BWknHZR1t3QMWWu/45saLm+WS289AZ503i9GVNM1Rcs+O845PvoFYjWstIUOBbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","k43FZ6ZB8Ke7oSc87rz3PRMg5a+LNU25H557YRknOBbnFRU7/fZUuVXhAyCWlFhKFlK8ZMCtL3Pm63iB7l/IXXruK4GbI5+ovDXITmKnURsD5jEcVNhFm9lTrtU6geGLbAAAAHQAAADEAAAAAwAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAwAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":["gAnu0h9b+GhMBWRD5uGwXQLZU+IkR7T5Han28nJNGHmWkWOOmJloo2EsNkho6+tEDcKeEKScBqDMt0Ms+GE7aUfTkeP8K8UDOm/lIlv6SEzFBlTgSaJTRmuybOX1x+EpbAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","g/mKHqmW47ekvXEY62N5fKTQ9oHBU5QKSZxjehyH9MZ8X7t4egnx/1F3SU39doGDA+eJlA2/r0rw2QzdjPbXyBY16RrkoAqqOgItSNzsCEvJl3pEsE9MVp8Ry8r3vs9TbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","j7thZ1r0Gs6AEJKBXe2IpYOkIQrN1ggSWQjb6Nz7KLazl+4Iikq9xEPjgs/oiNbYFB5BWknHZR1t3QMWWu/45saLm+WS289AZ503i9GVNM1Rcs+O845PvoFYjWstIUOBbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="]},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[{"Signature":"uRVam5lXLnOrgqIOlOPDBmabhgdKsS2tTGYSvYNd+mLD3DAo/r6xdVtK3ok2ZRrZBfXf3GUEJ7kRj5+p4w7+WRpzNYdnoMoTPyh+8vmKOby2umFsSmD29pwiXoSzdfiI","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":3,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ExpectedError":"","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing duplicate commit message":{"Name":"duplicate commit message","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"c3e7db86655d01a58ae7a54e4c181c1626014adf43c30d694785874e30c005b9","InputMessages":[{"Signature":"kFGy1aK4Kychuv0xFbXRVjFgDU6hCAaltJ+Nbjsie+2yEmuFSwss4IiyekCVHu+1F9TN31CUv+uZ4FwzPQSkR5oM709TN/61iEcY4j44Zm/OXL8pnEhSipyXSSngprJ6","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"kFGy1aK4Kychuv0xFbXRVjFgDU6hCAaltJ+Nbjsie+2yEmuFSwss4IiyekCVHu+1F9TN31CUv+uZ4FwzPQSkR5oM709TN/61iEcY4j44Zm/OXL8pnEhSipyXSSngprJ6","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"OutputMessages":null,"ExpectedError":"","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing duplicate prepare message":{"Name":"duplicate prepare message","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"2e51f4773ca9d8091d39dca8bbc3bc4989404992cdba8d4044bfd952c2b8335a","InputMessages":[{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":null,"ExpectedError":"","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing duplicate prepare msg justification":{"Name":"duplicate prepare msg justification","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"5b18ca0b470208d8d247543306850618f02bddcbaa7c37eb6d5b36eb3accb5fb","InputMessages":[{"Signature":"g6ow5oIPJSd019mwZeuAK0b0P/ABYvj2FaC3LsK6uUMJldgenesis5j23corYzoooSq0DDwIQx7g/GYLnakTA4zAkt5QFDuybo8fx+/34e+UxwKG/WXfaErpcLlhfQ/UlwUb+","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":["plIUtAwj7vH5lhPTpT82+EikZXM7tirBQJ+MyoRg1ofQJ00ykJyr8z+4HzWwgW4+AANoVvQuYEeFelI3dbw4URumPzEEMVlUtVl40VIHkaA1L+vGMIzxxrD+PeNgagJqbAAAAHQAAAAcAwAAAQAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQBAAAAAAAAAFAAAACoAgAAAQIDBAwAAADQAAAAlAEAAIEp5oYqUSC9CF4ZNrTvtaVfx9GcDQ/aDp7FdtGKvUoXqzoDP1KWt0xf2vhct7PaMgG2P+yna4g2E+OxyhN+djo0Ljsd3bzgFvjKPLzjLIsSXdjCWnY5gZwgtTnp58bFeWwAAAB0AAAAxAAAAAEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAATAAAAL6Vb7ffTvN1MWgtWIMgCE/JFMPw/tM1Jj5bRAYubCm0AAAAAAAAAABQAAAAUAAAAAECAwSEnhPcn4cPkyDDrML3nq3WEu40fnfuTScs19kmgbZcMOuVawnZe4TlyWRb1GHLgmcMafsrGhwbwgyT27FOmo+V0J2A7PXyiJ01o8pf0aEGCq32lvqo5yPenHuXeNOqigRsAAAAdAAAAMQAAAACAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAEwAAAC+lW+3307zdTFoLViDIAhPyRTD8P7TNSY+W0QGLmwptAAAAAAAAAAAUAAAAFAAAAABAgMEhJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","kvxUVc4gJi+BzwPlaEiF0e6Bu/qAPu5TDJmovyL08zHAClqdK9QpO32x7A6EO92DDbCmgwMyWcSKDHf6d1OfTqlG91Nx5dpPONvkkdxJ7cDCTeEQD3kMoCrDy+akPcW9bAAAAHQAAAAcAwAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQBAAAAAAAAAFAAAACoAgAAAQIDBAwAAADQAAAAlAEAAIEp5oYqUSC9CF4ZNrTvtaVfx9GcDQ/aDp7FdtGKvUoXqzoDP1KWt0xf2vhct7PaMgG2P+yna4g2E+OxyhN+djo0Ljsd3bzgFvjKPLzjLIsSXdjCWnY5gZwgtTnp58bFeWwAAAB0AAAAxAAAAAEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAATAAAAL6Vb7ffTvN1MWgtWIMgCE/JFMPw/tM1Jj5bRAYubCm0AAAAAAAAAABQAAAAUAAAAAECAwSEnhPcn4cPkyDDrML3nq3WEu40fnfuTScs19kmgbZcMOuVawnZe4TlyWRb1GHLgmcMafsrGhwbwgyT27FOmo+V0J2A7PXyiJ01o8pf0aEGCq32lvqo5yPenHuXeNOqigRsAAAAdAAAAMQAAAACAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAEwAAAC+lW+3307zdTFoLViDIAhPyRTD8P7TNSY+W0QGLmwptAAAAAAAAAAAUAAAAFAAAAABAgMEhJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","tKuBmgSxL83ImKAcRzx7jMBNOW/it0V9Qr6V3LO2N2NF9cjydKgqeov+0C8E0TDkAhvEbYOz64lAHpLHJnzZt7SVogd0aMZZCedTizuxwsYXLp8N8XORJN1GlzWjYmv1bAAAAHQAAAAcAwAAAwAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQBAAAAAAAAAFAAAACoAgAAAQIDBAwAAADQAAAAlAEAAIEp5oYqUSC9CF4ZNrTvtaVfx9GcDQ/aDp7FdtGKvUoXqzoDP1KWt0xf2vhct7PaMgG2P+yna4g2E+OxyhN+djo0Ljsd3bzgFvjKPLzjLIsSXdjCWnY5gZwgtTnp58bFeWwAAAB0AAAAxAAAAAEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAATAAAAL6Vb7ffTvN1MWgtWIMgCE/JFMPw/tM1Jj5bRAYubCm0AAAAAAAAAABQAAAAUAAAAAECAwSEnhPcn4cPkyDDrML3nq3WEu40fnfuTScs19kmgbZcMOuVawnZe4TlyWRb1GHLgmcMafsrGhwbwgyT27FOmo+V0J2A7PXyiJ01o8pf0aEGCq32lvqo5yPenHuXeNOqigRsAAAAdAAAAMQAAAACAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAEwAAAC+lW+3307zdTFoLViDIAhPyRTD8P7TNSY+W0QGLmwptAAAAAAAAAAAUAAAAFAAAAABAgMEhJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="]},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[],"ExpectedError":"invalid signed message: proposal not justified: change round msg not valid: no justifications quorum","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing duplicate prepare msg justification quorum":{"Name":"duplicate prepare msg justification quorum","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":2,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"3e3e18c3092be9b71beb13a33df58abeac35608b5ac98939fad4111492e0b920","InputMessages":[{"Signature":"gdRkNDmu71gJOb7xksCfUTm3pDaz6843YO7jZIzCmXBdBO8NvnkD3z5wObtarqxkBpO6ioc2OVHfQmN5E+gdaO0bRdpUkGldc6W+XWrU9zDuGhGEnjYZqAmrDoWbEbkf","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":["mdvo5DOkgZKXA7dQIzZqAS5pqWVN1i4ENFS8dQAkBNr00iU8rNhrUqaqnDEFxy0zBZiqWUs1jc7xoNBm2kQec5XHuxFHgaBKppso8kYfSh7aJzQC4spLnu/DEAW+l1EsbAAAAHQAAADkAwAAAQAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQBAAAAAAAAAFAAAABwAwAAAQIDBBAAAADUAAAAmAEAAFwCAACBKeaGKlEgvQheGTa077WlX8fRnA0P2g6exXbRir1KF6s6Az9SlrdMX9r4XLez2jIBtj/sp2uINhPjscoTfnY6NC47Hd284Bb4yjy84yyLEl3Ywlp2OYGcILU56efGxXlsAAAAdAAAAMQAAAABAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAEwAAAC+lW+3307zdTFoLViDIAhPyRTD8P7TNSY+W0QGLmwptAAAAAAAAAAAUAAAAFAAAAABAgMEhJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBISeE9yfhw+TIMOswveerdYS7jR+d+5NJyzX2SaBtlww65VrCdl7hOXJZFvUYcuCZwxp+ysaHBvCDJPbsU6aj5XQnYDs9fKInTWjyl/RoQYKrfaW+qjnI96ce5d406qKBGwAAAB0AAAAxAAAAAIAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAATAAAAL6Vb7ffTvN1MWgtWIMgCE/JFMPw/tM1Jj5bRAYubCm0AAAAAAAAAABQAAAAUAAAAAECAwSvSlxIBBSPmKJW3TsZ72jVZBiHkTve6/cTaeLNvuIRflC2Gw/dbTijN3WgljvNZX8HRQDlrtmTQEgOm9l7Mqkpr3XhyPno8Wqn2PY2GsWn7HLmQhRcFRq9LMDvmw4MpExsAAAAdAAAAMQAAAADAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAEwAAAC+lW+3307zdTFoLViDIAhPyRTD8P7TNSY+W0QGLmwptAAAAAAAAAAAUAAAAFAAAAABAgME","p12Y9YhX0PMd+7h4Ea6s7305MMRXd9IWr0dZQbha4IRiuVt69ipayX+Z1bd9fmQJC3IKmG022vCpn1ABfy1PVOAf/zRhKg4gTQ/HgpxROD8YDPeJurTrbyncuX6LUeoEbAAAAHQAAADkAwAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQBAAAAAAAAAFAAAABwAwAAAQIDBBAAAADUAAAAmAEAAFwCAACBKeaGKlEgvQheGTa077WlX8fRnA0P2g6exXbRir1KF6s6Az9SlrdMX9r4XLez2jIBtj/sp2uINhPjscoTfnY6NC47Hd284Bb4yjy84yyLEl3Ywlp2OYGcILU56efGxXlsAAAAdAAAAMQAAAABAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAEwAAAC+lW+3307zdTFoLViDIAhPyRTD8P7TNSY+W0QGLmwptAAAAAAAAAAAUAAAAFAAAAABAgMEhJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBISeE9yfhw+TIMOswveerdYS7jR+d+5NJyzX2SaBtlww65VrCdl7hOXJZFvUYcuCZwxp+ysaHBvCDJPbsU6aj5XQnYDs9fKInTWjyl/RoQYKrfaW+qjnI96ce5d406qKBGwAAAB0AAAAxAAAAAIAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAATAAAAL6Vb7ffTvN1MWgtWIMgCE/JFMPw/tM1Jj5bRAYubCm0AAAAAAAAAABQAAAAUAAAAAECAwSvSlxIBBSPmKJW3TsZ72jVZBiHkTve6/cTaeLNvuIRflC2Gw/dbTijN3WgljvNZX8HRQDlrtmTQEgOm9l7Mqkpr3XhyPno8Wqn2PY2GsWn7HLmQhRcFRq9LMDvmw4MpExsAAAAdAAAAMQAAAADAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAEwAAAC+lW+3307zdTFoLViDIAhPyRTD8P7TNSY+W0QGLmwptAAAAAAAAAAAUAAAAFAAAAABAgME","sObPryorHpaKZAuJEnMoTEfxa4OzPUCEaJb+kRdF+F7vsFco/Z441Q8Zi5ybqNGAAfe8M7zeusCNlLA+r5stK4DkJQ0xOI2V38dpTympMvLc3DFDvqhP4fk/I6zw736qbAAAAHQAAADkAwAAAwAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQBAAAAAAAAAFAAAABwAwAAAQIDBBAAAADUAAAAmAEAAFwCAACBKeaGKlEgvQheGTa077WlX8fRnA0P2g6exXbRir1KF6s6Az9SlrdMX9r4XLez2jIBtj/sp2uINhPjscoTfnY6NC47Hd284Bb4yjy84yyLEl3Ywlp2OYGcILU56efGxXlsAAAAdAAAAMQAAAABAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAEwAAAC+lW+3307zdTFoLViDIAhPyRTD8P7TNSY+W0QGLmwptAAAAAAAAAAAUAAAAFAAAAABAgMEhJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBISeE9yfhw+TIMOswveerdYS7jR+d+5NJyzX2SaBtlww65VrCdl7hOXJZFvUYcuCZwxp+ysaHBvCDJPbsU6aj5XQnYDs9fKInTWjyl/RoQYKrfaW+qjnI96ce5d406qKBGwAAAB0AAAAxAAAAAIAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAATAAAAL6Vb7ffTvN1MWgtWIMgCE/JFMPw/tM1Jj5bRAYubCm0AAAAAAAAAABQAAAAUAAAAAECAwSvSlxIBBSPmKJW3TsZ72jVZBiHkTve6/cTaeLNvuIRflC2Gw/dbTijN3WgljvNZX8HRQDlrtmTQEgOm9l7Mqkpr3XhyPno8Wqn2PY2GsWn7HLmQhRcFRq9LMDvmw4MpExsAAAAdAAAAMQAAAADAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAEwAAAC+lW+3307zdTFoLViDIAhPyRTD8P7TNSY+W0QGLmwptAAAAAAAAAAAUAAAAFAAAAABAgME"],"PrepareJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="]},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gAnu0h9b+GhMBWRD5uGwXQLZU+IkR7T5Han28nJNGHmWkWOOmJloo2EsNkho6+tEDcKeEKScBqDMt0Ms+GE7aUfTkeP8K8UDOm/lIlv6SEzFBlTgSaJTRmuybOX1x+Ep","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[{"Signature":"gAnu0h9b+GhMBWRD5uGwXQLZU+IkR7T5Han28nJNGHmWkWOOmJloo2EsNkho6+tEDcKeEKScBqDMt0Ms+GE7aUfTkeP8K8UDOm/lIlv6SEzFBlTgSaJTRmuybOX1x+Ep","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ExpectedError":"","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing duplicate rc msg justification":{"Name":"duplicate rc msg justification","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"5b18ca0b470208d8d247543306850618f02bddcbaa7c37eb6d5b36eb3accb5fb","InputMessages":[{"Signature":"pHc2xpV/4BWAPhaR/HZgkkAricm/z8acdQzo/d7BJpGhRZeTtYIteCR7VgVi8o0OCdWZ1YQ/KZqlQWra2Enka/XCCDqLlDmrdCnw8YDg1vmJPFV0s2MWwrDxqIBZTtIr","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":["t3wDXB0dnGx8wigQxQqFyeVgrXkVCQVQYd/gfUA+2qEwQWFGbUn2SES25c9LCXCfBp3r/ZFDj5fUFKT2TNy0+M8elwP42hQb6fdFCQh+L4RJIxSwNBlmxLj8Ftkx9roIbAAAAHQAAADEAAAAAQAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","mMq307rvpww3BxdPm0KCuat1CPoddAl9w/tnc524RivFpWby1PNqd9ymZl6/aFkOGOkeJoqoIZJgn22ff+c9UplIJTKxcIGm5d70ocze3Sx7TCq+A2xixwEsOb/A0831bAAAAHQAAADEAAAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","mMq307rvpww3BxdPm0KCuat1CPoddAl9w/tnc524RivFpWby1PNqd9ymZl6/aFkOGOkeJoqoIZJgn22ff+c9UplIJTKxcIGm5d70ocze3Sx7TCq+A2xixwEsOb/A0831bAAAAHQAAADEAAAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[],"ExpectedError":"invalid signed message: proposal not justified: change round has no quorum","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing duplicate rc msg justification (prepared)":{"Name":"duplicate rc msg justification (prepared)","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"5b18ca0b470208d8d247543306850618f02bddcbaa7c37eb6d5b36eb3accb5fb","InputMessages":[{"Signature":"quBGZHNzfVj2hsUIH8V+D0V8QKSWNh6gLYNleC8Ttmn98XfjCitJ5XQx2S7Zc9IIDGhBFJQRccqwzyu5HorR0M+lHFtmBlEyAq0lZLE/UJ3aBDLs/6yO0xcX4DbIgB40","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":["t3wDXB0dnGx8wigQxQqFyeVgrXkVCQVQYd/gfUA+2qEwQWFGbUn2SES25c9LCXCfBp3r/ZFDj5fUFKT2TNy0+M8elwP42hQb6fdFCQh+L4RJIxSwNBlmxLj8Ftkx9roIbAAAAHQAAADEAAAAAQAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","mMq307rvpww3BxdPm0KCuat1CPoddAl9w/tnc524RivFpWby1PNqd9ymZl6/aFkOGOkeJoqoIZJgn22ff+c9UplIJTKxcIGm5d70ocze3Sx7TCq+A2xixwEsOb/A0831bAAAAHQAAADEAAAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","mMq307rvpww3BxdPm0KCuat1CPoddAl9w/tnc524RivFpWby1PNqd9ymZl6/aFkOGOkeJoqoIZJgn22ff+c9UplIJTKxcIGm5d70ocze3Sx7TCq+A2xixwEsOb/A0831bAAAAHQAAADEAAAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="]},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[],"ExpectedError":"invalid signed message: proposal not justified: change round has no quorum","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing duplicate rc msg justification (prepared) quorum":{"Name":"duplicate rc msg justification (prepared) quorum","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":2,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"c1550b9c9d23a609909e4f749a8b87723643593833c123ced276ecf96dea1ac8","InputMessages":[{"Signature":"tFP5xUFzggRZ/D0bpMVLK8qTa12BuhA+Y6V7uMl85gL23m5aPDkBDOfwYcFyfbHxAglm7CbTUIqRF+cNsSK9lC3FSB7iVO7MkBDrttMgkXp0314ASmBOwRI/ubbG3/Ei","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":["t3wDXB0dnGx8wigQxQqFyeVgrXkVCQVQYd/gfUA+2qEwQWFGbUn2SES25c9LCXCfBp3r/ZFDj5fUFKT2TNy0+M8elwP42hQb6fdFCQh+L4RJIxSwNBlmxLj8Ftkx9roIbAAAAHQAAADEAAAAAQAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","mMq307rvpww3BxdPm0KCuat1CPoddAl9w/tnc524RivFpWby1PNqd9ymZl6/aFkOGOkeJoqoIZJgn22ff+c9UplIJTKxcIGm5d70ocze3Sx7TCq+A2xixwEsOb/A0831bAAAAHQAAADEAAAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","mMq307rvpww3BxdPm0KCuat1CPoddAl9w/tnc524RivFpWby1PNqd9ymZl6/aFkOGOkeJoqoIZJgn22ff+c9UplIJTKxcIGm5d70ocze3Sx7TCq+A2xixwEsOb/A0831bAAAAHQAAADEAAAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","lLT5TkZ3/7MmNTrG07fwT6CwDZ4una1ok5q6S7EHMBfoBJSNN5XOl3X1Hir/GbWLDs+i2Xrwlu+WjIqNYsaICol14dSQYu1LErxRH0dre4CUBLD7uPMIRUej/0GNt2S8bAAAAHQAAADEAAAAAwAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="]},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gAnu0h9b+GhMBWRD5uGwXQLZU+IkR7T5Han28nJNGHmWkWOOmJloo2EsNkho6+tEDcKeEKScBqDMt0Ms+GE7aUfTkeP8K8UDOm/lIlv6SEzFBlTgSaJTRmuybOX1x+Ep","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[{"Signature":"gAnu0h9b+GhMBWRD5uGwXQLZU+IkR7T5Han28nJNGHmWkWOOmJloo2EsNkho6+tEDcKeEKScBqDMt0Ms+GE7aUfTkeP8K8UDOm/lIlv6SEzFBlTgSaJTRmuybOX1x+Ep","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ExpectedError":"","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing duplicate signers":{"Name":"duplicate signers","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"b61f5233721865ca43afc68f4ad5045eeb123f6e8f095ce76ecf956dabc74713","InputMessages":[{"Signature":"rzzg3vDeJIORpwFale7F3swzhxCV8rzv9I21/RQG/NdZ7h+3W3tVO+9rRwHAwoLpCb4rNpL+pUd7LgD1TLRrzw6gHsaLMcKyk1q4UgjcAbccQb+n+5OzAwc7g63hbGB3","Signers":[1,1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[],"ExpectedError":"invalid signed message: invalid signed message: non unique signer","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing f+1 speed up":{"Name":"f+1 speed up","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"a7d293fbc40215b96d25c87dc10fce642f25cda2d004e745b2e08a5ebc5db2e3","InputMessages":[{"Signature":"l2L7FVW+G1CyLcDuAOHspS+CjtTYIrZXoDlIZD3sB+M3QaLxVZn5EL+Z9GDws//oCVDA2xMGfrpix9MtFLShHU7SD98JO3emM+VHEil3E7BJvb5uuyfmYdQQQPwKIMm1","Signers":[2],"Message":{"MsgType":3,"Height":0,"Round":10,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"g8g41B98ET4mdY91qVGKYCsQSTis5t/D01TIOGsCK5DgkH9Wl7xBiCQiRipTJvEnFJovyojhdZfVg0z9+obYvm7ki1FaCd6/MEMkL4XMxnzGu/DTWCyScyDvGQypRvku","Signers":[3],"Message":{"MsgType":3,"Height":0,"Round":10,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[{"Signature":"rLaFgc9H8I7jqpITXAiUNthMnGmAFMzjJPyl8UM/IbjHEPtX87hs3nOpnrQsZW/YGbNyh4zW47LxU2oWX+mxwJTdtG73QMIN++Bggo8HN7c/+RmmbaPOMwuuIy9PaBAW","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":10,"Identifier":"AQIDBA==","Root":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"DataRound":0,"RoundChangeJustification":[],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ExpectedError":"","ExpectedTimerState":{"Timeouts":1,"Round":10}},"*tests.MsgProcessingSpecTest_qbft message processing f+1 speed up prev prepared":{"Name":"f+1 speed up prev prepared","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"8daa5ad4f87b49d0db9640b379736f063788e862f2ece35af707d2adf4a531d5","InputMessages":[{"Signature":"l2L7FVW+G1CyLcDuAOHspS+CjtTYIrZXoDlIZD3sB+M3QaLxVZn5EL+Z9GDws//oCVDA2xMGfrpix9MtFLShHU7SD98JO3emM+VHEil3E7BJvb5uuyfmYdQQQPwKIMm1","Signers":[2],"Message":{"MsgType":3,"Height":0,"Round":10,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"qAwUrCdNFq2xyHyRnUOx6FJmAVSEQNgwNUfTcqJ34s2S9Lt/3UnktIxuKR77FzIpElDjLWJ31JAIvD47nZwD/3UiEZpdkTogrHPW8tFot7YeDTQSAwvYoDKleoWOXJ5H","Signers":[3],"Message":{"MsgType":3,"Height":0,"Round":10,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":1,"RoundChangeJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[{"Signature":"rLaFgc9H8I7jqpITXAiUNthMnGmAFMzjJPyl8UM/IbjHEPtX87hs3nOpnrQsZW/YGbNyh4zW47LxU2oWX+mxwJTdtG73QMIN++Bggo8HN7c/+RmmbaPOMwuuIy9PaBAW","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":10,"Identifier":"AQIDBA==","Root":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"DataRound":0,"RoundChangeJustification":[],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ExpectedError":"","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing happy flow":{"Name":"happy flow","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"a298f278a78362257e233e1db8eeb44c2bb9b45a55bb3555928d6723231ebcd2","InputMessages":[{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooE","Signers":[2],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRM","Signers":[3],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"kFGy1aK4Kychuv0xFbXRVjFgDU6hCAaltJ+Nbjsie+2yEmuFSwss4IiyekCVHu+1F9TN31CUv+uZ4FwzPQSkR5oM709TN/61iEcY4j44Zm/OXL8pnEhSipyXSSngprJ6","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"q+s2ftJ53lt+iNxpg6bfNxGrjGkY19VA5zRn92qJueuJgKCVyLQkrpNymxHC5bQADhtbiD8/LNzJ9bB0w14T3M8xooKaUCt6IX4qh5wqkfAicOB+KZ4+wGTfq/XQBGaj","Signers":[2],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"sbN4I4KmsP1QwuIS5hztulBMjvjyZsJIxc7VnPfzdBMPLq+BImXyrU2wPwzSyjPJDXsE9EQFb2D7NPnCA/gIutwGbRK/kwdRYUKH6VL5L5qPmw7Be2nDhly1J9iELyIq","Signers":[3],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"OutputMessages":[{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"kFGy1aK4Kychuv0xFbXRVjFgDU6hCAaltJ+Nbjsie+2yEmuFSwss4IiyekCVHu+1F9TN31CUv+uZ4FwzPQSkR5oM709TN/61iEcY4j44Zm/OXL8pnEhSipyXSSngprJ6","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"ExpectedError":"","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing happy flow seven operators":{"Name":"happy flow seven operators","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"pgeB9Ugap70IWdx7qiW7a/5Sui+4l3+5o8E9G7Jwft5FpQ//kRMQ8JHjF1CS3Ayd","Committee":[{"OperatorID":1,"PubKey":"pgeB9Ugap70IWdx7qiW7a/5Sui+4l3+5o8E9G7Jwft5FpQ//kRMQ8JHjF1CS3Ayd"},{"OperatorID":2,"PubKey":"jXMElrInKT59Ivf870VECBSYqoRdTtqhs7Or/D+Qc2MY93LkXDd6Gk5EPgenesistvwkR"},{"OperatorID":3,"PubKey":"rkjWpS2fbSz6YIt7WZh1uz/Ta5PIUobPIHtkeGBU4ThuB7FiNUTE5z1OvVd59zb/"},{"OperatorID":4,"PubKey":"uUAZ/1poWAPUaMIEoLiaOBdsfoYOi0UyOIzBZ2ubkkhvnlx1KQMnNYKACivwHB95"},{"OperatorID":5,"PubKey":"goikQONEw8kSjkdq2ejAonhyRiZEYfKJ29AhfksG45MjLCSCYKjtp3Uv7sPcgXpE"},{"OperatorID":6,"PubKey":"t7peJG6pEiaHaGK5C1xgtZyEv5P6Hbncm4LXr/0rielwAtcEC2obSlUbuBKcfEQH"},{"OperatorID":7,"PubKey":"lEKsE9YbFjwNewZRv5c/1jTXFaGJudKyyBP47EoWVsDtEmsINEmYmBbObebc/x6Y"}],"Quorum":5,"PartialQuorum":3,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"0230e78f218fbc7f8560d0997e9ae003e90d4e1cf710be1b3917cf659ef955c4","InputMessages":[{"Signature":"oOFgU02nLy4XKA6Fizu1ITRSg0FlV3McUPwr62esaS9zX/soBPyr9diSNleo8cSsFd8yx/Q9B/PNloSYJeLFLDfTGTtJAOOFIDAsS3FpPaCya28DRO3JJxTUPoxqYH3m","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"k2hQxGKhgx41LC/nDV3cPAo3WvZguzC8vIoQ1PjSdDWgCGXesjvUETwBC/DAwFzUAgBqgP6hMLtcOPnWvpMpYo30SkR63AozOpULQLVmvqw+VMDV/FU88S3k9yGjOIbq","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"he+zbT2vBhNi3POHmPfrpfYd5t0RwJtxq4vvvHzcEHIbo65gvI/vL0sUNcGmS/obAzFryTzMsVJlzqFiCIzCDjUD78hYPr7RmReIjyWphuRsPzQAI7Mg/i9rC7+FK9Ab","Signers":[2],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"sQfPsTx6QQwg+KhHUvIK8cDHCu5SKYjdspJ6n9Qux4Sk3n24e17ouNrKerrD9ouUB1e27+rOEMzrrd9dmOYktXsAtu9yVwh5Vb/TMtObhVRcFGhit5Us/B8jZPYihyDd","Signers":[3],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"prQNyn2phykASLK+lXXjgLngUlsn6EaBIzpqrnEArw3FJt8yoTm5tWGvxEu3Dw32EkH7pG5b/PNzwCqcL5NAkij9ZwkRebl0ulIDOdHI2rjj/mopDLVSdJ7EZZmWuRWi","Signers":[4],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"rID2cKfNY4/NV0KPqqmhmhKzSkmteIA9jIQponfb6/FtBqhiGZuYhdzXc0Cz8w8YGSmF0/5HwEp7ISBWwQAbK16UMt2Z3yS+XldUhk9CX5Tq7AI1abu+hVjnjgawulCU","Signers":[5],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"prY6zAXUUpnXxlNUU5Uk/g9+ujS7/jfHNUiW97OJq7tw+jM1OhkTpW3uzQTRQSdYEpuqGB7RAWJE5TqBY6L3QUZ+hF+lxAR8MCqTXFzt1ZDBDvTuYY3uKJS4BPNrLZJE","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"jvQ0kK2B8rh0j+hx8fimNX09CCTmRKHxCLxu8w5kY/cn2WbJxS3tONfJ+0RKlp0RCCaPq2a+eMU4V5aIrnq3NsBkmBOTgfkYpvKptsPatLxjEcozlJPflp2iwO1sGt3l","Signers":[2],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"q6d12fnH1xoG/aZX560ggOmg5yPjFwaxxXUjJno4SLBDgwHvnmr99JSN0q2HJnNtB5AYVyIVYhpK8I2717b39C/uqP9D8QmjRvWlxojuiKfJNRhOST6+X2k432YqY/Of","Signers":[3],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"suFiJlin87qR3t+tPmyPBO7jyk0g/G1sgExXrJKNbFOmcBYH3Lxn4uRbYRiXceP3CZW2UttEpJsoArnuNQ264bqCsHDoaCkjMWV04K2xidyCllaX82Z0V5TROrBS04n7","Signers":[4],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"iizbsd48rTR2KEVQh4yiLRnqCu7s1pT9UsSncAdSEXF/4SkfRgd/ggXhkZz4/Hh+CgkSFRXMhjk7oCEA1i9rLmn//TB2PAYKiICtMLqjk8dabDEKr9puIE0apLprsghx","Signers":[5],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"OutputMessages":[{"Signature":"k2hQxGKhgx41LC/nDV3cPAo3WvZguzC8vIoQ1PjSdDWgCGXesjvUETwBC/DAwFzUAgBqgP6hMLtcOPnWvpMpYo30SkR63AozOpULQLVmvqw+VMDV/FU88S3k9yGjOIbq","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"prY6zAXUUpnXxlNUU5Uk/g9+ujS7/jfHNUiW97OJq7tw+jM1OhkTpW3uzQTRQSdYEpuqGB7RAWJE5TqBY6L3QUZ+hF+lxAR8MCqTXFzt1ZDBDvTuYY3uKJS4BPNrLZJE","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"ExpectedError":"","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing happy flow ten operators":{"Name":"happy flow ten operators","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"uaw8M/4ZQSTsUSNYBhD6l6Czq7ScOaI0hAQhUIwIxCsJgy0Pm0FjXeul8TMDRmHx","Committee":[{"OperatorID":1,"PubKey":"uaw8M/4ZQSTsUSNYBhD6l6Czq7ScOaI0hAQhUIwIxCsJgy0Pm0FjXeul8TMDRmHx"},{"OperatorID":2,"PubKey":"kz1pTGkkb5iqKz1t524qR2rdHuj5H6A5SbEVvHoPZ3u5ZGIgBH/krSlDVUVCQlID"},{"OperatorID":3,"PubKey":"oa8IOuuo3GEYpGecefYZvvNk1VYbI8fxLPDSfgQIMZu+/rxyyutWy8+21ul3ilKG"},{"OperatorID":4,"PubKey":"rbbPE1h+tjwTMBvXBq0uQeiY7zjgW8F3SHU/GbiTh6NSfksUWkw4+ePzXeQF/QdH"},{"OperatorID":5,"PubKey":"ovesq67A79GGQk7tkM5WjCDtuuuf9+xT9fGA5wk3pppddGByCSve0md4ED5rGJNo"},{"OperatorID":6,"PubKey":"k0G6HgfsQWNlvKiso3P/s80hTVrPnkjyTPrc0+2uB+PaplJffnFvSdmjkBNW4Mye"},{"OperatorID":7,"PubKey":"jKJiWGQ9GMT6EaPHMt+EWZKfacZB1sXPUXgWYugOJa3hMm1CbTGoRXXnYRS8yz/R"},{"OperatorID":8,"PubKey":"q0rU1AgNbIxwQ0gfqb5DpEbdriONEftFDWGhOesWB/KjAmm7SijrQkMfybi/qNAt"},{"OperatorID":9,"PubKey":"obBtOlWzBdTeziCtVY5FteuDACOmXzBRXfZONryRSaDgvfQlc01s+Qewj67gYRtH"},{"OperatorID":10,"PubKey":"tJ7NtDheMBJsjieXeanh6kC+9ONNv116vN0genesisxyqUfEqqfST3Ku/Vx3rhBvtAidF"}],"Quorum":7,"PartialQuorum":4,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"5343638957c46929c846d4f10aa288d039ba22e862e4a7a8ed744253819387d6","InputMessages":[{"Signature":"jlTHhJM7XGNH2oNG6Zr/GMem+tIvLdguEkbGhpSFLsDINVz7Gx951whnZsiCmEJVEUz0azoMVIzs33rRJuC+XBgZakIS/LmYgCN1EHuKdoRw5aWoYjCTe72XW9DAyTt2","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"oQFlYs6zdlfdSUqDf/W1v4wYJZDtbUdm6rMyAw9zwWOi4cGN+cbRDYe5oxLblvrvF0lTMbkWipOgenesisEyMz8yjyf0oxjuqTGZJib4tzXXWpAdkzaT2s44YWzfbs5Y2pRCQ","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"pRUkT8iwUPf74VigvHh7mIQ6/Eq4SvMgiTl/r/Kmelxjh/hzqNCkw9eAC8JgEML0BYraYn+aE0JixxUvTT4NTkMfpJPHaRrAV3i9TW3TErTqkJZ/LNUFGnYo85hpU10u","Signers":[2],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"ii76dhRfqOh5zoMa2I3EHeKcQLbR/jLXFbf/twtAifl17Y2LUiklky6rMD/ll+4ZEgT54JNzn8sDeBR0el5U6RRc5oX/a3OHqbAI+4gK3oMqg4HqsFt61AxDNwtOu9uU","Signers":[3],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"kwwbPU0tRDkLH7CJzAJBLzAdy1ARW2ZxGABDC7vOGn0L+PkoxqzThX2XPLLz4/8TBs1CHC6TnEaKEq8DudssvvjyxF/U9bonbcaf2MJXnZEzKTLH6elx/EZfxOJOiRGs","Signers":[4],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"sMj/Ew24lKLQvjtSNDqEA606ZYAt54XsuPa7SsxHxfpiU11nk1qx+7BOXBmHvKCADpicDnr+qFdwGTpC8+Wi/0TkJ7nceiJl2iKpb9aUIDV0IDCsKknkjDnZ4NAubmvD","Signers":[5],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"iNvgm0Dd8OI8hPRE3eY9iI2QbM47yHQ0ML4Rmei479/OwCwNqMfXglChAbFURLVVD7oT3BBu4tLAkibDfxIQbcBW0CLNU/Q0QRpoD4vsVJ/57JJoioJQLR0IHPDY2EH1","Signers":[6],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"hGg5FeKLzs5DotY/yZAA3OT3qEZsFXq93/RnWug7nHFnQ4TgoXXGQE5+psBSQiC4DHEgPjT+VFD2lhTMbIxJaSVV/mohmMb0OlXBdjLQ8vHtnFSMr/RL9XBb7PvkMrZW","Signers":[7],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"pLt8DFEupjMOqSUJGZ3OJkJS3QU8zp7Bf3q1HASaOK6bGx4BQZycH5MkFVd6BE59FWdMbkMNnjnR8M8rLtspEZ9f7ppMqYPoBn+4RcY2vDcDOvA4Jnf09Ry/4MX6EVVR","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"tRI/RSmdR8DV/szggFUzOZ52wA1lAtGe9CbBUuj6sE2dj+09tGMOsDG3B3o/5f8fCGtW4BlWdy/1x5lPVX65hy2CXJuEBxgWesORIHQ6RDtIgribDkRpxcNt5C0ZrORU","Signers":[2],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"t3nif51+8mx7j2hCDbA3zFjp5KUKeQMVruxdrzu9PrJOelxnTA9bSVLV4hCdtLZnD8K6QBrz7GOZqzkizLGCzNCC/MLRK696YPS+GRVzu+QIUoSc9BHr9f4EYwir9sqA","Signers":[3],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"iXwY3GvLdlWEEnYcNoSB0RllOKRpVUjVr6vkceZx0ywg/0MDUvRWVx6fMYhzc3ezBQOf9zs5yDAP4JADao1HRvVikTZr0fLf3wsrIiGQ9WiLQ45tVpYm6Wp1qO02ltk0","Signers":[4],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"j0p+WIf/W3xtnIg4xVHIKuDZ5+L4IBopOpQf7ED1PqAGhL0YfXKg4eJxWi6sEDhCACuM6jIYsve2ldSv3TsPeTuYiRBQVCU5ciUA7aHj/a4qyFG035LjOTA0n6ZJ8FnR","Signers":[5],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"hFt0ZSWcPEkN9ZEF+Gdk8Mc2e79Wg6z/19FIwhDg9VkLLYuf5nY+JFVZqGAiBJy7FzGunFn4hBxGJnCd7axIH4NMaXV1u1I2cl6vl5djcEjDzqm7NG8kT5UmrSi367j/","Signers":[6],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"iycCd+uie8UJNdnHrO30uNQps+QfOFaqVua3AelRSyeqru4hGhRG50A2H9BuM7A6EcBAkidpwmIbSU+0NGfmzKaB4lYkHjZwOsIkzfCcZbGRzr/a4OKLTz/hJC/x5bpt","Signers":[7],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"OutputMessages":[{"Signature":"oQFlYs6zdlfdSUqDf/W1v4wYJZDtbUdm6rMyAw9zwWOi4cGN+cbRDYe5oxLblvrvF0lTMbkWipOgenesisEyMz8yjyf0oxjuqTGZJib4tzXXWpAdkzaT2s44YWzfbs5Y2pRCQ","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"pLt8DFEupjMOqSUJGZ3OJkJS3QU8zp7Bf3q1HASaOK6bGx4BQZycH5MkFVd6BE59FWdMbkMNnjnR8M8rLtspEZ9f7ppMqYPoBn+4RcY2vDcDOvA4Jnf09Ry/4MX6EVVR","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"ExpectedError":"","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing happy flow thirteen operators":{"Name":"happy flow thirteen operators","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"hI5zYqHx/eeuUNu47igpgY80yHA2Te2lJxNAy4TZZWIGVNVENfV4msUv+lRUKazt","Committee":[{"OperatorID":1,"PubKey":"hI5zYqHx/eeuUNu47igpgY80yHA2Te2lJxNAy4TZZWIGVNVENfV4msUv+lRUKazt"},{"OperatorID":2,"PubKey":"gLOhjAC9gn7p3Anu5jV1qhZHsjpE2kaCKlufbGqWLkGk2d8Zud87XprQaPd6yx9Z"},{"OperatorID":3,"PubKey":"hdGTJS5FHnymzBPtOa2J8NFZgkuTSNL6bRlcnMQ+3Yc42m0YCfUMme7pVRZqbpTZ"},{"OperatorID":4,"PubKey":"segmLvr3PGJeNMUmR3/KgSXx5ghNACzwU8EdOrcJWMrHC18g2aLLFE+g8ewHyNnz"},{"OperatorID":5,"PubKey":"kNPPyMK8POfloYXlgcsmadBB2meJ6JyL0juCIYFy0JhMDzrcrLz/hrlm044VfnNb"},{"OperatorID":6,"PubKey":"inPM4U47OFEcVAewYzVFUKPrWUBbtZAyURSQ61zC1XxKMXU3d95zPfyY5vAyqIa3"},{"OperatorID":7,"PubKey":"jp+9nrZ8sYqDz0AxKKBxD6HXkQc6wAC4Lg8mq7+LNJr7C9v/fcohAk468TIL0o3j"},{"OperatorID":8,"PubKey":"kdu3pEx4bR4kiUa7++Zq9MgbC6iLzVjSwOj6Aui8FYCtxKndIaKlJ6kZo9/Y2Ddo"},{"OperatorID":9,"PubKey":"mHjlwu4h7wD7ddCACZHbzNzeNV5gE6q0m26NM0c10+ZKnfe1nPvrAaWNat1/L0WP"},{"OperatorID":10,"PubKey":"kF19qpdOmicblY+rPEzD8J6ONES2rkdid7enfjhYI5fmqVtOPOrBDfUSyuPfOfoj"},{"OperatorID":11,"PubKey":"tnj/6m3UVXaCBWL7SQ7Wqjrdhv/VtD2DxC0YHujmalndSaGwm8sv7VZK0L8xaowq"},{"OperatorID":12,"PubKey":"lsQeEx+FxRb2yCq9hgTcgaNUVhSxBJg9H7zW4qADv76cPGPBBQKD/Z/v34MPglL3"},{"OperatorID":13,"PubKey":"p0Ku0QA3sia0RoXTDFjemn3CYyR7hYmehxQc0991MyNVwj3Kh8y6G5mq8H8HiIim"}],"Quorum":9,"PartialQuorum":5,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"ce494f9e2fc81dab675feaab631e7e828424971aec32d28b6b01977973ee4d93","InputMessages":[{"Signature":"ua5ZltJ697EUeF/m+kIpR55ytQeRb4RfKBTlWlWeGoeimvjBPRfKvLS228rcWcYCFbfzkOONi3MHc6KoNXMp1rfRhEsKAcGlgRtzjD0SNTb9goiC7/RB0+S+ltj5E+PC","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"iqJEwEhq0XwclzCcDsBtNphbQs4Jtc751F/v8Okb6CDH+JAM1+cuomeYIYfBhAMmGCNvzVXOVbyeybMgtVPIc8HgfAfHI4MUdsd9UkWDLyQmnHWc2m3SHHqXfa/AGqa/","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"qvpla6MtYcqvussBBhNhwL140fcWpZ29qJJwXyZ8GT/0SQSYFq45O66TIHEruW58FGpyoaRC4IEpcaEZV9tl2KVF4Jl1Mat3FiemjeHGAl9mdYzec1VHrJQvPd+T2Kb8","Signers":[2],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"kJAaA0aOtcUxSRVIfiz+UkWE1TPEaqhLI3NxKF7cV99ma2SNhhA2TT1gHn8xu49UGFtjplMOi7xwLIauO/Ab+keTJpAoDgLmuYmSgUS6DkW4QYVkKWpjRwH1yoPSnNpk","Signers":[3],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"lXGfsXlfcz2MAFiZUP6MZZO8YN2864Hml/moFalQ/7JyfJ/wbGwHuqaDGV6XtEQKE/BlH3CC6bDWBy/w+G57pS4QwgFxa92wduh/mFCSdT7hfZ4UrSom4YUVxdzT7WsT","Signers":[4],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"rpBQz3pjAHrFdK6PHlPPbt2iMV5cBSh2+ZRiCdxckyHXd0JifYDtQmMwtZG+ws9pGDVo6TGzPx05jecNYydTwKkvHMNgv3o2Gu8zr5kvo7zKANEkSupqHBRSXdxwttTE","Signers":[5],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gjnJtU5s1fljcjsnKcUJtOXHcR4cscQSbn+ZOV+7Jw5B+qgenesisCbGj8qc9GYgP43+ZGW0QqBetaiJy16Z7I3n7HfvRqUtQpz1BuBt7PafEP5GwIbztJJRsaRCNkTOEeIRM","Signers":[6],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"tATLWgvJzldrrji2xcnG/I7zaRN+qZTFcS3Tc4zI3/ArrTSZFy/wRhrd+66GEofmE+08hn7oIb+qQp30DZNjXmAMfkQ4WrT0/tpy9SVlnYzfSwaCAit1UgZOBnQW4/6h","Signers":[7],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"razhGliD6u51MckgttsVLH7xxL4ZJUfkSPn5yYBrkYNaOrMxdqccZbObNj0JKJDRD/ush7iNgKiQz5hbigUUv/wPaJyzHOjxTrRwTriwkvjaO10KuA70YM5QDoOheru6","Signers":[8],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"g4twts6kiOh9EEf+QowMIPwGANsFmbQ2qTSDFEupF0fZoQAdd5p0LL7/rNIKx+7HD4CzaVFbq43jMoRojBUv41jtyJAXUgAzLZRRESXQ7JxRZJQp8w7m2iEmRlYP8Huo","Signers":[9],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gCfDjAkV6Yr/e+cDwPmpPgPVj54brF0/AhBe20DHdo57SoGSPjUhde6CJppDShF6AMlMpWoV+n40XG0qaGCc/0nIEvRh+6f6l+3CHGnsCicyX8HalxsFpTsXPPZqABT7","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"hY9GSlXX3w5IQP3zPA8N4hFwoqW4zHjjQsQwhww11MapITqWj48c9uRYlYhYGGqIArOk5adtKD5ema+5HcgSTX5i+CPxBeXFv++WqkaZj8hUuOtQQnFBxsPrg4ddIK9m","Signers":[2],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"qXvylNZWO3umUSyoUxsNn6csUjrf2zuzAf3vJ3BE6IMio1EAoSPoXL8IlCX2DcX7CgRVCrxoDLme2wOy/Pgv0+sDaQ5BR57xvsOLxeo1Ud7xtL23WC5ygzmOAetF1hy4","Signers":[3],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"sJUH22H/Gft6FU23JtTClVf/mX1/jReyldB3irMstlauZZNJPVUbhWOAhc81HxO2EAcnMQ+/x/RYsVjI7T3/o0rG9CQwZ/28+L+Ihbf89eVcLOE8U8KWwFxd56RlTQW+","Signers":[4],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"rgzmwp0MdXvv9um6soJD4PYY75N/OPBHq7YHfWjvHlQAQ5pZxn7jhTYUyYPE2EzMF27V1mxZIx2E4zEkuxzQqie10G/KqykpexrtXhiAhNZWG3kE3S3RIpWLZeID8wb6","Signers":[5],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"l5gN66M01P6F44LEXHAbN6Y6guRgpnf4mRdiY1llrKH7/wWf9tvGj69C1b5ByiYuC/J4CBcw7tBb6O5jS1lQ4kBLSUjUtDckAZHFjUXQMUdjTc395g1uJzVVXgSEKCSH","Signers":[6],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"sl5wMJSHqVKziDsV+tCMdpO77OE5YgH9YcHDdK7MzlE4y1LRplF64GDC0q5jvLWFCbK5hSq8MCy7ouffCE6zkqM1oCfZctYAxKFRdxCTdNtsqlctQpcLiqYgbqEuUAl9","Signers":[7],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"hkNbuanF7UjgyOS+kOXuc+kSF9mK8/9hds23INVh97tTi4H0zX41+PqPGYUA/nNWE/yVIfDwhOjtAwWpvXMiC9LrSCpZ6HOQ2pqZeWmdDaaoEGRiTk1U11aQnZF8rVZy","Signers":[8],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"j8xbYt+NYMrk1kH++pH5AFNtvFqfCQXuGsSGtG8GkcYSwzZrLX0cjqGoK9F2GYM6FQKV3o88zaXfC2y/FeWLrKmYIACqdi2RnFKyRqL/ytVKovQ170UulMroo305JRvr","Signers":[9],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"OutputMessages":[{"Signature":"iqJEwEhq0XwclzCcDsBtNphbQs4Jtc751F/v8Okb6CDH+JAM1+cuomeYIYfBhAMmGCNvzVXOVbyeybMgtVPIc8HgfAfHI4MUdsd9UkWDLyQmnHWc2m3SHHqXfa/AGqa/","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gCfDjAkV6Yr/e+cDwPmpPgPVj54brF0/AhBe20DHdo57SoGSPjUhde6CJppDShF6AMlMpWoV+n40XG0qaGCc/0nIEvRh+6f6l+3CHGnsCicyX8HalxsFpTsXPPZqABT7","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"ExpectedError":"","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing invalid full data":{"Name":"invalid full data","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"5b18ca0b470208d8d247543306850618f02bddcbaa7c37eb6d5b36eb3accb5fb","InputMessages":[{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"OutputMessages":null,"ExpectedError":"invalid signed message: H(data) != root","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing invalid prepare justification round":{"Name":"invalid prepare justification round","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"5b18ca0b470208d8d247543306850618f02bddcbaa7c37eb6d5b36eb3accb5fb","InputMessages":[{"Signature":"lV7qh4KR4L0NVRbDOALw1xLfoPIundEiV5Ia2OYe2euXOg/os753xaZHd8/8ZZ1HA6mKcySE50WW6emsjM834SXfIhfKe4kFIvSVm//jI7M82MkKocLQUEY17Fvjv0K0","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":["uT7HGsiY+fRH0tqUYuZY5ceRL7gXCEQxALyW0R8MxwfWa9zhcAGoyFZlfoN5OvenGNxagVYosOViJQD1/mt1hxnPwWNm/sx1ZlLj1f950L/xL3z9GGSnHofQ3ESx8hXLbAAAAHQAAAAcAwAAAQAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQBAAAAAAAAAFAAAACoAgAAAQIDBAwAAADQAAAAlAEAAIEp5oYqUSC9CF4ZNrTvtaVfx9GcDQ/aDp7FdtGKvUoXqzoDP1KWt0xf2vhct7PaMgG2P+yna4g2E+OxyhN+djo0Ljsd3bzgFvjKPLzjLIsSXdjCWnY5gZwgtTnp58bFeWwAAAB0AAAAxAAAAAEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAATAAAAL6Vb7ffTvN1MWgtWIMgCE/JFMPw/tM1Jj5bRAYubCm0AAAAAAAAAABQAAAAUAAAAAECAwSEnhPcn4cPkyDDrML3nq3WEu40fnfuTScs19kmgbZcMOuVawnZe4TlyWRb1GHLgmcMafsrGhwbwgyT27FOmo+V0J2A7PXyiJ01o8pf0aEGCq32lvqo5yPenHuXeNOqigRsAAAAdAAAAMQAAAACAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAEwAAAC+lW+3307zdTFoLViDIAhPyRTD8P7TNSY+W0QGLmwptAAAAAAAAAAAUAAAAFAAAAABAgMEj7thZ1r0Gs6AEJKBXe2IpYOkIQrN1ggSWQjb6Nz7KLazl+4Iikq9xEPjgs/oiNbYFB5BWknHZR1t3QMWWu/45saLm+WS289AZ503i9GVNM1Rcs+O845PvoFYjWstIUOBbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","mWyO2ifTqsbbMEI/m2zBsL8yHW0TGk+FjVfhhU1QZfl0xI6vsUdYQF4vnt1jKZJjCVKOnCjp0Tv/OXNvwp+bFFvmeJt5rEz4TDTTp10UV1qBLWrgCxRMKw2VdeN25L3vbAAAAHQAAAAcAwAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQBAAAAAAAAAFAAAACoAgAAAQIDBAwAAADQAAAAlAEAAIEp5oYqUSC9CF4ZNrTvtaVfx9GcDQ/aDp7FdtGKvUoXqzoDP1KWt0xf2vhct7PaMgG2P+yna4g2E+OxyhN+djo0Ljsd3bzgFvjKPLzjLIsSXdjCWnY5gZwgtTnp58bFeWwAAAB0AAAAxAAAAAEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAATAAAAL6Vb7ffTvN1MWgtWIMgCE/JFMPw/tM1Jj5bRAYubCm0AAAAAAAAAABQAAAAUAAAAAECAwSEnhPcn4cPkyDDrML3nq3WEu40fnfuTScs19kmgbZcMOuVawnZe4TlyWRb1GHLgmcMafsrGhwbwgyT27FOmo+V0J2A7PXyiJ01o8pf0aEGCq32lvqo5yPenHuXeNOqigRsAAAAdAAAAMQAAAACAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAEwAAAC+lW+3307zdTFoLViDIAhPyRTD8P7TNSY+W0QGLmwptAAAAAAAAAAAUAAAAFAAAAABAgMEj7thZ1r0Gs6AEJKBXe2IpYOkIQrN1ggSWQjb6Nz7KLazl+4Iikq9xEPjgs/oiNbYFB5BWknHZR1t3QMWWu/45saLm+WS289AZ503i9GVNM1Rcs+O845PvoFYjWstIUOBbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","tg8eCPGErXutB2t1BZ1epWHTDi0ERGiTIClpSsLsuUjWrhenJNDisfSl0/9mR1NoBljSd4+JBzG+aK8E8fmfa4IyMQW2MITu9PtGi1WV/tj+MGkniDvW1r9WFuvDEem/bAAAAHQAAAAcAwAAAwAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQBAAAAAAAAAFAAAACoAgAAAQIDBAwAAADQAAAAlAEAAIEp5oYqUSC9CF4ZNrTvtaVfx9GcDQ/aDp7FdtGKvUoXqzoDP1KWt0xf2vhct7PaMgG2P+yna4g2E+OxyhN+djo0Ljsd3bzgFvjKPLzjLIsSXdjCWnY5gZwgtTnp58bFeWwAAAB0AAAAxAAAAAEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAATAAAAL6Vb7ffTvN1MWgtWIMgCE/JFMPw/tM1Jj5bRAYubCm0AAAAAAAAAABQAAAAUAAAAAECAwSEnhPcn4cPkyDDrML3nq3WEu40fnfuTScs19kmgbZcMOuVawnZe4TlyWRb1GHLgmcMafsrGhwbwgyT27FOmo+V0J2A7PXyiJ01o8pf0aEGCq32lvqo5yPenHuXeNOqigRsAAAAdAAAAMQAAAACAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAEwAAAC+lW+3307zdTFoLViDIAhPyRTD8P7TNSY+W0QGLmwptAAAAAAAAAAAUAAAAFAAAAABAgMEj7thZ1r0Gs6AEJKBXe2IpYOkIQrN1ggSWQjb6Nz7KLazl+4Iikq9xEPjgs/oiNbYFB5BWknHZR1t3QMWWu/45saLm+WS289AZ503i9GVNM1Rcs+O845PvoFYjWstIUOBbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","j7thZ1r0Gs6AEJKBXe2IpYOkIQrN1ggSWQjb6Nz7KLazl+4Iikq9xEPjgs/oiNbYFB5BWknHZR1t3QMWWu/45saLm+WS289AZ503i9GVNM1Rcs+O845PvoFYjWstIUOBbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="]},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[],"ExpectedError":"invalid signed message: proposal not justified: change round msg not valid: round change justification invalid: wrong msg round","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing invalid prepare justification value":{"Name":"invalid prepare justification value","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"5b18ca0b470208d8d247543306850618f02bddcbaa7c37eb6d5b36eb3accb5fb","InputMessages":[{"Signature":"s7pbPq2rQdyGpKkEoii2WNhIF7BixffBPfYiLSpVep6F8IK54j+4GvwY+C7UBbbOAhwuAFHCmx+ZMIU12b7I0phKw4PczlpiNsQZnSBRN4sy59YDHfBtnWby832GDNKO","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":["uIk1S1Ue3Ptyt3gkqIWSYXl0wIufeCedyF3kLIRoSX8nNfPLGo5257xzRXAl5ez8Aqz956e5HuOH17Pl10wg8JH9OIYgTneRSpjpVOiTbfGVgJO7xoxhQ95IghSTQd20bAAAAHQAAAAcAwAAAQAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQBAAAAAAAAAFAAAACoAgAAAQIDBAwAAADQAAAAlAEAAIEp5oYqUSC9CF4ZNrTvtaVfx9GcDQ/aDp7FdtGKvUoXqzoDP1KWt0xf2vhct7PaMgG2P+yna4g2E+OxyhN+djo0Ljsd3bzgFvjKPLzjLIsSXdjCWnY5gZwgtTnp58bFeWwAAAB0AAAAxAAAAAEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAATAAAAL6Vb7ffTvN1MWgtWIMgCE/JFMPw/tM1Jj5bRAYubCm0AAAAAAAAAABQAAAAUAAAAAECAwSEnhPcn4cPkyDDrML3nq3WEu40fnfuTScs19kmgbZcMOuVawnZe4TlyWRb1GHLgmcMafsrGhwbwgyT27FOmo+V0J2A7PXyiJ01o8pf0aEGCq32lvqo5yPenHuXeNOqigRsAAAAdAAAAMQAAAACAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAEwAAAC+lW+3307zdTFoLViDIAhPyRTD8P7TNSY+W0QGLmwptAAAAAAAAAAAUAAAAFAAAAABAgMEjvmowbYhqNxwgUqhrCICbdoe4lkEcIbPLQ510HLUcW74HZEHvAOawbmvVX/ZejfzGM8P+DLX9EZgz1/XzDpHG4StpIB4mnpj0WFLQsWkUG6Ny7zDomG+X9EKrsuqjee4bAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAq984O3r8/qILXXLMd5YI5vpw+RP83/YUl0wGWq+FEfkAAAAAAAAAAFAAAABQAAAAAQIDBA==","hE3l1uYNoCjAWtvWq65UYFfYNjluieiA4puU5FFGxjWzY0rxIgrktbO2ntFtP3mgEjxfHFSRGnLgn2XJQk9TSdEGbihgxubxL8eAnhDw5aPALA14LtJq50NgxaI/4yiSbAAAAHQAAAAcAwAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQBAAAAAAAAAFAAAACoAgAAAQIDBAwAAADQAAAAlAEAAIEp5oYqUSC9CF4ZNrTvtaVfx9GcDQ/aDp7FdtGKvUoXqzoDP1KWt0xf2vhct7PaMgG2P+yna4g2E+OxyhN+djo0Ljsd3bzgFvjKPLzjLIsSXdjCWnY5gZwgtTnp58bFeWwAAAB0AAAAxAAAAAEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAATAAAAL6Vb7ffTvN1MWgtWIMgCE/JFMPw/tM1Jj5bRAYubCm0AAAAAAAAAABQAAAAUAAAAAECAwSEnhPcn4cPkyDDrML3nq3WEu40fnfuTScs19kmgbZcMOuVawnZe4TlyWRb1GHLgmcMafsrGhwbwgyT27FOmo+V0J2A7PXyiJ01o8pf0aEGCq32lvqo5yPenHuXeNOqigRsAAAAdAAAAMQAAAACAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAEwAAAC+lW+3307zdTFoLViDIAhPyRTD8P7TNSY+W0QGLmwptAAAAAAAAAAAUAAAAFAAAAABAgMEjvmowbYhqNxwgUqhrCICbdoe4lkEcIbPLQ510HLUcW74HZEHvAOawbmvVX/ZejfzGM8P+DLX9EZgz1/XzDpHG4StpIB4mnpj0WFLQsWkUG6Ny7zDomG+X9EKrsuqjee4bAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAq984O3r8/qILXXLMd5YI5vpw+RP83/YUl0wGWq+FEfkAAAAAAAAAAFAAAABQAAAAAQIDBA==","sQF+KZfV3QuhwOZxqXG3ZRD1h3URsKGdy1/WB9uCJ7ZO+kWorRlfIayU1aOzMb+0BRR013G1wkxe9lZelnqCEuq3Io//ji4Jo47LSHXBk6ixAryRDDodDNBiKprOq7yYbAAAAHQAAAAcAwAAAwAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQBAAAAAAAAAFAAAACoAgAAAQIDBAwAAADQAAAAlAEAAIEp5oYqUSC9CF4ZNrTvtaVfx9GcDQ/aDp7FdtGKvUoXqzoDP1KWt0xf2vhct7PaMgG2P+yna4g2E+OxyhN+djo0Ljsd3bzgFvjKPLzjLIsSXdjCWnY5gZwgtTnp58bFeWwAAAB0AAAAxAAAAAEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAATAAAAL6Vb7ffTvN1MWgtWIMgCE/JFMPw/tM1Jj5bRAYubCm0AAAAAAAAAABQAAAAUAAAAAECAwSEnhPcn4cPkyDDrML3nq3WEu40fnfuTScs19kmgbZcMOuVawnZe4TlyWRb1GHLgmcMafsrGhwbwgyT27FOmo+V0J2A7PXyiJ01o8pf0aEGCq32lvqo5yPenHuXeNOqigRsAAAAdAAAAMQAAAACAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAEwAAAC+lW+3307zdTFoLViDIAhPyRTD8P7TNSY+W0QGLmwptAAAAAAAAAAAUAAAAFAAAAABAgMEjvmowbYhqNxwgUqhrCICbdoe4lkEcIbPLQ510HLUcW74HZEHvAOawbmvVX/ZejfzGM8P+DLX9EZgz1/XzDpHG4StpIB4mnpj0WFLQsWkUG6Ny7zDomG+X9EKrsuqjee4bAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAq984O3r8/qILXXLMd5YI5vpw+RP83/YUl0wGWq+FEfkAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","jvmowbYhqNxwgUqhrCICbdoe4lkEcIbPLQ510HLUcW74HZEHvAOawbmvVX/ZejfzGM8P+DLX9EZgz1/XzDpHG4StpIB4mnpj0WFLQsWkUG6Ny7zDomG+X9EKrsuqjee4bAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAq984O3r8/qILXXLMd5YI5vpw+RP83/YUl0wGWq+FEfkAAAAAAAAAAFAAAABQAAAAAQIDBA=="]},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[],"ExpectedError":"invalid signed message: proposal not justified: change round msg not valid: round change justification invalid: proposed data mistmatch","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing justification duplicate msg":{"Name":"justification duplicate msg","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":2,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"ddb0f7e1a8888a8de5295005872d8525d6afea053121993dc65e34fcb7f290b2","InputMessages":[{"Signature":"plIUtAwj7vH5lhPTpT82+EikZXM7tirBQJ+MyoRg1ofQJ00ykJyr8z+4HzWwgW4+AANoVvQuYEeFelI3dbw4URumPzEEMVlUtVl40VIHkaA1L+vGMIzxxrD+PeNgagJq","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":1,"RoundChangeJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[],"ExpectedError":"invalid signed message: no justifications quorum","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing justification invalid round":{"Name":"justification invalid round","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":2,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"ddb0f7e1a8888a8de5295005872d8525d6afea053121993dc65e34fcb7f290b2","InputMessages":[{"Signature":"iPPwFltblEDnwW/7Pn1xntnOV1zWfOKzfhpW8c5BCc8D6a8qmmnzou238Z3AokOUECS99A/Oo5NjZc8RZZ+kkEDn5WYxHjJhwYnyZwoJcXBwdNmGKZ9P1XL9Pdll2dDh","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":1,"RoundChangeJustification":["swCHbgegE0zGay7Gay3q8OydHaqMI6K67Ap9+b46U7NYAnQ4RcVd2joTF1UFl2lbD5bfGqIZ4yO85sWD+cAOM0y3vM+UjhuqlDO+XVbCBbaDe2ZyxHiN8IWmO6viliekbAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAwAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","iB8SwLM6mon6GnqIcHbqX19wx3w8fJs+Y1+YHYmhbyYcDdQzQyZnWWwfGQkZwCt0Dgsr1wSehvpJX6h1Q4j2mVgfDHNB9iYtDkcsp6Nbn7g8FNE9xlI0t/K51mtLHWsmbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAwAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","g1MWlhnddp3URqN4d09Z2CkXRHGfHXrqwTsXSd0XPYy6AwKOHmwmz2+VEGXga7KQBRMrluCd7MNdmS5nw/qQPk+mx9ighrGRTpp0EinousMe9eHru//wOMk9nuzRrpeAbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAwAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[],"ExpectedError":"invalid signed message: round change justification invalid: wrong msg round","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing justification invalid sig":{"Name":"justification invalid sig","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":2,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"ddb0f7e1a8888a8de5295005872d8525d6afea053121993dc65e34fcb7f290b2","InputMessages":[{"Signature":"ojbTL4LqyJiY8y6b+kCmKQb2bfwKKYIS2pP4cSeEbgi7Dz5nBQD+HNfxxw7ZVBLbC97DgsejsUzCdCVW+SZIZsA4CRdfSVaJLph3ahhrWni1hNHfl4p2XoUaqy5jrCAc","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":1,"RoundChangeJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[],"ExpectedError":"invalid signed message: round change justification invalid: msg signature invalid: failed to verify signature","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing justification multi signer":{"Name":"justification multi signer","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":2,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"ddb0f7e1a8888a8de5295005872d8525d6afea053121993dc65e34fcb7f290b2","InputMessages":[{"Signature":"pVwtqZCCfDTjJC4nz/aN7xMOugpGMfhUR+e6mwiySJZn311sudcpHFhc5Sv/WlrLALsq5VlXLDnL93sW9LmYtYM8jn41lxYJWzFJvMjVSHk4UWfuBOsU+MK6Br4SjaGe","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":1,"RoundChangeJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","oVCjKKVtT8qKSnWXQEdIH/+ofcRGZftvZ6NG7SS84TUvE9PoUFqNqDJcZlPAH7ItEAiChlk/JsF/sdy9zzpK/W92IsbVXf9EME0pXcsaE6Os3b8NmZ6yr8xQWqGNfOInbAAAAHwAAADMAAAAAgAAAAAAAAADAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAEwAAAC+lW+3307zdTFoLViDIAhPyRTD8P7TNSY+W0QGLmwptAAAAAAAAAAAUAAAAFAAAAABAgME"],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[],"ExpectedError":"invalid signed message: round change justification invalid: msg allows 1 signer","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing multi signer, no overlap":{"Name":"multi signer, no overlap","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"4e569d9a6c0421d2bb69a4c544f8f1e67c73a129d4e6bd1304ddbae8812cfa38","InputMessages":[{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooE","Signers":[2],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRM","Signers":[3],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"kFGy1aK4Kychuv0xFbXRVjFgDU6hCAaltJ+Nbjsie+2yEmuFSwss4IiyekCVHu+1F9TN31CUv+uZ4FwzPQSkR5oM709TN/61iEcY4j44Zm/OXL8pnEhSipyXSSngprJ6","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"lNchT8MaT2rRLlibZ2qrI3CbdSadz3CPYcG52FiumRzmj5EMSi6lzg32J6daFKNTAkfl+YRS1Yaw9+jGlIeXmsawPLIw1S4meXOjUSA7CS60zYAUqFaLNn7UZA9be99D","Signers":[2,3],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"kFGy1aK4Kychuv0xFbXRVjFgDU6hCAaltJ+Nbjsie+2yEmuFSwss4IiyekCVHu+1F9TN31CUv+uZ4FwzPQSkR5oM709TN/61iEcY4j44Zm/OXL8pnEhSipyXSSngprJ6","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"ExpectedError":"invalid signed message: msg allows 1 signer","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing multi signer, with overlap":{"Name":"multi signer, with overlap","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"d638d25771f3738c01e86e9d7d70f211b7128967f07649b61b6e2b9b5df6abd3","InputMessages":[{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooE","Signers":[2],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRM","Signers":[3],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"kFGy1aK4Kychuv0xFbXRVjFgDU6hCAaltJ+Nbjsie+2yEmuFSwss4IiyekCVHu+1F9TN31CUv+uZ4FwzPQSkR5oM709TN/61iEcY4j44Zm/OXL8pnEhSipyXSSngprJ6","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"lNchT8MaT2rRLlibZ2qrI3CbdSadz3CPYcG52FiumRzmj5EMSi6lzg32J6daFKNTAkfl+YRS1Yaw9+jGlIeXmsawPLIw1S4meXOjUSA7CS60zYAUqFaLNn7UZA9be99D","Signers":[2,3],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"sbN4I4KmsP1QwuIS5hztulBMjvjyZsJIxc7VnPfzdBMPLq+BImXyrU2wPwzSyjPJDXsE9EQFb2D7NPnCA/gIutwGbRK/kwdRYUKH6VL5L5qPmw7Be2nDhly1J9iELyIq","Signers":[3],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"OutputMessages":[{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"kFGy1aK4Kychuv0xFbXRVjFgDU6hCAaltJ+Nbjsie+2yEmuFSwss4IiyekCVHu+1F9TN31CUv+uZ4FwzPQSkR5oM709TN/61iEcY4j44Zm/OXL8pnEhSipyXSSngprJ6","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"ExpectedError":"invalid signed message: msg allows 1 signer","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing no prepare quorum (prepared)":{"Name":"no prepare quorum (prepared)","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"5b18ca0b470208d8d247543306850618f02bddcbaa7c37eb6d5b36eb3accb5fb","InputMessages":[{"Signature":"gi2kJ9w7ZxNSnQnxXlr8j1H85RWARKH2a7eIfdRXVpp1wc3c2m00SgRxeyq+uUGeDhpLo79xmS/L14PGYNY7fuI5LZiT5Kiql9KWlMEjvPxgCrFJnyNyPhrSvOZCdD7S","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":["ifCCssPYsrqGH6g6YR2Xyg/kXfqfLKQ+Tc20DJj/pJW1Nu5W6a00mW9uWaE2qSgkDT68p601DAAe3svR3LDekMQBQzLh8oTgmGo40gvdptSZqss1A5KNAUiJLMbSgb4mbAAAAHQAAABUAgAAAQAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQBAAAAAAAAAFAAAADgAQAAAQIDBAgAAADMAAAAgSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBISeE9yfhw+TIMOswveerdYS7jR+d+5NJyzX2SaBtlww65VrCdl7hOXJZFvUYcuCZwxp+ysaHBvCDJPbsU6aj5XQnYDs9fKInTWjyl/RoQYKrfaW+qjnI96ce5d406qKBGwAAAB0AAAAxAAAAAIAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAATAAAAL6Vb7ffTvN1MWgtWIMgCE/JFMPw/tM1Jj5bRAYubCm0AAAAAAAAAABQAAAAUAAAAAECAwQ=","jJQ5uSqq41Kw3i1HCyOC/lfU/0JfGc8FyuPH0DdS+aSAf0lmgeohkd5kacM4UMNdD4tcySYmAIkhBnoQIQHuyk+f5vzlZ/uQqLdTrjhsd/opyUlHM6JJbOhCxbFyK5aXbAAAAHQAAABUAgAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQBAAAAAAAAAFAAAADgAQAAAQIDBAgAAADMAAAAgSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBISeE9yfhw+TIMOswveerdYS7jR+d+5NJyzX2SaBtlww65VrCdl7hOXJZFvUYcuCZwxp+ysaHBvCDJPbsU6aj5XQnYDs9fKInTWjyl/RoQYKrfaW+qjnI96ce5d406qKBGwAAAB0AAAAxAAAAAIAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAATAAAAL6Vb7ffTvN1MWgtWIMgCE/JFMPw/tM1Jj5bRAYubCm0AAAAAAAAAABQAAAAUAAAAAECAwQ=","oAgm78uy2guX7phvv1oVFc7NAEK88uSId2sE7j/yxWzgMj/1sVWMVke/GGjoS+5FBn00LrOjSXrRmup2yq5Z9aMOrL69BbbCeBikp2cRXbnwcP3gJCXKYakMpOjX3wGgbAAAAHQAAABUAgAAAwAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQBAAAAAAAAAFAAAADgAQAAAQIDBAgAAADMAAAAgSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBISeE9yfhw+TIMOswveerdYS7jR+d+5NJyzX2SaBtlww65VrCdl7hOXJZFvUYcuCZwxp+ysaHBvCDJPbsU6aj5XQnYDs9fKInTWjyl/RoQYKrfaW+qjnI96ce5d406qKBGwAAAB0AAAAxAAAAAIAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAATAAAAL6Vb7ffTvN1MWgtWIMgCE/JFMPw/tM1Jj5bRAYubCm0AAAAAAAAAABQAAAAUAAAAAECAwQ="],"PrepareJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="]},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[],"ExpectedError":"invalid signed message: proposal not justified: change round msg not valid: no justifications quorum","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing no previous accepted proposal":{"Name":"no previous accepted proposal","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"5b18ca0b470208d8d247543306850618f02bddcbaa7c37eb6d5b36eb3accb5fb","InputMessages":[{"Signature":"kFGy1aK4Kychuv0xFbXRVjFgDU6hCAaltJ+Nbjsie+2yEmuFSwss4IiyekCVHu+1F9TN31CUv+uZ4FwzPQSkR5oM709TN/61iEcY4j44Zm/OXL8pnEhSipyXSSngprJ6","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"OutputMessages":null,"ExpectedError":"invalid signed message: did not receive proposal for this round","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing no previous proposal for prepare":{"Name":"no previous proposal for prepare","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"5b18ca0b470208d8d247543306850618f02bddcbaa7c37eb6d5b36eb3accb5fb","InputMessages":[{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":null,"ExpectedError":"invalid signed message: did not receive proposal for this round","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing no rc quorum":{"Name":"no rc quorum","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"5b18ca0b470208d8d247543306850618f02bddcbaa7c37eb6d5b36eb3accb5fb","InputMessages":[{"Signature":"qXFBJqQab/CcXx1fjdaG6/2IE0t60uN6WsVKZtwQFTvYljgsI5zac8BPg6Hz4O52CscwQXn+smxeU+Vp6VFFoqBCa3HKLQnr1zM26wk4tboI/iwfNssW5vLbuaR7np0p","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":["t3wDXB0dnGx8wigQxQqFyeVgrXkVCQVQYd/gfUA+2qEwQWFGbUn2SES25c9LCXCfBp3r/ZFDj5fUFKT2TNy0+M8elwP42hQb6fdFCQh+L4RJIxSwNBlmxLj8Ftkx9roIbAAAAHQAAADEAAAAAQAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","mMq307rvpww3BxdPm0KCuat1CPoddAl9w/tnc524RivFpWby1PNqd9ymZl6/aFkOGOkeJoqoIZJgn22ff+c9UplIJTKxcIGm5d70ocze3Sx7TCq+A2xixwEsOb/A0831bAAAAHQAAADEAAAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[],"ExpectedError":"invalid signed message: proposal not justified: change round has no quorum","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing no rc quorum (prepared)":{"Name":"no rc quorum (prepared)","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"5b18ca0b470208d8d247543306850618f02bddcbaa7c37eb6d5b36eb3accb5fb","InputMessages":[{"Signature":"lZy9p1aQ78bMBDYLj5G1xRLGVoLWskI/0R3RobkJeZ9GKL4f+R4nUyIMYVa+F1YYFcuesVXOlm/4SXrphvUZ8LV7fGaDjOfTgMWuFtUE44CH7oCV/VaQEOxZLRtq5b3D","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":["t3wDXB0dnGx8wigQxQqFyeVgrXkVCQVQYd/gfUA+2qEwQWFGbUn2SES25c9LCXCfBp3r/ZFDj5fUFKT2TNy0+M8elwP42hQb6fdFCQh+L4RJIxSwNBlmxLj8Ftkx9roIbAAAAHQAAADEAAAAAQAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","mMq307rvpww3BxdPm0KCuat1CPoddAl9w/tnc524RivFpWby1PNqd9ymZl6/aFkOGOkeJoqoIZJgn22ff+c9UplIJTKxcIGm5d70ocze3Sx7TCq+A2xixwEsOb/A0831bAAAAHQAAADEAAAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="]},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[],"ExpectedError":"invalid signed message: proposal not justified: change round has no quorum","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing post decided":{"Name":"post decided","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"b778306f5782f6df3bc1aadd173f2e5dc41fcec00fee887a59c372bc94be91c2","InputMessages":[{"Signature":"kFGy1aK4Kychuv0xFbXRVjFgDU6hCAaltJ+Nbjsie+2yEmuFSwss4IiyekCVHu+1F9TN31CUv+uZ4FwzPQSkR5oM709TN/61iEcY4j44Zm/OXL8pnEhSipyXSSngprJ6","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"q+s2ftJ53lt+iNxpg6bfNxGrjGkY19VA5zRn92qJueuJgKCVyLQkrpNymxHC5bQADhtbiD8/LNzJ9bB0w14T3M8xooKaUCt6IX4qh5wqkfAicOB+KZ4+wGTfq/XQBGaj","Signers":[2],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"sbN4I4KmsP1QwuIS5hztulBMjvjyZsJIxc7VnPfzdBMPLq+BImXyrU2wPwzSyjPJDXsE9EQFb2D7NPnCA/gIutwGbRK/kwdRYUKH6VL5L5qPmw7Be2nDhly1J9iELyIq","Signers":[3],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"otJRiw6BkIF2XRWOwVx+pfKbZlP6RyaRFX0klsywbIYGiMOfwBW5hVPWylklu2nGAWOaUKClM1RH3csiez6KGCex2qe57+32EiNFcHeJzoVqLXjLL74YyE7fCXuXuPGU","Signers":[4],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"OutputMessages":[],"ExpectedError":"","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing prepare future round":{"Name":"prepare future round","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"b61f5233721865ca43afc68f4ad5045eeb123f6e8f095ce76ecf956dabc74713","InputMessages":[{"Signature":"swCHbgegE0zGay7Gay3q8OydHaqMI6K67Ap9+b46U7NYAnQ4RcVd2joTF1UFl2lbD5bfGqIZ4yO85sWD+cAOM0y3vM+UjhuqlDO+XVbCBbaDe2ZyxHiN8IWmO6viliek","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":3,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":null,"ExpectedError":"invalid signed message: wrong msg round","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing prepare happy flow":{"Name":"prepare happy flow","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"976cd5cecd58bba892a38ec0ef02b3aed4656fb89fef473d8af78fedf095439d","InputMessages":[{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooE","Signers":[2],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRM","Signers":[3],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"kFGy1aK4Kychuv0xFbXRVjFgDU6hCAaltJ+Nbjsie+2yEmuFSwss4IiyekCVHu+1F9TN31CUv+uZ4FwzPQSkR5oM709TN/61iEcY4j44Zm/OXL8pnEhSipyXSSngprJ6","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"ExpectedError":"","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing prepare multi signer":{"Name":"prepare multi signer","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"b61f5233721865ca43afc68f4ad5045eeb123f6e8f095ce76ecf956dabc74713","InputMessages":[{"Signature":"lSq9YfMk+05uoePxyrEDjx/k9Sw4dwPXSEbUT7Gq16n9fwutds9Fl44UwTpimGsUEV1cKFglQWEwgenesis2LLDNKPbpLnGo6wdylG16+aW+7f1QWMr+onnrWDKOzNzHyqlEh","Signers":[1,2],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":null,"ExpectedError":"invalid signed message: msg allows 1 signer","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing prepare post decided":{"Name":"prepare post decided","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"f7e6076054dc1ef0518533722d30994f44c638d44d9f0aab230c6335f58600b2","InputMessages":[{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooE","Signers":[2],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRM","Signers":[3],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"kFGy1aK4Kychuv0xFbXRVjFgDU6hCAaltJ+Nbjsie+2yEmuFSwss4IiyekCVHu+1F9TN31CUv+uZ4FwzPQSkR5oM709TN/61iEcY4j44Zm/OXL8pnEhSipyXSSngprJ6","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"q+s2ftJ53lt+iNxpg6bfNxGrjGkY19VA5zRn92qJueuJgKCVyLQkrpNymxHC5bQADhtbiD8/LNzJ9bB0w14T3M8xooKaUCt6IX4qh5wqkfAicOB+KZ4+wGTfq/XQBGaj","Signers":[2],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"sbN4I4KmsP1QwuIS5hztulBMjvjyZsJIxc7VnPfzdBMPLq+BImXyrU2wPwzSyjPJDXsE9EQFb2D7NPnCA/gIutwGbRK/kwdRYUKH6VL5L5qPmw7Be2nDhly1J9iELyIq","Signers":[3],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"oBCGUs0KByzDk/HXikTym24T9pSNbwTwNUB6D13bcfec+gBWx5PHkGtJ3tqvDwD6DWINpQ+L4WazwDXLLLIV9QipQzlKZfFirNC/abOufNz5NIjqxYNwQoS5OWsycScV","Signers":[4],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"kFGy1aK4Kychuv0xFbXRVjFgDU6hCAaltJ+Nbjsie+2yEmuFSwss4IiyekCVHu+1F9TN31CUv+uZ4FwzPQSkR5oM709TN/61iEcY4j44Zm/OXL8pnEhSipyXSSngprJ6","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"ExpectedError":"","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing prepare prev round":{"Name":"prepare prev round","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":10,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":{"Signature":"p92pmykhc+Z+f4QD8UQziZaL6L/AHLNpWgLRObO7A5ulwfYKO7rALgenesisyU8nnbN10AkX0ZLICDF2S7v4lO5fxK5hS0iojON3ITAD2YxrhO7mEgsInSJUrqOMiosdDu2EF","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":10,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":["p5iorFgZ9bI9otv5TyEMkbxcQ5E8+LACHWsh3Fv54KJgoJbCxQ6v47tpF2De+WW3DvCjDTJvi/z4VSQ0H2ZYWEDOo5sfc49F7VAialmFiJ5glQh9D1hTMT2pIfLj30btbAAAAHQAAADEAAAAAQAAAAAAAAADAAAAAAAAAAAAAAAAAAAACgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","l2L7FVW+G1CyLcDuAOHspS+CjtTYIrZXoDlIZD3sB+M3QaLxVZn5EL+Z9GDws//oCVDA2xMGfrpix9MtFLShHU7SD98JO3emM+VHEil3E7BJvb5uuyfmYdQQQPwKIMm1bAAAAHQAAADEAAAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAACgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","g8g41B98ET4mdY91qVGKYCsQSTis5t/D01TIOGsCK5DgkH9Wl7xBiCQiRipTJvEnFJovyojhdZfVg0z9+obYvm7ki1FaCd6/MEMkL4XMxnzGu/DTWCyScyDvGQypRvkubAAAAHQAAADEAAAAAwAAAAAAAAADAAAAAAAAAAAAAAAAAAAACgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"3b9383a2b8f9dc723606044e95227cfc1914451ed35a80657c22ecab949d8324","InputMessages":[{"Signature":"r2UXagIhb/qkSbKPHM9WL/UJZdQWWyT0ZC+ykEJnCHiRI34XdH3Ojw8z5YqFb9VQC/rXPpgYcjkGAwCPk1iW9Mwcnylj3lp9PX8fi02bXRziMEXbD31rhOqYfH0XpCyI","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":9,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":null,"ExpectedError":"invalid signed message: past round","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing prepare unknown signer":{"Name":"prepare unknown signer","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"b61f5233721865ca43afc68f4ad5045eeb123f6e8f095ce76ecf956dabc74713","InputMessages":[{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[5],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":null,"ExpectedError":"invalid signed message: msg signature invalid: unknown signer","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing prepare wrong data":{"Name":"prepare wrong data","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"b61f5233721865ca43afc68f4ad5045eeb123f6e8f095ce76ecf956dabc74713","InputMessages":[{"Signature":"h+8M1EsUL5CICLI5vhjQYxP0vzqAqiz0IaYn5uAXop3k3phe2OUgrVUhXgLeR2qLB8f8daD93H4RbM7rkhg3t5+QS/04L3WuLiQap1HteFQWHXbuBvpnl5tTcDmWmlJq","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[171,223,56,59,122,252,254,162,11,93,114,204,119,150,8,230,250,112,249,19,252,223,246,20,151,76,6,90,175,133,17,249],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":null,"ExpectedError":"invalid signed message: proposed data mistmatch","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing prepare wrong height":{"Name":"prepare wrong height","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"b61f5233721865ca43afc68f4ad5045eeb123f6e8f095ce76ecf956dabc74713","InputMessages":[{"Signature":"iKWgpKtY9lB1+ipfY+/h/uixAx5TFmUYdfwwFZS9Sejy1fBKBvDvzpvD8xFg1UdIFNVtEOqJypfDko8sgF7roVcV8kFlPl4/U81oz+jqcDUgALj9X1peMm3jUL3oNmle","Signers":[1],"Message":{"MsgType":1,"Height":2,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":null,"ExpectedError":"invalid signed message: wrong msg height","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing prepare wrong sig":{"Name":"prepare wrong sig","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"b61f5233721865ca43afc68f4ad5045eeb123f6e8f095ce76ecf956dabc74713","InputMessages":[{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[2],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":null,"ExpectedError":"invalid signed message: msg signature invalid: failed to verify signature","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing previously prepared proposal":{"Name":"previously prepared proposal","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"c750be146fad8375d8107451eabf8458d3216afa550d4380fc476cce8077a90e","InputMessages":[{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooE","Signers":[2],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRM","Signers":[3],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"oEQ/+4Yv52mq7qFgQZ0tWaih5VMx6ivO2Bm3rBHtd8MiWIpmrLH0r1kA+YkwbmeLEWn0xyry0wJjdAMl0p+g5A5nbpUpkng+R2PmLsolc1hr3nJMnXebzq0AKfqYLEkN","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":1,"RoundChangeJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"g+23kWE7m/1jehx91y2yqtG5t0sP4ED+2sz2MipAQBG+P3nX7abKV+fAZS/hjhJvAZi70KYcBWNBGMcMiddJRm/5QbJoCUP2qPMQd/yzA9JvknfmdapAOPyXRAMeYyCe","Signers":[2],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":1,"RoundChangeJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"q6Ok2cz9kmwxKRj2bXJ6qUmth4N7o8zqaLHIUGb1i1wASpjuryw/D+3W7qrugenesisgwFH3S8GGI8LVdUA6JGEOSgQ1DHp+e3QbLGMuuiLGDdUKJ8doYLvLIVBR1kmf4u766","Signers":[3],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":1,"RoundChangeJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"qODeZIEirgU6ocvZxxk3vTPFp+08hZxB47ZhTtu5coLg9L5MtC794WC94itl+ZbPE8mmbO8sNITtame2WiEEF+qRQztTbrfUXB0bSM1aWJAZoH9YgI0MVht0lTPMYBr9","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":["oEQ/+4Yv52mq7qFgQZ0tWaih5VMx6ivO2Bm3rBHtd8MiWIpmrLH0r1kA+YkwbmeLEWn0xyry0wJjdAMl0p+g5A5nbpUpkng+R2PmLsolc1hr3nJMnXebzq0AKfqYLEkNbAAAAHQAAAAcAwAAAQAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQBAAAAAAAAAFAAAACoAgAAAQIDBAwAAADQAAAAlAEAAIEp5oYqUSC9CF4ZNrTvtaVfx9GcDQ/aDp7FdtGKvUoXqzoDP1KWt0xf2vhct7PaMgG2P+yna4g2E+OxyhN+djo0Ljsd3bzgFvjKPLzjLIsSXdjCWnY5gZwgtTnp58bFeWwAAAB0AAAAxAAAAAEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAATAAAAL6Vb7ffTvN1MWgtWIMgCE/JFMPw/tM1Jj5bRAYubCm0AAAAAAAAAABQAAAAUAAAAAECAwSEnhPcn4cPkyDDrML3nq3WEu40fnfuTScs19kmgbZcMOuVawnZe4TlyWRb1GHLgmcMafsrGhwbwgyT27FOmo+V0J2A7PXyiJ01o8pf0aEGCq32lvqo5yPenHuXeNOqigRsAAAAdAAAAMQAAAACAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAEwAAAC+lW+3307zdTFoLViDIAhPyRTD8P7TNSY+W0QGLmwptAAAAAAAAAAAUAAAAFAAAAABAgMEr0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","g+23kWE7m/1jehx91y2yqtG5t0sP4ED+2sz2MipAQBG+P3nX7abKV+fAZS/hjhJvAZi70KYcBWNBGMcMiddJRm/5QbJoCUP2qPMQd/yzA9JvknfmdapAOPyXRAMeYyCebAAAAHQAAAAcAwAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQBAAAAAAAAAFAAAACoAgAAAQIDBAwAAADQAAAAlAEAAIEp5oYqUSC9CF4ZNrTvtaVfx9GcDQ/aDp7FdtGKvUoXqzoDP1KWt0xf2vhct7PaMgG2P+yna4g2E+OxyhN+djo0Ljsd3bzgFvjKPLzjLIsSXdjCWnY5gZwgtTnp58bFeWwAAAB0AAAAxAAAAAEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAATAAAAL6Vb7ffTvN1MWgtWIMgCE/JFMPw/tM1Jj5bRAYubCm0AAAAAAAAAABQAAAAUAAAAAECAwSEnhPcn4cPkyDDrML3nq3WEu40fnfuTScs19kmgbZcMOuVawnZe4TlyWRb1GHLgmcMafsrGhwbwgyT27FOmo+V0J2A7PXyiJ01o8pf0aEGCq32lvqo5yPenHuXeNOqigRsAAAAdAAAAMQAAAACAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAEwAAAC+lW+3307zdTFoLViDIAhPyRTD8P7TNSY+W0QGLmwptAAAAAAAAAAAUAAAAFAAAAABAgMEr0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","q6Ok2cz9kmwxKRj2bXJ6qUmth4N7o8zqaLHIUGb1i1wASpjuryw/D+3W7qrugenesisgwFH3S8GGI8LVdUA6JGEOSgQ1DHp+e3QbLGMuuiLGDdUKJ8doYLvLIVBR1kmf4u766bAAAAHQAAAAcAwAAAwAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQBAAAAAAAAAFAAAACoAgAAAQIDBAwAAADQAAAAlAEAAIEp5oYqUSC9CF4ZNrTvtaVfx9GcDQ/aDp7FdtGKvUoXqzoDP1KWt0xf2vhct7PaMgG2P+yna4g2E+OxyhN+djo0Ljsd3bzgFvjKPLzjLIsSXdjCWnY5gZwgtTnp58bFeWwAAAB0AAAAxAAAAAEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAATAAAAL6Vb7ffTvN1MWgtWIMgCE/JFMPw/tM1Jj5bRAYubCm0AAAAAAAAAABQAAAAUAAAAAECAwSEnhPcn4cPkyDDrML3nq3WEu40fnfuTScs19kmgbZcMOuVawnZe4TlyWRb1GHLgmcMafsrGhwbwgyT27FOmo+V0J2A7PXyiJ01o8pf0aEGCq32lvqo5yPenHuXeNOqigRsAAAAdAAAAMQAAAACAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAEwAAAC+lW+3307zdTFoLViDIAhPyRTD8P7TNSY+W0QGLmwptAAAAAAAAAAAUAAAAFAAAAABAgMEr0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="]},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[{"Signature":"oQFlYs6zdlfdSUqDf/W1v4wYJZDtbUdm6rMyAw9zwWOi4cGN+cbRDYe5oxLblvrvF0lTMbkWipOgenesisEyMz8yjyf0oxjuqTGZJib4tzXXWpAdkzaT2s44YWzfbs5Y2pRCQ","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"kFGy1aK4Kychuv0xFbXRVjFgDU6hCAaltJ+Nbjsie+2yEmuFSwss4IiyekCVHu+1F9TN31CUv+uZ4FwzPQSkR5oM709TN/61iEcY4j44Zm/OXL8pnEhSipyXSSngprJ6","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"oEQ/+4Yv52mq7qFgQZ0tWaih5VMx6ivO2Bm3rBHtd8MiWIpmrLH0r1kA+YkwbmeLEWn0xyry0wJjdAMl0p+g5A5nbpUpkng+R2PmLsolc1hr3nJMnXebzq0AKfqYLEkN","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":1,"RoundChangeJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"qODeZIEirgU6ocvZxxk3vTPFp+08hZxB47ZhTtu5coLg9L5MtC794WC94itl+ZbPE8mmbO8sNITtame2WiEEF+qRQztTbrfUXB0bSM1aWJAZoH9YgI0MVht0lTPMYBr9","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":["oEQ/+4Yv52mq7qFgQZ0tWaih5VMx6ivO2Bm3rBHtd8MiWIpmrLH0r1kA+YkwbmeLEWn0xyry0wJjdAMl0p+g5A5nbpUpkng+R2PmLsolc1hr3nJMnXebzq0AKfqYLEkNbAAAAHQAAAAcAwAAAQAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQBAAAAAAAAAFAAAACoAgAAAQIDBAwAAADQAAAAlAEAAIEp5oYqUSC9CF4ZNrTvtaVfx9GcDQ/aDp7FdtGKvUoXqzoDP1KWt0xf2vhct7PaMgG2P+yna4g2E+OxyhN+djo0Ljsd3bzgFvjKPLzjLIsSXdjCWnY5gZwgtTnp58bFeWwAAAB0AAAAxAAAAAEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAATAAAAL6Vb7ffTvN1MWgtWIMgCE/JFMPw/tM1Jj5bRAYubCm0AAAAAAAAAABQAAAAUAAAAAECAwSEnhPcn4cPkyDDrML3nq3WEu40fnfuTScs19kmgbZcMOuVawnZe4TlyWRb1GHLgmcMafsrGhwbwgyT27FOmo+V0J2A7PXyiJ01o8pf0aEGCq32lvqo5yPenHuXeNOqigRsAAAAdAAAAMQAAAACAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAEwAAAC+lW+3307zdTFoLViDIAhPyRTD8P7TNSY+W0QGLmwptAAAAAAAAAAAUAAAAFAAAAABAgMEr0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","g+23kWE7m/1jehx91y2yqtG5t0sP4ED+2sz2MipAQBG+P3nX7abKV+fAZS/hjhJvAZi70KYcBWNBGMcMiddJRm/5QbJoCUP2qPMQd/yzA9JvknfmdapAOPyXRAMeYyCebAAAAHQAAAAcAwAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQBAAAAAAAAAFAAAACoAgAAAQIDBAwAAADQAAAAlAEAAIEp5oYqUSC9CF4ZNrTvtaVfx9GcDQ/aDp7FdtGKvUoXqzoDP1KWt0xf2vhct7PaMgG2P+yna4g2E+OxyhN+djo0Ljsd3bzgFvjKPLzjLIsSXdjCWnY5gZwgtTnp58bFeWwAAAB0AAAAxAAAAAEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAATAAAAL6Vb7ffTvN1MWgtWIMgCE/JFMPw/tM1Jj5bRAYubCm0AAAAAAAAAABQAAAAUAAAAAECAwSEnhPcn4cPkyDDrML3nq3WEu40fnfuTScs19kmgbZcMOuVawnZe4TlyWRb1GHLgmcMafsrGhwbwgyT27FOmo+V0J2A7PXyiJ01o8pf0aEGCq32lvqo5yPenHuXeNOqigRsAAAAdAAAAMQAAAACAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAEwAAAC+lW+3307zdTFoLViDIAhPyRTD8P7TNSY+W0QGLmwptAAAAAAAAAAAUAAAAFAAAAABAgMEr0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","q6Ok2cz9kmwxKRj2bXJ6qUmth4N7o8zqaLHIUGb1i1wASpjuryw/D+3W7qrugenesisgwFH3S8GGI8LVdUA6JGEOSgQ1DHp+e3QbLGMuuiLGDdUKJ8doYLvLIVBR1kmf4u766bAAAAHQAAAAcAwAAAwAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQBAAAAAAAAAFAAAACoAgAAAQIDBAwAAADQAAAAlAEAAIEp5oYqUSC9CF4ZNrTvtaVfx9GcDQ/aDp7FdtGKvUoXqzoDP1KWt0xf2vhct7PaMgG2P+yna4g2E+OxyhN+djo0Ljsd3bzgFvjKPLzjLIsSXdjCWnY5gZwgtTnp58bFeWwAAAB0AAAAxAAAAAEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAATAAAAL6Vb7ffTvN1MWgtWIMgCE/JFMPw/tM1Jj5bRAYubCm0AAAAAAAAAABQAAAAUAAAAAECAwSEnhPcn4cPkyDDrML3nq3WEu40fnfuTScs19kmgbZcMOuVawnZe4TlyWRb1GHLgmcMafsrGhwbwgyT27FOmo+V0J2A7PXyiJ01o8pf0aEGCq32lvqo5yPenHuXeNOqigRsAAAAdAAAAMQAAAACAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAEwAAAC+lW+3307zdTFoLViDIAhPyRTD8P7TNSY+W0QGLmwptAAAAAAAAAAAUAAAAFAAAAABAgMEr0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="]},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"mHxFVaQSuIJ8TJOcrLw4+iiJfcjA9xEvPMGwJF5xzoyg7xOUtTsE1k0pBqq+3ip+GULBsucWUvGejeWxN9LznuzFIhHwMEZa5GktwGIsuMGSOWJCDMyusI1ktg6iae6j","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ExpectedError":"","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing proposal duplicate message":{"Name":"proposal duplicate message","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"ce6d77d0602c7a368a6f86a32d70495b47e6d9fcfd2f5ad0d2952a3f5ac963e7","InputMessages":[{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ExpectedError":"invalid signed message: proposal is not valid with current state","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing proposal duplicate message different value":{"Name":"proposal duplicate message different value","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"ce6d77d0602c7a368a6f86a32d70495b47e6d9fcfd2f5ad0d2952a3f5ac963e7","InputMessages":[{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"owjCUEiXSjxCPA3UEdxzDt82X+c4mxidaunoH8TNF7fjvMTq8VCoZzQ4eHJzKP+MCNTqB7JshG2zXlL/7l5P0c6VyB27nLnBpuvHWYpiLFfk9uCby88hsj3iLIzL53ol","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[171,223,56,59,122,252,254,162,11,93,114,204,119,150,8,230,250,112,249,19,252,223,246,20,151,76,6,90,175,133,17,249],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJZGlmZmVyZW50"}],"OutputMessages":[{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ExpectedError":"invalid signed message: proposal is not valid with current state","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing proposal first round justification":{"Name":"proposal first round justification","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"ce6d77d0602c7a368a6f86a32d70495b47e6d9fcfd2f5ad0d2952a3f5ac963e7","InputMessages":[{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ExpectedError":"","ExpectedTimerState":{"Timeouts":0,"Round":0}},"*tests.MsgProcessingSpecTest_qbft message processing proposal future round prev not prepared":{"Name":"proposal future round prev not prepared","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"5901c834260f6533c0c480ee669d45a2d552dcf289c64d0a00f8ebfc38d4f604","InputMessages":[{"Signature":"p92pmykhc+Z+f4QD8UQziZaL6L/AHLNpWgLRObO7A5ulwfYKO7rALgenesisyU8nnbN10AkX0ZLICDF2S7v4lO5fxK5hS0iojON3ITAD2YxrhO7mEgsInSJUrqOMiosdDu2EF","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":10,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":["p5iorFgZ9bI9otv5TyEMkbxcQ5E8+LACHWsh3Fv54KJgoJbCxQ6v47tpF2De+WW3DvCjDTJvi/z4VSQ0H2ZYWEDOo5sfc49F7VAialmFiJ5glQh9D1hTMT2pIfLj30btbAAAAHQAAADEAAAAAQAAAAAAAAADAAAAAAAAAAAAAAAAAAAACgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","l2L7FVW+G1CyLcDuAOHspS+CjtTYIrZXoDlIZD3sB+M3QaLxVZn5EL+Z9GDws//oCVDA2xMGfrpix9MtFLShHU7SD98JO3emM+VHEil3E7BJvb5uuyfmYdQQQPwKIMm1bAAAAHQAAADEAAAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAACgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","g8g41B98ET4mdY91qVGKYCsQSTis5t/D01TIOGsCK5DgkH9Wl7xBiCQiRipTJvEnFJovyojhdZfVg0z9+obYvm7ki1FaCd6/MEMkL4XMxnzGu/DTWCyScyDvGQypRvkubAAAAHQAAADEAAAAAwAAAAAAAAADAAAAAAAAAAAAAAAAAAAACgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[{"Signature":"mHCRhO0iFQfy+eQK5r0uMBIY9ef3YFHxohp8mOuRUWyohfw1udKmyHIbw/PCP/vwFaDx+cjMnmeBrR7IN1S5SSB/7ZhYOkeUrqHgljPslvB41uEFG+6DY8k1GoPc7RCe","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":10,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ExpectedError":"","ExpectedTimerState":{"Timeouts":1,"Round":10}},"*tests.MsgProcessingSpecTest_qbft message processing proposal future round prev prepared":{"Name":"proposal future round prev prepared","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"1d6b7cd0d77efabb5755072f2b502baa33759c2d5ce8df098294bfeec231a560","InputMessages":[{"Signature":"r3hPQiDDmmPuWFBQTq5Cg/ZztWNraGWRYkjcSFYbz1iWsgk+NqQ0K6YII8Rkf2nKD+8GQ2Y/hPCHFPcjFvtc9nlq8651TLU8lktoYMbOvKMkKUdlMdaoN5eYKg+Wznqy","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":10,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":["p5iorFgZ9bI9otv5TyEMkbxcQ5E8+LACHWsh3Fv54KJgoJbCxQ6v47tpF2De+WW3DvCjDTJvi/z4VSQ0H2ZYWEDOo5sfc49F7VAialmFiJ5glQh9D1hTMT2pIfLj30btbAAAAHQAAADEAAAAAQAAAAAAAAADAAAAAAAAAAAAAAAAAAAACgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","l2L7FVW+G1CyLcDuAOHspS+CjtTYIrZXoDlIZD3sB+M3QaLxVZn5EL+Z9GDws//oCVDA2xMGfrpix9MtFLShHU7SD98JO3emM+VHEil3E7BJvb5uuyfmYdQQQPwKIMm1bAAAAHQAAADEAAAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAACgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","g8g41B98ET4mdY91qVGKYCsQSTis5t/D01TIOGsCK5DgkH9Wl7xBiCQiRipTJvEnFJovyojhdZfVg0z9+obYvm7ki1FaCd6/MEMkL4XMxnzGu/DTWCyScyDvGQypRvkubAAAAHQAAADEAAAAAwAAAAAAAAADAAAAAAAAAAAAAAAAAAAACgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":["jzbW5gXm9N0pqt4922ZkHE7VGIUQ7aHjWAumwABIfM7H6IK+kC+OKjQBJbTwrC24A+uB9QA9PyDjxp2TwB9KahAqAUKXkUBC8vrh5/LPnpIwYoMNXGk0TzJzFChodX7zbAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAACAAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","uC++Wfl/hbyiwm/sYMyFMnx2VkpPudcGkzT8txCige9bjgW1tExEJLqzIbQVZoNJAo+yFgbPeOWjHGYSm6CqQnY1rPOSRKIEdcK+QM4hNeHe4pIClDJuEMymzX4JLUMgbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAACAAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","uNlHnO64QhVO7wKmmwh5LvyJdYAXmjlCeKfNTmCKRUVpCwHtVwMpLtsRlnk5vAroB15v9vJwkwZTomeGsRdWBP02wQz7+vlQtC+CDNS6it5PigenczC8uN8FuZX464cmbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAACAAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="]},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[{"Signature":"mHCRhO0iFQfy+eQK5r0uMBIY9ef3YFHxohp8mOuRUWyohfw1udKmyHIbw/PCP/vwFaDx+cjMnmeBrR7IN1S5SSB/7ZhYOkeUrqHgljPslvB41uEFG+6DY8k1GoPc7RCe","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":10,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ExpectedError":"","ExpectedTimerState":{"Timeouts":1,"Round":10}},"*tests.MsgProcessingSpecTest_qbft message processing proposal justification (not prepared)":{"Name":"proposal justification (not prepared)","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":5,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"c36727f091866a125ef815fe77a50df5e38335a1bb44eca2dbe0d2a4c34de4d8","InputMessages":[{"Signature":"hRyVCNR8uvSvhzAyQN7zOOc4HQSsi64uPUL3kYrv3hXEvdRHmH5p6x6qe8tJoRI+BxGNdX+gXLRuxW8O0FV18IJZ50wkj0IeO56sllG47V6voSrnkiUaVhNBeL8JAKO1","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":5,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":["kW6iUpA50GpFa+2t6ePDCat26YdRRXfmNIPLmBXU42mlTfVo/nDtIGEQ0fmzz0XnAF+EdvbEjMMUN+k5YHIOHtEYIMeLZB44oCDSTOY7ECawp907ZQrHB5iOHnXGbd14bAAAAHQAAADEAAAAAQAAAAAAAAADAAAAAAAAAAAAAAAAAAAABQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","shil9Fx1Hzw+uDNtKQbohi14+Gz1Elz76l1XRZXHh7NH1vKk/0n54T3EdhuKV1SJEt2YzCWViMOcGIAGtGyqk7+4HU1TcwvWmlhpbLAttCUPgXk2aJzAzZbaPsZn6UK8bAAAAHQAAADEAAAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAABQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","kaQq8woHMXCJYNwszLvuFvZGpP1IqFyNGBdPz9a1Vf2KdEl2ecJjJ1t9jv+4+airEvRm8HvMVX6Pz8vw1LTkhQZyifwyrR/mH9ng0Gudv5QW/Ty8MCMrlN6eVOooLRn8bAAAAHQAAADEAAAAAwAAAAAAAAADAAAAAAAAAAAAAAAAAAAABQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[{"Signature":"kubofPw7I9feDsJrpjtDKkJBvBcZA5rZw6zii7ZNiTEKWr5cNqNSZChqGmu2IoQkEwwkpYTvJWQiP6PFivvkgLmSs33RuKpTdaLVW6LE99vYfvUGqw7e064EW/Dr8Pqe","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":5,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ExpectedError":"","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing proposal justification not highest":{"Name":"proposal justification not highest","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":3,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"beaef03728ef5dadfd5daf11046923930e787f0d77b824326bd7ec65c2338b45","InputMessages":[{"Signature":"mDGyWhXNykE1T/1U4Rhdsq2LS08cY8CE3e/Xer4XCpxFEYvRXtxsGazGoszfBMTBATX7SgxeyUcZId2G+pkb17ISk92L5qvD6Sg0hw0/mmO0o6pmTfqtxsDyAZI1pmPe","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":3,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":["oqogtSDFcKNGwA6LazB126oPQM/gL+Sq7d3P6Sf50v4DLwe27XVfTWh6kQJ3oIMyEVDnrq17ockOMGFABvAVgP5c4xufhWI2LYDs/78Qq+nnED51ID6vAde9NCvbPt9SbAAAAHQAAAAcAwAAAQAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAwAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQBAAAAAAAAAFAAAACoAgAAAQIDBAwAAADQAAAAlAEAAIEp5oYqUSC9CF4ZNrTvtaVfx9GcDQ/aDp7FdtGKvUoXqzoDP1KWt0xf2vhct7PaMgG2P+yna4g2E+OxyhN+djo0Ljsd3bzgFvjKPLzjLIsSXdjCWnY5gZwgtTnp58bFeWwAAAB0AAAAxAAAAAEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAATAAAAL6Vb7ffTvN1MWgtWIMgCE/JFMPw/tM1Jj5bRAYubCm0AAAAAAAAAABQAAAAUAAAAAECAwSEnhPcn4cPkyDDrML3nq3WEu40fnfuTScs19kmgbZcMOuVawnZe4TlyWRb1GHLgmcMafsrGhwbwgyT27FOmo+V0J2A7PXyiJ01o8pf0aEGCq32lvqo5yPenHuXeNOqigRsAAAAdAAAAMQAAAACAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAEwAAAC+lW+3307zdTFoLViDIAhPyRTD8P7TNSY+W0QGLmwptAAAAAAAAAAAUAAAAFAAAAABAgMEr0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","rMABLDOIdpu/rzp8kHcAbR2YXojlNEpQuL/Pk1yNRijjrsAaMRp9BSSDUYjVPCsED1AbgvT60kOKIE0ADWnXny4tghNa7Z+jM7/9n4QWxQ8pui89H0BlYeraGEkiEkOSbAAAAHQAAAAcAwAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAwAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQCAAAAAAAAAFAAAACoAgAAAQIDBAwAAADQAAAAlAEAAIAJ7tIfW/hoTAVkQ+bhsF0C2VPiJEe0+R2p9vJyTRh5lpFjjpiZaKNhLDZIaOvrRA3CnhCknAagzLdDLPhhO2lH05Hj/CvFAzpv5SJb+khMxQZU4EmiU0Zrsmzl9cfhKWwAAAB0AAAAxAAAAAEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAIAAAAAAAAATAAAAL6Vb7ffTvN1MWgtWIMgCE/JFMPw/tM1Jj5bRAYubCm0AAAAAAAAAABQAAAAUAAAAAECAwSD+YoeqZbjt6S9cRjrY3l8pND2gcFTlApJnGN6HIf0xnxfu3h6CfH/UXdJTf12gYMD54mUDb+vSvDZDN2M9tfIFjXpGuSgCqo6Ai1I3OwIS8mXekSwT0xWnxHLyve+z1NsAAAAdAAAAMQAAAACAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAACAAAAAAAAAEwAAAC+lW+3307zdTFoLViDIAhPyRTD8P7TNSY+W0QGLmwptAAAAAAAAAAAUAAAAFAAAAABAgMEj7thZ1r0Gs6AEJKBXe2IpYOkIQrN1ggSWQjb6Nz7KLazl+4Iikq9xEPjgs/oiNbYFB5BWknHZR1t3QMWWu/45saLm+WS289AZ503i9GVNM1Rcs+O845PvoFYjWstIUOBbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","k43FZ6ZB8Ke7oSc87rz3PRMg5a+LNU25H557YRknOBbnFRU7/fZUuVXhAyCWlFhKFlK8ZMCtL3Pm63iB7l/IXXruK4GbI5+ovDXITmKnURsD5jEcVNhFm9lTrtU6geGLbAAAAHQAAADEAAAAAwAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAwAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="]},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[],"ExpectedError":"invalid signed message: proposal not justified: signed prepare not valid","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing proposal multi signer":{"Name":"proposal multi signer","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"5b18ca0b470208d8d247543306850618f02bddcbaa7c37eb6d5b36eb3accb5fb","InputMessages":[{"Signature":"i2KA7xhK26lcbSQZIWhPm73i7z3ATWoWKBbXBB3yRzRjp7yiV72ujB3baC51ExipA+G1boTgenesislOMN3R376DJ7hHb302k5GRpcCG8XaeQaXoYqxUaIVU//cdfXrbxODT8","Signers":[1,2],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[],"ExpectedError":"invalid signed message: msg allows 1 signer","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing proposal past round (not prev prepared)":{"Name":"proposal past round (not prev prepared)","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":10,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"9be139118d79649f5843fa7bb161a665e928c7e85eb4b08b624b2a40dc8ef9a1","InputMessages":[{"Signature":"uNqCmfU1N4q3rOWnxyelS5IX3wEB5K/m0yJtFMdxJCQ8FqnhgIY2mVYHthsCa5RoFxNXm5f7iyO/gDsxojhDL7cJMvcl/ooxzBSqwOSvqITLqpANwwAq5yc76NbcbQzX","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":8,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":["jO4umGws/+d7qKqV+UNB2nlRhGuQWn/HGK6AglIq+rYUWPPYl52iNNriuF7ulcZFCRfpi9ShelsJX5z0z9A2VpefRsIoy9GVaYHfp7YanF5kc7OdQ2xQEKsu0L1wEb08bAAAAHQAAADEAAAAAQAAAAAAAAADAAAAAAAAAAAAAAAAAAAACAAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","skcW7hsV93EXLUY1ziHoh7VaBLHXSq5gS19uxOOgwM2X3zuLa1ZF3heRIU6zlM++DuXNSgDWJCIfIYXpTFOZN74ItTpx1Ubq+8Ot35ivny0MJliMkQuXhd8b9RciNqDrbAAAAHQAAADEAAAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAACAAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hqDeHvqZQxFEE9jAQHaL0AMzFclbfjEPM9RGbIKMyAp9eG5yctuaYd1XRncbImHhEKvUoHQMS95XMEZspSxINaWM5QLxU7c8qyOSksguF8/Dfrp0hXd3bSxA+epp/OOpbAAAAHQAAADEAAAAAwAAAAAAAAADAAAAAAAAAAAAAAAAAAAACAAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":["gaXXz5MgZf8Hi1FPtAtCGO5J4qFYxJGldNTR5SNggenesiscuIMQrx7b70TabooR2bKk4FK3NZ3wzFHKEHL4GQOJLhaAVgv9ti+8646jd3B08/bRST2uWawfE+hx7y2iKEMc2bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAABgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","kJQ8g9hzG36f1hKq2svFJKGoqe/NU+xHqFqhEO7zytAnxoDtDG32ftv422n7VwIQGKiLTwtH8jEDStgl4m1mboCm+ztTBTumeTTKStb17BEpv7uYIApl/UcZhagrZdDNbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAABgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","tyxD9rLnkLeRNic3xqkOP6OWo10lYQtu2K3RfRWlHf8rH1FbbBEpqSJq3RB1EvEgFYC7FADjrxzxTSA7jKiF5GNz+TSpeEAPh0Z8ajAH1ZFQNfg7JLIpAslGqgHe1pU/bAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAABgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="]},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[],"ExpectedError":"invalid signed message: past round","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing proposal post decided":{"Name":"proposal post decided","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"a298f278a78362257e233e1db8eeb44c2bb9b45a55bb3555928d6723231ebcd2","InputMessages":[{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooE","Signers":[2],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRM","Signers":[3],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"kFGy1aK4Kychuv0xFbXRVjFgDU6hCAaltJ+Nbjsie+2yEmuFSwss4IiyekCVHu+1F9TN31CUv+uZ4FwzPQSkR5oM709TN/61iEcY4j44Zm/OXL8pnEhSipyXSSngprJ6","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"q+s2ftJ53lt+iNxpg6bfNxGrjGkY19VA5zRn92qJueuJgKCVyLQkrpNymxHC5bQADhtbiD8/LNzJ9bB0w14T3M8xooKaUCt6IX4qh5wqkfAicOB+KZ4+wGTfq/XQBGaj","Signers":[2],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"sbN4I4KmsP1QwuIS5hztulBMjvjyZsJIxc7VnPfzdBMPLq+BImXyrU2wPwzSyjPJDXsE9EQFb2D7NPnCA/gIutwGbRK/kwdRYUKH6VL5L5qPmw7Be2nDhly1J9iELyIq","Signers":[3],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[{"Signature":"oQFlYs6zdlfdSUqDf/W1v4wYJZDtbUdm6rMyAw9zwWOi4cGN+cbRDYe5oxLblvrvF0lTMbkWipOgenesisEyMz8yjyf0oxjuqTGZJib4tzXXWpAdkzaT2s44YWzfbs5Y2pRCQ","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"pLt8DFEupjMOqSUJGZ3OJkJS3QU8zp7Bf3q1HASaOK6bGx4BQZycH5MkFVd6BE59FWdMbkMNnjnR8M8rLtspEZ9f7ppMqYPoBn+4RcY2vDcDOvA4Jnf09Ry/4MX6EVVR","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"ExpectedError":"invalid signed message: proposal is not valid with current state","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing proposal post prepare":{"Name":"proposal post prepare","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"976cd5cecd58bba892a38ec0ef02b3aed4656fb89fef473d8af78fedf095439d","InputMessages":[{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooE","Signers":[2],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRM","Signers":[3],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[{"Signature":"oQFlYs6zdlfdSUqDf/W1v4wYJZDtbUdm6rMyAw9zwWOi4cGN+cbRDYe5oxLblvrvF0lTMbkWipOgenesisEyMz8yjyf0oxjuqTGZJib4tzXXWpAdkzaT2s44YWzfbs5Y2pRCQ","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"pLt8DFEupjMOqSUJGZ3OJkJS3QU8zp7Bf3q1HASaOK6bGx4BQZycH5MkFVd6BE59FWdMbkMNnjnR8M8rLtspEZ9f7ppMqYPoBn+4RcY2vDcDOvA4Jnf09Ry/4MX6EVVR","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"ExpectedError":"invalid signed message: proposal is not valid with current state","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing proposal rc msg invalid":{"Name":"proposal rc msg invalid","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"5b18ca0b470208d8d247543306850618f02bddcbaa7c37eb6d5b36eb3accb5fb","InputMessages":[{"Signature":"goLOJVvKn+a8/Nn8s+00H/s7Uy5QVVPfdkbZC9Qgdrka9EJrHsCwS2MLLQRNNdl3F0Tge4XHqWfdlykt12JtrDWSkcSatWuc4dm76gr6o/eD9HnzGQfUsqfshBe1tHJk","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":["t3wDXB0dnGx8wigQxQqFyeVgrXkVCQVQYd/gfUA+2qEwQWFGbUn2SES25c9LCXCfBp3r/ZFDj5fUFKT2TNy0+M8elwP42hQb6fdFCQh+L4RJIxSwNBlmxLj8Ftkx9roIbAAAAHQAAADEAAAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","mMq307rvpww3BxdPm0KCuat1CPoddAl9w/tnc524RivFpWby1PNqd9ymZl6/aFkOGOkeJoqoIZJgn22ff+c9UplIJTKxcIGm5d70ocze3Sx7TCq+A2xixwEsOb/A0831bAAAAHQAAADEAAAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","lLT5TkZ3/7MmNTrG07fwT6CwDZ4una1ok5q6S7EHMBfoBJSNN5XOl3X1Hir/GbWLDs+i2Xrwlu+WjIqNYsaICol14dSQYu1LErxRH0dre4CUBLD7uPMIRUej/0GNt2S8bAAAAHQAAADEAAAAAwAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[],"ExpectedError":"invalid signed message: proposal not justified: change round msg not valid: msg signature invalid: failed to verify signature","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing proposal rc msg invalid (prepared)":{"Name":"proposal rc msg invalid (prepared)","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"5b18ca0b470208d8d247543306850618f02bddcbaa7c37eb6d5b36eb3accb5fb","InputMessages":[{"Signature":"gdTVbpLzged8JfgknuEc6pLtDHyOqj0q2ZK57SOry8Sm/yiaovGCUuHdaE6fQliLEbx+ALnL0MA3trOhEjj9X7MxULQR+vsU3ATeD/KFLSMKZNpNApxVIwEH84w8etUf","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":["oEQ/+4Yv52mq7qFgQZ0tWaih5VMx6ivO2Bm3rBHtd8MiWIpmrLH0r1kA+YkwbmeLEWn0xyry0wJjdAMl0p+g5A5nbpUpkng+R2PmLsolc1hr3nJMnXebzq0AKfqYLEkNbAAAAHQAAAAcAwAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQBAAAAAAAAAFAAAACoAgAAAQIDBAwAAADQAAAAlAEAAIEp5oYqUSC9CF4ZNrTvtaVfx9GcDQ/aDp7FdtGKvUoXqzoDP1KWt0xf2vhct7PaMgG2P+yna4g2E+OxyhN+djo0Ljsd3bzgFvjKPLzjLIsSXdjCWnY5gZwgtTnp58bFeWwAAAB0AAAAxAAAAAEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAATAAAAL6Vb7ffTvN1MWgtWIMgCE/JFMPw/tM1Jj5bRAYubCm0AAAAAAAAAABQAAAAUAAAAAECAwSEnhPcn4cPkyDDrML3nq3WEu40fnfuTScs19kmgbZcMOuVawnZe4TlyWRb1GHLgmcMafsrGhwbwgyT27FOmo+V0J2A7PXyiJ01o8pf0aEGCq32lvqo5yPenHuXeNOqigRsAAAAdAAAAMQAAAACAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAEwAAAC+lW+3307zdTFoLViDIAhPyRTD8P7TNSY+W0QGLmwptAAAAAAAAAAAUAAAAFAAAAABAgMEr0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","g+23kWE7m/1jehx91y2yqtG5t0sP4ED+2sz2MipAQBG+P3nX7abKV+fAZS/hjhJvAZi70KYcBWNBGMcMiddJRm/5QbJoCUP2qPMQd/yzA9JvknfmdapAOPyXRAMeYyCebAAAAHQAAAAcAwAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQBAAAAAAAAAFAAAACoAgAAAQIDBAwAAADQAAAAlAEAAIEp5oYqUSC9CF4ZNrTvtaVfx9GcDQ/aDp7FdtGKvUoXqzoDP1KWt0xf2vhct7PaMgG2P+yna4g2E+OxyhN+djo0Ljsd3bzgFvjKPLzjLIsSXdjCWnY5gZwgtTnp58bFeWwAAAB0AAAAxAAAAAEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAATAAAAL6Vb7ffTvN1MWgtWIMgCE/JFMPw/tM1Jj5bRAYubCm0AAAAAAAAAABQAAAAUAAAAAECAwSEnhPcn4cPkyDDrML3nq3WEu40fnfuTScs19kmgbZcMOuVawnZe4TlyWRb1GHLgmcMafsrGhwbwgyT27FOmo+V0J2A7PXyiJ01o8pf0aEGCq32lvqo5yPenHuXeNOqigRsAAAAdAAAAMQAAAACAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAEwAAAC+lW+3307zdTFoLViDIAhPyRTD8P7TNSY+W0QGLmwptAAAAAAAAAAAUAAAAFAAAAABAgMEr0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","q6Ok2cz9kmwxKRj2bXJ6qUmth4N7o8zqaLHIUGb1i1wASpjuryw/D+3W7qrugenesisgwFH3S8GGI8LVdUA6JGEOSgQ1DHp+e3QbLGMuuiLGDdUKJ8doYLvLIVBR1kmf4u766bAAAAHQAAAAcAwAAAwAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQBAAAAAAAAAFAAAACoAgAAAQIDBAwAAADQAAAAlAEAAIEp5oYqUSC9CF4ZNrTvtaVfx9GcDQ/aDp7FdtGKvUoXqzoDP1KWt0xf2vhct7PaMgG2P+yna4g2E+OxyhN+djo0Ljsd3bzgFvjKPLzjLIsSXdjCWnY5gZwgtTnp58bFeWwAAAB0AAAAxAAAAAEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAATAAAAL6Vb7ffTvN1MWgtWIMgCE/JFMPw/tM1Jj5bRAYubCm0AAAAAAAAAABQAAAAUAAAAAECAwSEnhPcn4cPkyDDrML3nq3WEu40fnfuTScs19kmgbZcMOuVawnZe4TlyWRb1GHLgmcMafsrGhwbwgyT27FOmo+V0J2A7PXyiJ01o8pf0aEGCq32lvqo5yPenHuXeNOqigRsAAAAdAAAAMQAAAACAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAEwAAAC+lW+3307zdTFoLViDIAhPyRTD8P7TNSY+W0QGLmwptAAAAAAAAAAAUAAAAFAAAAABAgMEr0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="]},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[],"ExpectedError":"invalid signed message: proposal not justified: change round msg not valid: msg signature invalid: failed to verify signature","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing round change after proposal accepted":{"Name":"round change after proposal accepted","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":2,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"72b1c8dee6a5445877ac15a6293da140c1db90f785142036486cda0d54259571","InputMessages":[{"Signature":"t3wDXB0dnGx8wigQxQqFyeVgrXkVCQVQYd/gfUA+2qEwQWFGbUn2SES25c9LCXCfBp3r/ZFDj5fUFKT2TNy0+M8elwP42hQb6fdFCQh+L4RJIxSwNBlmxLj8Ftkx9roI","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"mMq307rvpww3BxdPm0KCuat1CPoddAl9w/tnc524RivFpWby1PNqd9ymZl6/aFkOGOkeJoqoIZJgn22ff+c9UplIJTKxcIGm5d70ocze3Sx7TCq+A2xixwEsOb/A0831","Signers":[2],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"lLT5TkZ3/7MmNTrG07fwT6CwDZ4una1ok5q6S7EHMBfoBJSNN5XOl3X1Hir/GbWLDs+i2Xrwlu+WjIqNYsaICol14dSQYu1LErxRH0dre4CUBLD7uPMIRUej/0GNt2S8","Signers":[3],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"qV5LdutK2zk8Xbqu+xKdm1T33abJK26BV1I8cmor0GIZQdtFEJh4KP/6KMA1IZRSAH7cSAX9fvXO4/r4cVjuCRIFcdRL/cpnvfcAoTu0T8FBW1XqybwpwJcqglpkSvPk","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":["t3wDXB0dnGx8wigQxQqFyeVgrXkVCQVQYd/gfUA+2qEwQWFGbUn2SES25c9LCXCfBp3r/ZFDj5fUFKT2TNy0+M8elwP42hQb6fdFCQh+L4RJIxSwNBlmxLj8Ftkx9roIbAAAAHQAAADEAAAAAQAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","mMq307rvpww3BxdPm0KCuat1CPoddAl9w/tnc524RivFpWby1PNqd9ymZl6/aFkOGOkeJoqoIZJgn22ff+c9UplIJTKxcIGm5d70ocze3Sx7TCq+A2xixwEsOb/A0831bAAAAHQAAADEAAAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","lLT5TkZ3/7MmNTrG07fwT6CwDZ4una1ok5q6S7EHMBfoBJSNN5XOl3X1Hir/GbWLDs+i2Xrwlu+WjIqNYsaICol14dSQYu1LErxRH0dre4CUBLD7uPMIRUej/0GNt2S8bAAAAHQAAADEAAAAAwAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"oOgWjljrOn1qqbLNySUOZKIkshBr45oJtDsNdc5jPxfGaMHWZig8cAO9dp1jPBUpAxzS1b/i5+2jdUTT8LuJoONQmrC2cn9XBnxqJK7WajYRiXpj3VXK9IockYWpValu","Signers":[4],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[{"Signature":"qV5LdutK2zk8Xbqu+xKdm1T33abJK26BV1I8cmor0GIZQdtFEJh4KP/6KMA1IZRSAH7cSAX9fvXO4/r4cVjuCRIFcdRL/cpnvfcAoTu0T8FBW1XqybwpwJcqglpkSvPk","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":["t3wDXB0dnGx8wigQxQqFyeVgrXkVCQVQYd/gfUA+2qEwQWFGbUn2SES25c9LCXCfBp3r/ZFDj5fUFKT2TNy0+M8elwP42hQb6fdFCQh+L4RJIxSwNBlmxLj8Ftkx9roIbAAAAHQAAADEAAAAAQAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","mMq307rvpww3BxdPm0KCuat1CPoddAl9w/tnc524RivFpWby1PNqd9ymZl6/aFkOGOkeJoqoIZJgn22ff+c9UplIJTKxcIGm5d70ocze3Sx7TCq+A2xixwEsOb/A0831bAAAAHQAAADEAAAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","lLT5TkZ3/7MmNTrG07fwT6CwDZ4una1ok5q6S7EHMBfoBJSNN5XOl3X1Hir/GbWLDs+i2Xrwlu+WjIqNYsaICol14dSQYu1LErxRH0dre4CUBLD7uPMIRUej/0GNt2S8bAAAAHQAAADEAAAAAwAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gAnu0h9b+GhMBWRD5uGwXQLZU+IkR7T5Han28nJNGHmWkWOOmJloo2EsNkho6+tEDcKeEKScBqDMt0Ms+GE7aUfTkeP8K8UDOm/lIlv6SEzFBlTgSaJTRmuybOX1x+Ep","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ExpectedError":"","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing round change duplicate msg":{"Name":"round change duplicate msg","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":2,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"c91c804a02a9a20518a3556f1b6c5028465253b853cb2a0fe321964a58dbf92f","InputMessages":[{"Signature":"kW6iUpA50GpFa+2t6ePDCat26YdRRXfmNIPLmBXU42mlTfVo/nDtIGEQ0fmzz0XnAF+EdvbEjMMUN+k5YHIOHtEYIMeLZB44oCDSTOY7ECawp907ZQrHB5iOHnXGbd14","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":5,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"pU1oDz4k/PgMKMphKK23+QC7Fw+POBJV415byYpIQ21DAkzNmA4XBKpzgIuOc6EDBz3N4bD3VAQQwk67u4CJAAsrNJqRDXSHlzMW2r37onzIERBAYV+eNdjOr1/hufuo","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":5,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":1,"RoundChangeJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[],"ExpectedError":"","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing round change duplicate msg quorum":{"Name":"round change duplicate msg quorum","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":2,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"595889ae5096cb343497314ab7ea82299f8d05d01de3fad6902bfb559d5356ec","InputMessages":[{"Signature":"t3wDXB0dnGx8wigQxQqFyeVgrXkVCQVQYd/gfUA+2qEwQWFGbUn2SES25c9LCXCfBp3r/ZFDj5fUFKT2TNy0+M8elwP42hQb6fdFCQh+L4RJIxSwNBlmxLj8Ftkx9roI","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"oEQ/+4Yv52mq7qFgQZ0tWaih5VMx6ivO2Bm3rBHtd8MiWIpmrLH0r1kA+YkwbmeLEWn0xyry0wJjdAMl0p+g5A5nbpUpkng+R2PmLsolc1hr3nJMnXebzq0AKfqYLEkN","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":1,"RoundChangeJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"mMq307rvpww3BxdPm0KCuat1CPoddAl9w/tnc524RivFpWby1PNqd9ymZl6/aFkOGOkeJoqoIZJgn22ff+c9UplIJTKxcIGm5d70ocze3Sx7TCq+A2xixwEsOb/A0831","Signers":[2],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"lLT5TkZ3/7MmNTrG07fwT6CwDZ4una1ok5q6S7EHMBfoBJSNN5XOl3X1Hir/GbWLDs+i2Xrwlu+WjIqNYsaICol14dSQYu1LErxRH0dre4CUBLD7uPMIRUej/0GNt2S8","Signers":[3],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[{"Signature":"qV5LdutK2zk8Xbqu+xKdm1T33abJK26BV1I8cmor0GIZQdtFEJh4KP/6KMA1IZRSAH7cSAX9fvXO4/r4cVjuCRIFcdRL/cpnvfcAoTu0T8FBW1XqybwpwJcqglpkSvPk","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":["t3wDXB0dnGx8wigQxQqFyeVgrXkVCQVQYd/gfUA+2qEwQWFGbUn2SES25c9LCXCfBp3r/ZFDj5fUFKT2TNy0+M8elwP42hQb6fdFCQh+L4RJIxSwNBlmxLj8Ftkx9roIbAAAAHQAAADEAAAAAQAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","mMq307rvpww3BxdPm0KCuat1CPoddAl9w/tnc524RivFpWby1PNqd9ymZl6/aFkOGOkeJoqoIZJgn22ff+c9UplIJTKxcIGm5d70ocze3Sx7TCq+A2xixwEsOb/A0831bAAAAHQAAADEAAAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","lLT5TkZ3/7MmNTrG07fwT6CwDZ4una1ok5q6S7EHMBfoBJSNN5XOl3X1Hir/GbWLDs+i2Xrwlu+WjIqNYsaICol14dSQYu1LErxRH0dre4CUBLD7uPMIRUej/0GNt2S8bAAAAHQAAADEAAAAAwAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ExpectedError":"","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing round change duplicate msg quorum (prev prepared rc first)":{"Name":"round change duplicate msg quorum (prev prepared rc first)","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":2,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"ce52b74e850002c75ea9a52749285b8778c0411989e2e0547547ed26bdb31211","InputMessages":[{"Signature":"oEQ/+4Yv52mq7qFgQZ0tWaih5VMx6ivO2Bm3rBHtd8MiWIpmrLH0r1kA+YkwbmeLEWn0xyry0wJjdAMl0p+g5A5nbpUpkng+R2PmLsolc1hr3nJMnXebzq0AKfqYLEkN","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":1,"RoundChangeJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"t3wDXB0dnGx8wigQxQqFyeVgrXkVCQVQYd/gfUA+2qEwQWFGbUn2SES25c9LCXCfBp3r/ZFDj5fUFKT2TNy0+M8elwP42hQb6fdFCQh+L4RJIxSwNBlmxLj8Ftkx9roI","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"mMq307rvpww3BxdPm0KCuat1CPoddAl9w/tnc524RivFpWby1PNqd9ymZl6/aFkOGOkeJoqoIZJgn22ff+c9UplIJTKxcIGm5d70ocze3Sx7TCq+A2xixwEsOb/A0831","Signers":[2],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"lLT5TkZ3/7MmNTrG07fwT6CwDZ4una1ok5q6S7EHMBfoBJSNN5XOl3X1Hir/GbWLDs+i2Xrwlu+WjIqNYsaICol14dSQYu1LErxRH0dre4CUBLD7uPMIRUej/0GNt2S8","Signers":[3],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[{"Signature":"i4ma95c1eUi+zEK4p6Vc72Q97XYZnfVncPt3fEBqRkgzcJ4fQKAW6QceWXY2fy6zCLGRy4tu0EnQd7mPGQbP/yqf+9rv1eGv9aAwgF5Y/am60EYXQ9IEbYF8WozUgftP","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":["oEQ/+4Yv52mq7qFgQZ0tWaih5VMx6ivO2Bm3rBHtd8MiWIpmrLH0r1kA+YkwbmeLEWn0xyry0wJjdAMl0p+g5A5nbpUpkng+R2PmLsolc1hr3nJMnXebzq0AKfqYLEkNbAAAAHQAAAAcAwAAAQAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQBAAAAAAAAAFAAAACoAgAAAQIDBAwAAADQAAAAlAEAAIEp5oYqUSC9CF4ZNrTvtaVfx9GcDQ/aDp7FdtGKvUoXqzoDP1KWt0xf2vhct7PaMgG2P+yna4g2E+OxyhN+djo0Ljsd3bzgFvjKPLzjLIsSXdjCWnY5gZwgtTnp58bFeWwAAAB0AAAAxAAAAAEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAATAAAAL6Vb7ffTvN1MWgtWIMgCE/JFMPw/tM1Jj5bRAYubCm0AAAAAAAAAABQAAAAUAAAAAECAwSEnhPcn4cPkyDDrML3nq3WEu40fnfuTScs19kmgbZcMOuVawnZe4TlyWRb1GHLgmcMafsrGhwbwgyT27FOmo+V0J2A7PXyiJ01o8pf0aEGCq32lvqo5yPenHuXeNOqigRsAAAAdAAAAMQAAAACAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAEwAAAC+lW+3307zdTFoLViDIAhPyRTD8P7TNSY+W0QGLmwptAAAAAAAAAAAUAAAAFAAAAABAgMEr0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","mMq307rvpww3BxdPm0KCuat1CPoddAl9w/tnc524RivFpWby1PNqd9ymZl6/aFkOGOkeJoqoIZJgn22ff+c9UplIJTKxcIGm5d70ocze3Sx7TCq+A2xixwEsOb/A0831bAAAAHQAAADEAAAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","lLT5TkZ3/7MmNTrG07fwT6CwDZ4una1ok5q6S7EHMBfoBJSNN5XOl3X1Hir/GbWLDs+i2Xrwlu+WjIqNYsaICol14dSQYu1LErxRH0dre4CUBLD7uPMIRUej/0GNt2S8bAAAAHQAAADEAAAAAwAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="]},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ExpectedError":"","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing round change f+1 duplicate":{"Name":"round change f+1 duplicate","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"ff12e63cc23513903f5945833ddc44ac36b4034a5444cdff109956f7d7c5ffc3","InputMessages":[{"Signature":"t3wDXB0dnGx8wigQxQqFyeVgrXkVCQVQYd/gfUA+2qEwQWFGbUn2SES25c9LCXCfBp3r/ZFDj5fUFKT2TNy0+M8elwP42hQb6fdFCQh+L4RJIxSwNBlmxLj8Ftkx9roI","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"qiIUB/BsJ01sSyLqi7cEyLof+gCpLWjFZVKrREB5CHvN1VjLBCDaoUPnKIQtN2qqB+bQKYHksdMr0HyTckvzugOWZUhd4RgLad+1n9/Dg+y3j80jVLbWHw46bGiug7Ra","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":10,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":1,"RoundChangeJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[],"ExpectedError":"","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing round change f+1 not duplicate prepared":{"Name":"round change f+1 not duplicate prepared","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"6a7c1be9ab4c16a305f393c97744fd224bf1bbc2e627c04659d9e65d6cea571f","InputMessages":[{"Signature":"t3wDXB0dnGx8wigQxQqFyeVgrXkVCQVQYd/gfUA+2qEwQWFGbUn2SES25c9LCXCfBp3r/ZFDj5fUFKT2TNy0+M8elwP42hQb6fdFCQh+L4RJIxSwNBlmxLj8Ftkx9roI","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"kW6iUpA50GpFa+2t6ePDCat26YdRRXfmNIPLmBXU42mlTfVo/nDtIGEQ0fmzz0XnAF+EdvbEjMMUN+k5YHIOHtEYIMeLZB44oCDSTOY7ECawp907ZQrHB5iOHnXGbd14","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":5,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[],"ExpectedError":"","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing round change f+1 not prepared":{"Name":"round change f+1 not prepared","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"d8a2e893fc55a2aa3232d2c36a578260efa73cd083eecea1babec33c23725ba4","InputMessages":[{"Signature":"kW6iUpA50GpFa+2t6ePDCat26YdRRXfmNIPLmBXU42mlTfVo/nDtIGEQ0fmzz0XnAF+EdvbEjMMUN+k5YHIOHtEYIMeLZB44oCDSTOY7ECawp907ZQrHB5iOHnXGbd14","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":5,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"l2L7FVW+G1CyLcDuAOHspS+CjtTYIrZXoDlIZD3sB+M3QaLxVZn5EL+Z9GDws//oCVDA2xMGfrpix9MtFLShHU7SD98JO3emM+VHEil3E7BJvb5uuyfmYdQQQPwKIMm1","Signers":[2],"Message":{"MsgType":3,"Height":0,"Round":10,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[{"Signature":"kz8kpexujWeuen5vw85OPWLFzNyXxYOSnWo33YCCeTcrAlnQqzYZGlYa2hAq19FaFK8f/XxzHxr94KlxziSaHhlQeUpZGRTlvXO6k4gY9inwQeYNhsQEvLOOTsYjPr3D","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":5,"Identifier":"AQIDBA==","Root":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"DataRound":0,"RoundChangeJustification":[],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ExpectedError":"","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing round change f+1 prepared":{"Name":"round change f+1 prepared","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"141d47f572e1620701feab85a1131206783f540e418693fabb80992a68ebaf16","InputMessages":[{"Signature":"kW6iUpA50GpFa+2t6ePDCat26YdRRXfmNIPLmBXU42mlTfVo/nDtIGEQ0fmzz0XnAF+EdvbEjMMUN+k5YHIOHtEYIMeLZB44oCDSTOY7ECawp907ZQrHB5iOHnXGbd14","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":5,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"iAeiCubs/MqEu4U6XzCewYDQMkS9kAP313TU5bgbkwjlwtb+XSro2QCi02GnW1+mBGhmDeCOU0bJ8Xqtnku7l3U/MxeptqDkIRqMVUR9ziG5DomQMALrMllJvC+YtOsF","Signers":[2],"Message":{"MsgType":3,"Height":0,"Round":10,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":1,"RoundChangeJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[{"Signature":"kz8kpexujWeuen5vw85OPWLFzNyXxYOSnWo33YCCeTcrAlnQqzYZGlYa2hAq19FaFK8f/XxzHxr94KlxziSaHhlQeUpZGRTlvXO6k4gY9inwQeYNhsQEvLOOTsYjPr3D","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":5,"Identifier":"AQIDBA==","Root":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"DataRound":0,"RoundChangeJustification":[],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ExpectedError":"","ExpectedTimerState":{"Timeouts":1,"Round":5}},"*tests.MsgProcessingSpecTest_qbft message processing round change happy flow":{"Name":"round change happy flow","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"fbd68c0d90d12a2f3994e0af51d9d9dae5afedbcfebedcdcfca6f0b6b3b4c0ad","InputMessages":[{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"t3wDXB0dnGx8wigQxQqFyeVgrXkVCQVQYd/gfUA+2qEwQWFGbUn2SES25c9LCXCfBp3r/ZFDj5fUFKT2TNy0+M8elwP42hQb6fdFCQh+L4RJIxSwNBlmxLj8Ftkx9roI","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"mMq307rvpww3BxdPm0KCuat1CPoddAl9w/tnc524RivFpWby1PNqd9ymZl6/aFkOGOkeJoqoIZJgn22ff+c9UplIJTKxcIGm5d70ocze3Sx7TCq+A2xixwEsOb/A0831","Signers":[2],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"lLT5TkZ3/7MmNTrG07fwT6CwDZ4una1ok5q6S7EHMBfoBJSNN5XOl3X1Hir/GbWLDs+i2Xrwlu+WjIqNYsaICol14dSQYu1LErxRH0dre4CUBLD7uPMIRUej/0GNt2S8","Signers":[3],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"qV5LdutK2zk8Xbqu+xKdm1T33abJK26BV1I8cmor0GIZQdtFEJh4KP/6KMA1IZRSAH7cSAX9fvXO4/r4cVjuCRIFcdRL/cpnvfcAoTu0T8FBW1XqybwpwJcqglpkSvPk","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":["t3wDXB0dnGx8wigQxQqFyeVgrXkVCQVQYd/gfUA+2qEwQWFGbUn2SES25c9LCXCfBp3r/ZFDj5fUFKT2TNy0+M8elwP42hQb6fdFCQh+L4RJIxSwNBlmxLj8Ftkx9roIbAAAAHQAAADEAAAAAQAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","mMq307rvpww3BxdPm0KCuat1CPoddAl9w/tnc524RivFpWby1PNqd9ymZl6/aFkOGOkeJoqoIZJgn22ff+c9UplIJTKxcIGm5d70ocze3Sx7TCq+A2xixwEsOb/A0831bAAAAHQAAADEAAAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","lLT5TkZ3/7MmNTrG07fwT6CwDZ4una1ok5q6S7EHMBfoBJSNN5XOl3X1Hir/GbWLDs+i2Xrwlu+WjIqNYsaICol14dSQYu1LErxRH0dre4CUBLD7uPMIRUej/0GNt2S8bAAAAHQAAADEAAAAAwAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gAnu0h9b+GhMBWRD5uGwXQLZU+IkR7T5Han28nJNGHmWkWOOmJloo2EsNkho6+tEDcKeEKScBqDMt0Ms+GE7aUfTkeP8K8UDOm/lIlv6SEzFBlTgSaJTRmuybOX1x+Ep","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"g/mKHqmW47ekvXEY62N5fKTQ9oHBU5QKSZxjehyH9MZ8X7t4egnx/1F3SU39doGDA+eJlA2/r0rw2QzdjPbXyBY16RrkoAqqOgItSNzsCEvJl3pEsE9MVp8Ry8r3vs9T","Signers":[2],"Message":{"MsgType":1,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"j7thZ1r0Gs6AEJKBXe2IpYOkIQrN1ggSWQjb6Nz7KLazl+4Iikq9xEPjgs/oiNbYFB5BWknHZR1t3QMWWu/45saLm+WS289AZ503i9GVNM1Rcs+O845PvoFYjWstIUOB","Signers":[3],"Message":{"MsgType":1,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"g4KG9ysNs6oCilSUUjIeaLCVxNkOb04OhT3OxuqzozSCU7YyIwKguDcTNel9Dw0tCpaulMPrIm31VD5eE9OH5DklvF2hNjeVT/3YWtX2brkMuF9zag142guBiAnML6In","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"gsr1DCLhAStwnKO6zt7HBr7lFNjRQuInb4Ny/Ty+EyPV/fpB3txoI1NwJYHdRsl1E4EKSwxAPeyxJBD+Uqj9Yjq8wBYjWz+9xWgNIH73kln1XYVmWCZ35mhzUPaF0jWW","Signers":[2],"Message":{"MsgType":2,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null},{"Signature":"uaJFjWkFWOVdIW0w26l2oMCm68+vHetAT9JNBPyKKZmgH4y0GH1vEA1rB3+wF/0nCgrh+fuWHa7uAYrQ3Gk7p9h8kS8ZaIOZc+aZxQ9LOO1IKJoRTXmOdjWs9iV+gAra","Signers":[3],"Message":{"MsgType":2,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"OutputMessages":[{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"k4vgNWGo1HFng878a0/EORcr5VXfows36NJD9BzNfpyTrFN8QJWn4dFbJoqGabDWGTK1x2HCcDl92AAHNqgcN5qn2453TUjVKqjh3GPYm+VK05xNGMPQCfTvM93/xRFo","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"qV5LdutK2zk8Xbqu+xKdm1T33abJK26BV1I8cmor0GIZQdtFEJh4KP/6KMA1IZRSAH7cSAX9fvXO4/r4cVjuCRIFcdRL/cpnvfcAoTu0T8FBW1XqybwpwJcqglpkSvPk","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":["t3wDXB0dnGx8wigQxQqFyeVgrXkVCQVQYd/gfUA+2qEwQWFGbUn2SES25c9LCXCfBp3r/ZFDj5fUFKT2TNy0+M8elwP42hQb6fdFCQh+L4RJIxSwNBlmxLj8Ftkx9roIbAAAAHQAAADEAAAAAQAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","mMq307rvpww3BxdPm0KCuat1CPoddAl9w/tnc524RivFpWby1PNqd9ymZl6/aFkOGOkeJoqoIZJgn22ff+c9UplIJTKxcIGm5d70ocze3Sx7TCq+A2xixwEsOb/A0831bAAAAHQAAADEAAAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","lLT5TkZ3/7MmNTrG07fwT6CwDZ4una1ok5q6S7EHMBfoBJSNN5XOl3X1Hir/GbWLDs+i2Xrwlu+WjIqNYsaICol14dSQYu1LErxRH0dre4CUBLD7uPMIRUej/0GNt2S8bAAAAHQAAADEAAAAAwAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gAnu0h9b+GhMBWRD5uGwXQLZU+IkR7T5Han28nJNGHmWkWOOmJloo2EsNkho6+tEDcKeEKScBqDMt0Ms+GE7aUfTkeP8K8UDOm/lIlv6SEzFBlTgSaJTRmuybOX1x+Ep","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"g4KG9ysNs6oCilSUUjIeaLCVxNkOb04OhT3OxuqzozSCU7YyIwKguDcTNel9Dw0tCpaulMPrIm31VD5eE9OH5DklvF2hNjeVT/3YWtX2brkMuF9zag142guBiAnML6In","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"ExpectedError":"","ExpectedTimerState":{"Timeouts":1,"Round":2}},"*tests.MsgProcessingSpecTest_qbft message processing round change invalid height":{"Name":"round change invalid height","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":2,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"ddb0f7e1a8888a8de5295005872d8525d6afea053121993dc65e34fcb7f290b2","InputMessages":[{"Signature":"pbOQqpSIIZeAO1L79VPKaCJ6CcwXy9fATcJVHKvkCBehqVYhl3HC1kXuE/mrqzUwCP+Jssh2s435dMKwDCGweYcr5jdkwKK8zQYzQ4Vh6YWVueJ7s3vYHPINIIpAHtZ7","Signers":[1],"Message":{"MsgType":3,"Height":2,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[],"ExpectedError":"invalid signed message: wrong msg height","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing round change invalid sig":{"Name":"round change invalid sig","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":2,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"ddb0f7e1a8888a8de5295005872d8525d6afea053121993dc65e34fcb7f290b2","InputMessages":[{"Signature":"t3wDXB0dnGx8wigQxQqFyeVgrXkVCQVQYd/gfUA+2qEwQWFGbUn2SES25c9LCXCfBp3r/ZFDj5fUFKT2TNy0+M8elwP42hQb6fdFCQh+L4RJIxSwNBlmxLj8Ftkx9roI","Signers":[2],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[],"ExpectedError":"invalid signed message: msg signature invalid: failed to verify signature","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing round change justification no quorum":{"Name":"round change justification no quorum","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":2,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"ddb0f7e1a8888a8de5295005872d8525d6afea053121993dc65e34fcb7f290b2","InputMessages":[{"Signature":"ifCCssPYsrqGH6g6YR2Xyg/kXfqfLKQ+Tc20DJj/pJW1Nu5W6a00mW9uWaE2qSgkDT68p601DAAe3svR3LDekMQBQzLh8oTgmGo40gvdptSZqss1A5KNAUiJLMbSgb4m","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":1,"RoundChangeJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[],"ExpectedError":"invalid signed message: no justifications quorum","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing round change justification not proposer":{"Name":"round change justification not proposer","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":10,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"caadfd7b182f2f7fe27d6a7aeeedf0b3a9525d574418f38d78e90ab85cd74f34","InputMessages":[{"Signature":"i19W8ubQ3tgKgkm1E9zJe/AlHN5toAlPh9V1qS+9twl4GuSSLE8dpss4Y4Yn5yGPCNh0nlpyMQqx8oFjQoh6QegOELIRF86KI1N3HVT2cQ/ptFwAO/vhsLKnJieT2eG0","Signers":[1],"Message":{"MsgType":3,"Height":10,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"kM9obVnJ849ufmoemJpBYE4mZapbRSZJ8pPZCZ89zs07h5IBxuX9s/puuBm9p+TFFAx6x2C1ztDmfNNSBhW9OrjZDO5z7lwK+vX4I4r2w9LQD65VUhvpE9Uvipbnx954","Signers":[2],"Message":{"MsgType":3,"Height":10,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"pam6tfFdG5R9+NM2QlZBWbPBLQqD317nsIrTm32guUKx4CUZQLmGjTsb0a8fLnWqAjh4e/F9Rv5kS2PEVyNGclcGuV7n2tlwVgL2M/zDD0LIQYTFMa0d+GKQq9cLmjLs","Signers":[3],"Message":{"MsgType":3,"Height":10,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[{"Signature":"pvCqVb/1uGVljBPDUvyGjg1hDohRP+nrYgYGm0YO/T4QM+Ls9YUvBJrM9C0RmcExDgQlTfYmziR6ChOQPoHn1QKmQ3UCWiWXEY/CpK3sl1M8Es+FCjCm7clL7OMAq/AH","Signers":[1],"Message":{"MsgType":3,"Height":10,"Round":2,"Identifier":"AQIDBA==","Root":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"DataRound":0,"RoundChangeJustification":[],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ExpectedError":"","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing round change justification wrong round":{"Name":"round change justification wrong round","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":5,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"a445f35aed611003f62901a8a51767f322b753d5a393122c129e370616b18966","InputMessages":[{"Signature":"kLaoLW/uvu6ej5+KRNMCL8imfsUhjURv10ZvwAlQIx3hWcMPo5IwqVQoXXujNdkxF7a9BS8ViTeJHwSXmwtQ03dqoFuK10UwjUOUVJNr9xWQNvJsNLfy+LNYuTcc6kdK","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":5,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":1,"RoundChangeJustification":["gAnu0h9b+GhMBWRD5uGwXQLZU+IkR7T5Han28nJNGHmWkWOOmJloo2EsNkho6+tEDcKeEKScBqDMt0Ms+GE7aUfTkeP8K8UDOm/lIlv6SEzFBlTgSaJTRmuybOX1x+EpbAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","g/mKHqmW47ekvXEY62N5fKTQ9oHBU5QKSZxjehyH9MZ8X7t4egnx/1F3SU39doGDA+eJlA2/r0rw2QzdjPbXyBY16RrkoAqqOgItSNzsCEvJl3pEsE9MVp8Ry8r3vs9TbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","j7thZ1r0Gs6AEJKBXe2IpYOkIQrN1ggSWQjb6Nz7KLazl+4Iikq9xEPjgs/oiNbYFB5BWknHZR1t3QMWWu/45saLm+WS289AZ503i9GVNM1Rcs+O845PvoFYjWstIUOBbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[],"ExpectedError":"invalid signed message: round change justification invalid: wrong msg round","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing round change multi signers":{"Name":"round change multi signers","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":2,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"ddb0f7e1a8888a8de5295005872d8525d6afea053121993dc65e34fcb7f290b2","InputMessages":[{"Signature":"mVycWEvhNmtwoepb0VRK8PYTDUhbaHsRIsQza/ceSm8/htj4lG19xPP4DhVZf0nXEjeBgpIJvJXh+kIJ/pMvhZBGNLy0+69/qjC2UgVPwepH9ZpwCwDjSZef1LpZ+SPF","Signers":[1,2],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[],"ExpectedError":"invalid signed message: msg allows 1 signer","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing round change not prepared":{"Name":"round change not prepared","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":2,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"595889ae5096cb343497314ab7ea82299f8d05d01de3fad6902bfb559d5356ec","InputMessages":[{"Signature":"t3wDXB0dnGx8wigQxQqFyeVgrXkVCQVQYd/gfUA+2qEwQWFGbUn2SES25c9LCXCfBp3r/ZFDj5fUFKT2TNy0+M8elwP42hQb6fdFCQh+L4RJIxSwNBlmxLj8Ftkx9roI","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"mMq307rvpww3BxdPm0KCuat1CPoddAl9w/tnc524RivFpWby1PNqd9ymZl6/aFkOGOkeJoqoIZJgn22ff+c9UplIJTKxcIGm5d70ocze3Sx7TCq+A2xixwEsOb/A0831","Signers":[2],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"lLT5TkZ3/7MmNTrG07fwT6CwDZ4una1ok5q6S7EHMBfoBJSNN5XOl3X1Hir/GbWLDs+i2Xrwlu+WjIqNYsaICol14dSQYu1LErxRH0dre4CUBLD7uPMIRUej/0GNt2S8","Signers":[3],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[{"Signature":"qV5LdutK2zk8Xbqu+xKdm1T33abJK26BV1I8cmor0GIZQdtFEJh4KP/6KMA1IZRSAH7cSAX9fvXO4/r4cVjuCRIFcdRL/cpnvfcAoTu0T8FBW1XqybwpwJcqglpkSvPk","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":["t3wDXB0dnGx8wigQxQqFyeVgrXkVCQVQYd/gfUA+2qEwQWFGbUn2SES25c9LCXCfBp3r/ZFDj5fUFKT2TNy0+M8elwP42hQb6fdFCQh+L4RJIxSwNBlmxLj8Ftkx9roIbAAAAHQAAADEAAAAAQAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","mMq307rvpww3BxdPm0KCuat1CPoddAl9w/tnc524RivFpWby1PNqd9ymZl6/aFkOGOkeJoqoIZJgn22ff+c9UplIJTKxcIGm5d70ocze3Sx7TCq+A2xixwEsOb/A0831bAAAAHQAAADEAAAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","lLT5TkZ3/7MmNTrG07fwT6CwDZ4una1ok5q6S7EHMBfoBJSNN5XOl3X1Hir/GbWLDs+i2Xrwlu+WjIqNYsaICol14dSQYu1LErxRH0dre4CUBLD7uPMIRUej/0GNt2S8bAAAAHQAAADEAAAAAwAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ExpectedError":"","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing round change partial quorum":{"Name":"round change partial quorum","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"a91c62dca4813bc577f3441b7ce9e221fb96af65aa14b56af88af69c784b586e","InputMessages":[{"Signature":"mMq307rvpww3BxdPm0KCuat1CPoddAl9w/tnc524RivFpWby1PNqd9ymZl6/aFkOGOkeJoqoIZJgn22ff+c9UplIJTKxcIGm5d70ocze3Sx7TCq+A2xixwEsOb/A0831","Signers":[2],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"lLT5TkZ3/7MmNTrG07fwT6CwDZ4una1ok5q6S7EHMBfoBJSNN5XOl3X1Hir/GbWLDs+i2Xrwlu+WjIqNYsaICol14dSQYu1LErxRH0dre4CUBLD7uPMIRUej/0GNt2S8","Signers":[3],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"k43FZ6ZB8Ke7oSc87rz3PRMg5a+LNU25H557YRknOBbnFRU7/fZUuVXhAyCWlFhKFlK8ZMCtL3Pm63iB7l/IXXruK4GbI5+ovDXITmKnURsD5jEcVNhFm9lTrtU6geGL","Signers":[3],"Message":{"MsgType":3,"Height":0,"Round":3,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[{"Signature":"k4vgNWGo1HFng878a0/EORcr5VXfows36NJD9BzNfpyTrFN8QJWn4dFbJoqGabDWGTK1x2HCcDl92AAHNqgcN5qn2453TUjVKqjh3GPYm+VK05xNGMPQCfTvM93/xRFo","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"DataRound":0,"RoundChangeJustification":[],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ExpectedError":"","ExpectedTimerState":{"Timeouts":1,"Round":2}},"*tests.MsgProcessingSpecTest_qbft message processing round change past round":{"Name":"round change past round","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":50,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"92fa15d04e03ed9821c8a6f28089a5b3df80394f5ce908f9f5dfb6ba3db796fa","InputMessages":[{"Signature":"t3wDXB0dnGx8wigQxQqFyeVgrXkVCQVQYd/gfUA+2qEwQWFGbUn2SES25c9LCXCfBp3r/ZFDj5fUFKT2TNy0+M8elwP42hQb6fdFCQh+L4RJIxSwNBlmxLj8Ftkx9roI","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"kW6iUpA50GpFa+2t6ePDCat26YdRRXfmNIPLmBXU42mlTfVo/nDtIGEQ0fmzz0XnAF+EdvbEjMMUN+k5YHIOHtEYIMeLZB44oCDSTOY7ECawp907ZQrHB5iOHnXGbd14","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":5,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"qiIUB/BsJ01sSyLqi7cEyLof+gCpLWjFZVKrREB5CHvN1VjLBCDaoUPnKIQtN2qqB+bQKYHksdMr0HyTckvzugOWZUhd4RgLad+1n9/Dg+y3j80jVLbWHw46bGiug7Ra","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":10,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":1,"RoundChangeJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[],"ExpectedError":"invalid signed message: past round","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing round change past round quorum":{"Name":"round change past round quorum","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":50,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"92fa15d04e03ed9821c8a6f28089a5b3df80394f5ce908f9f5dfb6ba3db796fa","InputMessages":[{"Signature":"t3wDXB0dnGx8wigQxQqFyeVgrXkVCQVQYd/gfUA+2qEwQWFGbUn2SES25c9LCXCfBp3r/ZFDj5fUFKT2TNy0+M8elwP42hQb6fdFCQh+L4RJIxSwNBlmxLj8Ftkx9roI","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"kW6iUpA50GpFa+2t6ePDCat26YdRRXfmNIPLmBXU42mlTfVo/nDtIGEQ0fmzz0XnAF+EdvbEjMMUN+k5YHIOHtEYIMeLZB44oCDSTOY7ECawp907ZQrHB5iOHnXGbd14","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":5,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"qiIUB/BsJ01sSyLqi7cEyLof+gCpLWjFZVKrREB5CHvN1VjLBCDaoUPnKIQtN2qqB+bQKYHksdMr0HyTckvzugOWZUhd4RgLad+1n9/Dg+y3j80jVLbWHw46bGiug7Ra","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":10,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":1,"RoundChangeJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"rTXsnuZZhTrVhoYarSagv3o6qZ9+PtkXqj70rGlqVcvZ0cOgenesiss6j8XH7Xd+QJRO3Cx/XCI3jWXbi8Td6KwwNhracrhTW/j9HJn1gT5N19HuJbJMt5GaSA/gCe9/n7rIR","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":6,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":1,"RoundChangeJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gMja6tXQr3PrWPk906I0fFwG3kY4W4cCieb9RnpMippFKDz2QJRqN0XmjvA2JDrxDF/wfa9ABH8y4I4geFJCPP+GPcH7bmBO3UHaH2lWTohIF5ksgz2sghVJ3RpfPnbH","Signers":[2],"Message":{"MsgType":3,"Height":0,"Round":6,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":1,"RoundChangeJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gDf+9dh3oIwJ8JYriMM8jdlUbA56ujdzSb1j1pq587GG/XvCB0klLfmg9VJfgr8IAWHKj10lIROWtfub6ujTWpVUsloxGW+6fKejEeIsEcnXoUf0aE9ptOmKrg3GNDYH","Signers":[3],"Message":{"MsgType":3,"Height":0,"Round":6,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":1,"RoundChangeJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[],"ExpectedError":"invalid signed message: past round","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing round change peer prepared":{"Name":"round change peer prepared","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":2,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"7d6e2db3c1a0a9f0536c40b2e9f2d7d41cb89f102209f3f080bf3e44c89a786f","InputMessages":[{"Signature":"t3wDXB0dnGx8wigQxQqFyeVgrXkVCQVQYd/gfUA+2qEwQWFGbUn2SES25c9LCXCfBp3r/ZFDj5fUFKT2TNy0+M8elwP42hQb6fdFCQh+L4RJIxSwNBlmxLj8Ftkx9roI","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"g+23kWE7m/1jehx91y2yqtG5t0sP4ED+2sz2MipAQBG+P3nX7abKV+fAZS/hjhJvAZi70KYcBWNBGMcMiddJRm/5QbJoCUP2qPMQd/yzA9JvknfmdapAOPyXRAMeYyCe","Signers":[2],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":1,"RoundChangeJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"lLT5TkZ3/7MmNTrG07fwT6CwDZ4una1ok5q6S7EHMBfoBJSNN5XOl3X1Hir/GbWLDs+i2Xrwlu+WjIqNYsaICol14dSQYu1LErxRH0dre4CUBLD7uPMIRUej/0GNt2S8","Signers":[3],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[{"Signature":"kj14BXS8OWo2HG4sicjvFyC3WRhEzSxLRPYWFvmXQ38guTGQZrHOQq7N8yQr0LHHA3daxgcGmi6vNmRQO7I06Igd5R44ZW3bLf3xa6Rehmav3GQmYnjl2+ewkB+bMOPe","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":["t3wDXB0dnGx8wigQxQqFyeVgrXkVCQVQYd/gfUA+2qEwQWFGbUn2SES25c9LCXCfBp3r/ZFDj5fUFKT2TNy0+M8elwP42hQb6fdFCQh+L4RJIxSwNBlmxLj8Ftkx9roIbAAAAHQAAADEAAAAAQAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","g+23kWE7m/1jehx91y2yqtG5t0sP4ED+2sz2MipAQBG+P3nX7abKV+fAZS/hjhJvAZi70KYcBWNBGMcMiddJRm/5QbJoCUP2qPMQd/yzA9JvknfmdapAOPyXRAMeYyCebAAAAHQAAAAcAwAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQBAAAAAAAAAFAAAACoAgAAAQIDBAwAAADQAAAAlAEAAIEp5oYqUSC9CF4ZNrTvtaVfx9GcDQ/aDp7FdtGKvUoXqzoDP1KWt0xf2vhct7PaMgG2P+yna4g2E+OxyhN+djo0Ljsd3bzgFvjKPLzjLIsSXdjCWnY5gZwgtTnp58bFeWwAAAB0AAAAxAAAAAEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAATAAAAL6Vb7ffTvN1MWgtWIMgCE/JFMPw/tM1Jj5bRAYubCm0AAAAAAAAAABQAAAAUAAAAAECAwSEnhPcn4cPkyDDrML3nq3WEu40fnfuTScs19kmgbZcMOuVawnZe4TlyWRb1GHLgmcMafsrGhwbwgyT27FOmo+V0J2A7PXyiJ01o8pf0aEGCq32lvqo5yPenHuXeNOqigRsAAAAdAAAAMQAAAACAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAEwAAAC+lW+3307zdTFoLViDIAhPyRTD8P7TNSY+W0QGLmwptAAAAAAAAAAAUAAAAFAAAAABAgMEr0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","lLT5TkZ3/7MmNTrG07fwT6CwDZ4una1ok5q6S7EHMBfoBJSNN5XOl3X1Hir/GbWLDs+i2Xrwlu+WjIqNYsaICol14dSQYu1LErxRH0dre4CUBLD7uPMIRUej/0GNt2S8bAAAAHQAAADEAAAAAwAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="]},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ExpectedError":"","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing round change peer prepared different heights":{"Name":"round change peer prepared different heights","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":3,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"79f065a6e1e3b11e0cacc0e46c4253116a08c183b937801a2d355b9d5bbee969","InputMessages":[{"Signature":"qjzq+SaEJweJeLUnqfowxtT3shkZH5ZpE0QTJn9q88FmTnMSgDC4qr+jNyouzTdECGiPApfZgx0QkrlTc0KxJ1Zme46epjFrHOQo6/s7jKCO2C5/aOXlKf7jBpsJm9Dc","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":3,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"pr/wECaWTuId31/HKREzcjaIVPyaVW//cQlyYsc8JBnD0O2+kZeexwZYXEL17i7fB//4J6J6AUlaHc/5QltqNcUw4fIaBXi3VQjPtGHpAF9xiaAWarjEnQQhgT2UJN2P","Signers":[2],"Message":{"MsgType":3,"Height":0,"Round":3,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":1,"RoundChangeJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"jGF3fqgHCYuSupgY7JdTPI4EFHKiMdW7tAs5UGpdHN2jIer+NRGSIaw9H5Ahup7FBUfRXcsZN3+pycHrWiunlCJ7d760owPY9SKUfLNQ/6U75WPRlrDnfF1UredGl2yn","Signers":[3],"Message":{"MsgType":3,"Height":0,"Round":3,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":2,"RoundChangeJustification":["gAnu0h9b+GhMBWRD5uGwXQLZU+IkR7T5Han28nJNGHmWkWOOmJloo2EsNkho6+tEDcKeEKScBqDMt0Ms+GE7aUfTkeP8K8UDOm/lIlv6SEzFBlTgSaJTRmuybOX1x+EpbAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","g/mKHqmW47ekvXEY62N5fKTQ9oHBU5QKSZxjehyH9MZ8X7t4egnx/1F3SU39doGDA+eJlA2/r0rw2QzdjPbXyBY16RrkoAqqOgItSNzsCEvJl3pEsE9MVp8Ry8r3vs9TbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","j7thZ1r0Gs6AEJKBXe2IpYOkIQrN1ggSWQjb6Nz7KLazl+4Iikq9xEPjgs/oiNbYFB5BWknHZR1t3QMWWu/45saLm+WS289AZ503i9GVNM1Rcs+O845PvoFYjWstIUOBbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[{"Signature":"p5L+SHPvKZCuRlBxrQTUrGUtOrv4wWY/J5BYwJ+McFniMMSQEmCql2lnGPkySN4TB0KEbSX20G4tXVUZEI9smSsQvHPdlqBTd6V6e3vZArJhhlrzORL7iqri9xUEmcFo","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":3,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":["qjzq+SaEJweJeLUnqfowxtT3shkZH5ZpE0QTJn9q88FmTnMSgDC4qr+jNyouzTdECGiPApfZgx0QkrlTc0KxJ1Zme46epjFrHOQo6/s7jKCO2C5/aOXlKf7jBpsJm9DcbAAAAHQAAADEAAAAAQAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAwAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","pr/wECaWTuId31/HKREzcjaIVPyaVW//cQlyYsc8JBnD0O2+kZeexwZYXEL17i7fB//4J6J6AUlaHc/5QltqNcUw4fIaBXi3VQjPtGHpAF9xiaAWarjEnQQhgT2UJN2PbAAAAHQAAAAcAwAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAwAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQBAAAAAAAAAFAAAACoAgAAAQIDBAwAAADQAAAAlAEAAIEp5oYqUSC9CF4ZNrTvtaVfx9GcDQ/aDp7FdtGKvUoXqzoDP1KWt0xf2vhct7PaMgG2P+yna4g2E+OxyhN+djo0Ljsd3bzgFvjKPLzjLIsSXdjCWnY5gZwgtTnp58bFeWwAAAB0AAAAxAAAAAEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAATAAAAL6Vb7ffTvN1MWgtWIMgCE/JFMPw/tM1Jj5bRAYubCm0AAAAAAAAAABQAAAAUAAAAAECAwSEnhPcn4cPkyDDrML3nq3WEu40fnfuTScs19kmgbZcMOuVawnZe4TlyWRb1GHLgmcMafsrGhwbwgyT27FOmo+V0J2A7PXyiJ01o8pf0aEGCq32lvqo5yPenHuXeNOqigRsAAAAdAAAAMQAAAACAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAEwAAAC+lW+3307zdTFoLViDIAhPyRTD8P7TNSY+W0QGLmwptAAAAAAAAAAAUAAAAFAAAAABAgMEr0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","jGF3fqgHCYuSupgY7JdTPI4EFHKiMdW7tAs5UGpdHN2jIer+NRGSIaw9H5Ahup7FBUfRXcsZN3+pycHrWiunlCJ7d760owPY9SKUfLNQ/6U75WPRlrDnfF1UredGl2ynbAAAAHQAAAAcAwAAAwAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAwAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQCAAAAAAAAAFAAAACoAgAAAQIDBAwAAADQAAAAlAEAAIAJ7tIfW/hoTAVkQ+bhsF0C2VPiJEe0+R2p9vJyTRh5lpFjjpiZaKNhLDZIaOvrRA3CnhCknAagzLdDLPhhO2lH05Hj/CvFAzpv5SJb+khMxQZU4EmiU0Zrsmzl9cfhKWwAAAB0AAAAxAAAAAEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAIAAAAAAAAATAAAAL6Vb7ffTvN1MWgtWIMgCE/JFMPw/tM1Jj5bRAYubCm0AAAAAAAAAABQAAAAUAAAAAECAwSD+YoeqZbjt6S9cRjrY3l8pND2gcFTlApJnGN6HIf0xnxfu3h6CfH/UXdJTf12gYMD54mUDb+vSvDZDN2M9tfIFjXpGuSgCqo6Ai1I3OwIS8mXekSwT0xWnxHLyve+z1NsAAAAdAAAAMQAAAACAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAACAAAAAAAAAEwAAAC+lW+3307zdTFoLViDIAhPyRTD8P7TNSY+W0QGLmwptAAAAAAAAAAAUAAAAFAAAAABAgMEj7thZ1r0Gs6AEJKBXe2IpYOkIQrN1ggSWQjb6Nz7KLazl+4Iikq9xEPjgs/oiNbYFB5BWknHZR1t3QMWWu/45saLm+WS289AZ503i9GVNM1Rcs+O845PvoFYjWstIUOBbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":["gAnu0h9b+GhMBWRD5uGwXQLZU+IkR7T5Han28nJNGHmWkWOOmJloo2EsNkho6+tEDcKeEKScBqDMt0Ms+GE7aUfTkeP8K8UDOm/lIlv6SEzFBlTgSaJTRmuybOX1x+EpbAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","g/mKHqmW47ekvXEY62N5fKTQ9oHBU5QKSZxjehyH9MZ8X7t4egnx/1F3SU39doGDA+eJlA2/r0rw2QzdjPbXyBY16RrkoAqqOgItSNzsCEvJl3pEsE9MVp8Ry8r3vs9TbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","j7thZ1r0Gs6AEJKBXe2IpYOkIQrN1ggSWQjb6Nz7KLazl+4Iikq9xEPjgs/oiNbYFB5BWknHZR1t3QMWWu/45saLm+WS289AZ503i9GVNM1Rcs+O845PvoFYjWstIUOBbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="]},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ExpectedError":"","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing round change prepared":{"Name":"round change prepared","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":2,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"2cd6dac3861dfb85387839c80262f60c2801cd7461ffd8226cf2454cbde1a9ab","InputMessages":[{"Signature":"oEQ/+4Yv52mq7qFgQZ0tWaih5VMx6ivO2Bm3rBHtd8MiWIpmrLH0r1kA+YkwbmeLEWn0xyry0wJjdAMl0p+g5A5nbpUpkng+R2PmLsolc1hr3nJMnXebzq0AKfqYLEkN","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":1,"RoundChangeJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"mMq307rvpww3BxdPm0KCuat1CPoddAl9w/tnc524RivFpWby1PNqd9ymZl6/aFkOGOkeJoqoIZJgn22ff+c9UplIJTKxcIGm5d70ocze3Sx7TCq+A2xixwEsOb/A0831","Signers":[2],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"q6Ok2cz9kmwxKRj2bXJ6qUmth4N7o8zqaLHIUGb1i1wASpjuryw/D+3W7qrugenesisgwFH3S8GGI8LVdUA6JGEOSgQ1DHp+e3QbLGMuuiLGDdUKJ8doYLvLIVBR1kmf4u766","Signers":[3],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":1,"RoundChangeJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[{"Signature":"qJr1xMfPc6rhOrnSDyyMEJjh+MI4R16MIe+rdCFil/KgnRsv/VW8KHyWqlAPNNDlFYoUH+KL8AlUTIUU36bJ8V7ESc2LkwZWASzZFFw5rVT16a2UHivjd2Pg3pBdepEO","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":["oEQ/+4Yv52mq7qFgQZ0tWaih5VMx6ivO2Bm3rBHtd8MiWIpmrLH0r1kA+YkwbmeLEWn0xyry0wJjdAMl0p+g5A5nbpUpkng+R2PmLsolc1hr3nJMnXebzq0AKfqYLEkNbAAAAHQAAAAcAwAAAQAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQBAAAAAAAAAFAAAACoAgAAAQIDBAwAAADQAAAAlAEAAIEp5oYqUSC9CF4ZNrTvtaVfx9GcDQ/aDp7FdtGKvUoXqzoDP1KWt0xf2vhct7PaMgG2P+yna4g2E+OxyhN+djo0Ljsd3bzgFvjKPLzjLIsSXdjCWnY5gZwgtTnp58bFeWwAAAB0AAAAxAAAAAEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAATAAAAL6Vb7ffTvN1MWgtWIMgCE/JFMPw/tM1Jj5bRAYubCm0AAAAAAAAAABQAAAAUAAAAAECAwSEnhPcn4cPkyDDrML3nq3WEu40fnfuTScs19kmgbZcMOuVawnZe4TlyWRb1GHLgmcMafsrGhwbwgyT27FOmo+V0J2A7PXyiJ01o8pf0aEGCq32lvqo5yPenHuXeNOqigRsAAAAdAAAAMQAAAACAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAEwAAAC+lW+3307zdTFoLViDIAhPyRTD8P7TNSY+W0QGLmwptAAAAAAAAAAAUAAAAFAAAAABAgMEr0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","mMq307rvpww3BxdPm0KCuat1CPoddAl9w/tnc524RivFpWby1PNqd9ymZl6/aFkOGOkeJoqoIZJgn22ff+c9UplIJTKxcIGm5d70ocze3Sx7TCq+A2xixwEsOb/A0831bAAAAHQAAADEAAAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","q6Ok2cz9kmwxKRj2bXJ6qUmth4N7o8zqaLHIUGb1i1wASpjuryw/D+3W7qrugenesisgwFH3S8GGI8LVdUA6JGEOSgQ1DHp+e3QbLGMuuiLGDdUKJ8doYLvLIVBR1kmf4u766bAAAAHQAAAAcAwAAAwAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQBAAAAAAAAAFAAAACoAgAAAQIDBAwAAADQAAAAlAEAAIEp5oYqUSC9CF4ZNrTvtaVfx9GcDQ/aDp7FdtGKvUoXqzoDP1KWt0xf2vhct7PaMgG2P+yna4g2E+OxyhN+djo0Ljsd3bzgFvjKPLzjLIsSXdjCWnY5gZwgtTnp58bFeWwAAAB0AAAAxAAAAAEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAATAAAAL6Vb7ffTvN1MWgtWIMgCE/JFMPw/tM1Jj5bRAYubCm0AAAAAAAAAABQAAAAUAAAAAECAwSEnhPcn4cPkyDDrML3nq3WEu40fnfuTScs19kmgbZcMOuVawnZe4TlyWRb1GHLgmcMafsrGhwbwgyT27FOmo+V0J2A7PXyiJ01o8pf0aEGCq32lvqo5yPenHuXeNOqigRsAAAAdAAAAMQAAAACAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAEwAAAC+lW+3307zdTFoLViDIAhPyRTD8P7TNSY+W0QGLmwptAAAAAAAAAAAUAAAAFAAAAABAgMEr0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="]},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ExpectedError":"","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing round change quorum msg not prepared":{"Name":"round change quorum msg not prepared","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":2,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"ce52b74e850002c75ea9a52749285b8778c0411989e2e0547547ed26bdb31211","InputMessages":[{"Signature":"oEQ/+4Yv52mq7qFgQZ0tWaih5VMx6ivO2Bm3rBHtd8MiWIpmrLH0r1kA+YkwbmeLEWn0xyry0wJjdAMl0p+g5A5nbpUpkng+R2PmLsolc1hr3nJMnXebzq0AKfqYLEkN","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":1,"RoundChangeJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"mMq307rvpww3BxdPm0KCuat1CPoddAl9w/tnc524RivFpWby1PNqd9ymZl6/aFkOGOkeJoqoIZJgn22ff+c9UplIJTKxcIGm5d70ocze3Sx7TCq+A2xixwEsOb/A0831","Signers":[2],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"lLT5TkZ3/7MmNTrG07fwT6CwDZ4una1ok5q6S7EHMBfoBJSNN5XOl3X1Hir/GbWLDs+i2Xrwlu+WjIqNYsaICol14dSQYu1LErxRH0dre4CUBLD7uPMIRUej/0GNt2S8","Signers":[3],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[{"Signature":"i4ma95c1eUi+zEK4p6Vc72Q97XYZnfVncPt3fEBqRkgzcJ4fQKAW6QceWXY2fy6zCLGRy4tu0EnQd7mPGQbP/yqf+9rv1eGv9aAwgF5Y/am60EYXQ9IEbYF8WozUgftP","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":["oEQ/+4Yv52mq7qFgQZ0tWaih5VMx6ivO2Bm3rBHtd8MiWIpmrLH0r1kA+YkwbmeLEWn0xyry0wJjdAMl0p+g5A5nbpUpkng+R2PmLsolc1hr3nJMnXebzq0AKfqYLEkNbAAAAHQAAAAcAwAAAQAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQBAAAAAAAAAFAAAACoAgAAAQIDBAwAAADQAAAAlAEAAIEp5oYqUSC9CF4ZNrTvtaVfx9GcDQ/aDp7FdtGKvUoXqzoDP1KWt0xf2vhct7PaMgG2P+yna4g2E+OxyhN+djo0Ljsd3bzgFvjKPLzjLIsSXdjCWnY5gZwgtTnp58bFeWwAAAB0AAAAxAAAAAEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAATAAAAL6Vb7ffTvN1MWgtWIMgCE/JFMPw/tM1Jj5bRAYubCm0AAAAAAAAAABQAAAAUAAAAAECAwSEnhPcn4cPkyDDrML3nq3WEu40fnfuTScs19kmgbZcMOuVawnZe4TlyWRb1GHLgmcMafsrGhwbwgyT27FOmo+V0J2A7PXyiJ01o8pf0aEGCq32lvqo5yPenHuXeNOqigRsAAAAdAAAAMQAAAACAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAEwAAAC+lW+3307zdTFoLViDIAhPyRTD8P7TNSY+W0QGLmwptAAAAAAAAAAAUAAAAFAAAAABAgMEr0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","mMq307rvpww3BxdPm0KCuat1CPoddAl9w/tnc524RivFpWby1PNqd9ymZl6/aFkOGOkeJoqoIZJgn22ff+c9UplIJTKxcIGm5d70ocze3Sx7TCq+A2xixwEsOb/A0831bAAAAHQAAADEAAAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","lLT5TkZ3/7MmNTrG07fwT6CwDZ4una1ok5q6S7EHMBfoBJSNN5XOl3X1Hir/GbWLDs+i2Xrwlu+WjIqNYsaICol14dSQYu1LErxRH0dre4CUBLD7uPMIRUej/0GNt2S8bAAAAHQAAADEAAAAAwAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="]},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ExpectedError":"","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing round change quorum order 1":{"Name":"round change quorum order 1","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":2,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"f8fde754bbe247962fcbd9c8890f79f256ee4e5af7aaa13fc5da2ee85b89333c","InputMessages":[{"Signature":"lYIJUgf2Ib4eBAkcq+pgKeMJH9vjO4Rfey5ujbblB7LKWOkxkmhOEV3uo2FwPntzChOh+nG9GkvXtLNY1CMoTM06bv75DkS9ptloDrtSC3WnyYeybESPMD7fxQI/TSVq","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":1,"RoundChangeJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"mMq307rvpww3BxdPm0KCuat1CPoddAl9w/tnc524RivFpWby1PNqd9ymZl6/aFkOGOkeJoqoIZJgn22ff+c9UplIJTKxcIGm5d70ocze3Sx7TCq+A2xixwEsOb/A0831","Signers":[2],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"lLT5TkZ3/7MmNTrG07fwT6CwDZ4una1ok5q6S7EHMBfoBJSNN5XOl3X1Hir/GbWLDs+i2Xrwlu+WjIqNYsaICol14dSQYu1LErxRH0dre4CUBLD7uPMIRUej/0GNt2S8","Signers":[3],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[{"Signature":"oSON7mwywqgQXTlWWUPggYYHBLMkqvpSTuDjmzK7tKeQ7DrepLKslcCYuj5333S2DRDcPZIUKyXJzudVTqhDUcqtP2C0UQq/Z4BvdiNYlOrMH8uU9Z/x11RkKxAFnp+J","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":["lYIJUgf2Ib4eBAkcq+pgKeMJH9vjO4Rfey5ujbblB7LKWOkxkmhOEV3uo2FwPntzChOh+nG9GkvXtLNY1CMoTM06bv75DkS9ptloDrtSC3WnyYeybESPMD7fxQI/TSVqbAAAAHQAAAAcAwAAAQAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQBAAAAAAAAAFAAAACoAgAAAQIDBAwAAADQAAAAlAEAAIEp5oYqUSC9CF4ZNrTvtaVfx9GcDQ/aDp7FdtGKvUoXqzoDP1KWt0xf2vhct7PaMgG2P+yna4g2E+OxyhN+djo0Ljsd3bzgFvjKPLzjLIsSXdjCWnY5gZwgtTnp58bFeWwAAAB0AAAAxAAAAAEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAATAAAAL6Vb7ffTvN1MWgtWIMgCE/JFMPw/tM1Jj5bRAYubCm0AAAAAAAAAABQAAAAUAAAAAECAwSvSlxIBBSPmKJW3TsZ72jVZBiHkTve6/cTaeLNvuIRflC2Gw/dbTijN3WgljvNZX8HRQDlrtmTQEgOm9l7Mqkpr3XhyPno8Wqn2PY2GsWn7HLmQhRcFRq9LMDvmw4MpExsAAAAdAAAAMQAAAADAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAEwAAAC+lW+3307zdTFoLViDIAhPyRTD8P7TNSY+W0QGLmwptAAAAAAAAAAAUAAAAFAAAAABAgMEhJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","mMq307rvpww3BxdPm0KCuat1CPoddAl9w/tnc524RivFpWby1PNqd9ymZl6/aFkOGOkeJoqoIZJgn22ff+c9UplIJTKxcIGm5d70ocze3Sx7TCq+A2xixwEsOb/A0831bAAAAHQAAADEAAAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","lLT5TkZ3/7MmNTrG07fwT6CwDZ4una1ok5q6S7EHMBfoBJSNN5XOl3X1Hir/GbWLDs+i2Xrwlu+WjIqNYsaICol14dSQYu1LErxRH0dre4CUBLD7uPMIRUej/0GNt2S8bAAAAHQAAADEAAAAAwAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="]},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ExpectedError":"","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing round change quorum order 2":{"Name":"round change quorum order 2","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":2,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"a7aed8500c01312f41067319c037b4c8d180bbe733a6da1464390394dcd9cc02","InputMessages":[{"Signature":"mMq307rvpww3BxdPm0KCuat1CPoddAl9w/tnc524RivFpWby1PNqd9ymZl6/aFkOGOkeJoqoIZJgn22ff+c9UplIJTKxcIGm5d70ocze3Sx7TCq+A2xixwEsOb/A0831","Signers":[2],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"lYIJUgf2Ib4eBAkcq+pgKeMJH9vjO4Rfey5ujbblB7LKWOkxkmhOEV3uo2FwPntzChOh+nG9GkvXtLNY1CMoTM06bv75DkS9ptloDrtSC3WnyYeybESPMD7fxQI/TSVq","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":1,"RoundChangeJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"lLT5TkZ3/7MmNTrG07fwT6CwDZ4una1ok5q6S7EHMBfoBJSNN5XOl3X1Hir/GbWLDs+i2Xrwlu+WjIqNYsaICol14dSQYu1LErxRH0dre4CUBLD7uPMIRUej/0GNt2S8","Signers":[3],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[{"Signature":"lQjElt78K7bTHVodvLO+4k7x4kU5Zlizab3WFUJDUj/T0NF9qWm5jqrRAI7SJmRPC1n2cr1OXwFAwEbm6y9TT+QYw5s1RtasTiI6krEG/QHWE72iBhwkFLEIAqAG2Y+Z","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":["mMq307rvpww3BxdPm0KCuat1CPoddAl9w/tnc524RivFpWby1PNqd9ymZl6/aFkOGOkeJoqoIZJgn22ff+c9UplIJTKxcIGm5d70ocze3Sx7TCq+A2xixwEsOb/A0831bAAAAHQAAADEAAAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","lYIJUgf2Ib4eBAkcq+pgKeMJH9vjO4Rfey5ujbblB7LKWOkxkmhOEV3uo2FwPntzChOh+nG9GkvXtLNY1CMoTM06bv75DkS9ptloDrtSC3WnyYeybESPMD7fxQI/TSVqbAAAAHQAAAAcAwAAAQAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQBAAAAAAAAAFAAAACoAgAAAQIDBAwAAADQAAAAlAEAAIEp5oYqUSC9CF4ZNrTvtaVfx9GcDQ/aDp7FdtGKvUoXqzoDP1KWt0xf2vhct7PaMgG2P+yna4g2E+OxyhN+djo0Ljsd3bzgFvjKPLzjLIsSXdjCWnY5gZwgtTnp58bFeWwAAAB0AAAAxAAAAAEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAATAAAAL6Vb7ffTvN1MWgtWIMgCE/JFMPw/tM1Jj5bRAYubCm0AAAAAAAAAABQAAAAUAAAAAECAwSvSlxIBBSPmKJW3TsZ72jVZBiHkTve6/cTaeLNvuIRflC2Gw/dbTijN3WgljvNZX8HRQDlrtmTQEgOm9l7Mqkpr3XhyPno8Wqn2PY2GsWn7HLmQhRcFRq9LMDvmw4MpExsAAAAdAAAAMQAAAADAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAEwAAAC+lW+3307zdTFoLViDIAhPyRTD8P7TNSY+W0QGLmwptAAAAAAAAAAAUAAAAFAAAAABAgMEhJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","lLT5TkZ3/7MmNTrG07fwT6CwDZ4una1ok5q6S7EHMBfoBJSNN5XOl3X1Hir/GbWLDs+i2Xrwlu+WjIqNYsaICol14dSQYu1LErxRH0dre4CUBLD7uPMIRUej/0GNt2S8bAAAAHQAAADEAAAAAwAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="]},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ExpectedError":"","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing round change unknown signer":{"Name":"round change unknown signer","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":2,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"ddb0f7e1a8888a8de5295005872d8525d6afea053121993dc65e34fcb7f290b2","InputMessages":[{"Signature":"t3wDXB0dnGx8wigQxQqFyeVgrXkVCQVQYd/gfUA+2qEwQWFGbUn2SES25c9LCXCfBp3r/ZFDj5fUFKT2TNy0+M8elwP42hQb6fdFCQh+L4RJIxSwNBlmxLj8Ftkx9roI","Signers":[5],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[],"ExpectedError":"invalid signed message: msg signature invalid: unknown signer","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing second proposal for round":{"Name":"second proposal for round","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"ce6d77d0602c7a368a6f86a32d70495b47e6d9fcfd2f5ad0d2952a3f5ac963e7","InputMessages":[{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ExpectedError":"invalid signed message: proposal is not valid with current state","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing unknown commit signer":{"Name":"unknown commit signer","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"976cd5cecd58bba892a38ec0ef02b3aed4656fb89fef473d8af78fedf095439d","InputMessages":[{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooE","Signers":[2],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRM","Signers":[3],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"kFGy1aK4Kychuv0xFbXRVjFgDU6hCAaltJ+Nbjsie+2yEmuFSwss4IiyekCVHu+1F9TN31CUv+uZ4FwzPQSkR5oM709TN/61iEcY4j44Zm/OXL8pnEhSipyXSSngprJ6","Signers":[5],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"OutputMessages":[{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"kFGy1aK4Kychuv0xFbXRVjFgDU6hCAaltJ+Nbjsie+2yEmuFSwss4IiyekCVHu+1F9TN31CUv+uZ4FwzPQSkR5oM709TN/61iEcY4j44Zm/OXL8pnEhSipyXSSngprJ6","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"ExpectedError":"invalid signed message: msg signature invalid: unknown signer","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing unknown proposal signer":{"Name":"unknown proposal signer","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"5b18ca0b470208d8d247543306850618f02bddcbaa7c37eb6d5b36eb3accb5fb","InputMessages":[{"Signature":"p2bR+TrhEaOnm+80lhy6TB9ewT98miWqYIYD1nHR85Kh9koM0oYNgngRnXSRgGdEAHhUVdIX43m91q/6jw/+SeDRZx7E99zKcRi0+D+w9G9aZprxMrlQOh1fClthH2OJ","Signers":[5],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":null,"ExpectedError":"invalid signed message: msg signature invalid: unknown signer","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing valid justification":{"Name":"valid justification","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":2,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"4df173f288dd51463fc9277bdf8c1ded677a764992b916729eee9432681e54ce","InputMessages":[{"Signature":"oEQ/+4Yv52mq7qFgQZ0tWaih5VMx6ivO2Bm3rBHtd8MiWIpmrLH0r1kA+YkwbmeLEWn0xyry0wJjdAMl0p+g5A5nbpUpkng+R2PmLsolc1hr3nJMnXebzq0AKfqYLEkN","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":1,"RoundChangeJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"g+23kWE7m/1jehx91y2yqtG5t0sP4ED+2sz2MipAQBG+P3nX7abKV+fAZS/hjhJvAZi70KYcBWNBGMcMiddJRm/5QbJoCUP2qPMQd/yzA9JvknfmdapAOPyXRAMeYyCe","Signers":[2],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":1,"RoundChangeJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"q6Ok2cz9kmwxKRj2bXJ6qUmth4N7o8zqaLHIUGb1i1wASpjuryw/D+3W7qrugenesisgwFH3S8GGI8LVdUA6JGEOSgQ1DHp+e3QbLGMuuiLGDdUKJ8doYLvLIVBR1kmf4u766","Signers":[3],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":1,"RoundChangeJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":[{"Signature":"qODeZIEirgU6ocvZxxk3vTPFp+08hZxB47ZhTtu5coLg9L5MtC794WC94itl+ZbPE8mmbO8sNITtame2WiEEF+qRQztTbrfUXB0bSM1aWJAZoH9YgI0MVht0lTPMYBr9","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":["oEQ/+4Yv52mq7qFgQZ0tWaih5VMx6ivO2Bm3rBHtd8MiWIpmrLH0r1kA+YkwbmeLEWn0xyry0wJjdAMl0p+g5A5nbpUpkng+R2PmLsolc1hr3nJMnXebzq0AKfqYLEkNbAAAAHQAAAAcAwAAAQAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQBAAAAAAAAAFAAAACoAgAAAQIDBAwAAADQAAAAlAEAAIEp5oYqUSC9CF4ZNrTvtaVfx9GcDQ/aDp7FdtGKvUoXqzoDP1KWt0xf2vhct7PaMgG2P+yna4g2E+OxyhN+djo0Ljsd3bzgFvjKPLzjLIsSXdjCWnY5gZwgtTnp58bFeWwAAAB0AAAAxAAAAAEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAATAAAAL6Vb7ffTvN1MWgtWIMgCE/JFMPw/tM1Jj5bRAYubCm0AAAAAAAAAABQAAAAUAAAAAECAwSEnhPcn4cPkyDDrML3nq3WEu40fnfuTScs19kmgbZcMOuVawnZe4TlyWRb1GHLgmcMafsrGhwbwgyT27FOmo+V0J2A7PXyiJ01o8pf0aEGCq32lvqo5yPenHuXeNOqigRsAAAAdAAAAMQAAAACAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAEwAAAC+lW+3307zdTFoLViDIAhPyRTD8P7TNSY+W0QGLmwptAAAAAAAAAAAUAAAAFAAAAABAgMEr0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","g+23kWE7m/1jehx91y2yqtG5t0sP4ED+2sz2MipAQBG+P3nX7abKV+fAZS/hjhJvAZi70KYcBWNBGMcMiddJRm/5QbJoCUP2qPMQd/yzA9JvknfmdapAOPyXRAMeYyCebAAAAHQAAAAcAwAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQBAAAAAAAAAFAAAACoAgAAAQIDBAwAAADQAAAAlAEAAIEp5oYqUSC9CF4ZNrTvtaVfx9GcDQ/aDp7FdtGKvUoXqzoDP1KWt0xf2vhct7PaMgG2P+yna4g2E+OxyhN+djo0Ljsd3bzgFvjKPLzjLIsSXdjCWnY5gZwgtTnp58bFeWwAAAB0AAAAxAAAAAEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAATAAAAL6Vb7ffTvN1MWgtWIMgCE/JFMPw/tM1Jj5bRAYubCm0AAAAAAAAAABQAAAAUAAAAAECAwSEnhPcn4cPkyDDrML3nq3WEu40fnfuTScs19kmgbZcMOuVawnZe4TlyWRb1GHLgmcMafsrGhwbwgyT27FOmo+V0J2A7PXyiJ01o8pf0aEGCq32lvqo5yPenHuXeNOqigRsAAAAdAAAAMQAAAACAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAEwAAAC+lW+3307zdTFoLViDIAhPyRTD8P7TNSY+W0QGLmwptAAAAAAAAAAAUAAAAFAAAAABAgMEr0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","q6Ok2cz9kmwxKRj2bXJ6qUmth4N7o8zqaLHIUGb1i1wASpjuryw/D+3W7qrugenesisgwFH3S8GGI8LVdUA6JGEOSgQ1DHp+e3QbLGMuuiLGDdUKJ8doYLvLIVBR1kmf4u766bAAAAHQAAAAcAwAAAwAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQBAAAAAAAAAFAAAACoAgAAAQIDBAwAAADQAAAAlAEAAIEp5oYqUSC9CF4ZNrTvtaVfx9GcDQ/aDp7FdtGKvUoXqzoDP1KWt0xf2vhct7PaMgG2P+yna4g2E+OxyhN+djo0Ljsd3bzgFvjKPLzjLIsSXdjCWnY5gZwgtTnp58bFeWwAAAB0AAAAxAAAAAEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAATAAAAL6Vb7ffTvN1MWgtWIMgCE/JFMPw/tM1Jj5bRAYubCm0AAAAAAAAAABQAAAAUAAAAAECAwSEnhPcn4cPkyDDrML3nq3WEu40fnfuTScs19kmgbZcMOuVawnZe4TlyWRb1GHLgmcMafsrGhwbwgyT27FOmo+V0J2A7PXyiJ01o8pf0aEGCq32lvqo5yPenHuXeNOqigRsAAAAdAAAAMQAAAACAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAEwAAAC+lW+3307zdTFoLViDIAhPyRTD8P7TNSY+W0QGLmwptAAAAAAAAAAAUAAAAFAAAAABAgMEr0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="]},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"ExpectedError":"","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing wrong commit height":{"Name":"wrong commit height","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"976cd5cecd58bba892a38ec0ef02b3aed4656fb89fef473d8af78fedf095439d","InputMessages":[{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooE","Signers":[2],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRM","Signers":[3],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"mNN2W8lqr6zxmUAyn6ZbAfrW2+BuVQhNKodthTETbZFC8sQMYGWE84OywiZaDqMcBPTQvGuef9ZNrbSWrZCv5oPrFJZWsVKjwFfLOF9JcQJ4cyFk3EoF9dBRmzLb00Mg","Signers":[1],"Message":{"MsgType":2,"Height":10,"Round":1,"Identifier":"AQIDBA==","Root":[171,223,56,59,122,252,254,162,11,93,114,204,119,150,8,230,250,112,249,19,252,223,246,20,151,76,6,90,175,133,17,249],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"OutputMessages":[{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"kFGy1aK4Kychuv0xFbXRVjFgDU6hCAaltJ+Nbjsie+2yEmuFSwss4IiyekCVHu+1F9TN31CUv+uZ4FwzPQSkR5oM709TN/61iEcY4j44Zm/OXL8pnEhSipyXSSngprJ6","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"ExpectedError":"invalid signed message: wrong msg height","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing wrong commit signature":{"Name":"wrong commit signature","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"976cd5cecd58bba892a38ec0ef02b3aed4656fb89fef473d8af78fedf095439d","InputMessages":[{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooE","Signers":[2],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRM","Signers":[3],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"kFGy1aK4Kychuv0xFbXRVjFgDU6hCAaltJ+Nbjsie+2yEmuFSwss4IiyekCVHu+1F9TN31CUv+uZ4FwzPQSkR5oM709TN/61iEcY4j44Zm/OXL8pnEhSipyXSSngprJ6","Signers":[2],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"OutputMessages":[{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},{"Signature":"kFGy1aK4Kychuv0xFbXRVjFgDU6hCAaltJ+Nbjsie+2yEmuFSwss4IiyekCVHu+1F9TN31CUv+uZ4FwzPQSkR5oM709TN/61iEcY4j44Zm/OXL8pnEhSipyXSSngprJ6","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"ExpectedError":"invalid signed message: msg signature invalid: failed to verify signature","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing wrong proposal height":{"Name":"wrong proposal height","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"5b18ca0b470208d8d247543306850618f02bddcbaa7c37eb6d5b36eb3accb5fb","InputMessages":[{"Signature":"k/fLjlW3b3EomFbpA/r8cP9s9b1tmqUPe97nxML9+8YsCZcr5yQktY/YQjDGaHi7ENfMQuViMNTJ+IKLQ4j/wcbRSLuPKH1ciptzdXi3ynYJmcO3ATxlVT9BOcaSNSp+","Signers":[1],"Message":{"MsgType":0,"Height":2,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":null,"ExpectedError":"invalid signed message: wrong msg height","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing wrong proposal sig":{"Name":"wrong proposal sig","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"5b18ca0b470208d8d247543306850618f02bddcbaa7c37eb6d5b36eb3accb5fb","InputMessages":[{"Signature":"p2bR+TrhEaOnm+80lhy6TB9ewT98miWqYIYD1nHR85Kh9koM0oYNgngRnXSRgGdEAHhUVdIX43m91q/6jw/+SeDRZx7E99zKcRi0+D+w9G9aZprxMrlQOh1fClthH2OJ","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":null,"ExpectedError":"invalid signed message: msg signature invalid: failed to verify signature","ExpectedTimerState":null},"*tests.MsgProcessingSpecTest_qbft message processing wrong proposer":{"Name":"wrong proposer","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":null,"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"5b18ca0b470208d8d247543306850618f02bddcbaa7c37eb6d5b36eb3accb5fb","InputMessages":[{"Signature":"p2bR+TrhEaOnm+80lhy6TB9ewT98miWqYIYD1nHR85Kh9koM0oYNgngRnXSRgGdEAHhUVdIX43m91q/6jw/+SeDRZx7E99zKcRi0+D+w9G9aZprxMrlQOh1fClthH2OJ","Signers":[2],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"OutputMessages":null,"ExpectedError":"invalid signed message: proposal leader invalid","ExpectedTimerState":null},"*tests.MsgSpecTest_qbft message commit data nil or len 0":{"Name":"commit data nil or len 0","Messages":[{"Signature":"kFGy1aK4Kychuv0xFbXRVjFgDU6hCAaltJ+Nbjsie+2yEmuFSwss4IiyekCVHu+1F9TN31CUv+uZ4FwzPQSkR5oM709TN/61iEcY4j44Zm/OXL8pnEhSipyXSSngprJ6","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"EncodedMessages":["kFGy1aK4Kychuv0xFbXRVjFgDU6hCAaltJ+Nbjsie+2yEmuFSwss4IiyekCVHu+1F9TN31CUv+uZ4FwzPQSkR5oM709TN/61iEcY4j44Zm/OXL8pnEhSipyXSSngprJ6bAAAAHQAAADEAAAAAQAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"ExpectedRoots":[[136,123,180,37,172,175,172,86,125,95,80,141,218,157,176,63,209,56,46,129,222,11,111,80,221,158,163,28,53,249,155,18]],"ExpectedError":""},"*tests.MsgSpecTest_qbft message duplicate signers":{"Name":"duplicate signers","Messages":[{"Signature":"jrm3ljutzrUPsXYA27nEALtquvp6XA24ASN5Xw5aOuxIO0l0MGRqid3IX5urqjpXFO+MywR20DXdz6r5XFAObp0J1roT8gTKA1iUJb9DFL6IiQo+pF8LvT5KZenNLG0+","Signers":[1,1,2],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"EncodedMessages":null,"ExpectedRoots":null,"ExpectedError":"non unique signer"},"*tests.MsgSpecTest_qbft message get root":{"Name":"get root","Messages":[{"Signature":"gS4+OHR1pCWTWUQdJ2iaKNaJnYErXJPF8pZCoNMYRXg7unJW1jQEPverbtIgy9c4Btg2lCGlYHBGBSDDfxYHkgPD4u0PEnuKSzihJ59QS7vk+zHQGb1SaCWDNt/x7xiy","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":["oR+CDt4M5g2Djv4KS0bU3VWqnntuSpuwVNDTK+abQyKt2JDJIgtbPcBFb2GI4hWSB/RRqcYNrcFWTBVcPY2xb2lRxjsZ6XqLQGMI4CiLCf2ekWtY7WGp90CCngLmj5+UbAAAAHQAAADEAAAAAQAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="]},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"EncodedMessages":null,"ExpectedRoots":[[231,169,90,226,226,144,109,52,210,3,1,186,92,180,51,95,159,110,219,81,173,24,88,246,229,13,42,176,187,236,132,136]],"ExpectedError":""},"*tests.MsgSpecTest_qbft message msg identifier len == 0":{"Name":"msg identifier len == 0","Messages":[{"Signature":"tvcuAMVSIM1aAtylK5xQHpyWNqw7fwujGhp3ar1tE8doRc+J3I56+SE3GrQvS+hkEploHpIrvDh0Qy4o2rLvr7lac/DymMLh+wC+okgTPMq7OZUzHK9RQsGscBdkagmE","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"EncodedMessages":null,"ExpectedRoots":null,"ExpectedError":"message identifier is invalid"},"*tests.MsgSpecTest_qbft message msg identifier nil":{"Name":"msg identifier nil","Messages":[{"Signature":"tvcuAMVSIM1aAtylK5xQHpyWNqw7fwujGhp3ar1tE8doRc+J3I56+SE3GrQvS+hkEploHpIrvDh0Qy4o2rLvr7lac/DymMLh+wC+okgTPMq7OZUzHK9RQsGscBdkagmE","Signers":[1],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":null,"Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"EncodedMessages":null,"ExpectedRoots":null,"ExpectedError":"message identifier is invalid"},"*tests.MsgSpecTest_qbft message msg type unknown":{"Name":"msg type unknown","Messages":[{"Signature":"uZztqJwnhg1AaG9kPqR/jPFGySDo9SYnFCWpSSYSSuP3aSdspfn0hXjGV7VwagmoAbEPkKZ9oGbp8tK1LvohVGYF78hGMjjrNunwoGOiNi28/OZ6I0pwAmtfjNSiU6F/","Signers":[1],"Message":{"MsgType":6,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"EncodedMessages":null,"ExpectedRoots":null,"ExpectedError":"message type is invalid"},"*tests.MsgSpecTest_qbft message multi signers":{"Name":"multi signers","Messages":[{"Signature":"ok+cfOp6t9/h8xvePHOpDCnIif+wMjLeYWhHv5/Csldj3S/G2PgaYaZhxpSKKURCFXRMTSP1OhfbMinxQrnxZYg7SP2C6NDOa/9vV6NLaX/eusH+W91JkAgPd+seFNhr","Signers":[1,2,3],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"EncodedMessages":null,"ExpectedRoots":null,"ExpectedError":""},"*tests.MsgSpecTest_qbft message no signers":{"Name":"no signers","Messages":[{"Signature":"kFGy1aK4Kychuv0xFbXRVjFgDU6hCAaltJ+Nbjsie+2yEmuFSwss4IiyekCVHu+1F9TN31CUv+uZ4FwzPQSkR5oM709TN/61iEcY4j44Zm/OXL8pnEhSipyXSSngprJ6","Signers":null,"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":null}],"EncodedMessages":null,"ExpectedRoots":null,"ExpectedError":"message signers is empty"},"*tests.MsgSpecTest_qbft message prepare data encoding":{"Name":"prepare data encoding","Messages":[{"Signature":"gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5","Signers":[1],"Message":{"MsgType":1,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"EncodedMessages":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBAECAwQFBgcICQECAwQFBgcICQECAwQFBgcICQ=="],"ExpectedRoots":[[48,137,31,143,223,168,186,228,181,203,132,56,87,196,24,162,34,102,222,250,120,170,94,211,148,46,216,99,253,74,116,142]],"ExpectedError":""},"*tests.MsgSpecTest_qbft message propose data encoding":{"Name":"propose data encoding","Messages":[{"Signature":"gS4+OHR1pCWTWUQdJ2iaKNaJnYErXJPF8pZCoNMYRXg7unJW1jQEPverbtIgy9c4Btg2lCGlYHBGBSDDfxYHkgPD4u0PEnuKSzihJ59QS7vk+zHQGb1SaCWDNt/x7xiy","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":["oR+CDt4M5g2Djv4KS0bU3VWqnntuSpuwVNDTK+abQyKt2JDJIgtbPcBFb2GI4hWSB/RRqcYNrcFWTBVcPY2xb2lRxjsZ6XqLQGMI4CiLCf2ekWtY7WGp90CCngLmj5+UbAAAAHQAAADEAAAAAQAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="]},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"EncodedMessages":["gS4+OHR1pCWTWUQdJ2iaKNaJnYErXJPF8pZCoNMYRXg7unJW1jQEPverbtIgy9c4Btg2lCGlYHBGBSDDfxYHkgPD4u0PEnuKSzihJ59QS7vk+zHQGb1SaCWDNt/x7xiybAAAAHQAAABUAgAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAAAYAQAAAQIDBAQAAACBKeaGKlEgvQheGTa077WlX8fRnA0P2g6exXbRir1KF6s6Az9SlrdMX9r4XLez2jIBtj/sp2uINhPjscoTfnY6NC47Hd284Bb4yjy84yyLEl3Ywlp2OYGcILU56efGxXlsAAAAdAAAAMQAAAABAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAEwAAAC+lW+3307zdTFoLViDIAhPyRTD8P7TNSY+W0QGLmwptAAAAAAAAAAAUAAAAFAAAAABAgMEBAAAAKEfgg7eDOYNg47+CktG1N1Vqp57bkqbsFTQ0yvmm0MirdiQySILWz3ARW9hiOIVkgf0UanGDa3BVkwVXD2NsW9pUcY7Gel6i0BjCOAoiwn9npFrWO1hqfdAgp4C5o+flGwAAAB0AAAAxAAAAAEAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAEAAAAAAAAATAAAAL6Vb7ffTvN1MWgtWIMgCE/JFMPw/tM1Jj5bRAYubCm0AAAAAAAAAABQAAAAUAAAAAECAwQBAgMEBQYHCAkBAgMEBQYHCAkBAgMEBQYHCAk="],"ExpectedRoots":[[231,169,90,226,226,144,109,52,210,3,1,186,92,180,51,95,159,110,219,81,173,24,88,246,229,13,42,176,187,236,132,136]],"ExpectedError":""},"*tests.MsgSpecTest_qbft message rc not prev prepared justifications":{"Name":"rc not prev prepared justifications","Messages":[{"Signature":"p5iorFgZ9bI9otv5TyEMkbxcQ5E8+LACHWsh3Fv54KJgoJbCxQ6v47tpF2De+WW3DvCjDTJvi/z4VSQ0H2ZYWEDOo5sfc49F7VAialmFiJ5glQh9D1hTMT2pIfLj30bt","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":10,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"EncodedMessages":null,"ExpectedRoots":null,"ExpectedError":""},"*tests.MsgSpecTest_qbft message rc prev prepared justifications":{"Name":"rc prev prepared justifications","Messages":[{"Signature":"qiIUB/BsJ01sSyLqi7cEyLof+gCpLWjFZVKrREB5CHvN1VjLBCDaoUPnKIQtN2qqB+bQKYHksdMr0HyTckvzugOWZUhd4RgLad+1n9/Dg+y3j80jVLbWHw46bGiug7Ra","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":10,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":1,"RoundChangeJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","hJ4T3J+HD5Mgw6zC956t1hLuNH537k0nLNfZJoG2XDDrlWsJ2XuE5clkW9Rhy4JnDGn7KxocG8IMk9uxTpqPldCdgOz18oidNaPKX9GhBgqt9pb6qOcj3px7l3jTqooEbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","r0pcSAQUj5iiVt07Ge9o1WQYh5E73uv3E2nizb7iEX5QthsP3W04ozd1oJY7zWV/B0UA5a7Zk0BIDpvZezKpKa914cj56PFqp9j2NhrFp+xy5kIUXBUavSzA75sODKRMbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"EncodedMessages":null,"ExpectedRoots":null,"ExpectedError":""},"*tests.MsgSpecTest_qbft message rc prev prepared no justifications":{"Name":"rc prev prepared no justifications","Messages":[{"Signature":"gasMtJx89hTgfx9GW4FOWy/j4j3Eck+lhCp2ZpTtkyLF6d4cENqoD2A+9O1azqcsCePYebU/ITJd3XyRDm0c6aARJYG/d09QdwHREYu98MPBcMoT7yhBgj5X8z+H9ABs","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":10,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":1,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"EncodedMessages":null,"ExpectedRoots":null,"ExpectedError":"round change justification invalid"},"*tests.MsgSpecTest_qbft message round change data encoding":{"Name":"round change data encoding","Messages":[{"Signature":"lX7U4u+bPdR8CzavKWHnutSVVNjDPj7w5+LtRDuG9fS5sCn4TYUW481dDvCMqmshDlGn0OBmhL6FjBcqL13VtctA/8ub1i9vtNGrCY5Aq1rdq+u52t94hmAxa71MQLx8","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":2,"RoundChangeJustification":["gAnu0h9b+GhMBWRD5uGwXQLZU+IkR7T5Han28nJNGHmWkWOOmJloo2EsNkho6+tEDcKeEKScBqDMt0Ms+GE7aUfTkeP8K8UDOm/lIlv6SEzFBlTgSaJTRmuybOX1x+EpbAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","g/mKHqmW47ekvXEY62N5fKTQ9oHBU5QKSZxjehyH9MZ8X7t4egnx/1F3SU39doGDA+eJlA2/r0rw2QzdjPbXyBY16RrkoAqqOgItSNzsCEvJl3pEsE9MVp8Ry8r3vs9TbAAAAHQAAADEAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA==","j7thZ1r0Gs6AEJKBXe2IpYOkIQrN1ggSWQjb6Nz7KLazl+4Iikq9xEPjgs/oiNbYFB5BWknHZR1t3QMWWu/45saLm+WS289AZ503i9GVNM1Rcs+O845PvoFYjWstIUOBbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"EncodedMessages":["lX7U4u+bPdR8CzavKWHnutSVVNjDPj7w5+LtRDuG9fS5sCn4TYUW481dDvCMqmshDlGn0OBmhL6FjBcqL13VtctA/8ub1i9vtNGrCY5Aq1rdq+u52t94hmAxa71MQLx8bAAAAHQAAAAcAwAAAQAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQCAAAAAAAAAFAAAACoAgAAAQIDBAwAAADQAAAAlAEAAIAJ7tIfW/hoTAVkQ+bhsF0C2VPiJEe0+R2p9vJyTRh5lpFjjpiZaKNhLDZIaOvrRA3CnhCknAagzLdDLPhhO2lH05Hj/CvFAzpv5SJb+khMxQZU4EmiU0Zrsmzl9cfhKWwAAAB0AAAAxAAAAAEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAIAAAAAAAAATAAAAL6Vb7ffTvN1MWgtWIMgCE/JFMPw/tM1Jj5bRAYubCm0AAAAAAAAAABQAAAAUAAAAAECAwSD+YoeqZbjt6S9cRjrY3l8pND2gcFTlApJnGN6HIf0xnxfu3h6CfH/UXdJTf12gYMD54mUDb+vSvDZDN2M9tfIFjXpGuSgCqo6Ai1I3OwIS8mXekSwT0xWnxHLyve+z1NsAAAAdAAAAMQAAAACAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAACAAAAAAAAAEwAAAC+lW+3307zdTFoLViDIAhPyRTD8P7TNSY+W0QGLmwptAAAAAAAAAAAUAAAAFAAAAABAgMEj7thZ1r0Gs6AEJKBXe2IpYOkIQrN1ggSWQjb6Nz7KLazl+4Iikq9xEPjgs/oiNbYFB5BWknHZR1t3QMWWu/45saLm+WS289AZ503i9GVNM1Rcs+O845PvoFYjWstIUOBbAAAAHQAAADEAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBAECAwQFBgcICQECAwQFBgcICQECAwQFBgcICQ=="],"ExpectedRoots":[[255,37,105,150,151,199,216,111,6,29,71,44,70,211,148,250,182,62,67,71,231,157,61,215,80,166,24,71,71,4,48,19]],"ExpectedError":""},"*tests.MsgSpecTest_qbft message signed message encoding":{"Name":"signed message encoding","Messages":[{"Signature":"gS4+OHR1pCWTWUQdJ2iaKNaJnYErXJPF8pZCoNMYRXg7unJW1jQEPverbtIgy9c4Btg2lCGlYHBGBSDDfxYHkgPD4u0PEnuKSzihJ59QS7vk+zHQGb1SaCWDNt/x7xiy","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":["gSnmhipRIL0IXhk2tO+1pV/H0ZwND9oOnsgenesis0Yq9SherOgM/Upa3TF/a+Fy3s9oyAbY/7KdriDYT47HKE352OjQuOx3dvOAW+Mo8vOMsixJd2MJadjmBnCC1OennxsV5bAAAAHQAAADEAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="],"PrepareJustification":["oR+CDt4M5g2Djv4KS0bU3VWqnntuSpuwVNDTK+abQyKt2JDJIgtbPcBFb2GI4hWSB/RRqcYNrcFWTBVcPY2xb2lRxjsZ6XqLQGMI4CiLCf2ekWtY7WGp90CCngLmj5+UbAAAAHQAAADEAAAAAQAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAABQAAAAAQIDBA=="]},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"EncodedMessages":["gS4+OHR1pCWTWUQdJ2iaKNaJnYErXJPF8pZCoNMYRXg7unJW1jQEPverbtIgy9c4Btg2lCGlYHBGBSDDfxYHkgPD4u0PEnuKSzihJ59QS7vk+zHQGb1SaCWDNt/x7xiybAAAAHQAAABUAgAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAABMAAAAvpVvt99O83UxaC1YgyAIT8kUw/D+0zUmPltEBi5sKbQAAAAAAAAAAFAAAAAYAQAAAQIDBAQAAACBKeaGKlEgvQheGTa077WlX8fRnA0P2g6exXbRir1KF6s6Az9SlrdMX9r4XLez2jIBtj/sp2uINhPjscoTfnY6NC47Hd284Bb4yjy84yyLEl3Ywlp2OYGcILU56efGxXlsAAAAdAAAAMQAAAABAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAEwAAAC+lW+3307zdTFoLViDIAhPyRTD8P7TNSY+W0QGLmwptAAAAAAAAAAAUAAAAFAAAAABAgMEBAAAAKEfgg7eDOYNg47+CktG1N1Vqp57bkqbsFTQ0yvmm0MirdiQySILWz3ARW9hiOIVkgf0UanGDa3BVkwVXD2NsW9pUcY7Gel6i0BjCOAoiwn9npFrWO1hqfdAgp4C5o+flGwAAAB0AAAAxAAAAAEAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAEAAAAAAAAATAAAAL6Vb7ffTvN1MWgtWIMgCE/JFMPw/tM1Jj5bRAYubCm0AAAAAAAAAABQAAAAUAAAAAECAwQBAgMEBQYHCAkBAgMEBQYHCAkBAgMEBQYHCAk="],"ExpectedRoots":null,"ExpectedError":""},"*tests.MsgSpecTest_qbft message signer 0":{"Name":"signer 0","Messages":[{"Signature":"ok+cfOp6t9/h8xvePHOpDCnIif+wMjLeYWhHv5/Csldj3S/G2PgaYaZhxpSKKURCFXRMTSP1OhfbMinxQrnxZYg7SP2C6NDOa/9vV6NLaX/eusH+W91JkAgPd+seFNhr","Signers":[1,2,0],"Message":{"MsgType":2,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"}],"EncodedMessages":null,"ExpectedRoots":null,"ExpectedError":"signer ID 0 not allowed"},"*tests.RoundRobinSpecTest_qbft round robin 10 member committee":{"Name":"10 member committee","Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"uaw8M/4ZQSTsUSNYBhD6l6Czq7ScOaI0hAQhUIwIxCsJgy0Pm0FjXeul8TMDRmHx","Committee":[{"OperatorID":1,"PubKey":"uaw8M/4ZQSTsUSNYBhD6l6Czq7ScOaI0hAQhUIwIxCsJgy0Pm0FjXeul8TMDRmHx"},{"OperatorID":2,"PubKey":"kz1pTGkkb5iqKz1t524qR2rdHuj5H6A5SbEVvHoPZ3u5ZGIgBH/krSlDVUVCQlID"},{"OperatorID":3,"PubKey":"oa8IOuuo3GEYpGecefYZvvNk1VYbI8fxLPDSfgQIMZu+/rxyyutWy8+21ul3ilKG"},{"OperatorID":4,"PubKey":"rbbPE1h+tjwTMBvXBq0uQeiY7zjgW8F3SHU/GbiTh6NSfksUWkw4+ePzXeQF/QdH"},{"OperatorID":5,"PubKey":"ovesq67A79GGQk7tkM5WjCDtuuuf9+xT9fGA5wk3pppddGByCSve0md4ED5rGJNo"},{"OperatorID":6,"PubKey":"k0G6HgfsQWNlvKiso3P/s80hTVrPnkjyTPrc0+2uB+PaplJffnFvSdmjkBNW4Mye"},{"OperatorID":7,"PubKey":"jKJiWGQ9GMT6EaPHMt+EWZKfacZB1sXPUXgWYugOJa3hMm1CbTGoRXXnYRS8yz/R"},{"OperatorID":8,"PubKey":"q0rU1AgNbIxwQ0gfqb5DpEbdriONEftFDWGhOesWB/KjAmm7SijrQkMfybi/qNAt"},{"OperatorID":9,"PubKey":"obBtOlWzBdTeziCtVY5FteuDACOmXzBRXfZONryRSaDgvfQlc01s+Qewj67gYRtH"},{"OperatorID":10,"PubKey":"tJ7NtDheMBJsjieXeanh6kC+9ONNv116vN0genesisxyqUfEqqfST3Ku/Vx3rhBvtAidF"}],"Quorum":7,"PartialQuorum":4,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"Heights":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99],"Rounds":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99],"Proposers":[1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8]},"*tests.RoundRobinSpecTest_qbft round robin 13 member committee":{"Name":"13 member committee","Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"hI5zYqHx/eeuUNu47igpgY80yHA2Te2lJxNAy4TZZWIGVNVENfV4msUv+lRUKazt","Committee":[{"OperatorID":1,"PubKey":"hI5zYqHx/eeuUNu47igpgY80yHA2Te2lJxNAy4TZZWIGVNVENfV4msUv+lRUKazt"},{"OperatorID":2,"PubKey":"gLOhjAC9gn7p3Anu5jV1qhZHsjpE2kaCKlufbGqWLkGk2d8Zud87XprQaPd6yx9Z"},{"OperatorID":3,"PubKey":"hdGTJS5FHnymzBPtOa2J8NFZgkuTSNL6bRlcnMQ+3Yc42m0YCfUMme7pVRZqbpTZ"},{"OperatorID":4,"PubKey":"segmLvr3PGJeNMUmR3/KgSXx5ghNACzwU8EdOrcJWMrHC18g2aLLFE+g8ewHyNnz"},{"OperatorID":5,"PubKey":"kNPPyMK8POfloYXlgcsmadBB2meJ6JyL0juCIYFy0JhMDzrcrLz/hrlm044VfnNb"},{"OperatorID":6,"PubKey":"inPM4U47OFEcVAewYzVFUKPrWUBbtZAyURSQ61zC1XxKMXU3d95zPfyY5vAyqIa3"},{"OperatorID":7,"PubKey":"jp+9nrZ8sYqDz0AxKKBxD6HXkQc6wAC4Lg8mq7+LNJr7C9v/fcohAk468TIL0o3j"},{"OperatorID":8,"PubKey":"kdu3pEx4bR4kiUa7++Zq9MgbC6iLzVjSwOj6Aui8FYCtxKndIaKlJ6kZo9/Y2Ddo"},{"OperatorID":9,"PubKey":"mHjlwu4h7wD7ddCACZHbzNzeNV5gE6q0m26NM0c10+ZKnfe1nPvrAaWNat1/L0WP"},{"OperatorID":10,"PubKey":"kF19qpdOmicblY+rPEzD8J6ONES2rkdid7enfjhYI5fmqVtOPOrBDfUSyuPfOfoj"},{"OperatorID":11,"PubKey":"tnj/6m3UVXaCBWL7SQ7Wqjrdhv/VtD2DxC0YHujmalndSaGwm8sv7VZK0L8xaowq"},{"OperatorID":12,"PubKey":"lsQeEx+FxRb2yCq9hgTcgaNUVhSxBJg9H7zW4qADv76cPGPBBQKD/Z/v34MPglL3"},{"OperatorID":13,"PubKey":"p0Ku0QA3sia0RoXTDFjemn3CYyR7hYmehxQc0991MyNVwj3Kh8y6G5mq8H8HiIim"}],"Quorum":9,"PartialQuorum":5,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"Heights":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99],"Rounds":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99],"Proposers":[1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3,4,5,6,7,8,9,10,11,12,13,1,2,3]},"*tests.RoundRobinSpecTest_qbft round robin 4 member committee":{"Name":"4 member committee","Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"Heights":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99],"Rounds":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99],"Proposers":[1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2]},"*tests.RoundRobinSpecTest_qbft round robin 7 member committee":{"Name":"7 member committee","Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"pgeB9Ugap70IWdx7qiW7a/5Sui+4l3+5o8E9G7Jwft5FpQ//kRMQ8JHjF1CS3Ayd","Committee":[{"OperatorID":1,"PubKey":"pgeB9Ugap70IWdx7qiW7a/5Sui+4l3+5o8E9G7Jwft5FpQ//kRMQ8JHjF1CS3Ayd"},{"OperatorID":2,"PubKey":"jXMElrInKT59Ivf870VECBSYqoRdTtqhs7Or/D+Qc2MY93LkXDd6Gk5EPgenesistvwkR"},{"OperatorID":3,"PubKey":"rkjWpS2fbSz6YIt7WZh1uz/Ta5PIUobPIHtkeGBU4ThuB7FiNUTE5z1OvVd59zb/"},{"OperatorID":4,"PubKey":"uUAZ/1poWAPUaMIEoLiaOBdsfoYOi0UyOIzBZ2ubkkhvnlx1KQMnNYKACivwHB95"},{"OperatorID":5,"PubKey":"goikQONEw8kSjkdq2ejAonhyRiZEYfKJ29AhfksG45MjLCSCYKjtp3Uv7sPcgXpE"},{"OperatorID":6,"PubKey":"t7peJG6pEiaHaGK5C1xgtZyEv5P6Hbncm4LXr/0rielwAtcEC2obSlUbuBKcfEQH"},{"OperatorID":7,"PubKey":"lEKsE9YbFjwNewZRv5c/1jTXFaGJudKyyBP47EoWVsDtEmsINEmYmBbObebc/x6Y"}],"Quorum":5,"PartialQuorum":3,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"Heights":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,70,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,81,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,82,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,83,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,85,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,86,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,87,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,88,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,89,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,91,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,93,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,94,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,95,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,96,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,98,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99],"Rounds":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99],"Proposers":[1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2]},"*timeout.SpecTest_qbft timeout round 1":{"Name":"round 1","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":1,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":{"Signature":"o2PMqg1gJ2/lNK/e0Q3+aEulAnVsMR5aJzlYoN3/qjWSRB6qDGguAKX0KSAzgQgdFAedi0V6Fwj+7hOd/JtK5eBmLSdGfUAzdAfpcN8MUo17YTkRoDyaOPPj+Wz9nbZ6","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":1,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"ddb0f7e1a8888a8de5295005872d8525d6afea053121993dc65e34fcb7f290b2","OutputMessages":[{"Signature":"k4vgNWGo1HFng878a0/EORcr5VXfows36NJD9BzNfpyTrFN8QJWn4dFbJoqGabDWGTK1x2HCcDl92AAHNqgcN5qn2453TUjVKqjh3GPYm+VK05xNGMPQCfTvM93/xRFo","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"DataRound":0,"RoundChangeJustification":[],"PrepareJustification":[]},"FullData":null}],"ExpectedTimerState":{"Timeouts":1,"Round":2},"ExpectedError":""},"*timeout.SpecTest_qbft timeout round 2":{"Name":"round 2","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":2,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":{"Signature":"gztz02gJCBBld01h2Pb/7pG5BvLmymJYG+fOGi8fCq81dVcUcoNMZmCvloEyiT+OD5Cl1b+v6OaSbkM80leaeENvDl8G377UXnKTI4wY/74ve7a7iZgSLRf/x7fNM7fB","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":2,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"beaef03728ef5dadfd5daf11046923930e787f0d77b824326bd7ec65c2338b45","OutputMessages":[{"Signature":"gnKR6z179BmXvcPrkTLUj8AIB0o/CUiauMr7oNZH+ROBcUrga0fnhITLFKso9axqAlb5y2eApgqt9VcN5PSw+ZJQnsL4PVwaKPdpEeYkGNc2FFC2emiAm/N3xKEB5euh","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":3,"Identifier":"AQIDBA==","Root":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"DataRound":0,"RoundChangeJustification":[],"PrepareJustification":[]},"FullData":null}],"ExpectedTimerState":{"Timeouts":1,"Round":3},"ExpectedError":""},"*timeout.SpecTest_qbft timeout round 20":{"Name":"round 20","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":20,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":{"Signature":"gnjRmCfV+93Udx6vsP6kRZk09WJNRc4sOwyV1HhFLnutkjXL+wodu0BYBuQ6NMFqBrPKWLyDBs80pgenesisnUhRck3TD8OdBlL8KxbKGARCNkVZFNlA95vvwu4cHxsiVSPcV","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":20,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"d676b1182d7b6a8fabcdab524c90fceb308fccf55aa0215925802132ac02ee25","OutputMessages":[{"Signature":"owNIqDVHtOpxpLX0E5trDg8O128/XtjYH9XMkF2DjF7t3elGqURAIK1//+IdC+VyAlfQxrtinik03EhPFx4+ncTzCb1oMqnHMK5u8+2kwjK/rw0r+/xCFgvMJnp9QNFn","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":21,"Identifier":"AQIDBA==","Root":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"DataRound":0,"RoundChangeJustification":[],"PrepareJustification":[]},"FullData":null}],"ExpectedTimerState":{"Timeouts":1,"Round":21},"ExpectedError":""},"*timeout.SpecTest_qbft timeout round 3":{"Name":"round 3","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":3,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":{"Signature":"mLM16I0Xwhi58vLB+Hni7BYyZHYTRhoOt6Py11BwUWrIwM129hcqKntk4kwbF+b+GYUkrONt79QLYc5ztMC2oL+bVgGzq6K/Ro5/jRaIa+7TNB/0ZDEAV5Jt/2XRwHoL","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":3,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"71f78b5d9365cb66875cc81bc984a0d0a8fd9c683243a981de397adabd238f7f","OutputMessages":[{"Signature":"ijmsIOg8RDn+9DNN8dC7iZNwL1y/psWcOtnpj6FQblUIYXwN4Kgzy3tDsRxeWCteFeCGXuRzp+tZrwuW4W85kLHT7fubx3Coy+0Rkh4B3TwETtLujrsAffuRMp8iBE8K","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":4,"Identifier":"AQIDBA==","Root":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"DataRound":0,"RoundChangeJustification":[],"PrepareJustification":[]},"FullData":null}],"ExpectedTimerState":{"Timeouts":1,"Round":4},"ExpectedError":""},"*timeout.SpecTest_qbft timeout round 5":{"Name":"round 5","Pre":{"State":{"Share":{"OperatorID":1,"ValidatorPubKey":"joAGZVGoGzGCWHCe2vfdH2PNaGoOTbiym7t6z+ZWCGd69aUn2USO5Hg1SF4CtQvA","SharePubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn","Committee":[{"OperatorID":1,"PubKey":"l9lKgR1kSTYFKp0tSs1kcYl0z2eNvv0mcyTI6fjnA0pKa32HeeJ6AZU4w8Qlw+Xn"},{"OperatorID":2,"PubKey":"przr4wl9dBcbQMcSoDHOsDcds9PEAs8s5pG5Eg87q3XU1W36DzdZFUSZm/GMU1Pt"},{"OperatorID":3,"PubKey":"gJDgt2ZqRezF1O90GKyZ8J5sskQCn+pqCn/Mvp7gi8U53g36Zr5rq8hJPdmd0amN"},{"OperatorID":4,"PubKey":"p8CidrcKXuM5XH1tJlXtYFKKolLU0h7KX8xSI+UMxCvRaLKAq3q1MXNU3d/PPfnk"}],"Quorum":3,"PartialQuorum":2,"DomainType":[0,0,3,1],"FeeRecipientAddress":[83,89,83,181,166,4,0,116,148,140,241,133,234,167,210,171,189,102,128,143],"Graffiti":null},"ID":"AQIDBA==","Round":5,"Height":0,"LastPreparedRound":0,"LastPreparedValue":null,"ProposalAcceptedForCurrentRound":{"Signature":"q8R98okpfDj5G/JLg1oLEbuVamFDVoF+U2iD5c3mbxQbsq0mhCi9MjSvcEuUosYlCZrbHr1mEA2fkF5GYF9737XAGGVwOmQUKdJKYudPMyQRJwP7Gfb4GStBTiVXLR4i","Signers":[1],"Message":{"MsgType":0,"Height":0,"Round":5,"Identifier":"AQIDBA==","Root":[190,149,111,183,223,78,243,117,49,104,45,88,131,32,8,79,201,20,195,240,254,211,53,38,62,91,68,6,46,108,41,180],"DataRound":0,"RoundChangeJustification":null,"PrepareJustification":null},"FullData":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"Decided":false,"DecidedValue":null,"ProposeContainer":{"Msgs":{}},"PrepareContainer":{"Msgs":{}},"CommitContainer":{"Msgs":{}},"RoundChangeContainer":{"Msgs":{}}},"StartValue":"AQIDBAUGBwgJAQIDBAUGBwgJAQIDBAUGBwgJ"},"PostRoot":"da48cb7ea2776bbef227ff0da21527f51ff62239b411aa150cb72bb3129155ea","OutputMessages":[{"Signature":"ogsQ1onldfwZF60J1dnA7h3sHbIYirn6g9/Hs0eZL54ycGxet+T6slC94vl0E9ByGQ1lyJaFIjwGG4t30/gnV5fPKBSVusgkLfz6MLxJsKHqlZs5qqTfb84YVtmALyA5","Signers":[1],"Message":{"MsgType":3,"Height":0,"Round":6,"Identifier":"AQIDBA==","Root":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"DataRound":0,"RoundChangeJustification":[],"PrepareJustification":[]},"FullData":null}],"ExpectedTimerState":{"Timeouts":1,"Round":6},"ExpectedError":""}} \ No newline at end of file diff --git a/protocol/genesis/qbft/spectest/timeout_type.go b/protocol/genesis/qbft/spectest/timeout_type.go deleted file mode 100644 index d3c654a65d..0000000000 --- a/protocol/genesis/qbft/spectest/timeout_type.go +++ /dev/null @@ -1,62 +0,0 @@ -package qbft - -import ( - "encoding/hex" - "fmt" - "testing" - - "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - "github.com/ssvlabs/ssv-spec-pre-cc/types/testingutils" - - "github.com/ssvlabs/ssv/logging" - "github.com/ssvlabs/ssv/protocol/genesis/qbft/instance" - "github.com/ssvlabs/ssv/protocol/genesis/qbft/roundtimer" - - "github.com/stretchr/testify/require" -) - -type SpecTest struct { - Name string - Pre *instance.Instance - PostRoot string - OutputMessages []*qbft.SignedMessage - ExpectedTimerState *testingutils.TimerState - ExpectedError string -} - -func RunTimeout(t *testing.T, test *SpecTest) { - logger := logging.TestLogger(t) - err := test.Pre.UponRoundTimeout(logger) - - if len(test.ExpectedError) != 0 { - require.EqualError(t, err, test.ExpectedError) - } else { - require.NoError(t, err) - } - - // test calling timeout - timer, ok := test.Pre.GetConfig().GetTimer().(*roundtimer.TestQBFTTimer) - require.True(t, ok) - require.Equal(t, test.ExpectedTimerState.Timeouts, timer.State.Timeouts) - require.Equal(t, test.ExpectedTimerState.Round, timer.State.Round) - - // test output message - broadcastedMsgs := test.Pre.GetConfig().GetNetwork().(*testingutils.TestingNetwork).BroadcastedMsgs - if len(test.OutputMessages) > 0 || len(broadcastedMsgs) > 0 { - require.Len(t, broadcastedMsgs, len(test.OutputMessages)) - - for i, msg := range test.OutputMessages { - r1, _ := msg.GetRoot() - - msg2 := &qbft.SignedMessage{} - require.NoError(t, msg2.Decode(broadcastedMsgs[i].Data)) - r2, _ := msg2.GetRoot() - - require.EqualValuesf(t, r1, r2, fmt.Sprintf("output msg %d roots not equal", i)) - } - } - - postRoot, err := test.Pre.State.GetRoot() - require.NoError(t, err) - require.EqualValuesf(t, test.PostRoot, hex.EncodeToString(postRoot[:]), "post root not valid") -} diff --git a/protocol/genesis/qbft/storage/ibft_store.go b/protocol/genesis/qbft/storage/ibft_store.go deleted file mode 100644 index 1d44b4fa91..0000000000 --- a/protocol/genesis/qbft/storage/ibft_store.go +++ /dev/null @@ -1,54 +0,0 @@ -package qbftstorage - -import ( - "encoding/json" - - "go.uber.org/zap" - - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" -) - -// StoredInstance contains instance state alongside with a decided message (aggregated commits). -type StoredInstance struct { - State *genesisspecqbft.State - DecidedMessage *genesisspecqbft.SignedMessage -} - -// Encode returns a StoredInstance encoded bytes or error. -func (si *StoredInstance) Encode() ([]byte, error) { - return json.Marshal(si) -} - -// Decode returns error if decoding failed. -func (si *StoredInstance) Decode(data []byte) error { - return json.Unmarshal(data, &si) -} - -// InstanceStore manages instance data. -type InstanceStore interface { - // GetHighestInstance returns the highest instance for the given identifier. - GetHighestInstance(identifier []byte) (*StoredInstance, error) - - // GetInstancesInRange returns historical instances in the given range. - GetInstancesInRange(identifier []byte, from genesisspecqbft.Height, to genesisspecqbft.Height) ([]*StoredInstance, error) - - // SaveInstance updates/inserts the given instance to it's identifier's history. - SaveInstance(instance *StoredInstance) error - - // SaveHighestInstance saves the given instance as the highest of it's identifier. - SaveHighestInstance(instance *StoredInstance) error - - // SaveHighestAndHistoricalInstance saves the given instance as both the highest and historical. - SaveHighestAndHistoricalInstance(instance *StoredInstance) error - - // GetInstance returns an historical instance for the given identifier and height. - GetInstance(identifier []byte, height genesisspecqbft.Height) (*StoredInstance, error) - - // CleanAllInstances removes all historical and highest instances for the given identifier. - CleanAllInstances(logger *zap.Logger, msgID []byte) error -} - -// QBFTStore is the store used by QBFT components -type QBFTStore interface { - InstanceStore -} diff --git a/protocol/genesis/qbft/testing/storage.go b/protocol/genesis/qbft/testing/storage.go deleted file mode 100644 index 01e94b6b1d..0000000000 --- a/protocol/genesis/qbft/testing/storage.go +++ /dev/null @@ -1,43 +0,0 @@ -package testing - -import ( - "context" - "sync" - - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - genesisqbftstorage "github.com/ssvlabs/ssv/ibft/genesisstorage" - "go.uber.org/zap" - - "github.com/ssvlabs/ssv/storage/basedb" - "github.com/ssvlabs/ssv/storage/kv" -) - -var db basedb.Database -var dbOnce sync.Once - -func getDB(logger *zap.Logger) basedb.Database { - dbOnce.Do(func() { - dbInstance, err := kv.NewInMemory(logger, basedb.Options{ - Ctx: context.TODO(), - }) - if err != nil { - panic(err) - } - db = dbInstance - }) - return db -} - -var allRoles = []genesisspectypes.BeaconRole{ - genesisspectypes.BNRoleAttester, - genesisspectypes.BNRoleProposer, - genesisspectypes.BNRoleAggregator, - genesisspectypes.BNRoleSyncCommittee, - genesisspectypes.BNRoleSyncCommitteeContribution, - genesisspectypes.BNRoleValidatorRegistration, - genesisspectypes.BNRoleVoluntaryExit, -} - -func TestingStores(logger *zap.Logger) *genesisqbftstorage.QBFTStores { - return genesisqbftstorage.NewStoresFromRoles(getDB(logger), allRoles...) -} diff --git a/protocol/genesis/qbft/testing/utils.go b/protocol/genesis/qbft/testing/utils.go deleted file mode 100644 index 995f099039..0000000000 --- a/protocol/genesis/qbft/testing/utils.go +++ /dev/null @@ -1,93 +0,0 @@ -package testing - -import ( - "bytes" - - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - "github.com/ssvlabs/ssv-spec-pre-cc/types/testingutils" - - "github.com/pkg/errors" - "go.uber.org/zap" - - "github.com/ssvlabs/ssv/protocol/genesis/qbft" - "github.com/ssvlabs/ssv/protocol/genesis/qbft/controller" - "github.com/ssvlabs/ssv/protocol/genesis/qbft/roundtimer" -) - -var TestingConfig = func(logger *zap.Logger, keySet *testingutils.TestKeySet, role genesisspectypes.BeaconRole) *qbft.Config { - return &qbft.Config{ - Signer: testingutils.NewTestingKeyManager(), - SigningPK: keySet.Shares[1].GetPublicKey().Serialize(), - Domain: testingutils.TestingSSVDomainType, - ValueCheckF: func(data []byte) error { - if bytes.Equal(data, TestingInvalidValueCheck) { - return errors.New("invalid value") - } - - // as a base validation we do not accept nil values - if len(data) == 0 { - return errors.New("invalid value") - } - return nil - }, - ProposerF: func(state *genesisspecqbft.State, round genesisspecqbft.Round) genesisspectypes.OperatorID { - return 1 - }, - Storage: TestingStores(logger).Get(role), - Network: testingutils.NewTestingNetwork(), - Timer: roundtimer.NewTestingTimer(), - } -} - -var TestingInvalidValueCheck = []byte{1, 1, 1, 1} - -var TestingShare = func(keysSet *testingutils.TestKeySet) *genesisspectypes.Share { - return &genesisspectypes.Share{ - OperatorID: 1, - ValidatorPubKey: keysSet.ValidatorPK.Serialize(), - SharePubKey: keysSet.Shares[1].GetPublicKey().Serialize(), - DomainType: testingutils.TestingSSVDomainType, - Quorum: keysSet.Threshold, - PartialQuorum: keysSet.PartialThreshold, - Committee: keysSet.Committee(), - } -} - -var BaseInstance = func() *genesisspecqbft.Instance { - return baseInstance(TestingShare(testingutils.Testing4SharesSet()), testingutils.Testing4SharesSet(), []byte{1, 2, 3, 4}) -} - -var SevenOperatorsInstance = func() *genesisspecqbft.Instance { - return baseInstance(TestingShare(testingutils.Testing7SharesSet()), testingutils.Testing7SharesSet(), []byte{1, 2, 3, 4}) -} - -var TenOperatorsInstance = func() *genesisspecqbft.Instance { - return baseInstance(TestingShare(testingutils.Testing10SharesSet()), testingutils.Testing10SharesSet(), []byte{1, 2, 3, 4}) -} - -var ThirteenOperatorsInstance = func() *genesisspecqbft.Instance { - return baseInstance(TestingShare(testingutils.Testing13SharesSet()), testingutils.Testing13SharesSet(), []byte{1, 2, 3, 4}) -} - -var baseInstance = func(share *genesisspectypes.Share, keySet *testingutils.TestKeySet, identifier []byte) *genesisspecqbft.Instance { - ret := genesisspecqbft.NewInstance(testingutils.TestingConfig(keySet), share, identifier, genesisspecqbft.FirstHeight) - ret.StartValue = []byte{1, 2, 3, 4} - return ret -} - -func NewTestingQBFTController( - identifier []byte, - share *genesisspectypes.Share, - config qbft.IConfig, - fullNode bool, -) *controller.Controller { - ctrl := controller.NewController( - identifier, - share, - config, - fullNode, - ) - ctrl.StoredInstances = make(controller.InstanceContainer, 0, controller.InstanceContainerTestCapacity) - return ctrl -} diff --git a/protocol/genesis/qbft/testing_utils.go b/protocol/genesis/qbft/testing_utils.go deleted file mode 100644 index 8d83b52e34..0000000000 --- a/protocol/genesis/qbft/testing_utils.go +++ /dev/null @@ -1,109 +0,0 @@ -package qbft - -import ( - "github.com/attestantio/go-eth2-client/spec/phase0" - "github.com/herumi/bls-eth-go-binary/bls" - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - "github.com/ssvlabs/ssv-spec-pre-cc/types" - "github.com/ssvlabs/ssv-spec-pre-cc/types/testingutils" -) - -var TestingMessage = &genesisspecqbft.Message{ - MsgType: genesisspecqbft.ProposalMsgType, - Height: genesisspecqbft.FirstHeight, - Round: genesisspecqbft.FirstRound, - Identifier: []byte{1, 2, 3, 4}, - Root: [32]byte{1, 2, 3, 4}, -} - -var TestingSignedMsg = func() *genesisspecqbft.SignedMessage { - return SignMsg(TestingSK, 1, TestingMessage) -}() - -var SignMsg = func(sk *bls.SecretKey, id types.OperatorID, msg *genesisspecqbft.Message) *genesisspecqbft.SignedMessage { - domain := testingutils.TestingSSVDomainType - sigType := types.QBFTSignatureType - - r, _ := types.ComputeSigningRoot(msg, types.ComputeSignatureDomain(domain, sigType)) - sig := sk.SignByte(r[:]) - - return &genesisspecqbft.SignedMessage{ - Message: *msg, - Signers: []types.OperatorID{id}, - Signature: sig.Serialize(), - } -} - -var TestingSK = func() *bls.SecretKey { - types.InitBLS() - ret := &bls.SecretKey{} - ret.SetByCSPRNG() - return ret -}() - -var testingValidatorPK = phase0.BLSPubKey{1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4} - -var testingShare = &types.Share{ - OperatorID: 1, - ValidatorPubKey: testingValidatorPK[:], - SharePubKey: TestingSK.GetPublicKey().Serialize(), - DomainType: testingutils.TestingSSVDomainType, - Quorum: 3, - PartialQuorum: 2, - Committee: []*types.Operator{ - { - OperatorID: 1, - PubKey: TestingSK.GetPublicKey().Serialize(), - }, - }, -} - -var TestingInstanceStruct = &genesisspecqbft.Instance{ - State: &genesisspecqbft.State{ - Share: testingShare, - ID: []byte{1, 2, 3, 4}, - Round: 1, - Height: 1, - LastPreparedRound: 1, - LastPreparedValue: []byte{1, 2, 3, 4}, - ProposalAcceptedForCurrentRound: TestingSignedMsg, - Decided: false, - DecidedValue: []byte{1, 2, 3, 4}, - - ProposeContainer: &genesisspecqbft.MsgContainer{ - Msgs: map[genesisspecqbft.Round][]*genesisspecqbft.SignedMessage{ - 1: { - TestingSignedMsg, - }, - }, - }, - PrepareContainer: &genesisspecqbft.MsgContainer{ - Msgs: map[genesisspecqbft.Round][]*genesisspecqbft.SignedMessage{ - 1: { - TestingSignedMsg, - }, - }, - }, - CommitContainer: &genesisspecqbft.MsgContainer{ - Msgs: map[genesisspecqbft.Round][]*genesisspecqbft.SignedMessage{ - 1: { - TestingSignedMsg, - }, - }, - }, - RoundChangeContainer: &genesisspecqbft.MsgContainer{ - Msgs: map[genesisspecqbft.Round][]*genesisspecqbft.SignedMessage{ - 1: { - TestingSignedMsg, - }, - }, - }, - }, -} - -var TestingControllerStruct = &genesisspecqbft.Controller{ - Identifier: []byte{1, 2, 3, 4}, - Height: genesisspecqbft.Height(1), - Share: testingShare, - StoredInstances: genesisspecqbft.InstanceContainer{TestingInstanceStruct}, -} diff --git a/protocol/genesis/queue/exec_queue.go b/protocol/genesis/queue/exec_queue.go deleted file mode 100644 index b0b0e9083e..0000000000 --- a/protocol/genesis/queue/exec_queue.go +++ /dev/null @@ -1,17 +0,0 @@ -package queue - -// TODO: add missing tests - -// Fn represents a function to execute -type Fn func() error - -// Queue this interface is in protocol cause of the use in validator metadata -// Queue is an interface for event queue -type Queue interface { - Start() - Stop() - Queue(fn Fn) - QueueDistinct(Fn, string) - Wait() - Errors() []error -} diff --git a/protocol/genesis/ssv/genesisqueue/message_prioritizer.go b/protocol/genesis/ssv/genesisqueue/message_prioritizer.go deleted file mode 100644 index 2422ea93fb..0000000000 --- a/protocol/genesis/ssv/genesisqueue/message_prioritizer.go +++ /dev/null @@ -1,73 +0,0 @@ -package genesisqueue - -import ( - "github.com/attestantio/go-eth2-client/spec/phase0" - "github.com/ssvlabs/ssv-spec-pre-cc/qbft" -) - -// State represents a portion of the current state -// that is relevant to the prioritization of messages. -type State struct { - HasRunningInstance bool - Height qbft.Height - Round qbft.Round - Slot phase0.Slot - Quorum uint64 -} - -// MessagePrioritizer is an interface for prioritizing messages. -type MessagePrioritizer interface { - // Prior returns true if message A should be prioritized over B. - Prior(a, b *GenesisSSVMessage) bool -} - -type standardPrioritizer struct { - state *State -} - -// NewMessagePrioritizer returns a standard implementation for MessagePrioritizer -// which prioritizes messages according to the given State. -func NewMessagePrioritizer(state *State) MessagePrioritizer { - return &standardPrioritizer{state: state} -} - -func (p *standardPrioritizer) Prior(a, b *GenesisSSVMessage) bool { - msgScoreA, msgScoreB := scoreMessageType(a), scoreMessageType(b) - if msgScoreA != msgScoreB { - return msgScoreA > msgScoreB - } - - relativeHeightA, relativeHeightB := compareHeightOrSlot(p.state, a), compareHeightOrSlot(p.state, b) - if relativeHeightA != relativeHeightB { - return scoreHeight(relativeHeightA) > scoreHeight(relativeHeightB) - } - - scoreA, scoreB := scoreMessageSubtype(p.state, a, relativeHeightA), scoreMessageSubtype(p.state, b, relativeHeightB) - if scoreA != scoreB { - return scoreA > scoreB - } - - scoreA, scoreB = scoreRound(p.state, a), scoreRound(p.state, b) - if scoreA != scoreB { - return scoreA > scoreB - } - - scoreA, scoreB = scoreConsensusType(p.state, a), scoreConsensusType(p.state, b) - if scoreA != scoreB { - return scoreA > scoreB - } - - return true -} - -func scoreHeight(relativeHeight int) int { - switch relativeHeight { - case 0: - return 2 - case 1: - return 1 - case -1: - return 0 - } - return 0 -} diff --git a/protocol/genesis/ssv/genesisqueue/message_prioritizer_test.go b/protocol/genesis/ssv/genesisqueue/message_prioritizer_test.go deleted file mode 100644 index 2908c859ff..0000000000 --- a/protocol/genesis/ssv/genesisqueue/message_prioritizer_test.go +++ /dev/null @@ -1,389 +0,0 @@ -package genesisqueue - -import ( - "bytes" - "encoding/json" - "fmt" - "math/rand" - "sort" - "strings" - "testing" - - "github.com/aquasecurity/table" - "github.com/attestantio/go-eth2-client/spec/phase0" - "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - spectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - "github.com/ssvlabs/ssv-spec-pre-cc/types/testingutils" - "github.com/ssvlabs/ssv/protocol/genesis/message" - "github.com/ssvlabs/ssv/protocol/genesis/types" - "github.com/stretchr/testify/require" -) - -var messagePriorityTests = []struct { - name string - state *State - messages []mockMessage -}{ - { - name: "Running instance", - state: &State{ - HasRunningInstance: true, - Height: 100, - Slot: 64, - Quorum: 4, - }, - messages: []mockMessage{ - // 1. Events: - // 1.1. Events/ExecuteDuty - mockExecuteDutyMessage{Slot: 62, Role: spectypes.BNRoleProposer}, - // 1.2. Events/Timeout - mockTimeoutMessage{Height: 98, Role: spectypes.BNRoleProposer}, - - // 2. Current height/slot: - // 2.1. Consensus - // 2.1.1. Consensus/Proposal - mockConsensusMessage{Height: 100, Type: qbft.ProposalMsgType}, - // 2.1.2. Consensus/Prepare - mockConsensusMessage{Height: 100, Type: qbft.PrepareMsgType}, - // 2.1.3. Consensus/Commit - mockConsensusMessage{Height: 100, Type: qbft.CommitMsgType}, - // 2.1.4. Consensus/ - mockConsensusMessage{Height: 100, Type: qbft.RoundChangeMsgType}, - // 2.2. Pre-consensus - mockNonConsensusMessage{Slot: 64, Type: spectypes.SelectionProofPartialSig}, - // 2.3. Post-consensus - mockNonConsensusMessage{Slot: 64, Type: spectypes.PostConsensusPartialSig}, - - // 3. Higher height/slot: - // 3.1 Decided - mockConsensusMessage{Height: 101, Decided: true}, - // 3.2. Pre-consensus - mockNonConsensusMessage{Slot: 65, Type: spectypes.SelectionProofPartialSig}, - // 3.3. Consensus - mockConsensusMessage{Height: 101}, - // 3.4. Post-consensus - mockNonConsensusMessage{Slot: 65, Type: spectypes.PostConsensusPartialSig}, - - // 4. Lower height/slot: - // 4.1 Decided - mockConsensusMessage{Height: 99, Decided: true}, - // 4.2. Commit - mockConsensusMessage{Height: 99, Type: qbft.CommitMsgType}, - // 4.3. Pre-consensus - mockNonConsensusMessage{Slot: 63, Type: spectypes.SelectionProofPartialSig}, - }, - }, - { - name: "No running instance", - state: &State{ - HasRunningInstance: false, - Height: 100, - Slot: 64, - Quorum: 4, - }, - messages: []mockMessage{ - // 1. Current height/slot: - // 1.1. Pre-consensus - mockNonConsensusMessage{Slot: 64, Type: spectypes.SelectionProofPartialSig}, - // 1.2. Post-consensus - mockNonConsensusMessage{Slot: 64, Type: spectypes.PostConsensusPartialSig}, - // 1.3. Consensus - // 1.3.1. Consensus/Proposal - mockConsensusMessage{Height: 100, Type: qbft.ProposalMsgType}, - // 1.3.2. Consensus/Prepare - mockConsensusMessage{Height: 100, Type: qbft.PrepareMsgType}, - // 1.3.3. Consensus/Commit - mockConsensusMessage{Height: 100, Type: qbft.CommitMsgType}, - // 1.3.4. Consensus/ - mockConsensusMessage{Height: 100, Type: qbft.RoundChangeMsgType}, - - // 2. Higher height/slot: - // 2.1 Decided - mockConsensusMessage{Height: 101, Decided: true}, - // 2.2. Pre-consensus - mockNonConsensusMessage{Slot: 65, Type: spectypes.SelectionProofPartialSig}, - // 2.3. Consensus - mockConsensusMessage{Height: 101}, - // 2.4. Post-consensus - mockNonConsensusMessage{Slot: 65, Type: spectypes.PostConsensusPartialSig}, - - // 3. Lower height/slot: - // 3.1 Decided - mockConsensusMessage{Height: 99, Decided: true}, - // 3.2. Commit - mockConsensusMessage{Height: 99, Type: qbft.CommitMsgType}, - // 3.3. Pre-consensus - mockNonConsensusMessage{Slot: 63, Type: spectypes.SelectionProofPartialSig}, - }, - }, -} - -func TestMessagePrioritizer(t *testing.T) { - for _, test := range messagePriorityTests { - t.Run(test.name, func(t *testing.T) { - messages := make(messageSlice, len(test.messages)) - for i, m := range test.messages { - var err error - messages[i], err = DecodeGenesisSSVMessage(m.ssvMessage(test.state)) - require.NoError(t, err) - } - - var shuffles []messageSlice - for { - shuffledMessages := messages.shuffle() - if shuffledMessages.equal(messages) { - continue - } - shuffles = append(shuffles, shuffledMessages) - if len(shuffles) == 10 { - break - } - } - - prioritizer := NewMessagePrioritizer(test.state) - for _, shuffle := range shuffles { - shuffle.sort(prioritizer) - correctOrder := messages.equal(shuffle) - if !correctOrder { - require.Fail(t, "incorrect order:\n"+shuffle.dump(test.state)) - } - } - }) - } -} - -type mockMessage interface { - ssvMessage(*State) *spectypes.SSVMessage -} - -type mockConsensusMessage struct { - Role spectypes.BeaconRole - Type qbft.MessageType - Decided bool - Height qbft.Height -} - -func (m mockConsensusMessage) ssvMessage(state *State) *spectypes.SSVMessage { - var ( - typ = m.Type - signerCount = 1 - ) - if m.Decided { - typ = qbft.CommitMsgType - signerCount = int(state.Quorum) + 1 - } - - var signers []spectypes.OperatorID - for i := 0; i < signerCount; i++ { - signers = append(signers, spectypes.OperatorID(i)) - } - - factory := ssvMessageFactory(m.Role) - return factory( - &qbft.SignedMessage{ - Message: qbft.Message{ - MsgType: typ, - Height: m.Height, - Round: 2, - Identifier: []byte{1, 2, 3, 4}, - Root: [32]byte{1, 2, 3}, - RoundChangeJustification: [][]byte{{1, 2, 3, 4}}, - PrepareJustification: [][]byte{{1, 2, 3, 4}}, - }, - - FullData: []byte{1, 2, 3, 4}, - Signature: make([]byte, 96), - Signers: signers, - }, - nil, - ) -} - -type mockNonConsensusMessage struct { - Role spectypes.BeaconRole - Type spectypes.PartialSigMsgType - Slot phase0.Slot -} - -func (m mockNonConsensusMessage) ssvMessage(state *State) *spectypes.SSVMessage { - factory := ssvMessageFactory(m.Role) - return factory( - nil, - &spectypes.SignedPartialSignatureMessage{ - Message: spectypes.PartialSignatureMessages{ - Type: m.Type, - Slot: m.Slot, - Messages: []*spectypes.PartialSignatureMessage{ - { - PartialSignature: make([]byte, 96), - SigningRoot: [32]byte{}, - Signer: 1, - }, - }, - }, - Signature: make([]byte, 96), - Signer: spectypes.OperatorID(1), - }, - ) -} - -type mockExecuteDutyMessage struct { - Role spectypes.BeaconRole - Slot phase0.Slot -} - -func (m mockExecuteDutyMessage) ssvMessage(state *State) *spectypes.SSVMessage { - edd, err := json.Marshal(types.ExecuteDutyData{Duty: &spectypes.Duty{ - Type: m.Role, - Slot: m.Slot, - }}) - if err != nil { - panic(err) - } - data, err := (&types.EventMsg{ - Type: types.ExecuteDuty, - Data: edd, - }).Encode() - if err != nil { - panic(err) - } - return &spectypes.SSVMessage{ - MsgType: message.SSVEventMsgType, - MsgID: spectypes.NewMsgID(testingutils.TestingSSVDomainType, testingutils.TestingValidatorPubKey[:], m.Role), - Data: data, - } -} - -type mockTimeoutMessage struct { - Role spectypes.BeaconRole - Height qbft.Height -} - -func (m mockTimeoutMessage) ssvMessage(state *State) *spectypes.SSVMessage { - td := types.TimeoutData{Height: m.Height} - data, err := json.Marshal(td) - if err != nil { - panic(err) - } - eventMsgData, err := (&types.EventMsg{ - Type: types.Timeout, - Data: data, - }).Encode() - if err != nil { - panic(err) - } - return &spectypes.SSVMessage{ - MsgType: message.SSVEventMsgType, - MsgID: spectypes.NewMsgID(testingutils.TestingSSVDomainType, testingutils.TestingValidatorPubKey[:], m.Role), - Data: eventMsgData, - } -} - -type messageSlice []*GenesisSSVMessage - -func (m messageSlice) shuffle() messageSlice { - shuffled := make([]*GenesisSSVMessage, len(m)) - for i, j := range rand.Perm(len(m)) { - shuffled[i] = m[j] - } - return shuffled -} - -func (m messageSlice) sort(prioritizer MessagePrioritizer) { - sort.Slice(m, func(i, j int) bool { - return prioritizer.Prior(m[i], m[j]) - }) -} - -func (m messageSlice) equal(m2 messageSlice) bool { - if len(m) != len(m2) { - return false - } - for i := range m { - a, err := json.Marshal(m[i]) - if err != nil { - panic(err) - } - b, err := json.Marshal(m2[i]) - if err != nil { - panic(err) - } - if !bytes.Equal(a, b) { - return false - } - } - return true -} - -func (m messageSlice) dump(s *State) string { - b := &strings.Builder{} - tbl := table.New(b) - tbl.SetHeaders("#", "Kind", "Height/Slot", "Type", "Decided") - for i, msg := range m { - var ( - kind string - typ interface{} - heightOrSlot interface{} - relation string - ) - - switch compareHeightOrSlot(s, msg) { - case -1: - relation = "lower" - case 0: - relation = "current" - case 1: - relation = "higher" - } - - switch mm := msg.Body.(type) { - case *spectypes.SignedPartialSignatureMessage: - // heightOrSlot = mm.Message.Messages[0].Slot - typ = mm.Message.Type - if typ == spectypes.PostConsensusPartialSig { - kind = "post-consensus" - } else { - kind = "pre-consensus" - } - case *qbft.SignedMessage: - kind = "consensus" - heightOrSlot = mm.Message.Height - typ = mm.Message.MsgType - } - - decided := false - if sm, ok := msg.Body.(*qbft.SignedMessage); ok { - decided = isDecidedMesssage(s, sm) - } - tbl.AddRow( - fmt.Sprint(i), - kind, - fmt.Sprintf("%d (%s)", heightOrSlot, relation), - fmt.Sprint(typ), - fmt.Sprintf("%t", decided), - ) - } - tbl.Render() - return b.String() -} - -func ssvMessageFactory(role spectypes.BeaconRole) func(*qbft.SignedMessage, *spectypes.SignedPartialSignatureMessage) *spectypes.SSVMessage { - switch role { - case spectypes.BNRoleAttester: - return testingutils.SSVMsgAttester - case spectypes.BNRoleProposer: - return testingutils.SSVMsgProposer - case spectypes.BNRoleAggregator: - return testingutils.SSVMsgAggregator - case spectypes.BNRoleSyncCommittee: - return testingutils.SSVMsgSyncCommittee - case spectypes.BNRoleSyncCommitteeContribution: - return testingutils.SSVMsgSyncCommitteeContribution - case spectypes.BNRoleValidatorRegistration: - return testingutils.SSVMsgValidatorRegistration - case spectypes.BNRoleVoluntaryExit: - return testingutils.SSVMsgVoluntaryExit - default: - panic("invalid role") - } -} diff --git a/protocol/genesis/ssv/genesisqueue/messages.go b/protocol/genesis/ssv/genesisqueue/messages.go deleted file mode 100644 index fededfafc0..0000000000 --- a/protocol/genesis/ssv/genesisqueue/messages.go +++ /dev/null @@ -1,251 +0,0 @@ -package genesisqueue - -import ( - "fmt" - - "github.com/attestantio/go-eth2-client/spec/phase0" - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - - "github.com/ssvlabs/ssv/network/commons" - ssvmessage "github.com/ssvlabs/ssv/protocol/genesis/message" - ssvtypes "github.com/ssvlabs/ssv/protocol/genesis/types" -) - -var ( - ErrUnknownMessageType = fmt.Errorf("unknown message type") - ErrDecodeNetworkMsg = fmt.Errorf("could not decode data into an SSVMessage") -) - -type GenesisSSVMessage struct { - SignedSSVMessage *genesisspectypes.SignedSSVMessage - *genesisspectypes.SSVMessage - - // Body is the decoded Data. - Body interface{} // *EventMsg | *genesisspecqbft.SignedMessage | *genesisspectypes.SignedPartialSignatureMessage -} - -func (d *GenesisSSVMessage) DecodedSSVMessage() {} - -func (d *GenesisSSVMessage) Slot() (phase0.Slot, error) { - switch m := d.Body.(type) { - case *ssvtypes.EventMsg: // TODO: do we need slot in events? - if m.Type == ssvtypes.Timeout { - data, err := m.GetTimeoutData() - if err != nil { - return 0, ErrUnknownMessageType // TODO alan: other error - } - return phase0.Slot(data.Height), nil - } - return 0, ErrUnknownMessageType // TODO: alan: slot not supporting dutyexec msg? - case *genesisspecqbft.SignedMessage: // TODO: remove post-fork - return phase0.Slot(m.Message.Height), nil - case *genesisspectypes.SignedPartialSignatureMessage: // TODO: remove post-fork - return m.Message.Slot, nil - default: - return 0, ErrUnknownMessageType - } -} - -// DecodeGenesisSSVMessage decodes a genesis SSVMessage into a GenesisSSVMessage. -func DecodeGenesisSSVMessage(m *genesisspectypes.SSVMessage) (*GenesisSSVMessage, error) { - body, err := ExtractGenesisMsgBody(m) - if err != nil { - return nil, err - } - - return &GenesisSSVMessage{ - SSVMessage: &genesisspectypes.SSVMessage{ - MsgType: m.MsgType, - MsgID: m.MsgID, - Data: m.Data, - }, - Body: body, - }, nil -} - -// DecodeGenesisSignedSSVMessage decodes a genesis SignedSSVMessage into a GenesisSSVMessage. -func DecodeGenesisSignedSSVMessage(sm *genesisspectypes.SignedSSVMessage) (*GenesisSSVMessage, error) { - m, err := commons.DecodeGenesisNetworkMsg(sm.GetData()) - if err != nil { - return nil, fmt.Errorf("%w: %w", ErrDecodeNetworkMsg, err) - } - - d, err := DecodeGenesisSSVMessage(m) - if err != nil { - return nil, err - } - - d.SignedSSVMessage = sm - - return d, nil -} - -func ExtractGenesisMsgBody(m *genesisspectypes.SSVMessage) (any, error) { - var body any - switch m.MsgType { - case genesisspectypes.SSVConsensusMsgType: // TODO: Or message.SSVDecidedMsgType? - sm := &genesisspecqbft.SignedMessage{} - if err := sm.Decode(m.Data); err != nil { - return nil, fmt.Errorf("failed to decode genesis SignedMessage: %w", err) - } - body = sm - case genesisspectypes.SSVPartialSignatureMsgType: - sm := &genesisspectypes.SignedPartialSignatureMessage{} - if err := sm.Decode(m.Data); err != nil { - return nil, fmt.Errorf("failed to decode genesis SignedPartialSignatureMessage: %w", err) - } - body = sm - case ssvmessage.SSVEventMsgType: - msg := &ssvtypes.EventMsg{} - if err := msg.Decode(m.Data); err != nil { - return nil, fmt.Errorf("failed to decode EventMsg: %w", err) - } - body = msg - default: - return nil, ErrUnknownMessageType - } - - return body, nil -} - -// compareHeightOrSlot returns an integer comparing the message's height/slot to the current. -// The reuslt will be 0 if equal, -1 if lower, 1 if higher. -func compareHeightOrSlot(state *State, m *GenesisSSVMessage) int { - if mm, ok := m.Body.(*genesisspecqbft.SignedMessage); ok { - if mm.Message.Height == state.Height { - return 0 - } - if mm.Message.Height > state.Height { - return 1 - } - } else if mm, ok := m.Body.(*genesisspectypes.SignedPartialSignatureMessage); ok { - if mm.Message.Slot == state.Slot { - return 0 - } - if mm.Message.Slot > state.Slot { - return 1 - } - } - return -1 -} - -// scoreRound returns an integer comparing the message's round (if exist) to the current. -// The reuslt will be 0 if equal, -1 if lower, 1 if higher. -func scoreRound(state *State, m *GenesisSSVMessage) int { - if mm, ok := m.Body.(*genesisspecqbft.SignedMessage); ok { - if mm.Message.Round == state.Round { - return 2 - } - if mm.Message.Round > state.Round { - return 1 - } - return -1 - } - return 0 -} - -// scoreMessageType returns a score based on the top level message type, -// where event type messages are prioritized over other types. -func scoreMessageType(m *GenesisSSVMessage) int { - switch mm := m.Body.(type) { - case *ssvtypes.EventMsg: - switch mm.Type { - case ssvtypes.ExecuteDuty: - return 3 - case ssvtypes.Timeout: - return 2 - } - return 0 - default: - return 0 - } -} - -// scoreMessageSubtype returns an integer score for the message's type. -func scoreMessageSubtype(state *State, m *GenesisSSVMessage, relativeHeight int) int { - consensusMessage, isConsensusMessage := m.Body.(*genesisspecqbft.SignedMessage) - - var ( - isPreConsensusMessage = false - isPostConsensusMessage = false - ) - if mm, ok := m.Body.(*genesisspectypes.SignedPartialSignatureMessage); ok { - isPostConsensusMessage = mm.Message.Type == genesisspectypes.PostConsensusPartialSig - isPreConsensusMessage = !isPostConsensusMessage - } - - // Current height. - if relativeHeight == 0 { - if state.HasRunningInstance { - switch { - case isConsensusMessage: - return 3 - case isPreConsensusMessage: - return 2 - case isPostConsensusMessage: - return 1 - } - return 0 - } - switch { - case isPreConsensusMessage: - return 3 - case isPostConsensusMessage: - return 2 - case isConsensusMessage: - return 1 - } - return 0 - } - - // Higher height. - if relativeHeight == 1 { - switch { - case isDecidedMesssage(state, consensusMessage): - return 4 - case isPreConsensusMessage: - return 3 - case isConsensusMessage: - return 2 - case isPostConsensusMessage: - return 1 - } - return 0 - } - - // Lower height. - switch { - case isDecidedMesssage(state, consensusMessage): - return 2 - case isConsensusMessage && consensusMessage.Message.MsgType == genesisspecqbft.CommitMsgType: - return 1 - } - return 0 -} - -// scoreConsensusType returns an integer score for the type of a consensus message. -// When given a non-consensus message, scoreConsensusType returns 0. -func scoreConsensusType(state *State, m *GenesisSSVMessage) int { - if mm, ok := m.Body.(*genesisspecqbft.SignedMessage); ok { - switch mm.Message.MsgType { - case genesisspecqbft.ProposalMsgType: - return 4 - case genesisspecqbft.PrepareMsgType: - return 3 - case genesisspecqbft.CommitMsgType: - return 2 - case genesisspecqbft.RoundChangeMsgType: - return 1 - } - } - return 0 -} - -func isDecidedMesssage(s *State, sm *genesisspecqbft.SignedMessage) bool { - if sm == nil { - return false - } - return sm.Message.MsgType == genesisspecqbft.CommitMsgType && - uint64(len(sm.Signers)) > s.Quorum -} diff --git a/protocol/genesis/ssv/genesisqueue/metrics.go b/protocol/genesis/ssv/genesisqueue/metrics.go deleted file mode 100644 index a383c7b975..0000000000 --- a/protocol/genesis/ssv/genesisqueue/metrics.go +++ /dev/null @@ -1,32 +0,0 @@ -package genesisqueue - -import ( - spectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" -) - -// Metrics records metrics about the Queue. -type Metrics interface { - DroppedQueueMessage(messageID spectypes.MessageID) -} - -type queueWithMetrics struct { - Queue - metrics Metrics -} - -// WithMetrics returns a wrapping of the given Queue that records metrics using the given Metrics. -func WithMetrics(q Queue, metrics Metrics) Queue { - return &queueWithMetrics{ - Queue: q, - metrics: metrics, - } -} - -func (q *queueWithMetrics) TryPush(msg *GenesisSSVMessage) bool { - pushed := q.Queue.TryPush(msg) - if !pushed { - q.metrics.DroppedQueueMessage(msg.GetID()) - } - - return pushed -} diff --git a/protocol/genesis/ssv/genesisqueue/queue.go b/protocol/genesis/ssv/genesisqueue/queue.go deleted file mode 100644 index 28dfd1bd7d..0000000000 --- a/protocol/genesis/ssv/genesisqueue/queue.go +++ /dev/null @@ -1,203 +0,0 @@ -package genesisqueue - -import ( - "context" - "time" -) - -const ( - // inboxReadFrequency is the minimum time between reads from the inbox. - inboxReadFrequency = 1 * time.Millisecond -) - -// Filter is a function that returns true if the message should be popped. -type Filter func(*GenesisSSVMessage) bool - -// FilterAny returns a Filter that returns true for any message. -func FilterAny(*GenesisSSVMessage) bool { return true } - -// Queue is a queue of GenesisSSVMessage with dynamic (per-pop) prioritization. -type Queue interface { - // Push blocks until the message is pushed to the queue. - Push(*GenesisSSVMessage) - - // TryPush returns immediately with true if the message was pushed to the queue, - // or false if the queue is full. - TryPush(*GenesisSSVMessage) bool - - // Pop returns and removes the next message in the queue, or blocks until a message is available. - // When the context is canceled, Pop returns immediately with any leftover message or nil. - Pop(context.Context, MessagePrioritizer, Filter) *GenesisSSVMessage - - // TryPop returns immediately with the next message in the queue, or nil if there is none. - TryPop(MessagePrioritizer, Filter) *GenesisSSVMessage - - // Empty returns true if the queue is empty. - Empty() bool - - // Len returns the number of messages in the queue. - Len() int -} - -type priorityQueue struct { - head *item - inbox chan *GenesisSSVMessage - lastRead time.Time -} - -// New returns an implementation of Queue optimized for concurrent push and sequential pop. -// Pops aren't thread-safe, so don't call Pop from multiple goroutines. -func New(capacity int) Queue { - return &priorityQueue{ - inbox: make(chan *GenesisSSVMessage, capacity), - } -} - -// NewDefault returns an implementation of Queue optimized for concurrent push and sequential pop, -// with a capacity of 32 and a PusherDropping. -func NewDefault() Queue { - return New(32) -} - -func (q *priorityQueue) Push(msg *GenesisSSVMessage) { - q.inbox <- msg -} - -func (q *priorityQueue) TryPush(msg *GenesisSSVMessage) bool { - select { - case q.inbox <- msg: - return true - default: - return false - } -} - -func (q *priorityQueue) TryPop(prioritizer MessagePrioritizer, filter Filter) *GenesisSSVMessage { - // Read any pending messages from the inbox. - q.readInbox() - - // Pop the highest priority message. - if q.head != nil { - return q.pop(prioritizer, filter) - } - - return nil -} - -func (q *priorityQueue) Pop(ctx context.Context, prioritizer MessagePrioritizer, filter Filter) *GenesisSSVMessage { - // Read any pending messages from the inbox, if enough time has passed. - // inboxReadFrequency is a tradeoff between responsiveness and computational cost, - // since reading the inbox is more expensive than just reading the head. - if time.Since(q.lastRead) > inboxReadFrequency { - q.readInbox() - } - - // Try to pop immediately. - if q.head != nil { - if m := q.pop(prioritizer, filter); m != nil { - return m - } - } - - // Wait for a message to be pushed. -Wait: - for { - select { - case msg := <-q.inbox: - if q.head == nil { - q.head = &item{message: msg} - } else { - q.head = &item{message: msg, next: q.head} - } - if filter(msg) { - break Wait - } - case <-ctx.Done(): - break Wait - } - } - - // Read any messages that were pushed while waiting. - q.readInbox() - - // Pop the highest priority message. - if q.head != nil { - return q.pop(prioritizer, filter) - } - - return nil -} - -func (q *priorityQueue) readInbox() { - q.lastRead = time.Now() - - for { - select { - case msg := <-q.inbox: - if q.head == nil { - q.head = &item{message: msg} - } else { - q.head = &item{message: msg, next: q.head} - } - default: - return - } - } -} - -func (q *priorityQueue) pop(prioritizer MessagePrioritizer, filter Filter) *GenesisSSVMessage { - if q.head.next == nil { - if m := q.head.message; filter(m) { - q.head = nil - return m - } - return nil - } - - // Remove the highest priority message and return it. - var ( - prior *item - highest *item - current = q.head - ) - if filter(current.message) { - highest = current - } - for { - if (highest == nil || prioritizer.Prior(current.next.message, highest.message)) && filter(current.next.message) { - highest = current.next - prior = current - } - current = current.next - if current.next == nil { - break - } - } - if highest == nil { - return nil - } - if prior == nil { - q.head = highest.next - } else { - prior.next = highest.next - } - return highest.message -} - -func (q *priorityQueue) Empty() bool { - return q.head == nil && len(q.inbox) == 0 -} - -func (q *priorityQueue) Len() int { - n := len(q.inbox) - for i := q.head; i != nil; i = i.next { - n++ - } - return n -} - -// item is a node in a linked list of GenesisSSVMessage. -type item struct { - message *GenesisSSVMessage - next *item -} diff --git a/protocol/genesis/ssv/genesisqueue/queue_test.go b/protocol/genesis/ssv/genesisqueue/queue_test.go deleted file mode 100644 index 44e757d0f2..0000000000 --- a/protocol/genesis/ssv/genesisqueue/queue_test.go +++ /dev/null @@ -1,504 +0,0 @@ -package genesisqueue - -import ( - "context" - "fmt" - "math/rand" - "sync" - "sync/atomic" - "testing" - "time" - - "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - spectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - "github.com/stretchr/testify/require" - "golang.org/x/text/language" - "golang.org/x/text/message" -) - -var mockState = &State{ - HasRunningInstance: true, - Height: 100, - Slot: 64, - Quorum: 4, -} - -func TestPriorityQueue_TryPop(t *testing.T) { - queue := NewDefault() - require.True(t, queue.Empty()) - - // Push 2 messages. - msg1 := decodeAndPush(t, queue, mockConsensusMessage{Height: 100, Type: qbft.PrepareMsgType}, mockState) - msg2 := decodeAndPush(t, queue, mockConsensusMessage{Height: 101, Type: qbft.PrepareMsgType}, mockState) - require.False(t, queue.Empty()) - - // Pop 1st message. - popped := queue.TryPop(NewMessagePrioritizer(mockState), FilterAny) - require.Equal(t, msg1, popped) - - // Pop 2nd message. - popped = queue.TryPop(NewMessagePrioritizer(mockState), FilterAny) - require.True(t, queue.Empty()) - require.NotNil(t, popped) - require.Equal(t, msg2, popped) - - // Pop nil. - popped = queue.TryPop(NewMessagePrioritizer(mockState), FilterAny) - require.Nil(t, popped) -} - -func TestPriorityQueue_Filter(t *testing.T) { - queue := NewDefault() - require.True(t, queue.Empty()) - - // Push 1 message. - msg := decodeAndPush(t, queue, mockConsensusMessage{Height: 100, Type: qbft.PrepareMsgType}, mockState) - require.False(t, queue.Empty()) - - // Pop non-matching message. - popped := queue.TryPop(NewMessagePrioritizer(mockState), func(msg *GenesisSSVMessage) bool { - return msg.Body.(*qbft.SignedMessage).Message.Height == 101 - }) - require.False(t, queue.Empty()) - require.Nil(t, popped) - - // Pop matching message. - popped = queue.TryPop(NewMessagePrioritizer(mockState), func(msg *GenesisSSVMessage) bool { - return msg.Body.(*qbft.SignedMessage).Message.Height == 100 - }) - require.True(t, queue.Empty()) - require.NotNil(t, popped) - require.Equal(t, msg, popped) - - // Push 2 messages. - msg1 := decodeAndPush(t, queue, mockConsensusMessage{Height: 100, Type: qbft.PrepareMsgType}, mockState) - msg2 := decodeAndPush(t, queue, mockConsensusMessage{Height: 101, Type: qbft.PrepareMsgType}, mockState) - - // Pop 2nd message. - popped = queue.TryPop(NewMessagePrioritizer(mockState), func(msg *GenesisSSVMessage) bool { - return msg.Body.(*qbft.SignedMessage).Message.Height == 101 - }) - require.NotNil(t, popped) - require.Equal(t, msg2, popped) - - // Pop 1st message. - popped = queue.TryPop(NewMessagePrioritizer(mockState), func(msg *GenesisSSVMessage) bool { - return msg.Body.(*qbft.SignedMessage).Message.Height == 100 - }) - require.True(t, queue.Empty()) - require.NotNil(t, popped) - require.Equal(t, msg1, popped) - - // Pop nil. - popped = queue.TryPop(NewMessagePrioritizer(mockState), func(msg *GenesisSSVMessage) bool { - return msg.Body.(*qbft.SignedMessage).Message.Height == 100 - }) - require.Nil(t, popped) -} - -func TestPriorityQueue_Pop(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) - defer cancel() - - const ( - capacity = 3 - contextTimeout = 50 * time.Millisecond - pushDelay = 50 * time.Millisecond - precision = 50 * time.Millisecond - ) - queue := New(capacity) - require.True(t, queue.Empty()) - - msg, err := DecodeGenesisSSVMessage(mockConsensusMessage{Height: 100, Type: qbft.PrepareMsgType}.ssvMessage(mockState)) - require.NoError(t, err) - - // Push messages. - for i := 0; i < capacity; i++ { - queue.TryPush(msg) - } - require.False(t, queue.Empty()) - - // Should pop everything immediately. - for i := 0; i < capacity; i++ { - popped := queue.Pop(ctx, NewMessagePrioritizer(mockState), FilterAny) - require.Equal(t, msg, popped) - } - require.True(t, queue.Empty()) - - // Should pop nil after context cancellation. - { - ctx, cancel := context.WithTimeout(context.Background(), contextTimeout) - defer cancel() - expectedEnd := time.Now().Add(contextTimeout) - popped := queue.Pop(ctx, NewMessagePrioritizer(mockState), FilterAny) - require.Nil(t, popped) - require.WithinDuration(t, expectedEnd, time.Now(), precision) - } - - // Push messages in a goroutine. - go func() { - for i := 0; i < capacity; i++ { - time.Sleep(pushDelay) - queue.TryPush(msg) - } - }() - - // Should wait for the messages to be pushed. - expectedEnd := time.Now().Add(pushDelay * time.Duration(capacity)) - for i := 0; i < capacity; i++ { - popped := queue.Pop(ctx, NewMessagePrioritizer(mockState), FilterAny) - require.Equal(t, msg, popped) - } - require.True(t, queue.Empty()) - require.WithinDuration(t, expectedEnd, time.Now(), precision) -} - -// TestPriorityQueue_Order tests that the queue returns the messages in the correct order. -func TestPriorityQueue_Order(t *testing.T) { - for _, test := range messagePriorityTests { - t.Run(fmt.Sprintf("PriorityQueue: %s", test.name), func(t *testing.T) { - // Create the PriorityQueue and populate it with messages. - q := NewDefault() - - // Decode messages. - messages := make(messageSlice, len(test.messages)) - for i, m := range test.messages { - mm, err := DecodeGenesisSSVMessage(m.ssvMessage(test.state)) - require.NoError(t, err) - messages[i] = mm - } - - for i := 0; i < 20; i++ { - // Push shuffled messages to the queue. - for _, msg := range messages.shuffle() { - q.Push(msg) - } - - // Pop messages from the queue and compare to the expected order. - for i, excepted := range messages { - actual := q.TryPop(NewMessagePrioritizer(test.state), FilterAny) - require.Equal(t, excepted, actual, "incorrect message at index %d", i) - } - } - }) - } -} - -func TestPriorityQueue_Pop_NothingThenSomething(t *testing.T) { - queue := NewDefault() - require.True(t, queue.Empty()) - - wg := sync.WaitGroup{} - wg.Add(1) - - state := &State{ - HasRunningInstance: true, - Height: 1, - Slot: 1, - Round: 1, - Quorum: 4, - } - - decodeAndPush(t, queue, mockConsensusMessage{Height: qbft.Height(1), Type: qbft.CommitMsgType}, state) - decodeAndPush(t, queue, mockConsensusMessage{Height: qbft.Height(1), Type: qbft.CommitMsgType}, state) - time.Sleep(50 * time.Millisecond) - require.Equal(t, 2, queue.Len()) - expectedMsg := decodeAndPush(t, queue, mockConsensusMessage{Height: qbft.Height(2), Type: qbft.CommitMsgType}, state) - - go func() { - defer wg.Done() - matchHeight2 := func(msg *GenesisSSVMessage) bool { - return msg.Body.(*qbft.SignedMessage).Message.Height == 2 - } - popped := queue.Pop(context.Background(), NewMessagePrioritizer(state), matchHeight2) - require.Equal(t, expectedMsg, popped) - }() - - wg.Wait() - - // Ensure that the queue still contains the non-matching messages. - require.Equal(t, queue.Len(), 2) -} - -func TestPriorityQueue_Pop_WithLoopForNonMatchingAndMatchingMessages(t *testing.T) { - queue := NewDefault() - require.True(t, queue.Empty()) - - wg := sync.WaitGroup{} - wg.Add(1) - - state := &State{ - HasRunningInstance: true, - Height: 1, - Slot: 1, - Round: 1, - Quorum: 4, - } - - // Pop in a separate goroutine. The first two pops should get the matching messages, and the third should return nil. - go func() { - defer wg.Done() - popped := queue.Pop(context.Background(), NewMessagePrioritizer(state), func(msg *GenesisSSVMessage) bool { - _, ok := msg.Body.(*qbft.SignedMessage) - if !ok { - return true - } - return msg.Body.(*qbft.SignedMessage).Message.MsgType != qbft.CommitMsgType - }) - require.NotNil(t, popped) - - }() - - // Simulate delay before pushing messages. - time.Sleep(100 * time.Millisecond) - - // Push non-matching message. - decodeAndPush(t, queue, mockConsensusMessage{Height: qbft.Height(1), Type: qbft.CommitMsgType}, state) - - // Push one matching message. - decodeAndPush(t, queue, mockNonConsensusMessage{Slot: 1, Type: spectypes.PostConsensusPartialSig}, state) - - // Push non-matching message. - decodeAndPush(t, queue, mockConsensusMessage{Height: qbft.Height(1), Type: qbft.CommitMsgType}, state) - - wg.Wait() - - // Ensure that the queue still contains the non-matching messages. - require.False(t, queue.Empty()) -} - -type testMetrics struct { - dropped atomic.Uint64 -} - -func (n *testMetrics) DroppedQueueMessage(messageID spectypes.MessageID) { - n.dropped.Add(1) -} - -func TestWithMetrics(t *testing.T) { - metrics := &testMetrics{} - queue := WithMetrics(New(1), metrics) - require.True(t, queue.Empty()) - - // Push 1 message. - msg, err := DecodeGenesisSSVMessage(mockConsensusMessage{Height: 100, Type: qbft.PrepareMsgType}.ssvMessage(mockState)) - require.NoError(t, err) - pushed := queue.TryPush(msg) - require.True(t, pushed) - require.False(t, queue.Empty()) - require.EqualValues(t, 0, metrics.dropped.Load()) - - // Push above capacity. - pushed = queue.TryPush(msg) - require.False(t, pushed) - require.False(t, queue.Empty()) - require.EqualValues(t, 1, metrics.dropped.Load()) -} - -func BenchmarkPriorityQueue_Parallel(b *testing.B) { - benchmarkPriorityQueueParallel(b, func() Queue { - return New(32) - }, false) -} - -func BenchmarkPriorityQueue_Parallel_Lossy(b *testing.B) { - benchmarkPriorityQueueParallel(b, NewDefault, true) -} - -func benchmarkPriorityQueueParallel(b *testing.B, factory func() Queue, lossy bool) { - english := message.NewPrinter(language.English) - - const ( - pushers = 16 - poppers = 1 - messageCount = 2080 - ) - queue := factory() - - // Prepare messages. - messages := make([]*GenesisSSVMessage, messageCount) - for i := range messages { - var err error - msg, err := DecodeGenesisSSVMessage(mockConsensusMessage{Height: qbft.Height(rand.Intn(messageCount)), Type: qbft.PrepareMsgType}.ssvMessage(mockState)) - require.NoError(b, err) - messages[i] = msg - } - - // Metrics. - var ( - totalPushed int - totalPopped int - totalDuration time.Duration - ) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - start := time.Now() - - // Stream messages to pushers. - messageStream := make(chan *GenesisSSVMessage) - go func() { - for _, msg := range messages { - messageStream <- msg - } - close(messageStream) - }() - - // Spawn pushers. - var ( - pushersWg sync.WaitGroup - pushedCount atomic.Int64 - ) - for i := 0; i < pushers; i++ { - pushersWg.Add(1) - go func() { - defer pushersWg.Done() - for m := range messageStream { - if lossy { - queue.TryPush(m) - } else { - queue.Push(m) - } - pushedCount.Add(1) - time.Sleep(time.Duration(rand.Intn(300)) * time.Microsecond) - } - }() - } - - // Assert pushed messages. - var pushersAssertionWg sync.WaitGroup - pushersAssertionWg.Add(1) - go func() { - pushersWg.Wait() - defer pushersAssertionWg.Done() - totalPushed += messageCount - require.Equal(b, int64(messageCount), pushedCount.Load()) - }() - - // Pop all messages. - var poppersWg sync.WaitGroup - popped := make(chan *GenesisSSVMessage, messageCount*2) - poppingCtx, stopPopping := context.WithCancel(context.Background()) - for i := 0; i < poppers; i++ { - poppersWg.Add(1) - go func() { - defer poppersWg.Done() - for { - msg := queue.Pop(poppingCtx, NewMessagePrioritizer(mockState), FilterAny) - if msg == nil { - return - } - popped <- msg - } - }() - } - - // Wait for pushed messages assertion. - pushersAssertionWg.Wait() - stopPopping() - - // Wait for poppers. - go func() { - poppersWg.Wait() - close(popped) - }() - - allPopped := make(map[*GenesisSSVMessage]struct{}) - for msg := range popped { - allPopped[msg] = struct{}{} - } - totalPopped += len(allPopped) - totalDuration += time.Since(start) - - if !lossy { - // Assert that all messages were popped. - require.Equal(b, messageCount, len(allPopped), "popped messages count mismatch") - - for _, msg := range messages { - if _, ok := allPopped[msg]; !ok { - b.Log("message not popped") - b.Fail() - } - } - } - } - b.StopTimer() - - // Log metrics. - b.Log(english.Sprintf( - "popped %d/%d (%.3f%% loss) messages at %d messages/sec", - totalPopped, - totalPushed, - 100*(1-float64(totalPopped)/float64(totalPushed)), - int64(float64(totalPopped)/totalDuration.Seconds()), - )) -} - -func BenchmarkPriorityQueue_Concurrent(b *testing.B) { - prioritizer := NewMessagePrioritizer(mockState) - queue := NewDefault() - - messageCount := 10_000 - types := []qbft.MessageType{qbft.PrepareMsgType, qbft.CommitMsgType, qbft.RoundChangeMsgType} - msgs := make(chan *GenesisSSVMessage, messageCount*len(types)) - for _, i := range rand.Perm(messageCount) { - height := qbft.FirstHeight + qbft.Height(i) - for _, t := range types { - decoded, err := DecodeGenesisSSVMessage(mockConsensusMessage{Height: height, Type: t}.ssvMessage(mockState)) - require.NoError(b, err) - msgs <- decoded - } - } - - b.ResetTimer() - b.StartTimer() - - var pushersWg sync.WaitGroup - var pushed atomic.Int32 - for i := 0; i < 16; i++ { - pushersWg.Add(1) - go func() { - defer pushersWg.Done() - for n := b.N; n > 0; n-- { - select { - case msg := <-msgs: - queue.Push(msg) - pushed.Add(1) - default: - } - } - }() - } - - pushersCtx, cancel := context.WithCancel(context.Background()) - go func() { - pushersWg.Wait() - cancel() - }() - - var popperWg sync.WaitGroup - popperWg.Add(1) - popped := 0 - go func() { - defer popperWg.Done() - for n := b.N; n > 0; n-- { - msg := queue.Pop(pushersCtx, prioritizer, FilterAny) - if msg == nil { - return - } - popped++ - } - }() - - popperWg.Wait() - - b.Logf("popped %d messages", popped) - b.Logf("pushed %d messages", pushed.Load()) -} - -func decodeAndPush(t require.TestingT, queue Queue, msg mockMessage, state *State) *GenesisSSVMessage { - decoded, err := DecodeGenesisSSVMessage(msg.ssvMessage(state)) - require.NoError(t, err) - queue.Push(decoded) - return decoded -} diff --git a/protocol/genesis/ssv/runner/aggregator.go b/protocol/genesis/ssv/runner/aggregator.go deleted file mode 100644 index e0e352d1b5..0000000000 --- a/protocol/genesis/ssv/runner/aggregator.go +++ /dev/null @@ -1,340 +0,0 @@ -package runner - -import ( - "crypto/sha256" - "encoding/json" - spectypes "github.com/ssvlabs/ssv-spec/types" - - "github.com/attestantio/go-eth2-client/spec/phase0" - ssz "github.com/ferranbt/fastssz" - "github.com/pkg/errors" - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - genesisspecssv "github.com/ssvlabs/ssv-spec-pre-cc/ssv" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - "go.uber.org/zap" - - "github.com/ssvlabs/ssv/logging/fields" - "github.com/ssvlabs/ssv/protocol/genesis/qbft/controller" - "github.com/ssvlabs/ssv/protocol/genesis/ssv/runner/metrics" -) - -type AggregatorRunner struct { - BaseRunner *BaseRunner - - beacon genesisspecssv.BeaconNode - network genesisspecssv.Network - signer genesisspectypes.KeyManager - valCheck genesisspecqbft.ProposedValueCheckF - metrics metrics.ConsensusMetrics -} - -var _ Runner = &AggregatorRunner{} - -func NewAggregatorRunner( - domainType spectypes.DomainType, - beaconNetwork genesisspectypes.BeaconNetwork, - share *genesisspectypes.Share, - qbftController *controller.Controller, - beacon genesisspecssv.BeaconNode, - network genesisspecssv.Network, - signer genesisspectypes.KeyManager, - valCheck genesisspecqbft.ProposedValueCheckF, - highestDecidedSlot phase0.Slot, -) Runner { - return &AggregatorRunner{ - BaseRunner: &BaseRunner{ - BeaconRoleType: genesisspectypes.BNRoleAggregator, - DomainType: domainType, - BeaconNetwork: beaconNetwork, - Share: share, - QBFTController: qbftController, - highestDecidedSlot: highestDecidedSlot, - }, - beacon: beacon, - network: network, - signer: signer, - valCheck: valCheck, - metrics: metrics.NewConsensusMetrics(genesisspectypes.BNRoleAggregator), - } -} - -func (r *AggregatorRunner) StartNewDuty(logger *zap.Logger, duty *genesisspectypes.Duty) error { - return r.BaseRunner.baseStartNewDuty(logger, r, duty) -} - -// HasRunningDuty returns true if a duty is already running (StartNewDuty called and returned nil) -func (r *AggregatorRunner) HasRunningDuty() bool { - return r.BaseRunner.hasRunningDuty() -} - -func (r *AggregatorRunner) ProcessPreConsensus(logger *zap.Logger, signedMsg *genesisspectypes.SignedPartialSignatureMessage) error { - quorum, roots, err := r.BaseRunner.basePreConsensusMsgProcessing(r, signedMsg) - if err != nil { - return errors.Wrap(err, "failed processing selection proof message") - } - // quorum returns true only once (first time quorum achieved) - if !quorum { - return nil - } - - r.metrics.EndPreConsensus() - - // only 1 root, verified by basePreConsensusMsgProcessing - root := roots[0] - // reconstruct selection proof sig - fullSig, err := r.GetState().ReconstructBeaconSig(r.GetState().PreConsensusContainer, root, r.GetShare().ValidatorPubKey) - if err != nil { - // If the reconstructed signature verification failed, fall back to verifying each partial signature - r.BaseRunner.FallBackAndVerifyEachSignature(r.GetState().PreConsensusContainer, root) - return errors.Wrap(err, "got pre-consensus quorum but it has invalid signatures") - } - - duty := r.GetState().StartingDuty - - logger.Debug("🧩 got partial signature quorum", - zap.Any("signer", signedMsg.Signer), - fields.Slot(duty.Slot), - ) - - r.metrics.PauseDutyFullFlow() - - // get block data - res, ver, err := r.GetBeaconNode().SubmitAggregateSelectionProof(duty.Slot, duty.CommitteeIndex, duty.CommitteeLength, duty.ValidatorIndex, fullSig) - if err != nil { - return errors.Wrap(err, "failed to submit aggregate and proof") - } - - r.metrics.ContinueDutyFullFlow() - r.metrics.StartConsensus() - - byts, err := res.MarshalSSZ() - if err != nil { - return errors.Wrap(err, "could not marshal aggregate and proof") - } - input := &genesisspectypes.ConsensusData{ - Duty: *duty, - Version: ver, - DataSSZ: byts, - } - - if err := r.BaseRunner.decide(logger, r, input); err != nil { - return errors.Wrap(err, "can't start new duty runner instance for duty") - } - - return nil -} - -func (r *AggregatorRunner) ProcessConsensus(logger *zap.Logger, signedMsg *genesisspecqbft.SignedMessage) error { - decided, decidedValue, err := r.BaseRunner.baseConsensusMsgProcessing(logger, r, signedMsg) - if err != nil { - return errors.Wrap(err, "failed processing consensus message") - } - - // Decided returns true only once so if it is true it must be for the current running instance - if !decided { - return nil - } - - r.metrics.EndConsensus() - r.metrics.StartPostConsensus() - - aggregateAndProof, err := decidedValue.GetAggregateAndProof() - if err != nil { - return errors.Wrap(err, "could not get aggregate and proof") - } - - // specific duty sig - msg, err := r.BaseRunner.signBeaconObject(r, aggregateAndProof, decidedValue.Duty.Slot, genesisspectypes.DomainAggregateAndProof) - if err != nil { - return errors.Wrap(err, "failed signing attestation data") - } - postConsensusMsg := &genesisspectypes.PartialSignatureMessages{ - Type: genesisspectypes.PostConsensusPartialSig, - Slot: decidedValue.Duty.Slot, - Messages: []*genesisspectypes.PartialSignatureMessage{msg}, - } - - postSignedMsg, err := r.BaseRunner.signPostConsensusMsg(r, postConsensusMsg) - if err != nil { - return errors.Wrap(err, "could not sign post consensus msg") - } - - data, err := postSignedMsg.Encode() - if err != nil { - return errors.Wrap(err, "failed to encode post consensus signature msg") - } - - msgToBroadcast := &genesisspectypes.SSVMessage{ - MsgType: genesisspectypes.SSVPartialSignatureMsgType, - MsgID: genesisspectypes.NewMsgID(r.GetShare().DomainType, r.GetShare().ValidatorPubKey, r.BaseRunner.BeaconRoleType), - Data: data, - } - - if err := r.GetNetwork().Broadcast(msgToBroadcast); err != nil { - return errors.Wrap(err, "can't broadcast partial post consensus sig") - } - return nil -} - -func (r *AggregatorRunner) ProcessPostConsensus(logger *zap.Logger, signedMsg *genesisspectypes.SignedPartialSignatureMessage) error { - quorum, roots, err := r.BaseRunner.basePostConsensusMsgProcessing(logger, r, signedMsg) - if err != nil { - return errors.Wrap(err, "failed processing post consensus message") - } - - if !quorum { - return nil - } - - r.metrics.EndPostConsensus() - - for _, root := range roots { - sig, err := r.GetState().ReconstructBeaconSig(r.GetState().PostConsensusContainer, root, r.GetShare().ValidatorPubKey) - if err != nil { - // If the reconstructed signature verification failed, fall back to verifying each partial signature - for _, root := range roots { - r.BaseRunner.FallBackAndVerifyEachSignature(r.GetState().PostConsensusContainer, root) - } - return errors.Wrap(err, "got post-consensus quorum but it has invalid signatures") - } - specSig := phase0.BLSSignature{} - copy(specSig[:], sig) - - aggregateAndProof, err := r.GetState().DecidedValue.GetAggregateAndProof() - if err != nil { - return errors.Wrap(err, "could not get aggregate and proof") - } - - msg := &phase0.SignedAggregateAndProof{ - Message: aggregateAndProof, - Signature: specSig, - } - - proofSubmissionEnd := r.metrics.StartBeaconSubmission() - - if err := r.GetBeaconNode().SubmitSignedAggregateSelectionProof(msg); err != nil { - r.metrics.RoleSubmissionFailed() - return errors.Wrap(err, "could not submit to Beacon chain reconstructed signed aggregate") - } - - proofSubmissionEnd() - r.metrics.EndDutyFullFlow(r.GetState().RunningInstance.State.Round) - r.metrics.RoleSubmitted() - - logger.Debug("✅ successful submitted aggregate") - } - r.GetState().Finished = true - - return nil -} - -func (r *AggregatorRunner) expectedPreConsensusRootsAndDomain() ([]ssz.HashRoot, phase0.DomainType, error) { - return []ssz.HashRoot{genesisspectypes.SSZUint64(r.GetState().StartingDuty.Slot)}, genesisspectypes.DomainSelectionProof, nil -} - -// expectedPostConsensusRootsAndDomain an INTERNAL function, returns the expected post-consensus roots to sign -func (r *AggregatorRunner) expectedPostConsensusRootsAndDomain() ([]ssz.HashRoot, phase0.DomainType, error) { - aggregateAndProof, err := r.GetState().DecidedValue.GetAggregateAndProof() - if err != nil { - return nil, phase0.DomainType{}, errors.Wrap(err, "could not get aggregate and proof") - } - - return []ssz.HashRoot{aggregateAndProof}, genesisspectypes.DomainAggregateAndProof, nil -} - -// executeDuty steps: -// 1) sign a partial selection proof and wait for 2f+1 partial sigs from peers -// 2) reconstruct selection proof and send SubmitAggregateSelectionProof to BN -// 3) start consensus on duty + aggregation data -// 4) Once consensus decides, sign partial aggregation data and broadcast -// 5) collect 2f+1 partial sigs, reconstruct and broadcast valid SignedAggregateSubmitRequest sig to the BN -func (r *AggregatorRunner) executeDuty(logger *zap.Logger, duty *genesisspectypes.Duty) error { - r.metrics.StartDutyFullFlow() - r.metrics.StartPreConsensus() - - // sign selection proof - msg, err := r.BaseRunner.signBeaconObject(r, genesisspectypes.SSZUint64(duty.Slot), duty.Slot, genesisspectypes.DomainSelectionProof) - if err != nil { - return errors.Wrap(err, "could not sign randao") - } - msgs := genesisspectypes.PartialSignatureMessages{ - Type: genesisspectypes.SelectionProofPartialSig, - Slot: duty.Slot, - Messages: []*genesisspectypes.PartialSignatureMessage{msg}, - } - - // sign msg - signature, err := r.GetSigner().SignRoot(msgs, genesisspectypes.PartialSignatureType, r.GetShare().SharePubKey) - if err != nil { - return errors.Wrap(err, "could not sign PartialSignatureMessage for selection proof") - } - signedPartialMsg := &genesisspectypes.SignedPartialSignatureMessage{ - Message: msgs, - Signature: signature, - Signer: r.GetShare().OperatorID, - } - - // broadcast - data, err := signedPartialMsg.Encode() - if err != nil { - return errors.Wrap(err, "failed to encode selection proof pre-consensus signature msg") - } - msgToBroadcast := &genesisspectypes.SSVMessage{ - MsgType: genesisspectypes.SSVPartialSignatureMsgType, - MsgID: genesisspectypes.NewMsgID(r.GetShare().DomainType, r.GetShare().ValidatorPubKey, r.BaseRunner.BeaconRoleType), - Data: data, - } - if err := r.GetNetwork().Broadcast(msgToBroadcast); err != nil { - return errors.Wrap(err, "can't broadcast partial selection proof sig") - } - return nil -} - -func (r *AggregatorRunner) GetBaseRunner() *BaseRunner { - return r.BaseRunner -} - -func (r *AggregatorRunner) GetNetwork() genesisspecssv.Network { - return r.network -} - -func (r *AggregatorRunner) GetBeaconNode() genesisspecssv.BeaconNode { - return r.beacon -} - -func (r *AggregatorRunner) GetShare() *genesisspectypes.Share { - return r.BaseRunner.Share -} - -func (r *AggregatorRunner) GetState() *State { - return r.BaseRunner.State -} - -func (r *AggregatorRunner) GetValCheckF() genesisspecqbft.ProposedValueCheckF { - return r.valCheck -} - -func (r *AggregatorRunner) GetSigner() genesisspectypes.KeyManager { - return r.signer -} - -// Encode returns the encoded struct in bytes or error -func (r *AggregatorRunner) Encode() ([]byte, error) { - return json.Marshal(r) -} - -// Decode returns error if decoding failed -func (r *AggregatorRunner) Decode(data []byte) error { - return json.Unmarshal(data, &r) -} - -// GetRoot returns the root used for signing and verification -// GetRoot returns the root used for signing and verification -func (r *AggregatorRunner) GetRoot() ([32]byte, error) { - marshaledRoot, err := r.Encode() - if err != nil { - return [32]byte{}, errors.Wrap(err, "could not encode DutyRunnerState") - } - ret := sha256.Sum256(marshaledRoot) - return ret, nil -} diff --git a/protocol/genesis/ssv/runner/attester.go b/protocol/genesis/ssv/runner/attester.go deleted file mode 100644 index da2aa16a32..0000000000 --- a/protocol/genesis/ssv/runner/attester.go +++ /dev/null @@ -1,299 +0,0 @@ -package runner - -import ( - "crypto/sha256" - "encoding/hex" - "encoding/json" - spectypes "github.com/ssvlabs/ssv-spec/types" - "time" - - "github.com/attestantio/go-eth2-client/spec/phase0" - ssz "github.com/ferranbt/fastssz" - "github.com/pkg/errors" - "github.com/prysmaticlabs/go-bitfield" - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - genesisspecssv "github.com/ssvlabs/ssv-spec-pre-cc/ssv" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - specqbft "github.com/ssvlabs/ssv-spec/qbft" - "go.uber.org/zap" - - "github.com/ssvlabs/ssv/logging/fields" - "github.com/ssvlabs/ssv/protocol/genesis/qbft/controller" - "github.com/ssvlabs/ssv/protocol/genesis/ssv/runner/metrics" -) - -type AttesterRunner struct { - BaseRunner *BaseRunner - - beacon genesisspecssv.BeaconNode - network genesisspecssv.Network - signer genesisspectypes.KeyManager - valCheck genesisspecqbft.ProposedValueCheckF - - started time.Time - metrics metrics.ConsensusMetrics -} - -func NewAttesterRunnner( - domainType spectypes.DomainType, - beaconNetwork genesisspectypes.BeaconNetwork, - share *genesisspectypes.Share, - qbftController *controller.Controller, - beacon genesisspecssv.BeaconNode, - network genesisspecssv.Network, - signer genesisspectypes.KeyManager, - valCheck genesisspecqbft.ProposedValueCheckF, - highestDecidedSlot phase0.Slot, -) Runner { - return &AttesterRunner{ - BaseRunner: &BaseRunner{ - BeaconRoleType: genesisspectypes.BNRoleAttester, - DomainType: domainType, - BeaconNetwork: beaconNetwork, - Share: share, - QBFTController: qbftController, - highestDecidedSlot: highestDecidedSlot, - }, - - beacon: beacon, - network: network, - signer: signer, - valCheck: valCheck, - - metrics: metrics.NewConsensusMetrics(genesisspectypes.BNRoleAttester), - } -} - -func (r *AttesterRunner) StartNewDuty(logger *zap.Logger, duty *genesisspectypes.Duty) error { - return r.BaseRunner.baseStartNewDuty(logger, r, duty) -} - -// HasRunningDuty returns true if a duty is already running (StartNewDuty called and returned nil) -func (r *AttesterRunner) HasRunningDuty() bool { - return r.BaseRunner.hasRunningDuty() -} - -func (r *AttesterRunner) ProcessPreConsensus(logger *zap.Logger, signedMsg *genesisspectypes.SignedPartialSignatureMessage) error { - return errors.New("no pre consensus sigs required for attester role") -} - -func (r *AttesterRunner) ProcessConsensus(logger *zap.Logger, signedMsg *genesisspecqbft.SignedMessage) error { - decided, decidedValue, err := r.BaseRunner.baseConsensusMsgProcessing(logger, r, signedMsg) - if err != nil { - return errors.Wrap(err, "failed processing consensus message") - } - - // Decided returns true only once so if it is true it must be for the current running instance - if !decided { - return nil - } - - r.metrics.EndConsensus() - r.metrics.StartPostConsensus() - - attestationData, err := decidedValue.GetAttestationData() - if err != nil { - return errors.Wrap(err, "could not get attestation data") - } - - // specific duty sig - msg, err := r.BaseRunner.signBeaconObject(r, attestationData, decidedValue.Duty.Slot, genesisspectypes.DomainAttester) - if err != nil { - return errors.Wrap(err, "failed signing attestation data") - } - postConsensusMsg := &genesisspectypes.PartialSignatureMessages{ - Type: genesisspectypes.PostConsensusPartialSig, - Slot: decidedValue.Duty.Slot, - Messages: []*genesisspectypes.PartialSignatureMessage{msg}, - } - - postSignedMsg, err := r.BaseRunner.signPostConsensusMsg(r, postConsensusMsg) - if err != nil { - return errors.Wrap(err, "could not sign post consensus msg") - } - - data, err := postSignedMsg.Encode() - if err != nil { - return errors.Wrap(err, "failed to encode post consensus signature msg") - } - - msgToBroadcast := &genesisspectypes.SSVMessage{ - MsgType: genesisspectypes.SSVPartialSignatureMsgType, - MsgID: genesisspectypes.NewMsgID(genesisspectypes.DomainType(r.BaseRunner.DomainType), r.GetShare().ValidatorPubKey, r.BaseRunner.BeaconRoleType), - Data: data, - } - - if err := r.GetNetwork().Broadcast(msgToBroadcast); err != nil { - return errors.Wrap(err, "can't broadcast partial post consensus sig") - } - return nil -} - -func (r *AttesterRunner) ProcessPostConsensus(logger *zap.Logger, signedMsg *genesisspectypes.SignedPartialSignatureMessage) error { - quorum, roots, err := r.BaseRunner.basePostConsensusMsgProcessing(logger, r, signedMsg) - if err != nil { - return errors.Wrap(err, "failed processing post consensus message") - } - - duty := r.GetState().DecidedValue.Duty - logger = logger.With(fields.Slot(duty.Slot)) - logger.Debug("🧩 got partial signatures", - zap.Uint64("signer", signedMsg.Signer)) - - if !quorum { - return nil - } - - r.metrics.EndPostConsensus() - - attestationData, err := r.GetState().DecidedValue.GetAttestationData() - if err != nil { - return errors.Wrap(err, "could not get attestation data") - } - - for _, root := range roots { - sig, err := r.GetState().ReconstructBeaconSig(r.GetState().PostConsensusContainer, root, r.GetShare().ValidatorPubKey) - if err != nil { - // If the reconstructed signature verification failed, fall back to verifying each partial signature - for _, root := range roots { - r.BaseRunner.FallBackAndVerifyEachSignature(r.GetState().PostConsensusContainer, root) - } - return errors.Wrap(err, "got post-consensus quorum but it has invalid signatures") - } - specSig := phase0.BLSSignature{} - copy(specSig[:], sig) - - logger.Debug("🧩 reconstructed partial signatures", - zap.Uint64s("signers", getPostConsensusSigners(r.GetState(), root))) - - aggregationBitfield := bitfield.NewBitlist(r.GetState().DecidedValue.Duty.CommitteeLength) - aggregationBitfield.SetBitAt(duty.ValidatorCommitteeIndex, true) - signedAtt := &phase0.Attestation{ - Data: attestationData, - Signature: specSig, - AggregationBits: aggregationBitfield, - } - - attestationSubmissionEnd := r.metrics.StartBeaconSubmission() - consensusDuration := time.Since(r.started) - - // Submit it to the BN. - start := time.Now() - if err := r.beacon.SubmitAttestation(signedAtt); err != nil { - r.metrics.RoleSubmissionFailed() - logger.Error("❌ failed to submit attestation", zap.Error(err)) - return errors.Wrap(err, "could not submit to Beacon chain reconstructed attestation") - } - - attestationSubmissionEnd() - r.metrics.EndDutyFullFlow(r.GetState().RunningInstance.State.Round) - r.metrics.RoleSubmitted() - - logger.Info("✅ successfully submitted attestation", - zap.String("block_root", hex.EncodeToString(signedAtt.Data.BeaconBlockRoot[:])), - fields.ConsensusTime(consensusDuration), - fields.SubmissionTime(time.Since(start)), - fields.Height(specqbft.Height(r.BaseRunner.QBFTController.Height)), - fields.Round(specqbft.Round(r.GetState().RunningInstance.State.Round))) - } - r.GetState().Finished = true - - return nil -} - -func (r *AttesterRunner) expectedPreConsensusRootsAndDomain() ([]ssz.HashRoot, phase0.DomainType, error) { - return []ssz.HashRoot{}, genesisspectypes.DomainError, errors.New("no expected pre consensus roots for attester") -} - -// expectedPostConsensusRootsAndDomain an INTERNAL function, returns the expected post-consensus roots to sign -func (r *AttesterRunner) expectedPostConsensusRootsAndDomain() ([]ssz.HashRoot, phase0.DomainType, error) { - attestationData, err := r.GetState().DecidedValue.GetAttestationData() - if err != nil { - return nil, phase0.DomainType{}, errors.Wrap(err, "could not get attestation data") - } - - return []ssz.HashRoot{attestationData}, genesisspectypes.DomainAttester, nil -} - -// executeDuty steps: -// 1) get attestation data from BN -// 2) start consensus on duty + attestation data -// 3) Once consensus decides, sign partial attestation and broadcast -// 4) collect 2f+1 partial sigs, reconstruct and broadcast valid attestation sig to the BN -func (r *AttesterRunner) executeDuty(logger *zap.Logger, duty *genesisspectypes.Duty) error { - start := time.Now() - attData, ver, err := r.GetBeaconNode().GetAttestationData(duty.Slot, duty.CommitteeIndex) - if err != nil { - return errors.Wrap(err, "failed to get attestation data") - } - logger = logger.With(zap.Duration("attestation_data_time", time.Since(start))) - - r.started = time.Now() - - r.metrics.StartDutyFullFlow() - r.metrics.StartConsensus() - - attDataByts, err := attData.MarshalSSZ() - if err != nil { - return errors.Wrap(err, "could not marshal attestation data") - } - - input := &genesisspectypes.ConsensusData{ - Duty: *duty, - Version: ver, - DataSSZ: attDataByts, - } - - if err := r.BaseRunner.decide(logger, r, input); err != nil { - return errors.Wrap(err, "can't start new duty runner instance for duty") - } - return nil -} - -func (r *AttesterRunner) GetBaseRunner() *BaseRunner { - return r.BaseRunner -} - -func (r *AttesterRunner) GetNetwork() genesisspecssv.Network { - return r.network -} - -func (r *AttesterRunner) GetBeaconNode() genesisspecssv.BeaconNode { - return r.beacon -} - -func (r *AttesterRunner) GetShare() *genesisspectypes.Share { - return r.BaseRunner.Share -} - -func (r *AttesterRunner) GetState() *State { - return r.BaseRunner.State -} - -func (r *AttesterRunner) GetValCheckF() genesisspecqbft.ProposedValueCheckF { - return r.valCheck -} - -func (r *AttesterRunner) GetSigner() genesisspectypes.KeyManager { - return r.signer -} - -// Encode returns the encoded struct in bytes or error -func (r *AttesterRunner) Encode() ([]byte, error) { - return json.Marshal(r) -} - -// Decode returns error if decoding failed -func (r *AttesterRunner) Decode(data []byte) error { - return json.Unmarshal(data, &r) -} - -// GetRoot returns the root used for signing and verification -func (r *AttesterRunner) GetRoot() ([32]byte, error) { - marshaledRoot, err := r.Encode() - if err != nil { - return [32]byte{}, errors.Wrap(err, "could not encode DutyRunnerState") - } - ret := sha256.Sum256(marshaledRoot) - return ret, nil -} diff --git a/protocol/genesis/ssv/runner/compact.go b/protocol/genesis/ssv/runner/compact.go deleted file mode 100644 index cd9d62c8d7..0000000000 --- a/protocol/genesis/ssv/runner/compact.go +++ /dev/null @@ -1,18 +0,0 @@ -package runner - -import ( - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - "github.com/ssvlabs/ssv/protocol/genesis/qbft/controller" - "github.com/ssvlabs/ssv/protocol/genesis/qbft/instance" -) - -// Compacts the given message's associated instance if it's either... -// - Got a decided: to discard messages that are no longer needed. (proposes, prepares and sometimes commits) -// - Advanced a round: to discard messages from previous rounds. (otherwise it might grow indefinitely) -func (b *BaseRunner) compactInstanceIfNeeded(msg *genesisspecqbft.SignedMessage) { - if inst := b.QBFTController.StoredInstances.FindInstance(msg.Message.Height); inst != nil { - if controller.IsDecidedMsg(b.Share, msg) || msg.Message.MsgType == genesisspecqbft.RoundChangeMsgType { - instance.Compact(inst.State, msg) - } - } -} diff --git a/protocol/genesis/ssv/runner/duty_runners.go b/protocol/genesis/ssv/runner/duty_runners.go deleted file mode 100644 index b22e8abc67..0000000000 --- a/protocol/genesis/ssv/runner/duty_runners.go +++ /dev/null @@ -1,12 +0,0 @@ -package runner - -import genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - -// DutyRunners is a map of duty runners mapped by msg id hex. -type DutyRunners map[genesisspectypes.BeaconRole]Runner - -// DutyRunnerForMsgID returns a Runner from the provided msg ID, or nil if not found -func (ci DutyRunners) DutyRunnerForMsgID(msgID genesisspectypes.MessageID) Runner { - role := msgID.GetRoleType() - return ci[role] -} diff --git a/protocol/genesis/ssv/runner/metrics/metrics.go b/protocol/genesis/ssv/runner/metrics/metrics.go deleted file mode 100644 index 53d6496359..0000000000 --- a/protocol/genesis/ssv/runner/metrics/metrics.go +++ /dev/null @@ -1,231 +0,0 @@ -package metrics - -import ( - "time" - - "go.uber.org/zap" - - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" -) - -var ( - metricsConsensusDuration = promauto.NewHistogramVec(prometheus.HistogramOpts{ - Name: "genesis::ssv_validator_consensus_duration_seconds", - Help: "Consensus duration (seconds)", - Buckets: []float64{0.02, 0.05, 0.1, 0.2, 0.5, 1, 5}, - }, []string{"role"}) - metricsPreConsensusDuration = promauto.NewHistogramVec(prometheus.HistogramOpts{ - Name: "genesis::ssv_validator_pre_consensus_duration_seconds", - Help: "Pre-consensus duration (seconds)", - Buckets: []float64{0.02, 0.05, 0.1, 0.2, 0.5, 1, 5}, - }, []string{"role"}) - metricsPostConsensusDuration = promauto.NewHistogramVec(prometheus.HistogramOpts{ - Name: "genesis::ssv_validator_post_consensus_duration_seconds", - Help: "Post-consensus duration (seconds)", - Buckets: []float64{0.02, 0.05, 0.1, 0.2, 0.5, 1, 5}, - }, []string{"role"}) - metricsBeaconSubmissionDuration = promauto.NewHistogramVec(prometheus.HistogramOpts{ - Name: "genesis::ssv_validator_beacon_submission_duration_seconds", - Help: "Submission to beacon node duration (seconds)", - Buckets: []float64{0.02, 0.05, 0.1, 0.2, 0.5, 1, 5}, - }, []string{"role"}) - metricsDutyFullFlowDuration = promauto.NewHistogramVec(prometheus.HistogramOpts{ - Name: "genesis::ssv_validator_duty_full_flow_duration_seconds", - Help: "Duty full flow duration (seconds)", - Buckets: []float64{0.02, 0.05, 0.1, 0.2, 0.5, 1, 5}, - }, []string{"role"}) - metricsDutyFullFlowFirstRoundDuration = promauto.NewHistogramVec(prometheus.HistogramOpts{ - Name: "genesis::ssv_validator_duty_full_flow_first_round_duration_seconds", - Help: "Duty full flow at first round duration (seconds)", - Buckets: []float64{ - 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, - 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, - 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 2.9, 3.0, - 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9, 4.0, - 4.1, 4.2, 4.3, 4.4, 4.5, 4.6, 4.7, 4.8, 4.9, 5.0, - }, - }, []string{"role"}) - metricsRolesSubmitted = promauto.NewCounterVec(prometheus.CounterOpts{ - Name: "genesis::ssv_validator_roles_submitted", - Help: "Submitted roles", - }, []string{"role"}) - metricsRolesSubmissionFailures = promauto.NewCounterVec(prometheus.CounterOpts{ - Name: "genesis::ssv_validator_roles_failed", - Help: "Submitted roles", - }, []string{"role"}) - metricsInstancesStarted = promauto.NewCounterVec(prometheus.CounterOpts{ - Name: "genesis::ssv_instances_started", - Help: "Number of started QBFT instances", - }, []string{"role"}) - metricsInstancesDecided = promauto.NewCounterVec(prometheus.CounterOpts{ - Name: "genesis::ssv_instances_decided", - Help: "Number of decided QBFT instances", - }, []string{"role"}) -) - -func init() { - metricsList := []prometheus.Collector{ - metricsConsensusDuration, - metricsPreConsensusDuration, - metricsPostConsensusDuration, - metricsBeaconSubmissionDuration, - metricsDutyFullFlowDuration, - metricsRolesSubmitted, - metricsRolesSubmissionFailures, - } - logger := zap.L() - for _, metric := range metricsList { - if err := prometheus.Register(metric); err != nil { - logger.Debug("could not register prometheus collector") - } - } -} - -// ConsensusMetrics defines metrics for consensus process. -type ConsensusMetrics struct { - preConsensus prometheus.Observer - consensus prometheus.Observer - postConsensus prometheus.Observer - beaconSubmission prometheus.Observer - dutyFullFlow prometheus.Observer - dutyFullFlowFirstRound prometheus.Observer - rolesSubmitted prometheus.Counter - rolesSubmissionFailures prometheus.Counter - metricsInstancesStarted prometheus.Counter - metricsInstancesDecided prometheus.Counter - preConsensusStart time.Time - consensusStart time.Time - postConsensusStart time.Time - dutyFullFlowStart time.Time - dutyFullFlowCumulativeDuration time.Duration -} - -func NewConsensusMetrics(role genesisspectypes.BeaconRole) ConsensusMetrics { - values := []string{role.String()} - return ConsensusMetrics{ - preConsensus: metricsPreConsensusDuration.WithLabelValues(values...), - consensus: metricsConsensusDuration.WithLabelValues(values...), - postConsensus: metricsPostConsensusDuration.WithLabelValues(values...), - beaconSubmission: metricsBeaconSubmissionDuration.WithLabelValues(values...), - dutyFullFlow: metricsDutyFullFlowDuration.WithLabelValues(values...), - dutyFullFlowFirstRound: metricsDutyFullFlowFirstRoundDuration.WithLabelValues(values...), - rolesSubmitted: metricsRolesSubmitted.WithLabelValues(values...), - rolesSubmissionFailures: metricsRolesSubmissionFailures.WithLabelValues(values...), - metricsInstancesStarted: metricsInstancesStarted.WithLabelValues(values...), - metricsInstancesDecided: metricsInstancesDecided.WithLabelValues(values...), - } -} - -// StartPreConsensus stores pre-consensus start time. -func (cm *ConsensusMetrics) StartPreConsensus() { - if cm != nil { - cm.preConsensusStart = time.Now() - } -} - -// EndPreConsensus sends metrics for pre-consensus duration. -func (cm *ConsensusMetrics) EndPreConsensus() { - if cm != nil && cm.preConsensus != nil && !cm.preConsensusStart.IsZero() { - cm.preConsensus.Observe(time.Since(cm.preConsensusStart).Seconds()) - cm.preConsensusStart = time.Time{} - } -} - -// StartConsensus stores consensus start time. -func (cm *ConsensusMetrics) StartConsensus() { - if cm != nil { - cm.consensusStart = time.Now() - cm.metricsInstancesStarted.Inc() - } -} - -// EndConsensus sends metrics for consensus duration. -func (cm *ConsensusMetrics) EndConsensus() { - if cm != nil && cm.consensus != nil && !cm.consensusStart.IsZero() { - cm.consensus.Observe(time.Since(cm.consensusStart).Seconds()) - cm.consensusStart = time.Time{} - cm.metricsInstancesDecided.Inc() - } -} - -// StartPostConsensus stores post-consensus start time. -func (cm *ConsensusMetrics) StartPostConsensus() { - if cm != nil { - cm.postConsensusStart = time.Now() - } -} - -// EndPostConsensus sends metrics for post-consensus duration. -func (cm *ConsensusMetrics) EndPostConsensus() { - if cm != nil && cm.postConsensus != nil && !cm.postConsensusStart.IsZero() { - cm.postConsensus.Observe(time.Since(cm.postConsensusStart).Seconds()) - cm.postConsensusStart = time.Time{} - } -} - -// StartDutyFullFlow stores duty full flow start time. -func (cm *ConsensusMetrics) StartDutyFullFlow() { - if cm != nil { - cm.dutyFullFlowStart = time.Now() - cm.dutyFullFlowCumulativeDuration = 0 - } -} - -// PauseDutyFullFlow stores duty full flow cumulative duration with ability to continue the flow. -func (cm *ConsensusMetrics) PauseDutyFullFlow() { - if cm != nil { - cm.dutyFullFlowCumulativeDuration += time.Since(cm.dutyFullFlowStart) - cm.dutyFullFlowStart = time.Time{} - } -} - -// ContinueDutyFullFlow continues measuring duty full flow duration. -func (cm *ConsensusMetrics) ContinueDutyFullFlow() { - if cm != nil { - cm.dutyFullFlowStart = time.Now() - } -} - -// EndDutyFullFlow sends metrics for duty full flow duration. -func (cm *ConsensusMetrics) EndDutyFullFlow(round genesisspecqbft.Round) { - if cm != nil && cm.dutyFullFlow != nil && !cm.dutyFullFlowStart.IsZero() { - cm.dutyFullFlowCumulativeDuration += time.Since(cm.dutyFullFlowStart) - cm.dutyFullFlow.Observe(cm.dutyFullFlowCumulativeDuration.Seconds()) - - if round == 1 { - cm.dutyFullFlowFirstRound.Observe(cm.dutyFullFlowCumulativeDuration.Seconds()) - } - - cm.dutyFullFlowStart = time.Time{} - cm.dutyFullFlowCumulativeDuration = 0 - } -} - -// StartBeaconSubmission returns a function that sends metrics for beacon submission duration. -func (cm *ConsensusMetrics) StartBeaconSubmission() (endBeaconSubmission func()) { - if cm == nil || cm.beaconSubmission == nil { - return func() {} - } - - start := time.Now() - return func() { - cm.beaconSubmission.Observe(time.Since(start).Seconds()) - } -} - -// RoleSubmitted increases submitted roles counter. -func (cm *ConsensusMetrics) RoleSubmitted() { - if cm != nil && cm.rolesSubmitted != nil { - cm.rolesSubmitted.Inc() - } -} - -// RoleSubmissionFailed increases non-submitted roles counter. -func (cm *ConsensusMetrics) RoleSubmissionFailed() { - if cm != nil && cm.rolesSubmissionFailures != nil { - cm.rolesSubmissionFailures.Inc() - } -} diff --git a/protocol/genesis/ssv/runner/pre_consensus_justification.go b/protocol/genesis/ssv/runner/pre_consensus_justification.go deleted file mode 100644 index 55f27eaeec..0000000000 --- a/protocol/genesis/ssv/runner/pre_consensus_justification.go +++ /dev/null @@ -1,163 +0,0 @@ -package runner - -import ( - "github.com/attestantio/go-eth2-client/spec/phase0" - "github.com/pkg/errors" - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - "github.com/ssvlabs/ssv-spec-pre-cc/ssv" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - "go.uber.org/zap" -) - -// correctQBFTState returns true if QBFT controller state requires pre-consensus justification -func (b *BaseRunner) correctQBFTState(logger *zap.Logger, msg *genesisspecqbft.SignedMessage) bool { - inst := b.QBFTController.InstanceForHeight(logger, b.QBFTController.Height) - decidedInstance := inst != nil && inst.State != nil && inst.State.Decided - - // firstHeightNotDecided is true if height == 0 (special case) and did not start yet - firstHeightNotDecided := inst == nil && b.QBFTController.Height == msg.Message.Height && msg.Message.Height == genesisspecqbft.FirstHeight - - // notFirstHeightDecided returns true if height != 0, height decided and the message is for next height - notFirstHeightDecided := decidedInstance && msg.Message.Height > genesisspecqbft.FirstHeight && b.QBFTController.Height+1 == msg.Message.Height - - return firstHeightNotDecided || notFirstHeightDecided -} - -// shouldProcessingJustificationsForHeight returns true if pre-consensus justification should be processed, false otherwise -func (b *BaseRunner) shouldProcessingJustificationsForHeight(logger *zap.Logger, msg *genesisspecqbft.SignedMessage) bool { - correctMsgTYpe := msg.Message.MsgType == genesisspecqbft.ProposalMsgType || msg.Message.MsgType == genesisspecqbft.RoundChangeMsgType - correctBeaconRole := b.BeaconRoleType == genesisspectypes.BNRoleProposer || b.BeaconRoleType == genesisspectypes.BNRoleAggregator || b.BeaconRoleType == genesisspectypes.BNRoleSyncCommitteeContribution - return b.correctQBFTState(logger, msg) && correctMsgTYpe && correctBeaconRole -} - -// validatePreConsensusJustifications returns an error if pre-consensus justification is invalid, nil otherwise -func (b *BaseRunner) validatePreConsensusJustifications(data *genesisspectypes.ConsensusData, highestDecidedDutySlot phase0.Slot) error { - //test invalid consensus data - if err := data.Validate(); err != nil { - return err - } - - if b.BeaconRoleType != data.Duty.Type { - return errors.New("wrong beacon role") - } - - if data.Duty.Slot <= highestDecidedDutySlot { - return errors.New("duty.slot <= highest decided slot") - } - - // validate justification quorum - if !b.Share.HasQuorum(len(data.PreConsensusJustifications)) { - return errors.New("no quorum") - } - - signers := make(map[genesisspectypes.OperatorID]bool) - roots := make(map[[32]byte]bool) - rootCount := 0 - partialSigContainer := ssv.NewPartialSigContainer(b.Share.Quorum) - for i, msg := range data.PreConsensusJustifications { - if err := msg.Validate(); err != nil { - return err - } - - // check unique signers - if !signers[msg.Signer] { - signers[msg.Signer] = true - } else { - return errors.New("duplicate signer") - } - - // verify all justifications have the same root count - if i == 0 { - rootCount = len(msg.Message.Messages) - } else { - if rootCount != len(msg.Message.Messages) { - return errors.New("inconsistent root count") - } - } - - // validate roots - for _, partialSigMessage := range msg.Message.Messages { - // validate roots - if i == 0 { - // check signer did not sign duplicate root - if roots[partialSigMessage.SigningRoot] { - return errors.New("duplicate signed root") - } - - // record roots - roots[partialSigMessage.SigningRoot] = true - } else { - // compare roots - if !roots[partialSigMessage.SigningRoot] { - return errors.New("inconsistent roots") - } - } - partialSigContainer.AddSignature(partialSigMessage) - } - - // verify duty.slot == msg.slot - if err := b.validatePartialSigMsgForSlot(msg, data.Duty.Slot); err != nil { - return err - } - } - - // Verify the reconstructed signature for each root - for root := range roots { - _, err := b.State.ReconstructBeaconSig(partialSigContainer, root, b.Share.ValidatorPubKey) - if err != nil { - return errors.Wrap(err, "wrong pre-consensus partial signature") - } - } - - return nil -} - -// processPreConsensusJustification processes pre-consensus justification -// highestDecidedDutySlot is the highest decided duty slot known -// is the qbft message carrying the pre-consensus justification -/** Flow: -1) needs to process justifications -2) validate data -3) validate message -4) if no running instance, run instance with consensus data duty -5) add pre-consensus sigs to container -6) decided on duty -*/ -func (b *BaseRunner) processPreConsensusJustification(logger *zap.Logger, runner Runner, highestDecidedDutySlot phase0.Slot, msg *genesisspecqbft.SignedMessage) error { - if !b.shouldProcessingJustificationsForHeight(logger, msg) { - return nil - } - - cd := &genesisspectypes.ConsensusData{} - if err := cd.Decode(msg.FullData); err != nil { - return errors.Wrap(err, "could not decoded ConsensusData") - } - - if err := b.validatePreConsensusJustifications(cd, highestDecidedDutySlot); err != nil { - return err - } - - // if no duty is running start one - if !b.hasRunningDuty() { - b.baseSetupForNewDuty(&cd.Duty) - } - - // add pre-consensus sigs to state container - var r [][32]byte - for _, signedMsg := range cd.PreConsensusJustifications { - quorum, roots, err := b.basePartialSigMsgProcessing(signedMsg, b.State.PreConsensusContainer) - if err != nil { - return errors.Wrap(err, "invalid partial sig processing") - } - - if quorum { - r = roots - break - } - } - if len(r) == 0 { - return errors.New("invalid pre-consensus justification quorum") - } - - return b.decide(logger, runner, cd) -} diff --git a/protocol/genesis/ssv/runner/proposer.go b/protocol/genesis/ssv/runner/proposer.go deleted file mode 100644 index f12dae428f..0000000000 --- a/protocol/genesis/ssv/runner/proposer.go +++ /dev/null @@ -1,499 +0,0 @@ -package runner - -import ( - "crypto/sha256" - "encoding/json" - "fmt" - spectypes "github.com/ssvlabs/ssv-spec/types" - "time" - - "github.com/attestantio/go-eth2-client/api" - apiv1capella "github.com/attestantio/go-eth2-client/api/v1/capella" - apiv1deneb "github.com/attestantio/go-eth2-client/api/v1/deneb" - "github.com/attestantio/go-eth2-client/spec/capella" - "github.com/attestantio/go-eth2-client/spec/deneb" - "github.com/attestantio/go-eth2-client/spec/phase0" - ssz "github.com/ferranbt/fastssz" - "github.com/pkg/errors" - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - genesisspecssv "github.com/ssvlabs/ssv-spec-pre-cc/ssv" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - specqbft "github.com/ssvlabs/ssv-spec/qbft" - "go.uber.org/zap" - - "github.com/attestantio/go-eth2-client/spec" - - "github.com/ssvlabs/ssv/logging/fields" - "github.com/ssvlabs/ssv/protocol/genesis/qbft/controller" - "github.com/ssvlabs/ssv/protocol/genesis/ssv/runner/metrics" -) - -type ProposerRunner struct { - BaseRunner *BaseRunner - // ProducesBlindedBlocks is true when the runner will only produce blinded blocks - ProducesBlindedBlocks bool - - beacon genesisspecssv.BeaconNode - network genesisspecssv.Network - signer genesisspectypes.KeyManager - valCheck genesisspecqbft.ProposedValueCheckF - - metrics metrics.ConsensusMetrics - graffiti []byte -} - -func NewProposerRunner( - domainType spectypes.DomainType, - beaconNetwork genesisspectypes.BeaconNetwork, - share *genesisspectypes.Share, - qbftController *controller.Controller, - beacon genesisspecssv.BeaconNode, - network genesisspecssv.Network, - signer genesisspectypes.KeyManager, - valCheck genesisspecqbft.ProposedValueCheckF, - highestDecidedSlot phase0.Slot, - graffiti []byte, -) Runner { - return &ProposerRunner{ - BaseRunner: &BaseRunner{ - BeaconRoleType: genesisspectypes.BNRoleProposer, - DomainType: domainType, - BeaconNetwork: beaconNetwork, - Share: share, - QBFTController: qbftController, - highestDecidedSlot: highestDecidedSlot, - }, - - beacon: beacon, - network: network, - signer: signer, - valCheck: valCheck, - graffiti: graffiti, - metrics: metrics.NewConsensusMetrics(genesisspectypes.BNRoleProposer), - } -} - -func (r *ProposerRunner) StartNewDuty(logger *zap.Logger, duty *genesisspectypes.Duty) error { - return r.BaseRunner.baseStartNewDuty(logger, r, duty) -} - -// HasRunningDuty returns true if a duty is already running (StartNewDuty called and returned nil) -func (r *ProposerRunner) HasRunningDuty() bool { - return r.BaseRunner.hasRunningDuty() -} - -func (r *ProposerRunner) ProcessPreConsensus(logger *zap.Logger, signedMsg *genesisspectypes.SignedPartialSignatureMessage) error { - quorum, roots, err := r.BaseRunner.basePreConsensusMsgProcessing(r, signedMsg) - if err != nil { - return errors.Wrap(err, "failed processing randao message") - } - - duty := r.GetState().StartingDuty - logger = logger.With(fields.Slot(duty.Slot)) - logger.Debug("🧩 got partial RANDAO signatures", - zap.Uint64("signer", signedMsg.Signer)) - - // quorum returns true only once (first time quorum achieved) - if !quorum { - return nil - } - - r.metrics.EndPreConsensus() - - // only 1 root, verified in basePreConsensusMsgProcessing - root := roots[0] - // randao is relevant only for block proposals, no need to check type - fullSig, err := r.GetState().ReconstructBeaconSig(r.GetState().PreConsensusContainer, root, r.GetShare().ValidatorPubKey) - if err != nil { - // If the reconstructed signature verification failed, fall back to verifying each partial signature - r.BaseRunner.FallBackAndVerifyEachSignature(r.GetState().PreConsensusContainer, root) - return errors.Wrap(err, "got pre-consensus quorum but it has invalid signatures") - } - - logger.Debug("🧩 reconstructed partial RANDAO signatures", - zap.Uint64s("signers", getPreConsensusSigners(r.GetState(), root))) - - var ver spec.DataVersion - var obj ssz.Marshaler - var start = time.Now() - if r.ProducesBlindedBlocks { - // get block data - obj, ver, err = r.GetBeaconNode().GetBlindedBeaconBlock(duty.Slot, r.graffiti, fullSig) - if err != nil { - return errors.Wrap(err, "failed to get blinded beacon block") - } - } else { - // get block data - obj, ver, err = r.GetBeaconNode().GetBeaconBlock(duty.Slot, r.graffiti, fullSig) - if err != nil { - return errors.Wrap(err, "failed to get beacon block") - } - } - took := time.Since(start) - // Log essentials about the retrieved block. - blockSummary, summarizeErr := summarizeBlock(obj) - logger.Info("🧊 got beacon block proposal", - zap.String("block_hash", blockSummary.Hash.String()), - zap.Bool("blinded", blockSummary.Blinded), - zap.Duration("took", took), - zap.NamedError("summarize_err", summarizeErr)) - - byts, err := obj.MarshalSSZ() - if err != nil { - return errors.Wrap(err, "could not marshal beacon block") - } - - input := &genesisspectypes.ConsensusData{ - Duty: *duty, - Version: ver, - DataSSZ: byts, - } - - r.metrics.StartConsensus() - if err := r.BaseRunner.decide(logger, r, input); err != nil { - return errors.Wrap(err, "can't start new duty runner instance for duty") - } - - return nil -} - -func (r *ProposerRunner) ProcessConsensus(logger *zap.Logger, signedMsg *genesisspecqbft.SignedMessage) error { - decided, decidedValue, err := r.BaseRunner.baseConsensusMsgProcessing(logger, r, signedMsg) - if err != nil { - return errors.Wrap(err, "failed processing consensus message") - } - // Decided returns true only once so if it is true it must be for the current running instance - if !decided { - return nil - } - - r.metrics.EndConsensus() - r.metrics.StartPostConsensus() - - // specific duty sig - var blkToSign ssz.HashRoot - if r.decidedBlindedBlock() { - _, blkToSign, err = decidedValue.GetBlindedBlockData() - if err != nil { - return errors.Wrap(err, "could not get blinded block data") - } - } else { - _, blkToSign, err = decidedValue.GetBlockData() - if err != nil { - return errors.Wrap(err, "could not get block data") - } - } - - msg, err := r.BaseRunner.signBeaconObject( - r, - blkToSign, - decidedValue.Duty.Slot, - genesisspectypes.DomainProposer, - ) - if err != nil { - return errors.Wrap(err, "failed signing attestation data") - } - postConsensusMsg := &genesisspectypes.PartialSignatureMessages{ - Type: genesisspectypes.PostConsensusPartialSig, - Slot: decidedValue.Duty.Slot, - Messages: []*genesisspectypes.PartialSignatureMessage{msg}, - } - - postSignedMsg, err := r.BaseRunner.signPostConsensusMsg(r, postConsensusMsg) - if err != nil { - return errors.Wrap(err, "could not sign post consensus msg") - } - data, err := postSignedMsg.Encode() - if err != nil { - return errors.Wrap(err, "failed to encode post consensus signature msg") - } - msgToBroadcast := &genesisspectypes.SSVMessage{ - MsgType: genesisspectypes.SSVPartialSignatureMsgType, - MsgID: genesisspectypes.NewMsgID(r.GetShare().DomainType, r.GetShare().ValidatorPubKey, r.BaseRunner.BeaconRoleType), - Data: data, - } - if err := r.GetNetwork().Broadcast(msgToBroadcast); err != nil { - return errors.Wrap(err, "can't broadcast partial post consensus sig") - } - return nil -} - -func (r *ProposerRunner) ProcessPostConsensus(logger *zap.Logger, signedMsg *genesisspectypes.SignedPartialSignatureMessage) error { - quorum, roots, err := r.BaseRunner.basePostConsensusMsgProcessing(logger, r, signedMsg) - if err != nil { - return errors.Wrap(err, "failed processing post consensus message") - } - if !quorum { - return nil - } - - r.metrics.EndPostConsensus() - - for _, root := range roots { - sig, err := r.GetState().ReconstructBeaconSig(r.GetState().PostConsensusContainer, root, r.GetShare().ValidatorPubKey) - if err != nil { - // If the reconstructed signature verification failed, fall back to verifying each partial signature - for _, root := range roots { - r.BaseRunner.FallBackAndVerifyEachSignature(r.GetState().PostConsensusContainer, root) - } - return errors.Wrap(err, "got post-consensus quorum but it has invalid signatures") - } - specSig := phase0.BLSSignature{} - copy(specSig[:], sig) - - logger.Debug("🧩 reconstructed partial post consensus signatures proposer", - zap.Uint64s("signers", getPostConsensusSigners(r.GetState(), root))) - - blockSubmissionEnd := r.metrics.StartBeaconSubmission() - - start := time.Now() - var blk any - if r.decidedBlindedBlock() { - vBlindedBlk, _, err := r.GetState().DecidedValue.GetBlindedBlockData() - if err != nil { - return errors.Wrap(err, "could not get blinded block") - } - blk = vBlindedBlk - - if err := r.GetBeaconNode().SubmitBlindedBeaconBlock(vBlindedBlk, specSig); err != nil { - r.metrics.RoleSubmissionFailed() - logger.Error("❌ could not submit blinded Beacon block", - fields.SubmissionTime(time.Since(start)), - zap.Error(err)) - return errors.Wrap(err, "could not submit to Beacon chain reconstructed signed blinded Beacon block") - } - } else { - vBlk, _, err := r.GetState().DecidedValue.GetBlockData() - if err != nil { - return errors.Wrap(err, "could not get block") - } - blk = vBlk - - if err := r.GetBeaconNode().SubmitBeaconBlock(vBlk, specSig); err != nil { - r.metrics.RoleSubmissionFailed() - logger.Error("❌ could not submit Beacon block", - fields.SubmissionTime(time.Since(start)), - zap.Error(err)) - - return errors.Wrap(err, "could not submit to Beacon chain reconstructed signed Beacon block") - } - } - - blockSubmissionEnd() - r.metrics.EndDutyFullFlow(r.GetState().RunningInstance.State.Round) - r.metrics.RoleSubmitted() - - blockSummary, summarizeErr := summarizeBlock(blk) - logger.Info("✅ successfully submitted block proposal", - fields.Slot(signedMsg.Message.Slot), - fields.Height(specqbft.Height(r.BaseRunner.QBFTController.Height)), - fields.Round(specqbft.Round(r.GetState().RunningInstance.State.Round)), - zap.String("block_hash", blockSummary.Hash.String()), - zap.Bool("blinded", blockSummary.Blinded), - zap.Duration("took", time.Since(start)), - zap.NamedError("summarize_err", summarizeErr)) - } - r.GetState().Finished = true - return nil -} - -// decidedBlindedBlock returns true if decided value has a blinded block, false if regular block -// WARNING!! should be called after decided only -func (r *ProposerRunner) decidedBlindedBlock() bool { - _, _, err := r.BaseRunner.State.DecidedValue.GetBlindedBlockData() - return err == nil -} - -func (r *ProposerRunner) expectedPreConsensusRootsAndDomain() ([]ssz.HashRoot, phase0.DomainType, error) { - epoch := r.BaseRunner.BeaconNetwork.EstimatedEpochAtSlot(r.GetState().StartingDuty.Slot) - return []ssz.HashRoot{genesisspectypes.SSZUint64(epoch)}, genesisspectypes.DomainRandao, nil -} - -// expectedPostConsensusRootsAndDomain an INTERNAL function, returns the expected post-consensus roots to sign -func (r *ProposerRunner) expectedPostConsensusRootsAndDomain() ([]ssz.HashRoot, phase0.DomainType, error) { - if r.decidedBlindedBlock() { - _, data, err := r.GetState().DecidedValue.GetBlindedBlockData() - if err != nil { - return nil, phase0.DomainType{}, errors.Wrap(err, "could not get blinded block data") - } - return []ssz.HashRoot{data}, genesisspectypes.DomainProposer, nil - } - - _, data, err := r.GetState().DecidedValue.GetBlockData() - if err != nil { - return nil, phase0.DomainType{}, errors.Wrap(err, "could not get block data") - } - return []ssz.HashRoot{data}, genesisspectypes.DomainProposer, nil -} - -// executeDuty steps: -// 1) sign a partial randao sig and wait for 2f+1 partial sigs from peers -// 2) reconstruct randao and send GetBeaconBlock to BN -// 3) start consensus on duty + block data -// 4) Once consensus decides, sign partial block and broadcast -// 5) collect 2f+1 partial sigs, reconstruct and broadcast valid block sig to the BN -func (r *ProposerRunner) executeDuty(logger *zap.Logger, duty *genesisspectypes.Duty) error { - r.metrics.StartDutyFullFlow() - r.metrics.StartPreConsensus() - - // sign partial randao - epoch := r.GetBeaconNode().GetBeaconNetwork().EstimatedEpochAtSlot(duty.Slot) - msg, err := r.BaseRunner.signBeaconObject(r, genesisspectypes.SSZUint64(epoch), duty.Slot, genesisspectypes.DomainRandao) - if err != nil { - return errors.Wrap(err, "could not sign randao") - } - msgs := genesisspectypes.PartialSignatureMessages{ - Type: genesisspectypes.RandaoPartialSig, - Slot: duty.Slot, - Messages: []*genesisspectypes.PartialSignatureMessage{msg}, - } - - // sign msg - signature, err := r.GetSigner().SignRoot(msgs, genesisspectypes.PartialSignatureType, r.GetShare().SharePubKey) - if err != nil { - return errors.Wrap(err, "could not sign randao msg") - } - signedPartialMsg := &genesisspectypes.SignedPartialSignatureMessage{ - Message: msgs, - Signature: signature, - Signer: r.GetShare().OperatorID, - } - - // broadcast - data, err := signedPartialMsg.Encode() - if err != nil { - return errors.Wrap(err, "failed to encode randao pre-consensus signature msg") - } - msgToBroadcast := &genesisspectypes.SSVMessage{ - MsgType: genesisspectypes.SSVPartialSignatureMsgType, - MsgID: genesisspectypes.NewMsgID(r.GetShare().DomainType, r.GetShare().ValidatorPubKey, r.BaseRunner.BeaconRoleType), - Data: data, - } - if err := r.GetNetwork().Broadcast(msgToBroadcast); err != nil { - return errors.Wrap(err, "can't broadcast partial randao sig") - } - - logger.Debug("🔏 signed & broadcasted partial RANDAO signature") - - return nil -} - -func (r *ProposerRunner) GetBaseRunner() *BaseRunner { - return r.BaseRunner -} - -func (r *ProposerRunner) GetNetwork() genesisspecssv.Network { - return r.network -} - -func (r *ProposerRunner) GetBeaconNode() genesisspecssv.BeaconNode { - return r.beacon -} - -func (r *ProposerRunner) GetShare() *genesisspectypes.Share { - return r.BaseRunner.Share -} - -func (r *ProposerRunner) GetState() *State { - return r.BaseRunner.State -} - -func (r *ProposerRunner) GetValCheckF() genesisspecqbft.ProposedValueCheckF { - return r.valCheck -} - -func (r *ProposerRunner) GetSigner() genesisspectypes.KeyManager { - return r.signer -} - -// Encode returns the encoded struct in bytes or error -func (r *ProposerRunner) Encode() ([]byte, error) { - return json.Marshal(r) -} - -// Decode returns error if decoding failed -func (r *ProposerRunner) Decode(data []byte) error { - return json.Unmarshal(data, &r) -} - -// GetRoot returns the root used for signing and verification -func (r *ProposerRunner) GetRoot() ([32]byte, error) { - marshaledRoot, err := r.Encode() - if err != nil { - return [32]byte{}, errors.Wrap(err, "could not encode DutyRunnerState") - } - ret := sha256.Sum256(marshaledRoot) - return ret, nil -} - -// blockSummary contains essentials about a block. Useful for logging. -type blockSummary struct { - Hash phase0.Hash32 - Blinded bool - Version spec.DataVersion -} - -// summarizeBlock returns a blockSummary for the given block. -func summarizeBlock(block any) (summary blockSummary, err error) { - if block == nil { - return summary, fmt.Errorf("block is nil") - } - switch b := block.(type) { - case *api.VersionedProposal: - if b.Blinded { - switch b.Version { - case spec.DataVersionCapella: - return summarizeBlock(b.CapellaBlinded) - case spec.DataVersionDeneb: - return summarizeBlock(b.DenebBlinded) - default: - return summary, fmt.Errorf("unsupported blinded block version %d", b.Version) - } - } - switch b.Version { - case spec.DataVersionCapella: - return summarizeBlock(b.Capella) - case spec.DataVersionDeneb: - if b.Deneb == nil { - return summary, fmt.Errorf("deneb block contents is nil") - } - return summarizeBlock(b.Deneb.Block) - default: - return summary, fmt.Errorf("unsupported block version %d", b.Version) - } - - case *capella.BeaconBlock: - if b == nil || b.Body == nil || b.Body.ExecutionPayload == nil { - return summary, fmt.Errorf("block, body or execution payload is nil") - } - summary.Hash = b.Body.ExecutionPayload.BlockHash - summary.Version = spec.DataVersionCapella - - case *deneb.BeaconBlock: - if b == nil || b.Body == nil || b.Body.ExecutionPayload == nil { - return summary, fmt.Errorf("block, body or execution payload is nil") - } - summary.Hash = b.Body.ExecutionPayload.BlockHash - summary.Version = spec.DataVersionDeneb - - case *apiv1deneb.BlockContents: - return summarizeBlock(b.Block) - - case *apiv1capella.BlindedBeaconBlock: - if b == nil || b.Body == nil || b.Body.ExecutionPayloadHeader == nil { - return summary, fmt.Errorf("block, body or execution payload header is nil") - } - summary.Hash = b.Body.ExecutionPayloadHeader.BlockHash - summary.Blinded = true - summary.Version = spec.DataVersionCapella - - case *apiv1deneb.BlindedBeaconBlock: - if b == nil || b.Body == nil || b.Body.ExecutionPayloadHeader == nil { - return summary, fmt.Errorf("block, body or execution payload header is nil") - } - summary.Hash = b.Body.ExecutionPayloadHeader.BlockHash - summary.Blinded = true - summary.Version = spec.DataVersionDeneb - } - return -} diff --git a/protocol/genesis/ssv/runner/runner.go b/protocol/genesis/ssv/runner/runner.go deleted file mode 100644 index 6450218ba6..0000000000 --- a/protocol/genesis/ssv/runner/runner.go +++ /dev/null @@ -1,307 +0,0 @@ -package runner - -import ( - spectypes "github.com/ssvlabs/ssv-spec/types" - "sync" - - spec "github.com/attestantio/go-eth2-client/spec/phase0" - ssz "github.com/ferranbt/fastssz" - "github.com/pkg/errors" - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - genesisspecssv "github.com/ssvlabs/ssv-spec-pre-cc/ssv" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - "go.uber.org/zap" - - "github.com/ssvlabs/ssv/protocol/genesis/qbft/controller" -) - -type Getters interface { - GetBaseRunner() *BaseRunner - GetBeaconNode() genesisspecssv.BeaconNode - GetValCheckF() genesisspecqbft.ProposedValueCheckF - GetSigner() genesisspectypes.KeyManager - GetNetwork() genesisspecssv.Network -} - -type Runner interface { - genesisspectypes.Encoder - genesisspectypes.Root - Getters - - // StartNewDuty starts a new duty for the runner, returns error if can't - StartNewDuty(logger *zap.Logger, duty *genesisspectypes.Duty) error - // HasRunningDuty returns true if it has a running duty - HasRunningDuty() bool - // ProcessPreConsensus processes all pre-consensus msgs, returns error if can't process - ProcessPreConsensus(logger *zap.Logger, signedMsg *genesisspectypes.SignedPartialSignatureMessage) error - // ProcessConsensus processes all consensus msgs, returns error if can't process - ProcessConsensus(logger *zap.Logger, msg *genesisspecqbft.SignedMessage) error - // ProcessPostConsensus processes all post-consensus msgs, returns error if can't process - ProcessPostConsensus(logger *zap.Logger, signedMsg *genesisspectypes.SignedPartialSignatureMessage) error - // expectedPreConsensusRootsAndDomain an INTERNAL function, returns the expected pre-consensus roots to sign - expectedPreConsensusRootsAndDomain() ([]ssz.HashRoot, spec.DomainType, error) - // expectedPostConsensusRootsAndDomain an INTERNAL function, returns the expected post-consensus roots to sign - expectedPostConsensusRootsAndDomain() ([]ssz.HashRoot, spec.DomainType, error) - // executeDuty an INTERNAL function, executes a duty. - executeDuty(logger *zap.Logger, duty *genesisspectypes.Duty) error -} - -type BaseRunner struct { - mtx sync.RWMutex - State *State - Share *genesisspectypes.Share - QBFTController *controller.Controller - DomainType spectypes.DomainType `json:"-"` - BeaconNetwork genesisspectypes.BeaconNetwork - BeaconRoleType genesisspectypes.BeaconRole - - // implementation vars - TimeoutF TimeoutF `json:"-"` - - // highestDecidedSlot holds the highest decided duty slot and gets updated after each decided is reached - highestDecidedSlot spec.Slot -} - -// SetHighestDecidedSlot set highestDecidedSlot for base runner -func (b *BaseRunner) SetHighestDecidedSlot(slot spec.Slot) { - b.highestDecidedSlot = slot -} - -// setupForNewDuty is sets the runner for a new duty -func (b *BaseRunner) baseSetupForNewDuty(duty *genesisspectypes.Duty) { - // start new state - state := NewRunnerState(b.Share.Quorum, duty) - - // TODO: potentially incomplete locking of b.State. runner.Execute(duty) has access to - // b.State but currently does not write to it - b.mtx.Lock() // writes to b.State - b.State = state - b.mtx.Unlock() -} - -func NewBaseRunner( - state *State, - share *genesisspectypes.Share, - controller *controller.Controller, - beaconNetwork genesisspectypes.BeaconNetwork, - beaconRoleType genesisspectypes.BeaconRole, - highestDecidedSlot spec.Slot, -) *BaseRunner { - return &BaseRunner{ - State: state, - Share: share, - QBFTController: controller, - BeaconNetwork: beaconNetwork, - BeaconRoleType: beaconRoleType, - highestDecidedSlot: highestDecidedSlot, - } -} - -// baseStartNewDuty is a base func that all runner implementation can call to start a duty -func (b *BaseRunner) baseStartNewDuty(logger *zap.Logger, runner Runner, duty *genesisspectypes.Duty) error { - if err := b.ShouldProcessDuty(duty); err != nil { - return errors.Wrap(err, "can't start duty") - } - - b.baseSetupForNewDuty(duty) - - return runner.executeDuty(logger, duty) -} - -// baseStartNewBeaconDuty is a base func that all runner implementation can call to start a non-beacon duty -func (b *BaseRunner) baseStartNewNonBeaconDuty(logger *zap.Logger, runner Runner, duty *genesisspectypes.Duty) error { - if err := b.ShouldProcessNonBeaconDuty(duty); err != nil { - return errors.Wrap(err, "can't start non-beacon duty") - } - b.baseSetupForNewDuty(duty) - return runner.executeDuty(logger, duty) -} - -// basePreConsensusMsgProcessing is a base func that all runner implementation can call for processing a pre-consensus msg -func (b *BaseRunner) basePreConsensusMsgProcessing(runner Runner, signedMsg *genesisspectypes.SignedPartialSignatureMessage) (bool, [][32]byte, error) { - if err := b.ValidatePreConsensusMsg(runner, signedMsg); err != nil { - return false, nil, errors.Wrap(err, "invalid pre-consensus message") - } - - hasQuorum, roots, err := b.basePartialSigMsgProcessing(signedMsg, b.State.PreConsensusContainer) - return hasQuorum, roots, errors.Wrap(err, "could not process pre-consensus partial signature msg") -} - -// baseConsensusMsgProcessing is a base func that all runner implementation can call for processing a consensus msg -func (b *BaseRunner) baseConsensusMsgProcessing(logger *zap.Logger, runner Runner, msg *genesisspecqbft.SignedMessage) (decided bool, decidedValue *genesisspectypes.ConsensusData, err error) { - prevDecided := false - if b.hasRunningDuty() && b.State != nil && b.State.RunningInstance != nil { - prevDecided, _ = b.State.RunningInstance.IsDecided() - } - - // TODO: revert after pre-consensus liveness is fixed - if false { - if err := b.processPreConsensusJustification(logger, runner, b.highestDecidedSlot, msg); err != nil { - return false, nil, errors.Wrap(err, "invalid pre-consensus justification") - } - } - - decidedMsg, err := b.QBFTController.ProcessMsg(logger, msg) - b.compactInstanceIfNeeded(msg) - if err != nil { - return false, nil, err - } - - // we allow all consensus msgs to be processed, once the process finishes we check if there is an actual running duty - // do not return error if no running duty - if !b.hasRunningDuty() { - return false, nil, nil - } - - if decideCorrectly, err := b.didDecideCorrectly(prevDecided, decidedMsg); !decideCorrectly { - return false, nil, err - } else { - if inst := b.QBFTController.StoredInstances.FindInstance(decidedMsg.Message.Height); inst != nil { - logger := logger.With( - zap.Uint64("msg_height", uint64(msg.Message.Height)), - zap.Uint64("ctrl_height", uint64(b.QBFTController.Height)), - zap.Any("signers", msg.Signers), - ) - if err = b.QBFTController.SaveInstance(inst, decidedMsg); err != nil { - logger.Debug("❗ failed to save instance", zap.Error(err)) - } else { - logger.Debug("💾 saved instance") - } - } - } - - // decode consensus data - decidedValue = &genesisspectypes.ConsensusData{} - if err := decidedValue.Decode(decidedMsg.FullData); err != nil { - return true, nil, errors.Wrap(err, "failed to parse decided value to ConsensusData") - } - - // update the highest decided slot - b.highestDecidedSlot = decidedValue.Duty.Slot - - if err := b.validateDecidedConsensusData(runner, decidedValue); err != nil { - return true, nil, errors.Wrap(err, "decided ConsensusData invalid") - } - - runner.GetBaseRunner().State.DecidedValue = decidedValue - - return true, decidedValue, nil -} - -// basePostConsensusMsgProcessing is a base func that all runner implementation can call for processing a post-consensus msg -func (b *BaseRunner) basePostConsensusMsgProcessing(logger *zap.Logger, runner Runner, signedMsg *genesisspectypes.SignedPartialSignatureMessage) (bool, [][32]byte, error) { - if err := b.ValidatePostConsensusMsg(runner, signedMsg); err != nil { - return false, nil, errors.Wrap(err, "invalid post-consensus message") - } - - hasQuorum, roots, err := b.basePartialSigMsgProcessing(signedMsg, b.State.PostConsensusContainer) - return hasQuorum, roots, errors.Wrap(err, "could not process post-consensus partial signature msg") -} - -// basePartialSigMsgProcessing adds a validated (without signature verification) validated partial msg to the container, checks for quorum and returns true (and roots) if quorum exists -func (b *BaseRunner) basePartialSigMsgProcessing( - signedMsg *genesisspectypes.SignedPartialSignatureMessage, - container *genesisspecssv.PartialSigContainer, -) (bool, [][32]byte, error) { - roots := make([][32]byte, 0) - anyQuorum := false - for _, msg := range signedMsg.Message.Messages { - prevQuorum := container.HasQuorum(msg.SigningRoot) - - // Check if it has two signatures for the same signer - if container.HasSigner(msg.Signer, msg.SigningRoot) { - b.resolveDuplicateSignature(container, msg) - } else { - container.AddSignature(msg) - } - - hasQuorum := container.HasQuorum(msg.SigningRoot) - - if hasQuorum && !prevQuorum { - // Notify about first quorum only - roots = append(roots, msg.SigningRoot) - anyQuorum = true - } - } - - return anyQuorum, roots, nil -} - -// didDecideCorrectly returns true if the expected consensus instance decided correctly -func (b *BaseRunner) didDecideCorrectly(prevDecided bool, decidedMsg *genesisspecqbft.SignedMessage) (bool, error) { - if decidedMsg == nil { - return false, nil - } - - if b.State.RunningInstance == nil { - return false, errors.New("decided wrong instance") - } - - if decidedMsg.Message.Height != b.State.RunningInstance.GetHeight() { - return false, errors.New("decided wrong instance") - } - - // verify we decided running instance only, if not we do not proceed - if prevDecided { - return false, nil - } - - return true, nil -} - -func (b *BaseRunner) decide(logger *zap.Logger, runner Runner, input *genesisspectypes.ConsensusData) error { - byts, err := input.Encode() - if err != nil { - return errors.Wrap(err, "could not encode ConsensusData") - } - - if err := runner.GetValCheckF()(byts); err != nil { - return errors.Wrap(err, "input data invalid") - - } - - if err := runner.GetBaseRunner().QBFTController.StartNewInstance(logger, - genesisspecqbft.Height(input.Duty.Slot), - byts, - ); err != nil { - return errors.Wrap(err, "could not start new QBFT instance") - } - newInstance := runner.GetBaseRunner().QBFTController.InstanceForHeight(logger, runner.GetBaseRunner().QBFTController.Height) - if newInstance == nil { - return errors.New("could not find newly created QBFT instance") - } - - runner.GetBaseRunner().State.RunningInstance = newInstance - - b.registerTimeoutHandler(logger, newInstance, runner.GetBaseRunner().QBFTController.Height) - - return nil -} - -// hasRunningDuty returns true if a new duty didn't start or an existing duty marked as finished -func (b *BaseRunner) hasRunningDuty() bool { - b.mtx.RLock() // reads b.State - defer b.mtx.RUnlock() - - if b.State == nil { - return false - } - return !b.State.Finished -} - -func (b *BaseRunner) ShouldProcessDuty(duty *genesisspectypes.Duty) error { - if b.QBFTController.Height >= genesisspecqbft.Height(duty.Slot) && b.QBFTController.Height != 0 { - return errors.Errorf("duty for slot %d already passed. Current height is %d", duty.Slot, - b.QBFTController.Height) - } - return nil -} - -func (b *BaseRunner) ShouldProcessNonBeaconDuty(duty *genesisspectypes.Duty) error { - // assume StartingDuty is not nil if state is not nil - if b.State != nil && b.State.StartingDuty.Slot >= duty.Slot { - return errors.Errorf("duty for slot %d already passed. Current slot is %d", duty.Slot, - b.State.StartingDuty.Slot) - } - return nil -} diff --git a/protocol/genesis/ssv/runner/runner_signatures.go b/protocol/genesis/ssv/runner/runner_signatures.go deleted file mode 100644 index 6d8c6cdfc4..0000000000 --- a/protocol/genesis/ssv/runner/runner_signatures.go +++ /dev/null @@ -1,123 +0,0 @@ -package runner - -import ( - spec "github.com/attestantio/go-eth2-client/spec/phase0" - ssz "github.com/ferranbt/fastssz" - "github.com/herumi/bls-eth-go-binary/bls" - "github.com/pkg/errors" - genesisspecssv "github.com/ssvlabs/ssv-spec-pre-cc/ssv" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - - "github.com/ssvlabs/ssv/protocol/genesis/types" -) - -func (b *BaseRunner) signBeaconObject( - runner Runner, - obj ssz.HashRoot, - slot spec.Slot, - domainType spec.DomainType, -) (*genesisspectypes.PartialSignatureMessage, error) { - epoch := runner.GetBaseRunner().BeaconNetwork.EstimatedEpochAtSlot(slot) - domain, err := runner.GetBeaconNode().DomainData(epoch, domainType) - if err != nil { - return nil, errors.Wrap(err, "could not get beacon domain") - } - sig, r, err := runner.GetSigner().SignBeaconObject(obj, domain, runner.GetBaseRunner().Share.SharePubKey, domainType) - if err != nil { - return nil, errors.Wrap(err, "could not sign beacon object") - } - - return &genesisspectypes.PartialSignatureMessage{ - PartialSignature: sig, - SigningRoot: r, - Signer: runner.GetBaseRunner().Share.OperatorID, - }, nil -} - -func (b *BaseRunner) signPostConsensusMsg(runner Runner, msg *genesisspectypes.PartialSignatureMessages) (*genesisspectypes.SignedPartialSignatureMessage, error) { - signature, err := runner.GetSigner().SignRoot(msg, genesisspectypes.PartialSignatureType, b.Share.SharePubKey) - if err != nil { - return nil, errors.Wrap(err, "could not sign PartialSignatureMessage for PostConsensusContainer") - } - - return &genesisspectypes.SignedPartialSignatureMessage{ - Message: *msg, - Signature: signature, - Signer: b.Share.OperatorID, - }, nil -} - -// Validate message content without verifying signatures -func (b *BaseRunner) validatePartialSigMsgForSlot( - signedMsg *genesisspectypes.SignedPartialSignatureMessage, - slot spec.Slot, -) error { - if err := signedMsg.Validate(); err != nil { - return errors.Wrap(err, "SignedPartialSignatureMessage invalid") - } - - if signedMsg.Message.Slot != slot { - return errors.New("invalid partial sig slot") - } - - // Check if signer is in committee - signerInCommittee := false - for _, operator := range b.Share.Committee { - if operator.OperatorID == signedMsg.Signer { - signerInCommittee = true - break - } - } - if !signerInCommittee { - return errors.New("unknown signer") - } - - return nil -} - -func (b *BaseRunner) verifyBeaconPartialSignature(signer uint64, signature genesisspectypes.Signature, root [32]byte) error { - types.MetricsSignaturesVerifications.WithLabelValues().Inc() - - for _, n := range b.Share.Committee { - if n.GetID() == signer { - pk, err := types.DeserializeBLSPublicKey(n.GetPublicKey()) - if err != nil { - return errors.Wrap(err, "could not deserialized pk") - } - sig := &bls.Sign{} - if err := sig.Deserialize(signature); err != nil { - return errors.Wrap(err, "could not deserialized Signature") - } - - // verify - if !sig.VerifyByte(&pk, root[:]) { - return errors.New("wrong signature") - } - return nil - } - } - return errors.New("unknown signer") -} - -// Stores the container's existing signature or the new one, depending on their validity. If both are invalid, remove the existing one -func (b *BaseRunner) resolveDuplicateSignature(container *genesisspecssv.PartialSigContainer, msg *genesisspectypes.PartialSignatureMessage) { - - // Check previous signature validity - previousSignature, err := container.GetSignature(msg.Signer, msg.SigningRoot) - if err == nil { - err = b.verifyBeaconPartialSignature(msg.Signer, previousSignature, msg.SigningRoot) - if err == nil { - // Keep the previous sigature since it's correct - return - } - } - - // Previous signature is incorrect or doesn't exist - container.Remove(msg.Signer, msg.SigningRoot) - - // Hold the new signature, if correct - err = b.verifyBeaconPartialSignature(msg.Signer, msg.PartialSignature, msg.SigningRoot) - if err == nil { - container.AddSignature(msg) - } -} diff --git a/protocol/genesis/ssv/runner/runner_state.go b/protocol/genesis/ssv/runner/runner_state.go deleted file mode 100644 index b9849d4867..0000000000 --- a/protocol/genesis/ssv/runner/runner_state.go +++ /dev/null @@ -1,65 +0,0 @@ -package runner - -import ( - "crypto/sha256" - "encoding/json" - - "github.com/pkg/errors" - genesisspecssv "github.com/ssvlabs/ssv-spec-pre-cc/ssv" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - - "github.com/ssvlabs/ssv/protocol/genesis/qbft/instance" - "github.com/ssvlabs/ssv/protocol/genesis/types" -) - -// State holds all the relevant progress the duty execution progress -type State struct { - PreConsensusContainer *genesisspecssv.PartialSigContainer - PostConsensusContainer *genesisspecssv.PartialSigContainer - RunningInstance *instance.Instance - DecidedValue *genesisspectypes.ConsensusData - // CurrentDuty is the duty the node pulled locally from the beacon node, might be different from decided duty - StartingDuty *genesisspectypes.Duty - // flags - Finished bool // Finished marked true when there is a full successful cycle (pre, consensus and post) with quorum -} - -func NewRunnerState(quorum uint64, duty *genesisspectypes.Duty) *State { - return &State{ - PreConsensusContainer: genesisspecssv.NewPartialSigContainer(quorum), - PostConsensusContainer: genesisspecssv.NewPartialSigContainer(quorum), - - StartingDuty: duty, - Finished: false, - } -} - -// ReconstructBeaconSig aggregates collected partial beacon sigs -func (pcs *State) ReconstructBeaconSig(container *genesisspecssv.PartialSigContainer, root [32]byte, validatorPubKey []byte) ([]byte, error) { - // Reconstruct signatures - signature, err := types.ReconstructSignature(container, root, validatorPubKey) - if err != nil { - return nil, errors.Wrap(err, "could not reconstruct beacon sig") - } - return signature, nil -} - -// GetRoot returns the root used for signing and verification -func (pcs *State) GetRoot() ([32]byte, error) { - marshaledRoot, err := pcs.Encode() - if err != nil { - return [32]byte{}, errors.Wrap(err, "could not encode State") - } - ret := sha256.Sum256(marshaledRoot) - return ret, nil -} - -// Encode returns the encoded struct in bytes or error -func (pcs *State) Encode() ([]byte, error) { - return json.Marshal(pcs) -} - -// Decode returns error if decoding failed -func (pcs *State) Decode(data []byte) error { - return json.Unmarshal(data, &pcs) -} diff --git a/protocol/genesis/ssv/runner/runner_state_helpers.go b/protocol/genesis/ssv/runner/runner_state_helpers.go deleted file mode 100644 index 9ab34cf8b0..0000000000 --- a/protocol/genesis/ssv/runner/runner_state_helpers.go +++ /dev/null @@ -1,25 +0,0 @@ -package runner - -import ( - "encoding/hex" - - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" -) - -func getPreConsensusSigners(state *State, root [32]byte) []genesisspectypes.OperatorID { - sigs := state.PreConsensusContainer.Signatures[hex.EncodeToString(root[:])] - var signers []genesisspectypes.OperatorID - for op := range sigs { - signers = append(signers, op) - } - return signers -} - -func getPostConsensusSigners(state *State, root [32]byte) []genesisspectypes.OperatorID { - sigs := state.PostConsensusContainer.Signatures[hex.EncodeToString(root[:])] - var signers []genesisspectypes.OperatorID - for op := range sigs { - signers = append(signers, op) - } - return signers -} diff --git a/protocol/genesis/ssv/runner/runner_validations.go b/protocol/genesis/ssv/runner/runner_validations.go deleted file mode 100644 index 112f2531e8..0000000000 --- a/protocol/genesis/ssv/runner/runner_validations.go +++ /dev/null @@ -1,140 +0,0 @@ -package runner - -import ( - "bytes" - "sort" - - spec "github.com/attestantio/go-eth2-client/spec/phase0" - ssz "github.com/ferranbt/fastssz" - "github.com/pkg/errors" - genesisspecssv "github.com/ssvlabs/ssv-spec-pre-cc/ssv" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" -) - -func (b *BaseRunner) ValidatePreConsensusMsg(runner Runner, signedMsg *genesisspectypes.SignedPartialSignatureMessage) error { - if !b.hasRunningDuty() { - return errors.New("no running duty") - } - - if err := b.validatePartialSigMsgForSlot(signedMsg, b.State.StartingDuty.Slot); err != nil { - return err - } - - roots, domain, err := runner.expectedPreConsensusRootsAndDomain() - if err != nil { - return err - } - - return b.verifyExpectedRoot(runner, signedMsg, roots, domain) -} - -// Verify each signature in container removing the invalid ones -func (b *BaseRunner) FallBackAndVerifyEachSignature(container *genesisspecssv.PartialSigContainer, root [32]byte) { - - signatures := container.GetSignatures(root) - - for operatorID, signature := range signatures { - if err := b.verifyBeaconPartialSignature(operatorID, signature, root); err != nil { - container.Remove(operatorID, root) - } - } -} - -func (b *BaseRunner) ValidatePostConsensusMsg(runner Runner, signedMsg *genesisspectypes.SignedPartialSignatureMessage) error { - if !b.hasRunningDuty() { - return errors.New("no running duty") - } - - // TODO https://github.com/ssvlabs/ssv-spec-pre-cc/issues/142 need to fix with this issue solution instead. - if b.State.DecidedValue == nil { - return errors.New("no decided value") - } - - if b.State.RunningInstance == nil { - return errors.New("no running consensus instance") - } - decided, decidedValueByts := b.State.RunningInstance.IsDecided() - if !decided { - return errors.New("consensus instance not decided") - } - - decidedValue := &genesisspectypes.ConsensusData{} - if err := decidedValue.Decode(decidedValueByts); err != nil { - return errors.Wrap(err, "failed to parse decided value to ConsensusData") - } - - if err := b.validatePartialSigMsgForSlot(signedMsg, decidedValue.Duty.Slot); err != nil { - return err - } - - roots, domain, err := runner.expectedPostConsensusRootsAndDomain() - if err != nil { - return err - } - - return b.verifyExpectedRoot(runner, signedMsg, roots, domain) -} - -func (b *BaseRunner) validateDecidedConsensusData(runner Runner, val *genesisspectypes.ConsensusData) error { - byts, err := val.Encode() - if err != nil { - return errors.Wrap(err, "could not encode decided value") - } - if err := runner.GetValCheckF()(byts); err != nil { - return errors.Wrap(err, "decided value is invalid") - } - - return nil -} - -func (b *BaseRunner) verifyExpectedRoot(runner Runner, signedMsg *genesisspectypes.SignedPartialSignatureMessage, expectedRootObjs []ssz.HashRoot, domain spec.DomainType) error { - if len(expectedRootObjs) != len(signedMsg.Message.Messages) { - return errors.New("wrong expected roots count") - } - - // convert expected roots to map and mark unique roots when verified - sortedExpectedRoots, err := func(expectedRootObjs []ssz.HashRoot) ([][32]byte, error) { - epoch := b.BeaconNetwork.EstimatedEpochAtSlot(b.State.StartingDuty.Slot) - d, err := runner.GetBeaconNode().DomainData(epoch, domain) - if err != nil { - return nil, errors.Wrap(err, "could not get pre consensus root domain") - } - - ret := make([][32]byte, 0) - for _, rootI := range expectedRootObjs { - r, err := genesisspectypes.ComputeETHSigningRoot(rootI, d) - if err != nil { - return nil, errors.Wrap(err, "could not compute ETH signing root") - } - ret = append(ret, r) - } - - sort.Slice(ret, func(i, j int) bool { - return string(ret[i][:]) < string(ret[j][:]) - }) - return ret, nil - }(expectedRootObjs) - if err != nil { - return err - } - - sortedRoots := func(msgs genesisspectypes.PartialSignatureMessages) [][32]byte { - ret := make([][32]byte, 0) - for _, msg := range msgs.Messages { - ret = append(ret, msg.SigningRoot) - } - - sort.Slice(ret, func(i, j int) bool { - return string(ret[i][:]) < string(ret[j][:]) - }) - return ret - }(signedMsg.Message) - - // verify roots - for i, r := range sortedRoots { - if !bytes.Equal(sortedExpectedRoots[i][:], r[:]) { - return errors.New("wrong signing root") - } - } - return nil -} diff --git a/protocol/genesis/ssv/runner/sync_committee.go b/protocol/genesis/ssv/runner/sync_committee.go deleted file mode 100644 index 0fc0745b32..0000000000 --- a/protocol/genesis/ssv/runner/sync_committee.go +++ /dev/null @@ -1,274 +0,0 @@ -package runner - -import ( - "crypto/sha256" - "encoding/hex" - "encoding/json" - spectypes "github.com/ssvlabs/ssv-spec/types" - - "github.com/attestantio/go-eth2-client/spec/altair" - "github.com/attestantio/go-eth2-client/spec/phase0" - ssz "github.com/ferranbt/fastssz" - "github.com/pkg/errors" - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - genesisspecssv "github.com/ssvlabs/ssv-spec-pre-cc/ssv" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - specqbft "github.com/ssvlabs/ssv-spec/qbft" - "go.uber.org/zap" - - "github.com/ssvlabs/ssv/logging/fields" - "github.com/ssvlabs/ssv/protocol/genesis/qbft/controller" - "github.com/ssvlabs/ssv/protocol/genesis/ssv/runner/metrics" -) - -type SyncCommitteeRunner struct { - BaseRunner *BaseRunner - - beacon genesisspecssv.BeaconNode - network genesisspecssv.Network - signer genesisspectypes.KeyManager - valCheck genesisspecqbft.ProposedValueCheckF - - metrics metrics.ConsensusMetrics -} - -func NewSyncCommitteeRunner( - domainType spectypes.DomainType, - beaconNetwork genesisspectypes.BeaconNetwork, - share *genesisspectypes.Share, - qbftController *controller.Controller, - beacon genesisspecssv.BeaconNode, - network genesisspecssv.Network, - signer genesisspectypes.KeyManager, - valCheck genesisspecqbft.ProposedValueCheckF, - highestDecidedSlot phase0.Slot, -) Runner { - return &SyncCommitteeRunner{ - BaseRunner: &BaseRunner{ - BeaconRoleType: genesisspectypes.BNRoleSyncCommittee, - DomainType: domainType, - BeaconNetwork: beaconNetwork, - Share: share, - QBFTController: qbftController, - highestDecidedSlot: highestDecidedSlot, - }, - - beacon: beacon, - network: network, - signer: signer, - valCheck: valCheck, - metrics: metrics.NewConsensusMetrics(genesisspectypes.BNRoleSyncCommittee), - } -} - -func (r *SyncCommitteeRunner) StartNewDuty(logger *zap.Logger, duty *genesisspectypes.Duty) error { - return r.BaseRunner.baseStartNewDuty(logger, r, duty) -} - -// HasRunningDuty returns true if a duty is already running (StartNewDuty called and returned nil) -func (r *SyncCommitteeRunner) HasRunningDuty() bool { - return r.BaseRunner.hasRunningDuty() -} - -func (r *SyncCommitteeRunner) ProcessPreConsensus(logger *zap.Logger, signedMsg *genesisspectypes.SignedPartialSignatureMessage) error { - return errors.New("no pre consensus sigs required for sync committee role") -} - -func (r *SyncCommitteeRunner) ProcessConsensus(logger *zap.Logger, signedMsg *genesisspecqbft.SignedMessage) error { - decided, decidedValue, err := r.BaseRunner.baseConsensusMsgProcessing(logger, r, signedMsg) - if err != nil { - return errors.Wrap(err, "failed processing consensus message") - } - - // Decided returns true only once so if it is true it must be for the current running instance - if !decided { - return nil - } - - r.metrics.EndConsensus() - r.metrics.StartPostConsensus() - - // specific duty sig - root, err := decidedValue.GetSyncCommitteeBlockRoot() - if err != nil { - return errors.Wrap(err, "could not get sync committee block root") - } - msg, err := r.BaseRunner.signBeaconObject(r, genesisspectypes.SSZBytes(root[:]), decidedValue.Duty.Slot, genesisspectypes.DomainSyncCommittee) - if err != nil { - return errors.Wrap(err, "failed signing attestation data") - } - postConsensusMsg := &genesisspectypes.PartialSignatureMessages{ - Type: genesisspectypes.PostConsensusPartialSig, - Slot: decidedValue.Duty.Slot, - Messages: []*genesisspectypes.PartialSignatureMessage{msg}, - } - - postSignedMsg, err := r.BaseRunner.signPostConsensusMsg(r, postConsensusMsg) - if err != nil { - return errors.Wrap(err, "could not sign post consensus msg") - } - - data, err := postSignedMsg.Encode() - if err != nil { - return errors.Wrap(err, "failed to encode post consensus signature msg") - } - - msgToBroadcast := &genesisspectypes.SSVMessage{ - MsgType: genesisspectypes.SSVPartialSignatureMsgType, - MsgID: genesisspectypes.NewMsgID(r.GetShare().DomainType, r.GetShare().ValidatorPubKey, r.BaseRunner.BeaconRoleType), - Data: data, - } - - if err := r.GetNetwork().Broadcast(msgToBroadcast); err != nil { - return errors.Wrap(err, "can't broadcast partial post consensus sig") - } - return nil -} - -func (r *SyncCommitteeRunner) ProcessPostConsensus(logger *zap.Logger, signedMsg *genesisspectypes.SignedPartialSignatureMessage) error { - quorum, roots, err := r.BaseRunner.basePostConsensusMsgProcessing(logger, r, signedMsg) - if err != nil { - return errors.Wrap(err, "failed processing post consensus message") - } - - if !quorum { - return nil - } - - r.metrics.EndPostConsensus() - - blockRoot, err := r.GetState().DecidedValue.GetSyncCommitteeBlockRoot() - if err != nil { - return errors.Wrap(err, "could not get sync committee block root") - } - - for _, root := range roots { - sig, err := r.GetState().ReconstructBeaconSig(r.GetState().PostConsensusContainer, root, r.GetShare().ValidatorPubKey) - if err != nil { - // If the reconstructed signature verification failed, fall back to verifying each partial signature - for _, root := range roots { - r.BaseRunner.FallBackAndVerifyEachSignature(r.GetState().PostConsensusContainer, root) - } - return errors.Wrap(err, "got post-consensus quorum but it has invalid signatures") - } - specSig := phase0.BLSSignature{} - copy(specSig[:], sig) - - msg := &altair.SyncCommitteeMessage{ - Slot: r.GetState().DecidedValue.Duty.Slot, - BeaconBlockRoot: blockRoot, - ValidatorIndex: r.GetState().DecidedValue.Duty.ValidatorIndex, - Signature: specSig, - } - - messageSubmissionEnd := r.metrics.StartBeaconSubmission() - - if err := r.GetBeaconNode().SubmitSyncMessage(msg); err != nil { - r.metrics.RoleSubmissionFailed() - return errors.Wrap(err, "could not submit to Beacon chain reconstructed signed sync committee") - } - - messageSubmissionEnd() - r.metrics.EndDutyFullFlow(r.GetState().RunningInstance.State.Round) - r.metrics.RoleSubmitted() - - logger.Info("✅ successfully submitted sync committee", - fields.Slot(msg.Slot), - zap.String("block_root", hex.EncodeToString(msg.BeaconBlockRoot[:])), - fields.Height(specqbft.Height(r.BaseRunner.QBFTController.Height)), - fields.Round(specqbft.Round(r.GetState().RunningInstance.State.Round))) - } - r.GetState().Finished = true - - return nil -} - -func (r *SyncCommitteeRunner) expectedPreConsensusRootsAndDomain() ([]ssz.HashRoot, phase0.DomainType, error) { - return []ssz.HashRoot{}, genesisspectypes.DomainError, errors.New("no expected pre consensus roots for sync committee") -} - -// expectedPostConsensusRootsAndDomain an INTERNAL function, returns the expected post-consensus roots to sign -func (r *SyncCommitteeRunner) expectedPostConsensusRootsAndDomain() ([]ssz.HashRoot, phase0.DomainType, error) { - root, err := r.GetState().DecidedValue.GetSyncCommitteeBlockRoot() - if err != nil { - return nil, phase0.DomainType{}, errors.Wrap(err, "could not get sync committee block root") - } - - return []ssz.HashRoot{genesisspectypes.SSZBytes(root[:])}, genesisspectypes.DomainSyncCommittee, nil -} - -// executeDuty steps: -// 1) get sync block root from BN -// 2) start consensus on duty + block root data -// 3) Once consensus decides, sign partial block root and broadcast -// 4) collect 2f+1 partial sigs, reconstruct and broadcast valid sync committee sig to the BN -func (r *SyncCommitteeRunner) executeDuty(logger *zap.Logger, duty *genesisspectypes.Duty) error { - // TODO - waitOneThirdOrValidBlock - - root, ver, err := r.GetBeaconNode().GetSyncMessageBlockRoot(duty.Slot) - if err != nil { - return errors.Wrap(err, "failed to get sync committee block root") - } - - r.metrics.StartDutyFullFlow() - r.metrics.StartConsensus() - - input := &genesisspectypes.ConsensusData{ - Duty: *duty, - Version: ver, - DataSSZ: root[:], - } - - if err := r.BaseRunner.decide(logger, r, input); err != nil { - return errors.Wrap(err, "can't start new duty runner instance for duty") - } - return nil -} - -func (r *SyncCommitteeRunner) GetBaseRunner() *BaseRunner { - return r.BaseRunner -} - -func (r *SyncCommitteeRunner) GetNetwork() genesisspecssv.Network { - return r.network -} - -func (r *SyncCommitteeRunner) GetBeaconNode() genesisspecssv.BeaconNode { - return r.beacon -} - -func (r *SyncCommitteeRunner) GetShare() *genesisspectypes.Share { - return r.BaseRunner.Share -} - -func (r *SyncCommitteeRunner) GetState() *State { - return r.BaseRunner.State -} - -func (r *SyncCommitteeRunner) GetValCheckF() genesisspecqbft.ProposedValueCheckF { - return r.valCheck -} - -func (r *SyncCommitteeRunner) GetSigner() genesisspectypes.KeyManager { - return r.signer -} - -// Encode returns the encoded struct in bytes or error -func (r *SyncCommitteeRunner) Encode() ([]byte, error) { - return json.Marshal(r) -} - -// Decode returns error if decoding failed -func (r *SyncCommitteeRunner) Decode(data []byte) error { - return json.Unmarshal(data, &r) -} - -// GetRoot returns the root used for signing and verification -func (r *SyncCommitteeRunner) GetRoot() ([32]byte, error) { - marshaledRoot, err := r.Encode() - if err != nil { - return [32]byte{}, errors.Wrap(err, "could not encode DutyRunnerState") - } - ret := sha256.Sum256(marshaledRoot) - return ret, nil -} diff --git a/protocol/genesis/ssv/runner/sync_committee_aggregator.go b/protocol/genesis/ssv/runner/sync_committee_aggregator.go deleted file mode 100644 index c7de13ad18..0000000000 --- a/protocol/genesis/ssv/runner/sync_committee_aggregator.go +++ /dev/null @@ -1,444 +0,0 @@ -package runner - -import ( - "bytes" - "crypto/sha256" - "encoding/json" - spectypes "github.com/ssvlabs/ssv-spec/types" - - "github.com/attestantio/go-eth2-client/spec/altair" - "github.com/attestantio/go-eth2-client/spec/phase0" - ssz "github.com/ferranbt/fastssz" - "github.com/pkg/errors" - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - genesisspecssv "github.com/ssvlabs/ssv-spec-pre-cc/ssv" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - "go.uber.org/zap" - - "github.com/ssvlabs/ssv/protocol/genesis/qbft/controller" - "github.com/ssvlabs/ssv/protocol/genesis/ssv/runner/metrics" -) - -type SyncCommitteeAggregatorRunner struct { - BaseRunner *BaseRunner - - beacon genesisspecssv.BeaconNode - network genesisspecssv.Network - signer genesisspectypes.KeyManager - valCheck genesisspecqbft.ProposedValueCheckF - - metrics metrics.ConsensusMetrics -} - -func NewSyncCommitteeAggregatorRunner( - domainType spectypes.DomainType, - beaconNetwork genesisspectypes.BeaconNetwork, - share *genesisspectypes.Share, - qbftController *controller.Controller, - beacon genesisspecssv.BeaconNode, - network genesisspecssv.Network, - signer genesisspectypes.KeyManager, - valCheck genesisspecqbft.ProposedValueCheckF, - highestDecidedSlot phase0.Slot, -) Runner { - return &SyncCommitteeAggregatorRunner{ - BaseRunner: &BaseRunner{ - BeaconRoleType: genesisspectypes.BNRoleSyncCommitteeContribution, - DomainType: domainType, - BeaconNetwork: beaconNetwork, - Share: share, - QBFTController: qbftController, - highestDecidedSlot: highestDecidedSlot, - }, - - beacon: beacon, - network: network, - signer: signer, - valCheck: valCheck, - metrics: metrics.NewConsensusMetrics(genesisspectypes.BNRoleSyncCommitteeContribution), - } -} - -func (r *SyncCommitteeAggregatorRunner) StartNewDuty(logger *zap.Logger, duty *genesisspectypes.Duty) error { - return r.BaseRunner.baseStartNewDuty(logger, r, duty) -} - -// HasRunningDuty returns true if a duty is already running (StartNewDuty called and returned nil) -func (r *SyncCommitteeAggregatorRunner) HasRunningDuty() bool { - return r.BaseRunner.hasRunningDuty() -} - -func (r *SyncCommitteeAggregatorRunner) ProcessPreConsensus(logger *zap.Logger, signedMsg *genesisspectypes.SignedPartialSignatureMessage) error { - quorum, roots, err := r.BaseRunner.basePreConsensusMsgProcessing(r, signedMsg) - if err != nil { - return errors.Wrap(err, "failed processing sync committee selection proof message") - } - - // quorum returns true only once (first time quorum achieved) - if !quorum { - return nil - } - - r.metrics.EndPreConsensus() - - // collect selection proofs and subnets - var ( - selectionProofs []phase0.BLSSignature - subnets []uint64 - ) - for i, root := range roots { - // reconstruct selection proof sig - sig, err := r.GetState().ReconstructBeaconSig(r.GetState().PreConsensusContainer, root, r.GetShare().ValidatorPubKey) - if err != nil { - // If the reconstructed signature verification failed, fall back to verifying each partial signature - for _, root := range roots { - r.BaseRunner.FallBackAndVerifyEachSignature(r.GetState().PreConsensusContainer, root) - } - return errors.Wrap(err, "got pre-consensus quorum but it has invalid signatures") - } - blsSigSelectionProof := phase0.BLSSignature{} - copy(blsSigSelectionProof[:], sig) - - aggregator, err := r.GetBeaconNode().IsSyncCommitteeAggregator(sig) - if err != nil { - return errors.Wrap(err, "could not check if sync committee aggregator") - } - if !aggregator { - continue - } - - // fetch sync committee contribution - subnet, err := r.GetBeaconNode().SyncCommitteeSubnetID(phase0.CommitteeIndex(r.GetState().StartingDuty.ValidatorSyncCommitteeIndices[i])) - if err != nil { - return errors.Wrap(err, "could not get sync committee subnet ID") - } - - selectionProofs = append(selectionProofs, blsSigSelectionProof) - subnets = append(subnets, subnet) - } - if len(selectionProofs) == 0 { - r.GetState().Finished = true - return nil - } - - duty := r.GetState().StartingDuty - - // fetch contributions - r.metrics.PauseDutyFullFlow() - contributions, ver, err := r.GetBeaconNode().GetSyncCommitteeContribution(duty.Slot, selectionProofs, subnets) - if err != nil { - return errors.Wrap(err, "could not get sync committee contribution") - } - r.metrics.ContinueDutyFullFlow() - - byts, err := contributions.MarshalSSZ() - if err != nil { - return errors.Wrap(err, "could not marshal contributions") - } - - // create consensus object - input := &genesisspectypes.ConsensusData{ - Duty: *duty, - Version: ver, - DataSSZ: byts, - } - - r.metrics.StartConsensus() - if err := r.BaseRunner.decide(logger, r, input); err != nil { - return errors.Wrap(err, "can't start new duty runner instance for duty") - } - return nil -} - -func (r *SyncCommitteeAggregatorRunner) ProcessConsensus(logger *zap.Logger, signedMsg *genesisspecqbft.SignedMessage) error { - decided, decidedValue, err := r.BaseRunner.baseConsensusMsgProcessing(logger, r, signedMsg) - if err != nil { - return errors.Wrap(err, "failed processing consensus message") - } - - // Decided returns true only once so if it is true it must be for the current running instance - if !decided { - return nil - } - - r.metrics.EndConsensus() - r.metrics.StartPostConsensus() - - contributions, err := decidedValue.GetSyncCommitteeContributions() - if err != nil { - return errors.Wrap(err, "could not get contributions") - } - - // specific duty sig - msgs := make([]*genesisspectypes.PartialSignatureMessage, 0) - for _, c := range contributions { - contribAndProof, _, err := r.generateContributionAndProof(c.Contribution, c.SelectionProofSig) - if err != nil { - return errors.Wrap(err, "could not generate contribution and proof") - } - - signed, err := r.BaseRunner.signBeaconObject(r, contribAndProof, decidedValue.Duty.Slot, genesisspectypes.DomainContributionAndProof) - if err != nil { - return errors.Wrap(err, "failed to sign aggregate and proof") - } - - msgs = append(msgs, signed) - } - postConsensusMsg := &genesisspectypes.PartialSignatureMessages{ - Type: genesisspectypes.PostConsensusPartialSig, - Slot: decidedValue.Duty.Slot, - Messages: msgs, - } - - postSignedMsg, err := r.BaseRunner.signPostConsensusMsg(r, postConsensusMsg) - if err != nil { - return errors.Wrap(err, "could not sign post consensus msg") - } - - data, err := postSignedMsg.Encode() - if err != nil { - return errors.Wrap(err, "failed to encode post consensus signature msg") - } - - msgToBroadcast := &genesisspectypes.SSVMessage{ - MsgType: genesisspectypes.SSVPartialSignatureMsgType, - MsgID: genesisspectypes.NewMsgID(r.GetShare().DomainType, r.GetShare().ValidatorPubKey, r.BaseRunner.BeaconRoleType), - Data: data, - } - - if err := r.GetNetwork().Broadcast(msgToBroadcast); err != nil { - return errors.Wrap(err, "can't broadcast partial post consensus sig") - } - return nil -} - -func (r *SyncCommitteeAggregatorRunner) ProcessPostConsensus(logger *zap.Logger, signedMsg *genesisspectypes.SignedPartialSignatureMessage) error { - quorum, roots, err := r.BaseRunner.basePostConsensusMsgProcessing(logger, r, signedMsg) - if err != nil { - return errors.Wrap(err, "failed processing post consensus message") - } - - if !quorum { - return nil - } - - r.metrics.EndPostConsensus() - - // get contributions - contributions, err := r.GetState().DecidedValue.GetSyncCommitteeContributions() - if err != nil { - return errors.Wrap(err, "could not get contributions") - } - - for _, root := range roots { - sig, err := r.GetState().ReconstructBeaconSig(r.GetState().PostConsensusContainer, root, r.GetShare().ValidatorPubKey) - if err != nil { - // If the reconstructed signature verification failed, fall back to verifying each partial signature - for _, root := range roots { - r.BaseRunner.FallBackAndVerifyEachSignature(r.GetState().PostConsensusContainer, root) - } - return errors.Wrap(err, "got post-consensus quorum but it has invalid signatures") - } - specSig := phase0.BLSSignature{} - copy(specSig[:], sig) - - for _, contribution := range contributions { - // match the right contrib and proof root to signed root - contribAndProof, contribAndProofRoot, err := r.generateContributionAndProof(contribution.Contribution, contribution.SelectionProofSig) - if err != nil { - return errors.Wrap(err, "could not generate contribution and proof") - } - if !bytes.Equal(root[:], contribAndProofRoot[:]) { - continue // not the correct root - } - - signedContrib, err := r.GetState().ReconstructBeaconSig(r.GetState().PostConsensusContainer, root, r.GetShare().ValidatorPubKey) - if err != nil { - return errors.Wrap(err, "could not reconstruct contribution and proof sig") - } - blsSignedContribAndProof := phase0.BLSSignature{} - copy(blsSignedContribAndProof[:], signedContrib) - signedContribAndProof := &altair.SignedContributionAndProof{ - Message: contribAndProof, - Signature: blsSignedContribAndProof, - } - - submissionEnd := r.metrics.StartBeaconSubmission() - - if err := r.GetBeaconNode().SubmitSignedContributionAndProof(signedContribAndProof); err != nil { - r.metrics.RoleSubmissionFailed() - return errors.Wrap(err, "could not submit to Beacon chain reconstructed contribution and proof") - } - - submissionEnd() - r.metrics.EndDutyFullFlow(r.GetState().RunningInstance.State.Round) - r.metrics.RoleSubmitted() - - logger.Debug("✅ submitted successfully sync committee aggregator!") - break - } - } - r.GetState().Finished = true - return nil -} - -func (r *SyncCommitteeAggregatorRunner) generateContributionAndProof(contrib altair.SyncCommitteeContribution, proof phase0.BLSSignature) (*altair.ContributionAndProof, phase0.Root, error) { - contribAndProof := &altair.ContributionAndProof{ - AggregatorIndex: r.GetState().DecidedValue.Duty.ValidatorIndex, - Contribution: &contrib, - SelectionProof: proof, - } - - epoch := r.BaseRunner.BeaconNetwork.EstimatedEpochAtSlot(r.GetState().DecidedValue.Duty.Slot) - dContribAndProof, err := r.GetBeaconNode().DomainData(epoch, genesisspectypes.DomainContributionAndProof) - if err != nil { - return nil, phase0.Root{}, errors.Wrap(err, "could not get domain data") - } - contribAndProofRoot, err := genesisspectypes.ComputeETHSigningRoot(contribAndProof, dContribAndProof) - if err != nil { - return nil, phase0.Root{}, errors.Wrap(err, "could not compute signing root") - } - return contribAndProof, contribAndProofRoot, nil -} - -func (r *SyncCommitteeAggregatorRunner) expectedPreConsensusRootsAndDomain() ([]ssz.HashRoot, phase0.DomainType, error) { - sszIndexes := make([]ssz.HashRoot, 0) - for _, index := range r.GetState().StartingDuty.ValidatorSyncCommitteeIndices { - subnet, err := r.GetBeaconNode().SyncCommitteeSubnetID(phase0.CommitteeIndex(index)) - if err != nil { - return nil, genesisspectypes.DomainError, errors.Wrap(err, "could not get sync committee subnet ID") - } - data := &altair.SyncAggregatorSelectionData{ - Slot: r.GetState().StartingDuty.Slot, - SubcommitteeIndex: subnet, - } - sszIndexes = append(sszIndexes, data) - } - return sszIndexes, genesisspectypes.DomainSyncCommitteeSelectionProof, nil -} - -// expectedPostConsensusRootsAndDomain an INTERNAL function, returns the expected post-consensus roots to sign -func (r *SyncCommitteeAggregatorRunner) expectedPostConsensusRootsAndDomain() ([]ssz.HashRoot, phase0.DomainType, error) { - // get contributions - contributions, err := r.GetState().DecidedValue.GetSyncCommitteeContributions() - if err != nil { - return nil, phase0.DomainType{}, errors.Wrap(err, "could not get contributions") - } - - ret := make([]ssz.HashRoot, 0) - for _, contrib := range contributions { - contribAndProof, _, err := r.generateContributionAndProof(contrib.Contribution, contrib.SelectionProofSig) - if err != nil { - return nil, genesisspectypes.DomainError, errors.Wrap(err, "could not generate contribution and proof") - } - ret = append(ret, contribAndProof) - } - return ret, genesisspectypes.DomainContributionAndProof, nil -} - -// executeDuty steps: -// 1) sign a partial contribution proof (for each subcommittee index) and wait for 2f+1 partial sigs from peers -// 2) Reconstruct contribution proofs, check IsSyncCommitteeAggregator and start consensus on duty + contribution data -// 3) Once consensus decides, sign partial contribution data (for each subcommittee) and broadcast -// 4) collect 2f+1 partial sigs, reconstruct and broadcast valid SignedContributionAndProof (for each subcommittee) sig to the BN -func (r *SyncCommitteeAggregatorRunner) executeDuty(logger *zap.Logger, duty *genesisspectypes.Duty) error { - r.metrics.StartDutyFullFlow() - r.metrics.StartPreConsensus() - - // sign selection proofs - msgs := genesisspectypes.PartialSignatureMessages{ - Type: genesisspectypes.ContributionProofs, - Slot: duty.Slot, - Messages: []*genesisspectypes.PartialSignatureMessage{}, - } - for _, index := range r.GetState().StartingDuty.ValidatorSyncCommitteeIndices { - subnet, err := r.GetBeaconNode().SyncCommitteeSubnetID(phase0.CommitteeIndex(index)) - if err != nil { - return errors.Wrap(err, "could not get sync committee subnet ID") - } - data := &altair.SyncAggregatorSelectionData{ - Slot: duty.Slot, - SubcommitteeIndex: subnet, - } - msg, err := r.BaseRunner.signBeaconObject(r, data, duty.Slot, genesisspectypes.DomainSyncCommitteeSelectionProof) - if err != nil { - return errors.Wrap(err, "could not sign sync committee selection proof") - } - - msgs.Messages = append(msgs.Messages, msg) - } - - // package into signed partial sig - signature, err := r.GetSigner().SignRoot(msgs, genesisspectypes.PartialSignatureType, r.GetShare().SharePubKey) - if err != nil { - return errors.Wrap(err, "could not sign PartialSignatureMessage for contribution proofs") - } - signedPartialMsg := &genesisspectypes.SignedPartialSignatureMessage{ - Message: msgs, - Signature: signature, - Signer: r.GetShare().OperatorID, - } - - // broadcast - data, err := signedPartialMsg.Encode() - if err != nil { - return errors.Wrap(err, "failed to encode contribution proofs pre-consensus signature msg") - } - msgToBroadcast := &genesisspectypes.SSVMessage{ - MsgType: genesisspectypes.SSVPartialSignatureMsgType, - MsgID: genesisspectypes.NewMsgID(r.GetShare().DomainType, r.GetShare().ValidatorPubKey, r.BaseRunner.BeaconRoleType), - Data: data, - } - if err := r.GetNetwork().Broadcast(msgToBroadcast); err != nil { - return errors.Wrap(err, "can't broadcast partial contribution proof sig") - } - return nil -} - -func (r *SyncCommitteeAggregatorRunner) GetBaseRunner() *BaseRunner { - return r.BaseRunner -} - -func (r *SyncCommitteeAggregatorRunner) GetNetwork() genesisspecssv.Network { - return r.network -} - -func (r *SyncCommitteeAggregatorRunner) GetBeaconNode() genesisspecssv.BeaconNode { - return r.beacon -} - -func (r *SyncCommitteeAggregatorRunner) GetShare() *genesisspectypes.Share { - return r.BaseRunner.Share -} - -func (r *SyncCommitteeAggregatorRunner) GetState() *State { - return r.BaseRunner.State -} - -func (r *SyncCommitteeAggregatorRunner) GetValCheckF() genesisspecqbft.ProposedValueCheckF { - return r.valCheck -} - -func (r *SyncCommitteeAggregatorRunner) GetSigner() genesisspectypes.KeyManager { - return r.signer -} - -// Encode returns the encoded struct in bytes or error -func (r *SyncCommitteeAggregatorRunner) Encode() ([]byte, error) { - return json.Marshal(r) -} - -// Decode returns error if decoding failed -func (r *SyncCommitteeAggregatorRunner) Decode(data []byte) error { - return json.Unmarshal(data, &r) -} - -// GetRoot returns the root used for signing and verification -func (r *SyncCommitteeAggregatorRunner) GetRoot() ([32]byte, error) { - marshaledRoot, err := r.Encode() - if err != nil { - return [32]byte{}, errors.Wrap(err, "could not encode DutyRunnerState") - } - ret := sha256.Sum256(marshaledRoot) - return ret, nil -} diff --git a/protocol/genesis/ssv/runner/timer.go b/protocol/genesis/ssv/runner/timer.go deleted file mode 100644 index a198ef4bbd..0000000000 --- a/protocol/genesis/ssv/runner/timer.go +++ /dev/null @@ -1,20 +0,0 @@ -package runner - -import ( - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - "go.uber.org/zap" - - "github.com/ssvlabs/ssv/protocol/genesis/qbft/instance" - "github.com/ssvlabs/ssv/protocol/genesis/qbft/roundtimer" -) - -type TimeoutF func(logger *zap.Logger, identifier genesisspectypes.MessageID, height genesisspecqbft.Height) roundtimer.OnRoundTimeoutF - -func (b *BaseRunner) registerTimeoutHandler(logger *zap.Logger, instance *instance.Instance, height genesisspecqbft.Height) { - identifier := genesisspectypes.MessageIDFromBytes(instance.State.ID) - timer, ok := instance.GetConfig().GetTimer().(*roundtimer.RoundTimer) - if ok { - timer.OnTimeout(b.TimeoutF(logger, identifier, height)) - } -} diff --git a/protocol/genesis/ssv/runner/validator_registration.go b/protocol/genesis/ssv/runner/validator_registration.go deleted file mode 100644 index f26d71cc1b..0000000000 --- a/protocol/genesis/ssv/runner/validator_registration.go +++ /dev/null @@ -1,228 +0,0 @@ -package runner - -import ( - "crypto/sha256" - "encoding/hex" - "encoding/json" - - spectypes "github.com/ssvlabs/ssv-spec/types" - - v1 "github.com/attestantio/go-eth2-client/api/v1" - "github.com/attestantio/go-eth2-client/spec/phase0" - ssz "github.com/ferranbt/fastssz" - "github.com/pkg/errors" - "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - genesisspecssv "github.com/ssvlabs/ssv-spec-pre-cc/ssv" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - "go.uber.org/zap" - - "github.com/ssvlabs/ssv/logging/fields" - "github.com/ssvlabs/ssv/protocol/genesis/ssv/runner/metrics" -) - -type ValidatorRegistrationRunner struct { - BaseRunner *BaseRunner - - beacon genesisspecssv.BeaconNode - network genesisspecssv.Network - signer genesisspectypes.KeyManager - valCheck qbft.ProposedValueCheckF - - metrics metrics.ConsensusMetrics -} - -func NewValidatorRegistrationRunner( - domainType spectypes.DomainType, - beaconNetwork genesisspectypes.BeaconNetwork, - share *genesisspectypes.Share, - beacon genesisspecssv.BeaconNode, - network genesisspecssv.Network, - signer genesisspectypes.KeyManager, -) Runner { - return &ValidatorRegistrationRunner{ - BaseRunner: &BaseRunner{ - BeaconRoleType: genesisspectypes.BNRoleValidatorRegistration, - DomainType: domainType, - BeaconNetwork: beaconNetwork, - Share: share, - }, - - beacon: beacon, - network: network, - signer: signer, - metrics: metrics.NewConsensusMetrics(genesisspectypes.BNRoleValidatorRegistration), - } -} - -func (r *ValidatorRegistrationRunner) StartNewDuty(logger *zap.Logger, duty *genesisspectypes.Duty) error { - return r.BaseRunner.baseStartNewNonBeaconDuty(logger, r, duty) -} - -// HasRunningDuty returns true if a duty is already running (StartNewDuty called and returned nil) -func (r *ValidatorRegistrationRunner) HasRunningDuty() bool { - return r.BaseRunner.hasRunningDuty() -} - -func (r *ValidatorRegistrationRunner) ProcessPreConsensus(logger *zap.Logger, signedMsg *genesisspectypes.SignedPartialSignatureMessage) error { - quorum, roots, err := r.BaseRunner.basePreConsensusMsgProcessing(r, signedMsg) - if err != nil { - return errors.Wrap(err, "failed processing validator registration message") - } - - // quorum returns true only once (first time quorum achieved) - if !quorum { - return nil - } - - // only 1 root, verified in basePreConsensusMsgProcessing - root := roots[0] - fullSig, err := r.GetState().ReconstructBeaconSig(r.GetState().PreConsensusContainer, root, r.GetShare().ValidatorPubKey) - if err != nil { - // If the reconstructed signature verification failed, fall back to verifying each partial signature - r.BaseRunner.FallBackAndVerifyEachSignature(r.GetState().PreConsensusContainer, root) - return errors.Wrap(err, "got pre-consensus quorum but it has invalid signatures") - } - specSig := phase0.BLSSignature{} - copy(specSig[:], fullSig) - - feeRecipient := r.BaseRunner.Share.FeeRecipientAddress - if err := r.beacon.SubmitValidatorRegistration(r.GetShare().ValidatorPubKey, feeRecipient, specSig); err != nil { - return errors.Wrap(err, "could not submit validator registration") - } - - logger.Debug("validator registration submitted successfully", - fields.FeeRecipient(feeRecipient[:]), - zap.String("signature", hex.EncodeToString(specSig[:]))) - - r.GetState().Finished = true - return nil -} - -func (r *ValidatorRegistrationRunner) ProcessConsensus(logger *zap.Logger, signedMsg *qbft.SignedMessage) error { - return errors.New("no consensus phase for validator registration") -} - -func (r *ValidatorRegistrationRunner) ProcessPostConsensus(logger *zap.Logger, signedMsg *genesisspectypes.SignedPartialSignatureMessage) error { - return errors.New("no post consensus phase for validator registration") -} - -func (r *ValidatorRegistrationRunner) expectedPreConsensusRootsAndDomain() ([]ssz.HashRoot, phase0.DomainType, error) { - vr, err := r.calculateValidatorRegistration() - if err != nil { - return nil, genesisspectypes.DomainError, errors.Wrap(err, "could not calculate validator registration") - } - return []ssz.HashRoot{vr}, genesisspectypes.DomainApplicationBuilder, nil -} - -// expectedPostConsensusRootsAndDomain an INTERNAL function, returns the expected post-consensus roots to sign -func (r *ValidatorRegistrationRunner) expectedPostConsensusRootsAndDomain() ([]ssz.HashRoot, phase0.DomainType, error) { - return nil, [4]byte{}, errors.New("no post consensus roots for validator registration") -} - -func (r *ValidatorRegistrationRunner) executeDuty(logger *zap.Logger, duty *genesisspectypes.Duty) error { - logger.Debug("executing validator registration duty", - zap.String("state_fee_recipient", hex.EncodeToString(r.BaseRunner.Share.FeeRecipientAddress[:]))) - vr, err := r.calculateValidatorRegistration() - if err != nil { - return errors.Wrap(err, "could not calculate validator registration") - } - - // sign partial randao - msg, err := r.BaseRunner.signBeaconObject(r, vr, duty.Slot, genesisspectypes.DomainApplicationBuilder) - if err != nil { - return errors.Wrap(err, "could not sign validator registration") - } - msgs := genesisspectypes.PartialSignatureMessages{ - Type: genesisspectypes.ValidatorRegistrationPartialSig, - Slot: duty.Slot, - Messages: []*genesisspectypes.PartialSignatureMessage{msg}, - } - - // sign msg - signature, err := r.GetSigner().SignRoot(msgs, genesisspectypes.PartialSignatureType, r.GetShare().SharePubKey) - if err != nil { - return errors.Wrap(err, "could not sign randao msg") - } - signedPartialMsg := &genesisspectypes.SignedPartialSignatureMessage{ - Message: msgs, - Signature: signature, - Signer: r.GetShare().OperatorID, - } - - // broadcast - data, err := signedPartialMsg.Encode() - if err != nil { - return errors.Wrap(err, "failed to encode randao pre-consensus signature msg") - } - msgToBroadcast := &genesisspectypes.SSVMessage{ - MsgType: genesisspectypes.SSVPartialSignatureMsgType, - MsgID: genesisspectypes.NewMsgID(r.GetShare().DomainType, r.GetShare().ValidatorPubKey, r.BaseRunner.BeaconRoleType), - Data: data, - } - if err := r.GetNetwork().Broadcast(msgToBroadcast); err != nil { - return errors.Wrap(err, "can't broadcast partial randao sig") - } - return nil -} - -func (r *ValidatorRegistrationRunner) calculateValidatorRegistration() (*v1.ValidatorRegistration, error) { - pk := phase0.BLSPubKey{} - copy(pk[:], r.GetShare().ValidatorPubKey) - - epoch := r.BaseRunner.BeaconNetwork.EstimatedEpochAtSlot(r.BaseRunner.State.StartingDuty.Slot) - - return &v1.ValidatorRegistration{ - FeeRecipient: r.BaseRunner.Share.FeeRecipientAddress, - GasLimit: genesisspectypes.DefaultGasLimit, - Timestamp: r.BaseRunner.BeaconNetwork.EpochStartTime(epoch), - Pubkey: pk, - }, nil -} - -func (r *ValidatorRegistrationRunner) GetBaseRunner() *BaseRunner { - return r.BaseRunner -} - -func (r *ValidatorRegistrationRunner) GetNetwork() genesisspecssv.Network { - return r.network -} - -func (r *ValidatorRegistrationRunner) GetBeaconNode() genesisspecssv.BeaconNode { - return r.beacon -} - -func (r *ValidatorRegistrationRunner) GetShare() *genesisspectypes.Share { - return r.BaseRunner.Share -} - -func (r *ValidatorRegistrationRunner) GetState() *State { - return r.BaseRunner.State -} - -func (r *ValidatorRegistrationRunner) GetValCheckF() qbft.ProposedValueCheckF { - return r.valCheck -} - -func (r *ValidatorRegistrationRunner) GetSigner() genesisspectypes.KeyManager { - return r.signer -} - -// Encode returns the encoded struct in bytes or error -func (r *ValidatorRegistrationRunner) Encode() ([]byte, error) { - return json.Marshal(r) -} - -// Decode returns error if decoding failed -func (r *ValidatorRegistrationRunner) Decode(data []byte) error { - return json.Unmarshal(data, &r) -} - -// GetRoot returns the root used for signing and verification -func (r *ValidatorRegistrationRunner) GetRoot() ([32]byte, error) { - marshaledRoot, err := r.Encode() - if err != nil { - return [32]byte{}, errors.Wrap(err, "could not encode DutyRunnerState") - } - ret := sha256.Sum256(marshaledRoot) - return ret, nil -} diff --git a/protocol/genesis/ssv/runner/voluntary_exit.go b/protocol/genesis/ssv/runner/voluntary_exit.go deleted file mode 100644 index 7c5c995a72..0000000000 --- a/protocol/genesis/ssv/runner/voluntary_exit.go +++ /dev/null @@ -1,240 +0,0 @@ -package runner - -import ( - "crypto/sha256" - "encoding/hex" - "encoding/json" - spectypes "github.com/ssvlabs/ssv-spec/types" - - "github.com/attestantio/go-eth2-client/spec/phase0" - ssz "github.com/ferranbt/fastssz" - "github.com/pkg/errors" - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - genesisspecssv "github.com/ssvlabs/ssv-spec-pre-cc/ssv" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - "go.uber.org/zap" - - "github.com/ssvlabs/ssv/logging/fields" - "github.com/ssvlabs/ssv/protocol/genesis/ssv/runner/metrics" -) - -// Duty runner for validator voluntary exit duty -type VoluntaryExitRunner struct { - BaseRunner *BaseRunner - - beacon genesisspecssv.BeaconNode - network genesisspecssv.Network - signer genesisspectypes.KeyManager - valCheck genesisspecqbft.ProposedValueCheckF - - voluntaryExit *phase0.VoluntaryExit - - metrics metrics.ConsensusMetrics -} - -func NewVoluntaryExitRunner( - domainType spectypes.DomainType, - beaconNetwork genesisspectypes.BeaconNetwork, - share *genesisspectypes.Share, - beacon genesisspecssv.BeaconNode, - network genesisspecssv.Network, - signer genesisspectypes.KeyManager, -) Runner { - return &VoluntaryExitRunner{ - BaseRunner: &BaseRunner{ - BeaconRoleType: genesisspectypes.BNRoleVoluntaryExit, - DomainType: domainType, - BeaconNetwork: beaconNetwork, - Share: share, - }, - - beacon: beacon, - network: network, - signer: signer, - metrics: metrics.NewConsensusMetrics(genesisspectypes.BNRoleVoluntaryExit), - } -} - -func (r *VoluntaryExitRunner) StartNewDuty(logger *zap.Logger, duty *genesisspectypes.Duty) error { - return r.BaseRunner.baseStartNewNonBeaconDuty(logger, r, duty) -} - -// HasRunningDuty returns true if a duty is already running (StartNewDuty called and returned nil) -func (r *VoluntaryExitRunner) HasRunningDuty() bool { - return r.BaseRunner.hasRunningDuty() -} - -// Check for quorum of partial signatures over VoluntaryExit and, -// if has quorum, constructs SignedVoluntaryExit and submits to BeaconNode -func (r *VoluntaryExitRunner) ProcessPreConsensus(logger *zap.Logger, signedMsg *genesisspectypes.SignedPartialSignatureMessage) error { - quorum, roots, err := r.BaseRunner.basePreConsensusMsgProcessing(r, signedMsg) - if err != nil { - return errors.Wrap(err, "failed processing voluntary exit message") - } - - // quorum returns true only once (first time quorum achieved) - if !quorum { - return nil - } - - // only 1 root, verified in basePreConsensusMsgProcessing - root := roots[0] - fullSig, err := r.GetState().ReconstructBeaconSig(r.GetState().PreConsensusContainer, root, r.GetShare().ValidatorPubKey) - if err != nil { - // If the reconstructed signature verification failed, fall back to verifying each partial signature - r.BaseRunner.FallBackAndVerifyEachSignature(r.GetState().PreConsensusContainer, root) - return errors.Wrap(err, "got pre-consensus quorum but it has invalid signatures") - } - specSig := phase0.BLSSignature{} - copy(specSig[:], fullSig) - - // create SignedVoluntaryExit using VoluntaryExit created on r.executeDuty() and reconstructed signature - signedVoluntaryExit := &phase0.SignedVoluntaryExit{ - Message: r.voluntaryExit, - Signature: specSig, - } - - if err := r.beacon.SubmitVoluntaryExit(signedVoluntaryExit); err != nil { - return errors.Wrap(err, "could not submit voluntary exit") - } - - logger.Debug("voluntary exit submitted successfully", - fields.Epoch(r.voluntaryExit.Epoch), - zap.Uint64("validator_index", uint64(r.voluntaryExit.ValidatorIndex)), - zap.String("signature", hex.EncodeToString(specSig[:])), - ) - - r.GetState().Finished = true - return nil -} - -func (r *VoluntaryExitRunner) ProcessConsensus(logger *zap.Logger, signedMsg *genesisspecqbft.SignedMessage) error { - return errors.New("no consensus phase for voluntary exit") -} - -func (r *VoluntaryExitRunner) ProcessPostConsensus(logger *zap.Logger, signedMsg *genesisspectypes.SignedPartialSignatureMessage) error { - return errors.New("no post consensus phase for voluntary exit") -} - -func (r *VoluntaryExitRunner) expectedPreConsensusRootsAndDomain() ([]ssz.HashRoot, phase0.DomainType, error) { - vr, err := r.calculateVoluntaryExit() - if err != nil { - return nil, genesisspectypes.DomainError, errors.Wrap(err, "could not calculate voluntary exit") - } - return []ssz.HashRoot{vr}, genesisspectypes.DomainVoluntaryExit, nil -} - -// expectedPostConsensusRootsAndDomain an INTERNAL function, returns the expected post-consensus roots to sign -func (r *VoluntaryExitRunner) expectedPostConsensusRootsAndDomain() ([]ssz.HashRoot, phase0.DomainType, error) { - return nil, [4]byte{}, errors.New("no post consensus roots for voluntary exit") -} - -// Validator voluntary exit duty doesn't need consensus nor post-consensus. -// It just performs pre-consensus with VoluntaryExitPartialSig over -// a VoluntaryExit object to create a SignedVoluntaryExit -func (r *VoluntaryExitRunner) executeDuty(logger *zap.Logger, duty *genesisspectypes.Duty) error { - voluntaryExit, err := r.calculateVoluntaryExit() - if err != nil { - return errors.Wrap(err, "could not calculate voluntary exit") - } - - // get PartialSignatureMessage with voluntaryExit root and signature - msg, err := r.BaseRunner.signBeaconObject(r, voluntaryExit, duty.Slot, genesisspectypes.DomainVoluntaryExit) - if err != nil { - return errors.Wrap(err, "could not sign VoluntaryExit object") - } - - msgs := genesisspectypes.PartialSignatureMessages{ - Type: genesisspectypes.VoluntaryExitPartialSig, - Slot: duty.Slot, - Messages: []*genesisspectypes.PartialSignatureMessage{msg}, - } - - // sign PartialSignatureMessages object - signature, err := r.GetSigner().SignRoot(msgs, genesisspectypes.PartialSignatureType, r.GetShare().SharePubKey) - if err != nil { - return errors.Wrap(err, "could not sign randao msg") - } - signedPartialMsg := &genesisspectypes.SignedPartialSignatureMessage{ - Message: msgs, - Signature: signature, - Signer: r.GetShare().OperatorID, - } - - // broadcast - data, err := signedPartialMsg.Encode() - if err != nil { - return errors.Wrap(err, "failed to encode signedPartialMsg with VoluntaryExit") - } - msgToBroadcast := &genesisspectypes.SSVMessage{ - MsgType: genesisspectypes.SSVPartialSignatureMsgType, - MsgID: genesisspectypes.NewMsgID(r.GetShare().DomainType, r.GetShare().ValidatorPubKey, r.BaseRunner.BeaconRoleType), - Data: data, - } - if err := r.GetNetwork().Broadcast(msgToBroadcast); err != nil { - return errors.Wrap(err, "can't broadcast signedPartialMsg with VoluntaryExit") - } - - // stores value for later using in ProcessPreConsensus - r.voluntaryExit = voluntaryExit - - return nil -} - -// Returns *phase0.VoluntaryExit object with current epoch and own validator index -func (r *VoluntaryExitRunner) calculateVoluntaryExit() (*phase0.VoluntaryExit, error) { - epoch := r.BaseRunner.BeaconNetwork.EstimatedEpochAtSlot(r.BaseRunner.State.StartingDuty.Slot) - validatorIndex := r.GetState().StartingDuty.ValidatorIndex - return &phase0.VoluntaryExit{ - Epoch: epoch, - ValidatorIndex: validatorIndex, - }, nil -} - -func (r *VoluntaryExitRunner) GetBaseRunner() *BaseRunner { - return r.BaseRunner -} - -func (r *VoluntaryExitRunner) GetNetwork() genesisspecssv.Network { - return r.network -} - -func (r *VoluntaryExitRunner) GetBeaconNode() genesisspecssv.BeaconNode { - return r.beacon -} - -func (r *VoluntaryExitRunner) GetShare() *genesisspectypes.Share { - return r.BaseRunner.Share -} - -func (r *VoluntaryExitRunner) GetState() *State { - return r.BaseRunner.State -} - -func (r *VoluntaryExitRunner) GetValCheckF() genesisspecqbft.ProposedValueCheckF { - return r.valCheck -} - -func (r *VoluntaryExitRunner) GetSigner() genesisspectypes.KeyManager { - return r.signer -} - -// Encode returns the encoded struct in bytes or error -func (r *VoluntaryExitRunner) Encode() ([]byte, error) { - return json.Marshal(r) -} - -// Decode returns error if decoding failed -func (r *VoluntaryExitRunner) Decode(data []byte) error { - return json.Unmarshal(data, &r) -} - -// GetRoot returns the root used for signing and verification -func (r *VoluntaryExitRunner) GetRoot() ([32]byte, error) { - marshaledRoot, err := r.Encode() - if err != nil { - return [32]byte{}, errors.Wrap(err, "could not encode DutyRunnerState") - } - ret := sha256.Sum256(marshaledRoot) - return ret, nil -} diff --git a/protocol/genesis/ssv/spectest/msg_processing_type.go b/protocol/genesis/ssv/spectest/msg_processing_type.go deleted file mode 100644 index ae8132faf4..0000000000 --- a/protocol/genesis/ssv/spectest/msg_processing_type.go +++ /dev/null @@ -1,198 +0,0 @@ -package spectest - -import ( - "encoding/hex" - "path/filepath" - "reflect" - "strings" - "testing" - - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - genesisspecssv "github.com/ssvlabs/ssv-spec-pre-cc/ssv" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - spectestingutils "github.com/ssvlabs/ssv-spec-pre-cc/types/testingutils" - "github.com/stretchr/testify/require" - "go.uber.org/zap" - - typescomparable "github.com/ssvlabs/ssv-spec-pre-cc/types/testingutils/comparable" - - "github.com/ssvlabs/ssv/logging" - "github.com/ssvlabs/ssv/protocol/genesis/ssv/genesisqueue" - "github.com/ssvlabs/ssv/protocol/genesis/ssv/runner" - ssvtesting "github.com/ssvlabs/ssv/protocol/genesis/ssv/testing" - "github.com/ssvlabs/ssv/protocol/genesis/ssv/validator" - protocoltesting "github.com/ssvlabs/ssv/protocol/genesis/testing" -) - -type MsgProcessingSpecTest struct { - Name string - Runner runner.Runner - Duty *genesisspectypes.Duty - Messages []*genesisspectypes.SSVMessage - PostDutyRunnerStateRoot string - PostDutyRunnerState genesisspectypes.Root `json:"-"` // Field is ignored by encoding/json - // OutputMessages compares pre/ post signed partial sigs to output. We exclude consensus msgs as it's tested in consensus - OutputMessages []*genesisspectypes.SignedPartialSignatureMessage - BeaconBroadcastedRoots []string - DontStartDuty bool // if set to true will not start a duty for the runner - ExpectedError string -} - -func (test *MsgProcessingSpecTest) TestName() string { - return test.Name -} - -func RunMsgProcessing(t *testing.T, test *MsgProcessingSpecTest) { - logger := logging.TestLogger(t) - test.overrideStateComparison(t) - test.RunAsPartOfMultiTest(t, logger) -} - -func (test *MsgProcessingSpecTest) RunAsPartOfMultiTest(t *testing.T, logger *zap.Logger) { - v := ssvtesting.BaseValidator(logger, spectestingutils.KeySetForShare(test.Runner.GetBaseRunner().Share)) - v.DutyRunners[test.Runner.GetBaseRunner().BeaconRoleType] = test.Runner - v.Network = test.Runner.GetNetwork().(genesisspecqbft.Network) // TODO need to align - - var lastErr error - if !test.DontStartDuty { - lastErr = v.StartDuty(logger, test.Duty) - } - for _, msg := range test.Messages { - dmsg, err := genesisqueue.DecodeGenesisSSVMessage(msg) - if err != nil { - lastErr = err - continue - } - err = v.ProcessMessage(logger, dmsg) - if err != nil { - lastErr = err - } - } - - if len(test.ExpectedError) != 0 { - require.EqualError(t, lastErr, test.ExpectedError, "expected: %v", test.ExpectedError) - } else { - require.NoError(t, lastErr) - } - - // test output message - test.compareOutputMsgs(t, v) - - // test beacon broadcasted msgs - test.compareBroadcastedBeaconMsgs(t) - - // post root - postRoot, err := test.Runner.GetRoot() - require.NoError(t, err) - require.EqualValues(t, test.PostDutyRunnerStateRoot, hex.EncodeToString(postRoot[:])) -} - -func (test *MsgProcessingSpecTest) compareBroadcastedBeaconMsgs(t *testing.T) { - broadcastedRoots := test.Runner.GetBeaconNode().(*spectestingutils.TestingBeaconNode).BroadcastedRoots - require.Len(t, broadcastedRoots, len(test.BeaconBroadcastedRoots)) - for _, r1 := range test.BeaconBroadcastedRoots { - found := false - for _, r2 := range broadcastedRoots { - if r1 == hex.EncodeToString(r2[:]) { - found = true - break - } - } - require.Truef(t, found, "broadcasted beacon root not found") - } -} - -func (test *MsgProcessingSpecTest) compareOutputMsgs(t *testing.T, v *validator.Validator) { - filterPartialSigs := func(messages []*genesisspectypes.SSVMessage) []*genesisspectypes.SSVMessage { - ret := make([]*genesisspectypes.SSVMessage, 0) - for _, msg := range messages { - if msg.MsgType != genesisspectypes.SSVPartialSignatureMsgType { - continue - } - ret = append(ret, msg) - } - return ret - } - - net := v.Network.(genesisspecssv.Network) - broadcastedMsgs := filterPartialSigs(net.(*spectestingutils.TestingNetwork).BroadcastedMsgs) - require.Len(t, broadcastedMsgs, len(test.OutputMessages)) - index := 0 - for _, msg := range broadcastedMsgs { - if msg.MsgType != genesisspectypes.SSVPartialSignatureMsgType { - continue - } - - msg1 := &genesisspectypes.SignedPartialSignatureMessage{} - require.NoError(t, msg1.Decode(msg.Data)) - msg2 := test.OutputMessages[index] - require.Len(t, msg1.Message.Messages, len(msg2.Message.Messages)) - - // messages are not guaranteed to be in order so we map them and then test all roots to be equal - roots := make(map[string]string) - for i, partialSigMsg2 := range msg2.Message.Messages { - r2, err := partialSigMsg2.GetRoot() - require.NoError(t, err) - if _, found := roots[hex.EncodeToString(r2[:])]; !found { - roots[hex.EncodeToString(r2[:])] = "" - } else { - roots[hex.EncodeToString(r2[:])] = hex.EncodeToString(r2[:]) - } - - partialSigMsg1 := msg1.Message.Messages[i] - r1, err := partialSigMsg1.GetRoot() - require.NoError(t, err) - - if _, found := roots[hex.EncodeToString(r1[:])]; !found { - roots[hex.EncodeToString(r1[:])] = "" - } else { - roots[hex.EncodeToString(r1[:])] = hex.EncodeToString(r1[:]) - } - } - for k, v := range roots { - require.EqualValues(t, k, v, "missing output msg") - } - - index++ - } -} - -func (test *MsgProcessingSpecTest) overrideStateComparison(t *testing.T) { - testType := reflect.TypeOf(test).String() - testType = strings.Replace(testType, "spectest.", "tests.", 1) - overrideStateComparison(t, test, test.Name, testType) -} - -func overrideStateComparison(t *testing.T, test *MsgProcessingSpecTest, name string, testType string) { - var r runner.Runner - switch test.Runner.(type) { - case *runner.AttesterRunner: - r = &runner.AttesterRunner{} - case *runner.AggregatorRunner: - r = &runner.AggregatorRunner{} - case *runner.ProposerRunner: - r = &runner.ProposerRunner{} - case *runner.SyncCommitteeRunner: - r = &runner.SyncCommitteeRunner{} - case *runner.SyncCommitteeAggregatorRunner: - r = &runner.SyncCommitteeAggregatorRunner{} - case *runner.ValidatorRegistrationRunner: - r = &runner.ValidatorRegistrationRunner{} - case *runner.VoluntaryExitRunner: - r = &runner.VoluntaryExitRunner{} - default: - t.Fatalf("unknown runner type") - } - specDir, err := protocoltesting.GetSpecDir("", filepath.Join("ssv", "spectest")) - require.NoError(t, err) - r, err = typescomparable.UnmarshalStateComparison(specDir, name, testType, r) - require.NoError(t, err) - - // override - test.PostDutyRunnerState = r - - root, err := r.GetRoot() - require.NoError(t, err) - - test.PostDutyRunnerStateRoot = hex.EncodeToString(root[:]) -} diff --git a/protocol/genesis/ssv/spectest/multi_msg_processing_type.go b/protocol/genesis/ssv/spectest/multi_msg_processing_type.go deleted file mode 100644 index d7a3e41841..0000000000 --- a/protocol/genesis/ssv/spectest/multi_msg_processing_type.go +++ /dev/null @@ -1,44 +0,0 @@ -package spectest - -import ( - "path/filepath" - "reflect" - "strings" - "testing" - - "github.com/ssvlabs/ssv/logging" - "go.uber.org/zap" -) - -type MultiMsgProcessingSpecTest struct { - Name string - Tests []*MsgProcessingSpecTest - - logger *zap.Logger -} - -func (tests *MultiMsgProcessingSpecTest) TestName() string { - return tests.Name -} - -func (tests *MultiMsgProcessingSpecTest) Run(t *testing.T) { - tests.logger = logging.TestLogger(t) - tests.overrideStateComparison(t) - - for _, test := range tests.Tests { - t.Run(test.TestName(), func(t *testing.T) { - test.RunAsPartOfMultiTest(t, tests.logger) - }) - } -} - -// overrideStateComparison overrides the post state comparison for all tests in the multi test -func (tests *MultiMsgProcessingSpecTest) overrideStateComparison(t *testing.T) { - testsName := strings.ReplaceAll(tests.TestName(), " ", "_") - for _, test := range tests.Tests { - path := filepath.Join(testsName, test.TestName()) - testType := reflect.TypeOf(tests).String() - testType = strings.Replace(testType, "spectest.", "tests.", 1) - overrideStateComparison(t, test, path, testType) - } -} diff --git a/protocol/genesis/ssv/spectest/multi_start_new_runner_duty_type.go b/protocol/genesis/ssv/spectest/multi_start_new_runner_duty_type.go deleted file mode 100644 index 5a08597757..0000000000 --- a/protocol/genesis/ssv/spectest/multi_start_new_runner_duty_type.go +++ /dev/null @@ -1,167 +0,0 @@ -package spectest - -import ( - "encoding/hex" - "path/filepath" - "reflect" - "strings" - "testing" - - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - spectestingutils "github.com/ssvlabs/ssv-spec-pre-cc/types/testingutils" - typescomparable "github.com/ssvlabs/ssv-spec-pre-cc/types/testingutils/comparable" - "github.com/stretchr/testify/require" - "go.uber.org/zap" - - "github.com/ssvlabs/ssv/protocol/genesis/ssv/runner" - protocoltesting "github.com/ssvlabs/ssv/protocol/genesis/testing" -) - -type StartNewRunnerDutySpecTest struct { - Name string - Runner runner.Runner - Duty *genesisspectypes.Duty - PostDutyRunnerStateRoot string - PostDutyRunnerState genesisspectypes.Root `json:"-"` // Field is ignored by encoding/json - OutputMessages []*genesisspectypes.SignedPartialSignatureMessage - ExpectedError string -} - -func (test *StartNewRunnerDutySpecTest) TestName() string { - return test.Name -} - -// overrideStateComparison overrides the state comparison to compare the runner state -func (test *StartNewRunnerDutySpecTest) overrideStateComparison(t *testing.T) { - testType := reflect.TypeOf(test).String() - testType = strings.Replace(testType, "spectest.", "newduty.", 1) - overrideStateComparisonForStartNewRunnerDutySpecTest(t, test, test.Name, testType) -} - -func (test *StartNewRunnerDutySpecTest) RunAsPartOfMultiTest(t *testing.T, logger *zap.Logger) { - err := test.Runner.StartNewDuty(logger, test.Duty) - if len(test.ExpectedError) > 0 { - require.EqualError(t, err, test.ExpectedError) - } else { - require.NoError(t, err) - } - - // test output message - broadcastedMsgs := test.Runner.GetNetwork().(*spectestingutils.TestingNetwork).BroadcastedMsgs - if len(broadcastedMsgs) > 0 { - index := 0 - for _, msg := range broadcastedMsgs { - if msg.MsgType != genesisspectypes.SSVPartialSignatureMsgType { - continue - } - - msg1 := &genesisspectypes.SignedPartialSignatureMessage{} - require.NoError(t, msg1.Decode(msg.Data)) - msg2 := test.OutputMessages[index] - require.Len(t, msg1.Message.Messages, len(msg2.Message.Messages)) - - // messages are not guaranteed to be in order so we map them and then test all roots to be equal - roots := make(map[string]string) - for i, partialSigMsg2 := range msg2.Message.Messages { - r2, err := partialSigMsg2.GetRoot() - require.NoError(t, err) - if _, found := roots[hex.EncodeToString(r2[:])]; !found { - roots[hex.EncodeToString(r2[:])] = "" - } else { - roots[hex.EncodeToString(r2[:])] = hex.EncodeToString(r2[:]) - } - - partialSigMsg1 := msg1.Message.Messages[i] - r1, err := partialSigMsg1.GetRoot() - require.NoError(t, err) - - if _, found := roots[hex.EncodeToString(r1[:])]; !found { - roots[hex.EncodeToString(r1[:])] = "" - } else { - roots[hex.EncodeToString(r1[:])] = hex.EncodeToString(r1[:]) - } - } - for k, v := range roots { - require.EqualValues(t, k, v, "missing output msg") - } - - index++ - } - - require.Len(t, test.OutputMessages, index) - } - - // post root - postRoot, err := test.Runner.GetRoot() - require.NoError(t, err) - require.EqualValues(t, test.PostDutyRunnerStateRoot, hex.EncodeToString(postRoot[:])) -} - -func (test *StartNewRunnerDutySpecTest) Run(t *testing.T, logger *zap.Logger) { - test.overrideStateComparison(t) - test.RunAsPartOfMultiTest(t, logger) -} - -type MultiStartNewRunnerDutySpecTest struct { - Name string - Tests []*StartNewRunnerDutySpecTest -} - -func (tests *MultiStartNewRunnerDutySpecTest) TestName() string { - return tests.Name -} - -func (tests *MultiStartNewRunnerDutySpecTest) Run(t *testing.T, logger *zap.Logger) { - tests.overrideStateComparison(t) - - for _, test := range tests.Tests { - t.Run(test.TestName(), func(t *testing.T) { - test.RunAsPartOfMultiTest(t, logger) - }) - } -} - -// overrideStateComparison overrides the post state comparison for all tests in the multi test -func (tests *MultiStartNewRunnerDutySpecTest) overrideStateComparison(t *testing.T) { - testsName := strings.ReplaceAll(tests.TestName(), " ", "_") - for _, test := range tests.Tests { - path := filepath.Join(testsName, test.TestName()) - testType := reflect.TypeOf(tests).String() - testType = strings.Replace(testType, "spectest.", "newduty.", 1) - overrideStateComparisonForStartNewRunnerDutySpecTest(t, test, path, testType) - } -} - -func overrideStateComparisonForStartNewRunnerDutySpecTest(t *testing.T, test *StartNewRunnerDutySpecTest, name string, testType string) { - var r runner.Runner - switch test.Runner.(type) { - case *runner.AttesterRunner: - r = &runner.AttesterRunner{} - case *runner.AggregatorRunner: - r = &runner.AggregatorRunner{} - case *runner.ProposerRunner: - r = &runner.ProposerRunner{} - case *runner.SyncCommitteeRunner: - r = &runner.SyncCommitteeRunner{} - case *runner.SyncCommitteeAggregatorRunner: - r = &runner.SyncCommitteeAggregatorRunner{} - case *runner.ValidatorRegistrationRunner: - r = &runner.ValidatorRegistrationRunner{} - case *runner.VoluntaryExitRunner: - r = &runner.VoluntaryExitRunner{} - default: - t.Fatalf("unknown runner type") - } - specDir, err := protocoltesting.GetSpecDir("", filepath.Join("ssv", "spectest")) - require.NoError(t, err) - r, err = typescomparable.UnmarshalStateComparison(specDir, name, testType, r) - require.NoError(t, err) - - // override - test.PostDutyRunnerState = r - - root, err := r.GetRoot() - require.NoError(t, err) - - test.PostDutyRunnerStateRoot = hex.EncodeToString(root[:]) -} diff --git a/protocol/genesis/ssv/spectest/ssv_mapping_test.go b/protocol/genesis/ssv/spectest/ssv_mapping_test.go deleted file mode 100644 index 79743900d5..0000000000 --- a/protocol/genesis/ssv/spectest/ssv_mapping_test.go +++ /dev/null @@ -1,378 +0,0 @@ -package spectest - -import ( - "encoding/json" - "os" - "reflect" - "strings" - "testing" - - "github.com/ssvlabs/ssv-spec-pre-cc/ssv/spectest/tests" - "github.com/ssvlabs/ssv-spec-pre-cc/ssv/spectest/tests/partialsigcontainer" - "github.com/ssvlabs/ssv-spec-pre-cc/ssv/spectest/tests/runner/duties/newduty" - "github.com/ssvlabs/ssv-spec-pre-cc/ssv/spectest/tests/runner/duties/synccommitteeaggregator" - "github.com/ssvlabs/ssv-spec-pre-cc/ssv/spectest/tests/valcheck" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - "github.com/ssvlabs/ssv-spec-pre-cc/types/spectest/tests/partialsigmessage" - "github.com/ssvlabs/ssv-spec-pre-cc/types/testingutils" - "github.com/stretchr/testify/require" - "go.uber.org/zap" - - "github.com/ssvlabs/ssv/logging" - "github.com/ssvlabs/ssv/networkconfig" - "github.com/ssvlabs/ssv/protocol/genesis/qbft/controller" - "github.com/ssvlabs/ssv/protocol/genesis/qbft/instance" - qbfttesting "github.com/ssvlabs/ssv/protocol/genesis/qbft/testing" - "github.com/ssvlabs/ssv/protocol/genesis/ssv/runner" - ssvtesting "github.com/ssvlabs/ssv/protocol/genesis/ssv/testing" - protocoltesting "github.com/ssvlabs/ssv/protocol/genesis/testing" - "github.com/ssvlabs/ssv/protocol/genesis/types" -) - -func TestSSVMapping(t *testing.T) { - path, _ := os.Getwd() - jsonTests, err := protocoltesting.GetSpecTestJSON(path, "ssv") - require.NoError(t, err) - - logger := logging.TestLogger(t) - - untypedTests := map[string]interface{}{} - if err := json.Unmarshal(jsonTests, &untypedTests); err != nil { - panic(err.Error()) - } - - types.SetDefaultDomain(testingutils.TestingSSVDomainType) - - for name, test := range untypedTests { - name, test := name, test - r := prepareTest(t, logger, name, test) - if r != nil { - t.Run(r.name, func(t *testing.T) { - t.Parallel() - r.test(t) - }) - } - } -} - -type runnable struct { - name string - test func(t *testing.T) -} - -func prepareTest(t *testing.T, logger *zap.Logger, name string, test interface{}) *runnable { - testName := strings.Split(name, "_")[1] - testType := strings.Split(name, "_")[0] - - switch testType { - case reflect.TypeOf(&tests.MsgProcessingSpecTest{}).String(): - byts, err := json.Marshal(test) - require.NoError(t, err) - typedTest := &MsgProcessingSpecTest{ - Runner: &runner.AttesterRunner{}, - } - // TODO: fix blinded test - if strings.Contains(testName, "propose regular decide blinded") || strings.Contains(testName, "propose blinded decide regular") { - logger.Info("skipping blinded block test", zap.String("test", testName)) - return nil - } - require.NoError(t, json.Unmarshal(byts, &typedTest)) - - return &runnable{ - name: typedTest.TestName(), - test: func(t *testing.T) { - RunMsgProcessing(t, typedTest) - }, - } - case reflect.TypeOf(&tests.MultiMsgProcessingSpecTest{}).String(): - typedTest := &MultiMsgProcessingSpecTest{ - Name: test.(map[string]interface{})["Name"].(string), - } - subtests := test.(map[string]interface{})["Tests"].([]interface{}) - for _, subtest := range subtests { - typedTest.Tests = append(typedTest.Tests, msgProcessingSpecTestFromMap(t, subtest.(map[string]interface{}))) - } - - return &runnable{ - name: typedTest.TestName(), - test: func(t *testing.T) { - typedTest.Run(t) - }, - } - case reflect.TypeOf(&partialsigmessage.MsgSpecTest{}).String(): // no use of internal structs so can run as spec test runs - byts, err := json.Marshal(test) - require.NoError(t, err) - typedTest := &partialsigmessage.MsgSpecTest{} - require.NoError(t, json.Unmarshal(byts, &typedTest)) - - return &runnable{ - name: typedTest.TestName(), - test: func(t *testing.T) { - typedTest.Run(t) - }, - } - case reflect.TypeOf(&valcheck.SpecTest{}).String(): // no use of internal structs so can run as spec test runs TODO: need to use internal signer - byts, err := json.Marshal(test) - require.NoError(t, err) - typedTest := &valcheck.SpecTest{} - require.NoError(t, json.Unmarshal(byts, &typedTest)) - - return &runnable{ - name: typedTest.TestName(), - test: func(t *testing.T) { - typedTest.Run(t) - }, - } - case reflect.TypeOf(&valcheck.MultiSpecTest{}).String(): // no use of internal structs so can run as spec test runs TODO: need to use internal signer - byts, err := json.Marshal(test) - require.NoError(t, err) - typedTest := &valcheck.MultiSpecTest{} - require.NoError(t, json.Unmarshal(byts, &typedTest)) - - return &runnable{ - name: typedTest.TestName(), - test: func(t *testing.T) { - typedTest.Run(t) - }, - } - case reflect.TypeOf(&synccommitteeaggregator.SyncCommitteeAggregatorProofSpecTest{}).String(): // no use of internal structs so can run as spec test runs TODO: need to use internal signer - byts, err := json.Marshal(test) - require.NoError(t, err) - typedTest := &synccommitteeaggregator.SyncCommitteeAggregatorProofSpecTest{} - require.NoError(t, json.Unmarshal(byts, &typedTest)) - - return &runnable{ - name: typedTest.TestName(), - test: func(t *testing.T) { - RunSyncCommitteeAggProof(t, typedTest) - }, - } - case reflect.TypeOf(&newduty.MultiStartNewRunnerDutySpecTest{}).String(): - typedTest := &MultiStartNewRunnerDutySpecTest{ - Name: test.(map[string]interface{})["Name"].(string), - } - - return &runnable{ - name: typedTest.TestName(), - test: func(t *testing.T) { - subtests := test.(map[string]interface{})["Tests"].([]interface{}) - for _, subtest := range subtests { - typedTest.Tests = append(typedTest.Tests, newRunnerDutySpecTestFromMap(t, subtest.(map[string]interface{}))) - } - typedTest.Run(t, logger) - }, - } - case reflect.TypeOf(&partialsigcontainer.PartialSigContainerTest{}).String(): - byts, err := json.Marshal(test) - require.NoError(t, err) - typedTest := &partialsigcontainer.PartialSigContainerTest{} - require.NoError(t, json.Unmarshal(byts, &typedTest)) - - return &runnable{ - name: typedTest.TestName(), - test: func(t *testing.T) { - typedTest.Run(t) - }, - } - default: - t.Fatalf("unsupported test type %s [%s]", testType, testName) - return nil - } -} - -func newRunnerDutySpecTestFromMap(t *testing.T, m map[string]interface{}) *StartNewRunnerDutySpecTest { - runnerMap := m["Runner"].(map[string]interface{}) - baseRunnerMap := runnerMap["BaseRunner"].(map[string]interface{}) - - duty := &genesisspectypes.Duty{} - byts, _ := json.Marshal(m["Duty"]) - require.NoError(t, json.Unmarshal(byts, duty)) - - outputMsgs := make([]*genesisspectypes.SignedPartialSignatureMessage, 0) - if v, ok := m["OutputMessages"].([]interface{}); ok { - for _, msg := range v { - byts, _ = json.Marshal(msg) - typedMsg := &genesisspectypes.SignedPartialSignatureMessage{} - require.NoError(t, json.Unmarshal(byts, typedMsg)) - outputMsgs = append(outputMsgs, typedMsg) - } - } - - ks := testingutils.KeySetForShare(&genesisspectypes.Share{Quorum: uint64(baseRunnerMap["Share"].(map[string]interface{})["Quorum"].(float64))}) - - r := fixRunnerForRun(t, runnerMap, ks) - - return &StartNewRunnerDutySpecTest{ - Name: m["Name"].(string), - Duty: duty, - Runner: r, - PostDutyRunnerStateRoot: m["PostDutyRunnerStateRoot"].(string), - ExpectedError: m["ExpectedError"].(string), - OutputMessages: outputMsgs, - } -} - -func msgProcessingSpecTestFromMap(t *testing.T, m map[string]interface{}) *MsgProcessingSpecTest { - runnerMap := m["Runner"].(map[string]interface{}) - baseRunnerMap := runnerMap["BaseRunner"].(map[string]interface{}) - - duty := &genesisspectypes.Duty{} - byts, _ := json.Marshal(m["Duty"]) - require.NoError(t, json.Unmarshal(byts, duty)) - - msgs := make([]*genesisspectypes.SSVMessage, 0) - for _, msg := range m["Messages"].([]interface{}) { - byts, _ = json.Marshal(msg) - typedMsg := &genesisspectypes.SSVMessage{} - require.NoError(t, json.Unmarshal(byts, typedMsg)) - msgs = append(msgs, typedMsg) - } - - outputMsgs := make([]*genesisspectypes.SignedPartialSignatureMessage, 0) - require.NotNilf(t, m["OutputMessages"], "OutputMessages can't be nil") - for _, msg := range m["OutputMessages"].([]interface{}) { - byts, _ = json.Marshal(msg) - typedMsg := &genesisspectypes.SignedPartialSignatureMessage{} - require.NoError(t, json.Unmarshal(byts, typedMsg)) - outputMsgs = append(outputMsgs, typedMsg) - } - - beaconBroadcastedRoots := make([]string, 0) - if m["BeaconBroadcastedRoots"] != nil { - for _, r := range m["BeaconBroadcastedRoots"].([]interface{}) { - beaconBroadcastedRoots = append(beaconBroadcastedRoots, r.(string)) - } - } - - ks := testingutils.KeySetForShare(&genesisspectypes.Share{Quorum: uint64(baseRunnerMap["Share"].(map[string]interface{})["Quorum"].(float64))}) - - // runner - r := fixRunnerForRun(t, runnerMap, ks) - - return &MsgProcessingSpecTest{ - Name: m["Name"].(string), - Duty: duty, - Runner: r, - Messages: msgs, - PostDutyRunnerStateRoot: m["PostDutyRunnerStateRoot"].(string), - DontStartDuty: m["DontStartDuty"].(bool), - ExpectedError: m["ExpectedError"].(string), - OutputMessages: outputMsgs, - BeaconBroadcastedRoots: beaconBroadcastedRoots, - } -} - -func fixRunnerForRun(t *testing.T, runnerMap map[string]interface{}, ks *testingutils.TestKeySet) runner.Runner { - baseRunnerMap := runnerMap["BaseRunner"].(map[string]interface{}) - - base := &runner.BaseRunner{ - DomainType: networkconfig.TestNetwork.GenesisDomainType, - } - - byts, _ := json.Marshal(baseRunnerMap) - require.NoError(t, json.Unmarshal(byts, &base)) - - logger := logging.TestLogger(t) - - ret := baseRunnerForRole(logger, base.BeaconRoleType, base, ks) - - // specific for blinded block - if blindedBlocks, ok := runnerMap["ProducesBlindedBlocks"]; ok { - ret.(*runner.ProposerRunner).ProducesBlindedBlocks = blindedBlocks.(bool) - } - - if ret.GetBaseRunner().QBFTController != nil { - ret.GetBaseRunner().QBFTController = fixControllerForRun(t, logger, ret, ret.GetBaseRunner().QBFTController, ks) - if ret.GetBaseRunner().State != nil { - if ret.GetBaseRunner().State.RunningInstance != nil { - ret.GetBaseRunner().State.RunningInstance = fixInstanceForRun(t, ret.GetBaseRunner().State.RunningInstance, ret.GetBaseRunner().QBFTController, ret.GetBaseRunner().Share) - } - } - } - - return ret -} - -func fixControllerForRun(t *testing.T, logger *zap.Logger, runner runner.Runner, contr *controller.Controller, ks *testingutils.TestKeySet) *controller.Controller { - config := qbfttesting.TestingConfig(logger, ks, genesisspectypes.BNRoleAttester) - config.ValueCheckF = runner.GetValCheckF() - newContr := controller.NewController( - contr.Identifier, - contr.Share, - config, - false, - ) - newContr.StoredInstances = make(controller.InstanceContainer, 0, controller.InstanceContainerTestCapacity) - newContr.Height = contr.Height - newContr.StoredInstances = contr.StoredInstances - - for i, inst := range newContr.StoredInstances { - if inst == nil { - continue - } - newContr.StoredInstances[i] = fixInstanceForRun(t, inst, newContr, runner.GetBaseRunner().Share) - } - return newContr -} - -func fixInstanceForRun(t *testing.T, inst *instance.Instance, contr *controller.Controller, share *genesisspectypes.Share) *instance.Instance { - newInst := instance.NewInstance( - contr.GetConfig(), - share, - contr.Identifier, - contr.Height) - - newInst.State.DecidedValue = inst.State.DecidedValue - newInst.State.Decided = inst.State.Decided - newInst.State.Share = inst.State.Share - newInst.State.Round = inst.State.Round - newInst.State.Height = inst.State.Height - newInst.State.ProposalAcceptedForCurrentRound = inst.State.ProposalAcceptedForCurrentRound - newInst.State.ID = inst.State.ID - newInst.State.LastPreparedValue = inst.State.LastPreparedValue - newInst.State.LastPreparedRound = inst.State.LastPreparedRound - newInst.State.ProposeContainer = inst.State.ProposeContainer - newInst.State.PrepareContainer = inst.State.PrepareContainer - newInst.State.CommitContainer = inst.State.CommitContainer - newInst.State.RoundChangeContainer = inst.State.RoundChangeContainer - return newInst -} - -func baseRunnerForRole(logger *zap.Logger, role genesisspectypes.BeaconRole, base *runner.BaseRunner, ks *testingutils.TestKeySet) runner.Runner { - switch role { - case genesisspectypes.BNRoleAttester: - ret := ssvtesting.AttesterRunner(logger, ks) - ret.(*runner.AttesterRunner).BaseRunner = base - return ret - case genesisspectypes.BNRoleAggregator: - ret := ssvtesting.AggregatorRunner(logger, ks) - ret.(*runner.AggregatorRunner).BaseRunner = base - return ret - case genesisspectypes.BNRoleProposer: - ret := ssvtesting.ProposerRunner(logger, ks) - ret.(*runner.ProposerRunner).BaseRunner = base - return ret - case genesisspectypes.BNRoleSyncCommittee: - ret := ssvtesting.SyncCommitteeRunner(logger, ks) - ret.(*runner.SyncCommitteeRunner).BaseRunner = base - return ret - case genesisspectypes.BNRoleSyncCommitteeContribution: - ret := ssvtesting.SyncCommitteeContributionRunner(logger, ks) - ret.(*runner.SyncCommitteeAggregatorRunner).BaseRunner = base - return ret - case genesisspectypes.BNRoleValidatorRegistration: - ret := ssvtesting.ValidatorRegistrationRunner(logger, ks) - ret.(*runner.ValidatorRegistrationRunner).BaseRunner = base - return ret - case genesisspectypes.BNRoleVoluntaryExit: - ret := ssvtesting.VoluntaryExitRunner(logger, ks) - ret.(*runner.VoluntaryExitRunner).BaseRunner = base - return ret - case testingutils.UnknownDutyType: - ret := ssvtesting.UnknownDutyTypeRunner(logger, ks) - ret.(*runner.AttesterRunner).BaseRunner = base - return ret - default: - panic("unknown beacon role") - } -} diff --git a/protocol/genesis/ssv/spectest/sync_committee_aggregator_proof_type.go b/protocol/genesis/ssv/spectest/sync_committee_aggregator_proof_type.go deleted file mode 100644 index 3ecf10b114..0000000000 --- a/protocol/genesis/ssv/spectest/sync_committee_aggregator_proof_type.go +++ /dev/null @@ -1,85 +0,0 @@ -package spectest - -import ( - "encoding/hex" - "path/filepath" - "reflect" - "strings" - "testing" - - "github.com/ssvlabs/ssv-spec-pre-cc/ssv/spectest/tests/runner/duties/synccommitteeaggregator" - "github.com/ssvlabs/ssv-spec-pre-cc/types" - "github.com/ssvlabs/ssv-spec-pre-cc/types/testingutils" - typescomparable "github.com/ssvlabs/ssv-spec-pre-cc/types/testingutils/comparable" - "github.com/stretchr/testify/require" - - "github.com/ssvlabs/ssv/logging" - "github.com/ssvlabs/ssv/protocol/genesis/ssv/genesisqueue" - "github.com/ssvlabs/ssv/protocol/genesis/ssv/runner" - ssvtesting "github.com/ssvlabs/ssv/protocol/genesis/ssv/testing" - protocoltesting "github.com/ssvlabs/ssv/protocol/genesis/testing" -) - -func RunSyncCommitteeAggProof(t *testing.T, test *synccommitteeaggregator.SyncCommitteeAggregatorProofSpecTest) { - overrideStateComparisonForSyncCommitteeAggregatorProofSpecTest(t, test, test.Name) - - ks := testingutils.Testing4SharesSet() - share := testingutils.TestingShare(ks) - logger := logging.TestLogger(t) - v := ssvtesting.BaseValidator(logger, keySetForShare(share)) - r := v.DutyRunners[types.BNRoleSyncCommitteeContribution] - r.GetBeaconNode().(*testingutils.TestingBeaconNode).SetSyncCommitteeAggregatorRootHexes(test.ProofRootsMap) - - lastErr := v.StartDuty(logger, &testingutils.TestingSyncCommitteeContributionDuty) - for _, msg := range test.Messages { - dmsg, err := genesisqueue.DecodeGenesisSSVMessage(msg) - if err != nil { - lastErr = err - continue - } - err = v.ProcessMessage(logger, dmsg) - if err != nil { - lastErr = err - } - } - - if len(test.ExpectedError) != 0 { - require.EqualError(t, lastErr, test.ExpectedError) - } else { - require.NoError(t, lastErr) - } - - // post root - postRoot, err := r.GetBaseRunner().State.GetRoot() - require.NoError(t, err) - require.EqualValues(t, test.PostDutyRunnerStateRoot, hex.EncodeToString(postRoot[:])) -} - -func overrideStateComparisonForSyncCommitteeAggregatorProofSpecTest(t *testing.T, test *synccommitteeaggregator.SyncCommitteeAggregatorProofSpecTest, name string) { - testType := reflect.TypeOf(test).String() - testType = strings.Replace(testType, "spectest.", "synccommitteeaggregator.", 1) - - runnerState := &runner.State{} - specDir, err := protocoltesting.GetSpecDir("", filepath.Join("ssv", "spectest")) - require.NoError(t, err) - runnerState, err = typescomparable.UnmarshalStateComparison(specDir, name, testType, runnerState) - require.NoError(t, err) - - root, err := runnerState.GetRoot() - require.NoError(t, err) - - test.PostDutyRunnerStateRoot = hex.EncodeToString(root[:]) -} - -func keySetForShare(share *types.Share) *testingutils.TestKeySet { - if share.Quorum == 5 { - return testingutils.Testing7SharesSet() - } - if share.Quorum == 7 { - return testingutils.Testing10SharesSet() - } - if share.Quorum == 9 { - return testingutils.Testing13SharesSet() - } - return testingutils.Testing4SharesSet() -} diff --git a/protocol/genesis/ssv/testing/runner.go b/protocol/genesis/ssv/testing/runner.go deleted file mode 100644 index 898d0fd8e9..0000000000 --- a/protocol/genesis/ssv/testing/runner.go +++ /dev/null @@ -1,239 +0,0 @@ -package testing - -import ( - "github.com/attestantio/go-eth2-client/spec/phase0" - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - genesisspecssv "github.com/ssvlabs/ssv-spec-pre-cc/ssv" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - spectestingutils "github.com/ssvlabs/ssv-spec-pre-cc/types/testingutils" - "github.com/ssvlabs/ssv/networkconfig" - "github.com/ssvlabs/ssv/protocol/genesis/qbft/testing" - "github.com/ssvlabs/ssv/protocol/genesis/ssv/runner" - "go.uber.org/zap" -) - -var TestingGraffiti = [32]byte{1} -var TestingHighestDecidedSlot = phase0.Slot(0) - -var AttesterRunner = func(logger *zap.Logger, keySet *spectestingutils.TestKeySet) runner.Runner { - return baseRunner(logger, genesisspectypes.BNRoleAttester, genesisspecssv.AttesterValueCheckF(spectestingutils.NewTestingKeyManager(), genesisspectypes.BeaconTestNetwork, spectestingutils.TestingValidatorPubKey[:], spectestingutils.TestingValidatorIndex, nil), keySet) -} - -//var AttesterRunner7Operators = func(keySet *spectestingutils.TestKeySet) runner.Runner { -// return baseRunner(genesisspectypes.BNRoleAttester, genesisspecssv.AttesterValueCheckF(spectestingutils.NewTestingKeyManager(), genesisspectypes.BeaconTestNetwork, spectestingutils.TestingValidatorPubKey[:], spectestingutils.TestingValidatorIndex), keySet) -//} - -var ProposerRunner = func(logger *zap.Logger, keySet *spectestingutils.TestKeySet) runner.Runner { - return baseRunner(logger, genesisspectypes.BNRoleProposer, genesisspecssv.ProposerValueCheckF(spectestingutils.NewTestingKeyManager(), genesisspectypes.BeaconTestNetwork, spectestingutils.TestingValidatorPubKey[:], spectestingutils.TestingValidatorIndex, nil), keySet) -} - -var AggregatorRunner = func(logger *zap.Logger, keySet *spectestingutils.TestKeySet) runner.Runner { - return baseRunner(logger, genesisspectypes.BNRoleAggregator, genesisspecssv.AggregatorValueCheckF(spectestingutils.NewTestingKeyManager(), genesisspectypes.BeaconTestNetwork, spectestingutils.TestingValidatorPubKey[:], spectestingutils.TestingValidatorIndex), keySet) -} - -var SyncCommitteeRunner = func(logger *zap.Logger, keySet *spectestingutils.TestKeySet) runner.Runner { - return baseRunner(logger, genesisspectypes.BNRoleSyncCommittee, genesisspecssv.SyncCommitteeValueCheckF(spectestingutils.NewTestingKeyManager(), genesisspectypes.BeaconTestNetwork, spectestingutils.TestingValidatorPubKey[:], spectestingutils.TestingValidatorIndex), keySet) -} - -var SyncCommitteeContributionRunner = func(logger *zap.Logger, keySet *spectestingutils.TestKeySet) runner.Runner { - return baseRunner(logger, genesisspectypes.BNRoleSyncCommitteeContribution, genesisspecssv.SyncCommitteeContributionValueCheckF(spectestingutils.NewTestingKeyManager(), genesisspectypes.BeaconTestNetwork, spectestingutils.TestingValidatorPubKey[:], spectestingutils.TestingValidatorIndex), keySet) -} - -var ValidatorRegistrationRunner = func(logger *zap.Logger, keySet *spectestingutils.TestKeySet) runner.Runner { - ret := baseRunner(logger, genesisspectypes.BNRoleValidatorRegistration, nil, keySet) - return ret -} - -var VoluntaryExitRunner = func(logger *zap.Logger, keySet *spectestingutils.TestKeySet) runner.Runner { - return baseRunner(logger, genesisspectypes.BNRoleVoluntaryExit, nil, keySet) -} - -var UnknownDutyTypeRunner = func(logger *zap.Logger, keySet *spectestingutils.TestKeySet) runner.Runner { - return baseRunner(logger, spectestingutils.UnknownDutyType, spectestingutils.UnknownDutyValueCheck(), keySet) -} - -var baseRunner = func(logger *zap.Logger, role genesisspectypes.BeaconRole, valCheck genesisspecqbft.ProposedValueCheckF, keySet *spectestingutils.TestKeySet) runner.Runner { - share := spectestingutils.TestingShare(keySet) - identifier := genesisspectypes.NewMsgID(TestingSSVDomainType, spectestingutils.TestingValidatorPubKey[:], role) - net := spectestingutils.NewTestingNetwork() - km := spectestingutils.NewTestingKeyManager() - - config := testing.TestingConfig(logger, keySet, identifier.GetRoleType()) - config.ValueCheckF = valCheck - config.ProposerF = func(state *genesisspecqbft.State, round genesisspecqbft.Round) genesisspectypes.OperatorID { - return 1 - } - config.Network = net - config.Signer = km - - contr := testing.NewTestingQBFTController( - identifier[:], - share, - config, - false, - ) - - switch role { - case genesisspectypes.BNRoleAttester: - return runner.NewAttesterRunnner( - networkconfig.TestNetwork.GenesisDomainType, - genesisspectypes.BeaconTestNetwork, - share, - contr, - spectestingutils.NewTestingBeaconNode(), - net, - km, - valCheck, - TestingHighestDecidedSlot, - ) - case genesisspectypes.BNRoleAggregator: - return runner.NewAggregatorRunner( - networkconfig.TestNetwork.GenesisDomainType, - genesisspectypes.BeaconTestNetwork, - share, - contr, - spectestingutils.NewTestingBeaconNode(), - net, - km, - valCheck, - TestingHighestDecidedSlot, - ) - case genesisspectypes.BNRoleProposer: - return runner.NewProposerRunner( - networkconfig.TestNetwork.GenesisDomainType, - genesisspectypes.BeaconTestNetwork, - share, - contr, - spectestingutils.NewTestingBeaconNode(), - net, - km, - valCheck, - TestingHighestDecidedSlot, - TestingGraffiti[:], - ) - case genesisspectypes.BNRoleSyncCommittee: - return runner.NewSyncCommitteeRunner( - networkconfig.TestNetwork.GenesisDomainType, - genesisspectypes.BeaconTestNetwork, - share, - contr, - spectestingutils.NewTestingBeaconNode(), - net, - km, - valCheck, - TestingHighestDecidedSlot, - ) - case genesisspectypes.BNRoleSyncCommitteeContribution: - return runner.NewSyncCommitteeAggregatorRunner( - networkconfig.TestNetwork.GenesisDomainType, - genesisspectypes.BeaconTestNetwork, - share, - contr, - spectestingutils.NewTestingBeaconNode(), - net, - km, - valCheck, - TestingHighestDecidedSlot, - ) - case genesisspectypes.BNRoleValidatorRegistration: - return runner.NewValidatorRegistrationRunner( - networkconfig.TestNetwork.GenesisDomainType, - genesisspectypes.BeaconTestNetwork, - share, - spectestingutils.NewTestingBeaconNode(), - net, - km, - ) - case genesisspectypes.BNRoleVoluntaryExit: - return runner.NewVoluntaryExitRunner( - networkconfig.TestNetwork.GenesisDomainType, - genesisspectypes.BeaconTestNetwork, - share, - spectestingutils.NewTestingBeaconNode(), - net, - km, - ) - case spectestingutils.UnknownDutyType: - ret := runner.NewAttesterRunnner( - networkconfig.TestNetwork.GenesisDomainType, - genesisspectypes.BeaconTestNetwork, - share, - contr, - spectestingutils.NewTestingBeaconNode(), - net, - km, - valCheck, - TestingHighestDecidedSlot, - ) - ret.(*runner.AttesterRunner).BaseRunner.BeaconRoleType = spectestingutils.UnknownDutyType - return ret - default: - panic("unknown role type") - } -} - -// -//var DecidedRunner = func(keySet *spectestingutils.TestKeySet) runner.Runner { -// return decideRunner(spectestingutils.TestAttesterConsensusData, genesisspecqbft.FirstHeight, keySet) -//} -// -//var DecidedRunnerWithHeight = func(height genesisspecqbft.Height, keySet *spectestingutils.TestKeySet) runner.Runner { -// return decideRunner(spectestingutils.TestAttesterConsensusData, height, keySet) -//} -// -//var DecidedRunnerUnknownDutyType = func(keySet *spectestingutils.TestKeySet) runner.Runner { -// return decideRunner(spectestingutils.TestConsensusUnkownDutyTypeData, genesisspecqbft.FirstHeight, keySet) -//} -// -//var decideRunner = func(consensusInput *genesisspectypes.ConsensusData, height genesisspecqbft.Height, keySet *spectestingutils.TestKeySet) runner.Runner { -// v := BaseValidator(keySet) -// consensusDataByts, _ := consensusInput.Encode() -// msgs := DecidingMsgsForHeight(consensusDataByts, []byte{1, 2, 3, 4}, height, keySet) -// -// if err := v.DutyRunners[genesisspectypes.BNRoleAttester].StartNewDuty(consensusInput.Duty); err != nil { -// panic(err.Error()) -// } -// for _, msg := range msgs { -// ssvMsg := SSVMsgAttester(msg, nil) -// if err := v.ProcessMessage(ssvMsg); err != nil { -// panic(err.Error()) -// } -// } -// -// return v.DutyRunners[genesisspectypes.BNRoleAttester] -//} -// -//var DecidingMsgsForHeight = func(consensusData, msgIdentifier []byte, height genesisspecqbft.Height, keySet *spectestingutils.TestKeySet) []*genesisspecqbft.SignedMessage { -// msgs := make([]*genesisspecqbft.SignedMessage, 0) -// for h := genesisspecqbft.FirstHeight; h <= height; h++ { -// msgs = append(msgs, spectestingutils.SignQBFTMsg(keySet.Shares[1], 1, &genesisspecqbft.Message{ -// MsgType: genesisspecqbft.ProposalMsgType, -// Height: h, -// Round: genesisspecqbft.FirstRound, -// Identifier: msgIdentifier, -// Data: spectestingutils.ProposalDataBytes(consensusData, nil, nil), -// })) -// -// // prepare -// for i := uint64(1); i <= keySet.Threshold; i++ { -// msgs = append(msgs, spectestingutils.SignQBFTMsg(keySet.Shares[genesisspectypes.OperatorID(i)], genesisspectypes.OperatorID(i), &genesisspecqbft.Message{ -// MsgType: genesisspecqbft.PrepareMsgType, -// Height: h, -// Round: genesisspecqbft.FirstRound, -// Identifier: msgIdentifier, -// Data: spectestingutils.PrepareDataBytes(consensusData), -// })) -// } -// // commit -// for i := uint64(1); i <= keySet.Threshold; i++ { -// msgs = append(msgs, spectestingutils.SignQBFTMsg(keySet.Shares[genesisspectypes.OperatorID(i)], genesisspectypes.OperatorID(i), &genesisspecqbft.Message{ -// MsgType: genesisspecqbft.CommitMsgType, -// Height: h, -// Round: genesisspecqbft.FirstRound, -// Identifier: msgIdentifier, -// Data: spectestingutils.CommitDataBytes(consensusData), -// })) -// } -// } -// return msgs -//} diff --git a/protocol/genesis/ssv/testing/ssv_msgs.go b/protocol/genesis/ssv/testing/ssv_msgs.go deleted file mode 100644 index 5d38dba76c..0000000000 --- a/protocol/genesis/ssv/testing/ssv_msgs.go +++ /dev/null @@ -1,882 +0,0 @@ -package testing - -import ( - spec2 "github.com/attestantio/go-eth2-client/spec" - "github.com/attestantio/go-eth2-client/spec/altair" - "github.com/attestantio/go-eth2-client/spec/deneb" - spec "github.com/attestantio/go-eth2-client/spec/phase0" - "github.com/herumi/bls-eth-go-binary/bls" - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - "github.com/ssvlabs/ssv-spec-pre-cc/types/testingutils" -) - -var TestingSSVDomainType = genesisspectypes.JatoTestnet -var AttesterMsgID = func() []byte { - ret := genesisspectypes.NewMsgID(TestingSSVDomainType, testingutils.TestingValidatorPubKey[:], genesisspectypes.BNRoleAttester) - return ret[:] -}() - -var ProposerMsgID = func() []byte { - ret := genesisspectypes.NewMsgID(TestingSSVDomainType, testingutils.TestingValidatorPubKey[:], genesisspectypes.BNRoleProposer) - return ret[:] -}() -var AggregatorMsgID = func() []byte { - ret := genesisspectypes.NewMsgID(TestingSSVDomainType, testingutils.TestingValidatorPubKey[:], genesisspectypes.BNRoleAggregator) - return ret[:] -}() -var SyncCommitteeMsgID = func() []byte { - ret := genesisspectypes.NewMsgID(TestingSSVDomainType, testingutils.TestingValidatorPubKey[:], genesisspectypes.BNRoleSyncCommittee) - return ret[:] -}() -var SyncCommitteeContributionMsgID = func() []byte { - ret := genesisspectypes.NewMsgID(TestingSSVDomainType, testingutils.TestingValidatorPubKey[:], genesisspectypes.BNRoleSyncCommitteeContribution) - return ret[:] -}() -var ValidatorRegistrationMsgID = func() []byte { - ret := genesisspectypes.NewMsgID(TestingSSVDomainType, testingutils.TestingValidatorPubKey[:], genesisspectypes.BNRoleValidatorRegistration) - return ret[:] -}() - -var TestAttesterConsensusData = &genesisspectypes.ConsensusData{ - Duty: testingutils.TestingAttesterDuty, - DataSSZ: testingutils.TestingAttestationDataBytes, -} -var TestAttesterConsensusDataByts, _ = TestAttesterConsensusData.Encode() - -var TestAggregatorConsensusData = &genesisspectypes.ConsensusData{ - Duty: testingutils.TestingAggregatorDuty, - DataSSZ: testingutils.TestingAggregateAndProofBytes, -} -var TestAggregatorConsensusDataByts, _ = TestAggregatorConsensusData.Encode() - -var TestProposerBlindedBlockConsensusData = &genesisspectypes.ConsensusData{ - Duty: *testingutils.TestingProposerDutyV(spec2.DataVersionCapella), - Version: spec2.DataVersionCapella, - DataSSZ: testingutils.TestingBlindedBeaconBlockBytesV(spec2.DataVersionCapella), -} -var TestProposerBlindedBlockConsensusDataByts, _ = TestProposerBlindedBlockConsensusData.Encode() - -var TestSyncCommitteeConsensusData = &genesisspectypes.ConsensusData{ - Duty: testingutils.TestingSyncCommitteeDuty, - DataSSZ: testingutils.TestingSyncCommitteeBlockRoot[:], -} -var TestSyncCommitteeConsensusDataByts, _ = TestSyncCommitteeConsensusData.Encode() - -var TestSyncCommitteeContributionConsensusData = &genesisspectypes.ConsensusData{ - Duty: testingutils.TestingSyncCommitteeContributionDuty, - DataSSZ: testingutils.TestingContributionsDataBytes, -} -var TestSyncCommitteeContributionConsensusDataByts, _ = TestSyncCommitteeContributionConsensusData.Encode() - -var TestConsensusUnkownDutyTypeData = &genesisspectypes.ConsensusData{ - Duty: testingutils.TestingUnknownDutyType, - DataSSZ: testingutils.TestingAttestationDataBytes, -} -var TestConsensusUnkownDutyTypeDataByts, _ = TestConsensusUnkownDutyTypeData.Encode() - -var TestConsensusWrongDutyPKData = &genesisspectypes.ConsensusData{ - Duty: testingutils.TestingWrongDutyPK, - DataSSZ: testingutils.TestingAttestationDataBytes, -} -var TestConsensusWrongDutyPKDataByts, _ = TestConsensusWrongDutyPKData.Encode() - -var SSVMsgAttester = func(qbftMsg *genesisspecqbft.SignedMessage, partialSigMsg *genesisspectypes.SignedPartialSignatureMessage) *genesisspectypes.SSVMessage { - return ssvMsg(qbftMsg, partialSigMsg, genesisspectypes.NewMsgID(TestingSSVDomainType, testingutils.TestingValidatorPubKey[:], genesisspectypes.BNRoleAttester)) -} - -var SSVMsgWrongID = func(qbftMsg *genesisspecqbft.SignedMessage, partialSigMsg *genesisspectypes.SignedPartialSignatureMessage) *genesisspectypes.SSVMessage { - return ssvMsg(qbftMsg, partialSigMsg, genesisspectypes.NewMsgID(TestingSSVDomainType, testingutils.TestingWrongValidatorPubKey[:], genesisspectypes.BNRoleAttester)) -} - -var SSVMsgProposer = func(qbftMsg *genesisspecqbft.SignedMessage, partialSigMsg *genesisspectypes.SignedPartialSignatureMessage) *genesisspectypes.SSVMessage { - return ssvMsg(qbftMsg, partialSigMsg, genesisspectypes.NewMsgID(TestingSSVDomainType, testingutils.TestingValidatorPubKey[:], genesisspectypes.BNRoleProposer)) -} - -var SSVMsgAggregator = func(qbftMsg *genesisspecqbft.SignedMessage, partialSigMsg *genesisspectypes.SignedPartialSignatureMessage) *genesisspectypes.SSVMessage { - return ssvMsg(qbftMsg, partialSigMsg, genesisspectypes.NewMsgID(TestingSSVDomainType, testingutils.TestingValidatorPubKey[:], genesisspectypes.BNRoleAggregator)) -} - -var SSVMsgSyncCommittee = func(qbftMsg *genesisspecqbft.SignedMessage, partialSigMsg *genesisspectypes.SignedPartialSignatureMessage) *genesisspectypes.SSVMessage { - return ssvMsg(qbftMsg, partialSigMsg, genesisspectypes.NewMsgID(TestingSSVDomainType, testingutils.TestingValidatorPubKey[:], genesisspectypes.BNRoleSyncCommittee)) -} - -var SSVMsgSyncCommitteeContribution = func(qbftMsg *genesisspecqbft.SignedMessage, partialSigMsg *genesisspectypes.SignedPartialSignatureMessage) *genesisspectypes.SSVMessage { - return ssvMsg(qbftMsg, partialSigMsg, genesisspectypes.NewMsgID(TestingSSVDomainType, testingutils.TestingValidatorPubKey[:], genesisspectypes.BNRoleSyncCommitteeContribution)) -} - -var SSVMsgValidatorRegistration = func(qbftMsg *genesisspecqbft.SignedMessage, partialSigMsg *genesisspectypes.SignedPartialSignatureMessage) *genesisspectypes.SSVMessage { - return ssvMsg(qbftMsg, partialSigMsg, genesisspectypes.NewMsgID(TestingSSVDomainType, testingutils.TestingValidatorPubKey[:], genesisspectypes.BNRoleValidatorRegistration)) -} - -var ssvMsg = func(qbftMsg *genesisspecqbft.SignedMessage, postMsg *genesisspectypes.SignedPartialSignatureMessage, msgID genesisspectypes.MessageID) *genesisspectypes.SSVMessage { - var msgType genesisspectypes.MsgType - var data []byte - var err error - if qbftMsg != nil { - msgType = genesisspectypes.SSVConsensusMsgType - data, err = qbftMsg.Encode() - if err != nil { - panic(err) - } - } else if postMsg != nil { - msgType = genesisspectypes.SSVPartialSignatureMsgType - data, err = postMsg.Encode() - if err != nil { - panic(err) - } - } else { - panic("msg type undefined") - } - - return &genesisspectypes.SSVMessage{ - MsgType: msgType, - MsgID: msgID, - Data: data, - } -} - -var PostConsensusWrongAttestationMsg = func(sk *bls.SecretKey, id genesisspectypes.OperatorID, height genesisspecqbft.Height) *genesisspectypes.SignedPartialSignatureMessage { - return postConsensusAttestationMsg(sk, id, height, true, false) -} - -var PostConsensusWrongSigAttestationMsg = func(sk *bls.SecretKey, id genesisspectypes.OperatorID, height genesisspecqbft.Height) *genesisspectypes.SignedPartialSignatureMessage { - return postConsensusAttestationMsg(sk, id, height, false, true) -} - -var PostConsensusSigAttestationWrongBeaconSignerMsg = func(sk *bls.SecretKey, id, beaconSigner genesisspectypes.OperatorID, height genesisspecqbft.Height) *genesisspectypes.SignedPartialSignatureMessage { - ret := postConsensusAttestationMsg(sk, beaconSigner, height, false, true) - ret.Signer = id - return ret -} - -var PostConsensusAttestationMsg = func(sk *bls.SecretKey, id genesisspectypes.OperatorID, height genesisspecqbft.Height) *genesisspectypes.SignedPartialSignatureMessage { - return postConsensusAttestationMsg(sk, id, height, false, false) -} - -var PostConsensusAttestationTooManyRootsMsg = func(sk *bls.SecretKey, id genesisspectypes.OperatorID, height genesisspecqbft.Height) *genesisspectypes.SignedPartialSignatureMessage { - ret := postConsensusAttestationMsg(sk, id, height, false, false) - ret.Message.Messages = append(ret.Message.Messages, ret.Message.Messages[0]) - - msg := &genesisspectypes.PartialSignatureMessages{ - Type: genesisspectypes.PostConsensusPartialSig, - Slot: testingutils.TestingDutySlot, - Messages: ret.Message.Messages, - } - - sig, _ := testingutils.NewTestingKeyManager().SignRoot(msg, genesisspectypes.PartialSignatureType, sk.GetPublicKey().Serialize()) - return &genesisspectypes.SignedPartialSignatureMessage{ - Message: *msg, - Signature: sig, - Signer: id, - } -} - -var PostConsensusAttestationTooFewRootsMsg = func(sk *bls.SecretKey, id genesisspectypes.OperatorID, height genesisspecqbft.Height) *genesisspectypes.SignedPartialSignatureMessage { - msg := &genesisspectypes.PartialSignatureMessages{ - Type: genesisspectypes.PostConsensusPartialSig, - Slot: testingutils.TestingDutySlot, - Messages: []*genesisspectypes.PartialSignatureMessage{}, - } - - sig, _ := testingutils.NewTestingKeyManager().SignRoot(msg, genesisspectypes.PartialSignatureType, sk.GetPublicKey().Serialize()) - return &genesisspectypes.SignedPartialSignatureMessage{ - Message: *msg, - Signature: sig, - Signer: id, - } -} - -var postConsensusAttestationMsg = func( - sk *bls.SecretKey, - id genesisspectypes.OperatorID, - height genesisspecqbft.Height, - wrongRoot bool, - wrongBeaconSig bool, -) *genesisspectypes.SignedPartialSignatureMessage { - signer := testingutils.NewTestingKeyManager() - beacon := testingutils.NewTestingBeaconNode() - d, _ := beacon.DomainData(testingutils.TestingAttestationData.Target.Epoch, genesisspectypes.DomainAttester) - - attData := testingutils.TestingAttestationData - if wrongRoot { - attData = testingutils.TestingWrongAttestationData - } - - signed, root, _ := signer.SignBeaconObject(attData, d, sk.GetPublicKey().Serialize(), genesisspectypes.DomainAttester) - - if wrongBeaconSig { - signed, _, _ = signer.SignBeaconObject(attData, d, testingutils.Testing7SharesSet().ValidatorPK.Serialize(), genesisspectypes.DomainAttester) - } - - msgs := genesisspectypes.PartialSignatureMessages{ - Type: genesisspectypes.PostConsensusPartialSig, - Slot: testingutils.TestingDutySlot, - Messages: []*genesisspectypes.PartialSignatureMessage{ - { - PartialSignature: signed, - SigningRoot: root, - Signer: id, - }, - }, - } - sig, _ := signer.SignRoot(msgs, genesisspectypes.PartialSignatureType, sk.GetPublicKey().Serialize()) - return &genesisspectypes.SignedPartialSignatureMessage{ - Message: msgs, - Signature: sig, - Signer: id, - } -} - -var PostConsensusProposerMsg = func(sk *bls.SecretKey, id genesisspectypes.OperatorID) *genesisspectypes.SignedPartialSignatureMessage { - return postConsensusBeaconBlockMsg(sk, id, false, false) -} - -var PostConsensusProposerTooManyRootsMsg = func(sk *bls.SecretKey, id genesisspectypes.OperatorID) *genesisspectypes.SignedPartialSignatureMessage { - ret := postConsensusBeaconBlockMsg(sk, id, false, false) - ret.Message.Messages = append(ret.Message.Messages, ret.Message.Messages[0]) - - msg := &genesisspectypes.PartialSignatureMessages{ - Type: genesisspectypes.PostConsensusPartialSig, - Slot: testingutils.TestingDutySlot, - Messages: ret.Message.Messages, - } - - sig, _ := testingutils.NewTestingKeyManager().SignRoot(msg, genesisspectypes.PartialSignatureType, sk.GetPublicKey().Serialize()) - return &genesisspectypes.SignedPartialSignatureMessage{ - Message: *msg, - Signature: sig, - Signer: id, - } -} - -var PostConsensusProposerTooFewRootsMsg = func(sk *bls.SecretKey, id genesisspectypes.OperatorID) *genesisspectypes.SignedPartialSignatureMessage { - msg := &genesisspectypes.PartialSignatureMessages{ - Type: genesisspectypes.PostConsensusPartialSig, - Slot: testingutils.TestingDutySlot, - Messages: []*genesisspectypes.PartialSignatureMessage{}, - } - - sig, _ := testingutils.NewTestingKeyManager().SignRoot(msg, genesisspectypes.PartialSignatureType, sk.GetPublicKey().Serialize()) - return &genesisspectypes.SignedPartialSignatureMessage{ - Message: *msg, - Signature: sig, - Signer: id, - } -} - -var PostConsensusWrongProposerMsg = func(sk *bls.SecretKey, id genesisspectypes.OperatorID) *genesisspectypes.SignedPartialSignatureMessage { - return postConsensusBeaconBlockMsg(sk, id, true, false) -} - -var PostConsensusWrongSigProposerMsg = func(sk *bls.SecretKey, id genesisspectypes.OperatorID) *genesisspectypes.SignedPartialSignatureMessage { - return postConsensusBeaconBlockMsg(sk, id, false, true) -} - -var PostConsensusSigProposerWrongBeaconSignerMsg = func(sk *bls.SecretKey, id, beaconSigner genesisspectypes.OperatorID) *genesisspectypes.SignedPartialSignatureMessage { - ret := postConsensusBeaconBlockMsg(sk, beaconSigner, false, true) - ret.Signer = id - return ret -} - -var postConsensusBeaconBlockMsg = func( - sk *bls.SecretKey, - id genesisspectypes.OperatorID, - wrongRoot bool, - wrongBeaconSig bool, -) *genesisspectypes.SignedPartialSignatureMessage { - signer := testingutils.NewTestingKeyManager() - beacon := testingutils.NewTestingBeaconNode() - - block := testingutils.TestingBeaconBlockV(spec2.DataVersionDeneb).Deneb - if wrongRoot { - block = testingutils.TestingWrongBeaconBlockV(spec2.DataVersionDeneb).Deneb - } - - d, _ := beacon.DomainData(1, genesisspectypes.DomainProposer) // epoch doesn't matter here, hard coded - sig, root, _ := signer.SignBeaconObject(block, d, sk.GetPublicKey().Serialize(), genesisspectypes.DomainProposer) - if wrongBeaconSig { - sig, root, _ = signer.SignBeaconObject(block, d, testingutils.Testing7SharesSet().ValidatorPK.Serialize(), genesisspectypes.DomainProposer) - } - blsSig := spec.BLSSignature{} - copy(blsSig[:], sig) - - signed := deneb.SignedBeaconBlock{ - Message: block.Block, - Signature: blsSig, - } - - msgs := genesisspectypes.PartialSignatureMessages{ - Type: genesisspectypes.PostConsensusPartialSig, - Slot: testingutils.TestingDutySlot, - Messages: []*genesisspectypes.PartialSignatureMessage{ - { - PartialSignature: signed.Signature[:], - SigningRoot: root, - Signer: id, - }, - }, - } - msgSig, _ := signer.SignRoot(msgs, genesisspectypes.PartialSignatureType, sk.GetPublicKey().Serialize()) - return &genesisspectypes.SignedPartialSignatureMessage{ - Message: msgs, - Signature: msgSig, - Signer: id, - } -} - -var PreConsensusFailedMsg = func(msgSigner *bls.SecretKey, msgSignerID genesisspectypes.OperatorID) *genesisspectypes.SignedPartialSignatureMessage { - signer := testingutils.NewTestingKeyManager() - beacon := testingutils.NewTestingBeaconNode() - d, _ := beacon.DomainData(testingutils.TestingDutyEpoch, genesisspectypes.DomainRandao) - signed, root, _ := signer.SignBeaconObject(genesisspectypes.SSZUint64(testingutils.TestingDutyEpoch), d, msgSigner.GetPublicKey().Serialize(), genesisspectypes.DomainRandao) - - msg := genesisspectypes.PartialSignatureMessages{ - Type: genesisspectypes.RandaoPartialSig, - Slot: testingutils.TestingDutySlot, - Messages: []*genesisspectypes.PartialSignatureMessage{ - { - PartialSignature: signed[:], - SigningRoot: root, - Signer: msgSignerID, - }, - }, - } - sig, _ := signer.SignRoot(msg, genesisspectypes.PartialSignatureType, msgSigner.GetPublicKey().Serialize()) - return &genesisspectypes.SignedPartialSignatureMessage{ - Message: msg, - Signature: sig, - Signer: msgSignerID, - } -} - -var PreConsensusRandaoMsg = func(sk *bls.SecretKey, id genesisspectypes.OperatorID) *genesisspectypes.SignedPartialSignatureMessage { - return randaoMsg(sk, id, false, testingutils.TestingDutyEpoch, 1, false) -} - -// PreConsensusRandaoNextEpochMsg testing for a second duty start -var PreConsensusRandaoNextEpochMsg = func(sk *bls.SecretKey, id genesisspectypes.OperatorID) *genesisspectypes.SignedPartialSignatureMessage { - return randaoMsg(sk, id, false, testingutils.TestingDutyEpoch+1, 1, false) -} - -var PreConsensusRandaoDifferentEpochMsg = func(sk *bls.SecretKey, id genesisspectypes.OperatorID) *genesisspectypes.SignedPartialSignatureMessage { - return randaoMsg(sk, id, false, testingutils.TestingDutyEpoch+1, 1, false) -} - -var PreConsensusRandaoTooManyRootsMsg = func(sk *bls.SecretKey, id genesisspectypes.OperatorID) *genesisspectypes.SignedPartialSignatureMessage { - return randaoMsg(sk, id, false, testingutils.TestingDutyEpoch, 2, false) -} - -var PreConsensusRandaoTooFewRootsMsg = func(sk *bls.SecretKey, id genesisspectypes.OperatorID) *genesisspectypes.SignedPartialSignatureMessage { - return randaoMsg(sk, id, false, testingutils.TestingDutyEpoch, 0, false) -} - -var PreConsensusRandaoNoMsg = func(sk *bls.SecretKey, id genesisspectypes.OperatorID) *genesisspectypes.SignedPartialSignatureMessage { - return randaoMsg(sk, id, false, testingutils.TestingDutyEpoch, 0, false) -} - -var PreConsensusRandaoWrongBeaconSigMsg = func(sk *bls.SecretKey, id genesisspectypes.OperatorID) *genesisspectypes.SignedPartialSignatureMessage { - return randaoMsg(sk, id, false, testingutils.TestingDutyEpoch, 1, true) -} - -var PreConsensusRandaoDifferentSignerMsg = func( - msgSigner, randaoSigner *bls.SecretKey, - msgSignerID, - randaoSignerID genesisspectypes.OperatorID, -) *genesisspectypes.SignedPartialSignatureMessage { - signer := testingutils.NewTestingKeyManager() - beacon := testingutils.NewTestingBeaconNode() - d, _ := beacon.DomainData(testingutils.TestingDutyEpoch, genesisspectypes.DomainRandao) - signed, root, _ := signer.SignBeaconObject(genesisspectypes.SSZUint64(testingutils.TestingDutyEpoch), d, randaoSigner.GetPublicKey().Serialize(), genesisspectypes.DomainRandao) - - msg := genesisspectypes.PartialSignatureMessages{ - Type: genesisspectypes.RandaoPartialSig, - Slot: testingutils.TestingDutySlot, - Messages: []*genesisspectypes.PartialSignatureMessage{ - { - PartialSignature: signed[:], - SigningRoot: root, - Signer: randaoSignerID, - }, - }, - } - sig, _ := signer.SignRoot(msg, genesisspectypes.PartialSignatureType, msgSigner.GetPublicKey().Serialize()) - return &genesisspectypes.SignedPartialSignatureMessage{ - Message: msg, - Signature: sig, - Signer: msgSignerID, - } -} - -var randaoMsg = func( - sk *bls.SecretKey, - id genesisspectypes.OperatorID, - wrongRoot bool, - epoch spec.Epoch, - msgCnt int, - wrongBeaconSig bool, -) *genesisspectypes.SignedPartialSignatureMessage { - signer := testingutils.NewTestingKeyManager() - beacon := testingutils.NewTestingBeaconNode() - d, _ := beacon.DomainData(epoch, genesisspectypes.DomainRandao) - signed, root, _ := signer.SignBeaconObject(genesisspectypes.SSZUint64(epoch), d, sk.GetPublicKey().Serialize(), genesisspectypes.DomainRandao) - if wrongBeaconSig { - signed, root, _ = signer.SignBeaconObject(genesisspectypes.SSZUint64(testingutils.TestingDutyEpoch), d, testingutils.Testing7SharesSet().ValidatorPK.Serialize(), genesisspectypes.DomainRandao) - } - - msgs := genesisspectypes.PartialSignatureMessages{ - Type: genesisspectypes.RandaoPartialSig, - Slot: testingutils.TestingDutySlot, - Messages: []*genesisspectypes.PartialSignatureMessage{}, - } - for i := 0; i < msgCnt; i++ { - msg := &genesisspectypes.PartialSignatureMessage{ - PartialSignature: signed[:], - SigningRoot: root, - Signer: id, - } - if wrongRoot { - msg.SigningRoot = [32]byte{} - } - msgs.Messages = append(msgs.Messages, msg) - } - - sig, _ := signer.SignRoot(msgs, genesisspectypes.PartialSignatureType, sk.GetPublicKey().Serialize()) - return &genesisspectypes.SignedPartialSignatureMessage{ - Message: msgs, - Signature: sig, - Signer: id, - } -} - -var PreConsensusSelectionProofMsg = func(msgSK, beaconSK *bls.SecretKey, msgID, beaconID genesisspectypes.OperatorID) *genesisspectypes.SignedPartialSignatureMessage { - return PreConsensusCustomSlotSelectionProofMsg(msgSK, beaconSK, msgID, beaconID, testingutils.TestingDutySlot) -} - -var PreConsensusSelectionProofWrongBeaconSigMsg = func(msgSK, beaconSK *bls.SecretKey, msgID, beaconID genesisspectypes.OperatorID) *genesisspectypes.SignedPartialSignatureMessage { - return selectionProofMsg(msgSK, beaconSK, msgID, beaconID, testingutils.TestingDutySlot, testingutils.TestingDutySlot, 1, true) -} - -var PreConsensusSelectionProofNextEpochMsg = func(msgSK, beaconSK *bls.SecretKey, msgID, beaconID genesisspectypes.OperatorID) *genesisspectypes.SignedPartialSignatureMessage { - return selectionProofMsg(msgSK, beaconSK, msgID, beaconID, testingutils.TestingDutySlot2, testingutils.TestingDutySlot2, 1, false) -} - -var PreConsensusSelectionProofTooManyRootsMsg = func(msgSK, beaconSK *bls.SecretKey, msgID, beaconID genesisspectypes.OperatorID) *genesisspectypes.SignedPartialSignatureMessage { - return selectionProofMsg(msgSK, beaconSK, msgID, beaconID, testingutils.TestingDutySlot, testingutils.TestingDutySlot, 3, false) -} - -var PreConsensusSelectionProofTooFewRootsMsg = func(msgSK, beaconSK *bls.SecretKey, msgID, beaconID genesisspectypes.OperatorID) *genesisspectypes.SignedPartialSignatureMessage { - return selectionProofMsg(msgSK, beaconSK, msgID, beaconID, testingutils.TestingDutySlot, testingutils.TestingDutySlot, 0, false) -} - -var PreConsensusCustomSlotSelectionProofMsg = func(msgSK, beaconSK *bls.SecretKey, msgID, beaconID genesisspectypes.OperatorID, slot spec.Slot) *genesisspectypes.SignedPartialSignatureMessage { - return selectionProofMsg(msgSK, beaconSK, msgID, beaconID, slot, testingutils.TestingDutySlot, 1, false) -} - -var PreConsensusWrongMsgSlotSelectionProofMsg = func(msgSK, beaconSK *bls.SecretKey, msgID, beaconID genesisspectypes.OperatorID) *genesisspectypes.SignedPartialSignatureMessage { - return selectionProofMsg(msgSK, beaconSK, msgID, beaconID, testingutils.TestingDutySlot, testingutils.TestingDutySlot+1, 1, false) -} - -var selectionProofMsg = func( - sk *bls.SecretKey, - beaconsk *bls.SecretKey, - id genesisspectypes.OperatorID, - beaconid genesisspectypes.OperatorID, - slot spec.Slot, - msgSlot spec.Slot, - msgCnt int, - wrongBeaconSig bool, -) *genesisspectypes.SignedPartialSignatureMessage { - signer := testingutils.NewTestingKeyManager() - beacon := testingutils.NewTestingBeaconNode() - d, _ := beacon.DomainData(1, genesisspectypes.DomainSelectionProof) - signed, root, _ := signer.SignBeaconObject(genesisspectypes.SSZUint64(slot), d, beaconsk.GetPublicKey().Serialize(), genesisspectypes.DomainSelectionProof) - if wrongBeaconSig { - signed, root, _ = signer.SignBeaconObject(genesisspectypes.SSZUint64(slot), d, testingutils.Testing7SharesSet().ValidatorPK.Serialize(), genesisspectypes.DomainSelectionProof) - } - - _msgs := make([]*genesisspectypes.PartialSignatureMessage, 0) - for i := 0; i < msgCnt; i++ { - _msgs = append(_msgs, &genesisspectypes.PartialSignatureMessage{ - PartialSignature: signed[:], - SigningRoot: root, - Signer: beaconid, - }) - } - - msgs := genesisspectypes.PartialSignatureMessages{ - Type: genesisspectypes.SelectionProofPartialSig, - Slot: testingutils.TestingDutySlot, - Messages: _msgs, - } - msgSig, _ := signer.SignRoot(msgs, genesisspectypes.PartialSignatureType, sk.GetPublicKey().Serialize()) - return &genesisspectypes.SignedPartialSignatureMessage{ - Message: msgs, - Signature: msgSig, - Signer: id, - } -} - -var PreConsensusValidatorRegistrationMsg = func(msgSK *bls.SecretKey, msgID genesisspectypes.OperatorID) *genesisspectypes.SignedPartialSignatureMessage { - return validatorRegistrationMsg(msgSK, msgSK, msgID, msgID, 1, false, testingutils.TestingDutyEpoch, false) -} - -var PreConsensusValidatorRegistrationTooFewRootsMsg = func(msgSK *bls.SecretKey, msgID genesisspectypes.OperatorID) *genesisspectypes.SignedPartialSignatureMessage { - return validatorRegistrationMsg(msgSK, msgSK, msgID, msgID, 0, false, testingutils.TestingDutyEpoch, false) -} - -var PreConsensusValidatorRegistrationTooManyRootsMsg = func(msgSK *bls.SecretKey, msgID genesisspectypes.OperatorID) *genesisspectypes.SignedPartialSignatureMessage { - return validatorRegistrationMsg(msgSK, msgSK, msgID, msgID, 2, false, testingutils.TestingDutyEpoch, false) -} - -var PreConsensusValidatorRegistrationDifferentEpochMsg = func(msgSK *bls.SecretKey, msgID genesisspectypes.OperatorID) *genesisspectypes.SignedPartialSignatureMessage { - return validatorRegistrationMsg(msgSK, msgSK, msgID, msgID, 1, true, testingutils.TestingDutyEpoch, false) -} - -var validatorRegistrationMsg = func( - sk, beaconSK *bls.SecretKey, - id, beaconID genesisspectypes.OperatorID, - msgCnt int, - wrongRoot bool, - epoch spec.Epoch, - wrongBeaconSig bool, -) *genesisspectypes.SignedPartialSignatureMessage { - signer := testingutils.NewTestingKeyManager() - beacon := testingutils.NewTestingBeaconNode() - d, _ := beacon.DomainData(epoch, genesisspectypes.DomainApplicationBuilder) - - signed, root, _ := signer.SignBeaconObject(testingutils.TestingValidatorRegistration, d, beaconSK.GetPublicKey().Serialize(), genesisspectypes.DomainApplicationBuilder) - if wrongRoot { - signed, root, _ = signer.SignBeaconObject(testingutils.TestingValidatorRegistrationWrong, d, beaconSK.GetPublicKey().Serialize(), genesisspectypes.DomainApplicationBuilder) - } - if wrongBeaconSig { - signed, root, _ = signer.SignBeaconObject(testingutils.TestingValidatorRegistration, d, testingutils.Testing7SharesSet().ValidatorPK.Serialize(), genesisspectypes.DomainApplicationBuilder) - } - - msgs := genesisspectypes.PartialSignatureMessages{ - Type: genesisspectypes.ValidatorRegistrationPartialSig, - Slot: testingutils.TestingDutySlot, - Messages: []*genesisspectypes.PartialSignatureMessage{}, - } - - for i := 0; i < msgCnt; i++ { - msg := &genesisspectypes.PartialSignatureMessage{ - PartialSignature: signed[:], - SigningRoot: root, - Signer: beaconID, - } - msgs.Messages = append(msgs.Messages, msg) - } - - msg := &genesisspectypes.PartialSignatureMessage{ - PartialSignature: signed[:], - SigningRoot: root, - Signer: id, - } - if wrongRoot { - msg.SigningRoot = [32]byte{} - } - - sig, _ := signer.SignRoot(msgs, genesisspectypes.PartialSignatureType, sk.GetPublicKey().Serialize()) - return &genesisspectypes.SignedPartialSignatureMessage{ - Message: msgs, - Signature: sig, - Signer: id, - } -} - -var PostConsensusAggregatorMsg = func(sk *bls.SecretKey, id genesisspectypes.OperatorID) *genesisspectypes.SignedPartialSignatureMessage { - return postConsensusAggregatorMsg(sk, id, false, false) -} - -var PostConsensusAggregatorTooManyRootsMsg = func(sk *bls.SecretKey, id genesisspectypes.OperatorID) *genesisspectypes.SignedPartialSignatureMessage { - ret := postConsensusAggregatorMsg(sk, id, false, false) - ret.Message.Messages = append(ret.Message.Messages, ret.Message.Messages[0]) - - msg := &genesisspectypes.PartialSignatureMessages{ - Type: genesisspectypes.PostConsensusPartialSig, - Slot: testingutils.TestingDutySlot, - Messages: ret.Message.Messages, - } - - sig, _ := testingutils.NewTestingKeyManager().SignRoot(msg, genesisspectypes.PartialSignatureType, sk.GetPublicKey().Serialize()) - return &genesisspectypes.SignedPartialSignatureMessage{ - Message: *msg, - Signature: sig, - Signer: id, - } -} - -var PostConsensusAggregatorTooFewRootsMsg = func(sk *bls.SecretKey, id genesisspectypes.OperatorID) *genesisspectypes.SignedPartialSignatureMessage { - msg := &genesisspectypes.PartialSignatureMessages{ - Type: genesisspectypes.PostConsensusPartialSig, - Slot: testingutils.TestingDutySlot, - Messages: []*genesisspectypes.PartialSignatureMessage{}, - } - - sig, _ := testingutils.NewTestingKeyManager().SignRoot(msg, genesisspectypes.PartialSignatureType, sk.GetPublicKey().Serialize()) - return &genesisspectypes.SignedPartialSignatureMessage{ - Message: *msg, - Signature: sig, - Signer: id, - } -} - -var PostConsensusWrongAggregatorMsg = func(sk *bls.SecretKey, id genesisspectypes.OperatorID) *genesisspectypes.SignedPartialSignatureMessage { - return postConsensusAggregatorMsg(sk, id, true, false) -} - -var PostConsensusWrongSigAggregatorMsg = func(sk *bls.SecretKey, id genesisspectypes.OperatorID) *genesisspectypes.SignedPartialSignatureMessage { - return postConsensusAggregatorMsg(sk, id, false, true) -} - -var PostConsensusSigAggregatorWrongBeaconSignerMsg = func(sk *bls.SecretKey, id, beaconSigner genesisspectypes.OperatorID) *genesisspectypes.SignedPartialSignatureMessage { - ret := postConsensusAggregatorMsg(sk, beaconSigner, false, true) - ret.Signer = id - return ret -} - -var postConsensusAggregatorMsg = func( - sk *bls.SecretKey, - id genesisspectypes.OperatorID, - wrongRoot bool, - wrongBeaconSig bool, -) *genesisspectypes.SignedPartialSignatureMessage { - signer := testingutils.NewTestingKeyManager() - beacon := testingutils.NewTestingBeaconNode() - d, _ := beacon.DomainData(1, genesisspectypes.DomainAggregateAndProof) - - aggData := testingutils.TestingAggregateAndProof - if wrongRoot { - aggData = testingutils.TestingWrongAggregateAndProof - } - - signed, root, _ := signer.SignBeaconObject(aggData, d, sk.GetPublicKey().Serialize(), genesisspectypes.DomainAggregateAndProof) - if wrongBeaconSig { - signed, root, _ = signer.SignBeaconObject(aggData, d, testingutils.Testing7SharesSet().ValidatorPK.Serialize(), genesisspectypes.DomainAggregateAndProof) - } - - msgs := genesisspectypes.PartialSignatureMessages{ - Type: genesisspectypes.PostConsensusPartialSig, - Slot: testingutils.TestingDutySlot, - Messages: []*genesisspectypes.PartialSignatureMessage{ - { - PartialSignature: signed, - SigningRoot: root, - Signer: id, - }, - }, - } - sig, _ := signer.SignRoot(msgs, genesisspectypes.PartialSignatureType, sk.GetPublicKey().Serialize()) - return &genesisspectypes.SignedPartialSignatureMessage{ - Message: msgs, - Signature: sig, - Signer: id, - } -} - -var PostConsensusSyncCommitteeMsg = func(sk *bls.SecretKey, id genesisspectypes.OperatorID) *genesisspectypes.SignedPartialSignatureMessage { - return postConsensusSyncCommitteeMsg(sk, id, false, false) -} - -var PostConsensusSyncCommitteeTooManyRootsMsg = func(sk *bls.SecretKey, id genesisspectypes.OperatorID) *genesisspectypes.SignedPartialSignatureMessage { - ret := postConsensusSyncCommitteeMsg(sk, id, false, false) - ret.Message.Messages = append(ret.Message.Messages, ret.Message.Messages[0]) - - msg := &genesisspectypes.PartialSignatureMessages{ - Type: genesisspectypes.PostConsensusPartialSig, - Slot: testingutils.TestingDutySlot, - Messages: ret.Message.Messages, - } - - sig, _ := testingutils.NewTestingKeyManager().SignRoot(msg, genesisspectypes.PartialSignatureType, sk.GetPublicKey().Serialize()) - return &genesisspectypes.SignedPartialSignatureMessage{ - Message: *msg, - Signature: sig, - Signer: id, - } -} - -var PostConsensusSyncCommitteeTooFewRootsMsg = func(sk *bls.SecretKey, id genesisspectypes.OperatorID) *genesisspectypes.SignedPartialSignatureMessage { - msg := &genesisspectypes.PartialSignatureMessages{ - Type: genesisspectypes.PostConsensusPartialSig, - Slot: testingutils.TestingDutySlot, - Messages: []*genesisspectypes.PartialSignatureMessage{}, - } - - sig, _ := testingutils.NewTestingKeyManager().SignRoot(msg, genesisspectypes.PartialSignatureType, sk.GetPublicKey().Serialize()) - return &genesisspectypes.SignedPartialSignatureMessage{ - Message: *msg, - Signature: sig, - Signer: id, - } -} - -var PostConsensusWrongSyncCommitteeMsg = func(sk *bls.SecretKey, id genesisspectypes.OperatorID) *genesisspectypes.SignedPartialSignatureMessage { - return postConsensusSyncCommitteeMsg(sk, id, true, false) -} - -var PostConsensusWrongSigSyncCommitteeMsg = func(sk *bls.SecretKey, id genesisspectypes.OperatorID) *genesisspectypes.SignedPartialSignatureMessage { - return postConsensusSyncCommitteeMsg(sk, id, false, true) -} - -var PostConsensusSigSyncCommitteeWrongBeaconSignerMsg = func(sk *bls.SecretKey, id, beaconSigner genesisspectypes.OperatorID) *genesisspectypes.SignedPartialSignatureMessage { - ret := postConsensusSyncCommitteeMsg(sk, beaconSigner, false, true) - ret.Signer = id - return ret -} - -var postConsensusSyncCommitteeMsg = func( - sk *bls.SecretKey, - id genesisspectypes.OperatorID, - wrongRoot bool, - wrongBeaconSig bool, -) *genesisspectypes.SignedPartialSignatureMessage { - signer := testingutils.NewTestingKeyManager() - beacon := testingutils.NewTestingBeaconNode() - d, _ := beacon.DomainData(1, genesisspectypes.DomainSyncCommittee) - blockRoot := testingutils.TestingSyncCommitteeBlockRoot - if wrongRoot { - blockRoot = testingutils.TestingSyncCommitteeWrongBlockRoot - } - signed, root, _ := signer.SignBeaconObject(genesisspectypes.SSZBytes(blockRoot[:]), d, sk.GetPublicKey().Serialize(), genesisspectypes.DomainSyncCommittee) - if wrongBeaconSig { - signed, root, _ = signer.SignBeaconObject(genesisspectypes.SSZBytes(blockRoot[:]), d, testingutils.Testing7SharesSet().ValidatorPK.Serialize(), genesisspectypes.DomainSyncCommittee) - } - - msgs := genesisspectypes.PartialSignatureMessages{ - Type: genesisspectypes.PostConsensusPartialSig, - Slot: testingutils.TestingDutySlot, - Messages: []*genesisspectypes.PartialSignatureMessage{ - { - PartialSignature: signed, - SigningRoot: root, - Signer: id, - }, - }, - } - sig, _ := signer.SignRoot(msgs, genesisspectypes.PartialSignatureType, sk.GetPublicKey().Serialize()) - return &genesisspectypes.SignedPartialSignatureMessage{ - Message: msgs, - Signature: sig, - Signer: id, - } -} - -var PreConsensusContributionProofMsg = func(msgSK, beaconSK *bls.SecretKey, msgID, beaconID genesisspectypes.OperatorID) *genesisspectypes.SignedPartialSignatureMessage { - return PreConsensusCustomSlotContributionProofMsg(msgSK, beaconSK, msgID, beaconID, testingutils.TestingDutySlot) -} - -var PreConsensusContributionProofWrongBeaconSigMsg = func(msgSK, beaconSK *bls.SecretKey, msgID, beaconID genesisspectypes.OperatorID) *genesisspectypes.SignedPartialSignatureMessage { - return contributionProofMsg(msgSK, beaconSK, msgID, beaconID, testingutils.TestingDutySlot, testingutils.TestingDutySlot+1, false, true) -} - -var PreConsensusContributionProofNextEpochMsg = func(msgSK, beaconSK *bls.SecretKey, msgID, beaconID genesisspectypes.OperatorID) *genesisspectypes.SignedPartialSignatureMessage { - return contributionProofMsg(msgSK, beaconSK, msgID, beaconID, testingutils.TestingDutySlot2, testingutils.TestingDutySlot2, false, false) -} - -var PreConsensusCustomSlotContributionProofMsg = func(msgSK, beaconSK *bls.SecretKey, msgID, beaconID genesisspectypes.OperatorID, slot spec.Slot) *genesisspectypes.SignedPartialSignatureMessage { - return contributionProofMsg(msgSK, beaconSK, msgID, beaconID, slot, testingutils.TestingDutySlot, false, false) -} - -var PreConsensusWrongMsgSlotContributionProofMsg = func(msgSK, beaconSK *bls.SecretKey, msgID, beaconID genesisspectypes.OperatorID) *genesisspectypes.SignedPartialSignatureMessage { - return contributionProofMsg(msgSK, beaconSK, msgID, beaconID, testingutils.TestingDutySlot, testingutils.TestingDutySlot+1, false, false) -} - -var PreConsensusWrongOrderContributionProofMsg = func(msgSK, beaconSK *bls.SecretKey, msgID, beaconID genesisspectypes.OperatorID) *genesisspectypes.SignedPartialSignatureMessage { - return contributionProofMsg(msgSK, beaconSK, msgID, beaconID, testingutils.TestingDutySlot, testingutils.TestingDutySlot, true, false) -} - -var PreConsensusContributionProofTooManyRootsMsg = func(msgSK, beaconSK *bls.SecretKey, msgID, beaconID genesisspectypes.OperatorID) *genesisspectypes.SignedPartialSignatureMessage { - ret := contributionProofMsg(msgSK, beaconSK, msgID, beaconID, testingutils.TestingDutySlot, testingutils.TestingDutySlot, false, false) - msg := &genesisspectypes.PartialSignatureMessages{ - Type: genesisspectypes.ContributionProofs, - Slot: testingutils.TestingDutySlot, - Messages: append(ret.Message.Messages, ret.Message.Messages[0]), - } - - msgSig, _ := testingutils.NewTestingKeyManager().SignRoot(msg, genesisspectypes.PartialSignatureType, beaconSK.GetPublicKey().Serialize()) - return &genesisspectypes.SignedPartialSignatureMessage{ - Message: *msg, - Signature: msgSig, - Signer: msgID, - } -} - -var PreConsensusContributionProofTooFewRootsMsg = func(msgSK, beaconSK *bls.SecretKey, msgID, beaconID genesisspectypes.OperatorID) *genesisspectypes.SignedPartialSignatureMessage { - ret := contributionProofMsg(msgSK, beaconSK, msgID, beaconID, testingutils.TestingDutySlot, testingutils.TestingDutySlot, false, false) - msg := &genesisspectypes.PartialSignatureMessages{ - Type: genesisspectypes.ContributionProofs, - Slot: testingutils.TestingDutySlot, - Messages: ret.Message.Messages[0:2], - } - - msgSig, _ := testingutils.NewTestingKeyManager().SignRoot(msg, genesisspectypes.PartialSignatureType, beaconSK.GetPublicKey().Serialize()) - return &genesisspectypes.SignedPartialSignatureMessage{ - Message: *msg, - Signature: msgSig, - Signer: msgID, - } -} - -var contributionProofMsg = func( - sk, beaconsk *bls.SecretKey, - id, beaconid genesisspectypes.OperatorID, - slot spec.Slot, - msgSlot spec.Slot, - wrongMsgOrder bool, - wrongBeaconSig bool, -) *genesisspectypes.SignedPartialSignatureMessage { - signer := testingutils.NewTestingKeyManager() - beacon := testingutils.NewTestingBeaconNode() - d, _ := beacon.DomainData(1, genesisspectypes.DomainSyncCommitteeSelectionProof) - - msgs := make([]*genesisspectypes.PartialSignatureMessage, 0) - for index := range testingutils.TestingContributionProofIndexes { - subnet, _ := beacon.SyncCommitteeSubnetID(spec.CommitteeIndex(index)) - data := &altair.SyncAggregatorSelectionData{ - Slot: slot, - SubcommitteeIndex: subnet, - } - sig, root, _ := signer.SignBeaconObject(data, d, beaconsk.GetPublicKey().Serialize(), genesisspectypes.DomainSyncCommitteeSelectionProof) - if wrongBeaconSig { - sig, root, _ = signer.SignBeaconObject(data, d, testingutils.Testing7SharesSet().ValidatorPK.Serialize(), genesisspectypes.DomainSyncCommitteeSelectionProof) - } - - msg := &genesisspectypes.PartialSignatureMessage{ - PartialSignature: sig[:], - SigningRoot: ensureRoot(root), - Signer: beaconid, - } - - msgs = append(msgs, msg) - } - - if wrongMsgOrder { - m := msgs[0] - msgs[0] = msgs[1] - msgs[1] = m - } - - msg := &genesisspectypes.PartialSignatureMessages{ - Type: genesisspectypes.ContributionProofs, - Slot: testingutils.TestingDutySlot, - Messages: msgs, - } - - msgSig, _ := signer.SignRoot(msg, genesisspectypes.PartialSignatureType, sk.GetPublicKey().Serialize()) - return &genesisspectypes.SignedPartialSignatureMessage{ - Message: *msg, - Signature: msgSig, - Signer: id, - } -} - -// ensureRoot ensures that SigningRoot will have sufficient allocated memory -// otherwise we get panic from bls: -// github.com/herumi/bls-eth-go-binary/bls.(*Sign).VerifyByte:738 -func ensureRoot(root [32]byte) [32]byte { - tmp := [32]byte{} - copy(tmp[:], root[:]) - return tmp -} diff --git a/protocol/genesis/ssv/testing/validator.go b/protocol/genesis/ssv/testing/validator.go deleted file mode 100644 index a7583ddec8..0000000000 --- a/protocol/genesis/ssv/testing/validator.go +++ /dev/null @@ -1,40 +0,0 @@ -package testing - -import ( - "context" - - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - spectestingutils "github.com/ssvlabs/ssv-spec-pre-cc/types/testingutils" - "go.uber.org/zap" - - "github.com/ssvlabs/ssv/networkconfig" - "github.com/ssvlabs/ssv/protocol/genesis/ssv/runner" - "github.com/ssvlabs/ssv/protocol/genesis/ssv/validator" - "github.com/ssvlabs/ssv/protocol/genesis/types" -) - -var BaseValidator = func(logger *zap.Logger, keySet *spectestingutils.TestKeySet) *validator.Validator { - ctx, cancel := context.WithCancel(context.TODO()) - - return validator.NewValidator( - ctx, - cancel, - validator.Options{ - Network: spectestingutils.NewTestingNetwork(), - BeaconNetwork: networkconfig.TestNetwork.Beacon, - SSVShare: &types.SSVShare{ - Share: *spectestingutils.TestingShare(keySet), - }, - Signer: spectestingutils.NewTestingKeyManager(), - DutyRunners: map[genesisspectypes.BeaconRole]runner.Runner{ - genesisspectypes.BNRoleAttester: AttesterRunner(logger, keySet), - genesisspectypes.BNRoleProposer: ProposerRunner(logger, keySet), - genesisspectypes.BNRoleAggregator: AggregatorRunner(logger, keySet), - genesisspectypes.BNRoleSyncCommittee: SyncCommitteeRunner(logger, keySet), - genesisspectypes.BNRoleSyncCommitteeContribution: SyncCommitteeContributionRunner(logger, keySet), - genesisspectypes.BNRoleValidatorRegistration: ValidatorRegistrationRunner(logger, keySet), - genesisspectypes.BNRoleVoluntaryExit: VoluntaryExitRunner(logger, keySet), - }, - }, - ) -} diff --git a/protocol/genesis/ssv/validator/duty_executer.go b/protocol/genesis/ssv/validator/duty_executer.go deleted file mode 100644 index c9a80a81eb..0000000000 --- a/protocol/genesis/ssv/validator/duty_executer.go +++ /dev/null @@ -1,29 +0,0 @@ -package validator - -import ( - "github.com/pkg/errors" - "go.uber.org/zap" - - spectypes "github.com/ssvlabs/ssv-spec/types" - "github.com/ssvlabs/ssv/logging/fields" - "github.com/ssvlabs/ssv/protocol/genesis/types" -) - -func (v *Validator) OnExecuteDuty(logger *zap.Logger, msg types.EventMsg) error { - executeDutyData, err := msg.GetExecuteDutyData() - if err != nil { - return errors.Wrap(err, "failed to get execute duty data") - } - - logger = logger.With(fields.Slot(executeDutyData.Duty.Slot), fields.BeaconRole(spectypes.BeaconRole(executeDutyData.Duty.Type))) - - // force the validator to be started (subscribed to validator's topic and synced) - if _, err := v.Start(logger); err != nil { - return errors.Wrap(err, "could not start validator") - } - if err := v.StartDuty(logger, executeDutyData.Duty); err != nil { - return errors.Wrap(err, "could not start duty") - } - - return nil -} diff --git a/protocol/genesis/ssv/validator/events.go b/protocol/genesis/ssv/validator/events.go deleted file mode 100644 index a9c167bf53..0000000000 --- a/protocol/genesis/ssv/validator/events.go +++ /dev/null @@ -1,33 +0,0 @@ -package validator - -import ( - "fmt" - - "github.com/pkg/errors" - "go.uber.org/zap" - - "github.com/ssvlabs/ssv/protocol/genesis/ssv/genesisqueue" - "github.com/ssvlabs/ssv/protocol/genesis/ssv/runner" - "github.com/ssvlabs/ssv/protocol/genesis/types" -) - -func (v *Validator) handleEventMessage(logger *zap.Logger, msg *genesisqueue.GenesisSSVMessage, dutyRunner runner.Runner) error { - eventMsg, ok := msg.Body.(*types.EventMsg) - if !ok { - return errors.New("could not decode event message") - } - switch eventMsg.Type { - case types.Timeout: - if err := dutyRunner.GetBaseRunner().QBFTController.OnTimeout(logger, *eventMsg); err != nil { - return fmt.Errorf("timeout event: %w", err) - } - return nil - case types.ExecuteDuty: - if err := v.OnExecuteDuty(logger, *eventMsg); err != nil { - return fmt.Errorf("execute duty event: %w", err) - } - return nil - default: - return fmt.Errorf("unknown event msg - %s", eventMsg.Type.String()) - } -} diff --git a/protocol/genesis/ssv/validator/metrics.go b/protocol/genesis/ssv/validator/metrics.go deleted file mode 100644 index f6956b141f..0000000000 --- a/protocol/genesis/ssv/validator/metrics.go +++ /dev/null @@ -1,45 +0,0 @@ -package validator - -import ( - "time" - - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - - "github.com/ssvlabs/ssv/protocol/genesis/ssv/genesisqueue" -) - -type Metrics interface { - ValidatorInactive(publicKey []byte) - ValidatorNoIndex(publicKey []byte) - ValidatorError(publicKey []byte) - ValidatorReady(publicKey []byte) - ValidatorNotActivated(publicKey []byte) - ValidatorExiting(publicKey []byte) - ValidatorSlashed(publicKey []byte) - ValidatorNotFound(publicKey []byte) - ValidatorPending(publicKey []byte) - ValidatorRemoved(publicKey []byte) - ValidatorUnknown(publicKey []byte) - - genesisqueue.Metrics -} - -type NopMetrics struct{} - -func (n NopMetrics) ValidatorInactive([]byte) {} -func (n NopMetrics) ValidatorNoIndex([]byte) {} -func (n NopMetrics) ValidatorError([]byte) {} -func (n NopMetrics) ValidatorReady([]byte) {} -func (n NopMetrics) ValidatorNotActivated([]byte) {} -func (n NopMetrics) ValidatorExiting([]byte) {} -func (n NopMetrics) ValidatorSlashed([]byte) {} -func (n NopMetrics) ValidatorNotFound([]byte) {} -func (n NopMetrics) ValidatorPending([]byte) {} -func (n NopMetrics) ValidatorRemoved([]byte) {} -func (n NopMetrics) ValidatorUnknown([]byte) {} -func (n NopMetrics) IncomingQueueMessage(genesisspectypes.MessageID) {} -func (n NopMetrics) OutgoingQueueMessage(genesisspectypes.MessageID) {} -func (n NopMetrics) DroppedQueueMessage(genesisspectypes.MessageID) {} -func (n NopMetrics) MessageQueueSize(int) {} -func (n NopMetrics) MessageQueueCapacity(int) {} -func (n NopMetrics) MessageTimeInQueue(genesisspectypes.MessageID, time.Duration) {} diff --git a/protocol/genesis/ssv/validator/msgqueue_consumer.go b/protocol/genesis/ssv/validator/msgqueue_consumer.go deleted file mode 100644 index 269003de99..0000000000 --- a/protocol/genesis/ssv/validator/msgqueue_consumer.go +++ /dev/null @@ -1,211 +0,0 @@ -package validator - -import ( - "context" - "fmt" - - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - spectypes "github.com/ssvlabs/ssv-spec/types" - - "github.com/pkg/errors" - "go.uber.org/zap" - - "github.com/ssvlabs/ssv/logging/fields" - "github.com/ssvlabs/ssv/protocol/genesis/message" - "github.com/ssvlabs/ssv/protocol/genesis/qbft/instance" - "github.com/ssvlabs/ssv/protocol/genesis/ssv/genesisqueue" - "github.com/ssvlabs/ssv/protocol/genesis/types" -) - -// MessageHandler process the msg. return error if exist -type MessageHandler func(logger *zap.Logger, msg *genesisqueue.GenesisSSVMessage) error - -// queueContainer wraps a queue with its corresponding state -type queueContainer struct { - Q genesisqueue.Queue - queueState *genesisqueue.State -} - -// HandleMessage handles a genesisspectypes.SSVMessage. -// TODO: accept DecodedSSVMessage once p2p is upgraded to decode messages during validation. -// TODO: get rid of logger, add context -func (v *Validator) HandleMessage(logger *zap.Logger, msg *genesisqueue.GenesisSSVMessage) { - v.mtx.RLock() // read v.Queues - defer v.mtx.RUnlock() - - // logger.Debug("📬 handling SSV message", - // zap.Uint64("type", uint64(msg.MsgType)), - // fields.Role(msg.MsgID.GetRoleType())) - - if q, ok := v.Queues[msg.MsgID.GetRoleType()]; ok { - if pushed := q.Q.TryPush(msg); !pushed { - msgID := msg.MsgID.String() - logger.Warn("❗ dropping message because the queue is full", - zap.String("msg_type", message.MsgTypeToString(msg.MsgType)), - zap.String("msg_id", msgID)) - } - //logger.Debug("📬 queue: pushed message", fields.MessageID(spectypes.MessageID(msg.MsgID)), fields.MessageType(spectypes.MsgType(msg.MsgType))) - } else { - logger.Error("❌ missing queue for role type", fields.BeaconRole(spectypes.BeaconRole(msg.MsgID.GetRoleType()))) - } -} - -// StartQueueConsumer start ConsumeQueue with handler -func (v *Validator) StartQueueConsumer(logger *zap.Logger, msgID genesisspectypes.MessageID, handler MessageHandler) { - ctx, cancel := context.WithCancel(v.ctx) - defer cancel() - - for ctx.Err() == nil { - err := v.ConsumeQueue(logger, msgID, handler) - if err != nil { - logger.Debug("❗ failed consuming queue", zap.Error(err)) - } - } -} - -// ConsumeQueue consumes messages from the queue.Queue of the controller -// it checks for current state -func (v *Validator) ConsumeQueue(logger *zap.Logger, msgID genesisspectypes.MessageID, handler MessageHandler) error { - ctx, cancel := context.WithCancel(v.ctx) - defer cancel() - - var q queueContainer - err := func() error { - v.mtx.RLock() // read v.Queues - defer v.mtx.RUnlock() - var ok bool - q, ok = v.Queues[msgID.GetRoleType()] - if !ok { - return errors.New(fmt.Sprintf("queue not found for role %s", msgID.GetRoleType().String())) - } - return nil - }() - if err != nil { - return err - } - - logger.Debug("📬 queue consumer is running") - - lens := make([]int, 0, 10) - - for ctx.Err() == nil { - // Construct a representation of the current state. - state := *q.queueState - runner := v.DutyRunners.DutyRunnerForMsgID(msgID) - if runner == nil { - return fmt.Errorf("could not get duty runner for msg ID %v", msgID) - } - var runningInstance *instance.Instance - if runner.HasRunningDuty() { - runningInstance = runner.GetBaseRunner().State.RunningInstance - if runningInstance != nil { - decided, _ := runningInstance.IsDecided() - state.HasRunningInstance = !decided - } - } - state.Height = v.GetLastHeight(msgID) - state.Round = v.GetLastRound(msgID) - state.Quorum = v.Share.Quorum - - filter := genesisqueue.FilterAny - if !runner.HasRunningDuty() { - // If no duty is running, pop only ExecuteDuty messages. - filter = func(m *genesisqueue.GenesisSSVMessage) bool { - e, ok := m.Body.(*types.EventMsg) - if !ok { - return false - } - return e.Type == types.ExecuteDuty - } - } else if runningInstance != nil && runningInstance.State.ProposalAcceptedForCurrentRound == nil { - // If no proposal was accepted for the current round, skip prepare & commit messages - // for the current height and round. - filter = func(m *genesisqueue.GenesisSSVMessage) bool { - sm, ok := m.Body.(*genesisspecqbft.SignedMessage) - if !ok { - return true - } - if sm.Message.Height != state.Height || sm.Message.Round != state.Round { - return true - } - return sm.Message.MsgType != genesisspecqbft.PrepareMsgType && sm.Message.MsgType != genesisspecqbft.CommitMsgType - } - } - - // Pop the highest priority message for the current state. - msg := q.Q.Pop(ctx, genesisqueue.NewMessagePrioritizer(&state), filter) - if ctx.Err() != nil { - break - } - if msg == nil { - logger.Error("❗ got nil message from queue, but context is not done!") - break - } - lens = append(lens, q.Q.Len()) - if len(lens) >= 10 { - logger.Debug("📬 [TEMPORARY] queue statistics", - fields.MessageID(spectypes.MessageID(msg.MsgID)), fields.MessageType(spectypes.MsgType(msg.MsgType)), - zap.Ints("past_10_lengths", lens)) - lens = lens[:0] - } - - // Handle the message. - if err := handler(logger, msg); err != nil { - v.logMsg(logger, msg, "❗ could not handle message", - fields.MessageType(spectypes.MsgType(msg.SSVMessage.MsgType)), - zap.Error(err)) - } - } - - logger.Debug("📪 queue consumer is closed") - return nil -} - -func (v *Validator) logMsg(logger *zap.Logger, msg *genesisqueue.GenesisSSVMessage, logMsg string, withFields ...zap.Field) { - baseFields := []zap.Field{} - switch msg.SSVMessage.MsgType { - case genesisspectypes.SSVConsensusMsgType: - sm := msg.Body.(*genesisspecqbft.SignedMessage) - baseFields = []zap.Field{ - zap.Uint64("msg_height", uint64(sm.Message.Height)), - zap.Uint64("msg_round", uint64(sm.Message.Round)), - zap.Uint64("consensus_msg_type", uint64(sm.Message.MsgType)), - zap.Any("signers", sm.Signers), - } - case genesisspectypes.SSVPartialSignatureMsgType: - psm := msg.Body.(*genesisspectypes.SignedPartialSignatureMessage) - baseFields = []zap.Field{ - zap.Uint64("signer", psm.Signer), - fields.Slot(psm.Message.Slot), - } - } - logger.Debug(logMsg, append(baseFields, withFields...)...) -} - -// GetLastHeight returns the last height for the given identifier -func (v *Validator) GetLastHeight(identifier genesisspectypes.MessageID) genesisspecqbft.Height { - r := v.DutyRunners.DutyRunnerForMsgID(identifier) - if r == nil { - return genesisspecqbft.Height(0) - } - if ctrl := r.GetBaseRunner().QBFTController; ctrl != nil { - return ctrl.Height - } - return genesisspecqbft.Height(0) -} - -// GetLastRound returns the last height for the given identifier -func (v *Validator) GetLastRound(identifier genesisspectypes.MessageID) genesisspecqbft.Round { - r := v.DutyRunners.DutyRunnerForMsgID(identifier) - if r == nil { - return genesisspecqbft.Round(1) - } - if r != nil && r.HasRunningDuty() { - inst := r.GetBaseRunner().State.RunningInstance - if inst != nil { - return inst.State.Round - } - } - return genesisspecqbft.Round(1) -} diff --git a/protocol/genesis/ssv/validator/msgqueue_consumer_test.go b/protocol/genesis/ssv/validator/msgqueue_consumer_test.go deleted file mode 100644 index b0a36721de..0000000000 --- a/protocol/genesis/ssv/validator/msgqueue_consumer_test.go +++ /dev/null @@ -1,39 +0,0 @@ -package validator - -// -//import ( -// protocoltesting "github.com/ssvlabs/ssv/protocol/genesis/testing" -// "testing" -// -// "github.com/ssvlabs/ssv-spec-pre-cc/qbft" -// genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" -// "github.com/ssvlabs/ssv-spec-pre-cc/types/testingutils" -// "github.com/stretchr/testify/require" -//) -// -//func TestMessageConsumer(t *testing.T) { -// validator := testingutils.BaseValidator(testingutils.Testing4SharesSet()) -// validator.DutyRunners[genesisspectypes.BNRoleAttester] = testingutils.AttesterRunner(testingutils.Testing4SharesSet()) -// -// mid := genesisspectypes.NewMsgID(testingutils.TestingValidatorPubKey[:], genesisspectypes.BNRoleAttester) -// consensusDataBytes, err := testingutils.TestAttesterConsensusData.Encode() -// require.NoError(t, err) -// commitData := qbft.CommitData{Data: consensusDataBytes} -// commitDataBytes, err := commitData.Encode() -// signedMsg := protocoltesting.SignMsg(t, testingutils.Testing4SharesSet().Shares, []genesisspectypes.OperatorID{1, 2, 3, 4}, &qbft.Message{ -// MsgType: qbft.CommitMsgType, -// Height: 100, -// Round: 2, -// Identifier: mid[:], -// Data: commitDataBytes, -// }) -// msgData, err := signedMsg.Encode() -// require.NoError(t, err) -// -// err = validator.ProcessMessage(&genesisspectypes.SSVMessage{ -// MsgType: genesisspectypes.SSVConsensusMsgType, -// MsgID: mid, -// Data: msgData, -// }) -// require.NoError(t, err) -//} diff --git a/protocol/genesis/ssv/validator/opts.go b/protocol/genesis/ssv/validator/opts.go deleted file mode 100644 index 4e7ab5b9eb..0000000000 --- a/protocol/genesis/ssv/validator/opts.go +++ /dev/null @@ -1,53 +0,0 @@ -package validator - -import ( - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - - genesisibftstorage "github.com/ssvlabs/ssv/ibft/genesisstorage" - "github.com/ssvlabs/ssv/message/validation" - qbftctrl "github.com/ssvlabs/ssv/protocol/genesis/qbft/controller" - "github.com/ssvlabs/ssv/protocol/genesis/ssv/runner" - "github.com/ssvlabs/ssv/protocol/genesis/types" - "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon" -) - -const ( - DefaultQueueSize = 32 -) - -// Options represents options that should be passed to a new instance of Validator. -type Options struct { - Network genesisspecqbft.Network - BeaconNetwork beacon.BeaconNetwork - Storage *genesisibftstorage.QBFTStores - SSVShare *types.SSVShare - Signer genesisspectypes.KeyManager - DutyRunners runner.DutyRunners - NewDecidedHandler qbftctrl.NewDecidedHandler - FullNode bool - Exporter bool - QueueSize int - GasLimit uint64 - MessageValidator validation.MessageValidator - Metrics Metrics -} - -func (o *Options) defaults() { - if o.QueueSize == 0 { - o.QueueSize = DefaultQueueSize - } - if o.GasLimit == 0 { - o.GasLimit = genesisspectypes.DefaultGasLimit - } -} - -// State of the validator -type State uint32 - -const ( - // NotStarted the validator hasn't started - NotStarted State = iota - // Started validator is running - Started -) diff --git a/protocol/genesis/ssv/validator/startup.go b/protocol/genesis/ssv/validator/startup.go deleted file mode 100644 index 2f465e755d..0000000000 --- a/protocol/genesis/ssv/validator/startup.go +++ /dev/null @@ -1,74 +0,0 @@ -package validator - -import ( - "sync/atomic" - - "github.com/pkg/errors" - "github.com/ssvlabs/ssv-spec-pre-cc/p2p" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - spectypes "github.com/ssvlabs/ssv-spec/types" - "github.com/ssvlabs/ssv/logging" - genesistypes "github.com/ssvlabs/ssv/protocol/genesis/types" - - "go.uber.org/zap" - - "github.com/ssvlabs/ssv/logging/fields" -) - -// Start starts a Validator. -func (v *Validator) Start(logger *zap.Logger) (started bool, err error) { - pubKey := v.Share.ValidatorPubKey - logger = logger.Named(logging.NameValidator).With(fields.PubKey(pubKey)) - - if !atomic.CompareAndSwapUint32(&v.state, uint32(NotStarted), uint32(Started)) { - return false, nil - } - - n, ok := v.Network.(p2p.Subscriber) - if !ok { - return false, errors.New("network does not support subscription") - } - for role, dutyRunner := range v.DutyRunners { - logger := logger.With(fields.BeaconRole(spectypes.BeaconRole(role))) - share := dutyRunner.GetBaseRunner().Share - if share == nil { // TODO: handle missing share? - logger.Warn("❗ share is missing", fields.BeaconRole(spectypes.BeaconRole(role))) - continue - } - identifier := genesisspectypes.NewMsgID(genesistypes.GetDefaultDomain(), pubKey, role) - if ctrl := dutyRunner.GetBaseRunner().QBFTController; ctrl != nil { - highestInstance, err := ctrl.LoadHighestInstance(identifier[:]) - if err != nil { - logger.Warn("❗failed to load highest instance", - fields.PubKey(identifier.GetPubKey()), - zap.Error(err)) - } else if highestInstance != nil { - decidedValue := &genesisspectypes.ConsensusData{} - if err := decidedValue.Decode(highestInstance.State.DecidedValue); err != nil { - logger.Warn("❗failed to decode decided value", zap.Error(err)) - } else { - dutyRunner.GetBaseRunner().SetHighestDecidedSlot(decidedValue.Duty.Slot) - } - } - } - - if err := n.Subscribe(pubKey); err != nil { - return true, err - } - go v.StartQueueConsumer(logger, identifier, v.ProcessMessage) - } - return true, nil -} - -// Stop stops a Validator. -func (v *Validator) Stop() { - if atomic.CompareAndSwapUint32(&v.state, uint32(Started), uint32(NotStarted)) { - v.cancel() - - v.mtx.Lock() // write-lock for v.Queues - defer v.mtx.Unlock() - - // clear the msg q - v.Queues = make(map[genesisspectypes.BeaconRole]queueContainer) - } -} diff --git a/protocol/genesis/ssv/validator/timer.go b/protocol/genesis/ssv/validator/timer.go deleted file mode 100644 index 06487cbdaf..0000000000 --- a/protocol/genesis/ssv/validator/timer.go +++ /dev/null @@ -1,77 +0,0 @@ -package validator - -import ( - "encoding/json" - - "github.com/pkg/errors" - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - spectypes "github.com/ssvlabs/ssv-spec/types" - "go.uber.org/zap" - - "github.com/ssvlabs/ssv/logging/fields" - "github.com/ssvlabs/ssv/protocol/genesis/message" - "github.com/ssvlabs/ssv/protocol/genesis/qbft/roundtimer" - genesisqueue "github.com/ssvlabs/ssv/protocol/genesis/ssv/genesisqueue" - "github.com/ssvlabs/ssv/protocol/genesis/types" -) - -func (v *Validator) onTimeout(logger *zap.Logger, identifier genesisspectypes.MessageID, height genesisspecqbft.Height) roundtimer.OnRoundTimeoutF { - return func(round genesisspecqbft.Round) { - v.mtx.RLock() // read-lock for v.Queues, v.state - defer v.mtx.RUnlock() - - // only run if the validator is started - if v.state != uint32(Started) { - return - } - - dr := v.DutyRunners[identifier.GetRoleType()] - hasDuty := dr.HasRunningDuty() - if !hasDuty { - return - } - - msg, err := v.createTimerMessage(identifier, height, round) - if err != nil { - logger.Debug("❗ failed to create timer msg", zap.Error(err)) - return - } - dec, err := genesisqueue.DecodeGenesisSSVMessage(msg) - if err != nil { - logger.Debug("❌ failed to decode timer msg", zap.Error(err)) - return - } - - if pushed := v.Queues[identifier.GetRoleType()].Q.TryPush(dec); !pushed { - logger.Warn("❗️ dropping timeout message because the queue is full", - fields.BeaconRole(spectypes.BeaconRole(identifier.GetRoleType()))) - } - // logger.Debug("📬 queue: pushed message", fields.PubKey(identifier.GetPubKey()), fields.MessageID(dec.MsgID), fields.MessageType(dec.MsgType)) - } -} - -func (v *Validator) createTimerMessage(identifier genesisspectypes.MessageID, height genesisspecqbft.Height, round genesisspecqbft.Round) (*genesisspectypes.SSVMessage, error) { - td := types.TimeoutData{ - Height: height, - Round: round, - } - data, err := json.Marshal(td) - if err != nil { - return nil, errors.Wrap(err, "failed to marshal timeout data") - } - eventMsg := &types.EventMsg{ - Type: types.Timeout, - Data: data, - } - - eventMsgData, err := eventMsg.Encode() - if err != nil { - return nil, errors.Wrap(err, "failed to encode timeout signed msg") - } - return &genesisspectypes.SSVMessage{ - MsgType: message.SSVEventMsgType, - MsgID: identifier, - Data: eventMsgData, - }, nil -} diff --git a/protocol/genesis/ssv/validator/validator.go b/protocol/genesis/ssv/validator/validator.go deleted file mode 100644 index 650ddcffb4..0000000000 --- a/protocol/genesis/ssv/validator/validator.go +++ /dev/null @@ -1,176 +0,0 @@ -package validator - -import ( - "context" - "fmt" - "sync" - - "github.com/pkg/errors" - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - specqbft "github.com/ssvlabs/ssv-spec/qbft" - "go.uber.org/zap" - - "github.com/ssvlabs/ssv/utils/hashmap" - - "github.com/ssvlabs/ssv/ibft/genesisstorage" - "github.com/ssvlabs/ssv/logging/fields" - "github.com/ssvlabs/ssv/message/validation" - "github.com/ssvlabs/ssv/protocol/genesis/message" - "github.com/ssvlabs/ssv/protocol/genesis/ssv/genesisqueue" - "github.com/ssvlabs/ssv/protocol/genesis/ssv/runner" - "github.com/ssvlabs/ssv/protocol/genesis/types" -) - -// Validator represents an SSV ETH consensus validator Share assigned, coordinates duty execution and more. -// Every validator has a validatorID which is validator's public key. -// Each validator has multiple DutyRunners, for each duty type. -type Validator struct { - mtx *sync.RWMutex - ctx context.Context - cancel context.CancelFunc - - DutyRunners runner.DutyRunners - Network genesisspecqbft.Network - Share *types.SSVShare - Signer genesisspectypes.KeyManager - - Storage *genesisstorage.QBFTStores - Queues map[genesisspectypes.BeaconRole]queueContainer - - // dutyIDs is a map for logging a unique ID for a given duty - dutyIDs *hashmap.Map[genesisspectypes.BeaconRole, string] - - state uint32 - - messageValidator validation.MessageValidator -} - -// NewValidator creates a new instance of Validator. -func NewValidator(pctx context.Context, cancel func(), options Options) *Validator { - options.defaults() - - if options.Metrics == nil { - options.Metrics = &NopMetrics{} - } - - v := &Validator{ - mtx: &sync.RWMutex{}, - ctx: pctx, - cancel: cancel, - DutyRunners: options.DutyRunners, - Network: options.Network, - Storage: options.Storage, - Share: options.SSVShare, - Signer: options.Signer, - Queues: make(map[genesisspectypes.BeaconRole]queueContainer), - state: uint32(NotStarted), - dutyIDs: hashmap.New[genesisspectypes.BeaconRole, string](), - messageValidator: options.MessageValidator, - } - - for _, dutyRunner := range options.DutyRunners { - // Set timeout function. - dutyRunner.GetBaseRunner().TimeoutF = v.onTimeout - - // Setup the queue. - role := dutyRunner.GetBaseRunner().BeaconRoleType - - v.Queues[role] = queueContainer{ - Q: genesisqueue.WithMetrics(genesisqueue.New(options.QueueSize), options.Metrics), - queueState: &genesisqueue.State{ - HasRunningInstance: false, - Height: 0, - Slot: 0, - //Quorum: options.SSVShare.Share,// TODO - }, - } - } - - return v -} - -// StartDuty starts a duty for the validator -func (v *Validator) StartDuty(logger *zap.Logger, duty *genesisspectypes.Duty) error { - dutyRunner := v.DutyRunners[duty.Type] - if dutyRunner == nil { - return errors.Errorf("no runner for duty type %s", duty.Type.String()) - } - - // Log with duty ID. - baseRunner := dutyRunner.GetBaseRunner() - v.dutyIDs.Set(duty.Type, fields.FormatDutyID(baseRunner.BeaconNetwork.EstimatedEpochAtSlot(duty.Slot), duty.Slot, duty.Type.String(), duty.ValidatorIndex)) - logger = trySetDutyID(logger, v.dutyIDs, duty.Type) - - // Log with height. - if baseRunner.QBFTController != nil { - logger = logger.With(fields.Height(specqbft.Height(baseRunner.QBFTController.Height))) - } - - if duty.Type == genesisspectypes.BNRoleValidatorRegistration { - logger = logger.With(fields.FeeRecipient(v.Share.FeeRecipientAddress[:])) - } - - logger.Info("ℹ️ starting duty processing") - - return dutyRunner.StartNewDuty(logger, duty) -} - -// ProcessMessage processes Network Message of all types -func (v *Validator) ProcessMessage(logger *zap.Logger, msg *genesisqueue.GenesisSSVMessage) error { - messageID := msg.GetID() - dutyRunner := v.DutyRunners.DutyRunnerForMsgID(messageID) - if dutyRunner == nil { - return fmt.Errorf("could not get duty runner for msg ID %v", messageID) - } - - if err := validateMessage(v.Share.Share, msg); err != nil { - return fmt.Errorf("message invalid for msg ID %v: %w", messageID, err) - } - - switch msg.GetType() { - case genesisspectypes.SSVConsensusMsgType: - logger = trySetDutyID(logger, v.dutyIDs, messageID.GetRoleType()) - - signedMsg, ok := msg.Body.(*genesisspecqbft.SignedMessage) - if !ok { - return errors.New("could not decode consensus message from network message") - } - logger = logger.With(fields.Height(specqbft.Height(signedMsg.Message.Height))) - return dutyRunner.ProcessConsensus(logger, signedMsg) - case genesisspectypes.SSVPartialSignatureMsgType: - logger = trySetDutyID(logger, v.dutyIDs, messageID.GetRoleType()) - - signedMsg, ok := msg.Body.(*genesisspectypes.SignedPartialSignatureMessage) - if !ok { - return errors.New("could not decode post consensus message from network message") - } - if signedMsg.Message.Type == genesisspectypes.PostConsensusPartialSig { - return dutyRunner.ProcessPostConsensus(logger, signedMsg) - } - return dutyRunner.ProcessPreConsensus(logger, signedMsg) - case message.SSVEventMsgType: - return v.handleEventMessage(logger, msg, dutyRunner) - default: - return errors.New("unknown msg") - } -} - -func validateMessage(share genesisspectypes.Share, msg *genesisqueue.GenesisSSVMessage) error { - if !share.ValidatorPubKey.MessageIDBelongs(msg.GetID()) { - return errors.New("msg ID doesn't match validator ID") - } - - if len(msg.GetData()) == 0 { - return errors.New("msg data is invalid") - } - - return nil -} - -func trySetDutyID(logger *zap.Logger, dutyIDs *hashmap.Map[genesisspectypes.BeaconRole, string], role genesisspectypes.BeaconRole) *zap.Logger { - if dutyID, ok := dutyIDs.Get(role); ok { - return logger.With(fields.DutyID(dutyID)) - } - return logger -} diff --git a/protocol/genesis/testing/test_utils.go b/protocol/genesis/testing/test_utils.go deleted file mode 100644 index 764feb7fbf..0000000000 --- a/protocol/genesis/testing/test_utils.go +++ /dev/null @@ -1,254 +0,0 @@ -package testing - -import ( - "fmt" - "os" - "path" - "path/filepath" - "strings" - "testing" - - "github.com/herumi/bls-eth-go-binary/bls" - "github.com/pkg/errors" - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - "github.com/stretchr/testify/require" - "golang.org/x/mod/modfile" - "golang.org/x/mod/module" - - qbftstorage "github.com/ssvlabs/ssv/protocol/genesis/qbft/storage" - "github.com/ssvlabs/ssv/protocol/genesis/types" -) - -var ( - specModule = "github.com/ssvlabs/ssv-spec-pre-cc" - specTestPath = "spectest/generate/tests.json" -) - -// TODO: add missing tests - -// GenerateBLSKeys generates randomly nodes -func GenerateBLSKeys(oids ...genesisspectypes.OperatorID) (map[genesisspectypes.OperatorID]*bls.SecretKey, []*genesisspectypes.Operator) { - _ = bls.Init(bls.BLS12_381) - - nodes := make([]*genesisspectypes.Operator, 0) - sks := make(map[genesisspectypes.OperatorID]*bls.SecretKey) - - for _, oid := range oids { - sk := &bls.SecretKey{} - sk.SetByCSPRNG() - - nodes = append(nodes, &genesisspectypes.Operator{ - OperatorID: oid, - PubKey: sk.GetPublicKey().Serialize(), - }) - sks[oid] = sk - } - - return sks, nodes -} - -// MsgGenerator represents a message generator -type MsgGenerator func(height genesisspecqbft.Height) ([]genesisspectypes.OperatorID, *genesisspecqbft.Message) - -// CreateMultipleStoredInstances enables to create multiple stored instances (with decided messages). -func CreateMultipleStoredInstances( - sks map[genesisspectypes.OperatorID]*bls.SecretKey, - start genesisspecqbft.Height, - end genesisspecqbft.Height, - generator MsgGenerator, -) ([]*qbftstorage.StoredInstance, error) { - results := make([]*qbftstorage.StoredInstance, 0) - for i := start; i <= end; i++ { - signers, msg := generator(i) - if msg == nil { - break - } - sm, err := MultiSignMsg(sks, signers, msg) - if err != nil { - return nil, err - } - results = append(results, &qbftstorage.StoredInstance{ - State: &genesisspecqbft.State{ - ID: sm.Message.Identifier, - Round: sm.Message.Round, - Height: sm.Message.Height, - LastPreparedRound: sm.Message.Round, - LastPreparedValue: sm.FullData, - Decided: true, - DecidedValue: sm.FullData, - ProposeContainer: genesisspecqbft.NewMsgContainer(), - PrepareContainer: genesisspecqbft.NewMsgContainer(), - CommitContainer: genesisspecqbft.NewMsgContainer(), - RoundChangeContainer: genesisspecqbft.NewMsgContainer(), - }, - DecidedMessage: sm, - }) - } - return results, nil -} - -func signMessage(msg *genesisspecqbft.Message, sk *bls.SecretKey) (*bls.Sign, error) { - signatureDomain := genesisspectypes.ComputeSignatureDomain(types.GetDefaultDomain(), genesisspectypes.QBFTSignatureType) - root, err := genesisspectypes.ComputeSigningRoot(msg, signatureDomain) - if err != nil { - return nil, err - } - return sk.SignByte(root[:]), nil -} - -// MultiSignMsg signs a msg with multiple signers -func MultiSignMsg(sks map[genesisspectypes.OperatorID]*bls.SecretKey, signers []genesisspectypes.OperatorID, msg *genesisspecqbft.Message) (*genesisspecqbft.SignedMessage, error) { - _ = bls.Init(bls.BLS12_381) - - var operators = make([]genesisspectypes.OperatorID, 0) - var agg *bls.Sign - for _, oid := range signers { - signature, err := signMessage(msg, sks[oid]) - if err != nil { - return nil, err - } - operators = append(operators, oid) - if agg == nil { - agg = signature - } else { - agg.Add(signature) - } - } - - return &genesisspecqbft.SignedMessage{ - Message: *msg, - Signature: agg.Serialize(), - Signers: operators, - }, nil -} - -// SignMsg handle MultiSignMsg error and return just genesisspecqbft.SignedMessage -func SignMsg(t *testing.T, sks map[genesisspectypes.OperatorID]*bls.SecretKey, signers []genesisspectypes.OperatorID, msg *genesisspecqbft.Message) *genesisspecqbft.SignedMessage { - res, err := MultiSignMsg(sks, signers, msg) - require.NoError(t, err) - return res -} - -// AggregateSign sign genesisspecqbft.Message and then aggregate -func AggregateSign(t *testing.T, sks map[genesisspectypes.OperatorID]*bls.SecretKey, signers []genesisspectypes.OperatorID, consensusMessage *genesisspecqbft.Message) *genesisspecqbft.SignedMessage { - signedMsg := SignMsg(t, sks, signers, consensusMessage) - // TODO: use SignMsg instead of AggregateSign - // require.NoError(t, sigSignMsgnedMsg.Aggregate(signedMsg)) - return signedMsg -} - -// AggregateInvalidSign sign genesisspecqbft.Message and then change the signer id to mock invalid sig -func AggregateInvalidSign(t *testing.T, sks map[genesisspectypes.OperatorID]*bls.SecretKey, consensusMessage *genesisspecqbft.Message) *genesisspecqbft.SignedMessage { - sigend := SignMsg(t, sks, []genesisspectypes.OperatorID{1}, consensusMessage) - sigend.Signers = []genesisspectypes.OperatorID{2} - return sigend -} - -func GetSpecTestJSON(path string, module string) ([]byte, error) { - p, err := GetSpecDir(path, module) - if err != nil { - return nil, fmt.Errorf("could not get spec test dir: %w", err) - } - return os.ReadFile(filepath.Join(filepath.Clean(p), filepath.Clean(specTestPath))) -} - -// GetSpecDir returns the path to the ssv-spec-pre-cc module. -func GetSpecDir(path, module string) (string, error) { - if path == "" { - var err error - path, err = os.Getwd() - if err != nil { - return "", errors.New("could not get current directory") - } - } - goModFile, err := getGoModFile(path) - if err != nil { - return "", errors.New("could not get go.mod file") - } - - // check if there is a replace - var modPath, modVersion string - var replace *modfile.Replace - for _, r := range goModFile.Replace { - if strings.EqualFold(specModule, r.Old.Path) { - replace = r - break - } - } - - if replace != nil { - modPath = replace.New.Path - modVersion = replace.New.Version - } else { - // get from require - var req *modfile.Require - for _, r := range goModFile.Require { - if strings.EqualFold(specModule, r.Mod.Path) { - req = r - break - } - } - if req == nil { - return "", errors.Errorf("could not find %s module", specModule) - } - modPath = req.Mod.Path - modVersion = req.Mod.Version - } - - // get module path - p, err := GetModulePath(modPath, modVersion) - if err != nil { - return "", errors.Wrap(err, "could not get module path") - } - - if _, err := os.Stat(p); os.IsNotExist(err) { - return "", errors.Wrapf(err, "you don't have this module-%s/version-%s installed", modPath, modVersion) - } - - return filepath.Join(filepath.Clean(p), module), nil -} - -func GetModulePath(name, version string) (string, error) { - // first we need GOMODCACHE - cache, ok := os.LookupEnv("GOMODCACHE") - if !ok { - cache = path.Join(os.Getenv("GOPATH"), "pkg", "mod") - } - - // then we need to escape path - escapedPath, err := module.EscapePath(name) - if err != nil { - return "", err - } - - // version also - escapedVersion, err := module.EscapeVersion(version) - if err != nil { - return "", err - } - - return path.Join(cache, escapedPath+"@"+escapedVersion), nil -} - -func getGoModFile(path string) (*modfile.File, error) { - // find project root path - for { - if _, err := os.Stat(filepath.Join(path, "go.mod")); err == nil { - break - } - path = filepath.Dir(path) - if path == "/" { - return nil, errors.New("could not find go.mod file") - } - } - - // read go.mod - buf, err := os.ReadFile(filepath.Join(filepath.Clean(path), "go.mod")) - if err != nil { - return nil, errors.New("could not read go.mod") - } - - // parse go.mod - return modfile.Parse("go.mod", buf, nil) -} diff --git a/protocol/genesis/types/bls.go b/protocol/genesis/types/bls.go deleted file mode 100644 index d4e2b39fb9..0000000000 --- a/protocol/genesis/types/bls.go +++ /dev/null @@ -1,33 +0,0 @@ -package types - -import ( - lru "github.com/hashicorp/golang-lru/v2" - "github.com/herumi/bls-eth-go-binary/bls" -) - -var blsPublicKeyCache *lru.Cache[string, bls.PublicKey] - -func init() { - var err error - blsPublicKeyCache, err = lru.New[string, bls.PublicKey](128_000) - if err != nil { - panic(err) - } -} - -// DeserializeBLSPublicKey deserializes a bls.PublicKey from bytes, -// caching the result to avoid repeated deserialization. -func DeserializeBLSPublicKey(b []byte) (bls.PublicKey, error) { - pkStr := string(b) - if pk, ok := blsPublicKeyCache.Get(pkStr); ok { - return pk, nil - } - - pk := bls.PublicKey{} - if err := pk.Deserialize(b); err != nil { - return bls.PublicKey{}, err - } - blsPublicKeyCache.Add(pkStr, pk) - - return pk, nil -} diff --git a/protocol/genesis/types/crypto.go b/protocol/genesis/types/crypto.go deleted file mode 100644 index 6976b9cd49..0000000000 --- a/protocol/genesis/types/crypto.go +++ /dev/null @@ -1,100 +0,0 @@ -package types - -import ( - "encoding/hex" - - "github.com/herumi/bls-eth-go-binary/bls" - "github.com/pkg/errors" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" - genesisspecssv "github.com/ssvlabs/ssv-spec-pre-cc/ssv" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - "go.uber.org/zap" -) - -var ( - MetricsSignaturesVerifications = promauto.NewCounterVec(prometheus.CounterOpts{ - Name: "genesis::ssv_signature_verifications", - Help: "Number of signatures verifications", - }, []string{}) -) - -func init() { - logger := zap.L() - if err := prometheus.Register(MetricsSignaturesVerifications); err != nil { - logger.Debug("could not register prometheus collector") - } -} - -// VerifyByOperators verifies signature by the provided operators -// This is a copy of a function with the same name from the spec, except for it's use of -// DeserializeBLSPublicKey function and bounded.CGO -// -// TODO: rethink this function and consider moving/refactoring it. -func VerifyByOperators(s genesisspectypes.Signature, data genesisspectypes.MessageSignature, domain genesisspectypes.DomainType, sigType genesisspectypes.SignatureType, operators []*genesisspectypes.Operator) error { - MetricsSignaturesVerifications.WithLabelValues().Inc() - - sign := &bls.Sign{} - if err := sign.Deserialize(s); err != nil { - return errors.Wrap(err, "failed to deserialize signature") - } - - pks := make([]bls.PublicKey, 0) - for _, id := range data.GetSigners() { - found := false - for _, n := range operators { - if id == n.GetID() { - pk, err := DeserializeBLSPublicKey(n.GetPublicKey()) - if err != nil { - return errors.Wrap(err, "failed to deserialize public key") - } - - pks = append(pks, pk) - found = true - } - } - if !found { - return errors.New("unknown signer") - } - } - - computedRoot, err := genesisspectypes.ComputeSigningRoot(data, genesisspectypes.ComputeSignatureDomain(domain, sigType)) - if err != nil { - return errors.Wrap(err, "could not compute signing root") - } - - if res := sign.FastAggregateVerify(pks, computedRoot[:]); !res { - return errors.New("failed to verify signature") - } - return nil -} - -func ReconstructSignature(ps *genesisspecssv.PartialSigContainer, root [32]byte, validatorPubKey []byte) ([]byte, error) { - // Reconstruct signatures - signature, err := genesisspectypes.ReconstructSignatures(ps.Signatures[rootHex(root)]) - if err != nil { - return nil, errors.Wrap(err, "failed to reconstruct signatures") - } - if err := VerifyReconstructedSignature(signature, validatorPubKey, root); err != nil { - return nil, errors.Wrap(err, "failed to verify reconstruct signature") - } - return signature.Serialize(), nil -} - -func VerifyReconstructedSignature(sig *bls.Sign, validatorPubKey []byte, root [32]byte) error { - MetricsSignaturesVerifications.WithLabelValues().Inc() - - pk, err := DeserializeBLSPublicKey(validatorPubKey) - if err != nil { - return errors.Wrap(err, "could not deserialize validator pk") - } - - if res := sig.VerifyByte(&pk, root[:]); !res { - return errors.New("could not reconstruct a valid signature") - } - return nil -} - -func rootHex(r [32]byte) string { - return hex.EncodeToString(r[:]) -} diff --git a/protocol/genesis/types/domain.go b/protocol/genesis/types/domain.go deleted file mode 100644 index 599664de8b..0000000000 --- a/protocol/genesis/types/domain.go +++ /dev/null @@ -1,25 +0,0 @@ -package types - -import ( - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - - "github.com/ssvlabs/ssv/networkconfig" -) - -// TODO: get rid of singleton, pass domain as a parameter -var ( - domain = genesisspectypes.DomainType(networkconfig.Mainnet.DomainType()) -) - -// GetDefaultDomain returns the global domain used across the system -// DEPRECATED: use networkconfig.NetworkConfig.Domain instead -func GetDefaultDomain() genesisspectypes.DomainType { - return domain -} - -// SetDefaultDomain updates the global domain used across the system -// allows injecting domain for testnets -// DEPRECATED: use networkconfig.NetworkConfig.Domain instead -func SetDefaultDomain(d genesisspectypes.DomainType) { - domain = d -} diff --git a/protocol/genesis/types/messages.go b/protocol/genesis/types/messages.go deleted file mode 100644 index 2f363613a5..0000000000 --- a/protocol/genesis/types/messages.go +++ /dev/null @@ -1,68 +0,0 @@ -package types - -import ( - "encoding/json" - - "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - "github.com/ssvlabs/ssv-spec-pre-cc/types" -) - -type EventType int - -const ( - // Timeout in order to run timeoutData process - Timeout EventType = iota - // ExecuteDuty for when to start duty runner - ExecuteDuty -) - -func (e EventType) String() string { - switch e { - case Timeout: - return "timeoutData" - case ExecuteDuty: - return "executeDuty" - default: - return "unknown" - } -} - -type EventMsg struct { - Type EventType - Data []byte -} - -type TimeoutData struct { - Height qbft.Height - Round qbft.Round -} - -type ExecuteDutyData struct { - Duty *types.Duty -} - -func (m *EventMsg) GetTimeoutData() (*TimeoutData, error) { - td := &TimeoutData{} - if err := json.Unmarshal(m.Data, td); err != nil { - return nil, err - } - return td, nil -} - -func (m *EventMsg) GetExecuteDutyData() (*ExecuteDutyData, error) { - ed := &ExecuteDutyData{} - if err := json.Unmarshal(m.Data, ed); err != nil { - return nil, err - } - return ed, nil -} - -// Encode returns a msg encoded bytes or error -func (m *EventMsg) Encode() ([]byte, error) { - return json.Marshal(m) -} - -// Decode returns error if decoding failed -func (m *EventMsg) Decode(data []byte) error { - return json.Unmarshal(data, &m) -} diff --git a/protocol/genesis/types/share.go b/protocol/genesis/types/share.go deleted file mode 100644 index 9766490159..0000000000 --- a/protocol/genesis/types/share.go +++ /dev/null @@ -1,58 +0,0 @@ -package types - -import ( - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - spectypes "github.com/ssvlabs/ssv-spec/types" - - typesv2 "github.com/ssvlabs/ssv/protocol/v2/types" -) - -func ConvertToGenesisShare(share *spectypes.Share, operator *spectypes.CommitteeMember) *genesisspectypes.Share { - q, pc := ComputeQuorumAndPartialQuorum(uint64(len((share.Committee)))) - - key := make([]byte, len(share.ValidatorPubKey)) - copy(key, share.ValidatorPubKey[:]) - - genesisShare := &genesisspectypes.Share{ - OperatorID: operator.OperatorID, - SharePubKey: make([]byte, len(share.SharePubKey)), - ValidatorPubKey: key, - Committee: make([]*genesisspectypes.Operator, 0, len(share.Committee)), - Quorum: q, - PartialQuorum: pc, - DomainType: genesisspectypes.DomainType(share.DomainType), - FeeRecipientAddress: share.FeeRecipientAddress, - Graffiti: make([]byte, len(share.Graffiti)), - } - - copy(genesisShare.SharePubKey, share.SharePubKey) - copy(genesisShare.Graffiti, share.Graffiti) - - for _, c := range share.Committee { - newMember := &genesisspectypes.Operator{ - OperatorID: c.Signer, - PubKey: make([]byte, len(c.SharePubKey)), - } - copy(newMember.PubKey, c.SharePubKey) - genesisShare.Committee = append(genesisShare.Committee, newMember) - } - - return genesisShare -} - -// ConvertToGenesisSSVShare converts an Alan share to a genesis SSV share. -func ConvertToGenesisSSVShare(alanSSVShare *typesv2.SSVShare, operator *spectypes.CommitteeMember) *SSVShare { - genesisShare := ConvertToGenesisShare(&alanSSVShare.Share, operator) - - convertedMetadata := Metadata{ - BeaconMetadata: alanSSVShare.Metadata.BeaconMetadata, - OwnerAddress: alanSSVShare.Metadata.OwnerAddress, - Liquidated: alanSSVShare.Metadata.Liquidated, - // lastUpdated field is not converted because it's unexported - } - - return &SSVShare{ - Share: *genesisShare, - Metadata: convertedMetadata, - } -} diff --git a/protocol/genesis/types/share_test.go b/protocol/genesis/types/share_test.go deleted file mode 100644 index cdaf2a79a4..0000000000 --- a/protocol/genesis/types/share_test.go +++ /dev/null @@ -1,111 +0,0 @@ -package types - -import ( - "bytes" - "testing" - - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - spectypes "github.com/ssvlabs/ssv-spec/types" - "github.com/stretchr/testify/require" -) - -func TestConvertShare(t *testing.T) { - // Step 1: Create an Alan share with all fields populated - originalShare := &spectypes.Share{ - ValidatorPubKey: [48]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48}, - SharePubKey: []byte{49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60}, - Committee: []*spectypes.ShareMember{ - {SharePubKey: []byte{61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72}, Signer: 1}, - {SharePubKey: []byte{73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84}, Signer: 2}, - {SharePubKey: []byte{85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96}, Signer: 3}, - {SharePubKey: []byte{97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108}, Signer: 4}, - }, - DomainType: [4]byte{109, 110, 111, 112}, - FeeRecipientAddress: [20]byte{113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132}, - Graffiti: []byte{133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144}, - } - - // Step 2: Convert to Genesis share - genesisShare := ConvertToGenesisShare(originalShare, &spectypes.CommitteeMember{OperatorID: 1}) - - // Step 3: Convert share back to Alan share - convertedBackShare := convertFromGenesisShare(genesisShare) - - // Step 4: Expect equality with original share - require.True(t, sharesEqual(originalShare, convertedBackShare), "The converted back share should be equal to the original share") - - // Step 5: Modify final share and expect inequality - convertedBackShare.Graffiti[0] = 200 - require.False(t, sharesEqual(originalShare, convertedBackShare), "The modified converted back share should not be equal to the original share") - - // Step 6: Modify original share in the same way and expect equality again - originalShare.Graffiti[0] = 200 - require.True(t, sharesEqual(originalShare, convertedBackShare), "The modified original share should be equal to the modified converted back share") - - // Step 7: Modify committee member's signer in the final share and expect inequality - convertedBackShare.Committee[0].Signer = 420 - require.False(t, sharesEqual(originalShare, convertedBackShare), "The modified converted back share with changed committee signer should not be equal to the original share") - - // Step 8: Modify original share's committee member signer in the same way and expect equality again - originalShare.Committee[0].Signer = 420 - require.True(t, sharesEqual(originalShare, convertedBackShare), "The modified original share with changed committee signer should be equal to the modified converted back share") - -} - -func sharesEqual(share1, share2 *spectypes.Share) bool { - if !bytes.Equal(share1.ValidatorPubKey[:], share2.ValidatorPubKey[:]) { - return false - } - if !bytes.Equal(share1.SharePubKey, share2.SharePubKey) { - return false - } - if !bytes.Equal(share1.DomainType[:], share2.DomainType[:]) { - return false - } - if !bytes.Equal(share1.FeeRecipientAddress[:], share2.FeeRecipientAddress[:]) { - return false - } - if !bytes.Equal(share1.Graffiti, share2.Graffiti) { - return false - } - if len(share1.Committee) != len(share2.Committee) { - return false - } - for i := range share1.Committee { - if share1.Committee[i].Signer != share2.Committee[i].Signer { - return false - } - if !bytes.Equal(share1.Committee[i].SharePubKey, share2.Committee[i].SharePubKey) { - return false - } - } - return true -} - -func convertFromGenesisShare(genesisShare *genesisspectypes.Share) *spectypes.Share { - var key spectypes.ValidatorPK - copy(key[:], genesisShare.ValidatorPubKey[:]) - - share := &spectypes.Share{ - ValidatorPubKey: key, - SharePubKey: make([]byte, len(genesisShare.SharePubKey)), - Committee: make([]*spectypes.ShareMember, 0, len(genesisShare.Committee)), - DomainType: spectypes.DomainType(genesisShare.DomainType), - FeeRecipientAddress: genesisShare.FeeRecipientAddress, - Graffiti: make([]byte, len(genesisShare.Graffiti)), - } - - copy(share.SharePubKey, genesisShare.SharePubKey) - copy(share.Graffiti, genesisShare.Graffiti) - - for _, c := range genesisShare.Committee { - newMember := &spectypes.ShareMember{ - SharePubKey: make([]byte, len(c.PubKey)), - Signer: c.OperatorID, - } - copy(newMember.SharePubKey, c.PubKey) - share.Committee = append(share.Committee, newMember) - } - - return share -} diff --git a/protocol/genesis/types/signature_benchmark_linux_test.go b/protocol/genesis/types/signature_benchmark_linux_test.go deleted file mode 100644 index d3a9b295e2..0000000000 --- a/protocol/genesis/types/signature_benchmark_linux_test.go +++ /dev/null @@ -1,70 +0,0 @@ -//go:build linux - -package types - -import ( - "crypto" - "crypto/sha256" - "fmt" - "testing" - - "github.com/microsoft/go-crypto-openssl/openssl" - "github.com/microsoft/go-crypto-openssl/openssl/bbig/bridge" -) - -func init() { - if err := openssl.Init(); err != nil { - panic(err) - } -} - -func BenchmarkVerifyPKCS1v15OpenSSL(b *testing.B) { - dataOpenSSL := []byte("This is test data for OpenSSL verification.") - hashedOpenSSL := sha256.Sum256(dataOpenSSL) - - priv, pub := newOpenSSLRSAKey(2048) - - sig, err := openssl.SignRSAPKCS1v15(priv, crypto.SHA256, hashedOpenSSL[:]) - if err != nil { - b.Fatal(err) - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - err := openssl.VerifyRSAPKCS1v15(pub, crypto.SHA256, hashedOpenSSL[:], sig) - if err != nil { - b.Fatal(err) - } - } -} - -func BenchmarkSignPKCS1v15OpenSSL(b *testing.B) { - dataOpenSSL := []byte("This is test data for OpenSSL verification.") - hashedOpenSSL := sha256.Sum256(dataOpenSSL) - - priv, _ := newOpenSSLRSAKey(2048) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, err := openssl.SignRSAPKCS1v15(priv, crypto.SHA256, hashedOpenSSL[:]) - if err != nil { - b.Fatal(err) - } - } -} - -func newOpenSSLRSAKey(size int) (*openssl.PrivateKeyRSA, *openssl.PublicKeyRSA) { - N, E, D, P, Q, Dp, Dq, Qinv, err := bridge.GenerateKeyRSA(size) - if err != nil { - panic(fmt.Sprintf("GenerateKeyRSA(%d): %v", size, err)) - } - priv, err := bridge.NewPrivateKeyRSA(N, E, D, P, Q, Dp, Dq, Qinv) - if err != nil { - panic(fmt.Sprintf("NewPrivateKeyRSA(%d): %v", size, err)) - } - pub, err := bridge.NewPublicKeyRSA(N, E) - if err != nil { - panic(fmt.Sprintf("NewPublicKeyRSA(%d): %v", size, err)) - } - return priv, pub -} diff --git a/protocol/genesis/types/signature_benchmark_test.go b/protocol/genesis/types/signature_benchmark_test.go deleted file mode 100644 index 074182e10a..0000000000 --- a/protocol/genesis/types/signature_benchmark_test.go +++ /dev/null @@ -1,180 +0,0 @@ -package types - -import ( - "crypto" - "crypto/md5" - "crypto/rand" - "crypto/rsa" - "crypto/sha256" - "testing" - - "github.com/herumi/bls-eth-go-binary/bls" -) - -var ( - privateKey *rsa.PrivateKey - publicKey *rsa.PublicKey - signature []byte - data = []byte("This is some test data for verification.") - hashed = sha256.Sum256(data) -) - -var ( - privateKeyPSS *rsa.PrivateKey - publicKeyPSS *rsa.PublicKey - pssSignature []byte - dataPSS = []byte("This is some test data for PSS verification.") - hashedPSS = sha256.Sum256(dataPSS) -) - -var ( - privateKeyFast *rsa.PrivateKey - publicKeyFast *rsa.PublicKey - signatureFast []byte - dataFast = []byte("This is test data for fast verification.") - hashedFast = md5.Sum(dataFast) -) - -func init() { - var err error - privateKey, err = rsa.GenerateKey(rand.Reader, 2048) - if err != nil { - panic(err) - } - publicKey = &privateKey.PublicKey - - signature, err = rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, hashed[:]) - if err != nil { - panic(err) - } - - if err := bls.Init(bls.BLS12_381); err != nil { - panic(err) - } - - if err := bls.SetETHmode(bls.EthModeLatest); err != nil { - panic(err) - } - - privateKeyPSS, err = rsa.GenerateKey(rand.Reader, 2048) - if err != nil { - panic(err) - } - publicKeyPSS = &privateKeyPSS.PublicKey - - pssOptions := &rsa.PSSOptions{ - SaltLength: rsa.PSSSaltLengthAuto, - Hash: crypto.SHA256, - } - - pssSignature, err = rsa.SignPSS(rand.Reader, privateKeyPSS, crypto.SHA256, hashedPSS[:], pssOptions) - if err != nil { - panic(err) - } - - privateKeyFast, err = rsa.GenerateKey(rand.Reader, 2048) - if err != nil { - panic(err) - } - publicKeyFast = &privateKeyFast.PublicKey - - signatureFast, err = rsa.SignPKCS1v15(rand.Reader, privateKeyFast, crypto.MD5, hashedFast[:]) - if err != nil { - panic(err) - } -} - -func BenchmarkVerifyBLS(b *testing.B) { - secKey := new(bls.SecretKey) - secKey.SetByCSPRNG() - pubKey := secKey.GetPublicKey() - msg := []byte("This is some test data for verification.") - sig := secKey.SignByte(msg) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - if !sig.VerifyByte(pubKey, msg) { - b.Fatal("Verification failed") - } - } -} - -func BenchmarkVerifyPKCS1v15(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - err := rsa.VerifyPKCS1v15(publicKey, crypto.SHA256, hashed[:], signature) - if err != nil { - b.Fatal(err) - } - } -} - -func BenchmarkVerifyPKCS1v15FastHash(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - err := rsa.VerifyPKCS1v15(publicKeyFast, crypto.MD5, hashedFast[:], signatureFast) - if err != nil { - b.Fatal(err) - } - } -} - -func BenchmarkVerifyPSS(b *testing.B) { - pssOptions := &rsa.PSSOptions{ - SaltLength: rsa.PSSSaltLengthAuto, - Hash: crypto.SHA256, - } - b.ResetTimer() - for i := 0; i < b.N; i++ { - err := rsa.VerifyPSS(publicKeyPSS, crypto.SHA256, hashedPSS[:], pssSignature, pssOptions) - if err != nil { - b.Fatal(err) - } - } -} - -func BenchmarkSignBLS(b *testing.B) { - secKey := new(bls.SecretKey) - secKey.SetByCSPRNG() - msg := []byte("This is some test data for verification.") - - b.ResetTimer() - for i := 0; i < b.N; i++ { - _ = secKey.SignByte(msg) - } -} - -func BenchmarkSignPKCS1v15(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, hashed[:]) - if err != nil { - b.Fatal(err) - } - } -} - -func BenchmarkSignPKCS1v15FastHash(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, err := rsa.SignPKCS1v15(rand.Reader, privateKeyFast, crypto.MD5, hashedFast[:]) - if err != nil { - b.Fatal(err) - } - } -} - -func BenchmarkSignPSS(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - pssOptions := &rsa.PSSOptions{ - SaltLength: rsa.PSSSaltLengthAuto, - Hash: crypto.SHA256, - } - - _, err := rsa.SignPSS(rand.Reader, privateKeyPSS, crypto.SHA256, hashedPSS[:], pssOptions) - if err != nil { - b.Fatal(err) - } - } -} diff --git a/protocol/genesis/types/ssvshare.go b/protocol/genesis/types/ssvshare.go deleted file mode 100644 index d2659ee60d..0000000000 --- a/protocol/genesis/types/ssvshare.go +++ /dev/null @@ -1,122 +0,0 @@ -package types - -import ( - "bytes" - "encoding/binary" - "encoding/gob" - "fmt" - "sort" - "time" - - eth2apiv1 "github.com/attestantio/go-eth2-client/api/v1" - "github.com/attestantio/go-eth2-client/spec/bellatrix" - "github.com/attestantio/go-eth2-client/spec/phase0" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - - beaconprotocol "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon" -) - -const ( - MaxPossibleShareSize = 1245 - MaxAllowedShareSize = MaxPossibleShareSize * 8 // Leaving some room for protocol updates and calculation mistakes. -) - -// SSVShare is a combination of genesisspectypes.Share and its Metadata. -type SSVShare struct { - genesisspectypes.Share - Metadata -} - -// Encode encodes SSVShare using gob. -func (s *SSVShare) Encode() ([]byte, error) { - var b bytes.Buffer - e := gob.NewEncoder(&b) - if err := e.Encode(s); err != nil { - return nil, fmt.Errorf("encode SSVShare: %w", err) - } - - return b.Bytes(), nil -} - -// Decode decodes SSVShare using gob. -func (s *SSVShare) Decode(data []byte) error { - if len(data) > MaxAllowedShareSize { - return fmt.Errorf("share size is too big, got %v, max allowed %v", len(data), MaxAllowedShareSize) - } - - d := gob.NewDecoder(bytes.NewReader(data)) - if err := d.Decode(s); err != nil { - return fmt.Errorf("decode SSVShare: %w", err) - } - s.Quorum, s.PartialQuorum = ComputeQuorumAndPartialQuorum(uint64(len(s.Committee))) - return nil -} - -// BelongsToOperator checks whether the share belongs to operator. -func (s *SSVShare) BelongsToOperator(operatorID genesisspectypes.OperatorID) bool { - return operatorID != 0 && s.OperatorID == operatorID -} - -// HasBeaconMetadata checks whether the BeaconMetadata field is not nil. -func (s *SSVShare) HasBeaconMetadata() bool { - return s != nil && s.BeaconMetadata != nil -} - -func (s *SSVShare) IsAttesting(epoch phase0.Epoch) bool { - return s.HasBeaconMetadata() && - (s.BeaconMetadata.IsAttesting() || (s.BeaconMetadata.Status == eth2apiv1.ValidatorStatePendingQueued && s.BeaconMetadata.ActivationEpoch <= epoch)) -} - -func (s *SSVShare) SetFeeRecipient(feeRecipient bellatrix.ExecutionAddress) { - s.FeeRecipientAddress = feeRecipient -} - -// ComputeClusterIDHash will compute cluster ID hash with given owner address and operator ids -func ComputeClusterIDHash(address common.Address, operatorIds []uint64) []byte { - sort.Slice(operatorIds, func(i, j int) bool { - return operatorIds[i] < operatorIds[j] - }) - - // Encode the address and operator IDs in the same way as Solidity's abi.encodePacked - var data []byte - data = append(data, address.Bytes()...) // Address is 20 bytes - for _, id := range operatorIds { - idBytes := make([]byte, 32) // Each ID should be 32 bytes - binary.BigEndian.PutUint64(idBytes[24:], id) // PutUint64 fills the last 8 bytes; rest are 0 - data = append(data, idBytes...) - } - - // Hash the data using keccak256 - hash := crypto.Keccak256(data) - return hash -} - -func ComputeQuorumAndPartialQuorum(committeeSize uint64) (quorum uint64, partialQuorum uint64) { - f := (committeeSize - 1) / 3 - return f*2 + 1, f + 1 -} - -func ValidCommitteeSize(committeeSize int) bool { - f := (committeeSize - 1) / 3 - return (committeeSize-1)%3 == 0 && f >= 1 && f <= 4 -} - -// Metadata represents metadata of SSVShare. -type Metadata struct { - BeaconMetadata *beaconprotocol.ValidatorMetadata - OwnerAddress common.Address - Liquidated bool - - // lastUpdated is an internal field that can be used to track the last time the metadata was updated. - lastUpdated time.Time -} - -func (m *Metadata) MetadataLastUpdated() time.Time { - return m.lastUpdated -} - -func (m *Metadata) SetMetadataLastUpdated(t time.Time) { - m.lastUpdated = t -} diff --git a/protocol/genesis/types/ssvshare_test.go b/protocol/genesis/types/ssvshare_test.go deleted file mode 100644 index 26b4329735..0000000000 --- a/protocol/genesis/types/ssvshare_test.go +++ /dev/null @@ -1,188 +0,0 @@ -package types - -import ( - "encoding/hex" - "testing" - - eth2apiv1 "github.com/attestantio/go-eth2-client/api/v1" - "github.com/attestantio/go-eth2-client/spec/phase0" - "github.com/ethereum/go-ethereum/crypto" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" - "github.com/stretchr/testify/require" - - beaconprotocol "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon" -) - -func TestSSVShare_BelongsToOperator(t *testing.T) { - metadata := &SSVShare{ - Share: genesisspectypes.Share{ - OperatorID: 1, - }, - } - - require.True(t, metadata.BelongsToOperator(1)) - require.False(t, metadata.BelongsToOperator(2)) -} -func TestSSVShare_ComputeClusterIDHash(t *testing.T) { - var ( - aliceClusterHash = "a341933234aa1e6dfd3b8d6677172bdcd0986b1e6afc2e84d321f154d9736717" - testKeyAlice, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") - testAddrAlice = crypto.PubkeyToAddress(testKeyAlice.PublicKey) - ) - - clusterHash := ComputeClusterIDHash(testAddrAlice, []uint64{3, 2, 1, 4}) - clusterHash2 := ComputeClusterIDHash(testAddrAlice, []uint64{4, 3, 1, 2}) - // Convert the hash to a hexadecimal string - hashString := hex.EncodeToString(clusterHash) - hashString2 := hex.EncodeToString(clusterHash2) - - require.Equal(t, aliceClusterHash, hashString) - require.Equal(t, aliceClusterHash, hashString2) -} - -func TestSSVShare_HasBeaconMetadata(t *testing.T) { - tt := []struct { - Name string - ShareMetadata *SSVShare - Result bool - }{ - { - Name: "Metadata == nil", - ShareMetadata: nil, - Result: false, - }, - { - Name: "BeaconMetadata == nil", - ShareMetadata: &SSVShare{ - Metadata: Metadata{ - BeaconMetadata: nil, - }, - }, - Result: false, - }, - { - Name: "BeaconMetadata != nil", - ShareMetadata: &SSVShare{ - Metadata: Metadata{ - BeaconMetadata: &beaconprotocol.ValidatorMetadata{}, - }, - }, - Result: true, - }, - } - - for _, tc := range tt { - tc := tc - t.Run(tc.Name, func(t *testing.T) { - require.Equal(t, tc.Result, tc.ShareMetadata.HasBeaconMetadata()) - }) - } -} - -func TestValidCommitteeSize(t *testing.T) { - tt := []struct { - name string - valid bool - sizes []int - }{ - { - name: "valid", - valid: true, - sizes: []int{4, 7, 10, 13}, - }, - { - name: "invalid", - valid: false, - sizes: []int{0, 1, 2, 3, 5, 6, 8, 9, 11, 12, 14, 15, 16, 17, 18, 19, -1, -4, -7}, - }, - } - - for _, tc := range tt { - tc := tc - t.Run(tc.name, func(t *testing.T) { - for _, size := range tc.sizes { - require.Equal(t, tc.valid, ValidCommitteeSize(size)) - } - }) - } -} - -func TestSSVShare_IsAttesting(t *testing.T) { - currentEpoch := phase0.Epoch(100) // Example current epoch for testing - tt := []struct { - Name string - Share *SSVShare - Epoch phase0.Epoch - Expected bool - }{ - { - Name: "No BeaconMetadata", - Share: &SSVShare{ - Metadata: Metadata{ - BeaconMetadata: nil, - }, - }, - Epoch: currentEpoch, - Expected: false, - }, - { - Name: "Is Attesting", - Share: &SSVShare{ - Metadata: Metadata{ - BeaconMetadata: &beaconprotocol.ValidatorMetadata{ - Status: eth2apiv1.ValidatorStateActiveOngoing, - }, - }, - }, - Epoch: currentEpoch, - Expected: true, - }, - { - Name: "Pending Queued with Future Activation Epoch", - Share: &SSVShare{ - Metadata: Metadata{ - BeaconMetadata: &beaconprotocol.ValidatorMetadata{ - Status: eth2apiv1.ValidatorStatePendingQueued, - ActivationEpoch: currentEpoch + 1, - }, - }, - }, - Epoch: currentEpoch, - Expected: false, - }, - { - Name: "Pending Queued with Current Epoch as Activation Epoch", - Share: &SSVShare{ - Metadata: Metadata{ - BeaconMetadata: &beaconprotocol.ValidatorMetadata{ - Status: eth2apiv1.ValidatorStatePendingQueued, - ActivationEpoch: currentEpoch, - }, - }, - }, - Epoch: currentEpoch, - Expected: true, - }, - { - Name: "Pending Queued with Past Activation Epoch", - Share: &SSVShare{ - Metadata: Metadata{ - BeaconMetadata: &beaconprotocol.ValidatorMetadata{ - Status: eth2apiv1.ValidatorStatePendingQueued, - ActivationEpoch: currentEpoch - 1, - }, - }, - }, - Epoch: currentEpoch, - Expected: true, - }, - } - - for _, tc := range tt { - tc := tc - t.Run(tc.Name, func(t *testing.T) { - result := tc.Share.IsAttesting(tc.Epoch) - require.Equal(t, tc.Expected, result) - }) - } -} diff --git a/protocol/v2/ssv/runner/committee.go b/protocol/v2/ssv/runner/committee.go index 3866759348..ef44be95c2 100644 --- a/protocol/v2/ssv/runner/committee.go +++ b/protocol/v2/ssv/runner/committee.go @@ -66,7 +66,7 @@ func NewCommitteeRunner( return &CommitteeRunner{ BaseRunner: &BaseRunner{ RunnerRoleType: spectypes.RoleCommittee, - DomainType: networkConfig.AlanDomainType, + DomainType: networkConfig.DomainType, BeaconNetwork: networkConfig.Beacon.GetBeaconNetwork(), Share: share, QBFTController: qbftController, diff --git a/protocol/v2/ssv/spectest/ssv_mapping_test.go b/protocol/v2/ssv/spectest/ssv_mapping_test.go index 9a59fb171f..bf0398b76a 100644 --- a/protocol/v2/ssv/spectest/ssv_mapping_test.go +++ b/protocol/v2/ssv/spectest/ssv_mapping_test.go @@ -367,7 +367,7 @@ func fixRunnerForRun(t *testing.T, runnerMap map[string]interface{}, ks *spectes base := &runner.BaseRunner{} byts, _ := json.Marshal(baseRunnerMap) require.NoError(t, json.Unmarshal(byts, &base)) - base.DomainType = networkconfig.TestNetwork.AlanDomainType + base.DomainType = networkconfig.TestNetwork.DomainType logger := logging.TestLogger(t) @@ -384,7 +384,7 @@ func fixRunnerForRun(t *testing.T, runnerMap map[string]interface{}, ks *spectes } if (ret.GetBaseRunner().DomainType == spectypes.DomainType{}) { - ret.GetBaseRunner().DomainType = networkconfig.TestNetwork.AlanDomainType + ret.GetBaseRunner().DomainType = networkconfig.TestNetwork.DomainType } return ret diff --git a/protocol/v2/ssv/testing/runner.go b/protocol/v2/ssv/testing/runner.go index 3dbeff4a97..da448980d8 100644 --- a/protocol/v2/ssv/testing/runner.go +++ b/protocol/v2/ssv/testing/runner.go @@ -135,7 +135,7 @@ var ConstructBaseRunner = func( ) case spectypes.RoleAggregator: r, err = runner.NewAggregatorRunner( - networkconfig.TestNetwork.AlanDomainType, + networkconfig.TestNetwork.DomainType, spectypes.BeaconTestNetwork, shareMap, contr, @@ -148,7 +148,7 @@ var ConstructBaseRunner = func( ) case spectypes.RoleProposer: r, err = runner.NewProposerRunner( - networkconfig.TestNetwork.AlanDomainType, + networkconfig.TestNetwork.DomainType, spectypes.BeaconTestNetwork, shareMap, contr, @@ -162,7 +162,7 @@ var ConstructBaseRunner = func( ) case spectypes.RoleSyncCommitteeContribution: r, err = runner.NewSyncCommitteeAggregatorRunner( - networkconfig.TestNetwork.AlanDomainType, + networkconfig.TestNetwork.DomainType, spectypes.BeaconTestNetwork, shareMap, contr, @@ -175,7 +175,7 @@ var ConstructBaseRunner = func( ) case spectypes.RoleValidatorRegistration: r, err = runner.NewValidatorRegistrationRunner( - networkconfig.TestNetwork.AlanDomainType, + networkconfig.TestNetwork.DomainType, spectypes.BeaconTestNetwork, shareMap, tests.NewTestingBeaconNodeWrapped(), @@ -185,7 +185,7 @@ var ConstructBaseRunner = func( ) case spectypes.RoleVoluntaryExit: r, err = runner.NewVoluntaryExitRunner( - networkconfig.TestNetwork.AlanDomainType, + networkconfig.TestNetwork.DomainType, spectypes.BeaconTestNetwork, shareMap, tests.NewTestingBeaconNodeWrapped(), @@ -386,7 +386,7 @@ var ConstructBaseRunnerWithShareMap = func( ) case spectypes.RoleAggregator: r, err = runner.NewAggregatorRunner( - networkconfig.TestNetwork.AlanDomainType, + networkconfig.TestNetwork.DomainType, spectypes.BeaconTestNetwork, shareMap, contr, @@ -399,7 +399,7 @@ var ConstructBaseRunnerWithShareMap = func( ) case spectypes.RoleProposer: r, err = runner.NewProposerRunner( - networkconfig.TestNetwork.AlanDomainType, + networkconfig.TestNetwork.DomainType, spectypes.BeaconTestNetwork, shareMap, contr, @@ -413,7 +413,7 @@ var ConstructBaseRunnerWithShareMap = func( ) case spectypes.RoleSyncCommitteeContribution: r, err = runner.NewSyncCommitteeAggregatorRunner( - networkconfig.TestNetwork.AlanDomainType, + networkconfig.TestNetwork.DomainType, spectypes.BeaconTestNetwork, shareMap, contr, @@ -426,7 +426,7 @@ var ConstructBaseRunnerWithShareMap = func( ) case spectypes.RoleValidatorRegistration: r, err = runner.NewValidatorRegistrationRunner( - networkconfig.TestNetwork.AlanDomainType, + networkconfig.TestNetwork.DomainType, spectypes.BeaconTestNetwork, shareMap, tests.NewTestingBeaconNodeWrapped(), @@ -436,7 +436,7 @@ var ConstructBaseRunnerWithShareMap = func( ) case spectypes.RoleVoluntaryExit: r, err = runner.NewVoluntaryExitRunner( - networkconfig.TestNetwork.AlanDomainType, + networkconfig.TestNetwork.DomainType, spectypes.BeaconTestNetwork, shareMap, tests.NewTestingBeaconNodeWrapped(), diff --git a/protocol/v2/ssv/validator/opts.go b/protocol/v2/ssv/validator/opts.go index 6cf5bc7a25..42f69facca 100644 --- a/protocol/v2/ssv/validator/opts.go +++ b/protocol/v2/ssv/validator/opts.go @@ -1,18 +1,12 @@ package validator import ( - genesisspecqbft "github.com/ssvlabs/ssv-spec-pre-cc/qbft" - genesisspectypes "github.com/ssvlabs/ssv-spec-pre-cc/types" specqbft "github.com/ssvlabs/ssv-spec/qbft" spectypes "github.com/ssvlabs/ssv-spec/types" - "github.com/ssvlabs/ssv/ibft/genesisstorage" - genesisqbftctrl "github.com/ssvlabs/ssv/protocol/genesis/qbft/controller" - "github.com/ssvlabs/ssv/ibft/storage" "github.com/ssvlabs/ssv/message/validation" "github.com/ssvlabs/ssv/networkconfig" - genesisbeacon "github.com/ssvlabs/ssv/protocol/genesis/blockchain/beacon" "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon" qbftctrl "github.com/ssvlabs/ssv/protocol/v2/qbft/controller" "github.com/ssvlabs/ssv/protocol/v2/ssv/runner" @@ -28,7 +22,6 @@ type Options struct { NetworkConfig networkconfig.NetworkConfig Network specqbft.Network Beacon beacon.BeaconNode - GenesisBeacon genesisbeacon.BeaconNode Storage *storage.QBFTStores SSVShare *ssvtypes.SSVShare Operator *spectypes.CommitteeMember @@ -43,14 +36,6 @@ type Options struct { MessageValidator validation.MessageValidator Metrics Metrics Graffiti []byte - GenesisOptions -} - -type GenesisOptions struct { - Network genesisspecqbft.Network - Storage *genesisstorage.QBFTStores - Signer genesisspectypes.KeyManager - NewDecidedHandler genesisqbftctrl.NewDecidedHandler } func (o *Options) defaults() { diff --git a/registry/storage/shares.go b/registry/storage/shares.go index ec82654561..7604f3fcc6 100644 --- a/registry/storage/shares.go +++ b/registry/storage/shares.go @@ -13,7 +13,6 @@ import ( "go.uber.org/zap" "golang.org/x/exp/maps" - genesistypes "github.com/ssvlabs/ssv/protocol/genesis/types" beaconprotocol "github.com/ssvlabs/ssv/protocol/v2/blockchain/beacon" "github.com/ssvlabs/ssv/protocol/v2/types" "github.com/ssvlabs/ssv/storage/basedb" @@ -144,7 +143,7 @@ func (s *sharesStorage) load() error { if err := val.Decode(obj.Value); err != nil { return fmt.Errorf("failed to deserialize share: %w", err) } - val.DomainType = spectypes.DomainType(genesistypes.GetDefaultDomain()) + share, err := s.storageShareToSpecShare(val) if err != nil { return fmt.Errorf("failed to convert storage share to spec share: %w", err) diff --git a/registry/storage/shares_test.go b/registry/storage/shares_test.go index 513cc76cf4..fde107b290 100644 --- a/registry/storage/shares_test.go +++ b/registry/storage/shares_test.go @@ -217,7 +217,7 @@ func generateRandomValidatorStorageShare(splitKeys map[uint64]*bls.SecretKey) (* Committee: ibftCommittee, Quorum: quorum, PartialQuorum: partialQuorum, - DomainType: networkconfig.TestNetwork.DomainType(), + DomainType: networkconfig.TestNetwork.DomainType, FeeRecipientAddress: common.HexToAddress("0xFeedB14D8b2C76FdF808C29818b06b830E8C2c0e"), Graffiti: bytes.Repeat([]byte{0x01}, 32), }, @@ -259,7 +259,7 @@ func generateRandomValidatorSpecShare(splitKeys map[uint64]*bls.SecretKey) (*ssv ValidatorPubKey: spectypes.ValidatorPK(sk1.GetPublicKey().Serialize()), SharePubKey: sk2.GetPublicKey().Serialize(), Committee: ibftCommittee, - DomainType: networkconfig.TestNetwork.DomainType(), + DomainType: networkconfig.TestNetwork.DomainType, FeeRecipientAddress: common.HexToAddress("0xFeedB14D8b2C76FdF808C29818b06b830E8C2c0e"), Graffiti: bytes.Repeat([]byte{0x01}, 32), }, diff --git a/scripts/spec-alignment/genesis_differ.config.yaml b/scripts/spec-alignment/genesis_differ.config.yaml deleted file mode 100644 index 8c46ba8ac9..0000000000 --- a/scripts/spec-alignment/genesis_differ.config.yaml +++ /dev/null @@ -1,42 +0,0 @@ -ApprovedChanges: ["52b93267ba812308","9f2881f9e89b4c3","f8718ef9598a2d28","ea4da0c78bc1e930","11481543a56b03e7","4bc55d173976f499","5a326429bd7d816a","57938492d36e5b72","ea83b3555f29e44e","39a395cc56c381d8","2092a46a009de5e9","f9e12bb821abda59","74490095cad1f871","fef6a577794897e9","e243efb1fef8baca","b612f4f4bee5726c","b4072ece06d92c84","487d349a6296651e","1329fd2f0f7101e5","6ea163caa000821c","cb2a3fac03c9f70d","c155c7005d298b8a","5462556ab33327ae","66591f5d3e9c299d","ef530512222fa3a3","54f7ee00c5223d56","136792991a713119","519dec1f394a29bb","c16537938b23bb1c","930f8003cc73658a","b4d4b7c288d15580","8e871e3dc302502c","264f6c3cb6c044e","73b442121276436f","a5d665260b9545e7","e76da25dcc1b8b7b","3021b027e65343dc","37abca362396de40","10331350bdd5cea5","ff66465e82a0bce","7008ba0e5fb3bc50","17d86fc521251084","60ee89aed3dca274","cfb5a31338393004","774c67a1117bb600","bbbac3fd539c9da","4120ef6224cd7653","c13c14ac8b912a99","e34eb83c1de17e7b","d60440779e512dda","8b474f07634c3b5b","ac42b9ed129f763c","67809ff9e1f78fba","436d37b16e59e851","d201c716184904d6","422221ab59ac4658","30ed9a822232b7e1","c08c6d84582b86c1","c07315929c5bfdae","751997d95ea9340","7715acc5b4c5aa2","14d6cdfdf92136fc","c9db895746d32d2","a0a0164bd2ecb327","c0cb3984d0a20d8","1c8beb7d60ffa18a","b44005e951042d3","45749213deaece88","6afb57c28a55854c","5619c6b724070584","81385e7b399b3235","856eb69df47300bc","68ab7316969c38b","c8f63fe574c9cd3","a1dd0a169df78d67","4bb11f08323af914","466839f492add207","6c3507bea504fcc","560bb093d1aea569","a13eb5088003f50b","9f5f0eff2dca5e9", "397220931cab52bc","453245a906210497","1eb92714f465d122", "2015d5566f6182e1","9f0799ecd4426e43","20a0cfb49029370f","b5ae2491e369931a","94800037492ba19a","57f89a48ccc5bdb0","a825ac16288e518f","1a8754cea558330e","837449174a662384", "eb4770deec3d69ae","88c54a52b5c156ec","7b39050170e98fbe","a94f7bc050a7c5e0"] - -IgnoredIdentifiers: - - logger -ReducedPackageNames: - - spectypes - - specssv - - specqbft - - types - - ssv - - qbft - - instance - - controller - - genesisspecssv - - genesisspectypes - - genesisspecqbft -Comparisons: - - Packages: - Left: - - ./protocol/genesis/ssv/runner - - ./protocol/genesis/types - Right: - - ./ssv - Hints: - ReconstructSignature: PartialSigContainer.ReconstructSignature - - - Packages: - Left: - - ./protocol/genesis/qbft/controller - - ./protocol/genesis/qbft/instance - Right: - - ./qbft - Hints: - BaseCommitValidation: baseCommitValidation - - - Packages: - Left: - - ./protocol/genesis/types - Right: - - ./types - Hints: - VerifyByOperators: Signature.VerifyByOperators \ No newline at end of file diff --git a/scripts/spec-alignment/genesis_differ.sh b/scripts/spec-alignment/genesis_differ.sh deleted file mode 100755 index 728c9da25e..0000000000 --- a/scripts/spec-alignment/genesis_differ.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -# Get the path to the ssv-spec folder. -SPEC_PATH=$(go mod download -json github.com/ssvlabs/ssv-spec-pre-cc | jq -r .Dir) - -# Run Differ. -differ \ - --config ./genesis_differ.config.yaml \ - --output ./genesis_output.diff \ - ../../ \ - $SPEC_PATH \ No newline at end of file diff --git a/utils/boot_node/node.go b/utils/boot_node/node.go index 265a3476a3..a0c10df823 100644 --- a/utils/boot_node/node.go +++ b/utils/boot_node/node.go @@ -164,26 +164,15 @@ func (n *bootNode) createListener(logger *zap.Logger, ipAddr string, port uint16 log.Fatal(err) } - // Allocate a fake connection to forward postFork packets to the preFork listener. - unhandled := make(chan discover.ReadPacket, 100) // size taken from https://github.com/ethereum/go-ethereum/blob/v1.13.5/p2p/server.go#L551 - sharedConn := &discovery.SharedUDPConn{UDPConn: conn, Unhandled: unhandled} - - postForkListener, err := discover.ListenV5(conn, localNode, discover.Config{ + listener, err := discover.ListenV5(conn, localNode, discover.Config{ PrivateKey: privateKey, - Unhandled: unhandled, V5ProtocolID: &n.network.DiscoveryProtocolID, }) if err != nil { log.Fatal(err) } - preForkListener, err := discover.ListenV5(sharedConn, localNode, discover.Config{ - PrivateKey: privateKey, - }) - if err != nil { - log.Fatal(err) - } - return discovery.NewForkingDV5Listener(logger, preForkListener, postForkListener, 5*time.Second, n.network) + return listener } func (n *bootNode) createLocalNode(logger *zap.Logger, privKey *ecdsa.PrivateKey, ipAddr net.IP, port uint16) (*enode.LocalNode, error) { @@ -199,26 +188,6 @@ func (n *bootNode) createLocalNode(logger *zap.Logger, privKey *ecdsa.PrivateKey logger.Info("Running with External IP", zap.String("external_ip", n.externalIP)) } - // if *forkVersion != "" { - // fVersion, err = hex.DecodeString(*forkVersion) - // if err != nil { - // return nil, errors.Wrap(err, "Could not retrieve fork version") - // } - // if len(fVersion) != 4 { - // return nil, errors.Errorf("Invalid fork version size expected %d but got %d", 4, len(fVersion)) - // } - //} - // if *genesisValidatorRoot != "" { - // retRoot, err := hex.DecodeString(*genesisValidatorRoot) - // if err != nil { - // return nil, errors.Wrap(err, "Could not retrieve genesis validator root") - // } - // if len(retRoot) != 32 { - // return nil, errors.Errorf("Invalid root size, expected 32 but got %d", len(retRoot)) - // } - // genRoot = bytesutil.ToBytes32(retRoot) - //} - localNode := enode.NewLocalNode(db, privKey) localNode.Set(enr.WithEntry("ssv", true)) localNode.SetFallbackIP(external)