Skip to content

Commit

Permalink
Merge branch 'master' into fix/reduce-candidates-timeout
Browse files Browse the repository at this point in the history
  • Loading branch information
kamilsa authored Nov 22, 2024
2 parents 1c6357f + 31c105e commit 3329435
Show file tree
Hide file tree
Showing 11 changed files with 55 additions and 148 deletions.
34 changes: 9 additions & 25 deletions core/api/service/author/impl/author_api_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,36 +107,20 @@ namespace kagome::api {
return encoded_session_keys;
}

// logic here is polkadot specific only!
// it could be extended by reading config from chainspec palletSession/keys
// value
outcome::result<bool> AuthorApiImpl::hasSessionKeys(const BufferView &keys) {
if (keys.size() < 32 || keys.size() > 32 * 6 || (keys.size() % 32) != 0) {
SL_WARN(logger_,
"not valid key sequence, author_hasSessionKeys RPC call expects "
"no more than 6 public keys in concatenated string, keys should "
"be 32 byte in size");
OUTCOME_TRY(decoded,
keys_api_->decode_session_keys(
block_tree_.get()->bestBlock().hash, keys));
if (not decoded) {
return false;
}
scale::ScaleDecoderStream stream(keys);
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init,hicpp-member-init)
std::array<uint8_t, 32> key;
stream >> key;
if (store_->ed25519().findKeypair(
crypto::KeyTypes::GRANDPA,
crypto::Ed25519PublicKey(common::Blob<32>(key)))) {
auto it = crypto::polkadot_key_order.begin();
while (stream.currentIndex() < keys.size()) {
++it;
stream >> key;
if (not store_->sr25519().findKeypair(
*it, crypto::Sr25519PublicKey(common::Blob<32>(key)))) {
return false;
}
for (auto &[key, type] : *decoded) {
OUTCOME_TRY(has, key_store_->searchForKey(type, key));
if (not has) {
return false;
}
return true;
}
return false;
return true;
}

outcome::result<bool> AuthorApiImpl::hasKey(const BufferView &public_key,
Expand Down
7 changes: 3 additions & 4 deletions core/runtime/runtime_api/impl/session_keys_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,13 @@ namespace kagome::runtime {
ctx, "SessionKeys_generate_session_keys", seed);
}

outcome::result<std::vector<std::pair<crypto::KeyType, common::Buffer>>>
outcome::result<SessionKeysApi::DecodeSessionKeysResult>
SessionKeysApiImpl::decode_session_keys(
const primitives::BlockHash &block_hash,
common::BufferView encoded) const {
OUTCOME_TRY(ctx, executor_->ctx().ephemeralAt(block_hash));
return executor_
->call<std::vector<std::pair<crypto::KeyType, common::Buffer>>>(
ctx, "SessionKeys_decode_session_keys", encoded);
return executor_->call<SessionKeysApi::DecodeSessionKeysResult>(
ctx, "SessionKeys_decode_session_keys", encoded);
}

} // namespace kagome::runtime
6 changes: 3 additions & 3 deletions core/runtime/runtime_api/impl/session_keys_api.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ namespace kagome::runtime {
const primitives::BlockHash &block_hash,
std::optional<common::Buffer> seed) override;

outcome::result<std::vector<std::pair<crypto::KeyType, common::Buffer>>>
decode_session_keys(const primitives::BlockHash &block_hash,
common::BufferView encoded) const override;
outcome::result<DecodeSessionKeysResult> decode_session_keys(
const primitives::BlockHash &block_hash,
common::BufferView encoded) const override;

private:
std::shared_ptr<Executor> executor_;
Expand Down
9 changes: 5 additions & 4 deletions core/runtime/runtime_api/session_keys_api.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,15 @@ namespace kagome::runtime {
const primitives::BlockHash &block_hash,
std::optional<common::Buffer> seed) = 0;

using DecodeSessionKeysResult =
std::optional<std::vector<std::pair<Buffer, crypto::KeyType>>>;
/**
* @brief Decode the given public session keys.
* @return the list of public raw public keys + key type.
*/
virtual outcome::result<
std::vector<std::pair<crypto::KeyType, common::Buffer>>>
decode_session_keys(const primitives::BlockHash &block_hash,
common::BufferView encoded) const = 0;
virtual outcome::result<DecodeSessionKeysResult> decode_session_keys(
const primitives::BlockHash &block_hash,
common::BufferView encoded) const = 0;
};

} // namespace kagome::runtime
2 changes: 1 addition & 1 deletion docs/source/development/dev-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ private:
};

