Merge pull request #1568 from darcys22/sync-batching-keep-snl-state

Keep SNL state correct during resyncing of sqlite submodule
This commit is contained in:
Jason Rhinelander 2022-06-19 14:31:58 -03:00 committed by GitHub
commit d826fbee03
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 283 additions and 363 deletions

View File

@ -51,7 +51,7 @@ message(STATUS "CMake version ${CMAKE_VERSION}")
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.12 CACHE STRING "macOS deployment target (Apple clang only)")
project(oxen
VERSION 10.1.1
VERSION 10.1.2
LANGUAGES CXX C)
set(OXEN_RELEASE_CODENAME "Wistful Wagyu")

View File

@ -39,8 +39,15 @@
#include <sys/param.h>
#endif
#if defined(__ANDROID__)
#include <byteswap.h>
#if defined(__clang__) || defined(__GNUC__)
# define epee_int_utils_bswap_64(x) __builtin_bswap64(x)
# define epee_int_utils_bswap_32(x) __builtin_bswap32(x)
#else
# if defined(__linux__) || defined(__ANDROID__)
# include <byteswap.h>
# endif
# define epee_int_utils_bswap_64(x) bswap_64(x)
# define epee_int_utils_bswap_32(x) bswap_32(x)
#endif
#if defined(__sun) && defined(__SVR4)
@ -71,6 +78,7 @@ static inline uint64_t rol64(uint64_t x, int r) {
#endif
#ifndef __SIZEOF_INT128__
static inline uint64_t hi_dword(uint64_t val) {
return val >> 32;
}
@ -79,7 +87,27 @@ static inline uint64_t lo_dword(uint64_t val) {
return val & 0xFFFFFFFF;
}
static inline uint64_t div_with_remainder(uint64_t dividend, uint32_t divisor, uint32_t* remainder) {
dividend |= ((uint64_t)*remainder) << 32;
*remainder = dividend % divisor;
return dividend / divisor;
}
static inline bool shl128(uint64_t* hi, uint64_t* lo) {
bool carry = ((*hi) >> 63);
*hi <<= 1;
*hi += ((*lo) >> 63);
*lo <<= 1;
return carry;
}
#endif
static inline uint64_t mul128(uint64_t multiplier, uint64_t multiplicand, uint64_t* product_hi) {
#ifdef __SIZEOF_INT128__
unsigned __int128 result = (unsigned __int128) multiplier * (unsigned __int128) multiplicand;
*product_hi = result >> 64;
return (uint64_t) result;
#else
// multiplier = ab = a * 2^32 + b
// multiplicand = cd = c * 2^32 + d
// ab * cd = a * c * 2^64 + (a * d + b * c) * 2^32 + b * d
@ -103,16 +131,16 @@ static inline uint64_t mul128(uint64_t multiplier, uint64_t multiplicand, uint64
assert(ac <= *product_hi);
return product_lo;
}
static inline uint64_t div_with_reminder(uint64_t dividend, uint32_t divisor, uint32_t* remainder) {
dividend |= ((uint64_t)*remainder) << 32;
*remainder = dividend % divisor;
return dividend / divisor;
#endif
}
// Long division with 2^32 base
static inline uint32_t div128_32(uint64_t dividend_hi, uint64_t dividend_lo, uint32_t divisor, uint64_t* quotient_hi, uint64_t* quotient_lo) {
static inline void div128_32(uint64_t dividend_hi, uint64_t dividend_lo, uint32_t divisor, uint64_t* quotient_hi, uint64_t* quotient_lo) {
#ifdef __SIZEOF_INT128__
unsigned __int128 result = (((unsigned __int128) dividend_hi) << 64 | ((unsigned __int128) dividend_lo)) / divisor;
*quotient_lo = (uint64_t) result;
*quotient_hi = (uint64_t)(result >> 64);
#else
uint64_t dividend_dwords[4];
uint32_t remainder = 0;
@ -121,24 +149,21 @@ static inline uint32_t div128_32(uint64_t dividend_hi, uint64_t dividend_lo, uin
dividend_dwords[1] = hi_dword(dividend_lo);
dividend_dwords[0] = lo_dword(dividend_lo);
*quotient_hi = div_with_reminder(dividend_dwords[3], divisor, &remainder) << 32;
*quotient_hi |= div_with_reminder(dividend_dwords[2], divisor, &remainder);
*quotient_lo = div_with_reminder(dividend_dwords[1], divisor, &remainder) << 32;
*quotient_lo |= div_with_reminder(dividend_dwords[0], divisor, &remainder);
return remainder;
*quotient_hi = div_with_remainder(dividend_dwords[3], divisor, &remainder) << 32;
*quotient_hi |= div_with_remainder(dividend_dwords[2], divisor, &remainder);
*quotient_lo = div_with_remainder(dividend_dwords[1], divisor, &remainder) << 32;
*quotient_lo |= div_with_remainder(dividend_dwords[0], divisor, &remainder);
#endif
}
static inline bool shl128(uint64_t* hi, uint64_t* lo) {
bool carry = ((*hi) >> 63);
*hi <<= 1;
*hi += ((*lo) >> 63);
*lo <<= 1;
return carry;
}
// Long division with 2^64 base
static inline uint64_t div128_64(uint64_t dividend_hi, uint64_t dividend_lo, uint64_t divisor, uint64_t* quotient_hi, uint64_t* quotient_lo) {
static inline void div128_64(uint64_t dividend_hi, uint64_t dividend_lo, uint64_t divisor, uint64_t* quotient_hi, uint64_t* quotient_lo) {
#ifdef __SIZEOF_INT128__
unsigned __int128 result = (((unsigned __int128) dividend_hi) << 64 | ((unsigned __int128) dividend_lo)) / divisor;
*quotient_lo = (uint64_t) result;
*quotient_hi = (uint64_t)(result >> 64);
#else
uint64_t remainder = 0;
for (size_t i = 0; i < 128; i++) {
bool carry = remainder >> 63;
@ -152,125 +177,23 @@ static inline uint64_t div128_64(uint64_t dividend_hi, uint64_t dividend_lo, uin
}
*quotient_hi = dividend_hi;
*quotient_lo = dividend_lo;
return remainder;
#endif
}
// Calculates a*b/c, using 128-bit precision to avoid overflow. This assumes that the result is
// 64-bits, but only checks it (via assertion) in debug builds. As such you should only call this
// when this is true: for instance, when c is known to be greater than either a or b.
static inline uint64_t mul128_div64(uint64_t a, uint64_t b, uint64_t c) {
#ifdef __SIZEOF_INT128__
return (uint64_t) ((unsigned __int128) a) * ((unsigned __int128) b) / ((unsigned __int128) c);
#else
uint64_t hi;
uint64_t lo = mul128(a, b, &hi);
uint64_t resulthi, resultlo;
div128_64(hi, lo, c, &resulthi, &resultlo);
assert(resulthi == 0);
return resultlo;
}
#define IDENT16(x) ((uint16_t) (x))
#define IDENT32(x) ((uint32_t) (x))
#define IDENT64(x) ((uint64_t) (x))
#define SWAP16(x) ((((uint16_t) (x) & 0x00ff) << 8) | \
(((uint16_t) (x) & 0xff00) >> 8))
#define SWAP32(x) ((((uint32_t) (x) & 0x000000ff) << 24) | \
(((uint32_t) (x) & 0x0000ff00) << 8) | \
(((uint32_t) (x) & 0x00ff0000) >> 8) | \
(((uint32_t) (x) & 0xff000000) >> 24))
#define SWAP64(x) ((((uint64_t) (x) & 0x00000000000000ff) << 56) | \
(((uint64_t) (x) & 0x000000000000ff00) << 40) | \
(((uint64_t) (x) & 0x0000000000ff0000) << 24) | \
(((uint64_t) (x) & 0x00000000ff000000) << 8) | \
(((uint64_t) (x) & 0x000000ff00000000) >> 8) | \
(((uint64_t) (x) & 0x0000ff0000000000) >> 24) | \
(((uint64_t) (x) & 0x00ff000000000000) >> 40) | \
(((uint64_t) (x) & 0xff00000000000000) >> 56))
static inline uint16_t ident16(uint16_t x) { return x; }
static inline uint32_t ident32(uint32_t x) { return x; }
static inline uint64_t ident64(uint64_t x) { return x; }
#ifndef __OpenBSD__
# if defined(__ANDROID__) && defined(__swap16) && !defined(swap16)
# define swap16 __swap16
# elif !defined(swap16)
static inline uint16_t swap16(uint16_t x) {
return ((x & 0x00ff) << 8) | ((x & 0xff00) >> 8);
}
# endif
# if defined(__ANDROID__) && defined(__swap32) && !defined(swap32)
# define swap32 __swap32
# elif !defined(swap32)
static inline uint32_t swap32(uint32_t x) {
x = ((x & 0x00ff00ff) << 8) | ((x & 0xff00ff00) >> 8);
return (x << 16) | (x >> 16);
}
# endif
# if defined(__ANDROID__) && defined(__swap64) && !defined(swap64)
# define swap64 __swap64
# elif !defined(swap64)
static inline uint64_t swap64(uint64_t x) {
x = ((x & 0x00ff00ff00ff00ff) << 8) | ((x & 0xff00ff00ff00ff00) >> 8);
x = ((x & 0x0000ffff0000ffff) << 16) | ((x & 0xffff0000ffff0000) >> 16);
return (x << 32) | (x >> 32);
}
# endif
#endif /* __OpenBSD__ */
#if defined(__GNUC__)
#define UNUSED __attribute__((unused))
#else
#define UNUSED
#endif
static inline void mem_inplace_ident(void *mem UNUSED, size_t n UNUSED) { }
#undef UNUSED
static inline void mem_inplace_swap16(void *mem, size_t n) {
size_t i;
for (i = 0; i < n; i++) {
((uint16_t *) mem)[i] = swap16(((const uint16_t *) mem)[i]);
}
}
static inline void mem_inplace_swap32(void *mem, size_t n) {
size_t i;
for (i = 0; i < n; i++) {
((uint32_t *) mem)[i] = swap32(((const uint32_t *) mem)[i]);
}
}
static inline void mem_inplace_swap64(void *mem, size_t n) {
size_t i;
for (i = 0; i < n; i++) {
((uint64_t *) mem)[i] = swap64(((const uint64_t *) mem)[i]);
}
}
static inline void memcpy_ident16(void *dst, const void *src, size_t n) {
memcpy(dst, src, 2 * n);
}
static inline void memcpy_ident32(void *dst, const void *src, size_t n) {
memcpy(dst, src, 4 * n);
}
static inline void memcpy_ident64(void *dst, const void *src, size_t n) {
memcpy(dst, src, 8 * n);
}
static inline void memcpy_swap16(void *dst, const void *src, size_t n) {
size_t i;
for (i = 0; i < n; i++) {
((uint16_t *) dst)[i] = swap16(((const uint16_t *) src)[i]);
}
}
static inline void memcpy_swap32(void *dst, const void *src, size_t n) {
size_t i;
for (i = 0; i < n; i++) {
((uint32_t *) dst)[i] = swap32(((const uint32_t *) src)[i]);
}
}
static inline void memcpy_swap64(void *dst, const void *src, size_t n) {
size_t i;
for (i = 0; i < n; i++) {
((uint64_t *) dst)[i] = swap64(((const uint64_t *) src)[i]);
}
}
#ifdef _MSC_VER
@ -284,55 +207,28 @@ static_assert(false, "BYTE_ORDER is undefined. Perhaps, GNU extensions are not e
#endif
#if BYTE_ORDER == LITTLE_ENDIAN
#define SWAP16LE IDENT16
#define SWAP16BE SWAP16
#define swap16le ident16
#define swap16be swap16
#define mem_inplace_swap16le mem_inplace_ident
#define mem_inplace_swap16be mem_inplace_swap16
#define memcpy_swap16le memcpy_ident16
#define memcpy_swap16be memcpy_swap16
#define SWAP32LE IDENT32
#define SWAP32BE SWAP32
#define swap32le ident32
#define swap32be swap32
#define mem_inplace_swap32le mem_inplace_ident
#define mem_inplace_swap32be mem_inplace_swap32
#define memcpy_swap32le memcpy_ident32
#define memcpy_swap32be memcpy_swap32
#define SWAP64LE IDENT64
#define SWAP64BE SWAP64
#define swap64le ident64
#define swap64be swap64
#define mem_inplace_swap64le mem_inplace_ident
#define mem_inplace_swap64be mem_inplace_swap64
#define memcpy_swap64le memcpy_ident64
#define memcpy_swap64be memcpy_swap64
#endif
#if BYTE_ORDER == BIG_ENDIAN
#define SWAP16BE IDENT16
#define SWAP16LE SWAP16
#define swap16be ident16
#define swap16le swap16
#define mem_inplace_swap16be mem_inplace_ident
#define mem_inplace_swap16le mem_inplace_swap16
#define memcpy_swap16be memcpy_ident16
#define memcpy_swap16le memcpy_swap16
#define SWAP32BE IDENT32
#define SWAP32LE SWAP32
#define swap32be ident32
#define swap32le swap32
#define mem_inplace_swap32be mem_inplace_ident
#define mem_inplace_swap32le mem_inplace_swap32
#define memcpy_swap32be memcpy_ident32
#define memcpy_swap32le memcpy_swap32
#define SWAP64BE IDENT64
#define SWAP64LE SWAP64
#define swap64be ident64
#define swap64le swap64
#define mem_inplace_swap64be mem_inplace_ident
#define mem_inplace_swap64le mem_inplace_swap64
#define memcpy_swap64be memcpy_ident64
#define memcpy_swap64le memcpy_swap64
#define SWAP64LE(x) ((uint64_t) (x))
#define SWAP64BE epee_int_utils_bswap_64
#define SWAP32LE(x) ((uint32_t) (x))
#define SWAP32BE(x) epee_int_utils_bswap_32
static inline void memcpy_swap64le(void *dst, const void *src, size_t n) {
memcpy(dst, src, 8 * n);
}
#else
#define SWAP64BE(x) ((uint64_t) (x))
#define SWAP64LE epee_int_utils_bswap_64
#define SWAP32BE(x) ((uint32_t) (x))
#define SWAP32LE(x) epee_int_utils_bswap_32
static inline void memcpy_swap64le(void *dst, const void *src, size_t n) {
size_t i;
for (i = 0; i < n; i++) {
((uint64_t *) dst)[i] = swap64(((const uint64_t *) src)[i]);
}
}
#endif

View File

@ -53,20 +53,27 @@ namespace cryptonote {
create_schema();
}
upgrade_schema();
height = prepared_get<int64_t>("SELECT height FROM batch_db_info");
}
void BlockchainSQLite::create_schema() {
LOG_PRINT_L3("BlockchainDB_SQLITE::" << __func__);
db.exec(R"(
auto& netconf = cryptonote::get_config(m_nettype);
db.exec(fmt::format(R"(
CREATE TABLE batched_payments_accrued(
address VARCHAR NOT NULL,
amount BIGINT NOT NULL,
payout_offset INTEGER NOT NULL,
PRIMARY KEY(address),
CHECK(amount >= 0)
);
CREATE INDEX batched_payments_accrued_payout_offset_idx ON batched_payments_accrued(payout_offset);
CREATE TRIGGER batch_payments_delete_empty AFTER UPDATE ON batched_payments_accrued
FOR EACH ROW WHEN NEW.amount = 0 BEGIN
DELETE FROM batched_payments_accrued WHERE address = NEW.address;
@ -105,14 +112,68 @@ namespace cryptonote {
CREATE TRIGGER rollback_payment INSTEAD OF DELETE ON batched_payments_paid
FOR EACH ROW BEGIN
DELETE FROM batched_payments_raw WHERE address = OLD.address AND height_paid = OLD.height_paid;
INSERT INTO batched_payments_accrued(address, amount) VALUES(OLD.address, OLD.amount)
INSERT INTO batched_payments_accrued(address, payout_offset, amount) VALUES(OLD.address, OLD.height_paid % {}, OLD.amount)
ON CONFLICT(address) DO UPDATE SET amount = (amount + excluded.amount);
END;
)");
)",
netconf.BATCHING_INTERVAL));
MDEBUG("Database setup complete");
}
void BlockchainSQLite::upgrade_schema() {
bool have_offset = false;
SQLite::Statement msg_cols{db, "PRAGMA main.table_info(batched_payments_accrued)"};
while (msg_cols.executeStep()) {
auto [cid, name] = db::get<int64_t, std::string>(msg_cols);
if (name == "payout_offset")
have_offset = true;
}
if (!have_offset) {
MINFO("Adding payout_offset to batching db");
auto& netconf = get_config(m_nettype);
SQLite::Transaction transaction{
db,
SQLite::TransactionBehavior::IMMEDIATE
};
db.exec(fmt::format(R"(
ALTER TABLE batched_payments_accrued ADD COLUMN payout_offset INTEGER NOT NULL DEFAULT -1;
CREATE INDEX batched_payments_accrued_payout_offset_idx ON batched_payments_accrued(payout_offset);
DROP TRIGGER rollback_payment;
CREATE TRIGGER rollback_payment INSTEAD OF DELETE ON batched_payments_paid
FOR EACH ROW BEGIN
DELETE FROM batched_payments_raw WHERE address = OLD.address AND height_paid = OLD.height_paid;
INSERT INTO batched_payments_accrued(address, payout_offset, amount) VALUES(OLD.address, OLD.height_paid % {}, OLD.amount)
ON CONFLICT(address) DO UPDATE SET amount = (amount + excluded.amount);
END;
)", netconf.BATCHING_INTERVAL));
auto st = prepared_st("UPDATE batched_payments_accrued SET payout_offset = ? WHERE address = ?");
for (const auto& address : prepared_results<std::string>("SELECT address from batched_payments_accrued")) {
cryptonote::address_parse_info addr_info{};
cryptonote::get_account_address_from_str(addr_info, m_nettype, address);
auto offset = static_cast<int>(addr_info.address.modulus(netconf.BATCHING_INTERVAL));
exec_query(st, offset, address);
st->reset();
}
auto count = prepared_get<int>("SELECT COUNT(*) FROM batched_payments_accrued WHERE payout_offset NOT BETWEEN 0 AND ?",
static_cast<int>(netconf.BATCHING_INTERVAL));
if (count != 0) {
constexpr auto error = "Batching db update to add offsets failed: not all addresses were converted";
MFATAL(error);
throw std::runtime_error{error};
}
transaction.commit();
}
}
void BlockchainSQLite::reset_database() {
LOG_PRINT_L3("BlockchainDB_SQLITE::" << __func__);
@ -149,19 +210,31 @@ namespace cryptonote {
update_height(height - 1);
}
// Must be called with the address_str_cache_mutex held!
const std::string& BlockchainSQLite::get_address_str(const account_public_address& addr)
{
auto& address_str = address_str_cache[addr];
if (address_str.empty())
address_str = cryptonote::get_account_address_as_str(m_nettype, 0, addr);
return address_str;
}
bool BlockchainSQLite::add_sn_rewards(const std::vector<cryptonote::batch_sn_payment>& payments) {
LOG_PRINT_L3("BlockchainDB_SQLITE::" << __func__);
auto insert_payment = prepared_st(
"INSERT INTO batched_payments_accrued (address, amount) VALUES (?, ?)"
"INSERT INTO batched_payments_accrued (address, payout_offset, amount) VALUES (?, ?, ?)"
" ON CONFLICT (address) DO UPDATE SET amount = amount + excluded.amount");
const auto& netconf = get_config(m_nettype);
for (auto& payment: payments) {
std::string address_str = cryptonote::get_account_address_as_str(m_nettype, 0, payment.address_info.address);
auto offset = static_cast<int>(payment.address_info.address.modulus(netconf.BATCHING_INTERVAL));
auto amt = static_cast<int64_t>(payment.amount);
const auto& address_str = get_address_str(payment.address_info.address);
MTRACE(fmt::format("Adding record for SN reward contributor {} to database with amount {}",
address_str, amt));
db::exec_query(insert_payment, address_str, amt);
db::exec_query(insert_payment, address_str, offset, amt);
insert_payment->reset();
}
@ -174,7 +247,7 @@ namespace cryptonote {
"UPDATE batched_payments_accrued SET amount = (amount - ?) WHERE address = ?");
for (auto& payment: payments) {
std::string address_str = cryptonote::get_account_address_as_str(m_nettype, 0, payment.address_info.address);
const auto& address_str = get_address_str(payment.address_info.address);
auto result = db::exec_query(update_payment, static_cast<int64_t>(payment.amount), address_str);
if (!result) {
MERROR("tried to subtract payment from an address that doesn't exist: " << address_str);
@ -197,26 +270,18 @@ namespace cryptonote {
const auto& conf = get_config(m_nettype);
auto accrued_amounts = prepared_results<std::string, int64_t>(
"SELECT address, amount FROM batched_payments_accrued WHERE amount >= ? ORDER BY address ASC",
auto accrued_amounts = prepared_results<std::string_view, int64_t>(
"SELECT address, amount FROM batched_payments_accrued WHERE payout_offset = ? AND amount >= ? ORDER BY address ASC",
static_cast<int>(block_height % conf.BATCHING_INTERVAL),
static_cast<int64_t>(conf.MIN_BATCH_PAYMENT_AMOUNT * BATCH_REWARD_FACTOR));
std::vector<cryptonote::batch_sn_payment> payments;
for (auto [address, amount] : accrued_amounts) {
if (cryptonote::is_valid_address(address, m_nettype)) {
cryptonote::address_parse_info addr_info {};
cryptonote::get_account_address_from_str(addr_info, m_nettype, address);
uint64_t next_payout_height = addr_info.address.next_payout_height(block_height, conf.BATCHING_INTERVAL);
if (block_height == next_payout_height) {
payments.emplace_back(
std::move(address),
amount / BATCH_REWARD_FACTOR * BATCH_REWARD_FACTOR /* truncate to atomic OXEN */,
m_nettype);
}
} else {
MERROR("Invalid address returned from batching database: " << address);
}
auto& p = payments.emplace_back();
p.amount = amount / BATCH_REWARD_FACTOR * BATCH_REWARD_FACTOR; /* truncate to atomic OXEN */
[[maybe_unused]] bool addr_ok = cryptonote::get_account_address_from_str(p.address_info, m_nettype, address);
assert(addr_ok);
}
return payments;
@ -250,8 +315,7 @@ namespace cryptonote {
return result;
}
std::vector<cryptonote::batch_sn_payment> BlockchainSQLite::calculate_rewards(hf hf_version, uint64_t distribution_amount, service_nodes::service_node_info sn_info) {
void BlockchainSQLite::calculate_rewards(hf hf_version, uint64_t distribution_amount, const service_nodes::service_node_info& sn_info, std::vector<cryptonote::batch_sn_payment>& payments) {
LOG_PRINT_L3("BlockchainDB_SQLITE::" << __func__);
// Find out how much is due for the operator: fee_portions/PORTIONS * reward
@ -260,10 +324,10 @@ namespace cryptonote {
assert(operator_fee <= distribution_amount);
std::vector<cryptonote::batch_sn_payment> payments;
payments.clear();
// Pay the operator fee to the operator
if (operator_fee > 0)
payments.emplace_back(sn_info.operator_address, operator_fee, m_nettype);
payments.emplace_back(sn_info.operator_address, operator_fee);
// Pay the balance to all the contributors (including the operator again)
uint64_t total_contributed_to_sn = std::accumulate(
@ -276,10 +340,8 @@ namespace cryptonote {
// This calculates (contributor.amount / total_contributed_to_winner_sn) * (distribution_amount - operator_fee) but using 128 bit integer math
uint64_t c_reward = mul128_div64(contributor.amount, distribution_amount - operator_fee, total_contributed_to_sn);
if (c_reward > 0)
payments.emplace_back(contributor.address, c_reward, m_nettype);
payments.emplace_back(contributor.address, c_reward);
}
return payments;
}
// Calculates block rewards, then invokes either `add_sn_rewards` (if `add`) or
@ -302,11 +364,15 @@ namespace cryptonote {
uint64_t block_reward = block.reward * BATCH_REWARD_FACTOR;
uint64_t service_node_reward = cryptonote::service_node_reward_formula(0, block.major_version) * BATCH_REWARD_FACTOR;
std::vector<cryptonote::batch_sn_payment> payments;
// Step 1: Pay out the block producer their tx fees (note that, unlike the below, this applies
// even if the SN isn't currently payable).
if (block_reward < service_node_reward && m_nettype != cryptonote::network_type::FAKECHAIN)
throw std::logic_error{"Invalid payment: block reward is too small"};
std::lock_guard a_s_lock{address_str_cache_mutex};
if (uint64_t tx_fees = block_reward - service_node_reward;
tx_fees > 0
&& block.service_node_winner_key // "service_node_winner_key" tracks the pulse winner; 0 if a mined block
@ -315,9 +381,9 @@ namespace cryptonote {
if (auto service_node_winner = service_nodes_state.service_nodes_infos.find(block.service_node_winner_key);
service_node_winner != service_nodes_state.service_nodes_infos.end()) {
auto tx_fee_payments = calculate_rewards(block.major_version, tx_fees, *service_node_winner->second);
calculate_rewards(block.major_version, tx_fees, *service_node_winner->second, payments);
// Takes the block producer and adds its contributors to the batching database for the transaction fees
if (!(this->*add_or_subtract)(tx_fee_payments))
if (!(this->*add_or_subtract)(payments))
return false;
}
}
@ -331,21 +397,23 @@ namespace cryptonote {
auto payable_service_node = service_nodes_state.service_nodes_infos.find(node_pubkey);
if (payable_service_node == service_nodes_state.service_nodes_infos.end())
continue;
auto snode_rewards = calculate_rewards(block.major_version, service_node_reward / total_service_nodes_payable, * payable_service_node -> second);
calculate_rewards(block.major_version, service_node_reward / total_service_nodes_payable, * payable_service_node -> second, payments);
// Takes the node and adds its contributors to the batching database
if (!(this->*add_or_subtract)(snode_rewards))
if (!(this->*add_or_subtract)(payments))
return false;
}
// Step 3: Add Governance reward to the list
if (m_nettype != cryptonote::network_type::FAKECHAIN) {
std::vector<cryptonote::batch_sn_payment> governance_rewards;
cryptonote::address_parse_info governance_wallet_address;
cryptonote::get_account_address_from_str(governance_wallet_address, m_nettype,
cryptonote::get_config(m_nettype).governance_wallet_address(block.major_version));
if (parsed_governance_addr.first != block.major_version) {
cryptonote::get_account_address_from_str(parsed_governance_addr.second, m_nettype,
cryptonote::get_config(m_nettype).governance_wallet_address(block.major_version));
parsed_governance_addr.first = block.major_version;
}
uint64_t foundation_reward = cryptonote::governance_reward_formula(block.major_version) * BATCH_REWARD_FACTOR;
governance_rewards.emplace_back(governance_wallet_address.address, foundation_reward, m_nettype);
if (!(this->*add_or_subtract)(governance_rewards))
payments.clear();
payments.emplace_back(parsed_governance_addr.second.address, foundation_reward);
if (!(this->*add_or_subtract)(payments))
return false;
}
@ -491,7 +559,7 @@ namespace cryptonote {
return false;
}
total_oxen_payout_in_vouts += amount;
finalised_payments.emplace_back(from_db.address, amount, m_nettype);
finalised_payments.emplace_back(from_db.address_info, amount);
}
if (total_oxen_payout_in_vouts != total_oxen_payout_in_our_db) {
MERROR(fmt::format("Total batched payout amount incorrect. Should be {}, not {}", total_oxen_payout_in_our_db, total_oxen_payout_in_vouts));
@ -510,25 +578,28 @@ namespace cryptonote {
auto update_paid = prepared_st(
"INSERT INTO batched_payments_paid (address, amount, height_paid) VALUES (?,?,?)");
std::lock_guard a_s_lock{address_str_cache_mutex};
for (const auto& payment: paid_amounts) {
if (auto maybe_amount = db::exec_and_maybe_get<int64_t>(select_sum, payment.address))
const auto& address_str = get_address_str(payment.address_info.address);
if (auto maybe_amount = db::exec_and_maybe_get<int64_t>(select_sum, address_str))
{
// Truncate the thousanths amount to an atomic OXEN:
auto amount = static_cast<uint64_t>(*maybe_amount) / BATCH_REWARD_FACTOR * BATCH_REWARD_FACTOR;
if (amount != payment.amount) {
MERROR(fmt::format("Invalid amounts passed in to save payments for address {}: received {}, expected {} (truncated from {})",
payment.address, payment.amount, amount, *maybe_amount));
address_str, payment.amount, amount, *maybe_amount));
return false;
}
db::exec_query(update_paid, payment.address, static_cast<int64_t>(amount), static_cast<int64_t>(block_height));
db::exec_query(update_paid, address_str, static_cast<int64_t>(amount), static_cast<int64_t>(block_height));
update_paid->reset();
}
else {
// This shouldn't occur: we validate payout addresses much earlier in the block validation.
MERROR(fmt::format("Internal error: Invalid amounts passed in to save payments for address {}: that address has no accrued rewards",
payment.address));
address_str));
return false;
}
@ -541,12 +612,15 @@ namespace cryptonote {
LOG_PRINT_L3("BlockchainDB_SQLITE::" << __func__ << " Called with height: " << block_height);
std::vector<cryptonote::batch_sn_payment> payments_at_height;
auto paid = prepared_results<std::string, int64_t>(
auto paid = prepared_results<std::string_view, int64_t>(
"SELECT address, amount FROM batched_payments_paid WHERE height_paid = ? ORDER BY address",
static_cast<int64_t>(block_height));
for (auto [addr, amt] : paid)
payments_at_height.emplace_back(std::move(addr), static_cast<uint64_t>(amt), m_nettype);
for (auto [addr, amt] : paid) {
auto& p = payments_at_height.emplace_back();
p.amount = static_cast<uint64_t>(amt);
cryptonote::get_account_address_from_str(p.address_info, m_nettype, addr);
}
return payments_at_height;
}

View File

@ -51,6 +51,7 @@ public:
// Database management functions. Should be called on creation of BlockchainSQLite
void create_schema();
void upgrade_schema();
void reset_database();
// The batching database maintains a height variable to know if it gets out of sync with the mainchain. Calling increment and decrement is the primary method of interacting with this height variable
@ -69,6 +70,11 @@ private:
const service_nodes::service_node_list::state_t& service_nodes_state,
bool add);
std::unordered_map<account_public_address, std::string> address_str_cache;
std::pair<hf, cryptonote::address_parse_info> parsed_governance_addr = {hf::none, {}};
const std::string& get_address_str(const account_public_address& addr);
std::mutex address_str_cache_mutex;
public:
// get_accrued_earnings -> queries the database for the amount that has been accrued to `service_node_address` will return the atomic value in oxen that
@ -83,11 +89,12 @@ public:
// calculate_rewards -> takes the list of contributors from sn_info with their SN contribution
// amounts and will calculate how much of the block rewards should be the allocated to the
// contributors. The function will return a list suitable for passing to add_sn_payments
// contributors. The function will set a list suitable for passing to add_sn_payments into the
// vector (any existing values will be cleared).
//
// Note that distribution_amount here is typically passed as milli-atomic OXEN for extra
// precision.
std::vector<cryptonote::batch_sn_payment> calculate_rewards(hf hf_version, uint64_t distribution_amount, service_nodes::service_node_info sn_info);
void calculate_rewards(hf hf_version, uint64_t distribution_amount, const service_nodes::service_node_info& sn_info, std::vector<cryptonote::batch_sn_payment>& rewards);
// add/pop_block -> takes a block that contains new block rewards to be batched and added to the database
// and/or batching payments that need to be subtracted from the database, in addition it takes a reference to

View File

@ -110,7 +110,7 @@ void keccak(const uint8_t *in, size_t inlen, uint8_t *md, int mdlen)
for (i = 0; i < rsizw; i++) {
uint64_t ina;
memcpy(&ina, in + i * 8, 8);
st[i] ^= swap64le(ina);
st[i] ^= SWAP64LE(ina);
}
keccakf(st, KECCAK_ROUNDS);
}
@ -128,7 +128,7 @@ void keccak(const uint8_t *in, size_t inlen, uint8_t *md, int mdlen)
temp[rsiz - 1] |= 0x80;
for (i = 0; i < rsizw; i++)
st[i] ^= swap64le(((uint64_t *) temp)[i]);
st[i] ^= SWAP64LE(((uint64_t *) temp)[i]);
keccakf(st, KECCAK_ROUNDS);
@ -151,7 +151,7 @@ void keccak1600(const uint8_t *in, size_t inlen, uint8_t *md)
#define IS_ALIGNED_64(p) (0 == (7 & ((const char*)(p) - (const char*)0)))
#define KECCAK_PROCESS_BLOCK(st, block) { \
for (int i_ = 0; i_ < KECCAK_WORDS; i_++){ \
((st))[i_] ^= swap64le(((block))[i_]); \
((st))[i_] ^= SWAP64LE(((block))[i_]); \
}; \
keccakf(st, KECCAK_ROUNDS); }

View File

@ -159,17 +159,6 @@ namespace cryptonote {
return true;
}
//------------------------------------------------------------------------------------
batch_sn_payment::batch_sn_payment(std::string addr, uint64_t amt, cryptonote::network_type nettype):address(addr),amount(amt){
cryptonote::get_account_address_from_str(address_info, nettype, address);
};
batch_sn_payment::batch_sn_payment(cryptonote::address_parse_info& addr_info, uint64_t amt, cryptonote::network_type nettype):address_info(addr_info),amount(amt){
address = cryptonote::get_account_address_as_str(nettype, address_info.is_subaddress, address_info.address);
};
batch_sn_payment::batch_sn_payment(const cryptonote::account_public_address& addr, uint64_t amt, cryptonote::network_type nettype):amount(amt){
address_info = cryptonote::address_parse_info{addr,0};
address = cryptonote::get_account_address_as_str(nettype, address_info.is_subaddress, address_info.address);
};
//------------------------------------------------------------------------------------
uint8_t get_account_address_checksum(const public_address_outer_blob& bl)
{
const unsigned char* pbuf = reinterpret_cast<const unsigned char*>(&bl);

View File

@ -67,13 +67,13 @@ namespace cryptonote {
};
struct batch_sn_payment {
std::string address;
cryptonote::address_parse_info address_info;
uint64_t amount;
batch_sn_payment() = default;
batch_sn_payment(std::string addr, uint64_t amt, cryptonote::network_type nettype);
batch_sn_payment(cryptonote::address_parse_info& addr_info, uint64_t amt, cryptonote::network_type nettype);
batch_sn_payment(const cryptonote::account_public_address& addr, uint64_t amt, cryptonote::network_type nettype);
batch_sn_payment(const cryptonote::address_parse_info& addr_info, uint64_t amt)
: address_info{addr_info}, amount{amt} {}
batch_sn_payment(const cryptonote::account_public_address& addr, uint64_t amt)
: address_info{addr, 0}, amount{amt} {}
};
class ValidateMinerTxHook

View File

@ -47,52 +47,6 @@ namespace cryptonote {
using std::uint64_t;
using std::vector;
#if defined(__x86_64__)
static inline void mul(uint64_t a, uint64_t b, uint64_t &low, uint64_t &high) {
low = mul128(a, b, &high);
}
#else
static inline void mul(uint64_t a, uint64_t b, uint64_t &low, uint64_t &high) {
// __int128 isn't part of the standard, so the previous function wasn't portable. mul128() in Windows is fine,
// but this portable function should be used elsewhere. Credit for this function goes to latexi95.
uint64_t aLow = a & 0xFFFFFFFF;
uint64_t aHigh = a >> 32;
uint64_t bLow = b & 0xFFFFFFFF;
uint64_t bHigh = b >> 32;
uint64_t res = aLow * bLow;
uint64_t lowRes1 = res & 0xFFFFFFFF;
uint64_t carry = res >> 32;
res = aHigh * bLow + carry;
uint64_t highResHigh1 = res >> 32;
uint64_t highResLow1 = res & 0xFFFFFFFF;
res = aLow * bHigh;
uint64_t lowRes2 = res & 0xFFFFFFFF;
carry = res >> 32;
res = aHigh * bHigh + carry;
uint64_t highResHigh2 = res >> 32;
uint64_t highResLow2 = res & 0xFFFFFFFF;
//Addition
uint64_t r = highResLow1 + lowRes2;
carry = r >> 32;
low = (r << 32) | lowRes1;
r = highResHigh1 + highResLow2 + carry;
uint64_t d3 = r & 0xFFFFFFFF;
carry = r >> 32;
r = highResHigh2 + carry;
high = d3 | (r << 32);
}
#endif
static inline bool cadd(uint64_t a, uint64_t b) {
return a + b < a;
}
@ -104,15 +58,15 @@ namespace cryptonote {
bool check_hash(const crypto::hash &hash, difficulty_type difficulty) {
uint64_t low, high, top, cur;
// First check the highest word, this will most likely fail for a random hash.
mul(swap64le(((const uint64_t *) &hash)[3]), difficulty, top, high);
top = mul128(SWAP64LE(((const uint64_t *) &hash)[3]), difficulty, &high);
if (high != 0) {
return false;
}
mul(swap64le(((const uint64_t *) &hash)[0]), difficulty, low, cur);
mul(swap64le(((const uint64_t *) &hash)[1]), difficulty, low, high);
low = mul128(SWAP64LE(((const uint64_t *) &hash)[0]), difficulty, &cur);
low = mul128(SWAP64LE(((const uint64_t *) &hash)[1]), difficulty, &high);
bool carry = cadd(cur, low);
cur = high;
mul(swap64le(((const uint64_t *) &hash)[2]), difficulty, low, high);
low = mul128(SWAP64LE(((const uint64_t *) &hash)[2]), difficulty, &high);
carry = cadc(cur, low, carry);
carry = cadc(high, top, carry);
return !carry;

View File

@ -34,6 +34,7 @@
#include <cstdio>
#include <oxenc/endian.h>
#include <sodium.h>
#include <fmt/core.h>
#include "common/rules.h"
#include "common/hex.h"
@ -302,8 +303,7 @@ uint64_t Blockchain::get_current_blockchain_height(bool lock) const
bool Blockchain::load_missing_blocks_into_oxen_subsystems()
{
std::vector<uint64_t> start_height_options;
uint64_t const snl_height = std::max(hard_fork_begins(m_nettype, hf::hf9_service_nodes).value_or(0), m_service_node_list.height() + 1);
start_height_options.push_back(snl_height);
uint64_t snl_height = std::max(hard_fork_begins(m_nettype, hf::hf9_service_nodes).value_or(0), m_service_node_list.height() + 1);
uint64_t const ons_height = std::max(hard_fork_begins(m_nettype, hf::hf15_ons).value_or(0), m_ons_db.height() + 1);
start_height_options.push_back(ons_height);
uint64_t sqlite_height = 0;
@ -315,6 +315,13 @@ bool Blockchain::load_missing_blocks_into_oxen_subsystems()
if (m_nettype != network_type::FAKECHAIN)
throw std::logic_error("Blockchain missing SQLite Database");
}
// If the batching database falls behind it NEEDS the service node list information at that point in time
if (sqlite_height < snl_height)
{
m_service_node_list.blockchain_detached(sqlite_height + 1, true);
snl_height = std::min(sqlite_height, m_service_node_list.height() + 1);
}
start_height_options.push_back(snl_height);
uint64_t const end_height = m_db->height();
start_height_options.push_back(end_height);
uint64_t const start_height = *std::min_element(start_height_options.begin(), start_height_options.end());
@ -324,12 +331,12 @@ bool Blockchain::load_missing_blocks_into_oxen_subsystems()
if (total_blocks > 1)
MGINFO("Loading blocks into oxen subsystems, scanning blockchain from height: " << start_height << " to: " << end_height << " (snl: " << snl_height << ", ons: " << ons_height << ", sqlite: " << sqlite_height << ")");
using clock = std::chrono::steady_clock;
using work_time = std::chrono::duration<float>;
int64_t constexpr BLOCK_COUNT = 1000;
auto work_start = clock::now();
auto scan_start = work_start;
work_time ons_duration{}, snl_duration{}, sqlite_duration{}, ons_iteration_duration{}, snl_iteration_duration{}, sqlite_iteration_duration{};
using clock = std::chrono::steady_clock;
using dseconds = std::chrono::duration<double>;
int64_t constexpr BLOCK_COUNT = 500;
auto work_start = clock::now();
auto scan_start = work_start;
dseconds ons_duration{}, snl_duration{}, sqlite_duration{}, ons_iteration_duration{}, snl_iteration_duration{}, sqlite_iteration_duration{};
std::vector<cryptonote::block> blocks;
std::vector<cryptonote::transaction> txs;
@ -340,11 +347,16 @@ bool Blockchain::load_missing_blocks_into_oxen_subsystems()
block_count > 0;
block_count -= BLOCK_COUNT, index++)
{
if (index > 0 && (index % 10 == 0))
auto duration = dseconds{clock::now() - work_start};
if (duration >= 10s)
{
m_service_node_list.store();
auto duration = work_time{clock::now() - work_start};
MGINFO("... scanning height " << start_height + (index * BLOCK_COUNT) << " (" << duration.count() << "s) (snl: " << snl_iteration_duration.count() << "s; ons: " << ons_iteration_duration.count() << "s; sqlite: " << sqlite_iteration_duration.count() << "s)");
MGINFO(fmt::format("... scanning height {} ({:.3f}s) (snl: {:.3f}s, ons: {:.3f}s, batch: {:.3f}s)",
start_height + (index * BLOCK_COUNT),
duration.count(),
snl_iteration_duration.count(),
ons_iteration_duration.count(),
sqlite_iteration_duration.count()));
#ifdef ENABLE_SYSTEMD
// Tell systemd that we're doing something so that it should let us continue starting up
// (giving us 120s until we have to send the next notification):
@ -355,7 +367,9 @@ bool Blockchain::load_missing_blocks_into_oxen_subsystems()
ons_duration += ons_iteration_duration;
snl_duration += snl_iteration_duration;
sqlite_duration += sqlite_iteration_duration;
ons_iteration_duration = snl_iteration_duration = {};
ons_iteration_duration = 0s;
snl_iteration_duration = 0s;
sqlite_iteration_duration = 0s;
}
blocks.clear();
@ -421,8 +435,8 @@ bool Blockchain::load_missing_blocks_into_oxen_subsystems()
if (total_blocks > 1)
{
auto duration = work_time{clock::now() - scan_start};
MGINFO("Done recalculating oxen subsystems (" << duration.count() << "s) (snl: " << snl_duration.count() << "s; ons: " << ons_duration.count() << "s)" << "s; sqlite: " << sqlite_duration.count() << "s)");
MGINFO(fmt::format("Done recalculating oxen subsystems in {:.2f}s ({:.2f}s snl; {:.2f}s ons; {:.2f}s batch)",
dseconds{clock::now() - scan_start}.count(), snl_duration.count(), ons_duration.count(), sqlite_duration.count()));
}
if (total_blocks > 0)

View File

@ -358,7 +358,7 @@ namespace cryptonote
if (hard_fork_version >= hf::hf19_reward_batching) {
for (size_t i = 0; i < p_payouts.size(); i++)
batched_rewards.emplace_back(p_payouts[i].address, split_rewards[i], nettype);
batched_rewards.emplace_back(p_payouts[i].address, split_rewards[i]);
} else {
for (size_t i = 0; i < p_payouts.size(); i++)
rewards.push_back({reward_type::snode, p_payouts[i].address, split_rewards[i]});
@ -369,7 +369,7 @@ namespace cryptonote
if (hard_fork_version >= hf::hf19_reward_batching)
{
for (size_t i = 0; i < leader.payouts.size(); i++)
batched_rewards.emplace_back(leader.payouts[i].address, split_rewards[i], nettype);
batched_rewards.emplace_back(leader.payouts[i].address, split_rewards[i]);
}
else
{
@ -386,7 +386,7 @@ namespace cryptonote
if (uint64_t miner_amount = reward_parts.base_miner + reward_parts.miner_fee; miner_amount)
{
if (hard_fork_version >= hf::hf19_reward_batching) {
batched_rewards.emplace_back(miner_tx_context.miner_block_producer, miner_amount, nettype);
batched_rewards.emplace_back(miner_tx_context.miner_block_producer, miner_amount);
} else {
rewards.push_back({reward_type::miner, miner_tx_context.miner_block_producer, miner_amount});
}
@ -399,7 +399,7 @@ namespace cryptonote
hard_fork_version >= hf::hf16_pulse /*distribute_remainder*/);
if (hard_fork_version >= hf::hf19_reward_batching) {
for (size_t i = 0; i < leader.payouts.size(); i++)
batched_rewards.emplace_back(leader.payouts[i].address, split_rewards[i], nettype);
batched_rewards.emplace_back(leader.payouts[i].address, split_rewards[i]);
} else {
for (size_t i = 0; i < leader.payouts.size(); i++)
rewards.push_back({reward_type::snode, leader.payouts[i].address, split_rewards[i]});

View File

@ -15,8 +15,8 @@ public:
BlockchainSQLiteTest(BlockchainSQLiteTest &other)
: BlockchainSQLiteTest(other.m_nettype, check_if_copy_filename(other.filename)) {
auto all_payments_accrued = db::get_all<std::string, int64_t>(
other.prepared_st("SELECT address, amount FROM batched_payments_accrued"));
auto all_payments_accrued = db::get_all<std::string, int, int64_t>(
other.prepared_st("SELECT address, payout_offset, amount FROM batched_payments_accrued"));
auto all_payments_paid = db::get_all<std::string, int64_t, int64_t>(
other.prepared_st("SELECT address, amount, height_paid FROM batched_payments_raw"));
@ -34,10 +34,10 @@ public:
}
auto insert_payment_accrued = prepared_st(
"INSERT INTO batched_payments_accrued (address, amount) VALUES (?, ?)");
"INSERT INTO batched_payments_accrued (address, payout_offset, amount) VALUES (?, ?, ?)");
for (auto& [address, amount]: all_payments_accrued) {
db::exec_query(insert_payment_accrued, address, amount);
for (auto& [address, offset, amount]: all_payments_accrued) {
db::exec_query(insert_payment_accrued, address, offset, amount);
insert_payment_accrued->reset();
}

View File

@ -118,12 +118,10 @@ namespace
TEST(div128_32, handles_zero)
{
uint32_t reminder;
uint64_t hi;
uint64_t lo;
reminder = div128_32(0, 0, 7, &hi, &lo);
ASSERT_EQ(reminder, 0);
div128_32(0, 0, 7, &hi, &lo);
ASSERT_EQ(hi, 0);
ASSERT_EQ(lo, 0);
@ -132,73 +130,60 @@ namespace
TEST(div128_32, handles_one)
{
uint32_t reminder;
uint64_t hi;
uint64_t lo;
reminder = div128_32(0, 7, 1, &hi, &lo);
ASSERT_EQ(reminder, 0);
div128_32(0, 7, 1, &hi, &lo);
ASSERT_EQ(hi, 0);
ASSERT_EQ(lo, 7);
reminder = div128_32(7, 0, 1, &hi, &lo);
ASSERT_EQ(reminder, 0);
div128_32(7, 0, 1, &hi, &lo);
ASSERT_EQ(hi, 7);
ASSERT_EQ(lo, 0);
}
TEST(div128_32, handles_if_dividend_less_divider)
{
uint32_t reminder;
uint64_t hi;
uint64_t lo;
reminder = div128_32(0, 1383746, 1645825, &hi, &lo);
ASSERT_EQ(reminder, 1383746);
div128_32(0, 1383746, 1645825, &hi, &lo);
ASSERT_EQ(hi, 0);
ASSERT_EQ(lo, 0);
}
TEST(div128_32, handles_if_dividend_dwords_less_divider)
{
uint32_t reminder;
uint64_t hi;
uint64_t lo;
reminder = div128_32(0x5AD629E441074F28, 0x0DBCAB2B231081F1, 0xFE735CD6, &hi, &lo);
ASSERT_EQ(reminder, 0xB9C924E9);
div128_32(0x5AD629E441074F28, 0x0DBCAB2B231081F1, 0xFE735CD6, &hi, &lo);
ASSERT_EQ(hi, 0x000000005B63C274);
ASSERT_EQ(lo, 0x9084FC024383E48C);
}
TEST(div128_32, works_correctly)
{
uint32_t reminder;
uint64_t hi;
uint64_t lo;
reminder = div128_32(2, 0, 2, &hi, &lo);
ASSERT_EQ(reminder, 0);
div128_32(2, 0, 2, &hi, &lo);
ASSERT_EQ(hi, 1);
ASSERT_EQ(lo, 0);
reminder = div128_32(0xffffffffffffffff, 0, 0xffffffff, &hi, &lo);
ASSERT_EQ(reminder, 0);
div128_32(0xffffffffffffffff, 0, 0xffffffff, &hi, &lo);
ASSERT_EQ(hi, 0x0000000100000001);
ASSERT_EQ(lo, 0);
reminder = div128_32(0xffffffffffffffff, 5846, 0xffffffff, &hi, &lo);
ASSERT_EQ(reminder, 5846);
div128_32(0xffffffffffffffff, 5846, 0xffffffff, &hi, &lo);
ASSERT_EQ(hi, 0x0000000100000001);
ASSERT_EQ(lo, 0);
reminder = div128_32(0xffffffffffffffff - 1, 0, 0xffffffff, &hi, &lo);
ASSERT_EQ(reminder, 0xfffffffe);
div128_32(0xffffffffffffffff - 1, 0, 0xffffffff, &hi, &lo);
ASSERT_EQ(hi, 0x0000000100000000);
ASSERT_EQ(lo, 0xfffffffefffffffe);
reminder = div128_32(0x2649372534875028, 0xaedbfedc5adbc739, 0x27826534, &hi, &lo);
ASSERT_EQ(reminder, 0x1a6dc2e5);
div128_32(0x2649372534875028, 0xaedbfedc5adbc739, 0x27826534, &hi, &lo);
ASSERT_EQ(hi, 0x00000000f812c1f8);
ASSERT_EQ(lo, 0xddf2fdb09bc2e2e9);
}

View File

@ -56,7 +56,7 @@ TEST(SQLITE, AddSNRewards)
cryptonote::get_account_address_from_str(wallet_address, cryptonote::network_type::FAKECHAIN, "LCFxT37LAogDn1jLQKf4y7aAqfi21DjovX9qyijaLYQSdrxY1U5VGcnMJMjWrD9RhjeK5Lym67wZ73uh9AujXLQ1RKmXEyL");
t1.emplace_back(wallet_address.address, 16500000001'789/2, cryptonote::network_type::FAKECHAIN);
t1.emplace_back(wallet_address.address, 16500000001'789/2);
bool success = false;
success = sqliteDB.add_sn_rewards(t1);
@ -78,12 +78,12 @@ TEST(SQLITE, AddSNRewards)
// Pay an amount less than the database expects and test for failure
std::vector<cryptonote::batch_sn_payment> t2;
t2.emplace_back(wallet_address.address, expected_amount - 1000, cryptonote::network_type::FAKECHAIN);
t2.emplace_back(wallet_address.address, expected_amount - 1000);
EXPECT_FALSE(sqliteDB.save_payments(expected_payout, t2));
// Pay the amount back out and expect the database to be empty
std::vector<cryptonote::batch_sn_payment> t3;
t3.emplace_back(wallet_address.address, expected_amount, cryptonote::network_type::FAKECHAIN);
t3.emplace_back(wallet_address.address, expected_amount);
success = sqliteDB.save_payments(expected_payout, t3);
EXPECT_TRUE(success);
EXPECT_EQ(sqliteDB.batching_count(), 0);
@ -103,7 +103,8 @@ TEST(SQLITE, CalculateRewards)
cryptonote::get_account_address_from_str(first_address, cryptonote::network_type::TESTNET, "T6TzkJb5EiASaCkcH7idBEi1HSrpSQJE1Zq3aL65ojBMPZvqHNYPTL56i3dncGVNEYCG5QG5zrBmRiVwcg6b1cRM1SRNqbp44");
single_contributor.contributors.emplace_back(0, first_address.address);
single_contributor.contributors.back().amount = block.reward;
auto rewards = sqliteDB.calculate_rewards(block.major_version, block.reward, single_contributor);
std::vector<cryptonote::batch_sn_payment> rewards;
sqliteDB.calculate_rewards(block.major_version, block.reward, single_contributor, rewards);
auto hf_version = block.major_version;
// Check that 3 contributor receives their portion of the block reward
@ -118,25 +119,25 @@ TEST(SQLITE, CalculateRewards)
cryptonote::get_account_address_from_str(third_address, cryptonote::network_type::TESTNET, "T6SkkovCyLWViVDMgeJoF7X4vFrHnKX5jXyktaoGmRuNTdoFEx1xXu1joXdmeH9mx2LLNPq998fKKcsAHwdRJWhk126SapptR");
multiple_contributors.contributors.emplace_back(0, third_address.address);
multiple_contributors.contributors.back().amount = 34;
auto multiple_rewards = sqliteDB.calculate_rewards(block.major_version, block.reward, multiple_contributors);
sqliteDB.calculate_rewards(block.major_version, block.reward, multiple_contributors, rewards);
EXPECT_EQ(multiple_rewards[0].amount, 66);
EXPECT_EQ(multiple_rewards[1].amount, 66);
EXPECT_EQ(multiple_rewards[2].amount, 68);
EXPECT_EQ(rewards[0].amount, 66);
EXPECT_EQ(rewards[1].amount, 66);
EXPECT_EQ(rewards[2].amount, 68);
// Check that 3 contributors receives their portion of the block reward when the operator takes a 10% fee
multiple_contributors.portions_for_operator = cryptonote::old::STAKING_PORTIONS/10;
multiple_contributors.operator_address = first_address.address;
block.reward = 1000;
auto multiple_rewards_with_fee = sqliteDB.calculate_rewards(block.major_version, block.reward, multiple_contributors);
sqliteDB.calculate_rewards(block.major_version, block.reward, multiple_contributors, rewards);
// Operator gets 10%
EXPECT_EQ(multiple_rewards_with_fee[0].amount, 99);
EXPECT_EQ(tools::view_guts(multiple_rewards_with_fee[0].address_info.address), tools::view_guts(first_address.address));
EXPECT_EQ(rewards[0].amount, 99);
EXPECT_EQ(tools::view_guts(rewards[0].address_info.address), tools::view_guts(first_address.address));
// Contributors (including operator) receive the balance
EXPECT_EQ(multiple_rewards_with_fee[1].amount, 297);
EXPECT_EQ(tools::view_guts(multiple_rewards_with_fee[1].address_info.address), tools::view_guts(first_address.address));
EXPECT_EQ(multiple_rewards_with_fee[2].amount, 297);
EXPECT_EQ(tools::view_guts(multiple_rewards_with_fee[2].address_info.address), tools::view_guts(second_address.address));
EXPECT_EQ(multiple_rewards_with_fee[3].amount, 306);
EXPECT_EQ(tools::view_guts(multiple_rewards_with_fee[3].address_info.address), tools::view_guts(third_address.address));
EXPECT_EQ(rewards[1].amount, 297);
EXPECT_EQ(tools::view_guts(rewards[1].address_info.address), tools::view_guts(first_address.address));
EXPECT_EQ(rewards[2].amount, 297);
EXPECT_EQ(tools::view_guts(rewards[2].address_info.address), tools::view_guts(second_address.address));
EXPECT_EQ(rewards[3].amount, 306);
EXPECT_EQ(tools::view_guts(rewards[3].address_info.address), tools::view_guts(third_address.address));
}