update governance (to remove L2 stuff) and reward hook (to add UNLReport) not tested yet in consensus

This commit is contained in:
Richard Holland
2023-06-26 10:31:11 +00:00
parent 29e733cab3
commit 48dc6b29bf
3 changed files with 84 additions and 190 deletions

View File

@@ -16,11 +16,9 @@
* 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 this same governance hook installed on it and is blackholed.
* 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.
* The same voting rules apply but the voting threshold is 60% for everything.
* At L2, a successful vote is actioned by having the L1 seat that L2 table occupies cast an L1 vote.
*
* Hook Parameters:
* Parameter Name: {'I', 'M', 'C'}
@@ -63,7 +61,7 @@
* State Key: {'V', 'H|R|S' <topic type>, '\0 + topic id', 0..0, <member accid>}
* State Data: A vote by a member for a topic and topic data
*
* State Key: {'C', 'H|h|R|S' <topic type>, '\0 + topic id', 0*, <front truncated topic data>}
* State Key: {'C', 'H|R|S' <topic type>, '\0 + topic id', 0*, <front truncated topic data>}
* State Data: The number of members who have voted for that topic data and topic combination <1 byte>
*
* Hook Invocation:
@@ -76,7 +74,7 @@
*
* Parameter Name: {'T'}
* Parameter Value: The topic to vote on <2 bytes>
* { 'S|s|H|h' (seat L1, seat L2, hook L1, hook L2), '\0 + topic id' }, or
* { 'S|H' (seat L1, hook L1), '\0 + topic id' }, or
* { 'R' (reward), 'R|D' (rate, delay) }
*
* Parameter Name: {'V'}
@@ -126,11 +124,14 @@ int64_t hook(uint32_t r)
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);
// initial execution, setup hook
if (member_count == DOESNT_EXIST)
{
@@ -146,8 +147,6 @@ int64_t hook(uint32_t r)
if (imc > SEAT_COUNT)
NOPE("Governance: Initial Member Count must be <= Seat Count (20).");
if (is_L1)
{
if (hook_param(SVAR(irr), "IRR", 3) < 0)
NOPE("Governance: Initial Reward Rate Parameter missing (IRR).");
@@ -162,13 +161,10 @@ int64_t hook(uint32_t r)
// set reward delay
ASSERT(state_set(SVAR(ird), "RD", 2));
}
// set member count
ASSERT(state_set(SBUF(imc), "MC", 2));
member_count = imc;
for (uint8_t i = 0; GUARD(SEAT_COUNT), i < member_count; ++i)
@@ -209,46 +205,26 @@ int64_t hook(uint32_t r)
if (result != 2 || (
t != 'S' && // topic type: seat (L1)
t != 's' && // topic type: seat (L2)
t != 'H' && // topic type: hook (L1)
t != 'h' && // topic type: hook (L2)
t != 'R')) // topic type: reward
NOPE("Governance: Valid TOPIC must be specified as otxn parameter.");
if ((t == 'S' || t == 's') && n > (SEAT_COUNT - 1))
if (t == 'S' && n > (SEAT_COUNT - 1))
NOPE("Governance: Valid seat topics are 0 through 19.");
if ((t == 'H' || t == 'h') && n > HOOK_MAX)
if (t == 'H' && n > HOOK_MAX)
NOPE("Governance: Valid hook topics are 0 through 9.");
if (t == 'R' && n != 'R' && n != 'D')
NOPE("Governance: Valid reward topics are R (rate) and D (delay).");
if (is_L1)
{
if (t == 'h' || t == 's')
NOPE("Governance: Cannot vote on topic `h` or `s` at L1 table (did you mean `H`/`S`?).");
// pass
}
else
if (t != 'h' && t != 's')
{
// check if this is a real L2 table (is it seated at an L1 seat?)
// only setup and voting on the local hook hash is allowed until it gets a seat
uint8_t dummy;
if (state_foreign(SVAR(dummy), hook_accid + 12, 20, SBUF(zero32), SBUF(genesis)) != 1)
NOPE("Governance: This L2 table is not seated at the L1 table, so only `h`/`s` topic voting is allowed.");
}
// RH TODO: validate RR/RD xfl > 0
uint8_t topic_data[32];
uint8_t topic_size =
t == 'H' ? 32 : // hook topics are a 32 byte hook hash
t == 'h' ? 32 : // hook topics are a 32 byte hook hash (L2)
t == 'S' ? 20 : // account topics are a 20 byte account ID
t == 's' ? 20 :
8; // reward topics are an 8 byte le xfl
uint8_t padding = 32 - topic_size;
@@ -265,7 +241,8 @@ int64_t hook(uint32_t r)
// 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
@@ -329,79 +306,15 @@ int64_t hook(uint32_t r)
int64_t q80 = member_count * 0.8;
int64_t q66 = member_count * 0.66;
if (is_L1)
{
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.");
}
else
{
// for the current topic have we already cast a vote to the L1 table previously?
// reuse hook_accid for this
//
if (t == 'h')
{
// local voting topics
if (votes < member_count)
DONE("Governance: Vote recorded at L2. Not yet enough votes to action (100% needed).");
t = 'H';
// otherwise fall through
}
else if (t == 's')
{
if (votes < q80)
DONE("Governance: Vote recorded at L2. Not yet enough votes to action (80% needed).");
t = 'S';
// otherwise fall through
}
else
{
// the vote is for an L1 topic. we need to check what vote might have already been registered
// at the L1 table
hook_accid[0] = 'V';
hook_accid[1] = t;
hook_accid[2] = n;
uint8_t previous_topic_data_l1[32];
int64_t previous_topic_size_l1 =
state_foreign(SBUF(previous_topic_data_l1), SBUF(hook_accid), SBUF(zero32), SBUF(genesis));
if (previous_topic_size_l1 == topic_size && BUFFER_EQUAL_32(previous_topic_data_l1, topic_data))
DONE("Governance: Your whole-table's L1 vote is already cast this way for this topic.");
int64_t previous_topic_data_l1_zero =
BUFFER_EQUAL_32(previous_topic_data_l1, zero32);
// we need to check if this vote represents a lost majority status on the existing L1 topic data
previous_topic_data_l1[0] = 'C';
previous_topic_data_l1[1] = t;
previous_topic_data_l1[2] = n;
uint8_t existing_votes;
state(SVAR(existing_votes), SBUF(previous_topic_data_l1));
if (existing_votes < q66 && !previous_topic_data_l1_zero || votes >= q66)
{
// vacate the current vote at L1
uint8_t* td = votes < q66 ? zero32 : topic_data;
PREPARE_GENESIS_INVOKE(t,n, td); // rh upto
DONE(
votes < q66
? "Governance: Previous vote vacated at L1, vote recorded at L2. Not enough votes to action."
: "Governance: Actioning vote at L1.");
}
else
DONE("Governance: Vote recorded at L2. Not yet enough votes to action.");
}
// action vote
if (DEBUG)
TRACESTR("Actioning votes");

View File

@@ -1,11 +1,12 @@
#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 L2SEATS 400U
#define MAXUNL 128U
#define SVAR(x) &(x), sizeof(x)
#define ASSERT(x)\
if (!(x))\
@@ -13,7 +14,7 @@
#define DEBUG 1
uint8_t txn_mint[13850] =
uint8_t txn_mint[928] =
{
/* size,upto */
/* 3, 0 */ 0x12U, 0x00U, 0x60U, /* tt = GenesisMint */
@@ -45,7 +46,7 @@ uint8_t txn_mint[13850] =
// 210 bytes + 34 bytes per entry * number of entries + any alignment padding desired
};
uint8_t template1[40] = {
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
@@ -55,32 +56,10 @@ uint8_t template1[40] = {
/* 33, 1 */ 0xE1U // obj end
};
uint8_t template2[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 TEMPLATE1_DROPS(drops_tmp)\
#define TEMPLATE_DROPS(drops_tmp)\
{\
uint8_t* b = template1 + 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 TEMPLATE2_DROPS(drops_tmp)\
{\
uint8_t* b = template2 + 3U;\
uint8_t* b = template + 3U;\
*b++ = 0b01000000 + (( drops_tmp >> 56 ) & 0b00111111 );\
*b++ = (drops_tmp >> 48) & 0xFFU;\
*b++ = (drops_tmp >> 40) & 0xFFU;\
@@ -108,6 +87,13 @@ uint8_t template2[40] = {
uint8_t member_count_key[2] = {'M', 'C'};
uint8_t unlreport_keylet[32] =
{
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)
@@ -237,8 +223,6 @@ int64_t hook(uint32_t r)
uint64_t l1_drops = reward_drops / L1SEATS;
uint64_t l2_drops = reward_drops / L2SEATS;
otxn_slot(1);
slot_subfield(1, sfFee, 2);
@@ -249,76 +233,72 @@ int64_t hook(uint32_t r)
reward_drops += float_int(xfl_fee, 6, 1);
TEMPLATE1_DROPS(reward_drops);
TEMPLATE_DROPS(reward_drops);
uint8_t* upto = txn_mint + 209U;
uint8_t* end = upto + (34U * (L2SEATS + 1));
uint8_t* end = upto + (34U * (L1SEATS + 1));
// first account is always the rewardee
{
uint64_t* d = (uint64_t*)upto;
uint64_t* s = (uint64_t*)template1;
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
TEMPLATE1_DROPS(l1_drops);
TEMPLATE2_DROPS(l2_drops);
TEMPLATE_DROPS(l1_drops);
uint8_t table[20];
for (uint8_t l1_seat = 0, l2_seat = 0; GUARD(L2SEATS), upto < end && l1_seat < L1SEATS;)
// 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[(34 * MAXUNL) + 3];
if (slot_set(SBUF(unlreport_keylet), 1) == 1 &&
slot_subfield(1, sfActiveValidators, 1) == 1 &&
slot(SBUF(av_array), 1) > 0)
{
if (l2_seat == 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 + 15 /* offset to the first account */;
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)
{
if (state(SVAR(seat), av_upto, 20) != 1 || seat > L1SEATS)
continue;
can_reward[seat] = 1;
av_upto += 34U;
}
// iterate the seats at the table and add reward entries for the active validators
for (uint8_t l1_seat = 0; GUARD(L1SEATS), upto < end && l1_seat < 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*)template1;
uint64_t* s = (uint64_t*)template;
*d++ = *s++;
*d++ = *s++;
*(d+2) = *(s+2);
// we're at an l1 seat in the iteration, check if it's filled and if so if it's filled with an L2 table
if (state(upto + 13, 20, &l1_seat, 1) == 20)
{
// l1 seat is filled but by what?
state(SBUF(table), &l1_seat, 1);
l1_seat++;
uint8_t dummy[1];
if(state_foreign(SBUF(dummy), SBUF(member_count_key), SBUF(dummy), SBUF(table)) == DOESNT_EXIST)
{
// filled by a normal l1 member
upto += 34;
continue;
}
// filled by an l2 table... fall through
}
}
// we're iterating l2 seats
// copy template2 into next GenesisMints array position
uint64_t* d = (uint64_t*)upto;
uint64_t* s = (uint64_t*)template2;
*d++ = *s++;
*d++ = *s++;
*(d+2) = *(s+2);
uint8_t ns[32];
if (state_foreign(upto + 13, 20, l2_seat, 1, SBUF(ns), SBUF(table)) == 20)
{
// filled l2 seat
upto += 34;
}
l2_seat = (l2_seat + 1) % L1SEATS;
}
*upto++ = 0xF1U;

View File

@@ -212,3 +212,4 @@
#define sfHookExecutions ((15U << 16U) + 18U)
#define sfHookParameters ((15U << 16U) + 19U)
#define sfHookGrants ((15U << 16U) + 20U)
#define sfActiveValidators ((15U << 16U) + 95U)