Skip to content

Commit 1ea5d2d

Browse files
Snapshot endpoint (#7767)
Co-authored-by: Amaury Chamayou <amchamay@microsoft.com>
1 parent 7f28060 commit 1ea5d2d

23 files changed

Lines changed: 546 additions & 25 deletions

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
1111

1212
### Added
1313

14+
- Added `POST /node/snapshot:create`, gated by the `SnapshotCreate` RPC interface operator feature, to create a snapshot via an operator endpoint rather than a governance action.
1415
- Added `make_cose_verifier_from_pem_cert()` and `make_cose_verifier_from_der_cert()` that accept certificates in a known format. The existing `make_cose_verifier_cert()` is renamed to `make_cose_verifier_any_cert()` (#7768).
1516

1617
### Changed

doc/audit/builtin_maps.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,17 @@ Status information recorded when a primary produces a snapshot.
560560
:project: CCF
561561
:members:
562562

563+
``snapshot_create``
564+
~~~~~~~~~~~~~~~~~~~
565+
566+
Durability marker written when a snapshot is explicitly requested via the operator endpoint.
567+
This ensures the request is recorded as a real transaction even when it would otherwise
568+
carry only a transaction flag.
569+
570+
**Key** Sentinel value 0, represented as a little-endian 64-bit unsigned integer.
571+
572+
**Value** Sentinel value 0, represented as a little-endian 64-bit unsigned integer.
573+
563574
``encrypted_submitted_shares``
564575
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
565576

doc/host_config_schema/cchost_config.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@
138138
"enabled_operator_features": {
139139
"type": "array",
140140
"items": {
141-
"enum": ["SnapshotRead", "LedgerChunkRead"],
141+
"enum": ["SnapshotRead", "LedgerChunkRead", "SnapshotCreate"],
142142
"type": "string"
143143
},
144144
"description": "An array of features which should be enabled on this interface, providing access to endpoints with specific security or performance constraints."

doc/operations/configuration.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ Currently supported features are:
3030

3131
1. 'SnapshotRead': gates access to endpoints used to fetch snapshots directly from nodes (:http:GET:`/node/snapshot`, :http:HEAD:`/node/snapshot`, :http:GET:`/node/snapshot/{snapshot_name}` and :http:HEAD:`/node/snapshot/{snapshot_name}`).
3232
2. 'LedgerChunkRead': gates access to endpoints used to retrieve ledger chunks (:http:GET:`/node/ledger_chunk`, :http:HEAD:`/node/ledger_chunk`, :http:GET:`/node/ledger_chunk/{chunk_name}` and :http:HEAD:`/node/ledger_chunk/{chunk_name}`).
33+
3. 'SnapshotCreate': gates access to the operator endpoint used to create a snapshot on the next signature transaction (:http:POST:`/node/snapshot:create`).
3334

3435
Since these operations may require disk IO and produce large responses, these features should not be enabled on interfaces with public access, and instead restricted to interfaces with local connectivity for node-to-node and operator access.
3536

@@ -38,4 +39,4 @@ Since these operations may require disk IO and produce large responses, these fe
3839

3940
- Size strings are expressed as the value suffixed with the size in bytes (``B``, ``KB``, ``MB``, ``GB``, ``TB``, as factors of 1024), e.g. ``"20MB"``, ``"100KB"`` or ``"2048"`` (bytes).
4041

41-
- Time strings are expressed as the value suffixed with the duration (``us``, ``ms``, ``s``, ``min``, ``h``), e.g. ``"1000ms"``, ``"10s"`` or ``"30min"``.
42+
- Time strings are expressed as the value suffixed with the duration (``us``, ``ms``, ``s``, ``min``, ``h``), e.g. ``"1000ms"``, ``"10s"`` or ``"30min"``.

doc/operations/ledger_snapshot.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ To avoid this, it is possible for a new node to be added (or a service to be rec
166166
Snapshot Generation
167167
~~~~~~~~~~~~~~~~~~~
168168

169-
Snapshots are generated at regular intervals by the current primary node and stored under the directory specified via the ``snapshots.directory`` configuration entry (defaults to ``snapshots/``). The transaction interval at which snapshots are generated is specified via the ``snapshots.tx_count`` configuration entry (defaults to a new snapshot generated every ``10,000`` committed transactions). Snapshots can also be generated by the ``trigger_snapshot`` governance action, i.e. by submitting a proposal. A snapshot will then be generated at the next signature transaction.
169+
Snapshots are generated at regular intervals by the current primary node and stored under the directory specified via the ``snapshots.directory`` configuration entry (defaults to ``snapshots/``). The transaction interval at which snapshots are generated is specified via the ``snapshots.tx_count`` configuration entry (defaults to a new snapshot generated every ``10,000`` committed transactions). Snapshots can also be explicitly requested, we recommend doing so via :http:POST:`/node/snapshot:create` on an interface with the ``SnapshotCreate`` operator feature enabled. Some constitutions may instead use ``trigger_snapshot`` however the endpoint avoids the complex and costly governance flow. In both cases, the snapshot is generated at the next signature transaction.
170170

171171
Time-based snapshotting can also be enabled with ``snapshots.time_interval``. When this is set, a snapshot is eligible once more than ``snapshots.min_tx_count`` transactions have elapsed since the last snapshot. These transactions include CCF internal signature and snapshot bookkeeping transactions as well as application writes.
172172

doc/schemas/node_openapi.json

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -603,7 +603,8 @@
603603
"OperatorFeature": {
604604
"enum": [
605605
"SnapshotRead",
606-
"LedgerChunkRead"
606+
"LedgerChunkRead",
607+
"SnapshotCreate"
607608
],
608609
"type": "string"
609610
},
@@ -929,7 +930,7 @@
929930
"info": {
930931
"description": "This API provides public, uncredentialed access to service and node state.",
931932
"title": "CCF Public Node API",
932-
"version": "5.0.4"
933+
"version": "5.0.5"
933934
},
934935
"openapi": "3.0.0",
935936
"paths": {
@@ -1841,6 +1842,22 @@
18411842
}
18421843
]
18431844
},
1845+
"/node/snapshot:create": {
1846+
"post": {
1847+
"operationId": "PostNodeSnapshotCreate",
1848+
"responses": {
1849+
"204": {
1850+
"description": "Default response description"
1851+
},
1852+
"default": {
1853+
"$ref": "#/components/responses/default"
1854+
}
1855+
},
1856+
"x-ccf-forwarding": {
1857+
"$ref": "#/components/x-ccf-forwarding/never"
1858+
}
1859+
}
1860+
},
18441861
"/node/state": {
18451862
"get": {
18461863
"operationId": "GetNodeState",

include/ccf/kv/unit.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ namespace ccf::kv::serialisers
99
// Unit serialisations are used as a utility type to convert ccf::kv::Maps to
1010
// ccf::kv::Maps and ccf::kv::Sets. Specifically, these are implemented as
1111
// wrappers so that ccf::kv::Value<T> is essentially ccf::kv::Map<Unit, T>,
12-
// and ccf::kv::Set<T> is ccf::kv::Map<T, Unit>. This is used as a template
13-
// parameter allowing the caller to specify what value is inserted into the
14-
// ledger.
12+
// ccf::kv::Set<T> is ccf::kv::Map<T, Unit>, and ccf::kv::UnitValue is
13+
// ccf::kv::Map<Unit, Unit>. This is used as a template parameter allowing
14+
// the caller to specify what value is inserted into the ledger.
1515

1616
// This is the default UnitCreator, returning 8 null bytes for compatibility
1717
// with old ledgers (where Values were previously Maps with a single entry

include/ccf/kv/unit_value.h

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the Apache 2.0 License.
3+
#pragma once
4+
5+
#include "ccf/kv/get_name.h"
6+
#include "ccf/kv/hooks.h"
7+
#include "ccf/kv/unit_value_handle.h"
8+
#include "ccf/kv/untyped.h"
9+
10+
namespace ccf::kv
11+
{
12+
/** Defines the schema of a touched-only type accessed by a @c ccf::Tx. This
13+
* value is a container for an optional single marker indicating whether it
14+
* has been touched.
15+
*
16+
* This is implemented as a @c ccf::kv::Map from Unit to Unit, and the
17+
* serialisation of the fixed key and value are overridable with the Unit
18+
* template parameter.
19+
*/
20+
template <typename Unit = ccf::kv::serialisers::ZeroBlitUnitCreator>
21+
class UnitValue : public GetName
22+
{
23+
public:
24+
using ReadOnlyHandle = ccf::kv::ReadableUnitValueHandle<Unit>;
25+
using WriteOnlyHandle = ccf::kv::WriteableUnitValueHandle<Unit>;
26+
using Handle = ccf::kv::UnitValueHandle<Unit>;
27+
28+
using Write = std::optional<ccf::kv::serialisers::SerialisedEntry>;
29+
using MapHook = ccf::kv::MapHook<Write>;
30+
using CommitHook = ccf::kv::CommitHook<Write>;
31+
32+
using GetName::GetName;
33+
34+
static ccf::kv::serialisers::SerialisedEntry create_unit()
35+
{
36+
return Unit::get();
37+
}
38+
39+
private:
40+
static Write deserialise_write(const ccf::kv::untyped::Write& w)
41+
{
42+
const auto it = w.find(Unit::get());
43+
if (it == w.end() || !it->second.has_value())
44+
{
45+
return std::nullopt;
46+
}
47+
48+
return Unit::get();
49+
}
50+
51+
public:
52+
static ccf::kv::untyped::CommitHook wrap_commit_hook(const CommitHook& hook)
53+
{
54+
return [hook](Version v, const ccf::kv::untyped::Write& w) {
55+
hook(v, deserialise_write(w));
56+
};
57+
}
58+
59+
static ccf::kv::untyped::MapHook wrap_map_hook(const MapHook& hook)
60+
{
61+
return [hook](Version v, const ccf::kv::untyped::Write& w) {
62+
return hook(v, deserialise_write(w));
63+
};
64+
}
65+
};
66+
}

include/ccf/kv/unit_value_handle.h

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the Apache 2.0 License.
3+
#pragma once
4+
5+
#include "ccf/kv/unit.h"
6+
#include "ccf/kv/untyped_map_handle.h"
7+
8+
namespace ccf::kv
9+
{
10+
/** Grants read access to a @c ccf::kv::UnitValue, as part of a @c
11+
* ccf::kv::Tx.
12+
*/
13+
template <typename Unit>
14+
class ReadableUnitValueHandle
15+
{
16+
protected:
17+
ccf::kv::untyped::MapHandle& read_handle;
18+
19+
public:
20+
ReadableUnitValueHandle(ccf::kv::untyped::MapHandle& uh) : read_handle(uh)
21+
{}
22+
23+
/** Test if the value has been touched.
24+
*
25+
* @return Boolean true iff the value is defined
26+
*/
27+
bool has()
28+
{
29+
return read_handle.has(Unit::get());
30+
}
31+
32+
/** Get version when this value was last written to, by a previous
33+
* transaction.
34+
*
35+
* @see ccf::kv::ReadableMapHandle::get_version_of_previous_write
36+
*
37+
* @return Optional containing version of applied transaction which last
38+
* wrote to this value, or nullopt if such a version does not exist
39+
*/
40+
std::optional<Version> get_version_of_previous_write()
41+
{
42+
return read_handle.get_version_of_previous_write(Unit::get());
43+
}
44+
};
45+
46+
/** Grants write access to a @c ccf::kv::UnitValue, as part of a @c
47+
* ccf::kv::Tx.
48+
*/
49+
template <typename Unit>
50+
class WriteableUnitValueHandle
51+
{
52+
protected:
53+
ccf::kv::untyped::MapHandle& write_handle;
54+
55+
public:
56+
WriteableUnitValueHandle(ccf::kv::untyped::MapHandle& uh) : write_handle(uh)
57+
{}
58+
59+
/** Touch this value.
60+
*
61+
* If this value was previously defined, it will be overwritten. Even if the
62+
* previous value was identical, this produces a serialised write in the
63+
* ledger.
64+
*/
65+
void touch()
66+
{
67+
write_handle.put(Unit::get(), Unit::get());
68+
}
69+
70+
/** Delete this value, restoring its original undefined state.
71+
*/
72+
void clear()
73+
{
74+
write_handle.clear();
75+
}
76+
};
77+
78+
/** Grants read and write access to a @c ccf::kv::UnitValue, as part of a @c
79+
* ccf::kv::Tx.
80+
*
81+
* @see ccf::kv::ReadableUnitValueHandle
82+
* @see ccf::kv::WriteableUnitValueHandle
83+
*/
84+
template <typename Unit>
85+
class UnitValueHandle : public AbstractHandle,
86+
public ReadableUnitValueHandle<Unit>,
87+
public WriteableUnitValueHandle<Unit>
88+
{
89+
protected:
90+
ccf::kv::untyped::MapHandle untyped_handle;
91+
92+
using ReadableBase = ReadableUnitValueHandle<Unit>;
93+
using WriteableBase = WriteableUnitValueHandle<Unit>;
94+
95+
public:
96+
UnitValueHandle(
97+
ccf::kv::untyped::ChangeSet& changes, const std::string& map_name) :
98+
ReadableBase(untyped_handle),
99+
WriteableBase(untyped_handle),
100+
untyped_handle(changes, map_name)
101+
{}
102+
};
103+
}

include/ccf/kv/version.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// Licensed under the Apache 2.0 License.
33
#pragma once
44

5+
#include <cstdint>
6+
57
namespace ccf::kv
68
{
79
// Version indexes modifications to the local kv store.

0 commit comments

Comments
 (0)