-
Notifications
You must be signed in to change notification settings - Fork 252
Make previous identity endorsement fetching robust to the ledger gaps #7913
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
+2,472
−357
Closed
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
30fba92
Relax identity chain completeness guarantee
maxtropets 0633000
Review fixes
maxtropets 5cf01f5
Fix all races by hardening mutexs
maxtropets bb080be
No leaking comments, doc fix
maxtropets 1a2304d
Merge branch 'main' into f/partial-chain-receipts
achamayou File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,10 +6,8 @@ | |
| #include "ccf/node_subsystem_interface.h" | ||
| #include "ccf/tx_id.h" | ||
|
|
||
| #include <exception> | ||
| #include <map> | ||
| #include <optional> | ||
| #include <string> | ||
| #include <vector> | ||
|
|
||
| namespace ccf | ||
|
|
@@ -24,29 +22,19 @@ namespace ccf | |
| /// Status of the network identity endorsement fetching process. | ||
| enum class FetchStatus : uint8_t | ||
| { | ||
| Retry, ///< Fetching should be retried | ||
| Done, ///< Fetching completed successfully | ||
| Failed ///< Fetching failed | ||
| Done, ///< Fetching trusted identities completed successfully | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is problematic in two ways:
Does the FetchStatus need to be in a public header? If it must, can we at least make the order Partial, Done, Failed? |
||
| Partial, ///< Chain is still being built or fetching attempts were | ||
| ///< exhausted (e.g. ledger files missing). Readers see the | ||
| ///< validated subset; @ref | ||
| ///< NetworkIdentitySubsystemInterface::trigger_extension | ||
| ///< can request more. | ||
| Failed ///< Fetching failed with error and cannot be resumed | ||
| }; | ||
|
|
||
| /// Map from sequence number to EC public key, representing the trusted | ||
| /// network identity keys over the history of the service. | ||
| using TrustedKeys = std::map<ccf::SeqNo, ccf::crypto::ECPublicKeyPtr>; | ||
|
|
||
| /// Exception thrown when identity data is requested before the | ||
| /// asynchronous identity-history-fetching process has completed. | ||
| struct IdentityHistoryNotFetched : public std::exception | ||
| { | ||
| std::string msg; | ||
|
|
||
| IdentityHistoryNotFetched(std::string msg) : msg(std::move(msg)) {} | ||
|
|
||
| [[nodiscard]] const char* what() const noexcept override | ||
| { | ||
| return msg.c_str(); | ||
| } | ||
| }; | ||
|
|
||
| /// Interface for accessing the network identity subsystem, which manages | ||
| /// the service's cryptographic identity and its historical trusted keys. | ||
| class NetworkIdentitySubsystemInterface : public ccf::AbstractNodeSubSystem | ||
|
|
@@ -62,34 +50,36 @@ namespace ccf | |
| /// Returns a reference to the current network identity. | ||
| virtual const std::unique_ptr<NetworkIdentity>& get() = 0; | ||
|
|
||
| /// Returns the current status of endorsement fetching. | ||
| /// Returns the current status of endorsement fetching. Callers | ||
| /// should check this before acting on a nullopt/nullptr/empty | ||
| /// reader result: in @ref FetchStatus::Partial more data may | ||
| /// arrive via @ref NetworkIdentitySubsystemInterface::trigger_extension; | ||
| /// in @ref FetchStatus::Failed the fetch is unrecoverable. | ||
| [[nodiscard]] virtual FetchStatus endorsements_fetching_status() const = 0; | ||
|
|
||
| /// Schedule a fresh attempt to fetch the next missing predecessor | ||
| /// endorsement. No-op outside @ref FetchStatus::Partial. Thread-safe | ||
| /// and idempotent: concurrent callers trigger at most one cycle. | ||
| virtual void trigger_extension() = 0; | ||
|
|
||
| /// Returns the COSE endorsements chain for the given sequence number, | ||
| /// or std::nullopt if the chain is not available for the given sequence | ||
| /// number. | ||
| /// | ||
| /// @throws IdentityHistoryNotFetched if identity history fetching has not | ||
| /// completed. | ||
| /// or std::nullopt if the chain does not yet reach back to the | ||
| /// requested seqno (see @ref | ||
| /// NetworkIdentitySubsystemInterface::trigger_extension). | ||
| [[nodiscard]] virtual std::optional<CoseEndorsementsChain> | ||
| get_cose_endorsements_chain(ccf::SeqNo seqno) const = 0; | ||
|
|
||
| /// Returns the trusted EC public key that was active at the given | ||
| /// sequence number, or nullptr if the sequence number precedes the | ||
| /// earliest known trusted key. | ||
| /// | ||
| /// @throws IdentityHistoryNotFetched if identity history fetching has not | ||
| /// completed. | ||
| /// @throws std::logic_error if no trusted keys have been fetched, or if | ||
| /// internal key resolution is inconsistent. | ||
| /// sequence number, or nullptr if the sequence number predates the | ||
| /// earliest known trusted key (see @ref | ||
| /// NetworkIdentitySubsystemInterface::trigger_extension). | ||
| [[nodiscard]] virtual ccf::crypto::ECPublicKeyPtr get_trusted_identity_for( | ||
| ccf::SeqNo seqno) const = 0; | ||
|
|
||
| /// Returns all trusted network identity keys as a map from sequence | ||
| /// number to EC public key. | ||
| /// | ||
| /// @throws IdentityHistoryNotFetched if identity history fetching has not | ||
| /// completed. | ||
| /// number to EC public key. In @ref FetchStatus::Partial older epochs | ||
| /// may be missing -- see @ref | ||
| /// NetworkIdentitySubsystemInterface::trigger_extension. | ||
| [[nodiscard]] virtual TrustedKeys get_trusted_keys() const = 0; | ||
| }; | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the Apache 2.0 License. | ||
| #pragma once | ||
|
|
||
| #include "ccf/tx_id.h" | ||
| #include "service/tables/previous_service_identity.h" | ||
| #include "tasks/task_system.h" | ||
|
|
||
| #include <chrono> | ||
| #include <functional> | ||
| #include <optional> | ||
|
|
||
| namespace ccf | ||
| { | ||
| struct INodeStateAccessor | ||
| { | ||
| virtual ~INodeStateAccessor() = default; | ||
|
|
||
| [[nodiscard]] virtual bool is_part_of_network() const = 0; | ||
|
|
||
| // Current service's create-txid, or nullopt if not yet available. | ||
| virtual std::optional<TxID> read_current_service_from() = 0; | ||
|
|
||
| // Topmost previous-identity endorsement entry, or nullopt if none. | ||
| virtual std::optional<CoseEndorsement> read_topmost_endorsement() = 0; | ||
| }; | ||
|
|
||
| struct IHistoricalStateAccessor | ||
| { | ||
| virtual ~IHistoricalStateAccessor() = default; | ||
|
|
||
| // Endorsement entry at the given historical kv version, or nullopt | ||
| // if the historical state is not yet loaded. Implementations may | ||
| // throw on hard errors. | ||
| virtual std::optional<CoseEndorsement> get_endorsement_at(SeqNo) = 0; | ||
| }; | ||
|
|
||
| struct TaskScheduler | ||
| { | ||
| virtual ~TaskScheduler() = default; | ||
|
|
||
| virtual void add_task(std::function<void()> fn) = 0; | ||
|
|
||
| virtual void add_delayed_task( | ||
| std::function<void()> fn, std::chrono::milliseconds delay) = 0; | ||
| }; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,130 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the Apache 2.0 License. | ||
| #pragma once | ||
|
|
||
| #include "ccf/service/tables/service.h" | ||
| #include "node/historical_queries.h" | ||
| #include "node/rpc/network_identity_accessors.h" | ||
| #include "node/rpc/node_interface.h" | ||
| #include "service/internal_tables_access.h" | ||
| #include "tasks/basic_task.h" | ||
| #include "tasks/task_system.h" | ||
|
|
||
| #include <fmt/format.h> | ||
| #include <memory> | ||
| #include <stdexcept> | ||
| #include <utility> | ||
|
|
||
| namespace ccf | ||
| { | ||
| class NodeStateAccessor : public INodeStateAccessor | ||
| { | ||
| protected: | ||
| AbstractNodeState& node_state; | ||
|
|
||
| public: | ||
| NodeStateAccessor(AbstractNodeState& node_state_) : node_state(node_state_) | ||
| {} | ||
|
|
||
| [[nodiscard]] bool is_part_of_network() const override | ||
| { | ||
| return node_state.is_part_of_network(); | ||
| } | ||
|
|
||
| std::optional<TxID> read_current_service_from() override | ||
| { | ||
| auto store = node_state.get_store(); | ||
| auto tx = store->create_read_only_tx(); | ||
| auto* service_info_handle = | ||
| tx.template ro<ccf::Service>(ccf::Tables::SERVICE); | ||
| auto service_info = service_info_handle->get(); | ||
| if ( | ||
| !service_info || !service_info->current_service_create_txid.has_value()) | ||
| { | ||
| return std::nullopt; | ||
| } | ||
| if (service_info->status != ServiceStatus::OPEN) | ||
| { | ||
| // It can happen that node advances its internal state machine to | ||
| // part-of-network, but the service opening tx has not been | ||
| // replicated yet. This will cause the first fetched endorsement | ||
| // to be obsolete, but waiting for ServiceStatus::OPEN is | ||
| // sufficient, as it's supposed to arrive in the same TX that | ||
| // the previous identity endorsement. | ||
| return std::nullopt; | ||
| } | ||
| return service_info->current_service_create_txid; | ||
| } | ||
|
|
||
| std::optional<CoseEndorsement> read_topmost_endorsement() override | ||
| { | ||
| auto store = node_state.get_store(); | ||
| auto tx = store->create_read_only_tx(); | ||
| return tx | ||
| .template ro<ccf::PreviousServiceIdentityEndorsement>( | ||
| ccf::Tables::PREVIOUS_SERVICE_IDENTITY_ENDORSEMENT) | ||
| ->get(); | ||
| } | ||
| }; | ||
|
|
||
| class HistoricalStateAccessor : public IHistoricalStateAccessor | ||
| { | ||
| protected: | ||
| std::shared_ptr<historical::StateCacheImpl> historical_cache; | ||
|
|
||
| public: | ||
| HistoricalStateAccessor( | ||
| std::shared_ptr<historical::StateCacheImpl> historical_cache_) : | ||
| historical_cache(std::move(historical_cache_)) | ||
| {} | ||
|
|
||
| std::optional<CoseEndorsement> get_endorsement_at(SeqNo seq) override | ||
| { | ||
| auto state = historical_cache->get_state_at( | ||
| ccf::historical::CompoundHandle{ | ||
| ccf::historical::RequestNamespace::System, seq}, | ||
| seq); | ||
| if (!state) | ||
| { | ||
| return std::nullopt; | ||
| } | ||
| if (!state->store) | ||
| { | ||
| throw std::runtime_error(fmt::format( | ||
| "Historical state with seqno {} is loaded but its store is " | ||
| "missing", | ||
| seq)); | ||
| } | ||
| auto htx = state->store->create_read_only_tx(); | ||
| auto endorsement = | ||
| htx | ||
| .template ro<ccf::PreviousServiceIdentityEndorsement>( | ||
| ccf::Tables::PREVIOUS_SERVICE_IDENTITY_ENDORSEMENT) | ||
| ->get(); | ||
| if (!endorsement.has_value()) | ||
| { | ||
| throw std::runtime_error(fmt::format( | ||
| "COSE endorsement entry for seqno {} is missing from its " | ||
| "historical state", | ||
| seq)); | ||
| } | ||
| return endorsement; | ||
| } | ||
| }; | ||
|
|
||
| class TaskSchedulerImpl : public TaskScheduler | ||
| { | ||
| public: | ||
| void add_task(std::function<void()> fn) override | ||
| { | ||
| ccf::tasks::add_task(ccf::tasks::make_basic_task(std::move(fn))); | ||
| } | ||
|
|
||
| void add_delayed_task( | ||
| std::function<void()> fn, std::chrono::milliseconds delay) override | ||
| { | ||
| auto task = ccf::tasks::make_basic_task(std::move(fn)); | ||
| ccf::tasks::add_delayed_task(task, delay); | ||
| } | ||
| }; | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.