Skip to content

Commit a588b59

Browse files
authored
Prepare System contract for Instant Finality
eosio.system Savanna: vote-accounting, EVM-vote auth, and onblock/kick robustness (builds on #62)
2 parents 3438580 + e0aff4e commit a588b59

23 files changed

Lines changed: 2040 additions & 114 deletions

.github/workflows/ubuntu-2204.yml

Lines changed: 69 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,78 @@ on:
66
- "release/*"
77
pull_request:
88
types: [assigned, opened, synchronize, reopened, labeled]
9+
workflow_dispatch:
910
env:
10-
BUILDER_IMAGE: "apm2006/leap:v3.2.5-cdt4.0.1"
11+
CDT_VERSION: "4.1.1"
12+
CDT_DEB_SHA256: "d946e6b64f297442d19e486401aa49f956080b07a1fa6335f29976106175b588"
13+
# Commit SHA of tag teloszero-v1.2.2 (tags are mutable; pin the commit)
14+
TELOSZERO_REF: "fcb9f5fc627619a8b06cd6b871c462b1d70d3aa4"
15+
EXPECTED_EOSIO_SYSTEM_ABI_SHA256: "5545f53b6b2e407acb45c6d14e283866c451d8e47c567baeb74284a55c93c9e1"
16+
EXPECTED_EOSIO_SYSTEM_WASM_SHA256: "c4dc8dad19768a7ac3bc3519111c6ea632faf9a829eae9366fcb51bba94969f4"
1117
jobs:
12-
ubuntu-2404-build:
13-
name: Ubuntu 22.04 | Build
14-
runs-on: ubuntu-latest
18+
ubuntu-2204-build:
19+
name: Ubuntu 22.04 | Spring/Telos Zero build
20+
runs-on: ubuntu-22.04
21+
container: apm2006/leap:v3.2.5-cdt4.0.1
22+
defaults:
23+
run:
24+
shell: bash
1525
steps:
1626
- name: Checkout
1727
uses: actions/checkout@v4
18-
- name: Build
28+
- name: Checkout TelosZero Core
29+
uses: actions/checkout@v4
30+
with:
31+
repository: TelosNetwork/teloszero-core
32+
ref: ${{ env.TELOSZERO_REF }}
33+
path: spring-src
34+
submodules: recursive
35+
- name: Install CDT
36+
run: |
37+
set -euo pipefail
38+
cdt_deb="/tmp/cdt_${CDT_VERSION}-1_amd64.deb"
39+
curl -L --retry 3 --fail \
40+
"https://github.com/AntelopeIO/cdt/releases/download/v${CDT_VERSION}/cdt_${CDT_VERSION}-1_amd64.deb" \
41+
-o "${cdt_deb}"
42+
echo "${CDT_DEB_SHA256} ${cdt_deb}" | sha256sum -c -
43+
dpkg -i "${cdt_deb}"
44+
cdt-cpp --version | grep "${CDT_VERSION}"
45+
test -f "/usr/opt/cdt/${CDT_VERSION}/include/eosiolib/contracts/eosio/instant_finality.hpp"
46+
test -f "/usr/opt/cdt/${CDT_VERSION}/include/eosiolib/core/eosio/crypto_bls_ext.hpp"
47+
- name: Build TelosZero tester libraries
48+
run: |
49+
set -euo pipefail
50+
cmake -S spring-src -B spring-build -DCMAKE_BUILD_TYPE=Release
51+
cmake --build spring-build --target eosio_testing -- -j "$(nproc)"
52+
test -f spring-build/libraries/testing/libeosio_testing.a
53+
test -d spring-build/lib/cmake/leap
54+
- name: Build contracts
55+
run: |
56+
set -euo pipefail
57+
cmake -S . -B build \
58+
-DCMAKE_BUILD_TYPE=Release \
59+
-DBUILD_TESTS=yes \
60+
-Dleap_DIR="${PWD}/spring-build/lib/cmake/leap"
61+
cmake --build build -- -j "$(nproc)" VERBOSE=1
62+
- name: Test
63+
run: |
64+
set -euo pipefail
65+
ctest --test-dir build/tests --output-on-failure -j "$(nproc)"
66+
- name: Report build artifact hashes
67+
run: |
68+
set -euo pipefail
69+
abi_hash="$(sha256sum build/contracts/eosio.system/eosio.system.abi | awk '{print $1}')"
70+
wasm_hash="$(sha256sum build/contracts/eosio.system/eosio.system.wasm | awk '{print $1}')"
71+
echo "eosio.system.abi ${abi_hash}"
72+
echo "eosio.system.wasm ${wasm_hash}"
73+
# Only enforce the expected release hashes on manually-dispatched runs
74+
# and release branches. Asserting on every PR would fail CI for any
75+
# legitimate contract change until the expected hashes are updated.
76+
- name: Assert release artifact hashes
77+
if: github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/heads/release/')
1978
run: |
20-
set -e
21-
export DOCKER="docker run --rm -v $(pwd):/root/target ${BUILDER_IMAGE}"
22-
docker pull ${BUILDER_IMAGE}
23-
echo ${DOCKER}
24-
echo =====
25-
mkdir build
26-
${DOCKER} bash -c "cd build && cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=yes -Dleap_DIR=/opt/leap/build/lib/cmake/leap .."
27-
echo =====
28-
${DOCKER} bash -c "cd build && make -j $(nproc) VERBOSE=1"
29-
echo =====
30-
${DOCKER} bash -c 'cd build/tests && ctest -j $(nproc)'
79+
set -euo pipefail
80+
abi_hash="$(sha256sum build/contracts/eosio.system/eosio.system.abi | awk '{print $1}')"
81+
wasm_hash="$(sha256sum build/contracts/eosio.system/eosio.system.wasm | awk '{print $1}')"
82+
test "${abi_hash}" = "${EXPECTED_EOSIO_SYSTEM_ABI_SHA256}"
83+
test "${wasm_hash}" = "${EXPECTED_EOSIO_SYSTEM_WASM_SHA256}"

contracts/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ option(SYSTEM_BLOCKCHAIN_PARAMETERS
1111
find_package(cdt)
1212

1313
set(CDT_VERSION_MIN "3.0")
14-
set(CDT_VERSION_SOFT_MAX "4.0")
14+
set(CDT_VERSION_SOFT_MAX "4.1")
1515
# set(CDT_VERSION_HARD_MAX "")
1616

1717
# Check the version of CDT

contracts/eosio.system/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ add_contract(
44
${CMAKE_CURRENT_SOURCE_DIR}/src/eosio.system.cpp
55
${CMAKE_CURRENT_SOURCE_DIR}/src/delegate_bandwidth.cpp
66
${CMAKE_CURRENT_SOURCE_DIR}/src/exchange_state.cpp
7+
${CMAKE_CURRENT_SOURCE_DIR}/src/finalizer_key.cpp
78
${CMAKE_CURRENT_SOURCE_DIR}/src/name_bidding.cpp
89
${CMAKE_CURRENT_SOURCE_DIR}/src/native.cpp
910
${CMAKE_CURRENT_SOURCE_DIR}/src/producer_pay.cpp

contracts/eosio.system/include/eosio.system/eosio.system.hpp

Lines changed: 127 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <eosio/singleton.hpp>
88
#include <eosio/system.hpp>
99
#include <eosio/time.hpp>
10+
#include <eosio/instant_finality.hpp>
1011

1112
#include <eosio.system/exchange_state.hpp>
1213
#include <eosio.system/native.hpp>
@@ -427,6 +428,75 @@ namespace eosiosystem {
427428
EOSLIB_SERIALIZE( producer_info2, (owner)(votepay_share)(last_votepay_share_update) )
428429
};
429430

431+
// finalizer_key_info stores information about a finalizer key.
432+
struct [[eosio::table("finkeys"), eosio::contract("eosio.system")]] finalizer_key_info {
433+
uint64_t id; // automatically generated ID for the key in the table
434+
name finalizer_name; // name of the finalizer owning the key
435+
std::string finalizer_key; // finalizer key in base64url format
436+
std::vector<char> finalizer_key_binary; // finalizer key in binary format in Affine little endian non-montgomery g1
437+
438+
uint64_t primary_key() const { return id; }
439+
uint64_t by_fin_name() const { return finalizer_name.value; }
440+
eosio::checksum256 by_fin_key() const { return eosio::sha256(finalizer_key_binary.data(), finalizer_key_binary.size()); }
441+
442+
bool is_active(uint64_t finalizer_active_key_id) const { return id == finalizer_active_key_id; }
443+
};
444+
445+
typedef eosio::multi_index<
446+
"finkeys"_n, finalizer_key_info,
447+
indexed_by<"byfinname"_n, const_mem_fun<finalizer_key_info, uint64_t, &finalizer_key_info::by_fin_name>>,
448+
indexed_by<"byfinkey"_n, const_mem_fun<finalizer_key_info, eosio::checksum256, &finalizer_key_info::by_fin_key>>
449+
> finalizer_keys_table;
450+
451+
// finalizer_info stores information about a finalizer.
452+
struct [[eosio::table("finalizers"), eosio::contract("eosio.system")]] finalizer_info {
453+
name finalizer_name; // finalizer's name
454+
uint64_t active_key_id; // finalizer's active finalizer key's id in finalizer_keys_table
455+
std::vector<char> active_key_binary; // active finalizer key in binary format
456+
uint32_t finalizer_key_count = 0; // number of finalizer keys registered by this finalizer
457+
458+
uint64_t primary_key() const { return finalizer_name.value; }
459+
};
460+
461+
typedef eosio::multi_index< "finalizers"_n, finalizer_info > finalizers_table;
462+
463+
// finalizer_auth_info stores a finalizer's key id and its finalizer authority.
464+
struct finalizer_auth_info {
465+
finalizer_auth_info() = default;
466+
explicit finalizer_auth_info(const finalizer_info& finalizer);
467+
468+
uint64_t key_id;
469+
eosio::finalizer_authority fin_authority;
470+
471+
bool operator==(const finalizer_auth_info& other) const {
472+
return key_id == other.key_id &&
473+
fin_authority.public_key == other.fin_authority.public_key;
474+
}
475+
476+
EOSLIB_SERIALIZE( finalizer_auth_info, (key_id)(fin_authority) )
477+
};
478+
479+
// A single entry storing information about last proposed finalizers.
480+
struct [[eosio::table("lastpropfins"), eosio::contract("eosio.system")]] last_prop_finalizers_info {
481+
std::vector<finalizer_auth_info> last_proposed_finalizers; // sorted by ascending finalizer key id
482+
483+
uint64_t primary_key() const { return 0; }
484+
485+
EOSLIB_SERIALIZE( last_prop_finalizers_info, (last_proposed_finalizers) )
486+
};
487+
488+
typedef eosio::multi_index< "lastpropfins"_n, last_prop_finalizers_info > last_prop_fins_table;
489+
490+
// A single entry storing next available finalizer key_id so IDs are never reused.
491+
struct [[eosio::table("finkeyidgen"), eosio::contract("eosio.system")]] fin_key_id_generator_info {
492+
uint64_t next_finalizer_key_id = 0;
493+
uint64_t primary_key() const { return 0; }
494+
495+
EOSLIB_SERIALIZE( fin_key_id_generator_info, (next_finalizer_key_id) )
496+
};
497+
498+
typedef eosio::multi_index< "finkeyidgen"_n, fin_key_id_generator_info > fin_key_id_gen_table;
499+
430500
// Voter info. Voter info stores information about the voter:
431501
// - `owner` the voter
432502
// - `proxy` the proxy set by the voter, if any
@@ -903,6 +973,11 @@ namespace eosiosystem {
903973
voters_table _voters;
904974
producers_table _producers;
905975
producers_table2 _producers2;
976+
finalizer_keys_table _finalizer_keys;
977+
finalizers_table _finalizers;
978+
last_prop_fins_table _last_prop_finalizers;
979+
std::optional<std::vector<finalizer_auth_info>> _last_prop_finalizers_cached;
980+
fin_key_id_gen_table _fin_key_id_generator;
906981
global_state_singleton _global;
907982
global_state2_singleton _global2;
908983
global_state3_singleton _global3;
@@ -1411,10 +1486,47 @@ namespace eosiosystem {
14111486
*
14121487
* Deactivate the block producer with account name `producer`.
14131488
* @param producer - the block producer account to unregister.
1414-
*/
1489+
*/
14151490
[[eosio::action]]
14161491
void unregprod( const name& producer );
14171492

1493+
/**
1494+
* Permanently transition to Savanna consensus by establishing the first finalizer policy.
1495+
*
1496+
* @pre Requires authority of the system contract.
1497+
* @pre The current Telos producer schedule must have active finalizer keys.
1498+
*/
1499+
[[eosio::action]]
1500+
void switchtosvnn();
1501+
1502+
/**
1503+
* Register a BLS finalizer key for a registered producer.
1504+
*
1505+
* @param finalizer_name - producer account registering the finalizer key.
1506+
* @param finalizer_key - public finalizer key in base64url format.
1507+
* @param proof_of_possession - proof of possession signature in base64url format.
1508+
*/
1509+
[[eosio::action]]
1510+
void regfinkey( const name& finalizer_name, const std::string& finalizer_key, const std::string& proof_of_possession );
1511+
1512+
/**
1513+
* Activate a registered finalizer key.
1514+
*
1515+
* @param finalizer_name - producer account activating the finalizer key.
1516+
* @param finalizer_key - registered public finalizer key.
1517+
*/
1518+
[[eosio::action]]
1519+
void actfinkey( const name& finalizer_name, const std::string& finalizer_key );
1520+
1521+
/**
1522+
* Delete a registered finalizer key.
1523+
*
1524+
* @param finalizer_name - producer account deleting the finalizer key.
1525+
* @param finalizer_key - registered public finalizer key.
1526+
*/
1527+
[[eosio::action]]
1528+
void delfinkey( const name& finalizer_name, const std::string& finalizer_key );
1529+
14181530
/**
14191531
* Set ram action sets the ram supply.
14201532
* @param max_ram_size - the amount of ram supply to set.
@@ -1786,14 +1898,25 @@ namespace eosiosystem {
17861898
// defined in voting.cpp
17871899
void register_producer( const name& producer, const eosio::block_signing_authority& producer_authority, const std::string& url, uint16_t location );
17881900
void update_elected_producers( const block_timestamp& timestamp );
1789-
void update_votes( const name& voter, const name& proxy, const std::vector<name>& producers, bool voting );
1901+
void update_votes( const name& voter, const name& proxy, const std::vector<name>& producers, bool voting, bool recalculating = false );
17901902
void propagate_weight_change( const voter_info& voter );
17911903
double update_producer_votepay_share( const producers_table2::const_iterator& prod_itr,
1792-
const time_point& ct,
1793-
double shares_rate, bool reset_to_zero = false );
1904+
const time_point& ct,
1905+
double shares_rate, bool reset_to_zero = false );
17941906
double update_total_votepay_share( const time_point& ct,
17951907
double additional_shares_delta = 0.0, double shares_rate_delta = 0.0 );
17961908

1909+
// defined in finalizer_key.cpp
1910+
bool is_savanna_consensus();
1911+
bool has_active_finalizer_key( const name& producer ) const;
1912+
void set_proposed_finalizers( std::vector<finalizer_auth_info> finalizers );
1913+
const std::vector<finalizer_auth_info>& get_last_proposed_finalizers();
1914+
uint64_t get_next_finalizer_key_id();
1915+
finalizers_table::const_iterator get_finalizer_itr( const name& finalizer_name ) const;
1916+
std::vector<finalizer_auth_info> get_finalizers_for_producers( const std::vector<producer_location_pair>& producers ) const;
1917+
bool active_schedule_matches_last_scheduled_producers( const std::vector<name>& active_schedule ) const;
1918+
std::vector<producer_location_pair> get_last_scheduled_producers() const;
1919+
17971920
template <auto system_contract::*...Ptrs>
17981921
class registration {
17991922
public:

contracts/eosio.system/src/delegate_bandwidth.cpp

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,6 @@ namespace eosiosystem {
263263
//create/update/delete refund
264264
auto net_balance = stake_net_delta;
265265
auto cpu_balance = stake_cpu_delta;
266-
bool need_deferred_trx = false;
267266

268267

269268
// net and cpu are same sign by assertions in delegatebw and undelegatebw
@@ -298,9 +297,6 @@ namespace eosiosystem {
298297

299298
if ( req->is_empty() ) {
300299
refunds_tbl.erase( req );
301-
need_deferred_trx = false;
302-
} else {
303-
need_deferred_trx = true;
304300
}
305301
} else if ( net_balance.amount < 0 || cpu_balance.amount < 0 ) { //need to create refund
306302
refunds_tbl.emplace( from, [&]( refund_request& r ) {
@@ -319,22 +315,13 @@ namespace eosiosystem {
319315
}
320316
r.request_time = current_time_point();
321317
});
322-
need_deferred_trx = true;
323318
} // else stake increase requested with no existing row in refunds_tbl -> nothing to do with refunds_tbl
324319
} /// end if is_delegating_to_self || is_undelegating
325320

326-
if ( need_deferred_trx ) {
327-
eosio::transaction out;
328-
out.actions.emplace_back( permission_level{from, active_permission},
329-
get_self(), "refund"_n,
330-
from
331-
);
332-
out.delay_sec = refund_delay_sec;
333-
eosio::cancel_deferred( from.value ); // TODO: Remove this line when replacing deferred transactions is fixed
334-
out.send( from.value, from, true );
335-
} else {
336-
eosio::cancel_deferred( from.value );
337-
}
321+
// Deferred transactions are disabled under Spring/Savanna, so the
322+
// refund is no longer scheduled automatically. Any pending refund
323+
// remains in refunds_tbl and must be claimed explicitly with the
324+
// refund action once refund_delay_sec has elapsed.
338325

339326
auto transfer_amount = net_balance + cpu_balance;
340327
if ( 0 < transfer_amount.amount ) {

0 commit comments

Comments
 (0)