Skip to content

Commit

Permalink
[upgrade] migrate make whole (0LNetworkCommunity#103)
Browse files Browse the repository at this point in the history
  • Loading branch information
0o-de-lally authored Nov 18, 2023
1 parent edbf5cc commit 03d9f10
Show file tree
Hide file tree
Showing 20 changed files with 495 additions and 81 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -220,14 +220,14 @@ module diem_framework::reconfiguration {
// This is used together with stake::end_epoch() for testing with last_reconfiguration_time
// It must be called each time an epoch changes
#[test_only]
public fun test_helper_increment_epoch_dont_reconfigure() acquires Configuration {
public fun test_helper_increment_epoch_dont_reconfigure(count: u64) acquires Configuration {
let config_ref = borrow_global_mut<Configuration>(@diem_framework);
let current_time = timestamp::now_microseconds();
// if (current_time == config_ref.last_reconfiguration_time) {
// return
// };
config_ref.last_reconfiguration_time = current_time;
config_ref.epoch = config_ref.epoch + 1;
config_ref.epoch = config_ref.epoch + count;

epoch_helper::set_epoch(config_ref.epoch);

Expand Down
26 changes: 24 additions & 2 deletions framework/libra-framework/sources/ol_sources/burn.move
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ module ol_framework::burn {
if (to_withdraw > 0 && to_withdraw <= available_to_burn) {
let user_share = coin::extract(coins, to_withdraw);

burn_with_user_preference(vm, *user, user_share);
vm_burn_with_user_preference(vm, *user, user_share);
};


Expand Down Expand Up @@ -135,8 +135,30 @@ module ol_framework::burn {
state.lifetime_burned = state.lifetime_burned + value_sent;
}

// /// performs a burn or recycle according to the signer's preference
// public fun burn_with_my_preference(
// sig: &signer, user_share: Coin<LibraCoin>
// ) acquires BurnCounter, UserBurnPreference {
// let payer = signer::address_of(sig);
// let value_sent = coin::value(&user_share);
// if (exists<UserBurnPreference>(payer)) {
// if (borrow_global<UserBurnPreference>(payer).send_community) {
// match_index::match_and_recycle(vm, &mut user_share);

// // update the root state tracker
// let state = borrow_global_mut<BurnCounter>(@ol_framework);
// state.lifetime_recycled = state.lifetime_recycled + value_sent;
// }
// };

// // do a default burn which is not attributed
// // also checks for Superman 3
// burn_and_track(user_share);

// }

/// performs a burn or recycle according to the attributed user's preference
public fun burn_with_user_preference(
public fun vm_burn_with_user_preference(
vm: &signer, payer: address, user_share: Coin<LibraCoin>
) acquires BurnCounter, UserBurnPreference {
system_addresses::assert_ol(vm);
Expand Down
78 changes: 58 additions & 20 deletions framework/libra-framework/sources/ol_sources/genesis_migration.move
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ module ol_framework::genesis_migration {
use ol_framework::libra_coin::LibraCoin;
use ol_framework::transaction_fee;
use ol_framework::pledge_accounts;
use ol_framework::make_whole;
use diem_framework::system_addresses;
// use diem_std::debug::print;

Expand All @@ -26,6 +27,10 @@ module ol_framework::genesis_migration {
const EGENESIS_BALANCE_TOO_HIGH: u64 = 1;
/// balances converted incorrectly, more coins created than the target
const EMINTED_OVER_TARGET: u64 = 2;
/// not sufficient infra escrow balance
const ENO_INFRA_BALANCE: u64 = 3;
/// makewhole is not initialized
const EMAKEWHOLE_NOT_INIT: u64 = 4;

/// Called by root in genesis to initialize the GAS coin
public fun migrate_legacy_user(
Expand Down Expand Up @@ -88,30 +93,63 @@ module ol_framework::genesis_migration {
};
}

/// for an uprade using an escrow percent. Only to be called at genesis
// escrow percent has 6 decimal precision (1m);
public fun fork_escrow_init(vm: &signer, user_sig: &signer, escrow_pct: u64) {
system_addresses::assert_vm(vm);
let user_addr = signer::address_of(user_sig);
// establish the infrastructure escrow pledge
/// for an uprade using an escrow percent. Only to be called at genesis
// escrow percent has 6 decimal precision (1m);
public fun fork_escrow_init(vm: &signer, user_sig: &signer, escrow_pct: u64) {
system_addresses::assert_vm(vm);
let user_addr = signer::address_of(user_sig);
// establish the infrastructure escrow pledge

let escrow_pct = fixed_point32::create_from_rational(escrow_pct, 1000000);
let escrow_pct = fixed_point32::create_from_rational(escrow_pct, 1000000);

let (unlocked, total) = ol_account::balance(user_addr);
let (unlocked, total) = ol_account::balance(user_addr);

let locked = 0;
if ((total > unlocked) && (total > 0)) {
locked = (total - unlocked);
};
let locked = 0;
if ((total > unlocked) && (total > 0)) {
locked = (total - unlocked);
};

if (locked > 0) {
let to_escrow = fixed_point32::multiply_u64(locked, escrow_pct);
let coin_opt = coin::vm_withdraw(vm, user_addr, to_escrow);
if (option::is_some(&coin_opt)) {
let c = option::extract(&mut coin_opt);
pledge_accounts::genesis_infra_escrow_pledge(vm, user_sig, c);
};
option::destroy_none(coin_opt);
if (locked > 0) {
let to_escrow = fixed_point32::multiply_u64(locked, escrow_pct);
let coin_opt = ol_account::vm_withdraw_unlimited(vm, user_addr, to_escrow);
if (option::is_some(&coin_opt)) {
let c = option::extract(&mut coin_opt);
pledge_accounts::save_pledge(user_sig, @0x0, c);
};
option::destroy_none(coin_opt);
};
}

//////// MAKE WHOLE INIT ////////
struct MinerMathError has key {}

/// initializes the Miner Math Error incident make-whole
// note: this exits silently when there's no infra_escrow, since some tests
// don't need it
fun init_make_whole(vm: &signer, make_whole_budget: u64) {
system_addresses::assert_ol(vm);
// withdraw from infraescrow
let opt = pledge_accounts::withdraw_from_all_pledge_accounts(vm,
make_whole_budget);
if (option::is_none(&opt)) {
option::destroy_none(opt);
return // exit quietly
} else {
let coin = option::extract(&mut opt);
option::destroy_none(opt);

let burns_unclaimed = true;
make_whole::init_incident<MinerMathError>(vm, coin, burns_unclaimed);
}
}

/// creates an individual claim for a user
// note: this exits silently when there's no infra_escrow, since some tests
// don't need it
fun vm_create_credit_user(vm: &signer, user: address, value: u64) {
system_addresses::assert_ol(vm);
if (!make_whole::is_init<MinerMathError>(signer::address_of(vm))) return;
make_whole::create_each_user_credit<MinerMathError>(vm, user, value);

}
}
109 changes: 109 additions & 0 deletions framework/libra-framework/sources/ol_sources/make_whole.move
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// Make Whole
// Oops someone made a mistake.
// when there's a mistake made by code, or otherwise,
// someone can sponsor the fix with Make Whole.
// Note on design of this module, and indetended use:
// As per 0L historical practices, the root addresses of protocol (0x0, 0x1,
// etc.) do not ever hold any balances, except temporarily during an epoch in
// the case of system fees (no "protocol treasuries").
// So any Make Whole is done peer-to-peer with sponsors
// and the code and database only provide coordination guarantees.

module ol_framework::make_whole {
use std::signer;
use diem_std::table::{Self, Table};
use diem_framework::coin::{Self, Coin};
use ol_framework::libra_coin::LibraCoin;
use ol_framework::epoch_helper;
use ol_framework::burn;
use ol_framework::ol_account;

/// This incident budget already exists
const EINCIDENT_BUDGET_EXISTS: u64 = 0;
/// hey bro, I don't owe you nothing
const EU_NO_OWED: u64 = 1;
/// Dude what's up with that? you already claimed this one
const EALREADY_CLAIMED: u64 = 2;

struct MakeWhole<phantom IncidentId> has key {
// holds loose coins in escrow
escrow: Coin<LibraCoin>,
// when the escrow finishes, coins burned
expiration_epoch: u64,
// list of unclaimed credits due
// will not be able to claim after expiry
unclaimed: Table<address, u64>,
// list of claimed credits
claimed: Table<address, u64>,
// counter for total claimed
total_claimed: u64,
// burn on expiration
burn_unclaimed: bool,

}

/// a sponsor can initiate an incident
///
public fun init_incident<T: key>(sponsor: &signer, coins: Coin<LibraCoin>,
burn_unclaimed: bool) {
// Don't let your mouth write no check that your tail can't cash.
let sponsor_addr = signer::address_of(sponsor);
assert!(!exists<MakeWhole<T>>(sponsor_addr), EINCIDENT_BUDGET_EXISTS);
move_to<MakeWhole<T>>(sponsor, MakeWhole{
escrow: coins,
// TODO: 90 day default
expiration_epoch: epoch_helper::get_current_epoch() + 90,
unclaimed: table::new<address, u64>(),
claimed: table::new<address, u64>(),
total_claimed: 0,
burn_unclaimed,
})
}

public fun is_init<T>(sponsor: address): bool {
exists<MakeWhole<T>>(sponsor)
}

/// creates a credit for incident T
public fun create_each_user_credit<T>(sponsor: &signer, user: address, value: u64)
acquires MakeWhole {
let sponsor_addr = signer::address_of(sponsor);
let state = borrow_global_mut<MakeWhole<T>>(sponsor_addr);

table::upsert(&mut state.unclaimed, user, value);
}

/// user claims credit
public fun claim_credit<T>(user_sig: &signer, sponsor: address)
acquires MakeWhole {
let user_addr = signer::address_of(user_sig);

let state = borrow_global_mut<MakeWhole<T>>(sponsor);
assert!(table::contains(&state.unclaimed, user_addr), EU_NO_OWED);
assert!(!table::contains(&state.claimed, user_addr), EALREADY_CLAIMED);

let value = table::remove(&mut state.unclaimed, user_addr);
let owed_coins = coin::extract(&mut state.escrow, value);

ol_account::deposit_coins(user_addr, owed_coins);

table::upsert(&mut state.claimed, user_addr, value);
}

/// anyone can call the method after expiry and the coins will be burned.
public fun lazy_expire<T>(sponsor_addr: address) acquires MakeWhole {
// funds go back to sponsor or burned
let state = borrow_global_mut<MakeWhole<T>>(sponsor_addr);
if (epoch_helper::get_current_epoch() > state.expiration_epoch) {
let unused_coins = coin::extract_all(&mut state.escrow);

if (state.burn_unclaimed) {
burn::burn_and_track(unused_coins);
// Some debts are fun when you are acquiring them, but none are fun when you set about retiring them
}
else {
ol_account::deposit_coins(sponsor_addr, unused_coins);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ module ol_framework::ol_account {
friend diem_framework::genesis;
friend diem_framework::resource_account;
friend diem_framework::transaction_fee;
friend ol_framework::genesis_migration;

/// Account does not exist.
const EACCOUNT_NOT_FOUND: u64 = 1;
Expand Down
30 changes: 4 additions & 26 deletions framework/libra-framework/sources/ol_sources/pledge_accounts.move
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
use ol_framework::epoch_helper;
use ol_framework::burn;
use diem_framework::system_addresses;
use diem_framework::coin::{Self, Coin};
use diem_framework::coin;

// use diem_std::debug::print;

Expand Down Expand Up @@ -138,7 +138,7 @@
}
}


/// saves a pledge if it exists
public fun save_pledge(
sig: &signer,
address_of_beneficiary: address,
Expand Down Expand Up @@ -205,8 +205,7 @@
// add funds to an existing pledge account
// Note: only funds that are Unlocked and otherwise unrestricted can be used in pledge account.
fun add_coin_to_pledge_account(sender_addr: address, idx: u64, coin: coin::Coin<LibraCoin>) acquires MyPledges, BeneficiaryPolicy {
// let sender_addr = signer::address_of(sender);
// let (found, _idx) = pledge_at_idx(&sender_addr, &address_of_beneficiary);

let amount = coin::value(&coin);
let my_pledges = borrow_global_mut<MyPledges>(sender_addr);
let pledge_account = vector::borrow_mut(&mut my_pledges.list, idx);
Expand Down Expand Up @@ -491,15 +490,7 @@
}


//// Genesis helper
// private function only to be used at genesis for infra escrow
// This used only at genesis, and CAN BYPASS THE WITHDRAW LIMITS
public fun genesis_infra_escrow_pledge(root: &signer, account: &signer, coin: Coin<LibraCoin>) acquires MyPledges, BeneficiaryPolicy {
// TODO: add genesis time here, once the timestamp genesis issue is fixed.
// chain_status::assert_genesis();
system_addresses::assert_ol(root);
save_pledge(account, @vm_reserved, coin);
}
//NOTE: deprecated with refactor

////////// TX //////////
// for general pledge accounts
Expand All @@ -511,19 +502,6 @@

////////// GETTERS //////////

// Danger: If the VM calls this and there is an error there will be a halt.
// always call pledge_at_idx() first.
// NOTE: cannot wrap in option witout changing the struct abilities to copy, drop.
// can't do that because Diem<LibraCoin> cannot be copy, or drop.
// public fun maybe_find_a_pledge(account: &address, address_of_beneficiary: &address): &mut PledgeAccount acquires MyPledges {
// let (found, idx) = pledge_at_idx(account, address_of_beneficiary);
// assert!(found, error::invalid_state(ENO_PLEDGE_INIT));

// let my_pledges = borrow_global_mut<MyPledges>(*account).list;
// let p = vector::borrow_mut(&mut my_pledges, idx);
// p
// }

fun pledge_at_idx(account: &address, address_of_beneficiary: &address): (bool, u64) acquires MyPledges {
if (exists<MyPledges>(*account)) {
let my_pledges = &borrow_global<MyPledges>(*account).list;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ module ol_framework::test_meta {
// // create a new epoch
// stake::end_epoch();

// reconfiguration::test_helper_increment_epoch_dont_reconfigure();
// reconfiguration::test_helper_increment_epoch_dont_reconfigure(1);

// let b = reconfiguration::get_current_epoch();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ module ol_framework::test_boundary {
slow_wallet::slow_wallet_epoch_drip(root, 500000);

// NOTE: for e2e epoch tests, we need to go into an operating epoch (not 0 or 1). Advance to epoch #2
reconfiguration::test_helper_increment_epoch_dont_reconfigure();
reconfiguration::test_helper_increment_epoch_dont_reconfigure();
reconfiguration::test_helper_increment_epoch_dont_reconfigure(1);
reconfiguration::test_helper_increment_epoch_dont_reconfigure(1);

set
}
Expand Down Expand Up @@ -151,4 +151,4 @@ module ol_framework::test_boundary {
assert!(vector::length(&epoch_boundary::get_auction_winners()) == vector::length(&qualified_bidders) , 7357003);
assert!(epoch_boundary::get_reconfig_success(), 7357001);
}
}
}
Loading

0 comments on commit 03d9f10

Please sign in to comment.