Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
0e11ce4
Add Savanna finalizer support to system contract
molty365 May 12, 2026
8032d16
Guard Savanna switch on active schedule
molty365 May 12, 2026
40dfd38
Fix Savanna missed-block autokick
molty365 May 19, 2026
6ebd171
Make Savanna release checks Spring-compatible
molty365 Jun 5, 2026
79a0e8d
Make schedule metrics comparison membership-based
TheJudii Jun 10, 2026
39fb3db
Remove deferred refund transactions for Spring compatibility
TheJudii Jun 10, 2026
817a377
Gate release hash assertion and pin TelosZero ref by commit
TheJudii Jun 10, 2026
0899a80
Add finalizer key and Savanna transition tests
TheJudii Jun 10, 2026
de46331
Update expected eosio.system WASM hash for fixed contract
TheJudii Jun 10, 2026
4042029
Fix stale BP rotation duplicate schedules
molty365 Jun 9, 2026
e92aac0
Update rollup test and release hash
TheJudii Jun 10, 2026
a007bb6
Use bash for Spring build workflow
TheJudii Jun 10, 2026
531daee
Fix CDT header checks in workflow
TheJudii Jun 10, 2026
cd529d1
Add autokick threshold and location-swap regression tests
TheJudii Jun 11, 2026
05a3998
Port upstream reference-contracts finalizer key test suite
TheJudii Jun 11, 2026
b6c3da6
Audit remediation: fix recalc halt, EVM-vote auth/underflow, kick und…
molty365 Jun 17, 2026
08c0863
Address review: scope finalizer-key deletion guard, order EVM account…
molty365 Jun 17, 2026
acb8e56
Floor global vote weight once after the delta loop; fix TEDP comment
molty365 Jun 18, 2026
12aaa85
Restore producer kick penalty registration guard
molty365 Jun 30, 2026
e0aff4e
Add system contract regression tests
molty365 Jul 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 69 additions & 16 deletions .github/workflows/ubuntu-2204.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,78 @@ on:
- "release/*"
pull_request:
types: [assigned, opened, synchronize, reopened, labeled]
workflow_dispatch:
env:
BUILDER_IMAGE: "apm2006/leap:v3.2.5-cdt4.0.1"
CDT_VERSION: "4.1.1"
CDT_DEB_SHA256: "d946e6b64f297442d19e486401aa49f956080b07a1fa6335f29976106175b588"
# Commit SHA of tag teloszero-v1.2.2 (tags are mutable; pin the commit)
TELOSZERO_REF: "fcb9f5fc627619a8b06cd6b871c462b1d70d3aa4"
EXPECTED_EOSIO_SYSTEM_ABI_SHA256: "5545f53b6b2e407acb45c6d14e283866c451d8e47c567baeb74284a55c93c9e1"
EXPECTED_EOSIO_SYSTEM_WASM_SHA256: "c4dc8dad19768a7ac3bc3519111c6ea632faf9a829eae9366fcb51bba94969f4"
jobs:
ubuntu-2404-build:
name: Ubuntu 22.04 | Build
runs-on: ubuntu-latest
ubuntu-2204-build:
name: Ubuntu 22.04 | Spring/Telos Zero build
runs-on: ubuntu-22.04
container: apm2006/leap:v3.2.5-cdt4.0.1
defaults:
run:
shell: bash
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Build
- name: Checkout TelosZero Core
uses: actions/checkout@v4
with:
repository: TelosNetwork/teloszero-core
ref: ${{ env.TELOSZERO_REF }}
path: spring-src
submodules: recursive
- name: Install CDT
run: |
set -euo pipefail
cdt_deb="/tmp/cdt_${CDT_VERSION}-1_amd64.deb"
curl -L --retry 3 --fail \
"https://github.com/AntelopeIO/cdt/releases/download/v${CDT_VERSION}/cdt_${CDT_VERSION}-1_amd64.deb" \
-o "${cdt_deb}"
echo "${CDT_DEB_SHA256} ${cdt_deb}" | sha256sum -c -
dpkg -i "${cdt_deb}"
cdt-cpp --version | grep "${CDT_VERSION}"
test -f "/usr/opt/cdt/${CDT_VERSION}/include/eosiolib/contracts/eosio/instant_finality.hpp"
test -f "/usr/opt/cdt/${CDT_VERSION}/include/eosiolib/core/eosio/crypto_bls_ext.hpp"
- name: Build TelosZero tester libraries
run: |
set -euo pipefail
cmake -S spring-src -B spring-build -DCMAKE_BUILD_TYPE=Release
cmake --build spring-build --target eosio_testing -- -j "$(nproc)"
test -f spring-build/libraries/testing/libeosio_testing.a
test -d spring-build/lib/cmake/leap
- name: Build contracts
run: |
set -euo pipefail
cmake -S . -B build \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_TESTS=yes \
-Dleap_DIR="${PWD}/spring-build/lib/cmake/leap"
cmake --build build -- -j "$(nproc)" VERBOSE=1
- name: Test
run: |
set -euo pipefail
ctest --test-dir build/tests --output-on-failure -j "$(nproc)"
- name: Report build artifact hashes
run: |
set -euo pipefail
abi_hash="$(sha256sum build/contracts/eosio.system/eosio.system.abi | awk '{print $1}')"
wasm_hash="$(sha256sum build/contracts/eosio.system/eosio.system.wasm | awk '{print $1}')"
echo "eosio.system.abi ${abi_hash}"
echo "eosio.system.wasm ${wasm_hash}"
# Only enforce the expected release hashes on manually-dispatched runs
# and release branches. Asserting on every PR would fail CI for any
# legitimate contract change until the expected hashes are updated.
- name: Assert release artifact hashes
if: github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/heads/release/')
run: |
set -e
export DOCKER="docker run --rm -v $(pwd):/root/target ${BUILDER_IMAGE}"
docker pull ${BUILDER_IMAGE}
echo ${DOCKER}
echo =====
mkdir build
${DOCKER} bash -c "cd build && cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=yes -Dleap_DIR=/opt/leap/build/lib/cmake/leap .."
echo =====
${DOCKER} bash -c "cd build && make -j $(nproc) VERBOSE=1"
echo =====
${DOCKER} bash -c 'cd build/tests && ctest -j $(nproc)'
set -euo pipefail
abi_hash="$(sha256sum build/contracts/eosio.system/eosio.system.abi | awk '{print $1}')"
wasm_hash="$(sha256sum build/contracts/eosio.system/eosio.system.wasm | awk '{print $1}')"
test "${abi_hash}" = "${EXPECTED_EOSIO_SYSTEM_ABI_SHA256}"
test "${wasm_hash}" = "${EXPECTED_EOSIO_SYSTEM_WASM_SHA256}"
2 changes: 1 addition & 1 deletion contracts/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ option(SYSTEM_BLOCKCHAIN_PARAMETERS
find_package(cdt)

set(CDT_VERSION_MIN "3.0")
set(CDT_VERSION_SOFT_MAX "4.0")
set(CDT_VERSION_SOFT_MAX "4.1")
# set(CDT_VERSION_HARD_MAX "")

# Check the version of CDT
Expand Down
1 change: 1 addition & 0 deletions contracts/eosio.system/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ add_contract(
${CMAKE_CURRENT_SOURCE_DIR}/src/eosio.system.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/delegate_bandwidth.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/exchange_state.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/finalizer_key.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/name_bidding.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/native.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/producer_pay.cpp
Expand Down
131 changes: 127 additions & 4 deletions contracts/eosio.system/include/eosio.system/eosio.system.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <eosio/singleton.hpp>
#include <eosio/system.hpp>
#include <eosio/time.hpp>
#include <eosio/instant_finality.hpp>

#include <eosio.system/exchange_state.hpp>
#include <eosio.system/native.hpp>
Expand Down Expand Up @@ -427,6 +428,75 @@ namespace eosiosystem {
EOSLIB_SERIALIZE( producer_info2, (owner)(votepay_share)(last_votepay_share_update) )
};

// finalizer_key_info stores information about a finalizer key.
struct [[eosio::table("finkeys"), eosio::contract("eosio.system")]] finalizer_key_info {
uint64_t id; // automatically generated ID for the key in the table
name finalizer_name; // name of the finalizer owning the key
std::string finalizer_key; // finalizer key in base64url format
std::vector<char> finalizer_key_binary; // finalizer key in binary format in Affine little endian non-montgomery g1

uint64_t primary_key() const { return id; }
uint64_t by_fin_name() const { return finalizer_name.value; }
eosio::checksum256 by_fin_key() const { return eosio::sha256(finalizer_key_binary.data(), finalizer_key_binary.size()); }

bool is_active(uint64_t finalizer_active_key_id) const { return id == finalizer_active_key_id; }
};

typedef eosio::multi_index<
"finkeys"_n, finalizer_key_info,
indexed_by<"byfinname"_n, const_mem_fun<finalizer_key_info, uint64_t, &finalizer_key_info::by_fin_name>>,
indexed_by<"byfinkey"_n, const_mem_fun<finalizer_key_info, eosio::checksum256, &finalizer_key_info::by_fin_key>>
> finalizer_keys_table;

// finalizer_info stores information about a finalizer.
struct [[eosio::table("finalizers"), eosio::contract("eosio.system")]] finalizer_info {
name finalizer_name; // finalizer's name
uint64_t active_key_id; // finalizer's active finalizer key's id in finalizer_keys_table
std::vector<char> active_key_binary; // active finalizer key in binary format
uint32_t finalizer_key_count = 0; // number of finalizer keys registered by this finalizer

uint64_t primary_key() const { return finalizer_name.value; }
};

typedef eosio::multi_index< "finalizers"_n, finalizer_info > finalizers_table;

// finalizer_auth_info stores a finalizer's key id and its finalizer authority.
struct finalizer_auth_info {
finalizer_auth_info() = default;
explicit finalizer_auth_info(const finalizer_info& finalizer);

uint64_t key_id;
eosio::finalizer_authority fin_authority;

bool operator==(const finalizer_auth_info& other) const {
return key_id == other.key_id &&
fin_authority.public_key == other.fin_authority.public_key;
}

EOSLIB_SERIALIZE( finalizer_auth_info, (key_id)(fin_authority) )
};

// A single entry storing information about last proposed finalizers.
struct [[eosio::table("lastpropfins"), eosio::contract("eosio.system")]] last_prop_finalizers_info {
std::vector<finalizer_auth_info> last_proposed_finalizers; // sorted by ascending finalizer key id

uint64_t primary_key() const { return 0; }

EOSLIB_SERIALIZE( last_prop_finalizers_info, (last_proposed_finalizers) )
};

typedef eosio::multi_index< "lastpropfins"_n, last_prop_finalizers_info > last_prop_fins_table;

// A single entry storing next available finalizer key_id so IDs are never reused.
struct [[eosio::table("finkeyidgen"), eosio::contract("eosio.system")]] fin_key_id_generator_info {
uint64_t next_finalizer_key_id = 0;
uint64_t primary_key() const { return 0; }

EOSLIB_SERIALIZE( fin_key_id_generator_info, (next_finalizer_key_id) )
};

typedef eosio::multi_index< "finkeyidgen"_n, fin_key_id_generator_info > fin_key_id_gen_table;

// Voter info. Voter info stores information about the voter:
// - `owner` the voter
// - `proxy` the proxy set by the voter, if any
Expand Down Expand Up @@ -903,6 +973,11 @@ namespace eosiosystem {
voters_table _voters;
producers_table _producers;
producers_table2 _producers2;
finalizer_keys_table _finalizer_keys;
finalizers_table _finalizers;
last_prop_fins_table _last_prop_finalizers;
std::optional<std::vector<finalizer_auth_info>> _last_prop_finalizers_cached;
fin_key_id_gen_table _fin_key_id_generator;
global_state_singleton _global;
global_state2_singleton _global2;
global_state3_singleton _global3;
Expand Down Expand Up @@ -1411,10 +1486,47 @@ namespace eosiosystem {
*
* Deactivate the block producer with account name `producer`.
* @param producer - the block producer account to unregister.
*/
*/
[[eosio::action]]
void unregprod( const name& producer );

/**
* Permanently transition to Savanna consensus by establishing the first finalizer policy.
*
* @pre Requires authority of the system contract.
* @pre The current Telos producer schedule must have active finalizer keys.
*/
[[eosio::action]]
void switchtosvnn();

/**
* Register a BLS finalizer key for a registered producer.
*
* @param finalizer_name - producer account registering the finalizer key.
* @param finalizer_key - public finalizer key in base64url format.
* @param proof_of_possession - proof of possession signature in base64url format.
*/
[[eosio::action]]
void regfinkey( const name& finalizer_name, const std::string& finalizer_key, const std::string& proof_of_possession );

/**
* Activate a registered finalizer key.
*
* @param finalizer_name - producer account activating the finalizer key.
* @param finalizer_key - registered public finalizer key.
*/
[[eosio::action]]
void actfinkey( const name& finalizer_name, const std::string& finalizer_key );

/**
* Delete a registered finalizer key.
*
* @param finalizer_name - producer account deleting the finalizer key.
* @param finalizer_key - registered public finalizer key.
*/
[[eosio::action]]
void delfinkey( const name& finalizer_name, const std::string& finalizer_key );

/**
* Set ram action sets the ram supply.
* @param max_ram_size - the amount of ram supply to set.
Expand Down Expand Up @@ -1786,14 +1898,25 @@ namespace eosiosystem {
// defined in voting.cpp
void register_producer( const name& producer, const eosio::block_signing_authority& producer_authority, const std::string& url, uint16_t location );
void update_elected_producers( const block_timestamp& timestamp );
void update_votes( const name& voter, const name& proxy, const std::vector<name>& producers, bool voting );
void update_votes( const name& voter, const name& proxy, const std::vector<name>& producers, bool voting, bool recalculating = false );
void propagate_weight_change( const voter_info& voter );
double update_producer_votepay_share( const producers_table2::const_iterator& prod_itr,
const time_point& ct,
double shares_rate, bool reset_to_zero = false );
const time_point& ct,
double shares_rate, bool reset_to_zero = false );
double update_total_votepay_share( const time_point& ct,
double additional_shares_delta = 0.0, double shares_rate_delta = 0.0 );

// defined in finalizer_key.cpp
bool is_savanna_consensus();
bool has_active_finalizer_key( const name& producer ) const;
void set_proposed_finalizers( std::vector<finalizer_auth_info> finalizers );
const std::vector<finalizer_auth_info>& get_last_proposed_finalizers();
uint64_t get_next_finalizer_key_id();
finalizers_table::const_iterator get_finalizer_itr( const name& finalizer_name ) const;
std::vector<finalizer_auth_info> get_finalizers_for_producers( const std::vector<producer_location_pair>& producers ) const;
bool active_schedule_matches_last_scheduled_producers( const std::vector<name>& active_schedule ) const;
std::vector<producer_location_pair> get_last_scheduled_producers() const;

template <auto system_contract::*...Ptrs>
class registration {
public:
Expand Down
21 changes: 4 additions & 17 deletions contracts/eosio.system/src/delegate_bandwidth.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,6 @@ namespace eosiosystem {
//create/update/delete refund
auto net_balance = stake_net_delta;
auto cpu_balance = stake_cpu_delta;
bool need_deferred_trx = false;


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

if ( req->is_empty() ) {
refunds_tbl.erase( req );
need_deferred_trx = false;
} else {
need_deferred_trx = true;
}
} else if ( net_balance.amount < 0 || cpu_balance.amount < 0 ) { //need to create refund
refunds_tbl.emplace( from, [&]( refund_request& r ) {
Expand All @@ -319,22 +315,13 @@ namespace eosiosystem {
}
r.request_time = current_time_point();
});
need_deferred_trx = true;
} // else stake increase requested with no existing row in refunds_tbl -> nothing to do with refunds_tbl
} /// end if is_delegating_to_self || is_undelegating

if ( need_deferred_trx ) {
eosio::transaction out;
out.actions.emplace_back( permission_level{from, active_permission},
get_self(), "refund"_n,
from
);
out.delay_sec = refund_delay_sec;
eosio::cancel_deferred( from.value ); // TODO: Remove this line when replacing deferred transactions is fixed
out.send( from.value, from, true );
} else {
eosio::cancel_deferred( from.value );
}
// Deferred transactions are disabled under Spring/Savanna, so the
// refund is no longer scheduled automatically. Any pending refund
// remains in refunds_tbl and must be claimed explicitly with the
// refund action once refund_delay_sec has elapsed.

auto transfer_amount = net_balance + cpu_balance;
if ( 0 < transfer_amount.amount ) {
Expand Down
Loading
Loading