mirror of
https://github.com/Xahau/xahaud.git
synced 2026-04-29 15:37:46 +00:00
Merge pull request #70 from Xahau/governance_hook_l2
Governance hook l2
This commit is contained in:
@@ -443,6 +443,7 @@ target_sources (rippled PRIVATE
|
||||
src/ripple/app/tx/impl/SetRegularKey.cpp
|
||||
src/ripple/app/tx/impl/SetHook.cpp
|
||||
src/ripple/app/tx/impl/ClaimReward.cpp
|
||||
src/ripple/app/tx/impl/GenesisMint.cpp
|
||||
src/ripple/app/tx/impl/Import.cpp
|
||||
src/ripple/app/tx/impl/Invoke.cpp
|
||||
src/ripple/app/tx/impl/SetSignerList.cpp
|
||||
|
||||
236
hook/govern.c
236
hook/govern.c
@@ -10,13 +10,20 @@
|
||||
/**
|
||||
* Xahau Governance Hook
|
||||
*
|
||||
* The governance model is a 20 seat round table.
|
||||
* Each seat may be filled or empty (provided there is at least one filled seat.)
|
||||
* The primary governance table sits on the genesis account. This is also called the L1 table.
|
||||
* Seats at this table are called L1s or L1 members.
|
||||
* At L1, for votes relating to table membership 80% of the filled seats must vote in favour.
|
||||
* At L1, for votes relating to everything else 100% of the filled seats must vote in favour.
|
||||
* One or more L1 seats may contain an account that has an L2 governance hook installed on it and is blackholed.
|
||||
* This is referred to as an L2 table. The seats at the table are called L2s or L2 members.
|
||||
* There may be multiple L2 tables.
|
||||
*
|
||||
* Hook Parameters:
|
||||
* Parameter Name: {'I', 'M', 'C'}
|
||||
* Parameter Value: Initial Member Count <1 byte>
|
||||
*
|
||||
* Parameter Name: {'I', 'D', 'A'}
|
||||
* Parameter Value: Initial Distribution Amount to each initial member <8 byte BE drops>
|
||||
*
|
||||
* Parameter Name: {'I', 'R', 'R'}
|
||||
* Parameter Value: Initial Reward Rate <8 byte XFL fraction between 0 and 1, LE>
|
||||
*
|
||||
@@ -29,7 +36,8 @@
|
||||
* ...
|
||||
*
|
||||
* Topics:
|
||||
* 'H[0-9]' - Hook Hash in positions 0-9 <32 byte hash>
|
||||
* 'H[0-9]' - Hook Hash in positions 0-9 <32 byte hash> on genesis
|
||||
* 'h[0-9]' - Hook Hash in positions 0-9 <32 byte hash> on local L2 table (when applicable)
|
||||
* 'RR' - reward rate <le xfl 8 bytes>
|
||||
* 'RD' - reward delay <le xfl 8 bytes>
|
||||
* 'S[0-19]' - who is a governance member occupying that seat <20 byte accid>
|
||||
@@ -66,14 +74,14 @@
|
||||
*
|
||||
* Parameter Name: {'T'}
|
||||
* Parameter Value: The topic to vote on <2 bytes>
|
||||
* { 'S|H' (seat, hook), '\0 + topic id' }, or
|
||||
* { 'S|H' (seat L1, hook L1), '\0 + topic id' }, or
|
||||
* { 'R' (reward), 'R|D' (rate, delay) }
|
||||
*
|
||||
* Parameter Name: {'V'}
|
||||
* Parameter Value: The data to vote for this topic (accid, hook hash, reward rate/delay)
|
||||
**/
|
||||
|
||||
#define SVAR(x) x, sizeof(x)
|
||||
#define SVAR(x) &x, sizeof(x)
|
||||
|
||||
#define DONE(x)\
|
||||
accept(SVAR(x),(uint32_t)__LINE__);
|
||||
@@ -83,143 +91,113 @@
|
||||
|
||||
#define DEBUG 1
|
||||
|
||||
// genesis account id
|
||||
uint8_t genesis[20] =
|
||||
{0xB5U,0xF7U,0x62U,0x79U,0x8AU,0x53U,0xD5U,0x43U,0xA0U,0x14U,
|
||||
0xCAU,0xF8U,0xB2U,0x97U,0xCFU,0xF8U,0xF2U,0xF9U,0x37U,0xE8U};
|
||||
|
||||
uint8_t zero32[32];
|
||||
|
||||
int64_t hook(uint32_t r)
|
||||
{
|
||||
_g(1,1);
|
||||
|
||||
if (otxn_type() != 99) // ttINVOKE
|
||||
int64_t tt = otxn_type();
|
||||
|
||||
if (tt != 99) // ttINVOKE only
|
||||
DONE("Governance: Passing non-Invoke txn. HookOn should be changed to avoid this.");
|
||||
|
||||
// get the account id
|
||||
uint8_t account_field[32];
|
||||
otxn_field(account_field + 12, 20, sfAccount);
|
||||
|
||||
uint8_t hook_accid[20];
|
||||
hook_account(SBUF(hook_accid));
|
||||
uint8_t hook_accid[32];
|
||||
hook_account(hook_accid + 12, 20);
|
||||
|
||||
// outgoing txns to other hooks allowed
|
||||
if (BUFFER_EQUAL_20(hook_accid + 12, account_field + 12))
|
||||
{
|
||||
uint8_t dest_acc[20];
|
||||
if (otxn_field(SBUF(dest_acc), sfDestination) == 20 && !BUFFER_EQUAL_20(hook_accid + 12, dest_acc))
|
||||
DONE("Goverance: Passing outgoing txn.");
|
||||
}
|
||||
|
||||
int64_t is_L1 = BUFFER_EQUAL_20(hook_accid + 12, genesis);
|
||||
|
||||
if (!is_L1)
|
||||
NOPE("Governance: This is the L1 governance hook. It can only be used on genesis account.");
|
||||
|
||||
|
||||
int64_t member_count = state(0,0, "MC", 2);
|
||||
if (DEBUG)
|
||||
TRACEVAR(member_count);
|
||||
|
||||
// outgoing txns to other hooks allowed
|
||||
// but a self ttINVOKE means rewards hook is trying to get the governance hook to distribute governance rewards
|
||||
int64_t is_distribution = 0;
|
||||
int64_t is_setup = member_count == DOESNT_EXIST;
|
||||
|
||||
if (BUFFER_EQUAL_20(hook_accid, account_field + 12))
|
||||
{
|
||||
uint8_t dest_acc[20];
|
||||
if (otxn_field(SBUF(dest_acc), sfDestination) == 20 && !BUFFER_EQUAL_20(hook_accid, dest_acc))
|
||||
DONE("Goverance: Passing outgoing txn.");
|
||||
|
||||
is_distribution = 1;
|
||||
}
|
||||
|
||||
// initial execution, setup hook
|
||||
|
||||
int64_t distribution_amount;
|
||||
|
||||
|
||||
if (is_setup)
|
||||
if (member_count == DOESNT_EXIST)
|
||||
{
|
||||
// gather hook parameters
|
||||
|
||||
int64_t imc, ida, irr, ird;
|
||||
uint8_t imc;
|
||||
uint64_t irr, ird;
|
||||
TRACEVAR(imc);
|
||||
if (hook_param(SVAR(imc), "IMC", 3) < 0)
|
||||
NOPE("Governance: Initial Member Count Parameter missing (IMC).");
|
||||
|
||||
if (hook_param(SVAR(ida), "IDA", 3) < 0)
|
||||
NOPE("Governance: Initial Distribution Amount Prameter missing (IDA).");
|
||||
|
||||
if (hook_param(SVAR(irr), "IRR", 3) < 0)
|
||||
NOPE("Governance: Initial Reward Rate Parameter missing (IRR).");
|
||||
|
||||
if (hook_param(SVAR(ird), "IRD", 3) < 0)
|
||||
NOPE("Governance: Initial Reward Delay Parameter miss (IRD).");
|
||||
|
||||
// sanity check parameters
|
||||
TRACEVAR(imc);
|
||||
|
||||
if (imc == 0)
|
||||
NOPE("Governance: Initial Member Count must be > 0.");
|
||||
|
||||
if (imc > SEAT_COUNT)
|
||||
NOPE("Governance: Initial Member Count must be <= Seat Count (20).");
|
||||
|
||||
if (hook_param(SVAR(irr), "IRR", 3) < 0)
|
||||
NOPE("Governance: Initial Reward Rate Parameter missing (IRR).");
|
||||
|
||||
if (hook_param(SVAR(ird), "IRD", 3) < 0)
|
||||
NOPE("Governance: Initial Reward Delay Parameter miss (IRD).");
|
||||
|
||||
if (ird == 0)
|
||||
NOPE("Governance: Initial Reward Delay must be > 0.");
|
||||
|
||||
// set reward rate
|
||||
ASSERT(state_set(SVAR(irr), "RR", 2));
|
||||
|
||||
etxn_reserve(imc);
|
||||
// set reward delay
|
||||
ASSERT(state_set(SVAR(ird), "RD", 2));
|
||||
|
||||
// set member count
|
||||
ASSERT(state_set(SBUF(imc), "MC", 2));
|
||||
|
||||
// set reward rate
|
||||
ASSERT(state_set(SVAR(irr), "RR", 2));
|
||||
|
||||
// set reward delay
|
||||
ASSERT(state_set(SVAR(ird), "RD", 2));
|
||||
|
||||
distribution_amount = ida;
|
||||
member_count = imc;
|
||||
}
|
||||
else if (is_distribution)
|
||||
{
|
||||
ASSERT(member_count > 0);
|
||||
TRACEVAR(member_count);
|
||||
|
||||
uint8_t amt[8];
|
||||
ASSERT(otxn_param(SBUF(amt), "reward", 5) == 8);
|
||||
distribution_amount = INT64_FROM_BUF(amt);
|
||||
distribution_amount /= member_count;
|
||||
ASSERT(distribution_amount > 0);
|
||||
}
|
||||
|
||||
if (is_setup || is_distribution)
|
||||
{
|
||||
for (uint8_t i = 0; GUARD(SEAT_COUNT), i < member_count; ++i)
|
||||
{
|
||||
uint8_t member_acc[20];
|
||||
if (is_setup)
|
||||
{
|
||||
uint8_t member_pkey[3] = {'I', 'S', i};
|
||||
if (hook_param(SBUF(member_acc), member_pkey, 3) != 20)
|
||||
NOPE("Governance: One or more initial member account ID's is missing");
|
||||
|
||||
// 0... X where X is member id started from 1
|
||||
// maps to the member's account ID
|
||||
// reverse key
|
||||
ASSERT(state_set(SBUF(member_acc), SVAR(i)) == 20);
|
||||
|
||||
// 0, 0... ACCOUNT ID maps to member_id (as above)
|
||||
// forward key
|
||||
ASSERT(state_set(SVAR(i), SBUF(member_acc)) == 1);
|
||||
}
|
||||
else if (state(SBUF(member_acc), SVAR(i)) != 20)
|
||||
break;
|
||||
|
||||
// emit initial
|
||||
uint8_t tx[PREPARE_PAYMENT_SIMPLE_SIZE];
|
||||
PREPARE_PAYMENT_SIMPLE(tx, distribution_amount, member_acc, 0, 0);
|
||||
|
||||
// emit the transaction
|
||||
uint8_t emithash[32];
|
||||
int64_t emit_result = emit(SBUF(emithash), SBUF(tx));
|
||||
ASSERT(emit_result > 0);
|
||||
|
||||
if (DEBUG)
|
||||
TRACEVAR(emit_result);
|
||||
uint8_t member_pkey[3] = {'I', 'S', i};
|
||||
if (hook_param(SBUF(member_acc), member_pkey, 3) != 20)
|
||||
NOPE("Governance: One or more initial member account ID's is missing");
|
||||
|
||||
// 0... X where X is member id started from 1
|
||||
// maps to the member's account ID
|
||||
trace(SBUF("Member:"), SBUF(member_acc), 1);
|
||||
// reverse key
|
||||
ASSERT(state_set(SBUF(member_acc), SVAR(i)) == 20);
|
||||
|
||||
// 0, 0... ACCOUNT ID maps to member_id (as above)
|
||||
// forward key
|
||||
ASSERT(state_set(SVAR(i), SBUF(member_acc)) == 1);
|
||||
}
|
||||
|
||||
DONE(is_setup
|
||||
? "Governance: Setup completed successfully."
|
||||
: "Governance: Reward distribution completed successfully.");
|
||||
DONE("Governance: Setup completed successfully.");
|
||||
}
|
||||
|
||||
|
||||
// otherwise a normal execution (not initial)
|
||||
// first let's check if the invoking party is a member
|
||||
|
||||
int64_t member_id = state(0,0,account_field + 12, 20);
|
||||
if (member_id < 0)
|
||||
NOPE("Governance: You are not currently a governance member.");
|
||||
NOPE("Governance: You are not currently a governance member at this table.");
|
||||
|
||||
// the only thing a member can do is vote for a topic
|
||||
// so lets process their vote
|
||||
@@ -227,29 +205,32 @@ int64_t hook(uint32_t r)
|
||||
// { 'S|H|R', '\0 + topicid' }
|
||||
uint8_t topic[2];
|
||||
int64_t result = otxn_param(SBUF(topic), "T", 1);
|
||||
uint8_t t = topic[0]; // topic type
|
||||
uint8_t n = topic[1]; // number (seats) (or R/D for reward rate/delay)
|
||||
|
||||
if (result != 2 || (
|
||||
topic[0] != 'S' && // topic type: seat
|
||||
topic[0] != 'H' && // topic type: hook
|
||||
topic[0] != 'R')) // topic type: reward
|
||||
t != 'S' && // topic type: seat (L1)
|
||||
t != 'H' && // topic type: hook (L1)
|
||||
t != 'R')) // topic type: reward
|
||||
NOPE("Governance: Valid TOPIC must be specified as otxn parameter.");
|
||||
|
||||
|
||||
if (topic[0] == 'S' && topic[1] > (SEAT_COUNT - 1))
|
||||
if (t == 'S' && n > (SEAT_COUNT - 1))
|
||||
NOPE("Governance: Valid seat topics are 0 through 19.");
|
||||
|
||||
if (topic[0] == 'H' && topic[1] > HOOK_MAX)
|
||||
if (t == 'H' && n > HOOK_MAX)
|
||||
NOPE("Governance: Valid hook topics are 0 through 9.");
|
||||
|
||||
if (topic[0] == 'R' && topic[1] != 'R' && topic[1] != 'D')
|
||||
if (t == 'R' && n != 'R' && n != 'D')
|
||||
NOPE("Governance: Valid reward topics are R (rate) and D (delay).");
|
||||
|
||||
// RH TODO: validate RR/RD xfl > 0
|
||||
|
||||
// RH TODO: validate RR/RD xfl > 0
|
||||
uint8_t topic_data[32];
|
||||
uint8_t topic_size =
|
||||
topic[0] == 'H' ? 32 : // hook topics are a 32 byte hook hash
|
||||
topic[0] == 'S' ? 20 : // account topics are a 20 byte account ID
|
||||
8; // reward topics are an 8 byte le xfl
|
||||
t == 'H' ? 32 : // hook topics are a 32 byte hook hash
|
||||
t == 'S' ? 20 : // account topics are a 20 byte account ID
|
||||
8; // reward topics are an 8 byte le xfl
|
||||
|
||||
uint8_t padding = 32 - topic_size;
|
||||
|
||||
@@ -260,12 +241,13 @@ int64_t hook(uint32_t r)
|
||||
|
||||
// reuse account_field to create vote key
|
||||
account_field[0] = 'V';
|
||||
account_field[1] = topic[0];
|
||||
account_field[2] = topic[1];
|
||||
account_field[1] = t;
|
||||
account_field[2] = n;
|
||||
|
||||
// get their previous vote if any on this topic
|
||||
uint8_t previous_topic_data[32];
|
||||
int64_t previous_topic_size = state(previous_topic_data + padding, topic_size, SBUF(account_field));
|
||||
int64_t previous_topic_size =
|
||||
state(previous_topic_data + padding, topic_size, SBUF(account_field));
|
||||
|
||||
// check if the vote they're making has already been cast before,
|
||||
// if it is identical to their existing vote for this topic then just end with tesSUCCESS
|
||||
@@ -285,8 +267,8 @@ int64_t hook(uint32_t r)
|
||||
uint8_t votes = 0;
|
||||
// override the first two bytes to turn it into a vote count key
|
||||
previous_topic_data[0] = 'C';
|
||||
previous_topic_data[1] = topic[0];
|
||||
previous_topic_data[2] = topic[1];
|
||||
previous_topic_data[1] = t;
|
||||
previous_topic_data[2] = n;
|
||||
|
||||
if (state(&votes, 1, SBUF(previous_topic_data)) && votes > 0)
|
||||
{
|
||||
@@ -303,8 +285,8 @@ int64_t hook(uint32_t r)
|
||||
// so store the first bytes
|
||||
uint64_t saved_data = *((uint64_t*)topic_data);
|
||||
topic_data[0] = 'C';
|
||||
topic_data[1] = topic[0];
|
||||
topic_data[2] = topic[1];
|
||||
topic_data[1] = t;
|
||||
topic_data[2] = n;
|
||||
|
||||
state(&votes, 1, SBUF(topic_data));
|
||||
votes++;
|
||||
@@ -327,23 +309,27 @@ int64_t hook(uint32_t r)
|
||||
trace(SBUF("topic"), topic, 2, 1);
|
||||
}
|
||||
|
||||
// now check if we hit threshold
|
||||
if (votes < (topic[0] == 'S' ? (member_count * 0.8) : member_count))
|
||||
DONE("Governance: Vote recorded. Not yet enough votes to action.");
|
||||
|
||||
// 100%/80% threshold as needed is reached
|
||||
int64_t q80 = member_count * 0.8;
|
||||
|
||||
if (votes <
|
||||
t == 'S'
|
||||
? q80 // L1s have 80% threshold for membership/seat voting
|
||||
: member_count) // L1s have 100% threshold for all other voting
|
||||
|
||||
DONE("Governance: Vote recorded at L1. Not yet enough votes to action.");
|
||||
|
||||
// action vote
|
||||
|
||||
if (DEBUG)
|
||||
TRACESTR("Actioning votes");
|
||||
|
||||
switch (topic[0])
|
||||
switch (t)
|
||||
{
|
||||
case 'R':
|
||||
{
|
||||
// reward topics
|
||||
ASSERT(state_set(topic_data, 8, SBUF(topic))); // interest rate is on the the FF, 0...0 key
|
||||
if (topic[1] == 'R')
|
||||
if (n == 'R')
|
||||
DONE("Governance: Reward rate change actioned!");
|
||||
|
||||
DONE("Governance: Reward delay change actioned!");
|
||||
@@ -355,14 +341,14 @@ int64_t hook(uint32_t r)
|
||||
|
||||
// first get the hook ledget object
|
||||
uint8_t keylet[34];
|
||||
util_keylet(SBUF(keylet), KEYLET_HOOK, SBUF(hook_accid), 0,0,0,0);
|
||||
util_keylet(SBUF(keylet), KEYLET_HOOK, hook_accid + 12, 20, 0,0,0,0);
|
||||
slot_set(SBUF(keylet), 5);
|
||||
|
||||
// now get the hooks array
|
||||
slot_subfield(5, sfHooks, 6);
|
||||
|
||||
// now check the entry
|
||||
if (slot_subarray(6, topic[1], 7) == 7)
|
||||
if (slot_subarray(6, n, 7) == 7)
|
||||
{
|
||||
// it exists
|
||||
// check if its identical
|
||||
@@ -392,7 +378,7 @@ int64_t hook(uint32_t r)
|
||||
|
||||
|
||||
uint8_t* h[10];
|
||||
h[topic[1]] = hookhash;
|
||||
h[n] = hookhash;
|
||||
|
||||
uint8_t emit_buf[1024];
|
||||
uint32_t emit_size = 0;
|
||||
@@ -414,7 +400,7 @@ int64_t hook(uint32_t r)
|
||||
{
|
||||
// add / change member
|
||||
uint8_t previous_member[32];
|
||||
int previous_present = (state(previous_member + 12, 20, topic[1], 1) == 20);
|
||||
int previous_present = (state(previous_member + 12, 20, n, 1) == 20);
|
||||
|
||||
if (previous_present && !topic_data_zero)
|
||||
{
|
||||
@@ -478,10 +464,10 @@ int64_t hook(uint32_t r)
|
||||
{
|
||||
// add the new member
|
||||
// reverse key
|
||||
ASSERT(state_set(topic_data, 20, topic[1], 1) == 20);
|
||||
ASSERT(state_set(topic_data, 20, n, 1) == 20);
|
||||
|
||||
// forward key
|
||||
ASSERT(state_set(topic[1], 1, SBUF(topic_data)) == 20);
|
||||
ASSERT(state_set(n, 1, SBUF(topic_data)) == 20);
|
||||
}
|
||||
|
||||
DONE("Governance: Action member change.");
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
all: reward govern
|
||||
all: reward govern mint
|
||||
reward:
|
||||
wasmcc reward.c -o reward.wasm -Oz -Wl,--allow-undefined -I../
|
||||
wasm-opt reward.wasm -o reward.wasm \
|
||||
@@ -109,3 +109,58 @@ govern:
|
||||
-Oz
|
||||
hook-cleaner govern.wasm
|
||||
guard_checker govern.wasm
|
||||
mint:
|
||||
wasmcc mint.c -o mint.wasm -Oz -Wl,--allow-undefined -I../
|
||||
wasm-opt mint.wasm -o mint.wasm \
|
||||
--shrink-level=100000000 \
|
||||
--coalesce-locals-learning \
|
||||
--vacuum \
|
||||
--merge-blocks \
|
||||
--merge-locals \
|
||||
--flatten \
|
||||
--ignore-implicit-traps \
|
||||
-ffm \
|
||||
--const-hoisting \
|
||||
--code-folding \
|
||||
--code-pushing \
|
||||
--dae-optimizing \
|
||||
--dce \
|
||||
--simplify-globals-optimizing \
|
||||
--simplify-locals-nonesting \
|
||||
--reorder-locals \
|
||||
--rereloop \
|
||||
--precompute-propagate \
|
||||
--local-cse \
|
||||
--remove-unused-brs \
|
||||
--memory-packing \
|
||||
-c \
|
||||
--avoid-reinterprets \
|
||||
-Oz
|
||||
hook-cleaner mint.wasm
|
||||
wasm-opt mint.wasm -o mint.wasm \
|
||||
--shrink-level=100000000 \
|
||||
--coalesce-locals-learning \
|
||||
--vacuum \
|
||||
--merge-blocks \
|
||||
--merge-locals \
|
||||
--flatten \
|
||||
--ignore-implicit-traps \
|
||||
-ffm \
|
||||
--const-hoisting \
|
||||
--code-folding \
|
||||
--code-pushing \
|
||||
--dae-optimizing \
|
||||
--dce \
|
||||
--simplify-globals-optimizing \
|
||||
--simplify-locals-nonesting \
|
||||
--reorder-locals \
|
||||
--rereloop \
|
||||
--precompute-propagate \
|
||||
--local-cse \
|
||||
--remove-unused-brs \
|
||||
--memory-packing \
|
||||
-c \
|
||||
--avoid-reinterprets \
|
||||
-Oz
|
||||
hook-cleaner mint.wasm
|
||||
guard_checker mint.wasm
|
||||
|
||||
164
hook/mint.c
Normal file
164
hook/mint.c
Normal file
@@ -0,0 +1,164 @@
|
||||
// This hook just tests GenesisMint transactor, it is not for production use
|
||||
|
||||
#include "hookapi.h"
|
||||
#define ASSERT(x)\
|
||||
if (!(x))\
|
||||
rollback(SBUF("Reward: Assertion failure."),__LINE__);
|
||||
|
||||
#define DEBUG 1
|
||||
|
||||
uint8_t txn_mint[13850] =
|
||||
{
|
||||
/* 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 entry[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 ENTRY_DROPS(drops_tmp)\
|
||||
{\
|
||||
uint8_t* b = entry + 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 preamble[4] = {0xE0U, 0x60U, 0x61U, 0x00};
|
||||
|
||||
int64_t hook(uint32_t r)
|
||||
{
|
||||
etxn_reserve(1);
|
||||
_g(1,1);
|
||||
|
||||
|
||||
// emit the txn
|
||||
uint64_t drops = 12345;
|
||||
|
||||
ENTRY_DROPS(drops);
|
||||
|
||||
#define COUNT 400
|
||||
uint8_t* upto = txn_mint + 209U;
|
||||
uint8_t* end = upto + (34U * COUNT);
|
||||
|
||||
for (; GUARD(COUNT), upto < end; upto += 34)
|
||||
{
|
||||
uint64_t* d = (uint64_t*)upto;
|
||||
uint64_t* s = (uint64_t*)entry;
|
||||
|
||||
*d++ = *s++;
|
||||
*d++ = *s++;
|
||||
//*d++ = *s++;
|
||||
//*d++ = *s++;
|
||||
|
||||
*(d+2) = *(s+2);
|
||||
|
||||
otxn_field(upto + 13, 20, sfDestination);
|
||||
*((uint32_t*)(upto + 13)) = upto;
|
||||
|
||||
/*
|
||||
*
|
||||
*upto++ = 0xE0U; // obj start
|
||||
*upto++ = 0x60U;
|
||||
*upto++ = 0x61U; // amt
|
||||
*((uint32_t*)upto) = preamble;
|
||||
upto += 3;
|
||||
|
||||
*((uint64_t*)upto) = drops;
|
||||
upto += 8;
|
||||
|
||||
*upto++ = 0x83U; // acc
|
||||
*upto++ = 0x14U;
|
||||
*((uint16_t*)upto) = preamble2;
|
||||
upto += 2;
|
||||
*/
|
||||
|
||||
}
|
||||
*upto++ = 0xF1U; // array end
|
||||
|
||||
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("MintTest: Emit failed."), __LINE__);
|
||||
|
||||
|
||||
accept(SBUF("MintTest: Emitted txn successfully."), __LINE__);
|
||||
}
|
||||
242
hook/reward.c
242
hook/reward.c
@@ -1,65 +1,100 @@
|
||||
#include "hookapi.h"
|
||||
//#define REWARD_DELAY 2600000LL
|
||||
#define REWARD_DELAY 60LL
|
||||
#define REWARD_DELAY 5LL
|
||||
#define REWARD_MULTIPLIER_XFL 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
|
||||
|
||||
// this txn template is sent back to this account to distribute a duplicate reward to governance members
|
||||
// this distribution is handled by the governance reward hook
|
||||
uint8_t txn_loopback[235] =
|
||||
uint8_t txn_mint[928] =
|
||||
{
|
||||
/* size,upto */
|
||||
/* 3, 0 */ 0x12U, 0x00U, 0x63U, /* tt = Invoke */
|
||||
/* 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, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* src acc */
|
||||
/* 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,
|
||||
|
||||
/* 28,207 */ 0xF0U,0x13U,0xE0U,0x17U,0x70U,0x18U,0x06U,0x72U, /* hook params */
|
||||
0x65U,0x77U,0x61U,0x72U,0x64U,0x70U,0x19U,0x08U, /* reward key */
|
||||
/* 223 */ 0x00U,0x00U,0x00U,0x00U,0x00U,0x00U,0x00U,0x00U, /* value/drops */
|
||||
0xE1U,0xF1U,0xE1U,0xF1 /* end params */
|
||||
/* 235 */
|
||||
/* 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
|
||||
};
|
||||
|
||||
|
||||
// this txn template is used to pay out rewards
|
||||
uint8_t txn_payment[238] =
|
||||
#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] =
|
||||
{
|
||||
/* size,upto */
|
||||
/* 3, 0 */ 0x12U, 0x00U, 0x00U, /* tt = Payment */
|
||||
/* 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 */ 0x61U, 0x99U, 0x99U, 0x99U, 0x99U, 0x99U, 0x99U, 0x99U, 0x99U, 0x99U, /* amount field 9 bytes */
|
||||
/* 9, 34 */ 0x68U, 0x40U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, /* fee */
|
||||
/* 35, 43 */ 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, 78 */ 0x81U, 0x14U, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* src acc */
|
||||
/* 22,100 */ 0x83U, 0x14U, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* dst acc */
|
||||
/* 116,122 */ /* emit details */
|
||||
/* 0,238 */
|
||||
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
|
||||
};
|
||||
|
||||
#define HOOK_ACCOUNT (txn_loopback + 71U)
|
||||
#define HOOK_ACCOUNT_2 (txn_payment + 80U)
|
||||
#define ACC_FIELD (txn_payment + 102U)
|
||||
#define AMOUNT_OUT (txn_payment + 26U)
|
||||
#define AMOUNT_OUT_2 (txn_loopback + 223U)
|
||||
|
||||
uint8_t msg_buf[] = "You must wait 0000000 seconds";
|
||||
int64_t hook(uint32_t r)
|
||||
{
|
||||
@@ -70,21 +105,21 @@ int64_t hook(uint32_t r)
|
||||
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(ACC_FIELD, 20, sfAccount);
|
||||
otxn_field(SBUF(otxn_acc), sfAccount);
|
||||
|
||||
// write the hook account into the txn template
|
||||
hook_account(HOOK_ACCOUNT, 20);
|
||||
hook_account(SBUF(hook_acc));
|
||||
|
||||
// there are two txn templates so also write into the second template
|
||||
hook_account(HOOK_ACCOUNT_2, 20);
|
||||
|
||||
if (BUFFER_EQUAL_20(HOOK_ACCOUNT, ACC_FIELD))
|
||||
if (BUFFER_EQUAL_20(hook_acc, otxn_acc))
|
||||
accept(SBUF("Reward: Passing outgoing txn"), __LINE__);
|
||||
|
||||
// get the account root keylet
|
||||
uint8_t kl[34];
|
||||
util_keylet(SBUF(kl), KEYLET_ACCOUNT, ACC_FIELD, 20, 0,0,0,0);
|
||||
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);
|
||||
@@ -184,39 +219,118 @@ int64_t hook(uint32_t r)
|
||||
TRACEVAR(xfl_reward);
|
||||
|
||||
|
||||
// write drops to paymen txn
|
||||
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 drops = float_int(xfl_reward, 6, 1);
|
||||
uint8_t* b = AMOUNT_OUT;
|
||||
*b++ = 0b01000000 + (( drops >> 56 ) & 0b00111111 );
|
||||
*b++ = (drops >> 48) & 0xFFU;
|
||||
*b++ = (drops >> 40) & 0xFFU;
|
||||
*b++ = (drops >> 32) & 0xFFU;
|
||||
*b++ = (drops >> 24) & 0xFFU;
|
||||
*b++ = (drops >> 16) & 0xFFU;
|
||||
*b++ = (drops >> 8) & 0xFFU;
|
||||
*b++ = (drops >> 0) & 0xFFU;
|
||||
|
||||
// copy to other template
|
||||
*((uint64_t*)AMOUNT_OUT_2) = *((uint64_t*)AMOUNT_OUT);
|
||||
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);
|
||||
|
||||
// emit the payment transaction
|
||||
uint8_t emithash[32];
|
||||
int64_t emit_result = emit(SBUF(emithash), SBUF(txn_payment));
|
||||
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 reward failed."), __LINE__);
|
||||
|
||||
// emit the loopback txn
|
||||
emit_result = emit(SBUF(emithash), SBUF(txn_loopback));
|
||||
|
||||
if (DEBUG)
|
||||
TRACEVAR(emit_result);
|
||||
|
||||
if (emit_result < 0)
|
||||
rollback(SBUF("Reward: Emit loopback failed."), __LINE__);
|
||||
|
||||
|
||||
@@ -211,4 +211,5 @@
|
||||
#define sfDisabledValidators ((15U << 16U) + 17U)
|
||||
#define sfHookExecutions ((15U << 16U) + 18U)
|
||||
#define sfHookParameters ((15U << 16U) + 19U)
|
||||
#define sfHookGrants ((15U << 16U) + 20U)
|
||||
#define sfHookGrants ((15U << 16U) + 20U)
|
||||
#define sfActiveValidators ((15U << 16U) + 95U)
|
||||
|
||||
@@ -92,7 +92,7 @@ RCLConsensus::Adaptor::Adaptor(
|
||||
rand_int(
|
||||
crypto_prng(),
|
||||
std::numeric_limits<std::uint64_t>::max() - 1))
|
||||
, nUnlVote_(validatorKeys_.nodeID, j_)
|
||||
, nUnlVote_(validatorKeys_.nodeID, j_, app)
|
||||
{
|
||||
assert(valCookie_ != 0);
|
||||
|
||||
|
||||
@@ -416,7 +416,7 @@ private:
|
||||
/** A ledger wrapped in a CachedView. */
|
||||
using CachedLedger = CachedView<Ledger>;
|
||||
|
||||
std::uint32_t constexpr FLAG_LEDGER_INTERVAL = 256;
|
||||
std::uint32_t constexpr FLAG_LEDGER_INTERVAL = 16;
|
||||
/** Returns true if the given ledgerIndex is a flag ledgerIndex */
|
||||
bool
|
||||
isFlagLedger(LedgerIndex seq);
|
||||
|
||||
@@ -1327,8 +1327,11 @@ ApplicationImp::setup(boost::program_options::variables_map const& cmdline)
|
||||
|
||||
if (config_->IMPORT_VL_KEYS.empty())
|
||||
{
|
||||
JLOG(m_journal.fatal()) << "IMPORT_VL_KEYS section must be specified in validators file.";
|
||||
return false;
|
||||
JLOG(m_journal.warn()) << "[import_vl_keys] section not specified validators file. "
|
||||
<< "Will attempt to use keys from ledger.";
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
|
||||
namespace ripple {
|
||||
|
||||
NegativeUNLVote::NegativeUNLVote(NodeID const& myId, beast::Journal j)
|
||||
: myId_(myId), j_(j)
|
||||
NegativeUNLVote::NegativeUNLVote(NodeID const& myId, beast::Journal j, Application& app)
|
||||
: myId_(myId), j_(j), app_(app)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -99,9 +99,98 @@ NegativeUNLVote::doVoting(
|
||||
assert(nidToKeyMap.count(n));
|
||||
addTx(seq, nidToKeyMap[n], ToReEnable, initialSet);
|
||||
}
|
||||
|
||||
// do reporting when enabled
|
||||
if (prevLedger->rules().enabled(featureXahauGenesis) && scoreTable->size() > 0)
|
||||
addReportingTx(seq, *scoreTable, nidToKeyMap, initialSet);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NegativeUNLVote::addReportingTx(
|
||||
LedgerIndex seq,
|
||||
hash_map<NodeID, std::uint32_t> const& scoreTable,
|
||||
hash_map<NodeID, PublicKey> const& nidToKeyMap,
|
||||
std::shared_ptr<SHAMap> const& initalSet)
|
||||
{
|
||||
// RH NOTE: now that we use one key per txn with lots of txns
|
||||
// this ordering step is probably not needed
|
||||
std::set<PublicKey> ordered;
|
||||
for (auto const& [n, score]: scoreTable)
|
||||
{
|
||||
if (score > (FLAG_LEDGER_INTERVAL>>1))
|
||||
ordered.emplace(nidToKeyMap.at(n));
|
||||
}
|
||||
|
||||
for (auto const& pk : ordered)
|
||||
{
|
||||
STTx repUnlTx(ttUNL_REPORT, [&](auto& obj)
|
||||
{
|
||||
obj.set(([&]()
|
||||
{
|
||||
auto inner = std::make_unique<STObject>(sfActiveValidator);
|
||||
inner->setFieldVL(sfPublicKey, pk);
|
||||
return inner;
|
||||
})());
|
||||
obj.setFieldU32(sfLedgerSequence, seq);
|
||||
});
|
||||
|
||||
uint256 txID = repUnlTx.getTransactionID();
|
||||
Serializer s;
|
||||
repUnlTx.add(s);
|
||||
if (!initalSet->addGiveItem(
|
||||
SHAMapNodeType::tnTRANSACTION_NM,
|
||||
std::make_shared<SHAMapItem>(txID, s.slice())))
|
||||
{
|
||||
JLOG(j_.warn()) << "R-UNL: ledger seq=" << seq
|
||||
<< ", add ttUNL_REPORT tx failed";
|
||||
}
|
||||
else
|
||||
{
|
||||
JLOG(j_.debug()) << "R-UNL: ledger seq=" << seq
|
||||
<< ", add a ttUNL_REPORT (active_val) Tx with txID: " << txID
|
||||
<< ", size=" << s.size()
|
||||
<< ", " << repUnlTx.getJson(JsonOptions::none);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// do import VL key voting
|
||||
auto const& keyMap = app_.config().IMPORT_VL_KEYS;
|
||||
for (auto const& [_, pk] : keyMap)
|
||||
{
|
||||
STTx repUnlTx(ttUNL_REPORT, [&](auto& obj)
|
||||
{
|
||||
obj.set(([&]()
|
||||
{
|
||||
auto inner = std::make_unique<STObject>(sfImportVLKey);
|
||||
inner->setFieldVL(sfPublicKey, pk);
|
||||
return inner;
|
||||
})());
|
||||
obj.setFieldU32(sfLedgerSequence, seq);
|
||||
});
|
||||
|
||||
uint256 txID = repUnlTx.getTransactionID();
|
||||
Serializer s;
|
||||
repUnlTx.add(s);
|
||||
if (!initalSet->addGiveItem(
|
||||
SHAMapNodeType::tnTRANSACTION_NM,
|
||||
std::make_shared<SHAMapItem>(txID, s.slice())))
|
||||
{
|
||||
JLOG(j_.warn()) << "R-UNL: ledger seq=" << seq
|
||||
<< ", add ttUNL_REPORT tx failed (import_vl_key)";
|
||||
}
|
||||
else
|
||||
{
|
||||
JLOG(j_.debug()) << "R-UNL: ledger seq=" << seq
|
||||
<< ", add a ttUNL_REPORT (import_vl) Tx with txID: " << txID
|
||||
<< ", size=" << s.size()
|
||||
<< ", " << repUnlTx.getJson(JsonOptions::none);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
NegativeUNLVote::addTx(
|
||||
LedgerIndex seq,
|
||||
|
||||
@@ -91,7 +91,7 @@ public:
|
||||
* @param myId the NodeID of the local node
|
||||
* @param j log
|
||||
*/
|
||||
NegativeUNLVote(NodeID const& myId, beast::Journal j);
|
||||
NegativeUNLVote(NodeID const& myId, beast::Journal j, Application& app);
|
||||
~NegativeUNLVote() = default;
|
||||
|
||||
/**
|
||||
@@ -128,6 +128,7 @@ private:
|
||||
beast::Journal j_;
|
||||
mutable std::mutex mutex_;
|
||||
hash_map<NodeID, LedgerIndex> newValidators_;
|
||||
Application& app_;
|
||||
|
||||
/**
|
||||
* UNLModify Tx candidates
|
||||
@@ -153,6 +154,16 @@ private:
|
||||
NegativeUNLModify modify,
|
||||
std::shared_ptr<SHAMap> const& initialSet);
|
||||
|
||||
/**
|
||||
* As above, but make a report object instead of an n-unl
|
||||
*/
|
||||
void
|
||||
addReportingTx(
|
||||
LedgerIndex seq,
|
||||
hash_map<NodeID, std::uint32_t> const& scoreTable,
|
||||
hash_map<NodeID, PublicKey> const& nidToKeyMap,
|
||||
std::shared_ptr<SHAMap> const& initalSet);
|
||||
|
||||
/**
|
||||
* Pick one candidate from a vector of candidates.
|
||||
*
|
||||
|
||||
@@ -79,9 +79,6 @@ namespace ripple {
|
||||
|
||||
class NetworkOPsImp final : public NetworkOPs
|
||||
{
|
||||
// ledger_seq -> validator key -> validation message
|
||||
std::map<uint32_t, std::map<PublicKey, STValidation>> xpop_history;
|
||||
|
||||
/**
|
||||
* Transaction with input flags and results to be applied in batches.
|
||||
*/
|
||||
@@ -2341,37 +2338,6 @@ NetworkOPsImp::recvValidation(
|
||||
|
||||
handleNewValidation(app_, val, source);
|
||||
|
||||
// manage xpop validation history
|
||||
if (app_.config().XPOP_HISTORY)
|
||||
{
|
||||
if (val->isTrusted())
|
||||
{
|
||||
uint32_t seq = val->getFieldU32(sfLedgerSequence);
|
||||
|
||||
if (xpop_history.find(seq) == xpop_history.end())
|
||||
xpop_history.emplace(seq, std::map<PublicKey, STValidation>{});
|
||||
|
||||
xpop_history[seq].emplace(val->getSignerPublic(), *val);
|
||||
}
|
||||
|
||||
uint32_t delete_threshold =
|
||||
app_.getLedgerMaster().getValidLedgerIndex() + 1 - *(app_.config().XPOP_HISTORY);
|
||||
|
||||
if (xpop_history.find(delete_threshold) != xpop_history.end())
|
||||
xpop_history.erase(delete_threshold);
|
||||
|
||||
if (xpop_history.size() > *(app_.config().XPOP_HISTORY))
|
||||
{
|
||||
std::unordered_set<uint32_t> to_delete;
|
||||
for (auto const& kp : xpop_history)
|
||||
if (kp.first < delete_threshold)
|
||||
to_delete.emplace(kp.first);
|
||||
|
||||
for (uint32_t td: to_delete)
|
||||
xpop_history.erase(td);
|
||||
}
|
||||
}
|
||||
|
||||
pubValidation(val);
|
||||
|
||||
// We will always relay trusted validations; if configured, we will
|
||||
|
||||
@@ -28,6 +28,9 @@
|
||||
#include <ripple/protocol/Indexes.h>
|
||||
#include <ripple/protocol/TxFlags.h>
|
||||
#include <string_view>
|
||||
#include <ripple/app/hook/Guard.h>
|
||||
#include <ripple/protocol/AccountID.h>
|
||||
#include <ripple/app/hook/applyHook.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
@@ -74,6 +77,21 @@ Change::preflight(PreflightContext const& ctx)
|
||||
return temDISABLED;
|
||||
}
|
||||
|
||||
if (ctx.tx.getTxnType() == ttUNL_REPORT)
|
||||
{
|
||||
if (!ctx.rules.enabled(featureXahauGenesis))
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Change: UNLReport is not enabled.";
|
||||
return temDISABLED;
|
||||
}
|
||||
|
||||
if (!ctx.tx.isFieldPresent(sfActiveValidator) && !ctx.tx.isFieldPresent(sfImportVLKey))
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Change: UNLReport must specify at least one of sfImportVLKey, sfActiveValidator";
|
||||
return temMALFORMED;
|
||||
}
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -131,6 +149,7 @@ Change::preclaim(PreclaimContext const& ctx)
|
||||
return tesSUCCESS;
|
||||
case ttAMENDMENT:
|
||||
case ttUNL_MODIFY:
|
||||
case ttUNL_REPORT:
|
||||
case ttEMIT_FAILURE:
|
||||
return tesSUCCESS;
|
||||
default:
|
||||
@@ -151,18 +170,330 @@ Change::doApply()
|
||||
return applyUNLModify();
|
||||
case ttEMIT_FAILURE:
|
||||
return applyEmitFailure();
|
||||
case ttUNL_REPORT:
|
||||
return applyUNLReport();
|
||||
default:
|
||||
assert(0);
|
||||
return tefFAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
TER
|
||||
Change::applyUNLReport()
|
||||
{
|
||||
auto sle = view().peek(keylet::UNLReport());
|
||||
|
||||
auto const seq = view().info().seq;
|
||||
|
||||
bool const created = !sle;
|
||||
|
||||
if (created)
|
||||
sle = std::make_shared<SLE>(keylet::UNLReport());
|
||||
|
||||
bool const reset =
|
||||
sle->isFieldPresent(sfPreviousTxnLgrSeq) &&
|
||||
sle->getFieldU32(sfPreviousTxnLgrSeq) < seq;
|
||||
|
||||
auto canonicalize = [&](SField const& arrayType, SField const& objType)
|
||||
-> std::vector<STObject>
|
||||
{
|
||||
auto const existing =
|
||||
reset || !sle->isFieldPresent(arrayType)
|
||||
? STArray(arrayType)
|
||||
: sle->getFieldArray(arrayType);
|
||||
|
||||
// canonically order using std::set
|
||||
std::map<PublicKey, AccountID> ordered;
|
||||
for (auto const& obj: existing)
|
||||
{
|
||||
auto pk = obj.getFieldVL(sfPublicKey);
|
||||
if (!publicKeyType(makeSlice(pk)))
|
||||
continue;
|
||||
|
||||
PublicKey p(makeSlice(pk));
|
||||
ordered.emplace(p,
|
||||
obj.isFieldPresent(sfAccount) ? obj.getAccountID(sfAccount) : calcAccountID(p));
|
||||
};
|
||||
|
||||
if (ctx_.tx.isFieldPresent(objType))
|
||||
{
|
||||
auto pk =
|
||||
const_cast<ripple::STTx&>(ctx_.tx)
|
||||
.getField(objType)
|
||||
.downcast<STObject>()
|
||||
.getFieldVL(sfPublicKey);
|
||||
|
||||
if (publicKeyType(makeSlice(pk)))
|
||||
{
|
||||
PublicKey p(makeSlice(pk));
|
||||
ordered.emplace(p, calcAccountID(p));
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<STObject> out;
|
||||
out.reserve(ordered.size());
|
||||
for (auto const& [k, a]: ordered)
|
||||
{
|
||||
out.emplace_back(objType);
|
||||
out.back().setFieldVL(sfPublicKey, k);
|
||||
out.back().setAccountID(sfAccount, a);
|
||||
}
|
||||
|
||||
return out;
|
||||
};
|
||||
|
||||
|
||||
bool const hasAV = ctx_.tx.isFieldPresent(sfActiveValidator);
|
||||
bool const hasVL = ctx_.tx.isFieldPresent(sfImportVLKey);
|
||||
|
||||
// update
|
||||
if (hasAV)
|
||||
sle->setFieldArray(sfActiveValidators,
|
||||
STArray(canonicalize(sfActiveValidators, sfActiveValidator),sfActiveValidators));
|
||||
|
||||
if (hasVL)
|
||||
sle->setFieldArray(sfImportVLKeys,
|
||||
STArray(canonicalize(sfImportVLKeys, sfImportVLKey),sfImportVLKeys));
|
||||
|
||||
if (created)
|
||||
view().insert(sle);
|
||||
else
|
||||
view().update(sle);
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
void
|
||||
Change::preCompute()
|
||||
{
|
||||
assert(account_ == beast::zero);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Change::activateXahauGenesis()
|
||||
{
|
||||
|
||||
return;
|
||||
|
||||
JLOG(j_.warn()) << "featureXahauGenesis amendment activation code starting";
|
||||
|
||||
constexpr XRPAmount GENESIS { 1'000'000 * DROPS_PER_XRP };
|
||||
|
||||
constexpr XRPAmount INFRA { 10'000'000 * DROPS_PER_XRP};
|
||||
constexpr XRPAmount EXCHANGE { 2'000'000 * DROPS_PER_XRP};
|
||||
|
||||
const static std::vector<std::pair<std::string, XRPAmount>>
|
||||
initial_distribution =
|
||||
{
|
||||
{"rMYm3TY5D3rXYVAz6Zr2PDqEcjsTYbNiAT", INFRA},
|
||||
};
|
||||
|
||||
const static std::vector<std::pair<uint256, std::vector<uint8_t>>>
|
||||
genesis_hooks =
|
||||
{
|
||||
{ ripple::uint256("0000000000000000000000000000000000000000000000000000000000000001"),
|
||||
{0x0}
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
Sandbox sb(&view());
|
||||
|
||||
// Step 1: mint genesis distribution
|
||||
for (auto const& [account, amount] : initial_distribution)
|
||||
{
|
||||
auto accid_raw = parseBase58<AccountID>(account);
|
||||
if (!accid_raw)
|
||||
{
|
||||
JLOG(j_.warn())
|
||||
<< "featureXahauGenesis could not parse an r-address: " << account << ", bailing.";
|
||||
return;
|
||||
}
|
||||
|
||||
auto accid = *accid_raw;
|
||||
|
||||
auto const kl = keylet::account(accid);
|
||||
|
||||
auto sle = sb.peek(kl);
|
||||
auto const exists = !!sle;
|
||||
|
||||
STAmount bal = exists ? sle->getFieldAmount(sfBalance) + STAmount{amount} : STAmount{amount};
|
||||
if (bal <= beast::zero)
|
||||
{
|
||||
JLOG(j_.warn())
|
||||
<< "featureXahauGenesis tried to set <= 0 balance on " << account << ", bailing";
|
||||
return;
|
||||
}
|
||||
|
||||
// the account should not exist but if it does then handle it properly
|
||||
if (!exists)
|
||||
{
|
||||
sle = std::make_shared<SLE>(kl);
|
||||
sle->setAccountID(sfAccount, accid);
|
||||
|
||||
std::uint32_t const seqno{
|
||||
sb.rules().enabled(featureDeletableAccounts) ? sb.seq()
|
||||
: 1};
|
||||
sle->setFieldU32(sfSequence, seqno);
|
||||
|
||||
}
|
||||
|
||||
sle->setFieldAmount(sfBalance, bal);
|
||||
|
||||
if (exists)
|
||||
sb.update(sle);
|
||||
else
|
||||
sb.insert(sle);
|
||||
|
||||
};
|
||||
|
||||
// Step 2: burn genesis funds to (almost) zero
|
||||
static auto const accid = calcAccountID(
|
||||
generateKeyPair(KeyType::secp256k1, generateSeed("masterpassphrase"))
|
||||
.first);
|
||||
|
||||
auto const kl = keylet::account(accid);
|
||||
auto sle = sb.peek(kl);
|
||||
if (!sle)
|
||||
{
|
||||
JLOG(j_.warn())
|
||||
<< "featureXahauGenesis genesis account doesn't exist!!";
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
sle->setFieldAmount(sfBalance, GENESIS);
|
||||
|
||||
// Step 3: blackhole genesis
|
||||
sle->setAccountID(sfRegularKey, noAccount());
|
||||
sle->setFieldU32(sfFlags, lsfDisableMaster);
|
||||
|
||||
|
||||
// Step 4: install genesis hooks
|
||||
|
||||
sle->setFieldU32(sfOwnerCount, sle->getFieldU32(sfOwnerCount) + genesis_hooks.size());
|
||||
sb.update(sle);
|
||||
|
||||
if (sb.exists(keylet::hook(accid)))
|
||||
{
|
||||
JLOG(j_.warn())
|
||||
<< "featureXahauGenesis genesis account already has hooks object in ledger, bailing";
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
ripple::STArray hooks{sfHooks, genesis_hooks.size()};
|
||||
int hookCount = 0;
|
||||
|
||||
for (auto const& [hookOn, wasmBytes] : genesis_hooks)
|
||||
{
|
||||
|
||||
std::ostringstream loggerStream;
|
||||
auto result =
|
||||
validateGuards(
|
||||
wasmBytes, // wasm to verify
|
||||
loggerStream,
|
||||
"rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"
|
||||
);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
std::string s = loggerStream.str();
|
||||
|
||||
char* data = s.data();
|
||||
size_t len = s.size();
|
||||
|
||||
char* last = data;
|
||||
size_t i = 0;
|
||||
for (; i < len; ++i)
|
||||
{
|
||||
if (data[i] == '\n')
|
||||
{
|
||||
data[i] = '\0';
|
||||
j_.warn() << last;
|
||||
last = data + i;
|
||||
}
|
||||
}
|
||||
|
||||
if (last < data + i)
|
||||
j_.warn() << last;
|
||||
|
||||
JLOG(j_.warn())
|
||||
<< "featureXahauGenesis initial hook failed to validate guards, bailing";
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
std::optional<std::string> result2 =
|
||||
hook::HookExecutor::validateWasm(wasmBytes.data(), (size_t)wasmBytes.size());
|
||||
|
||||
if (result2)
|
||||
{
|
||||
JLOG(j_.warn())
|
||||
<< "featureXahauGenesis tried to set a hook with invalid code. VM error: "
|
||||
<< *result2 << ", bailing";
|
||||
return;
|
||||
}
|
||||
|
||||
auto hookHash = ripple::sha512Half_s(ripple::Slice(wasmBytes.data(), wasmBytes.size()));
|
||||
auto const kl = keylet::hookDefinition(hookHash);
|
||||
if (view().exists(kl))
|
||||
{
|
||||
JLOG(j_.warn())
|
||||
<< "featureXahauGenesis genesis hookDefinition already exists !!! bailing";
|
||||
return;
|
||||
}
|
||||
|
||||
auto hookDef = std::make_shared<SLE>(kl);
|
||||
|
||||
hookDef->setFieldH256(sfHookHash, hookHash);
|
||||
hookDef->setFieldH256(sfHookOn, hookOn);
|
||||
hookDef->setFieldH256(sfHookNamespace, UINT256_BIT[hookCount++]);
|
||||
hookDef->setFieldArray(sfHookParameters, STArray{});
|
||||
hookDef->setFieldU8(sfHookApiVersion, 0);
|
||||
hookDef->setFieldVL(sfCreateCode, wasmBytes);
|
||||
hookDef->setFieldH256(sfHookSetTxnID, ctx_.tx.getTransactionID());
|
||||
hookDef->setFieldU64(sfReferenceCount, 1);
|
||||
hookDef->setFieldAmount(sfFee,
|
||||
XRPAmount {hook::computeExecutionFee(result->first)});
|
||||
if (result->second > 0)
|
||||
hookDef->setFieldAmount(sfHookCallbackFee,
|
||||
XRPAmount {hook::computeExecutionFee(result->second)});
|
||||
|
||||
sb.insert(hookDef);
|
||||
|
||||
STObject hookObj {sfHook};
|
||||
hookObj.setFieldH256(sfHookHash, hookHash);
|
||||
hooks.push_back(hookObj);
|
||||
|
||||
}
|
||||
|
||||
|
||||
auto sle = std::make_shared<SLE>(keylet::hook(accid));
|
||||
sle->setFieldArray(sfHooks, hooks);
|
||||
sle->setAccountID(sfAccount, accid);
|
||||
|
||||
auto const page = sb.dirInsert(
|
||||
keylet::ownerDir(accid),
|
||||
keylet::hook(accid),
|
||||
describeOwnerDir(accid));
|
||||
|
||||
if (!page)
|
||||
{
|
||||
JLOG(j_.warn())
|
||||
<< "featureXahauGenesis genesis directory full when trying to insert hooks object, bailing";
|
||||
return;
|
||||
}
|
||||
sle->setFieldU64(sfOwnerNode, *page);
|
||||
sb.insert(sle);
|
||||
}
|
||||
|
||||
JLOG(j_.warn()) << "featureXahauGenesis amendment executed successfully";
|
||||
|
||||
sb.apply(ctx_.rawView());
|
||||
}
|
||||
|
||||
void
|
||||
Change::activateTrustLinesToSelfFix()
|
||||
{
|
||||
@@ -323,6 +654,8 @@ Change::applyAmendment()
|
||||
|
||||
if (amendment == fixTrustLinesToSelf)
|
||||
activateTrustLinesToSelfFix();
|
||||
else if (amendment == featureXahauGenesis)
|
||||
activateXahauGenesis();
|
||||
|
||||
ctx_.app.getAmendmentTable().enable(amendment);
|
||||
|
||||
|
||||
@@ -59,6 +59,9 @@ private:
|
||||
void
|
||||
activateTrustLinesToSelfFix();
|
||||
|
||||
void
|
||||
activateXahauGenesis();
|
||||
|
||||
TER
|
||||
applyAmendment();
|
||||
|
||||
@@ -70,6 +73,9 @@ private:
|
||||
|
||||
TER
|
||||
applyEmitFailure();
|
||||
|
||||
TER
|
||||
applyUNLReport();
|
||||
};
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
221
src/ripple/app/tx/impl/GenesisMint.cpp
Normal file
221
src/ripple/app/tx/impl/GenesisMint.cpp
Normal file
@@ -0,0 +1,221 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/app/tx/impl/GenesisMint.h>
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <ripple/ledger/View.h>
|
||||
#include <ripple/protocol/Feature.h>
|
||||
#include <ripple/protocol/Indexes.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
TxConsequences
|
||||
GenesisMint::makeTxConsequences(PreflightContext const& ctx)
|
||||
{
|
||||
// RH TODO: review this
|
||||
return TxConsequences{ctx.tx, TxConsequences::normal};
|
||||
}
|
||||
|
||||
NotTEC
|
||||
GenesisMint::preflight(PreflightContext const& ctx)
|
||||
{
|
||||
if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
|
||||
return ret;
|
||||
|
||||
auto& tx = ctx.tx;
|
||||
|
||||
auto const id = ctx.tx[sfAccount];
|
||||
|
||||
static auto const genesisAccountId = calcAccountID(
|
||||
generateKeyPair(KeyType::secp256k1, generateSeed("masterpassphrase"))
|
||||
.first);
|
||||
|
||||
if (id != genesisAccountId || !tx.isFieldPresent(sfEmitDetails))
|
||||
{
|
||||
JLOG(ctx.j.warn())
|
||||
<< "GenesisMint: can only be used by the genesis account in an emitted transaction.";
|
||||
return temMALFORMED;
|
||||
}
|
||||
|
||||
auto const& dests = tx.getFieldArray(sfGenesisMints);
|
||||
if (dests.empty())
|
||||
{
|
||||
JLOG(ctx.j.warn())
|
||||
<< "GenesisMint: destinations array empty.";
|
||||
return temMALFORMED;
|
||||
}
|
||||
|
||||
if (dests.size() > 512)
|
||||
{
|
||||
JLOG(ctx.j.warn())
|
||||
<< "GenesisMint: destinations array exceeds 512 entries.";
|
||||
return temMALFORMED;
|
||||
}
|
||||
|
||||
std::unordered_set<AccountID> alreadySeen;
|
||||
for (auto const& dest: dests)
|
||||
{
|
||||
if (dest.getFName() != sfGenesisMint)
|
||||
{
|
||||
JLOG(ctx.j.warn())
|
||||
<< "GenesisMint: destinations array contained an invalid entry.";
|
||||
return temMALFORMED;
|
||||
}
|
||||
|
||||
|
||||
bool const hasAmt = dest.isFieldPresent(sfAmount);
|
||||
bool const hasMarks = dest.isFieldPresent(sfGovernanceMarks);
|
||||
bool const hasFlags = dest.isFieldPresent(sfGovernanceFlags);
|
||||
|
||||
if (!hasAmt && !hasMarks && !hasFlags)
|
||||
{
|
||||
JLOG(ctx.j.warn())
|
||||
<< "GenesisMint: each destination must have at least one of: "
|
||||
<< "sfAmount, sfGovernanceFlags, sfGovernance marks.";
|
||||
return temMALFORMED;
|
||||
}
|
||||
|
||||
if (hasAmt)
|
||||
{
|
||||
auto const amt = dest.getFieldAmount(sfAmount);
|
||||
if (!isXRP(amt))
|
||||
{
|
||||
JLOG(ctx.j.warn())
|
||||
<< "GenesisMint: only native amounts can be minted.";
|
||||
return temMALFORMED;
|
||||
}
|
||||
|
||||
if (amt <= beast::zero)
|
||||
{
|
||||
JLOG(ctx.j.warn())
|
||||
<< "GenesisMint: only positive amounts can be minted.";
|
||||
return temMALFORMED;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
auto const accid = dest.getAccountID(sfDestination);
|
||||
|
||||
if (accid == noAccount() || accid == xrpAccount())
|
||||
{
|
||||
JLOG(ctx.j.warn())
|
||||
<< "GenesisMint: destinations includes disallowed account zero or one.";
|
||||
return temMALFORMED;
|
||||
}
|
||||
|
||||
if (alreadySeen.find(accid) != alreadySeen.end())
|
||||
{
|
||||
JLOG(ctx.j.warn())
|
||||
<< "GenesisMint: duplicate in destinations.";
|
||||
return temMALFORMED;
|
||||
}
|
||||
|
||||
alreadySeen.emplace(accid);
|
||||
}
|
||||
|
||||
return preflight2(ctx);
|
||||
}
|
||||
|
||||
TER
|
||||
GenesisMint::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
if (!ctx.view.rules().enabled(featureHooks))
|
||||
return temDISABLED;
|
||||
|
||||
if (!ctx.view.rules().enabled(featureXahauGenesis))
|
||||
return temDISABLED;
|
||||
|
||||
// RH UPTO:
|
||||
// check that printing won't exceed 200% of the total coins on the ledger
|
||||
// this will act as a hard cap against malfunction
|
||||
// modify the invariant checkers
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
GenesisMint::doApply()
|
||||
{
|
||||
auto const& dests = ctx_.tx.getFieldArray(sfGenesisMints);
|
||||
|
||||
for (auto const& dest: dests)
|
||||
{
|
||||
auto const amt = dest[~sfAmount];
|
||||
auto const flags = dest[~sfGovernanceFlags];
|
||||
auto const marks = dest[~sfGovernanceMarks];
|
||||
|
||||
auto const id = dest.getAccountID(sfDestination);
|
||||
auto const k = keylet::account(id);
|
||||
auto sle = view().peek(k);
|
||||
|
||||
bool const created = !sle;
|
||||
|
||||
if (created)
|
||||
{
|
||||
// Create the account.
|
||||
std::uint32_t const seqno{
|
||||
view().rules().enabled(featureDeletableAccounts) ? view().seq()
|
||||
: 1};
|
||||
sle = std::make_shared<SLE>(k);
|
||||
sle->setAccountID(sfAccount, id);
|
||||
|
||||
sle->setFieldU32(sfSequence, seqno);
|
||||
|
||||
if (amt)
|
||||
sle->setFieldAmount(sfBalance, *amt);
|
||||
else // give them 2 XRP if the account didn't exist, same as ttIMPORT
|
||||
sle->setFieldAmount(sfBalance, XRPAmount {2 * DROPS_PER_XRP});
|
||||
}
|
||||
else if (amt)
|
||||
{
|
||||
// Credit the account
|
||||
STAmount startBal = sle->getFieldAmount(sfBalance);
|
||||
STAmount finalBal = startBal + *amt;
|
||||
if (finalBal > startBal)
|
||||
sle->setFieldAmount(sfBalance, finalBal);
|
||||
else
|
||||
{
|
||||
JLOG(ctx_.journal.warn())
|
||||
<< "GenesisMint: cannot credit " << dest << " due to balance overflow";
|
||||
}
|
||||
}
|
||||
|
||||
// set flags and marks as applicable
|
||||
if (flags)
|
||||
sle->setFieldH256(sfGovernanceFlags, *flags);
|
||||
|
||||
if (marks)
|
||||
sle->setFieldH256(sfGovernanceMarks, *marks);
|
||||
|
||||
if (created)
|
||||
view().insert(sle);
|
||||
else
|
||||
view().update(sle);
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
XRPAmount
|
||||
GenesisMint::calculateBaseFee(ReadView const& view, STTx const& tx)
|
||||
{
|
||||
return XRPAmount { 0 } ;
|
||||
}
|
||||
|
||||
} // namespace ripple
|
||||
57
src/ripple/app/tx/impl/GenesisMint.h
Normal file
57
src/ripple/app/tx/impl/GenesisMint.h
Normal file
@@ -0,0 +1,57 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_TX_GENESISMINT_H_INCLUDED
|
||||
#define RIPPLE_TX_GENESISMINT_H_INCLUDED
|
||||
|
||||
#include <ripple/app/tx/impl/Transactor.h>
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <ripple/core/Config.h>
|
||||
#include <ripple/protocol/Indexes.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
class GenesisMint : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Custom};
|
||||
|
||||
explicit GenesisMint(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static XRPAmount
|
||||
calculateBaseFee(ReadView const& view, STTx const& tx);
|
||||
|
||||
static TxConsequences
|
||||
makeTxConsequences(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
#endif
|
||||
@@ -163,11 +163,28 @@ Import::preflight(PreflightContext const& ctx)
|
||||
if (!xpop)
|
||||
return temMALFORMED;
|
||||
|
||||
// check if we recognise the vl key
|
||||
auto const& vlKeys = ctx.app.config().IMPORT_VL_KEYS;
|
||||
auto const found = std::ranges::find(vlKeys, (*xpop)[jss::validation][jss::unl][jss::public_key].asString());
|
||||
if (found == vlKeys.end())
|
||||
return telIMPORT_VL_KEY_NOT_RECOGNISED;
|
||||
// we will check if we recognise the vl key in preclaim because it may be from on-ledger object
|
||||
std::optional<PublicKey> masterVLKey;
|
||||
{
|
||||
std::string strPk = (*xpop)[jss::validation][jss::unl][jss::public_key].asString();
|
||||
auto pkHex = strUnHex(strPk);
|
||||
if (!pkHex)
|
||||
{
|
||||
JLOG(ctx.j.warn())
|
||||
<< "Import: validation.unl.public_key was not valid hex.";
|
||||
return temMALFORMED;
|
||||
}
|
||||
|
||||
auto const pkType = publicKeyType(makeSlice(*pkHex));
|
||||
if (!pkType)
|
||||
{
|
||||
JLOG(ctx.j.warn())
|
||||
<< "Import: validation.unl.public_key was not a recognised public key type.";
|
||||
return temMALFORMED;
|
||||
}
|
||||
|
||||
masterVLKey = PublicKey(makeSlice(*pkHex));
|
||||
}
|
||||
|
||||
auto const [stpTrans, meta] = getInnerTxn(tx, ctx.j, &(*xpop));
|
||||
|
||||
@@ -337,7 +354,9 @@ Import::preflight(PreflightContext const& ctx)
|
||||
return temMALFORMED;
|
||||
}
|
||||
|
||||
if (strHex(m->masterKey) != *found)
|
||||
// we will check the master key matches a known one in preclaim, because the import vl key might be
|
||||
// from the on-ledger object
|
||||
if (m->masterKey != masterVLKey)
|
||||
{
|
||||
JLOG(ctx.j.warn())
|
||||
<< "Import: manifest master key did not match top level master key in unl section of xpop "
|
||||
@@ -352,6 +371,7 @@ Import::preflight(PreflightContext const& ctx)
|
||||
<< tx.getTransactionID();
|
||||
return temMALFORMED;
|
||||
}
|
||||
|
||||
|
||||
// manifest signing (ephemeral) key
|
||||
auto const signingKey = m->signingKey;
|
||||
@@ -876,7 +896,34 @@ Import::preclaim(PreclaimContext const& ctx)
|
||||
if (sleVL && sleVL->getFieldU32(sfImportSequence) > vlInfo->first)
|
||||
return tefPAST_IMPORT_VL_SEQ;
|
||||
|
||||
return tesSUCCESS;
|
||||
// check master VL key
|
||||
std::string strPk = (*xpop)[jss::validation][jss::unl][jss::public_key].asString();
|
||||
|
||||
if (auto const& found = ctx.app.config().IMPORT_VL_KEYS.find(strPk);
|
||||
found != ctx.app.config().IMPORT_VL_KEYS.end())
|
||||
return tesSUCCESS;
|
||||
|
||||
// not found in our local VL keys
|
||||
auto pkHex = strUnHex(strPk);
|
||||
if (!pkHex)
|
||||
return tefINTERNAL;
|
||||
|
||||
auto const pkType = publicKeyType(makeSlice(*pkHex));
|
||||
if (!pkType)
|
||||
return tefINTERNAL;
|
||||
|
||||
PublicKey const pk (makeSlice(*pkHex));
|
||||
|
||||
// check on ledger
|
||||
if (auto const unlRep = ctx.view.read(keylet::UNLReport()); unlRep)
|
||||
{
|
||||
auto const& vlKeys = unlRep->getFieldArray(sfImportVLKeys);
|
||||
for (auto const& k: vlKeys)
|
||||
if (PublicKey(k[sfPublicKey]) == pk)
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
return telIMPORT_VL_KEY_NOT_RECOGNISED;
|
||||
}
|
||||
|
||||
TER
|
||||
|
||||
@@ -143,13 +143,15 @@ XRPNotCreated::visitEntry(
|
||||
bool
|
||||
XRPNotCreated::finalize(
|
||||
STTx const& tx,
|
||||
TER const,
|
||||
TER const res,
|
||||
XRPAmount const fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j)
|
||||
{
|
||||
|
||||
if (view.rules().enabled(featureImport) && tx.getTxnType() == ttIMPORT)
|
||||
auto const tt = tx.getTxnType();
|
||||
|
||||
if (view.rules().enabled(featureImport) && tt == ttIMPORT && res == tesSUCCESS)
|
||||
{
|
||||
// different rules for ttIMPORT
|
||||
auto const [inner, meta] = Import::getInnerTxn(tx, j);
|
||||
@@ -176,6 +178,24 @@ XRPNotCreated::finalize(
|
||||
return (drops_ == dropsAdded.drops() - fee.drops());
|
||||
}
|
||||
|
||||
if (view.rules().enabled(featureXahauGenesis) && tt == ttGENESIS_MINT && res == tesSUCCESS)
|
||||
{
|
||||
// different rules for ttGENESIS_MINT
|
||||
auto const& dests = tx.getFieldArray(sfGenesisMints);
|
||||
XRPAmount dropsAdded { beast::zero };
|
||||
for (auto const& dest: dests)
|
||||
dropsAdded += dest.getFieldAmount(sfAmount).xrp();
|
||||
|
||||
JLOG(j.trace())
|
||||
<< "Invariant XRPNotCreated GenesisMint: "
|
||||
<< "dropsAdded: " << dropsAdded
|
||||
<< " fee.drops(): " << fee.drops()
|
||||
<< " drops_: " << drops_
|
||||
<< " dropsAdded - fee.drops(): " << dropsAdded - fee.drops();
|
||||
|
||||
return (drops_ == dropsAdded.drops() - fee.drops());
|
||||
}
|
||||
|
||||
// The net change should never be positive, as this would mean that the
|
||||
// transaction created XRP out of thin air. That's not possible.
|
||||
if (drops_ > 0)
|
||||
@@ -426,6 +446,7 @@ LedgerEntryTypesMatch::visitEntry(
|
||||
case ltNFTOKEN_OFFER:
|
||||
case ltURI_TOKEN:
|
||||
case ltIMPORT_VLSEQ:
|
||||
case ltUNL_REPORT:
|
||||
break;
|
||||
default:
|
||||
invalidTypeAdded_ = true;
|
||||
@@ -518,16 +539,16 @@ ValidNewAccountRoot::finalize(
|
||||
if (accountsCreated_ == 0)
|
||||
return true;
|
||||
|
||||
if (accountsCreated_ > 1)
|
||||
auto tt = tx.getTxnType();
|
||||
|
||||
if (accountsCreated_ > 1 && tt != ttGENESIS_MINT)
|
||||
{
|
||||
JLOG(j.fatal()) << "Invariant failed: multiple accounts "
|
||||
"created in a single transaction";
|
||||
return false;
|
||||
}
|
||||
|
||||
// From this point on we know exactly one account was created.
|
||||
auto tt = tx.getTxnType();
|
||||
if ((tt == ttPAYMENT || tt == ttIMPORT) && result == tesSUCCESS)
|
||||
if ((tt == ttPAYMENT || tt == ttIMPORT || tt == ttGENESIS_MINT) && result == tesSUCCESS)
|
||||
{
|
||||
std::uint32_t const startingSeq{
|
||||
view.rules().enabled(featureDeletableAccounts) ? view.seq() : 1};
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
#include <ripple/app/tx/impl/Import.h>
|
||||
#include <ripple/app/tx/impl/Invoke.h>
|
||||
#include <ripple/app/tx/impl/URIToken.h>
|
||||
#include <ripple/app/tx/impl/GenesisMint.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
@@ -140,6 +141,7 @@ invoke_preflight(PreflightContext const& ctx)
|
||||
case ttAMENDMENT:
|
||||
case ttFEE:
|
||||
case ttUNL_MODIFY:
|
||||
case ttUNL_REPORT:
|
||||
case ttEMIT_FAILURE:
|
||||
return invoke_preflight_helper<Change>(ctx);
|
||||
case ttHOOK_SET:
|
||||
@@ -156,6 +158,8 @@ invoke_preflight(PreflightContext const& ctx)
|
||||
return invoke_preflight_helper<NFTokenAcceptOffer>(ctx);
|
||||
case ttCLAIM_REWARD:
|
||||
return invoke_preflight_helper<ClaimReward>(ctx);
|
||||
case ttGENESIS_MINT:
|
||||
return invoke_preflight_helper<GenesisMint>(ctx);
|
||||
case ttIMPORT:
|
||||
return invoke_preflight_helper<Import>(ctx);
|
||||
case ttINVOKE:
|
||||
@@ -261,6 +265,7 @@ invoke_preclaim(PreclaimContext const& ctx)
|
||||
case ttAMENDMENT:
|
||||
case ttFEE:
|
||||
case ttUNL_MODIFY:
|
||||
case ttUNL_REPORT:
|
||||
case ttEMIT_FAILURE:
|
||||
return invoke_preclaim<Change>(ctx);
|
||||
case ttNFTOKEN_MINT:
|
||||
@@ -275,6 +280,8 @@ invoke_preclaim(PreclaimContext const& ctx)
|
||||
return invoke_preclaim<NFTokenAcceptOffer>(ctx);
|
||||
case ttCLAIM_REWARD:
|
||||
return invoke_preclaim<ClaimReward>(ctx);
|
||||
case ttGENESIS_MINT:
|
||||
return invoke_preclaim<GenesisMint>(ctx);
|
||||
case ttIMPORT:
|
||||
return invoke_preclaim<Import>(ctx);
|
||||
case ttINVOKE:
|
||||
@@ -339,6 +346,7 @@ invoke_calculateBaseFee(ReadView const& view, STTx const& tx)
|
||||
case ttAMENDMENT:
|
||||
case ttFEE:
|
||||
case ttUNL_MODIFY:
|
||||
case ttUNL_REPORT:
|
||||
case ttEMIT_FAILURE:
|
||||
return Change::calculateBaseFee(view, tx);
|
||||
case ttNFTOKEN_MINT:
|
||||
@@ -353,6 +361,8 @@ invoke_calculateBaseFee(ReadView const& view, STTx const& tx)
|
||||
return NFTokenAcceptOffer::calculateBaseFee(view, tx);
|
||||
case ttCLAIM_REWARD:
|
||||
return ClaimReward::calculateBaseFee(view, tx);
|
||||
case ttGENESIS_MINT:
|
||||
return GenesisMint::calculateBaseFee(view, tx);
|
||||
case ttIMPORT:
|
||||
return Import::calculateBaseFee(view, tx);
|
||||
case ttINVOKE:
|
||||
@@ -496,7 +506,8 @@ invoke_apply(ApplyContext& ctx)
|
||||
}
|
||||
case ttAMENDMENT:
|
||||
case ttFEE:
|
||||
case ttUNL_MODIFY:
|
||||
case ttUNL_MODIFY:
|
||||
case ttUNL_REPORT:
|
||||
case ttEMIT_FAILURE: {
|
||||
Change p(ctx);
|
||||
return p();
|
||||
@@ -525,6 +536,10 @@ invoke_apply(ApplyContext& ctx)
|
||||
ClaimReward p(ctx);
|
||||
return p();
|
||||
}
|
||||
case ttGENESIS_MINT: {
|
||||
GenesisMint p(ctx);
|
||||
return p();
|
||||
}
|
||||
case ttIMPORT: {
|
||||
Import p(ctx);
|
||||
return p();
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <ripple/basics/base_uint.h>
|
||||
#include <ripple/beast/net/IPEndpoint.h>
|
||||
#include <ripple/beast/utility/Journal.h>
|
||||
#include <ripple/protocol/PublicKey.h>
|
||||
#include <ripple/protocol/SystemParameters.h> // VFALCO Breaks levelization
|
||||
#include <boost/beast/core/string.hpp>
|
||||
#include <boost/filesystem.hpp> // VFALCO FIX: This include should not be here
|
||||
@@ -150,7 +151,7 @@ public:
|
||||
std::vector<std::string> IPS_FIXED; // Fixed Peer IPs from rippled.cfg.
|
||||
std::vector<std::string> SNTP_SERVERS; // SNTP servers from rippled.cfg.
|
||||
|
||||
std::vector<std::string> IMPORT_VL_KEYS;
|
||||
std::map<std::string, PublicKey> IMPORT_VL_KEYS; // hex string -> class PublicKey (for caching purposes)
|
||||
|
||||
enum StartUpType { FRESH, NORMAL, LOAD, LOAD_FILE, REPLAY, NETWORK };
|
||||
StartUpType START_UP = NORMAL;
|
||||
@@ -176,10 +177,6 @@ public:
|
||||
int RELAY_UNTRUSTED_VALIDATIONS = 1;
|
||||
int RELAY_UNTRUSTED_PROPOSALS = 0;
|
||||
|
||||
// The number of ledgers worth of valdiations to keep for the purpose
|
||||
// of servicing xpop requests these are lost on restart
|
||||
std::optional<uint32_t> XPOP_HISTORY;
|
||||
|
||||
// True to ask peers not to relay current IP.
|
||||
bool PEER_PRIVATE = false;
|
||||
// peers_max is a legacy configuration, which is going to be replaced
|
||||
|
||||
@@ -99,7 +99,6 @@ struct ConfigSection
|
||||
#define SECTION_LEDGER_REPLAY "ledger_replay"
|
||||
#define SECTION_BETA_RPC_API "beta_rpc_api"
|
||||
#define SECTION_SWEEP_INTERVAL "sweep_interval"
|
||||
#define SECTION_XPOP_HISTORY "xpop_history"
|
||||
#define SECTION_NETWORK_ID "network_id"
|
||||
#define SECTION_IMPORT_VL_KEYS "import_vl_keys"
|
||||
|
||||
|
||||
@@ -502,9 +502,6 @@ Config::loadFromString(std::string const& fileContents)
|
||||
|
||||
std::string strTemp;
|
||||
|
||||
if (getSingleSection(secConfig, SECTION_XPOP_HISTORY, strTemp, j_))
|
||||
XPOP_HISTORY = beast::lexicalCastThrow<uint32_t>(strTemp);
|
||||
|
||||
if (getSingleSection(secConfig, SECTION_NETWORK_ID, strTemp, j_))
|
||||
{
|
||||
if (strTemp == "main")
|
||||
@@ -907,15 +904,22 @@ Config::loadFromString(std::string const& fileContents)
|
||||
auto iniFile = parseIniFile(data, true);
|
||||
|
||||
if (auto importKeys =
|
||||
getIniFileSection(iniFile, SECTION_IMPORT_VL_KEYS))
|
||||
IMPORT_VL_KEYS = *importKeys;
|
||||
else
|
||||
Throw<std::runtime_error>(
|
||||
"The file specified in [" SECTION_VALIDATORS_FILE
|
||||
"] "
|
||||
"does not contain a [" SECTION_IMPORT_VL_KEYS
|
||||
"] section: " +
|
||||
validatorsFile.string());
|
||||
getIniFileSection(iniFile, SECTION_IMPORT_VL_KEYS); importKeys)
|
||||
{
|
||||
for (std::string const& strPk : *importKeys)
|
||||
{
|
||||
auto pkHex = strUnHex(strPk);
|
||||
if (!pkHex)
|
||||
Throw<std::runtime_error>(
|
||||
"Import VL Key '" + strPk + "' was not valid hex.");
|
||||
|
||||
auto const pkType = publicKeyType(makeSlice(*pkHex));
|
||||
if (!pkType)
|
||||
Throw<std::runtime_error>(
|
||||
"Import VL Key '" + strPk + "' was not a valid key type.");
|
||||
IMPORT_VL_KEYS.emplace(strPk, makeSlice(*pkHex));
|
||||
}
|
||||
}
|
||||
|
||||
if (RUN_STANDALONE)
|
||||
break;
|
||||
|
||||
@@ -74,7 +74,7 @@ namespace detail {
|
||||
// Feature.cpp. Because it's only used to reserve storage, and determine how
|
||||
// large to make the FeatureBitset, it MAY be larger. It MUST NOT be less than
|
||||
// the actual number of amendments. A LogicError on startup will verify this.
|
||||
static constexpr std::size_t numFeatures = 63;
|
||||
static constexpr std::size_t numFeatures = 64;
|
||||
|
||||
/** Amendments that this server supports and the default voting behavior.
|
||||
Whether they are enabled depends on the Rules defined in the validated
|
||||
@@ -351,6 +351,7 @@ extern uint256 const fixUniversalNumber;
|
||||
extern uint256 const fixNonFungibleTokensV1_2;
|
||||
extern uint256 const fixNFTokenRemint;
|
||||
extern uint256 const featureImport;
|
||||
extern uint256 const featureXahauGenesis;
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
|
||||
@@ -111,6 +111,9 @@ fees() noexcept;
|
||||
Keylet const&
|
||||
negativeUNL() noexcept;
|
||||
|
||||
Keylet const&
|
||||
UNLReport() noexcept;
|
||||
|
||||
/** The beginning of an order book */
|
||||
struct book_t
|
||||
{
|
||||
|
||||
@@ -172,6 +172,13 @@ enum LedgerEntryType : std::uint16_t
|
||||
*/
|
||||
ltURI_TOKEN = 0x0055,
|
||||
|
||||
/** A ledger object that reports on the active dUNL validators
|
||||
* that were validating for more than 240 of the last 256 ledgers
|
||||
*
|
||||
* \sa keylet::UNLReport
|
||||
*/
|
||||
ltUNL_REPORT = 0x0052,
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
/** A special type, matching any ledger entry type.
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace ripple {
|
||||
@ingroup protocol
|
||||
*/
|
||||
/** Smallest legal byte size of a transaction. */
|
||||
std::size_t constexpr txMinSizeBytes = 32;
|
||||
std::size_t constexpr txMinSizeBytes = 10;
|
||||
|
||||
/** Largest legal byte size of a transaction. */
|
||||
std::size_t constexpr txMaxSizeBytes = megabytes(1);
|
||||
|
||||
@@ -477,6 +477,8 @@ extern SF_UINT256 const sfHookSetTxnID;
|
||||
extern SF_UINT256 const sfOfferID;
|
||||
extern SF_UINT256 const sfEscrowID;
|
||||
extern SF_UINT256 const sfURITokenID;
|
||||
extern SF_UINT256 const sfGovernanceFlags;
|
||||
extern SF_UINT256 const sfGovernanceMarks;
|
||||
|
||||
// currency amount (common)
|
||||
extern SF_AMOUNT const sfAmount;
|
||||
@@ -581,6 +583,8 @@ extern SField const sfHookExecution;
|
||||
extern SField const sfHookDefinition;
|
||||
extern SField const sfHookParameter;
|
||||
extern SField const sfHookGrant;
|
||||
extern SField const sfActiveValidator;
|
||||
extern SField const sfImportVLKey;
|
||||
|
||||
// array of objects (common)
|
||||
// ARRAY/1 is reserved for end of array
|
||||
@@ -594,6 +598,7 @@ extern SField const sfAffectedNodes;
|
||||
extern SField const sfMemos;
|
||||
extern SField const sfNFTokens;
|
||||
extern SField const sfHooks;
|
||||
extern SField const sfGenesisMint;
|
||||
|
||||
// array of objects (uncommon)
|
||||
extern SField const sfMajorities;
|
||||
@@ -603,6 +608,9 @@ extern SField const sfHookExecution;
|
||||
extern SField const sfHookParameters;
|
||||
extern SField const sfHooks;
|
||||
extern SField const sfHookGrants;
|
||||
extern SField const sfGenesisMints;
|
||||
extern SField const sfActiveValidators;
|
||||
extern SField const sfImportVLKeys;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -85,7 +85,8 @@ constexpr std::ratio<204, 256> preFixAmendmentMajorityCalcThreshold;
|
||||
constexpr std::ratio<80, 100> postFixAmendmentMajorityCalcThreshold;
|
||||
|
||||
/** The minimum amount of time an amendment must hold a majority */
|
||||
constexpr std::chrono::seconds const defaultAmendmentMajorityTime = days{5};
|
||||
constexpr std::chrono::seconds const defaultAmendmentMajorityTime = std::chrono::seconds{16};
|
||||
//days{5};
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
|
||||
@@ -146,7 +146,12 @@ enum TxType : std::uint16_t
|
||||
ttURITOKEN_CREATE_SELL_OFFER = 48,
|
||||
ttURITOKEN_CANCEL_SELL_OFFER = 49,
|
||||
|
||||
/** This transaciton accepts a proof of burn from an external network as a basis
|
||||
/** This transaction can only be used by the genesis account, which is controlled exclusively by
|
||||
* rewards/governance hooks, to print new XRP to be delivered directly to an array of destinations,
|
||||
* according to reward schedule */
|
||||
ttGENESIS_MINT = 96,
|
||||
|
||||
/** This transaction accepts a proof of burn from an external network as a basis
|
||||
* for minting according to featureImport */
|
||||
ttIMPORT = 97,
|
||||
|
||||
@@ -175,6 +180,7 @@ enum TxType : std::uint16_t
|
||||
*/
|
||||
ttUNL_MODIFY = 102,
|
||||
ttEMIT_FAILURE = 103,
|
||||
ttUNL_REPORT = 104,
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -454,9 +454,10 @@ REGISTER_FIX (fixNonFungibleTokensV1_2, Supported::yes, VoteBehavior::De
|
||||
REGISTER_FIX (fixNFTokenRemint, Supported::yes, VoteBehavior::DefaultNo);
|
||||
REGISTER_FEATURE(Hooks, Supported::yes, VoteBehavior::DefaultYes);
|
||||
REGISTER_FEATURE(BalanceRewards, Supported::yes, VoteBehavior::DefaultYes);
|
||||
REGISTER_FEATURE(PaychanAndEscrowForTokens, Supported::yes, VoteBehavior::DefaultNo);
|
||||
REGISTER_FEATURE(URIToken, Supported::yes, VoteBehavior::DefaultNo);
|
||||
REGISTER_FEATURE(Import, Supported::yes, VoteBehavior::DefaultNo);
|
||||
REGISTER_FEATURE(PaychanAndEscrowForTokens, Supported::yes, VoteBehavior::DefaultYes);
|
||||
REGISTER_FEATURE(URIToken, Supported::yes, VoteBehavior::DefaultYes);
|
||||
REGISTER_FEATURE(Import, Supported::yes, VoteBehavior::DefaultYes);
|
||||
REGISTER_FEATURE(XahauGenesis, Supported::yes, VoteBehavior::DefaultYes);
|
||||
|
||||
// The following amendments are obsolete, but must remain supported
|
||||
// because they could potentially get enabled.
|
||||
|
||||
@@ -71,6 +71,7 @@ enum class LedgerNameSpace : std::uint16_t {
|
||||
NFTOKEN_SELL_OFFERS = 'i',
|
||||
URI_TOKEN = 'U',
|
||||
IMPORT_VLSEQ = 'I',
|
||||
UNL_REPORT = 'R',
|
||||
|
||||
// No longer used or supported. Left here to reserve the space
|
||||
// to avoid accidental reuse.
|
||||
@@ -236,6 +237,14 @@ negativeUNL() noexcept
|
||||
return ret;
|
||||
}
|
||||
|
||||
Keylet const&
|
||||
UNLReport() noexcept
|
||||
{
|
||||
static Keylet const ret{
|
||||
ltUNL_REPORT, indexHash(LedgerNameSpace::UNL_REPORT)};
|
||||
return ret;
|
||||
}
|
||||
|
||||
Keylet
|
||||
book_t::operator()(Book const& b) const
|
||||
{
|
||||
|
||||
@@ -125,6 +125,29 @@ InnerObjectFormats::InnerObjectFormats()
|
||||
{sfNFTokenID, soeREQUIRED},
|
||||
{sfURI, soeOPTIONAL},
|
||||
});
|
||||
|
||||
add(sfGenesisMint.jsonName.c_str(),
|
||||
sfGenesisMint.getCode(),
|
||||
{
|
||||
{sfDestination, soeREQUIRED},
|
||||
{sfAmount, soeOPTIONAL},
|
||||
{sfGovernanceFlags, soeOPTIONAL},
|
||||
{sfGovernanceMarks, soeOPTIONAL},
|
||||
});
|
||||
|
||||
add(sfActiveValidator.jsonName.c_str(),
|
||||
sfActiveValidator.getCode(),
|
||||
{
|
||||
{sfPublicKey, soeREQUIRED},
|
||||
{sfAccount, soeOPTIONAL},
|
||||
});
|
||||
|
||||
add(sfImportVLKey.jsonName.c_str(),
|
||||
sfImportVLKey.getCode(),
|
||||
{
|
||||
{sfPublicKey, soeREQUIRED},
|
||||
{sfAccount, soeOPTIONAL},
|
||||
});
|
||||
}
|
||||
|
||||
InnerObjectFormats const&
|
||||
|
||||
@@ -63,6 +63,8 @@ LedgerFormats::LedgerFormats()
|
||||
{sfRewardAccumulator, soeOPTIONAL},
|
||||
{sfFirstNFTokenSequence, soeOPTIONAL},
|
||||
{sfImportSequence, soeOPTIONAL},
|
||||
{sfGovernanceFlags, soeOPTIONAL},
|
||||
{sfGovernanceMarks, soeOPTIONAL},
|
||||
},
|
||||
commonFields);
|
||||
|
||||
@@ -298,6 +300,16 @@ LedgerFormats::LedgerFormats()
|
||||
},
|
||||
commonFields);
|
||||
|
||||
add(jss::UNLReport,
|
||||
ltUNL_REPORT,
|
||||
{
|
||||
{sfImportVLKeys, soeOPTIONAL},
|
||||
{sfActiveValidators, soeOPTIONAL},
|
||||
{sfPreviousTxnID, soeREQUIRED},
|
||||
{sfPreviousTxnLgrSeq, soeREQUIRED},
|
||||
},
|
||||
commonFields);
|
||||
|
||||
add(jss::EmittedTxn,
|
||||
ltEMITTED_TXN,
|
||||
{
|
||||
|
||||
@@ -230,6 +230,8 @@ CONSTRUCT_TYPED_SFIELD(sfHookSetTxnID, "HookSetTxnID", UINT256,
|
||||
CONSTRUCT_TYPED_SFIELD(sfOfferID, "OfferID", UINT256, 34);
|
||||
CONSTRUCT_TYPED_SFIELD(sfEscrowID, "EscrowID", UINT256, 35);
|
||||
CONSTRUCT_TYPED_SFIELD(sfURITokenID, "URITokenID", UINT256, 36);
|
||||
CONSTRUCT_TYPED_SFIELD(sfGovernanceFlags, "GovernanceFlags", UINT256, 99);
|
||||
CONSTRUCT_TYPED_SFIELD(sfGovernanceMarks, "GovernanceMarks", UINT256, 98);
|
||||
|
||||
// currency amount (common)
|
||||
CONSTRUCT_TYPED_SFIELD(sfAmount, "Amount", AMOUNT, 1);
|
||||
@@ -336,6 +338,9 @@ CONSTRUCT_UNTYPED_SFIELD(sfHookExecution, "HookExecution", OBJECT,
|
||||
CONSTRUCT_UNTYPED_SFIELD(sfHookDefinition, "HookDefinition", OBJECT, 22);
|
||||
CONSTRUCT_UNTYPED_SFIELD(sfHookParameter, "HookParameter", OBJECT, 23);
|
||||
CONSTRUCT_UNTYPED_SFIELD(sfHookGrant, "HookGrant", OBJECT, 24);
|
||||
CONSTRUCT_UNTYPED_SFIELD(sfGenesisMint, "GenesisMint", OBJECT, 96);
|
||||
CONSTRUCT_UNTYPED_SFIELD(sfActiveValidator, "ActiveValidator", OBJECT, 95);
|
||||
CONSTRUCT_UNTYPED_SFIELD(sfImportVLKey, "ImportVLKey", OBJECT, 94);
|
||||
|
||||
// array of objects
|
||||
// ARRAY/1 is reserved for end of array
|
||||
@@ -356,6 +361,9 @@ CONSTRUCT_UNTYPED_SFIELD(sfDisabledValidators, "DisabledValidators", ARRAY,
|
||||
CONSTRUCT_UNTYPED_SFIELD(sfHookExecutions, "HookExecutions", ARRAY, 18);
|
||||
CONSTRUCT_UNTYPED_SFIELD(sfHookParameters, "HookParameters", ARRAY, 19);
|
||||
CONSTRUCT_UNTYPED_SFIELD(sfHookGrants, "HookGrants", ARRAY, 20);
|
||||
CONSTRUCT_UNTYPED_SFIELD(sfGenesisMints, "GenesisMints", ARRAY, 96);
|
||||
CONSTRUCT_UNTYPED_SFIELD(sfActiveValidators, "ActiveValidators", ARRAY, 95);
|
||||
CONSTRUCT_UNTYPED_SFIELD(sfImportVLKeys, "ImportVLKeys", ARRAY, 94);
|
||||
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -564,7 +564,7 @@ isPseudoTx(STObject const& tx)
|
||||
return false;
|
||||
|
||||
auto tt = safe_cast<TxType>(*t);
|
||||
return tt == ttAMENDMENT || tt == ttFEE || tt == ttUNL_MODIFY || tt == ttEMIT_FAILURE;
|
||||
return tt == ttAMENDMENT || tt == ttFEE || tt == ttUNL_MODIFY || tt == ttEMIT_FAILURE || tt == ttUNL_REPORT;
|
||||
}
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
@@ -192,6 +192,16 @@ TxFormats::TxFormats()
|
||||
},
|
||||
commonFields);
|
||||
|
||||
add(jss::UNLReport,
|
||||
ttUNL_REPORT,
|
||||
{
|
||||
{sfLedgerSequence, soeREQUIRED},
|
||||
{sfActiveValidator, soeOPTIONAL},
|
||||
{sfImportVLKey, soeOPTIONAL},
|
||||
|
||||
},
|
||||
commonFields);
|
||||
|
||||
add(jss::TicketCreate,
|
||||
ttTICKET_CREATE,
|
||||
{
|
||||
@@ -358,6 +368,13 @@ TxFormats::TxFormats()
|
||||
},
|
||||
commonFields);
|
||||
|
||||
add(jss::GenesisMint,
|
||||
ttGENESIS_MINT,
|
||||
{
|
||||
{sfGenesisMints, soeREQUIRED},
|
||||
},
|
||||
commonFields);
|
||||
|
||||
add(jss::Import,
|
||||
ttIMPORT,
|
||||
{
|
||||
|
||||
@@ -72,6 +72,7 @@ JSS(FeeSettings); // ledger type.
|
||||
JSS(FIELDS); // out: RPC server_definitions
|
||||
JSS(Flags); // in/out: TransactionSign; field.
|
||||
JSS(incomplete_shards); // out: OverlayImpl, PeerImp
|
||||
JSS(GenesisMint); // tt
|
||||
JSS(HookApiVersion); // field
|
||||
JSS(HookHash); // field
|
||||
JSS(HookNamespace); // field
|
||||
@@ -116,6 +117,7 @@ JSS(RippleState); // ledger type.
|
||||
JSS(SLE_hit_rate); // out: GetCounts.
|
||||
JSS(SetFee); // transaction type.
|
||||
JSS(UNLModify); // transaction type.
|
||||
JSS(UNLReport); // transaction type.
|
||||
JSS(SettleDelay); // in: TransactionSign
|
||||
JSS(SendMax); // in: TransactionSign
|
||||
JSS(Sequence); // in/out: TransactionSign; field.
|
||||
|
||||
@@ -752,7 +752,7 @@ voteAndCheck(
|
||||
std::size_t expect,
|
||||
PreVote const& pre = defaultPreVote)
|
||||
{
|
||||
NegativeUNLVote vote(myId, history.env.journal);
|
||||
NegativeUNLVote vote(myId, history.env.journal, history.env.app());
|
||||
pre(vote);
|
||||
auto txSet = std::make_shared<SHAMap>(
|
||||
SHAMapType::TRANSACTION, history.env.app().getNodeFamily());
|
||||
@@ -773,7 +773,7 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::suite
|
||||
jtx::Env env(*this);
|
||||
|
||||
NodeID myId(0xA0);
|
||||
NegativeUNLVote vote(myId, env.journal);
|
||||
NegativeUNLVote vote(myId, env.journal, env.app());
|
||||
|
||||
// one add, one remove
|
||||
auto txSet = std::make_shared<SHAMap>(
|
||||
@@ -797,7 +797,7 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::suite
|
||||
jtx::Env env(*this);
|
||||
|
||||
NodeID myId(0xA0);
|
||||
NegativeUNLVote vote(myId, env.journal);
|
||||
NegativeUNLVote vote(myId, env.journal, env.app());
|
||||
|
||||
uint256 pad_0(0);
|
||||
uint256 pad_f = ~pad_0;
|
||||
@@ -834,7 +834,7 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::suite
|
||||
if (history.goodHistory)
|
||||
{
|
||||
NegativeUNLVote vote(
|
||||
history.UNLNodeIDs[3], history.env.journal);
|
||||
history.UNLNodeIDs[3], history.env.journal, history.env.app());
|
||||
BEAST_EXPECT(!vote.buildScoreTable(
|
||||
history.lastLedger(),
|
||||
history.UNLNodeIDSet,
|
||||
@@ -849,7 +849,7 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::suite
|
||||
if (history.goodHistory)
|
||||
{
|
||||
NegativeUNLVote vote(
|
||||
history.UNLNodeIDs[3], history.env.journal);
|
||||
history.UNLNodeIDs[3], history.env.journal, history.env.app());
|
||||
BEAST_EXPECT(!vote.buildScoreTable(
|
||||
history.lastLedger(),
|
||||
history.UNLNodeIDSet,
|
||||
@@ -872,7 +872,7 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::suite
|
||||
history.UNLNodeIDs[idx] == myId &&
|
||||
l->seq() % 2 == 0);
|
||||
});
|
||||
NegativeUNLVote vote(myId, history.env.journal);
|
||||
NegativeUNLVote vote(myId, history.env.journal, history.env.app());
|
||||
BEAST_EXPECT(!vote.buildScoreTable(
|
||||
history.lastLedger(),
|
||||
history.UNLNodeIDSet,
|
||||
@@ -915,7 +915,7 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::suite
|
||||
history.validations.add(badNode, v2);
|
||||
}
|
||||
|
||||
NegativeUNLVote vote(myId, history.env.journal);
|
||||
NegativeUNLVote vote(myId, history.env.journal, history.env.app());
|
||||
|
||||
// local node still on wrong chain, can build a scoreTable,
|
||||
// but all other nodes' scores are zero
|
||||
@@ -954,7 +954,7 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::suite
|
||||
[&](std::shared_ptr<Ledger const> const& l,
|
||||
std::size_t idx) -> bool { return true; });
|
||||
NegativeUNLVote vote(
|
||||
history.UNLNodeIDs[3], history.env.journal);
|
||||
history.UNLNodeIDs[3], history.env.journal, history.env.app());
|
||||
auto scoreTable = vote.buildScoreTable(
|
||||
history.lastLedger(),
|
||||
history.UNLNodeIDSet,
|
||||
@@ -1031,7 +1031,7 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::suite
|
||||
for (auto const& n : history.UNLNodeIDs)
|
||||
goodScoreTable[n] = NegativeUNLVote::negativeUNLHighWaterMark + 1;
|
||||
|
||||
NegativeUNLVote vote(history.UNLNodeIDs[0], history.env.journal);
|
||||
NegativeUNLVote vote(history.UNLNodeIDs[0], history.env.journal, history.env.app());
|
||||
|
||||
{
|
||||
// all good scores
|
||||
@@ -1165,7 +1165,7 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::suite
|
||||
jtx::Env env(*this);
|
||||
|
||||
NodeID myId(0xA0);
|
||||
NegativeUNLVote vote(myId, env.journal);
|
||||
NegativeUNLVote vote(myId, env.journal, env.app());
|
||||
|
||||
std::array<std::uint32_t, 3> unlSizes = {34, 35, 80};
|
||||
std::array<std::uint32_t, 3> nUnlPercent = {0, 50, 100};
|
||||
@@ -1338,7 +1338,7 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::suite
|
||||
jtx::Env env(*this);
|
||||
|
||||
NodeID myId(0xA0);
|
||||
NegativeUNLVote vote(myId, env.journal);
|
||||
NegativeUNLVote vote(myId, env.journal, env.app());
|
||||
|
||||
// test cases:
|
||||
// newValidators_ of the NegativeUNLVote empty, add one
|
||||
@@ -1451,7 +1451,7 @@ class NegativeUNLVoteScoreTable_test : public beast::unit_test::suite
|
||||
return add_50 || add_100 || add_me;
|
||||
});
|
||||
|
||||
NegativeUNLVote vote(myId, history.env.journal);
|
||||
NegativeUNLVote vote(myId, history.env.journal, history.env.app());
|
||||
auto scoreTable = vote.buildScoreTable(
|
||||
history.lastLedger(),
|
||||
history.UNLNodeIDSet,
|
||||
|
||||
Reference in New Issue
Block a user