Kagome
Polkadot Runtime Engine in C++17
author_api_impl.cpp
Go to the documentation of this file.
1 
7 
8 #include <jsonrpc-lean/fault.h>
9 #include <boost/assert.hpp>
10 #include <boost/system/error_code.hpp>
11 #include <stdexcept>
12 
15 #include "crypto/crypto_store.hpp"
20 #include "crypto/hasher.hpp"
23 #include "scale/scale_decoder_stream.hpp"
26 
27 namespace kagome::api {
28  const std::vector<crypto::KnownKeyTypeId> kKeyTypes{
32  };
33 
39  sptr<blockchain::BlockTree> block_tree)
40  : keys_api_(std::move(key_api)),
41  pool_{std::move(pool)},
42  store_{std::move(store)},
43  keys_{std::move(keys)},
44  key_store_{std::move(key_store)},
45  block_tree_{std::move(block_tree)},
46  logger_{log::createLogger("AuthorApi", "author_api")} {
47  BOOST_ASSERT_MSG(keys_api_ != nullptr, "session keys api is nullptr");
48  BOOST_ASSERT_MSG(pool_ != nullptr, "transaction pool is nullptr");
49  BOOST_ASSERT_MSG(store_ != nullptr, "crypto store is nullptr");
50  BOOST_ASSERT_MSG(keys_ != nullptr, "session keys store is nullptr");
51  BOOST_ASSERT_MSG(key_store_ != nullptr, "key store is nullptr");
52  BOOST_ASSERT_MSG(block_tree_ != nullptr, "block tree is nullptr");
53  BOOST_ASSERT_MSG(logger_ != nullptr, "logger is nullptr");
54  }
55 
57  std::shared_ptr<api::ApiService> const &api_service) {
58  BOOST_ASSERT(api_service != nullptr);
59  api_service_ = api_service;
60  }
61 
62  outcome::result<common::Hash256> AuthorApiImpl::submitExtrinsic(
64  const primitives::Extrinsic &extrinsic) {
65  return pool_->submitExtrinsic(source, extrinsic);
66  }
67 
68  outcome::result<void> AuthorApiImpl::insertKey(
69  crypto::KeyTypeId key_type,
70  const gsl::span<const uint8_t> &seed,
71  const gsl::span<const uint8_t> &public_key) {
72  if (std::find(kKeyTypes.begin(), kKeyTypes.end(), key_type)
73  == kKeyTypes.end()) {
74  std::string types;
75  for (auto &type : kKeyTypes) {
76  types.append(encodeKeyTypeIdToStr(type));
77  types.push_back(' ');
78  }
79  types.pop_back();
80  SL_INFO(logger_, "Unsupported key type, only [{}] are accepted", types);
81  return outcome::failure(crypto::CryptoStoreError::UNSUPPORTED_KEY_TYPE);
82  };
83  if (crypto::KEY_TYPE_BABE == key_type && keys_->getBabeKeyPair()) {
84  SL_INFO(logger_, "Babe key already exists and won't be replaced");
85  return outcome::failure(crypto::CryptoStoreError::BABE_ALREADY_EXIST);
86  }
87  if (crypto::KEY_TYPE_GRAN == key_type && keys_->getGranKeyPair()) {
88  SL_INFO(logger_, "Grandpa key already exists and won't be replaced");
89  return outcome::failure(crypto::CryptoStoreError::GRAN_ALREADY_EXIST);
90  }
91  if (crypto::KEY_TYPE_AUDI == key_type && keys_->getAudiKeyPair()) {
92  SL_INFO(logger_,
93  "Authority discovery key already exists and won't be replaced");
94  return outcome::failure(crypto::CryptoStoreError::AUDI_ALREADY_EXIST);
95  }
96  if (crypto::KEY_TYPE_BABE == key_type
97  or crypto::KEY_TYPE_AUDI == key_type) {
98  OUTCOME_TRY(seed_typed, crypto::Sr25519Seed::fromSpan(seed));
99  OUTCOME_TRY(public_key_typed,
100  crypto::Sr25519PublicKey::fromSpan(public_key));
101  OUTCOME_TRY(keypair,
102  store_->generateSr25519Keypair(key_type, seed_typed));
103  if (public_key_typed != keypair.public_key) {
104  return outcome::failure(crypto::CryptoStoreError::WRONG_PUBLIC_KEY);
105  }
106  }
107  if (crypto::KEY_TYPE_GRAN == key_type) {
108  OUTCOME_TRY(seed_typed, crypto::Ed25519Seed::fromSpan(seed));
109  OUTCOME_TRY(public_key_typed,
110  crypto::Ed25519PublicKey::fromSpan(public_key));
111  OUTCOME_TRY(
112  keypair,
113  store_->generateEd25519Keypair(crypto::KEY_TYPE_GRAN, seed_typed));
114  if (public_key_typed != keypair.public_key) {
115  return outcome::failure(crypto::CryptoStoreError::WRONG_PUBLIC_KEY);
116  }
117  }
118  auto res = key_store_->saveKeyPair(key_type, public_key, seed);
119  // explicitly load keys from store to cache
120  keys_->getBabeKeyPair();
121  keys_->getGranKeyPair();
122  keys_->getAudiKeyPair();
123  return res;
124  }
125 
126  outcome::result<common::Buffer> AuthorApiImpl::rotateKeys() {
127  OUTCOME_TRY(encoded_session_keys,
128  keys_api_->generate_session_keys(
129  block_tree_->deepestLeaf().hash, std::nullopt));
130  return std::move(encoded_session_keys);
131  }
132 
133  // logic here is polkadot specific only!
134  // it could be extended by reading config from chainspec palletSession/keys
135  // value
136  outcome::result<bool> AuthorApiImpl::hasSessionKeys(
137  const gsl::span<const uint8_t> &keys) {
138  scale::ScaleDecoderStream stream(keys);
139  std::array<uint8_t, 32> key;
140  if (keys.size() < 32 || keys.size() > 32 * 6 || (keys.size() % 32) != 0) {
141  SL_WARN(logger_,
142  "not valid key sequence, author_hasSessionKeys RPC call expects "
143  "no more than 6 public keys in concatenated string, keys should "
144  "be 32 byte in size");
145  return false;
146  }
147  stream >> key;
148  if (store_->findEd25519Keypair(
150  crypto::Ed25519PublicKey(common::Blob<32>(key)))) {
151  unsigned count = 1;
152  while (stream.currentIndex() < keys.size()) {
153  stream >> key;
154  if (not store_->findSr25519Keypair(
156  crypto::Sr25519PublicKey(common::Blob<32>(key)))) {
157  return false;
158  }
159  }
160  return true;
161  }
162  return false;
163  }
164 
165  outcome::result<bool> AuthorApiImpl::hasKey(
166  const gsl::span<const uint8_t> &public_key, crypto::KeyTypeId key_type) {
167  auto res = key_store_->searchForSeed(key_type, public_key);
168  if (not res)
169  return res.error();
170  else
171  return res.value() ? true : false;
172  }
173 
174  outcome::result<std::vector<primitives::Extrinsic>>
176  auto &pending_txs = pool_->getPendingTransactions();
177 
178  std::vector<primitives::Extrinsic> result;
179  result.reserve(pending_txs.size());
180 
181  std::transform(pending_txs.begin(),
182  pending_txs.end(),
183  std::back_inserter(result),
184  [](auto &it) { return it.second->ext; });
185 
186  return result;
187  }
188 
189  outcome::result<std::vector<primitives::Extrinsic>>
191  const std::vector<primitives::ExtrinsicKey> &keys) {
192  BOOST_ASSERT_MSG(false, "not implemented"); // NOLINT
193  return outcome::failure(boost::system::error_code{});
194  }
195 
196  outcome::result<AuthorApi::SubscriptionId>
198  if (auto service = api_service_.lock()) {
199  OUTCOME_TRY(
200  tx,
201  pool_->constructTransaction(TransactionSource::External, extrinsic));
202  OUTCOME_TRY(sub_id, service->subscribeForExtrinsicLifecycle(tx.hash));
203  OUTCOME_TRY(tx_hash,
205  // submit and watch could be executed only
206  // from RPC call, so External source is chosen
207  TransactionSource::External,
208  extrinsic));
209  BOOST_ASSERT(tx_hash == tx.hash);
210 
211  SL_DEBUG(logger_, "Subscribe for ex hash={}", tx_hash);
212 
213  return sub_id;
214  }
215 
216  throw jsonrpc::InternalErrorFault(
217  "Internal error. Api service not initialized.");
218  }
219 
220  outcome::result<bool> AuthorApiImpl::unwatchExtrinsic(SubscriptionId sub_id) {
221  if (auto service = api_service_.lock()) {
222  return service->unsubscribeFromExtrinsicLifecycle(sub_id);
223  }
224  throw jsonrpc::InternalErrorFault(
225  "Internal error. Api service not initialized.");
226  }
227 
228 } // namespace kagome::api
outcome::result< bool > hasSessionKeys(const gsl::span< const uint8_t > &keys) override
checks if the keystore has private keys for the given session public keys
std::weak_ptr< api::ApiService > api_service_
outcome::result< bool > hasKey(const gsl::span< const uint8_t > &public_key, crypto::KeyTypeId key_type) override
checks if the keystore has private keys for the given public key and key type
outcome::result< common::Hash256 > submitExtrinsic(TransactionSource source, const primitives::Extrinsic &extrinsic) override
validates and sends extrinsic to transaction pool
STL namespace.
sptr< runtime::SessionKeysApi > keys_api_
uint32_t KeyTypeId
Key type identifier.
Definition: key_type.hpp:21
outcome::result< std::vector< primitives::Extrinsic > > pendingExtrinsics() override
sptr< crypto::KeyFileStorage > key_store_
void setApiService(sptr< api::ApiService > const &api_service) override
AuthorApiImpl(sptr< runtime::SessionKeysApi > key_api, sptr< transaction_pool::TransactionPool > pool, sptr< crypto::CryptoStore > store, sptr< crypto::SessionKeys > keys, sptr< crypto::KeyFileStorage > key_store, sptr< blockchain::BlockTree > block_tree)
sptr< transaction_pool::TransactionPool > pool_
outcome::result< SubscriptionId > submitAndWatchExtrinsic(Extrinsic extrinsic) override
constexpr KnownKeyTypeId polkadot_key_order[6]
std::string encodeKeyTypeIdToStr(KeyTypeId key_type_id)
makes string representation of KeyTypeId
Definition: key_type.cpp:29
sptr< blockchain::BlockTree > block_tree_
primitives::SubscriptionId SubscriptionId
Definition: author_api.hpp:30
Extrinsic class represents extrinsic.
Definition: extrinsic.hpp:24
Logger createLogger(const std::string &tag)
Definition: logger.cpp:112
outcome::result< common::Buffer > rotateKeys() override
Generate new session keys and returns the corresponding public keys.
outcome::result< std::vector< primitives::Extrinsic > > removeExtrinsic(const std::vector< primitives::ExtrinsicKey > &keys) override
std::shared_ptr< T > sptr
sptr< crypto::CryptoStore > store_
outcome::result< void > insertKey(crypto::KeyTypeId key_type, const gsl::span< const uint8_t > &seed, const gsl::span< const uint8_t > &public_key) override
insert an anonimous key pair into the keystore
outcome::result< bool > unwatchExtrinsic(SubscriptionId subscription_id) override
sptr< crypto::SessionKeys > keys_
const std::vector< crypto::KnownKeyTypeId > kKeyTypes