Skip to content

Commit

Permalink
Merge pull request #703 from AntelopeIO/merge-1.0-main
Browse files Browse the repository at this point in the history
[1.0 -> main] Harden ordered_diff; Version bump v1.0.0 final
  • Loading branch information
arhag authored Sep 5, 2024
2 parents 07da271 + fd37d91 commit 55cb742
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 8 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ nodeos --full-version
```
You should see a [semantic version](https://semver.org) string followed by a `git` commit hash with no errors. For example:
```
v3.1.2-0b64f879e3ebe2e4df09d2e62f1fc164cc1125d1
v1.0.1-9026a03c09c9b4f93edca696b5eef259f0ab96b3
```

## Build and Install from Source
Expand Down Expand Up @@ -84,7 +84,7 @@ cd spring
```

### Step 2 - Checkout Release Tag or Branch
Choose which [release](https://github.com/AntelopeIO/spring/releases) or [branch](#branches) you would like to build, then check it out. If you are not sure, use the [latest release](https://github.com/AntelopeIO/spring/releases/latest). For example, if you want to build release 3.1.2 then you would check it out using its tag, `v3.1.2`. In the example below, replace `v0.0.0` with your selected release tag accordingly:
Choose which [release](https://github.com/AntelopeIO/spring/releases) or [branch](#branches) you would like to build, then check it out. If you are not sure, use the [latest release](https://github.com/AntelopeIO/spring/releases/latest). For example, if you want to build release 1.0.1 then you would check it out using its tag, `v1.0.1`. In the example below, replace `v0.0.0` with your selected release tag accordingly:
```bash
git fetch --all --tags
git checkout v0.0.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@

namespace eosio::chain {

static_assert(std::numeric_limits<uint16_t>::max() >= config::max_finalizers - 1);
using finalizers_differ = fc::ordered_diff<finalizer_authority, uint16_t>;
// Verify finalizers_differ::size_type can represent all index values in the
// diff between two policies that could each hold up to max_finalizers entries.
static_assert(std::numeric_limits<finalizers_differ::size_type>::max() >= config::max_finalizers - 1);
using finalizers_diff_t = finalizers_differ::diff_result;

struct finalizer_policy_diff {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@

namespace eosio::chain {

static_assert(std::numeric_limits<uint16_t>::max() >= config::max_proposers - 1);
using producer_auth_differ = fc::ordered_diff<producer_authority, uint16_t>;
// Verify producer_auth_differ::size_type can represent all index values in the
// diff between two policies that could each hold up to max_proposers entries.
static_assert(std::numeric_limits<producer_auth_differ::size_type>::max() >= config::max_proposers - 1);
using producer_auth_diff_t = producer_auth_differ::diff_result;

struct proposer_policy_diff {
Expand Down
23 changes: 19 additions & 4 deletions libraries/libfc/include/fc/container/ordered_diff.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
#pragma once

#include <fc/exception/exception.hpp>
#include <fc/utility.hpp>

#include <vector>
#include <utility>

Expand All @@ -9,6 +12,8 @@ namespace fc {
* @class ordered_diff
* @brief Provides ability to generate and apply diff of containers of type T
*
* NOTE: Part of Spring Consensus. Used for finalizer and proposer policies.
*
* Example use:
* std::vector<char> source = { 'a', 'b', 'f', 'c', 'd' };
* std::vector<char> target = { 'b', 'f', 'c', 'd', 'e', 'h' };
Expand All @@ -24,6 +29,8 @@ template <typename T, typename SizeType = size_t, template<typename Y, typename.
requires std::equality_comparable<T> && std::random_access_iterator<typename Container<T>::iterator>
class ordered_diff {
public:
using size_type = SizeType;

struct diff_result {
Container<SizeType> remove_indexes;
Container<std::pair<SizeType, T>> insert_indexes;
Expand All @@ -34,13 +41,16 @@ class ordered_diff {
size_t s = 0;
size_t t = 0;

FC_ASSERT(source.empty() || (source.size() - 1) <= std::numeric_limits<SizeType>::max());
FC_ASSERT(target.empty() || (target.size() - 1) <= std::numeric_limits<SizeType>::max());

diff_result result;
while (s < source.size() || t < target.size()) {
assert(s <= source.size());
assert(t <= target.size());
if (s < source.size() && t < target.size()) {
if (source[s] == target[t]) {
// nothing to do, skip over
assert(s <= std::numeric_limits<SizeType>::max());
assert(t <= std::numeric_limits<SizeType>::max());
++s;
++t;
} else { // not equal
Expand All @@ -65,8 +75,9 @@ class ordered_diff {
assert(t <= std::numeric_limits<SizeType>::max());
result.insert_indexes.emplace_back(t, target[t]);
++t;
} else { // source[s + 1] == target[t]
// target matches next source, remove current source
} else {
// not misalignment by one and source not equal to next target, so remove from source
// may be inserted later by other conditions if needed
assert(s <= std::numeric_limits<SizeType>::max());
result.remove_indexes.push_back(s);
++s;
Expand Down Expand Up @@ -97,12 +108,16 @@ class ordered_diff {
// Remove from the source based on diff.remove_indexes
std::ptrdiff_t offset = 0;
for (SizeType index : diff.remove_indexes) {
FC_ASSERT(index + offset < container.size(), "diff.remove_indexes index ${idx} + offset ${o} not in range ${s}",
("idx", index)("o", offset)("s", container.size()));
container.erase(container.begin() + index + offset);
--offset;
}

// Insert into the source based on diff.insert_indexes
for (auto& [index, value] : diff.insert_indexes) {
FC_ASSERT(index <= container.size(), "diff.insert_indexes index ${idx} not in range ${s}",
("idx", index)("s", container.size()));
container.insert(container.begin() + index, std::move(value));
}
return container;
Expand Down
73 changes: 73 additions & 0 deletions libraries/libfc/test/test_ordered_diff.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
#include <fc/container/ordered_diff.hpp>
#include <fc/exception/exception.hpp>

#include <algorithm>
#include <random>

using namespace fc;

BOOST_AUTO_TEST_SUITE(ordered_diff_tests)
Expand Down Expand Up @@ -39,20 +42,41 @@ BOOST_AUTO_TEST_CASE(ordered_diff_test) try {
source = ordered_diff<char, int>::apply_diff(std::move(source), result);
BOOST_TEST(source == target);
}
{ // All elements removed, size 1
vector<char> source = {'a'};
vector<char> target;
auto result = ordered_diff<char, int>::diff(source, target);
source = ordered_diff<char, int>::apply_diff(std::move(source), result);
BOOST_TEST(source == target);
}
{ // All elements inserted
vector<char> source;
vector<char> target = {'a', 'b', 'c', 'd', 'e'};
auto result = ordered_diff<char>::diff(source, target);
source = ordered_diff<char>::apply_diff(std::move(source), result);
BOOST_TEST(source == target);
}
{ // All elements inserted, size 1
vector<char> source;
vector<char> target = {'a'};
auto result = ordered_diff<char>::diff(source, target);
source = ordered_diff<char>::apply_diff(std::move(source), result);
BOOST_TEST(source == target);
}
{ // No change
vector<char> source = {'a', 'b', 'c', 'd', 'e'};
vector<char> target = source;
auto result = ordered_diff<char>::diff(source, target);
source = ordered_diff<char>::apply_diff(std::move(source), result);
BOOST_TEST(source == target);
}
{ // No change, size 1
vector<char> source = {'a'};
vector<char> target = source;
auto result = ordered_diff<char>::diff(source, target);
source = ordered_diff<char>::apply_diff(std::move(source), result);
BOOST_TEST(source == target);
}
{ // Mix of removals and inserts
vector<char> source = {'a', 'b', 'c', 'd', 'e'};
vector<char> target = {'a', 'c', 'e', 'f', 'g', 'h'};
Expand All @@ -74,13 +98,41 @@ BOOST_AUTO_TEST_CASE(ordered_diff_test) try {
source = ordered_diff<char>::apply_diff(std::move(source), result);
BOOST_TEST(source == target);
}
{ // Complete change, size 1
vector<char> source = {'a'};
vector<char> target = {'f'};
auto result = ordered_diff<char>::diff(source, target);
source = ordered_diff<char>::apply_diff(std::move(source), result);
BOOST_TEST(source == target);
}
{ // Complete change equal sizes
vector<char> source = {'a', 'b', 'c', 'd'};
vector<char> target = {'f', 'g', 'h', 'i'};
auto result = ordered_diff<char>::diff(source, target);
source = ordered_diff<char>::apply_diff(std::move(source), result);
BOOST_TEST(source == target);
}
{ // Diff order
vector<char> source = {'a', 'b', 'c', 'd', 'e'};
vector<char> target = {'e', 'd', 'c', 'b', 'a'};
auto result = ordered_diff<char>::diff(source, target);
source = ordered_diff<char>::apply_diff(std::move(source), result);
BOOST_TEST(source == target);
}
{ // Diff order, size 2
vector<char> source = {'a', 'b'};
vector<char> target = {'b', 'a'};
auto result = ordered_diff<char>::diff(source, target);
source = ordered_diff<char>::apply_diff(std::move(source), result);
BOOST_TEST(source == target);
}
{ // Diff order, size 2
vector<char> source = {'b', 'a'};
vector<char> target = {'a', 'b'};
auto result = ordered_diff<char>::diff(source, target);
source = ordered_diff<char>::apply_diff(std::move(source), result);
BOOST_TEST(source == target);
}
{ // shift left
vector<char> source = {'a', 'b', 'c', 'd', 'e'};
vector<char> target = {'b', 'c', 'd', 'e', 'f'};
Expand Down Expand Up @@ -119,6 +171,27 @@ BOOST_AUTO_TEST_CASE(ordered_diff_test) try {
source = ordered_diff<uint8_t, uint8_t>::apply_diff(std::move(source), result);
BOOST_TEST(source == target);
}
{ // full, random
std::random_device rnd_device;
std::mt19937 mersenne_engine {rnd_device()};
std::uniform_int_distribution<uint8_t> dist {0, std::numeric_limits<uint8_t>::max()};
auto gen = [&](){ return dist(mersenne_engine); };
vector<uint8_t> source(std::numeric_limits<uint8_t>::max()+1);
std::generate(source.begin(), source.end(), gen);
vector<uint8_t> target(source.size());
std::reverse_copy(source.begin(), source.end(), target.begin());
auto result = ordered_diff<uint8_t, uint8_t>::diff(source, target);
source = ordered_diff<uint8_t, uint8_t>::apply_diff(std::move(source), result);
BOOST_TEST(source == target);
target.clear();
result = ordered_diff<uint8_t, uint8_t>::diff(source, target);
source = ordered_diff<uint8_t, uint8_t>::apply_diff(std::move(source), result);
BOOST_TEST(source == target);
source.clear();
result = ordered_diff<uint8_t, uint8_t>::diff(source, target);
source = ordered_diff<uint8_t, uint8_t>::apply_diff(std::move(source), result);
BOOST_TEST(source == target);
}
{ // non-unique full
vector<uint8_t> source(std::numeric_limits<uint8_t>::max()*2);
std::iota(source.begin(), source.begin()+std::numeric_limits<uint8_t>::max(), 0);
Expand Down

0 comments on commit 55cb742

Please sign in to comment.