From a9841f13f3b91215f7ff0221162c061a5e7ab2df Mon Sep 17 00:00:00 2001 From: battlmonstr Date: Tue, 24 Dec 2024 14:03:50 +0100 Subject: [PATCH] DomainPutQuery, DomainGetLatestQuery, DomainDeleteQuery, LocalState update --- .../db/datastore/kvdb/big_endian_codec.cpp | 34 ++++ .../db/datastore/kvdb/big_endian_codec.hpp | 35 +++++ silkworm/db/datastore/kvdb/codec.hpp | 47 ++++++ silkworm/db/datastore/kvdb/domain.hpp | 145 ++++++++++++++++++ silkworm/db/datastore/kvdb/history.hpp | 44 ++++++ silkworm/db/datastore/kvdb/inverted_index.hpp | 33 ++++ silkworm/db/datastore/kvdb/kvts_codec.hpp | 142 +++++++++++++++++ silkworm/db/datastore/kvdb/raw_codec.hpp | 52 +++++++ .../db/datastore/snapshots/common/codec.hpp | 4 + .../snapshots/segment/kv_segment_test.cpp | 2 +- .../snapshots/test_util/string_codec.hpp | 2 +- silkworm/db/state/account_codecs.hpp | 20 +++ silkworm/db/state/accounts_domain.hpp | 4 + silkworm/db/state/address_codecs.hpp | 13 ++ silkworm/db/state/code_domain.hpp | 5 + silkworm/db/state/commitment_domain.hpp | 5 + silkworm/db/state/receipts_domain.hpp | 5 + silkworm/db/state/storage_codecs.cpp | 33 ++++ silkworm/db/state/storage_codecs.hpp | 36 +++++ silkworm/db/state/storage_domain.hpp | 4 + silkworm/execution/local_state.cpp | 86 ++++++++--- silkworm/execution/local_state.hpp | 46 +++--- silkworm/execution/state_factory.cpp | 2 +- 23 files changed, 755 insertions(+), 44 deletions(-) create mode 100644 silkworm/db/datastore/kvdb/big_endian_codec.cpp create mode 100644 silkworm/db/datastore/kvdb/big_endian_codec.hpp create mode 100644 silkworm/db/datastore/kvdb/codec.hpp create mode 100644 silkworm/db/datastore/kvdb/kvts_codec.hpp create mode 100644 silkworm/db/datastore/kvdb/raw_codec.hpp create mode 100644 silkworm/db/state/storage_codecs.cpp diff --git a/silkworm/db/datastore/kvdb/big_endian_codec.cpp b/silkworm/db/datastore/kvdb/big_endian_codec.cpp new file mode 100644 index 0000000000..f22c36a368 --- /dev/null +++ b/silkworm/db/datastore/kvdb/big_endian_codec.cpp @@ -0,0 +1,34 @@ +/* + Copyright 2024 The Silkworm Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#include "big_endian_codec.hpp" + +#include + +namespace silkworm::datastore::kvdb { + +Slice BigEndianU64Codec::encode() { + data.resize(sizeof(uint64_t), 0); + endian::store_big_u64(data.data(), value); + return to_slice(data); +} + +void BigEndianU64Codec::decode(Slice slice) { + SILKWORM_ASSERT(slice.size() >= sizeof(uint64_t)); + value = endian::load_big_u64(static_cast(slice.data())); +} + +} // namespace silkworm::datastore::kvdb diff --git a/silkworm/db/datastore/kvdb/big_endian_codec.hpp b/silkworm/db/datastore/kvdb/big_endian_codec.hpp new file mode 100644 index 0000000000..d028973a70 --- /dev/null +++ b/silkworm/db/datastore/kvdb/big_endian_codec.hpp @@ -0,0 +1,35 @@ +/* + Copyright 2024 The Silkworm Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#pragma once + +#include "codec.hpp" + +namespace silkworm::datastore::kvdb { + +struct BigEndianU64Codec : public Codec { + uint64_t value{0}; + Bytes data; + + ~BigEndianU64Codec() override = default; + Slice encode() override; + void decode(Slice slice) override; +}; + +static_assert(EncoderConcept); +static_assert(DecoderConcept); + +} // namespace silkworm::datastore::kvdb diff --git a/silkworm/db/datastore/kvdb/codec.hpp b/silkworm/db/datastore/kvdb/codec.hpp new file mode 100644 index 0000000000..4526313128 --- /dev/null +++ b/silkworm/db/datastore/kvdb/codec.hpp @@ -0,0 +1,47 @@ +/* + Copyright 2024 The Silkworm Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#pragma once + +#include "mdbx.hpp" + +namespace silkworm::datastore::kvdb { + +struct Encoder { + virtual ~Encoder() = default; + virtual Slice encode() = 0; +}; + +template +concept EncoderConcept = + std::derived_from && + requires(TEncoder encoder) { encoder.value; }; + +struct Decoder { + virtual ~Decoder() = default; + virtual void decode(Slice slice) = 0; +}; + +template +concept DecoderConcept = + std::derived_from && + requires(TDecoder decoder) { decoder.value; }; + +struct Codec : public Encoder, public Decoder { + ~Codec() override = default; +}; + +} // namespace silkworm::datastore::kvdb diff --git a/silkworm/db/datastore/kvdb/domain.hpp b/silkworm/db/datastore/kvdb/domain.hpp index 20ac81d35f..7c2dbe2878 100644 --- a/silkworm/db/datastore/kvdb/domain.hpp +++ b/silkworm/db/datastore/kvdb/domain.hpp @@ -18,7 +18,11 @@ #include +#include "../common/step.hpp" +#include "big_endian_codec.hpp" +#include "codec.hpp" #include "history.hpp" +#include "kvts_codec.hpp" #include "mdbx.hpp" namespace silkworm::datastore::kvdb { @@ -29,4 +33,145 @@ struct Domain { std::optional history; }; +struct InvertedStepCodec : public Codec { + Step value{0}; + BigEndianU64Codec codec; + static constexpr size_t kEncodedSize = sizeof(decltype(BigEndianU64Codec::value)); + + ~InvertedStepCodec() override = default; + + Slice encode() override { + codec.value = ~value.value; + return codec.encode(); + } + + void decode(Slice slice) override { + codec.decode(slice); + value = Step(~codec.value); + } +}; + +static_assert(EncoderConcept); +static_assert(DecoderConcept); + +template +using DomainKeyEncoder = KVTSKeyEncoder; + +template +using DomainValueEncoder = KVTSValueEncoder; + +template +using DomainKeyDecoder = KVTSKeyDecoder; + +template +using DomainValueDecoder = KVTSValueDecoder; + +template +struct DomainPutLatestQuery { + RWTxn& tx; + Domain entity; + + using TKey = decltype(TKeyEncoder::value); + using TValue = decltype(TValueEncoder::value); + + void exec(const TKey& key, const TValue& value, Step step) { + DomainKeyEncoder key_encoder{entity.has_large_values}; + key_encoder.value.key.value = key; + key_encoder.value.timestamp.value = step; + + DomainValueEncoder value_encoder{entity.has_large_values}; + value_encoder.value.value.value = value; + value_encoder.value.timestamp.value = step; + + tx.rw_cursor(entity.values_table)->insert(key_encoder.encode(), value_encoder.encode()); + } +}; + +template +struct DomainGetLatestQuery { + RWTxn& tx; + Domain entity; + + using TKey = decltype(TKeyEncoder::value); + using TValue = decltype(TValueDecoder::value); + + struct Result { + TValue value; + Step step; + }; + + std::optional exec(const TKey& key) { + DomainKeyEncoder key_encoder{/* has_large_values = */ false}; + key_encoder.value.key.value = key; + Slice key_slice = key_encoder.encode(); + + auto result = tx.ro_cursor(entity.values_table)->lower_bound(key_slice, false); + if (!result) return std::nullopt; + + DomainKeyDecoder> key_decoder{entity.has_large_values}; + key_decoder.decode(result.key); + if (key_decoder.value.key.value != from_slice(key_slice)) return std::nullopt; + + DomainValueDecoder> empty_value_decoder{entity.has_large_values}; + empty_value_decoder.decode(result.value); + if (empty_value_decoder.value.value.value.empty()) return std::nullopt; + + DomainValueDecoder value_decoder{entity.has_large_values}; + value_decoder.decode(result.value); + + Step step = Step(key_decoder.value.timestamp.value.value | value_decoder.value.timestamp.value.value); + + return Result{std::move(value_decoder.value.value.value), step}; + } +}; + +template +struct DomainPutQuery { + RWTxn& tx; + Domain entity; + + using TKey = decltype(TKeyEncoder::value); + using TValue = decltype(TValueEncoder::value); + + void exec( + const TKey& key, + const TValue& value, + Timestamp timestamp, + const std::optional& prev_value, + Step prev_step) { + DomainPutLatestQuery value_query{tx, entity}; + value_query.exec(key, value, prev_step); + + if (entity.history) { + if (prev_value) { + HistoryPutQuery history_query{tx, *entity.history}; + history_query.exec(key, *prev_value, timestamp); + } else { + HistoryDeleteQuery history_query{tx, *entity.history}; + history_query.exec(key, timestamp); + } + } + } +}; + +template +struct DomainDeleteQuery { + RWTxn& tx; + Domain entity; + + using TKey = decltype(TKeyEncoder::value); + using TValue = decltype(TValueEncoder::value); + + void exec( + const TKey& key, + Timestamp timestamp, + const std::optional& prev_value, + Step prev_step) { + if (prev_value) { + DomainPutQuery> query{tx, entity}; + query.exec(key, ByteView{}, timestamp, prev_value, prev_step); + } + } +}; + } // namespace silkworm::datastore::kvdb diff --git a/silkworm/db/datastore/kvdb/history.hpp b/silkworm/db/datastore/kvdb/history.hpp index 4ff9c1974f..dc5d7bec62 100644 --- a/silkworm/db/datastore/kvdb/history.hpp +++ b/silkworm/db/datastore/kvdb/history.hpp @@ -17,6 +17,7 @@ #pragma once #include "inverted_index.hpp" +#include "kvts_codec.hpp" #include "mdbx.hpp" namespace silkworm::datastore::kvdb { @@ -27,4 +28,47 @@ struct History { InvertedIndex inverted_index; }; +template +using HistoryKeyEncoder = KVTSKeyEncoder; + +template +using HistoryValueEncoder = KVTSValueEncoder; + +template +struct HistoryPutQuery { + RWTxn& tx; + History entity; + + using TKey = decltype(TKeyEncoder::value); + using TValue = decltype(TValueEncoder::value); + + void exec(const TKey& key, const TValue& value, Timestamp timestamp) { + HistoryKeyEncoder key_encoder{entity.has_large_values}; + key_encoder.value.key.value = key; + key_encoder.value.timestamp.value = timestamp; + + HistoryValueEncoder value_encoder{entity.has_large_values}; + value_encoder.value.value.value = value; + value_encoder.value.timestamp.value = timestamp; + + tx.rw_cursor(entity.values_table)->insert(key_encoder.encode(), value_encoder.encode()); + + InvertedIndexPutQuery inverted_index_query{tx, entity.inverted_index}; + inverted_index_query.exec(key, timestamp, false); + } +}; + +template +struct HistoryDeleteQuery { + RWTxn& tx; + History entity; + + using TKey = decltype(TKeyEncoder::value); + + void exec(const TKey& key, Timestamp timestamp) { + HistoryPutQuery> query{tx, entity}; + query.exec(key, ByteView{}, timestamp); + } +}; + } // namespace silkworm::datastore::kvdb diff --git a/silkworm/db/datastore/kvdb/inverted_index.hpp b/silkworm/db/datastore/kvdb/inverted_index.hpp index 363c794f0e..178c32ee17 100644 --- a/silkworm/db/datastore/kvdb/inverted_index.hpp +++ b/silkworm/db/datastore/kvdb/inverted_index.hpp @@ -16,6 +16,9 @@ #pragma once +#include "../common/timestamp.hpp" +#include "big_endian_codec.hpp" +#include "codec.hpp" #include "mdbx.hpp" namespace silkworm::datastore::kvdb { @@ -25,4 +28,34 @@ struct InvertedIndex { const MapConfig& index_table; }; +using TimestampEncoder = BigEndianU64Codec; + +template +struct InvertedIndexPutQuery { + RWTxn& tx; + InvertedIndex entity; + + using TKey = decltype(TKeyEncoder::value); + + void exec(const TKey& key, const Timestamp timestamp, bool with_index_update) { + return exec(key, timestamp, with_index_update); + } + + template + void exec(const TKey& key, const TTimestamp& timestamp, bool with_index_update) { + TKeyEncoder key_encoder; + key_encoder.value = key; + Slice key_slice = key_encoder.encode(); + + TTimestampEncoder ts_encoder; + ts_encoder.value = timestamp; + Slice ts_slice = ts_encoder.encode(); + + tx.rw_cursor(entity.keys_table)->insert(ts_slice, key_slice); + if (with_index_update) { + tx.rw_cursor(entity.index_table)->insert(key_slice, ts_slice); + } + } +}; + } // namespace silkworm::datastore::kvdb diff --git a/silkworm/db/datastore/kvdb/kvts_codec.hpp b/silkworm/db/datastore/kvdb/kvts_codec.hpp new file mode 100644 index 0000000000..ea0d1c7433 --- /dev/null +++ b/silkworm/db/datastore/kvdb/kvts_codec.hpp @@ -0,0 +1,142 @@ +/* + Copyright 2024 The Silkworm Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#pragma once + +#include "codec.hpp" +#include "raw_codec.hpp" + +namespace silkworm::datastore::kvdb { + +template +class KVTSKeyEncoder : public Encoder { + public: + struct { + TEncoder key; + TTimestampEncoder timestamp; + } value; + + explicit KVTSKeyEncoder(bool has_large_values) + : has_large_values_{has_large_values} {} + ~KVTSKeyEncoder() override = default; + + Slice encode() override { + data_.clear(); + if (has_large_values_) { + // encode as key + timestamp + data_.append(from_slice(value.key.encode())); + data_.append(from_slice(value.timestamp.encode())); + return to_slice(data_); + } else { + return value.key.encode(); + } + } + + private: + bool has_large_values_; + Bytes data_; +}; + +template +class KVTSValueEncoder : public Encoder { + public: + struct { + TEncoder value; + TTimestampEncoder timestamp; + } value; + + explicit KVTSValueEncoder(bool has_large_values) + : has_large_values_{has_large_values} {} + ~KVTSValueEncoder() override = default; + + Slice encode() override { + data_.clear(); + if (has_large_values_) { + return value.value.encode(); + } else { + // encode as timestamp + value + data_.append(from_slice(value.timestamp.encode())); + data_.append(from_slice(value.value.encode())); + return to_slice(data_); + } + } + + private: + bool has_large_values_; + Bytes data_; +}; + +static_assert(EncoderConcept, RawEncoder>>); +static_assert(EncoderConcept, RawEncoder>>); + +template +class KVTSKeyDecoder : public Decoder { + public: + struct { + TDecoder key; + TTimestampDecoder timestamp; + } value; + + explicit KVTSKeyDecoder(bool has_large_values) + : has_large_values_{has_large_values} {} + ~KVTSKeyDecoder() override = default; + + void decode(Slice data) override { + if (has_large_values_) { + // decode as key + timestamp + SILKWORM_ASSERT(data.size() >= kEncodedTimestampSize); + value.key.decode(to_slice(from_slice(data).substr(0, data.size() - kEncodedTimestampSize))); + value.timestamp.decode(to_slice(from_slice(data).substr(data.size() - kEncodedTimestampSize, kEncodedTimestampSize))); + } else { + value.key.decode(data); + } + } + + private: + bool has_large_values_; +}; + +template +class KVTSValueDecoder : public Decoder { + public: + struct { + TDecoder value; + TTimestampDecoder timestamp; + } value; + + explicit KVTSValueDecoder(bool has_large_values) + : has_large_values_{has_large_values} {} + ~KVTSValueDecoder() override = default; + + void decode(Slice slice) override { + if (has_large_values_) { + value.value.decode(slice); + } else { + // decode as timestamp + value + SILKWORM_ASSERT(slice.size() >= kEncodedTimestampSize); + value.timestamp.decode(to_slice(from_slice(slice).substr(0, kEncodedTimestampSize))); + value.value.decode(to_slice(from_slice(slice).substr(kEncodedTimestampSize))); + } + } + + private: + bool has_large_values_; +}; + +static_assert(DecoderConcept, RawDecoder, sizeof(uint64_t)>>); +static_assert(DecoderConcept, RawDecoder, sizeof(uint64_t)>>); + +} // namespace silkworm::datastore::kvdb diff --git a/silkworm/db/datastore/kvdb/raw_codec.hpp b/silkworm/db/datastore/kvdb/raw_codec.hpp new file mode 100644 index 0000000000..226d3478e6 --- /dev/null +++ b/silkworm/db/datastore/kvdb/raw_codec.hpp @@ -0,0 +1,52 @@ +/* + Copyright 2024 The Silkworm Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#pragma once + +#include + +#include "codec.hpp" + +namespace silkworm::datastore::kvdb { + +template +concept BytesOrByteView = std::same_as || std::same_as; + +template +struct RawDecoder : public Decoder { + TBytes value; + ~RawDecoder() override = default; + void decode(Slice slice) override { + value = from_slice(slice); + } +}; + +static_assert(DecoderConcept>); +static_assert(DecoderConcept>); + +template +struct RawEncoder : public Encoder { + TBytes value; + ~RawEncoder() override = default; + Slice encode() override { + return to_slice(ByteView{value}); + } +}; + +static_assert(EncoderConcept>); +static_assert(EncoderConcept>); + +} // namespace silkworm::datastore::kvdb diff --git a/silkworm/db/datastore/snapshots/common/codec.hpp b/silkworm/db/datastore/snapshots/common/codec.hpp index fb145bd4d4..3a50a61597 100644 --- a/silkworm/db/datastore/snapshots/common/codec.hpp +++ b/silkworm/db/datastore/snapshots/common/codec.hpp @@ -44,4 +44,8 @@ concept DecoderConcept = std::derived_from && requires(TDecoder decoder) { decoder.value; }; +struct Codec : public Encoder, public Decoder { + ~Codec() override = default; +}; + } // namespace silkworm::snapshots diff --git a/silkworm/db/datastore/snapshots/segment/kv_segment_test.cpp b/silkworm/db/datastore/snapshots/segment/kv_segment_test.cpp index df89118eba..d5dd6ba178 100644 --- a/silkworm/db/datastore/snapshots/segment/kv_segment_test.cpp +++ b/silkworm/db/datastore/snapshots/segment/kv_segment_test.cpp @@ -25,7 +25,7 @@ namespace silkworm::snapshots::segment { -struct CharCodec : public Encoder, public Decoder { +struct CharCodec : public Codec { char value{}; Bytes word; diff --git a/silkworm/db/datastore/snapshots/test_util/string_codec.hpp b/silkworm/db/datastore/snapshots/test_util/string_codec.hpp index ff3ceab140..7d2100ae8d 100644 --- a/silkworm/db/datastore/snapshots/test_util/string_codec.hpp +++ b/silkworm/db/datastore/snapshots/test_util/string_codec.hpp @@ -24,7 +24,7 @@ namespace silkworm::snapshots { -struct StringCodec : public Encoder, public Decoder { +struct StringCodec : public Codec { std::string value; Bytes word; diff --git a/silkworm/db/state/account_codecs.hpp b/silkworm/db/state/account_codecs.hpp index d5624305a9..e1d03b28f9 100644 --- a/silkworm/db/state/account_codecs.hpp +++ b/silkworm/db/state/account_codecs.hpp @@ -17,6 +17,7 @@ #pragma once #include +#include #include #include @@ -25,6 +26,25 @@ namespace silkworm::db::state { +struct AccountKVDBCodec : public datastore::kvdb::Codec { + Account value; + Bytes data; + + ~AccountKVDBCodec() override = default; + + datastore::kvdb::Slice encode() override { + data = AccountCodec::encode_for_storage_v3(value); + return datastore::kvdb::to_slice(data); + } + + void decode(datastore::kvdb::Slice slice) override { + value = unwrap_or_throw(AccountCodec::from_encoded_storage_v3(datastore::kvdb::from_slice(slice))); + } +}; + +static_assert(datastore::kvdb::EncoderConcept); +static_assert(datastore::kvdb::DecoderConcept); + struct AccountDecoder : public snapshots::Decoder { Account value; diff --git a/silkworm/db/state/accounts_domain.hpp b/silkworm/db/state/accounts_domain.hpp index 52959d1d2b..a8b5dd59ad 100644 --- a/silkworm/db/state/accounts_domain.hpp +++ b/silkworm/db/state/accounts_domain.hpp @@ -24,6 +24,10 @@ namespace silkworm::db::state { +using AccountsDomainGetLatestQuery = datastore::kvdb::DomainGetLatestQuery; +using AccountsDomainPutQuery = datastore::kvdb::DomainPutQuery; +using AccountsDomainDeleteQuery = datastore::kvdb::DomainDeleteQuery; + using AccountsDomainKVSegmentReader = snapshots::segment::KVSegmentReader; } // namespace silkworm::db::state diff --git a/silkworm/db/state/address_codecs.hpp b/silkworm/db/state/address_codecs.hpp index bb6b43962e..ca9d736faa 100644 --- a/silkworm/db/state/address_codecs.hpp +++ b/silkworm/db/state/address_codecs.hpp @@ -20,10 +20,23 @@ #include +#include #include namespace silkworm::db::state { +struct AddressKVDBEncoder : public datastore::kvdb::Encoder { + evmc::address value; + + ~AddressKVDBEncoder() override = default; + + datastore::kvdb::Slice encode() override { + return {&value.bytes, kAddressLength}; + } +}; + +static_assert(datastore::kvdb::EncoderConcept); + struct AddressDecoder : public snapshots::Decoder { evmc::address value; diff --git a/silkworm/db/state/code_domain.hpp b/silkworm/db/state/code_domain.hpp index d56ffd3b20..1ca5912b06 100644 --- a/silkworm/db/state/code_domain.hpp +++ b/silkworm/db/state/code_domain.hpp @@ -16,6 +16,7 @@ #pragma once +#include #include #include @@ -23,6 +24,10 @@ namespace silkworm::db::state { +using CodeDomainGetLatestQuery = datastore::kvdb::DomainGetLatestQuery>; +using CodeDomainPutQuery = datastore::kvdb::DomainPutQuery>; +using CodeDomainDeleteQuery = datastore::kvdb::DomainDeleteQuery>; + using CodeDomainKVSegmentReader = snapshots::segment::KVSegmentReader>; } // namespace silkworm::db::state diff --git a/silkworm/db/state/commitment_domain.hpp b/silkworm/db/state/commitment_domain.hpp index 4afc9bbdaf..565a459f8d 100644 --- a/silkworm/db/state/commitment_domain.hpp +++ b/silkworm/db/state/commitment_domain.hpp @@ -16,11 +16,16 @@ #pragma once +#include #include #include namespace silkworm::db::state { +using CommitmentDomainGetLatestQuery = datastore::kvdb::DomainGetLatestQuery, datastore::kvdb::RawDecoder>; +using CommitmentDomainPutQuery = datastore::kvdb::DomainPutQuery, datastore::kvdb::RawEncoder>; +using CommitmentDomainDeleteQuery = datastore::kvdb::DomainDeleteQuery, datastore::kvdb::RawEncoder>; + using CommitmentDomainKVSegmentReader = snapshots::segment::KVSegmentReader, snapshots::RawDecoder>; } // namespace silkworm::db::state diff --git a/silkworm/db/state/receipts_domain.hpp b/silkworm/db/state/receipts_domain.hpp index 929d965bcc..de901acbc8 100644 --- a/silkworm/db/state/receipts_domain.hpp +++ b/silkworm/db/state/receipts_domain.hpp @@ -18,11 +18,16 @@ #include +#include #include #include namespace silkworm::db::state { +using ReceiptsDomainGetLatestQuery = datastore::kvdb::DomainGetLatestQuery, datastore::kvdb::RawDecoder>; +using ReceiptsDomainPutQuery = datastore::kvdb::DomainPutQuery, datastore::kvdb::RawEncoder>; +using ReceiptsDomainDeleteQuery = datastore::kvdb::DomainDeleteQuery, datastore::kvdb::RawEncoder>; + enum class ReceiptsDomainKey : uint8_t { kCumulativeGasUsedInBlockKey = 0, kCumulativeBlobGasUsedInBlockKey = 1, diff --git a/silkworm/db/state/storage_codecs.cpp b/silkworm/db/state/storage_codecs.cpp new file mode 100644 index 0000000000..39c4873161 --- /dev/null +++ b/silkworm/db/state/storage_codecs.cpp @@ -0,0 +1,33 @@ +/* + Copyright 2024 The Silkworm Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#include "storage_codecs.hpp" + +namespace silkworm::db::state { + +datastore::kvdb::Slice StorageAddressAndLocationKVDBEncoder::encode() { + // TODO: this extra copy could be avoided if encoders are able to contain a reference + encoder.address.value = value.address; + encoder.location_hash.value = value.location_hash; + + data.clear(); + data.reserve(kAddressLength + kHashLength); + data.append(datastore::kvdb::from_slice(encoder.address.encode())); + data.append(datastore::kvdb::from_slice(encoder.location_hash.encode())); + return datastore::kvdb::to_slice(data); +} + +} // namespace silkworm::db::state diff --git a/silkworm/db/state/storage_codecs.hpp b/silkworm/db/state/storage_codecs.hpp index 638aa5bc73..86b90e5a08 100644 --- a/silkworm/db/state/storage_codecs.hpp +++ b/silkworm/db/state/storage_codecs.hpp @@ -16,12 +16,28 @@ #pragma once +#include #include #include "address_codecs.hpp" namespace silkworm::db::state { +struct Bytes32KVDBCodec : public datastore::kvdb::Codec { + evmc::bytes32 value; + ~Bytes32KVDBCodec() override = default; + datastore::kvdb::Slice encode() override { + return {&value.bytes, sizeof(value.bytes)}; + } + void decode(datastore::kvdb::Slice slice) override { + SILKWORM_ASSERT(slice.size() >= sizeof(value.bytes)); + std::memcpy(value.bytes, slice.data(), sizeof(value.bytes)); + } +}; + +static_assert(datastore::kvdb::EncoderConcept); +static_assert(datastore::kvdb::DecoderConcept); + struct Bytes32Decoder : public snapshots::Decoder { evmc::bytes32 value; ~Bytes32Decoder() override = default; @@ -34,6 +50,26 @@ struct Bytes32Decoder : public snapshots::Decoder { static_assert(snapshots::DecoderConcept); +struct StorageAddressAndLocationKVDBEncoder : public datastore::kvdb::Encoder { + struct { + evmc::address address; + evmc::bytes32 location_hash; + } value; + + struct { + AddressKVDBEncoder address; + Bytes32KVDBCodec location_hash; + } encoder; + + Bytes data; + + ~StorageAddressAndLocationKVDBEncoder() override = default; + + datastore::kvdb::Slice encode() override; +}; + +static_assert(datastore::kvdb::EncoderConcept); + struct StorageAddressAndLocationDecoder : public snapshots::Decoder { struct { AddressDecoder address; diff --git a/silkworm/db/state/storage_domain.hpp b/silkworm/db/state/storage_domain.hpp index d43a7a1fa1..a50b5f32c8 100644 --- a/silkworm/db/state/storage_domain.hpp +++ b/silkworm/db/state/storage_domain.hpp @@ -23,6 +23,10 @@ namespace silkworm::db::state { +using StorageDomainGetLatestQuery = datastore::kvdb::DomainGetLatestQuery; +using StorageDomainPutQuery = datastore::kvdb::DomainPutQuery; +using StorageDomainDeleteQuery = datastore::kvdb::DomainDeleteQuery; + using StorageDomainKVSegmentReader = snapshots::segment::KVSegmentReader; } // namespace silkworm::db::state diff --git a/silkworm/execution/local_state.cpp b/silkworm/execution/local_state.cpp index b543d2730a..72d0b66b7f 100644 --- a/silkworm/execution/local_state.cpp +++ b/silkworm/execution/local_state.cpp @@ -17,33 +17,43 @@ #include "local_state.hpp" #include +#include +#include +#include namespace silkworm::execution { +using namespace db::state; +using namespace datastore; + std::optional LocalState::read_account(const evmc::address& address) const noexcept { - if (block_num_) { - return db::read_account(txn_, address, *block_num_ + 1); - } else { - // TODO read using txn_id - return std::nullopt; + AccountsDomainGetLatestQuery query{tx_, data_store_.state_db().accounts_domain()}; + auto result = query.exec(address); + if (result) { + return std::move(result->value); } + return std::nullopt; } -ByteView LocalState::read_code(const evmc::address& /*address*/, const evmc::bytes32& code_hash) const noexcept { - auto code_optional = db::read_code(txn_, code_hash); - if (!code_optional) { - return ByteView{}; +ByteView LocalState::read_code(const evmc::address& address, const evmc::bytes32& /*code_hash*/) const noexcept { + CodeDomainGetLatestQuery query{tx_, data_store_.state_db().code_domain()}; + auto result = query.exec(address); + if (result) { + return std::move(result->value); } - return *code_optional; + return ByteView{}; } -evmc::bytes32 LocalState::read_storage(const evmc::address& address, uint64_t incarnation, const evmc::bytes32& location) const noexcept { - if (block_num_) { - return db::read_storage(txn_, address, incarnation, location, *block_num_ + 1); - } else { - // TODO read using txn_id - return {}; +evmc::bytes32 LocalState::read_storage( + const evmc::address& address, + uint64_t /*incarnation*/, + const evmc::bytes32& location) const noexcept { + StorageDomainGetLatestQuery query{tx_, data_store_.state_db().storage_domain()}; + auto result = query.exec({address, location}); + if (result) { + return std::move(result->value); } + return {}; } uint64_t LocalState::previous_incarnation(const evmc::address& /*address*/) const noexcept { @@ -51,15 +61,15 @@ uint64_t LocalState::previous_incarnation(const evmc::address& /*address*/) cons } std::optional LocalState::read_header(BlockNum block_num, const evmc::bytes32& block_hash) const noexcept { - return data_model_.read_header(block_num, block_hash); + return data_model().read_header(block_num, block_hash); } bool LocalState::read_body(BlockNum block_num, const evmc::bytes32& block_hash, BlockBody& out) const noexcept { - return data_model_.read_body(block_hash, block_num, out); + return data_model().read_body(block_hash, block_num, out); } std::optional LocalState::total_difficulty(BlockNum block_num, const evmc::bytes32& block_hash) const noexcept { - return data_model_.read_total_difficulty(block_num, block_hash); + return data_model().read_total_difficulty(block_num, block_hash); } evmc::bytes32 LocalState::state_root_hash() const { @@ -73,7 +83,43 @@ BlockNum LocalState::current_canonical_block() const { std::optional LocalState::canonical_hash(BlockNum block_num) const { // This method should not be called by EVM::execute - return data_model_.read_canonical_header_hash(block_num); + return data_model().read_canonical_header_hash(block_num); +} + +void LocalState::update_account( + const evmc::address& address, + std::optional initial, + std::optional current) { + // TODO: prev_step - a step at which initial value was produced + Step prev_step = Step::from_txn_id(txn_id_); + AccountsDomainPutQuery query{tx_, data_store_.state_db().accounts_domain()}; + // TODO: handle current = nullopt + query.exec(address, *current, txn_id_, initial, prev_step); +} + +void LocalState::update_account_code( + const evmc::address& address, + uint64_t /*incarnation*/, + const evmc::bytes32& /*code_hash*/, + ByteView code) { + // TODO: prev_step - a step at which initial value was produced + Step prev_step = Step::from_txn_id(txn_id_); + CodeDomainPutQuery query{tx_, data_store_.state_db().code_domain()}; + // TODO: initial_code + std::optional initial_code = std::nullopt; + query.exec(address, code, txn_id_, initial_code, prev_step); +} + +void LocalState::update_storage( + const evmc::address& address, + uint64_t /*incarnation*/, + const evmc::bytes32& location, + const evmc::bytes32& initial, + const evmc::bytes32& current) { + // TODO: prev_step - a step at which initial value was produced + Step prev_step = Step::from_txn_id(txn_id_); + StorageDomainPutQuery query{tx_, data_store_.state_db().storage_domain()}; + query.exec({address, location}, current, txn_id_, initial, prev_step); } } // namespace silkworm::execution diff --git a/silkworm/execution/local_state.hpp b/silkworm/execution/local_state.hpp index 2d84f6806f..a16a9d0c62 100644 --- a/silkworm/execution/local_state.hpp +++ b/silkworm/execution/local_state.hpp @@ -33,11 +33,12 @@ namespace silkworm::execution { class LocalState : public State { public: - explicit LocalState(std::optional block_num, std::optional txn_id, db::DataStoreRef data_store) - : block_num_{block_num}, - txnid_{txn_id}, - txn_{data_store.chaindata.access_ro().start_ro_tx()}, - data_model_{txn_, data_store.blocks_repository} {} + explicit LocalState( + TxnId txn_id, + db::DataStoreRef data_store) + : txn_id_{txn_id}, + data_store_{std::move(data_store)}, + tx_{data_store_.chaindata.access_rw().start_rw_tx()} {} std::optional read_account(const evmc::address& address) const noexcept override; @@ -72,30 +73,33 @@ class LocalState : public State { void begin_block(BlockNum /*block_num*/, size_t /*updated_accounts_count*/) override {} void update_account( - const evmc::address& /*address*/, - std::optional /*initial*/, - std::optional /*current*/) override {} + const evmc::address& address, + std::optional initial, + std::optional current) override; void update_account_code( - const evmc::address& /*address*/, - uint64_t /*incarnation*/, - const evmc::bytes32& /*code_hash*/, - ByteView /*code*/) override {} + const evmc::address& address, + uint64_t incarnation, + const evmc::bytes32& code_hash, + ByteView code) override; void update_storage( - const evmc::address& /*address*/, - uint64_t /*incarnation*/, - const evmc::bytes32& /*location*/, - const evmc::bytes32& /*initial*/, - const evmc::bytes32& /*current*/) override {} + const evmc::address& address, + uint64_t incarnation, + const evmc::bytes32& location, + const evmc::bytes32& initial, + const evmc::bytes32& current) override; void unwind_state_changes(BlockNum /*block_num*/) override {} private: - std::optional block_num_; - std::optional txnid_; - mutable datastore::kvdb::ROTxnManaged txn_; - db::DataModel data_model_; + db::DataModel data_model() const { + return db::DataModelFactory{data_store_}(tx_); + } + + TxnId txn_id_; + db::DataStoreRef data_store_; + mutable datastore::kvdb::RWTxnManaged tx_; }; } // namespace silkworm::execution diff --git a/silkworm/execution/state_factory.cpp b/silkworm/execution/state_factory.cpp index 1649acb2c6..c6f15891c9 100644 --- a/silkworm/execution/state_factory.cpp +++ b/silkworm/execution/state_factory.cpp @@ -30,7 +30,7 @@ std::shared_ptr StateFactory::create_state( TxnId txn_id) { if (tx.is_local()) { auto& local_tx = dynamic_cast(tx); - return std::make_shared(std::nullopt, txn_id, local_tx.data_store()); + return std::make_shared(txn_id, local_tx.data_store()); } else { // NOLINT(readability-else-after-return) return std::make_shared(executor, tx, storage, txn_id); }