mirror of
https://github.com/Xahau/xahaud.git
synced 2025-11-04 18:55:49 +00:00
346 lines
11 KiB
C
346 lines
11 KiB
C
#include "hookapi.h"
|
|
#define DEFAULT_REWARD_DELAY 6199553087261802496ULL
|
|
#define DEFAULT_REWARD_RATE 6038156834009797973ULL
|
|
//0.00333333333f
|
|
|
|
#define L1SEATS 20U
|
|
#define MAXUNL 128U
|
|
#define SVAR(x) &(x), sizeof(x)
|
|
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback(SBUF("Reward: Assertion failure."),__LINE__);
|
|
|
|
#define DEBUG 1
|
|
|
|
uint8_t txn_mint[928] =
|
|
{
|
|
/* size,upto */
|
|
/* 3, 0 */ 0x12U, 0x00U, 0x60U, /* tt = GenesisMint */
|
|
/* 5, 3 */ 0x22U, 0x80U, 0x00U, 0x00U, 0x00U, /* flags = tfCanonical */
|
|
/* 5, 8 */ 0x24U, 0x00U, 0x00U, 0x00U, 0x00U, /* sequence = 0 */
|
|
/* 6, 13 */ 0x20U, 0x1AU, 0x00U, 0x00U, 0x00U, 0x00U, /* first ledger seq */
|
|
/* 6, 19 */ 0x20U, 0x1BU, 0x00U, 0x00U, 0x00U, 0x00U, /* last ledger seq */
|
|
/* 9, 25 */ 0x68U, 0x40U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, /* fee */
|
|
/* 35, 34 */ 0x73U, 0x21U, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* pubkey */
|
|
/* 22, 69 */ 0x81U, 0x14U, 0xB5U,0xF7U,0x62U,0x79U,0x8AU,0x53U,0xD5U,0x43U,0xA0U,0x14U,
|
|
0xCAU,0xF8U,0xB2U,0x97U,0xCFU,0xF8U,0xF2U,0xF9U,0x37U,0xE8U, /* src acc */
|
|
|
|
/* 116, 91 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* emit detail */
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
|
|
/* 207, ... */ 0xF0U, 0x60U, /* gen mints arr */
|
|
/* 34 bytes per entries + 1 tail byte
|
|
E060
|
|
61
|
|
4111111111111111 // amount
|
|
8114
|
|
1234567891234567891234567891234567891234 // account
|
|
E1
|
|
... repeat
|
|
F1 // tail byte
|
|
*
|
|
* */
|
|
// 210 bytes + 34 bytes per entry * number of entries + any alignment padding desired
|
|
};
|
|
|
|
uint8_t template[40] = {
|
|
/* 0, 2 */ 0xE0U, 0x60U, // obj start
|
|
/* 2, 1 */ 0x61U, // amount header
|
|
/* 3, 8 */ 0,0,0,0,0,0,0,0, // amount payload
|
|
/* 11, 2 */ 0x83U, 0x14U, // account header
|
|
/* 13, 20 */ 0,0,0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,0,0, // account payload
|
|
/* 33, 1 */ 0xE1U // obj end
|
|
};
|
|
|
|
|
|
#define TEMPLATE_DROPS(drops_tmp)\
|
|
{\
|
|
uint8_t* b = template + 3U;\
|
|
*b++ = 0b01000000 + (( drops_tmp >> 56 ) & 0b00111111 );\
|
|
*b++ = (drops_tmp >> 48) & 0xFFU;\
|
|
*b++ = (drops_tmp >> 40) & 0xFFU;\
|
|
*b++ = (drops_tmp >> 32) & 0xFFU;\
|
|
*b++ = (drops_tmp >> 24) & 0xFFU;\
|
|
*b++ = (drops_tmp >> 16) & 0xFFU;\
|
|
*b++ = (drops_tmp >> 8) & 0xFFU;\
|
|
*b++ = (drops_tmp >> 0) & 0xFFU;\
|
|
}
|
|
|
|
#define BE_DROPS(drops)\
|
|
{\
|
|
uint64_t drops_tmp = drops;\
|
|
uint8_t* b = (uint8_t*)&drops;\
|
|
*b++ = 0b01000000 + (( drops_tmp >> 56 ) & 0b00111111 );\
|
|
*b++ = (drops_tmp >> 48) & 0xFFU;\
|
|
*b++ = (drops_tmp >> 40) & 0xFFU;\
|
|
*b++ = (drops_tmp >> 32) & 0xFFU;\
|
|
*b++ = (drops_tmp >> 24) & 0xFFU;\
|
|
*b++ = (drops_tmp >> 16) & 0xFFU;\
|
|
*b++ = (drops_tmp >> 8) & 0xFFU;\
|
|
*b++ = (drops_tmp >> 0) & 0xFFU;\
|
|
}
|
|
|
|
uint8_t member_count_key[2] = {'M', 'C'};
|
|
|
|
|
|
uint8_t unlreport_keylet[34] =
|
|
{
|
|
0,0,0x61U,0xE3U,0x2EU,0x7AU,0x24U,0xA2U,0x38U,0xF1U,0xC6U,0x19U,
|
|
0xD5U,0xF9U,0xDDU,0xCCU,0x41U,0xA9U,0x4BU,0x33U,0xB6U,0x6CU,
|
|
0x01U,0x63U,0xF7U,0xEFU,0xCCU,0x8AU,0x19U,0xC9U,0xFDU,0x6FU,
|
|
0x28U,0xDCU
|
|
};
|
|
|
|
uint8_t msg_buf[] = "You must wait 0000000 seconds";
|
|
int64_t hook(uint32_t r)
|
|
{
|
|
etxn_reserve(1);
|
|
_g(1,1);
|
|
|
|
// only process ttCLAIM_REWARD
|
|
if (otxn_type() != 98)
|
|
accept(SBUF("Reward: Passing non-claim txn"), __LINE__);
|
|
|
|
uint8_t otxn_acc[20];
|
|
uint8_t hook_acc[20];
|
|
|
|
// get the account id
|
|
otxn_field(SBUF(otxn_acc), sfAccount);
|
|
|
|
// write the hook account into the txn template
|
|
hook_account(SBUF(hook_acc));
|
|
|
|
if (BUFFER_EQUAL_20(hook_acc, otxn_acc))
|
|
accept(SBUF("Reward: Passing outgoing txn"), __LINE__);
|
|
|
|
// the default rate and delay are used if somehow the keys are missing from hook state (graceful failure)
|
|
int64_t xfl_rr = DEFAULT_REWARD_RATE;
|
|
int64_t xfl_rd = DEFAULT_REWARD_DELAY;
|
|
|
|
// load state if it exists (which it should)
|
|
state(&xfl_rr, 8, "RR", 2);
|
|
state(&xfl_rd, 8, "RD", 2);
|
|
|
|
// if either of these is 0 that's disabled
|
|
if (xfl_rr <= 0 || xfl_rd <= 0 )
|
|
rollback(SBUF("Reward: Rewards are disabled by governance."), __LINE__);
|
|
|
|
int64_t required_delay = float_int(xfl_rd, 0, 0);
|
|
|
|
if (required_delay < 0 || float_sign(xfl_rr) != 0 ||
|
|
float_compare(xfl_rr, float_one(), COMPARE_GREATER) ||
|
|
float_compare(xfl_rd, float_one(), COMPARE_GREATER))
|
|
rollback(SBUF("Reward: Rewards incorrectly configured by governance or unrecoverable error."), __LINE__);
|
|
|
|
// get the account root keylet
|
|
uint8_t kl[34];
|
|
util_keylet(SBUF(kl), KEYLET_ACCOUNT, SBUF(otxn_acc), 0,0,0,0);
|
|
|
|
// slot the account root, this can't fail
|
|
slot_set(SBUF(kl), 1);
|
|
|
|
// this is a first time claim reward has run and will setup these fields
|
|
if (slot_subfield(1, sfRewardAccumulator, 2) != 2)
|
|
accept(SBUF("Reward: Passing reward setup txn"), __LINE__);
|
|
|
|
// this is an actual claim reward
|
|
slot_subfield(1, sfRewardLgrFirst, 3);
|
|
slot_subfield(1, sfRewardLgrLast, 4);
|
|
slot_subfield(1, sfBalance, 5);
|
|
slot_subfield(1, sfRewardTime, 6);
|
|
|
|
int64_t time = slot(0,0,6);
|
|
|
|
int64_t time_elapsed = ledger_last_time() - time;
|
|
|
|
|
|
if (time_elapsed < required_delay)
|
|
{
|
|
//2 600 000
|
|
time_elapsed = required_delay - time_elapsed;
|
|
msg_buf[14] += (time_elapsed / 1000000) % 10;
|
|
msg_buf[15] += (time_elapsed / 100000) % 10;
|
|
msg_buf[16] += (time_elapsed / 10000) % 10;
|
|
msg_buf[17] += (time_elapsed / 1000) % 10;
|
|
msg_buf[18] += (time_elapsed / 100) % 10;
|
|
msg_buf[19] += (time_elapsed / 10) % 10;
|
|
msg_buf[20] += (time_elapsed ) % 10;
|
|
|
|
rollback(SBUF(msg_buf), __LINE__);
|
|
}
|
|
|
|
int64_t accumulator = slot(0,0,2);
|
|
int64_t first = slot(0,0,3);
|
|
int64_t last = slot(0,0,4);
|
|
int64_t bal = slot(0,0,5);
|
|
|
|
if (DEBUG)
|
|
{
|
|
TRACEVAR(accumulator);
|
|
TRACEVAR(first);
|
|
TRACEVAR(last);
|
|
}
|
|
|
|
ASSERT(/*accumulator > 0 &&*/ first > 0 && last > 0);
|
|
|
|
// we need to add the final block ourselves
|
|
|
|
int64_t cur = ledger_seq();
|
|
|
|
int64_t elapsed = cur - first;
|
|
|
|
ASSERT(elapsed > 0);
|
|
|
|
int64_t elapsed_since_last = ledger_seq() - last;
|
|
|
|
bal &= ~0xE000000000000000ULL;
|
|
bal /= 1000000LL;
|
|
|
|
if (DEBUG)
|
|
{
|
|
TRACEVAR(bal);
|
|
TRACEVAR(accumulator);
|
|
}
|
|
|
|
if (bal > 0 && elapsed_since_last > 0)
|
|
accumulator += bal * elapsed_since_last;
|
|
|
|
if (DEBUG)
|
|
TRACEVAR(accumulator);
|
|
|
|
|
|
int64_t xfl_accum = float_set(0, accumulator);
|
|
ASSERT(xfl_accum > 0);
|
|
|
|
int64_t xfl_elapsed = float_set(0, elapsed);
|
|
ASSERT(xfl_elapsed > 0);
|
|
|
|
int64_t xfl_reward = float_divide(xfl_accum, xfl_elapsed);
|
|
xfl_reward = float_multiply(xfl_rr, xfl_reward);
|
|
|
|
if (DEBUG)
|
|
TRACEVAR(xfl_reward);
|
|
|
|
|
|
uint64_t reward_drops = float_int(xfl_reward, 6, 1);
|
|
|
|
uint64_t l1_drops = reward_drops / L1SEATS;
|
|
|
|
|
|
otxn_slot(1);
|
|
slot_subfield(1, sfFee, 2);
|
|
int64_t xfl_fee = slot_float(2);
|
|
|
|
// user gets back the fee they spent running the hook
|
|
if (xfl_fee > 0)
|
|
reward_drops += float_int(xfl_fee, 6, 1);
|
|
|
|
|
|
TEMPLATE_DROPS(reward_drops);
|
|
|
|
uint8_t* upto = txn_mint + 209U;
|
|
uint8_t* end = upto + (34U * (L1SEATS + 1));
|
|
|
|
// first account is always the rewardee
|
|
{
|
|
uint64_t* d = (uint64_t*)upto;
|
|
uint64_t* s = (uint64_t*)template;
|
|
*d++ = *s++;
|
|
*d++ = *s++;
|
|
*(d+2) = *(s+2);
|
|
otxn_field(upto + 13, 20, sfAccount);
|
|
}
|
|
upto += 34U;
|
|
|
|
// now iterate all possible seats in all possible tables
|
|
TEMPLATE_DROPS(l1_drops);
|
|
|
|
|
|
// there are two conditions for L1 governance members to receive rewards:
|
|
// 1. they must be an L1 member
|
|
// 2. they must be an active validator
|
|
|
|
// load the UNLReport, this will let us know who has been validating and who hasn't
|
|
|
|
uint64_t can_reward[L1SEATS];
|
|
|
|
|
|
uint8_t av_array[(60 * MAXUNL) + 4];
|
|
if (slot_set(SBUF(unlreport_keylet), 1) == 1 &&
|
|
slot_subfield(1, sfActiveValidators, 1) == 1 &&
|
|
slot(SBUF(av_array), 1) > 0)
|
|
{
|
|
// at least some validators have been validating so those get a reward if they are on the governance table
|
|
// we are going to assume the UNL never exceeds 64
|
|
uint8_t seat = 0;
|
|
uint8_t* av_upto = av_array + 39 /* offset to the first key */;
|
|
uint64_t av_size = slot_count(1);
|
|
if (av_size > MAXUNL) av_size = MAXUNL;
|
|
for (uint64_t i = 0; GUARD(MAXUNL), i < av_size; ++i, av_upto += 60U)
|
|
{
|
|
trace(SBUF("av:"), av_upto, 20, 1);
|
|
if (state(SVAR(seat), av_upto, 20) != 1 || seat > L1SEATS)
|
|
continue;
|
|
can_reward[seat] = 1;
|
|
}
|
|
|
|
// iterate the seats at the table and add reward entries for the active validators
|
|
for (uint8_t l1_seat = 0; upto < end && l1_seat < L1SEATS; l1_seat++)
|
|
{
|
|
GUARD(L1SEATS);
|
|
if (!can_reward[l1_seat])
|
|
continue;
|
|
|
|
// copy template 1 into next GenesisMints array position
|
|
uint64_t* d = (uint64_t*)upto;
|
|
uint64_t* s = (uint64_t*)template;
|
|
*d++ = *s++;
|
|
*d++ = *s++;
|
|
*(d+2) = *(s+2);
|
|
|
|
if (state(upto + 13, 20, &l1_seat, 1) == 20)
|
|
upto += 34;
|
|
}
|
|
|
|
}
|
|
*upto++ = 0xF1U;
|
|
|
|
|
|
// populate other txn fields
|
|
etxn_details(txn_mint + 91, 116);
|
|
int64_t fee = etxn_fee_base(txn_mint, upto - txn_mint);
|
|
BE_DROPS(fee);
|
|
|
|
*((uint64_t*)(txn_mint + 26)) = fee;
|
|
|
|
int64_t seq = ledger_seq() + 1;
|
|
txn_mint[15] = (seq >> 24U) & 0xFFU;
|
|
txn_mint[16] = (seq >> 16U) & 0xFFU;
|
|
txn_mint[17] = (seq >> 8U) & 0xFFU;
|
|
txn_mint[18] = seq & 0xFFU;
|
|
|
|
seq += 4;
|
|
txn_mint[21] = (seq >> 24U) & 0xFFU;
|
|
txn_mint[22] = (seq >> 16U) & 0xFFU;
|
|
txn_mint[23] = (seq >> 8U) & 0xFFU;
|
|
txn_mint[24] = seq & 0xFFU;
|
|
|
|
|
|
trace(SBUF("emit:"), txn_mint, upto-txn_mint, 1);
|
|
|
|
uint8_t emithash[32];
|
|
int64_t emit_result = emit(SBUF(emithash), txn_mint, upto - txn_mint);
|
|
|
|
if (DEBUG)
|
|
TRACEVAR(emit_result);
|
|
|
|
if (emit_result < 0)
|
|
rollback(SBUF("Reward: Emit loopback failed."), __LINE__);
|
|
|
|
|
|
accept(SBUF("Reward: Emitted reward txn successfully."), __LINE__);
|
|
}
|