// 3. instead, create an interface for "Hasher":
// "Interface" leyer
// "Interface" layer
class Hasher {
public:
virtual ~Hasher() = default;
Expand Down
10 changes: 5 additions & 5 deletions docs/source/development/rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ All used terms are described in [terms.md](./terms.md).
6. **major design rule** - avoid using preprocessor macros in header files except as include guards.
7. **major design rule** - only classes, structures, unions and free operator functions should be *declared* at file scope in `.h` file. Only classes, structures , unions, and inline (member or free operator) functions should be *defined* at file scope in a `.h` file.
7. **major design rule** - only classes, structures, unions and free operator functions should be *declared* at file scope in `.h` file. Only classes, structures, unions, and inline (member or free operator) functions should be *defined* at file scope in a `.h` file.
8. **major design rule** - place a unique and predictable (internal) include guard around the contents of each header file.
Expand Down Expand Up @@ -99,11 +99,11 @@ All used terms are described in [terms.md](./terms.md).
Example: defining an iterator class along with a container class in the same component enables user extensibility, improves maintainability and enhances reusability while preserving encapsulation.
21. **principle** - all tests must be done in isolation. Testing a component in isolation is an effective way to ensure reliability.
21. **principle** - all tests must be done in isolation. Testing a component in isolation is an effective way to ensure reliability.
22. **principle** - every directed acyclic graph can be assigned unique level numbers; a graph with cycles cannot. A physical dependency graph that can be assigned unique level numbers is said to be *levelizable*.
23. **principle** - in most real-world situations, large designs must be levelizable if they are able to be tested effectively.
23. **principle** - in most real-world situations, large designs must be levelizable if they are able to be tested effectively.
24. **principle** - testing only the functionality *directly implemented* within a component enables the complexity of the test to be proportional to the complexity of the component.
Expand All @@ -117,7 +117,7 @@ All used terms are described in [terms.md](./terms.md).
29. **major design rule** - prepend every global identifier with its package prefix.
30. **major design rule** - avoid cyclic dependencies among packages.
30. **major design rule** - avoid cyclic dependencies among packages.
31. **guideline** - avoid declaring results returned by value from functions as `const`.
Expand All @@ -129,7 +129,7 @@ All used terms are described in [terms.md](./terms.md).
35. **guideline** - explicitly declare (public or private) the constructor and assignment operator for any class defined in a header file.
36. **minor design rule** - in every class that declares or is derived from a class that declares a virtual function, explicitly declare the destructor as the first virtual function in the class and define it out of line.
36. **minor design rule** - in every class that declares or is derived from a class that declares a virtual function, explicitly declare the destructor as the first virtual function in the class and define it out of line.
[^1]: "John Lakos - Large Scale C++ Software Design".
4 changes: 2 additions & 2 deletions docs/source/overview/getting_started.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ git submodule update --init --recursive

### Build

First build will likely take long time. However, you can cache binaries to [hunter-binary-cache](https://github.com/soramitsu/hunter-binary-cache) or even download binaries from the cache in case someone has already compiled project with the same compiler. To this end, you need to set up two environment variables:
First build will likely take a long time. However, you can cache binaries to [hunter-binary-cache](https://github.com/soramitsu/hunter-binary-cache) or even download binaries from the cache in case someone has already compiled project with the same compiler. To this end, you need to set up two environment variables:
```
GITHUB_HUNTER_USERNAME=<github account name>
GITHUB_HUNTER_TOKEN=<github token>
Expand All @@ -51,7 +51,7 @@ ctest

#### Build node application

If you'd like to build node use the following instruction
If you'd like to build node use the following instructions

```
mkdir build && cd build
Expand Down
4 changes: 2 additions & 2 deletions docs/source/tutorials/kagome_in_docker.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ All Rights Reserved
SPDX-License-Identifier: Apache-2.0
)

## Runing Kagome in docker container
## Running Kagome in docker container

In this tutorial you will learn how to execute Kagome-based Polkadot-host chain which can be used as a cryptocurrency, and interact with it by sending extrinsics and executing queries.

Expand All @@ -31,7 +31,7 @@ docker run \ # Run docker container
--interactive \ # Interactive mode
--tty \ # Allocate pseudo-TTY
--rm \ # Remove container (not image) after stop
--user `id -u`:`id -g` \ # Delegate current user permitions in container
--user `id -u`:`id -g` \ # Delegate current user permissions in container
--volume `pwd`:/kagome \ # Map current directory to directory in container
--publish DOCKER_PORT:HOST_PORT \ # Bind ports
soramitsu/kagome \ # Name of docker image of Kagome
Expand Down
2 changes: 1 addition & 1 deletion docs/source/tutorials/private_network.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ Once activated, the second node performs several steps before beginning the actu
### Step 4: Start syncing node

Syncing node cannot participate in block production or block finalization. However, it can connect to the network and import all produced blocks. Besides that, syncing node can also receive extrinsics and broadcast them to the network.In the world of blockchain, a syncing node plays a unique role. While it can't contribute to block production or finalization, it serves as a connector to the network, importing all produced blocks. More than that, a syncing node can receive extrinsics and subsequently broadcast them to the network.
Syncing node cannot participate in block production or block finalization. However, it can connect to the network and import all produced blocks. Besides that, syncing node can also receive extrinsics and broadcast them to the network. In the world of blockchain, a syncing node plays a unique role. While it can't contribute to block production or finalization, it serves as a connector to the network, importing all produced blocks. More than that, a syncing node can receive extrinsics and subsequently broadcast them to the network.

To initiate a syncing node, you can use the binary `kagome` as outlined in the command below:

Expand Down
121 changes: 23 additions & 98 deletions test/core/api/service/author/author_api_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ using kagome::application::AppConfigurationMock;
using kagome::blockchain::BlockTree;
using kagome::blockchain::BlockTreeMock;
using kagome::crypto::KeyType;
using kagome::crypto::KeyTypes;
using kagome::network::TransactionsTransmitterMock;
using kagome::primitives::BlockId;
using kagome::primitives::BlockInfo;
Expand Down Expand Up @@ -111,7 +112,7 @@ struct AuthorApiTest : public ::testing::Test {
sptr<SessionKeys> keys;
sptr<KeyFileStorage> key_store;
Sr25519Keypair key_pair;
sptr<SessionKeysApi> key_api;
sptr<SessionKeysApiMock> key_api;
sptr<TransactionPoolMock> transaction_pool;
sptr<ApiServiceMock> api_service_mock;
sptr<AuthorApiImpl> author_api;
Expand Down Expand Up @@ -267,125 +268,49 @@ TEST_F(AuthorApiTest, InsertKeyGran) {
}

/**
* @given empty keys sequence
* @given invalid keys
* @when hasSessionKeys called
* @then call succeeds, false result
* NOTE could be special error
*/
TEST_F(AuthorApiTest, HasSessionKeysEmpty) {
Buffer keys;
EXPECT_OUTCOME_SUCCESS(res, author_api->hasSessionKeys(keys));
TEST_F(AuthorApiTest, HasSessionKeysInvalid) {
EXPECT_CALL(*key_api, decode_session_keys(_, _))
.WillOnce(Return(std::nullopt));
EXPECT_OUTCOME_SUCCESS(res, author_api->hasSessionKeys({}));
EXPECT_EQ(res.value(), false);
}

/**
* @given keys sequence less than 1 key
* @given keystore contains some keys
* @when hasSessionKeys called
* @then call succeeds, false result
* NOTE could be special error
*/
TEST_F(AuthorApiTest, HasSessionKeysLessThanOne) {
Buffer keys;
keys.resize(31);
EXPECT_OUTCOME_SUCCESS(res, author_api->hasSessionKeys(keys));
TEST_F(AuthorApiTest, HasSessionKeysSome) {
EXPECT_CALL(*key_api, decode_session_keys(_, _))
.WillOnce(Return(std::vector{
std::make_pair(Buffer{key_pair.public_key}, KeyTypes::BABE),
std::make_pair(Buffer{}, KeyTypes::AURA),
}));
EXPECT_OUTCOME_SUCCESS(res, author_api->hasSessionKeys({}));
EXPECT_EQ(res.value(), false);
}

/**
* @given keys sequence greater than 6 keys
* @given keystore contains all keys
* @when hasSessionKeys called
* @then call succeeds, false result
* NOTE could be special error
*/
TEST_F(AuthorApiTest, HasSessionKeysOverload) {
Buffer keys;
keys.resize(32 * 6 + 1);
EXPECT_OUTCOME_SUCCESS(res, author_api->hasSessionKeys(keys));
EXPECT_EQ(res.value(), false);
}

/**
* @given keys sequence not equal to n*32 in size
* @when hasSessionKeys called
* @then call succeeds, false result
* NOTE could be special error
*/
TEST_F(AuthorApiTest, HasSessionKeysNotEqualKeys) {
Buffer keys;
keys.resize(32 * 5 + 1);
EXPECT_OUTCOME_SUCCESS(res, author_api->hasSessionKeys(keys));
EXPECT_EQ(res.value(), false);
}

/**
* @given keys sequence of 6 keys
* @when hasSessionKeys called, all keys found
* @then call succeeds, true result
*/
TEST_F(AuthorApiTest, HasSessionKeysSuccess6Keys) {
Buffer keys;
keys.resize(32 * 6);
const Ed25519Keypair edOk{};
const Sr25519Keypair srOk{};
InSequence s;
EXPECT_CALL(store->ed25519(), findKeypair(KeyTypes::GRANDPA, _))
.Times(1)
.WillOnce(Return(OptRef(edOk)));
EXPECT_CALL(store->sr25519(), findKeypair(KeyTypes::BABE, _))
.Times(1)
.WillOnce(Return(OptRef(srOk)));
EXPECT_CALL(store->sr25519(), findKeypair(KeyTypes::IM_ONLINE, _))
.Times(1)
.WillOnce(Return(OptRef(srOk)));
EXPECT_CALL(store->sr25519(), findKeypair(KeyTypes::PARACHAIN, _))
.Times(1)
.WillOnce(Return(OptRef(srOk)));
EXPECT_CALL(store->sr25519(), findKeypair(KeyTypes::ASSIGNMENT, _))
.Times(1)
.WillOnce(Return(OptRef(srOk)));
EXPECT_CALL(store->sr25519(), findKeypair(KeyTypes::AUTHORITY_DISCOVERY, _))
.Times(1)
.WillOnce(Return(OptRef(srOk)));
EXPECT_OUTCOME_SUCCESS(res, author_api->hasSessionKeys(keys));
EXPECT_EQ(res.value(), true);
}

/**
* @given keys sequence of 1 key
* @when hasSessionKeys called, all keys found
* @then call succeeds, true result
* NOTE could be special error
*/
TEST_F(AuthorApiTest, HasSessionKeysSuccess1Keys) {
Buffer keys;
keys.resize(32);
Ed25519Keypair edOk{};
EXPECT_CALL(store->ed25519(), findKeypair(KeyTypes::GRANDPA, _))
.Times(1)
.WillOnce(Return(edOk));
EXPECT_OUTCOME_SUCCESS(res, author_api->hasSessionKeys(keys));
TEST_F(AuthorApiTest, HasSessionKeysAll) {
EXPECT_CALL(*key_api, decode_session_keys(_, _))
.WillOnce(Return(std::vector{
std::make_pair(Buffer{key_pair.public_key}, KeyTypes::BABE),
}));
EXPECT_OUTCOME_SUCCESS(res, author_api->hasSessionKeys({}));
EXPECT_EQ(res.value(), true);
}

/**
* @given keys sequence of 6 keys
* @when hasSessionKeys called, 1 key not found
* @then call succeeds, false result
*/
TEST_F(AuthorApiTest, HasSessionKeysFailureNotFound) {
Buffer keys;
keys.resize(32 * 6);
Ed25519Keypair edOk{};
auto srErr = std::nullopt;
EXPECT_CALL(store->ed25519(), findKeypair(_, _))
.Times(1)
.WillOnce(Return(edOk));
EXPECT_CALL(store->sr25519(), findKeypair(_, _))
.Times(1)
.WillOnce(Return(srErr));
EXPECT_OUTCOME_SUCCESS(res, author_api->hasSessionKeys(keys));
EXPECT_EQ(res.value(), false);
}

/**
* @given pub_key and type
* @when hasKey called, 1 key found
Expand Down
4 changes: 1 addition & 3 deletions test/mock/core/runtime/session_keys_api_mock.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@ namespace kagome::runtime {
(const primitives::BlockHash &, std::optional<common::Buffer>),
(override));

using TypedKey = std::pair<crypto::KeyType, common::Buffer>;

MOCK_METHOD(outcome::result<std::vector<TypedKey>>,
MOCK_METHOD(outcome::result<DecodeSessionKeysResult>,
decode_session_keys,
(const primitives::BlockHash &, common::BufferView),
(const, override));
Expand Down

0 comments on commit 3329435

Please sign in to comment.