1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-03-09 15:40:10 +00:00

Add fee burning and blackhole address (#703)

* Fix tlbc crash

* Burn specified fraction of fees

* Add "blackhole" for burning TONs

---------

Co-authored-by: SpyCheese <mikle98@yandex.ru>
This commit is contained in:
EmelyanenkoK 2023-05-18 11:26:02 +03:00 committed by GitHub
parent d5cd548502
commit ef64b92f08
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 187 additions and 57 deletions

View file

@ -1364,42 +1364,61 @@ std::ostream& operator<<(std::ostream& os, const CurrencyCollection& cc) {
bool ValueFlow::set_zero() {
return from_prev_blk.set_zero() && to_next_blk.set_zero() && imported.set_zero() && exported.set_zero() &&
fees_collected.set_zero() && fees_imported.set_zero() && recovered.set_zero() && created.set_zero() &&
minted.set_zero();
minted.set_zero() && burned.set_zero();
}
bool ValueFlow::validate() const {
return is_valid() && from_prev_blk + imported + fees_imported + created + minted + recovered ==
to_next_blk + exported + fees_collected;
to_next_blk + exported + fees_collected + burned;
}
bool ValueFlow::store(vm::CellBuilder& cb) const {
vm::CellBuilder cb2;
return cb.store_long_bool(block::gen::ValueFlow::cons_tag[0], 32) // value_flow ^[
&& from_prev_blk.store(cb2) // from_prev_blk:CurrencyCollection
&& to_next_blk.store(cb2) // to_next_blk:CurrencyCollection
&& imported.store(cb2) // imported:CurrencyCollection
&& exported.store(cb2) // exported:CurrencyCollection
&& cb.store_ref_bool(cb2.finalize()) // ]
&& fees_collected.store(cb) // fees_collected:CurrencyCollection
&& fees_imported.store(cb2) // ^[ fees_imported:CurrencyCollection
&& recovered.store(cb2) // recovered:CurrencyCollection
&& created.store(cb2) // created:CurrencyCollection
&& minted.store(cb2) // minted:CurrencyCollection
&& cb.store_ref_bool(cb2.finalize()); // ] = ValueFlow;
auto type = burned.is_zero() ? block::gen::ValueFlow::value_flow : block::gen::ValueFlow::value_flow_v2;
return cb.store_long_bool(block::gen::ValueFlow::cons_tag[type], 32) // ^[
&& from_prev_blk.store(cb2) // from_prev_blk:CurrencyCollection
&& to_next_blk.store(cb2) // to_next_blk:CurrencyCollection
&& imported.store(cb2) // imported:CurrencyCollection
&& exported.store(cb2) // exported:CurrencyCollection
&& cb.store_ref_bool(cb2.finalize()) // ]
&& fees_collected.store(cb) // fees_collected:CurrencyCollection
&& (burned.is_zero() || burned.store(cb)) // fees_burned:CurrencyCollection
&& fees_imported.store(cb2) // ^[ fees_imported:CurrencyCollection
&& recovered.store(cb2) // recovered:CurrencyCollection
&& created.store(cb2) // created:CurrencyCollection
&& minted.store(cb2) // minted:CurrencyCollection
&& cb.store_ref_bool(cb2.finalize()); // ] = ValueFlow;
}
bool ValueFlow::fetch(vm::CellSlice& cs) {
block::gen::ValueFlow::Record f;
if (!(tlb::unpack(cs, f) && from_prev_blk.validate_unpack(std::move(f.r1.from_prev_blk)) &&
to_next_blk.validate_unpack(std::move(f.r1.to_next_blk)) &&
imported.validate_unpack(std::move(f.r1.imported)) && exported.validate_unpack(std::move(f.r1.exported)) &&
fees_collected.validate_unpack(std::move(f.fees_collected)) &&
fees_imported.validate_unpack(std::move(f.r2.fees_imported)) &&
recovered.validate_unpack(std::move(f.r2.recovered)) && created.validate_unpack(std::move(f.r2.created)) &&
minted.validate_unpack(std::move(f.r2.minted)))) {
if (cs.size() < 32) {
return invalidate();
}
return true;
auto tag = cs.prefetch_ulong(32);
block::gen::ValueFlow::Record_value_flow f1;
if (tag == block::gen::ValueFlow::cons_tag[block::gen::ValueFlow::value_flow] && tlb::unpack(cs, f1) &&
from_prev_blk.validate_unpack(std::move(f1.r1.from_prev_blk)) &&
to_next_blk.validate_unpack(std::move(f1.r1.to_next_blk)) &&
imported.validate_unpack(std::move(f1.r1.imported)) && exported.validate_unpack(std::move(f1.r1.exported)) &&
fees_collected.validate_unpack(std::move(f1.fees_collected)) && burned.set_zero() &&
fees_imported.validate_unpack(std::move(f1.r2.fees_imported)) &&
recovered.validate_unpack(std::move(f1.r2.recovered)) && created.validate_unpack(std::move(f1.r2.created)) &&
minted.validate_unpack(std::move(f1.r2.minted))) {
return true;
}
block::gen::ValueFlow::Record_value_flow_v2 f2;
if (tag == block::gen::ValueFlow::cons_tag[block::gen::ValueFlow::value_flow_v2] && tlb::unpack(cs, f2) &&
from_prev_blk.validate_unpack(std::move(f2.r1.from_prev_blk)) &&
to_next_blk.validate_unpack(std::move(f2.r1.to_next_blk)) &&
imported.validate_unpack(std::move(f2.r1.imported)) && exported.validate_unpack(std::move(f2.r1.exported)) &&
fees_collected.validate_unpack(std::move(f2.fees_collected)) &&
burned.validate_unpack(std::move(f2.burned)) &&
fees_imported.validate_unpack(std::move(f2.r2.fees_imported)) &&
recovered.validate_unpack(std::move(f2.r2.recovered)) && created.validate_unpack(std::move(f2.r2.created)) &&
minted.validate_unpack(std::move(f2.r2.minted))) {
return true;
}
return invalidate();
}
bool ValueFlow::unpack(Ref<vm::CellSlice> csr) {
@ -1424,7 +1443,8 @@ bool ValueFlow::show(std::ostream& os) const {
show_one(os, " to_next_blk:", to_next_blk) && show_one(os, " imported:", imported) &&
show_one(os, " exported:", exported) && show_one(os, " fees_collected:", fees_collected) &&
show_one(os, " fees_imported:", fees_imported) && show_one(os, " recovered:", recovered) &&
show_one(os, " created:", created) && show_one(os, " minted:", minted) && say(os, ")")) ||
show_one(os, " created:", created) && show_one(os, " minted:", minted) &&
(burned.is_zero() || show_one(os, " burned:", burned)) && say(os, ")")) ||
(say(os, "...<invalid-value-flow>)") && false);
}

View file

@ -456,7 +456,7 @@ struct ShardState {
struct ValueFlow {
struct SetZero {};
CurrencyCollection from_prev_blk, to_next_blk, imported, exported, fees_collected, fees_imported, recovered, created,
minted;
minted, burned;
ValueFlow() = default;
ValueFlow(SetZero)
: from_prev_blk{0}
@ -467,7 +467,8 @@ struct ValueFlow {
, fees_imported{0}
, recovered{0}
, created{0}
, minted{0} {
, minted{0}
, burned{0} {
}
bool is_valid() const {
return from_prev_blk.is_valid() && minted.is_valid();

View file

@ -472,6 +472,19 @@ value_flow#b8e48dfb ^[ from_prev_blk:CurrencyCollection
minted:CurrencyCollection
] = ValueFlow;
value_flow_v2#3ebf98b7 ^[ from_prev_blk:CurrencyCollection
to_next_blk:CurrencyCollection
imported:CurrencyCollection
exported:CurrencyCollection ]
fees_collected:CurrencyCollection
burned:CurrencyCollection
^[
fees_imported:CurrencyCollection
recovered:CurrencyCollection
created:CurrencyCollection
minted:CurrencyCollection
] = ValueFlow;
//
//
bt_leaf$0 {X:Type} leaf:X = BinTree X;
@ -595,6 +608,11 @@ _ minter_addr:bits256 = ConfigParam 2; // ConfigParam 0 is used if absent
_ fee_collector_addr:bits256 = ConfigParam 3; // ConfigParam 1 is used if absent
_ dns_root_addr:bits256 = ConfigParam 4; // root TON DNS resolver
burning_config#01
blackhole_addr:(Maybe bits256)
fee_burn_nom:# fee_burn_denom:# { fee_burn_nom <= fee_burn_denom } { fee_burn_denom >= 1 } = BurningConfig;
_ BurningConfig = ConfigParam 5;
_ mint_new_price:Grams mint_add_price:Grams = ConfigParam 6;
_ to_mint:ExtraCurrencyCollection = ConfigParam 7;

View file

@ -855,11 +855,12 @@ Ref<McShardHash> McShardHash::from_block(Ref<vm::Cell> block_root, const ton::Fi
ton::RootHash rhash = block_root->get_hash().bits();
CurrencyCollection fees_collected, funds_created;
if (init_fees) {
block::gen::ValueFlow::Record flow;
if (!(tlb::unpack_cell(rec.value_flow, flow) && fees_collected.unpack(flow.fees_collected) &&
funds_created.unpack(flow.r2.created))) {
block::ValueFlow flow;
if (!flow.unpack(vm::load_cell_slice_ref(rec.value_flow))) {
return {};
}
fees_collected = flow.fees_collected;
funds_created = flow.created;
}
return Ref<McShardHash>(true, ton::BlockId{ton::ShardIdFull(shard), (unsigned)info.seq_no}, info.start_lt,
info.end_lt, info.gen_utime, rhash, fhash, fees_collected, funds_created, ~0U,
@ -909,11 +910,12 @@ Ref<McShardDescr> McShardDescr::from_block(Ref<vm::Cell> block_root, Ref<vm::Cel
ton::RootHash rhash = block_root->get_hash().bits();
CurrencyCollection fees_collected, funds_created;
if (init_fees) {
block::gen::ValueFlow::Record flow;
if (!(tlb::unpack_cell(rec.value_flow, flow) && fees_collected.unpack(flow.fees_collected) &&
funds_created.unpack(flow.r2.created))) {
block::ValueFlow flow;
if (!flow.unpack(vm::load_cell_slice_ref(rec.value_flow))) {
return {};
}
fees_collected = flow.fees_collected;
funds_created = flow.created;
}
auto res = Ref<McShardDescr>(true, ton::BlockId{ton::ShardIdFull(shard), (unsigned)info.seq_no}, info.start_lt,
info.end_lt, info.gen_utime, rhash, fhash, fees_collected, funds_created, ~0U,
@ -1954,6 +1956,24 @@ std::unique_ptr<vm::Dictionary> Config::get_suspended_addresses(ton::UnixTime no
return std::make_unique<vm::Dictionary>(rec.addresses->prefetch_ref(), 288);
}
BurningConfig Config::get_burning_config() const {
td::Ref<vm::Cell> param = get_config_param(5);
gen::BurningConfig::Record rec;
if (param.is_null() || !tlb::unpack_cell(param, rec)) {
return {};
}
BurningConfig c;
c.fee_burn_nom = rec.fee_burn_nom;
c.fee_burn_denom = rec.fee_burn_denom;
vm::CellSlice& addr = rec.blackhole_addr.write();
if (addr.fetch_long(1)) {
td::Bits256 x;
addr.fetch_bits_to(x.bits(), 256);
c.blackhole_addr = x;
}
return c;
}
td::Result<std::pair<ton::UnixTime, ton::UnixTime>> Config::unpack_validator_set_start_stop(Ref<vm::Cell> vset_root) {
if (vset_root.is_null()) {
return td::Status::Error("validator set absent");

View file

@ -504,6 +504,22 @@ class ShardConfig {
bool set_shard_info(ton::ShardIdFull shard, Ref<vm::Cell> value);
};
struct BurningConfig {
td::optional<td::Bits256> blackhole_addr;
td::uint32 fee_burn_nom = 0, fee_burn_denom = 1;
td::RefInt256 calculate_burned_fees(const td::RefInt256& x) const {
if (x.is_null()) {
return x;
}
return x * fee_burn_nom / td::make_refint(fee_burn_denom);
}
CurrencyCollection calculate_burned_fees(const CurrencyCollection& x) const {
return CurrencyCollection{calculate_burned_fees(x.grams)};
}
};
class Config {
enum {
default_mc_catchain_lifetime = 200,
@ -617,6 +633,7 @@ class Config {
std::vector<ton::ValidatorDescr> compute_total_validator_set(int next) const;
td::Result<SizeLimitsConfig> get_size_limits_config() const;
std::unique_ptr<vm::Dictionary> get_suspended_addresses(ton::UnixTime now) const;
BurningConfig get_burning_config() const;
static std::vector<ton::ValidatorDescr> do_compute_validator_set(const block::CatchainValidatorsConfig& ccv_conf,
ton::ShardIdFull shard,
const block::ValidatorSet& vset, ton::UnixTime time,

View file

@ -672,6 +672,12 @@ bool Transaction::unpack_input_msg(bool ihr_delivered, const ActionPhaseConfig*
return false;
}
total_fees += in_fwd_fee;
if (account.workchain == ton::masterchainId && cfg->mc_blackhole_addr &&
cfg->mc_blackhole_addr.value() == account.addr) {
blackhole_burned.grams = msg_balance_remaining.grams;
msg_balance_remaining.grams = td::zero_refint();
LOG(DEBUG) << "Burning " << blackhole_burned.grams << " nanoton (blackhole address)";
}
return true;
}
@ -2618,6 +2624,7 @@ td::Status FetchConfigParams::fetch_config_params(const block::Config& config,
action_phase_cfg->workchains = &config.get_workchain_list();
action_phase_cfg->bounce_msg_body = (config.has_capability(ton::capBounceMsgBody) ? 256 : 0);
action_phase_cfg->size_limits = size_limits;
action_phase_cfg->mc_blackhole_addr = config.get_burning_config().blackhole_addr;
}
{
// fetch block_grams_created

View file

@ -153,6 +153,7 @@ struct ActionPhaseConfig {
MsgPrices fwd_mc; // from/to masterchain
SizeLimitsConfig size_limits;
const WorkchainSet* workchains{nullptr};
td::optional<td::Bits256> mc_blackhole_addr;
const MsgPrices& fetch_msg_prices(bool is_masterchain) const {
return is_masterchain ? fwd_mc : fwd_std;
}
@ -332,6 +333,7 @@ struct Transaction {
td::RefInt256 due_payment;
td::RefInt256 in_fwd_fee, msg_fwd_fees;
block::CurrencyCollection total_fees{0};
block::CurrencyCollection blackhole_burned{0};
ton::UnixTime last_paid;
Ref<vm::Cell> root;
Ref<vm::Cell> new_total_state;

View file

@ -1998,7 +1998,7 @@ TypeExpr* parse_anonymous_constructor(Lexer& lex, Constructor& cs) {
if (types[i].parent_type_idx >= 0) {
types[i].parent_type_idx = -2;
}
delete cs2;
cs2->~Constructor();
return TypeExpr::mk_apply_empty(lex.cur().loc, 0, &types[i]);
}
}