Skip to content

Commit ab1a11b

Browse files
services: add new service for fetching blocks from NeoFS
Close #3496 Co-authored-by: Anna Shaleva <shaleva.ann@nspcc.ru> Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
1 parent 705a775 commit ab1a11b

File tree

10 files changed

+753
-47
lines changed

10 files changed

+753
-47
lines changed

config/protocol.testnet.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,3 +100,15 @@ ApplicationConfiguration:
100100
Enabled: false
101101
Addresses:
102102
- ":2113"
103+
NeoFSBlockFetcher:
104+
Enabled: false
105+
Addresses:
106+
- st1.storage.fs.neo.org:8080
107+
Timeout: 10m
108+
DownloaderWorkersCount: 500
109+
OIDBatchSize: 8000
110+
BQueueSize: 32000 # must be larger than OIDBatchSize; highly recommended to be 2*OIDBatchSize or 3*OIDBatchSize
111+
SkipIndexFilesSearch: false
112+
ContainerID: "EPGuD26wYgQJbmDdVBoYoNZiMKHwFMJT3A5WqPjdUHxH"
113+
BlockAttribute: "block"
114+
IndexFileAttribute: "oid"

docs/neofs-blockstorage.md

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# NeoFS BlockStorage
2+
This service was proposed as a part of [ Using NeoFS to store blocks and snapshots #3463](https://github.com/neo-project/neo/issues/3463).
3+
It is an alternative to the P2P synchronization protocol by using NeoFS to
4+
store and retrieve blockchain blocks and snapshots. This improves
5+
synchronization efficiency, reduces local storage, and streamlines node
6+
operations.
7+
8+
## Components and functionality
9+
10+
### Storage schema
11+
A single container is used to store blocks and index files. Each block
12+
is stored as a separate object with a unique OID and a set of attributes:
13+
- block index
14+
- block primary index
15+
- size.
16+
17+
Index files contain OIDs of ordered blocks. Each index file is an object
18+
containing a constant number of OIDs in
19+
binary form, where every number represent the order of the index file.
20+
21+
Attributes have the following structure: `AttributeName:IntValue`.
22+
In the container are stored blocks with attributes `block:1`,
23+
`block:2`, etc. And index files with attributes `oid:1`, `oid:2`, etc.
24+
25+
### NeoFS BlockFetcher
26+
The NeoFS BlockFetcher service is designed as an alternative to P2P
27+
synchronisation protocol. It allows to fetch blocks, primarily used at
28+
the start of a node's lifecycle, providing an alternative to downloading
29+
blocks through the P2P NeoGo network. By default, the service is disabled.
30+
31+
The NeoFS BlockFetcher service has two modes of operation:
32+
- Index File Search: Search for index files, which contain OIDs of blocks,
33+
and fetch blocks from NeoFS by provided OIDs.
34+
- Direct Block Search: Search and fetch blocks directly from NeoFS container.
35+
36+
### BlockFetcher operation flow
37+
1. **OID Fetching**:
38+
Depending on the mode, the service either:
39+
- Searches for index files by attribute and reads blocks OIDs from it.
40+
- Searches batches of blocks directly by attributes.
41+
42+
Once the OIDs are retrieved, they are immediately redirected to the
43+
block downloading routines for further processing. The channel that
44+
is used to redirect block OIDs to downloading routines is buffered
45+
to provide smooth OIDs delivery without delays. Size of this channel
46+
can be configured via `OIDBatchSize` parameter.
47+
2. **Parallel Block Downloading**:
48+
The number of downloading routines can be configured via
49+
`DownloaderWorkersCount` parameter. It's up to the user to find the
50+
balance between the downloading speed and blocks persist speed for every
51+
node that uses NeoFS BlockFetcher. Downloaded blocks are placed into a
52+
buffer with a size of `BQueueSize` parameter for further processing.
53+
3. **Block Insertion**:
54+
Downloaded blocks are inserted into the blockchain using the same logic
55+
as in the P2P synchronisation protocol. The bqueue is used to store
56+
downloaded blocks before they are inserted into the blockchain. The
57+
size of the bqueue can be configured via the `BQueueSize` parameter
58+
and should be larger than the `OIDBatchSize` parameter to avoid blocking
59+
the downloading routines.
60+
61+
Once all blocks available in the NeoFS container are processed, the service
62+
shuts down automatically.
63+
64+
### NeoFS BlockFetcher Configuration
65+
`NeoFSBlockFetcher` configuration section contains settings for NeoFS
66+
BlockFetcher module and has the following structure:
67+
```
68+
NeoFSBlockFetcher:
69+
Enabled: true
70+
UnlockWallet:
71+
Path: "./wallet.json"
72+
Password: "pass"
73+
Addresses:
74+
- st1.storage.fs.neo.org:8080
75+
Timeout: 10m
76+
DownloaderWorkersCount: 500
77+
OIDBatchSize: 8000
78+
BQueueSize: 16000
79+
SkipIndexFilesSearch: false
80+
ContainerID: "EPGuD26wYgQJbmDdVBoYoNZiMKHwFMJT3A5WqPjdUHxH"
81+
BlockAttribute: "block"
82+
IndexFileAttribute: "oid"
83+
```
84+
where:
85+
- `Enabled` enables NeoFS BlockFetcher module.
86+
- `UnlockWallet` contains wallet settings to retrieve account to sign requests to
87+
NeoFS. Without this setting, the module will use randomly generated private key.
88+
For configuration details see [Unlock Wallet Configuration](https://github.com/nspcc-dev/neo-go/blob/master/docs/node-configuration.md#unlock-wallet-configuration)
89+
- `Addresses` is a list of NeoFS storage nodes addresses.
90+
- `Timeout` is a timeout for a single request to NeoFS storage node.
91+
- `ContainerID` is a container ID to fetch blocks from.
92+
- `BlockAttribute` is an attribute name of NeoFS object that contains block
93+
data.
94+
- `IndexFileAttribute` is an attribute name of NeoFS object that contains OIDs of
95+
blocks objects.
96+
- `DownloaderWorkersCount` is a number of workers that download blocks from
97+
NeoFS.
98+
- `OIDBatchSize` is the number of blocks to search per a single request to NeoFS
99+
in case of disabled index files search. Also, for both modes of BlockFetcher
100+
operation this setting manages the buffer size of OIDs and blocks transferring channels.
101+
- `BQueueSize` is a size of the block queue used to manage consecutive blocks
102+
addition to the chain. It must be larger than
103+
`OIDBatchSize` and highly recommended to be 2*`OIDBatchSize` or 3*`OIDBatchSize`.
104+
- `SkipIndexFilesSearch` is a flag that allows skipping index files search
105+
in NeoFS storage nodes and search for blocks directly. It is set to `false`
106+
by default.

docs/node-configuration.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ node-related settings described in the table below.
2121
| GarbageCollectionPeriod | `uint32` | 10000 | Controls MPT garbage collection interval (in blocks) for configurations with `RemoveUntraceableBlocks` enabled and `KeepOnlyLatestState` disabled. In this mode the node stores a number of MPT trees (corresponding to `MaxTraceableBlocks` and `StateSyncInterval`), but the DB needs to be clean from old entries from time to time. Doing it too often will cause too much processing overhead, doing it too rarely will leave more useless data in the DB. |
2222
| KeepOnlyLatestState | `bool` | `false` | Specifies if MPT should only store the latest state (or a set of latest states, see `P2PStateExchangeExtensions` section in the ProtocolConfiguration for details). If true, DB size will be smaller, but older roots won't be accessible. This value should remain the same for the same database. | |
2323
| LogPath | `string` | "", so only console logging | File path where to store node logs. |
24+
| NeoFSBlockFetcher | [NeoFSBlockFetcher Configuration](https://github.com/nspcc-dev/neo-go/blob/master/docs/neofs-blockstorage.md#NeoFSBlockFetcher-Configuration) | | NeoFSBlockFetcher module configuration. See the [NeoFSBlockFetcher Configuration](https://github.com/nspcc-dev/neo-go/blob/master/docs/neofs-blockstorage.md#NeoFSBlockFetcher-Configuration) section for details. |
2425
| Oracle | [Oracle Configuration](#Oracle-Configuration) | | Oracle module configuration. See the [Oracle Configuration](#Oracle-Configuration) section for details. |
2526
| P2P | [P2P Configuration](#P2P-Configuration) | | Configuration values for P2P network interaction. See the [P2P Configuration](#P2P-Configuration) section for details. |
2627
| P2PNotary | [P2P Notary Configuration](#P2P-Notary-Configuration) | | P2P Notary module configuration. See the [P2P Notary Configuration](#P2P-Notary-Configuration) section for details. |

pkg/config/application_config.go

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,13 @@ type ApplicationConfiguration struct {
2323
Pprof BasicService `yaml:"Pprof"`
2424
Prometheus BasicService `yaml:"Prometheus"`
2525

26-
Relay bool `yaml:"Relay"`
27-
Consensus Consensus `yaml:"Consensus"`
28-
RPC RPC `yaml:"RPC"`
29-
Oracle OracleConfiguration `yaml:"Oracle"`
30-
P2PNotary P2PNotary `yaml:"P2PNotary"`
31-
StateRoot StateRoot `yaml:"StateRoot"`
26+
Relay bool `yaml:"Relay"`
27+
Consensus Consensus `yaml:"Consensus"`
28+
RPC RPC `yaml:"RPC"`
29+
Oracle OracleConfiguration `yaml:"Oracle"`
30+
P2PNotary P2PNotary `yaml:"P2PNotary"`
31+
StateRoot StateRoot `yaml:"StateRoot"`
32+
NeoFSBlockFetcher NeoFSBlockFetcher `yaml:"NeoFSBlockFetcher"`
3233
}
3334

3435
// EqualsButServices returns true when the o is the same as a except for services
@@ -141,3 +142,13 @@ func (a *ApplicationConfiguration) GetAddresses() ([]AnnounceableAddress, error)
141142
}
142143
return addrs, nil
143144
}
145+
146+
// Validate checks ApplicationConfiguration for internal consistency and returns
147+
// an error if any invalid settings are found. This ensures that the application
148+
// configuration is valid and safe to use for further operations.
149+
func (a *ApplicationConfiguration) Validate() error {
150+
if err := a.NeoFSBlockFetcher.Validate(); err != nil {
151+
return fmt.Errorf("failed to validate NeoFSBlockFetcher section: %w", err)
152+
}
153+
return nil
154+
}

pkg/config/blockfetcher_config.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package config
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"time"
7+
8+
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
9+
)
10+
11+
// NeoFSBlockFetcher represents the configuration for the NeoFS BlockFetcher service.
12+
type NeoFSBlockFetcher struct {
13+
InternalService `yaml:",inline"`
14+
Timeout time.Duration `yaml:"Timeout"`
15+
ContainerID string `yaml:"ContainerID"`
16+
Addresses []string `yaml:"Addresses"`
17+
OIDBatchSize int `yaml:"OIDBatchSize"`
18+
BlockAttribute string `yaml:"BlockAttribute"`
19+
IndexFileAttribute string `yaml:"IndexFileAttribute"`
20+
DownloaderWorkersCount int `yaml:"DownloaderWorkersCount"`
21+
BQueueSize int `yaml:"BQueueSize"`
22+
SkipIndexFilesSearch bool `yaml:"SkipIndexFilesSearch"`
23+
}
24+
25+
// Validate checks NeoFSBlockFetcher for internal consistency and ensures
26+
// that all required fields are properly set. It returns an error if the
27+
// configuration is invalid or if the ContainerID cannot be properly decoded.
28+
func (cfg *NeoFSBlockFetcher) Validate() error {
29+
if !cfg.Enabled {
30+
return nil
31+
}
32+
if cfg.ContainerID == "" {
33+
return errors.New("container ID is not set")
34+
}
35+
var containerID cid.ID
36+
err := containerID.DecodeString(cfg.ContainerID)
37+
if err != nil {
38+
return fmt.Errorf("invalid container ID: %w", err)
39+
}
40+
if cfg.BQueueSize < cfg.OIDBatchSize {
41+
return fmt.Errorf("BQueueSize (%d) is lower than OIDBatchSize (%d)", cfg.BQueueSize, cfg.OIDBatchSize)
42+
}
43+
if len(cfg.Addresses) == 0 {
44+
return errors.New("addresses are not set")
45+
}
46+
return nil
47+
}

pkg/config/config.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,10 @@ func LoadFile(configPath string, relativePath ...string) (Config, error) {
116116
if err != nil {
117117
return Config{}, err
118118
}
119+
err = config.ApplicationConfiguration.Validate()
120+
if err != nil {
121+
return Config{}, err
122+
}
119123

120124
return config, nil
121125
}

0 commit comments

Comments
 (0)