Skip to content

Commit 3921808

Browse files
committed
dyndiff; updated-incentive; reduced fees;
1 parent 66f07f8 commit 3921808

12 files changed

Lines changed: 312 additions & 24 deletions

File tree

ex/lib/api/api_epoch.ex

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,16 @@ defmodule API.Epoch do
2525
|> Base58.encode()
2626
end
2727

28+
def get_diff_bits(epoch \\ nil) do
29+
epoch = if epoch do epoch else Consensus.chain_epoch() end
30+
API.Contract.get("bic:epoch:diff_bits:#{epoch}", :to_integer) || 24
31+
end
32+
33+
def get_total_sols(epoch \\ nil) do
34+
epoch = if epoch do epoch else Consensus.chain_epoch() end
35+
API.Contract.get("bic:epoch:total_sols:#{epoch}", :to_integer) || 0
36+
end
37+
2838
def get_pop(pk) do
2939
pk = if byte_size(pk) != 48, do: Base58.decode(pk), else: pk
3040
Consensus.chain_pop(pk)

ex/lib/api/api_peer.ex

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,8 @@ defmodule API.Peer do
114114
|> anr_for_web()
115115
end
116116

117-
def anr_for_web(anrs) when is_list(anrs) do Enum.map(anrs, & anr_for_web(&1)) end
117+
def anr_for_web(anrs) when is_list(anrs) do Enum.map(anrs, & anr_for_web(&1)) |> Enum.filter(& &1) end
118+
def anr_for_web(nil) do nil end
118119
def anr_for_web(anr) do
119120
%{
120121
pk: Base58.encode(anr.pk),

ex/lib/bic/base.ex

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
defmodule BIC.Base do
22
import ConsensusKV
33

4-
def exec_cost(txu) do
4+
def exec_cost(epoch, txu) do
55
bytes = byte_size(txu.tx_encoded) + 32 + 96
6-
BIC.Coin.to_cents( 3 + div(bytes, 256) * 3 )
6+
#3x lower fee, 1 cent AMA base per tx
7+
if epoch >= 295 do
8+
BIC.Coin.to_cents( 1 + div(bytes, 256) * 1 )
9+
else
10+
BIC.Coin.to_cents( 3 + div(bytes, 256) * 3 )
11+
end
712

813
#for future update
914
#BIC.Coin.to_tenthousandth( 18 + div(bytes, 256) * 3 )
@@ -22,18 +27,28 @@ defmodule BIC.Base do
2227

2328
Enum.each(txus, fn(txu)->
2429
kv_put("bic:base:nonce:#{txu.tx.signer}", txu.tx.nonce, %{to_integer: true})
25-
exec_cost = exec_cost(txu)
30+
exec_cost = exec_cost(env.entry_epoch, txu)
2631
kv_increment("bic:coin:balance:#{txu.tx.signer}:AMA", -exec_cost)
27-
kv_increment("bic:coin:balance:#{env.entry_signer}:AMA", exec_cost)
32+
33+
if env.entry_epoch >= 295 do
34+
#burn 50% to prevent MEV/FreeChainGrowth attack
35+
half_exec_cost = div(exec_cost, 2)
36+
kv_increment("bic:coin:balance:#{env.entry_signer}:AMA", half_exec_cost)
37+
kv_increment("bic:coin:balance:#{BIC.Coin.burn_address()}:AMA", half_exec_cost)
38+
else
39+
kv_increment("bic:coin:balance:#{env.entry_signer}:AMA", exec_cost)
40+
end
2841
end)
2942

3043
#parallel verify sols
3144
segment_vr_hash = kv_get("bic:epoch:segment_vr_hash")
45+
diff_bits = kv_get("bic:epoch:diff_bits", %{to_integer: true}) || 24
3246
steam = Task.async_stream(txus, fn txu ->
3347
sol = Enum.find_value(txu.tx.actions, fn(a)-> a.function == "submit_sol" and length(a.args) != [] and hd(a.args) end)
3448
if sol do
3549
hash = Blake3.hash(sol)
36-
valid = try do BIC.Sol.verify(sol, %{hash: hash, vr_b3: env.entry_vr_b3, segment_vr_hash: segment_vr_hash}) catch _,_ -> false end
50+
opts = %{hash: hash, vr_b3: env.entry_vr_b3, segment_vr_hash: segment_vr_hash, diff_bits: diff_bits}
51+
valid = try do BIC.Sol.verify(sol, opts) catch _,_ -> false end
3752
%{hash: hash, valid: valid}
3853
end
3954
end)
@@ -53,12 +68,6 @@ defmodule BIC.Base do
5368
Process.delete(:mutations_reverse)
5469
seed_random(env.entry_vr, "", "", "")
5570

56-
if env.entry_epoch >= 282 do
57-
else
58-
#thank you come again
59-
kv_increment("bic:coin:balance:#{env.entry_signer}:AMA", BIC.Coin.to_flat(1))
60-
end
61-
6271
if rem(env.entry_height, 1000) == 0 do
6372
kv_put("bic:epoch:segment_vr_hash", Blake3.hash(env.entry_vr))
6473
end
@@ -110,9 +119,19 @@ defmodule BIC.Base do
110119

111120
muts = Process.get(:mutations, []); Process.delete(:mutations)
112121
muts_rev = Process.get(:mutations_reverse, []); Process.delete(:mutations_reverse)
122+
113123
exec_used = (result[:exec_used] || 0) * 100
114-
kv_increment("bic:coin:balance:#{env.entry_signer}:AMA", exec_used)
115124
kv_increment("bic:coin:balance:#{env.tx_signer}:AMA", -exec_used)
125+
126+
if env.entry_epoch >= 295 do
127+
#burn 50% to prevent MEV/FreeChainGrowth attack
128+
half_exec_cost = div(exec_used, 2)
129+
kv_increment("bic:coin:balance:#{env.entry_signer}:AMA", half_exec_cost)
130+
kv_increment("bic:coin:balance:#{BIC.Coin.burn_address()}:AMA", half_exec_cost)
131+
else
132+
kv_increment("bic:coin:balance:#{env.entry_signer}:AMA", exec_used)
133+
end
134+
116135
Process.put(:mutations_gas, Process.get(:mutations, []))
117136
Process.put(:mutations_gas_reverse, Process.get(:mutations_reverse, []))
118137
Process.put(:mutations, muts)

ex/lib/bic/epoch.ex

Lines changed: 94 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ defmodule BIC.Epoch do
77

88
@a 23_072_960_000
99
@c 1110.573766
10-
@start_epoch 500
10+
@start_epoch 420
1111

1212
def epoch_emission(epoch) when epoch >= @start_epoch do
1313
floor(0.5 * @a / :math.pow(epoch - @start_epoch + @c, 1.5))
@@ -140,12 +140,15 @@ defmodule BIC.Epoch do
140140
end
141141

142142
def next(env) do
143-
epoch_fin = env.entry_epoch
144-
epoch_next = epoch_fin + 1
143+
if env.entry_epoch >= 295 or env.entry_epoch < 420 do
144+
next_420(env)
145+
else
146+
epoch_cur = env.entry_epoch
147+
epoch_next = epoch_cur + 1
145148
top_x = 99
146149

147150
# slash sols for malicious trainers
148-
removedTrainers = kv_get("bic:epoch:trainers:removed:#{epoch_fin}", %{term: true}) || []
151+
removedTrainers = kv_get("bic:epoch:trainers:removed:#{epoch_cur}", %{term: true}) || []
149152
leaders = kv_get_prefix("bic:epoch:solutions_count:", %{to_integer: true})
150153
|> Enum.reduce(%{}, fn({pk, count}, acc)->
151154
if pk in removedTrainers do
@@ -156,14 +159,14 @@ defmodule BIC.Epoch do
156159
end)
157160
|> Enum.sort_by(& {elem(&1,1), elem(&1,0)}, :desc)
158161

159-
trainers = kv_get("bic:epoch:trainers:#{epoch_fin}", %{term: true})
162+
trainers = kv_get("bic:epoch:trainers:#{epoch_cur}", %{term: true})
160163
trainers_to_recv_emissions = leaders
161164
|> Enum.filter(& elem(&1,0) in trainers)
162165
|> Enum.take(top_x)
163166

164167
total_sols = Enum.reduce(trainers_to_recv_emissions, 0, & &2 + elem(&1,1))
165168
Enum.each(trainers_to_recv_emissions, fn({trainer, trainer_sols})->
166-
coins = div(trainer_sols * epoch_emission(epoch_fin), total_sols)
169+
coins = div(trainer_sols * epoch_emission(epoch_cur), total_sols)
167170

168171
emission_address = kv_get("bic:epoch:emission_address:#{trainer}")
169172
if emission_address do
@@ -189,6 +192,91 @@ defmodule BIC.Epoch do
189192

190193
height = String.pad_leading("#{env.entry_height+1}", 12, "0")
191194
kv_put("bic:epoch:trainers:height:#{height}", new_validators, %{term: true})
195+
end
196+
end
197+
198+
def next_420(env) do
199+
epoch_cur = env.entry_epoch
200+
epoch_next = epoch_cur + 1
201+
top_x = 99
202+
203+
# slash sols for malicious trainers
204+
removedTrainers = kv_get("bic:epoch:trainers:removed:#{epoch_cur}", %{term: true}) || []
205+
leaders = kv_get_prefix("bic:epoch:solutions_count:", %{to_integer: true})
206+
|> Enum.reduce(%{}, fn({pk, count}, acc)->
207+
if pk in removedTrainers do
208+
acc
209+
else
210+
Map.put(acc, pk, count)
211+
end
212+
end)
213+
|> Enum.sort_by(& {elem(&1,1), elem(&1,0)}, :desc)
214+
215+
trainers = kv_get("bic:epoch:trainers:#{epoch_cur}", %{term: true})
216+
trainers_to_recv_emissions = leaders
217+
|> Enum.filter(& elem(&1,0) in trainers and elem(&1,0) not in @peddlebike67)
218+
|> Enum.take(top_x)
219+
220+
epoch_total_emission = epoch_emission(epoch_cur)
221+
epoch_early_adopter_emission = div(epoch_total_emission, 7)
222+
epoch_communityfund_emission = epoch_total_emission - epoch_early_adopter_emission
223+
#Community fund for grants such as building open source code and building onchain/ecosystem
224+
#alot of interest from early adopters to receive grants for building
225+
226+
n_count = length(@peddlebike67)
227+
q = div(epoch_communityfund_emission, n_count)
228+
r = rem(epoch_communityfund_emission, n_count)
229+
n_summed = List.duplicate(q + 1, r) ++ List.duplicate(q, n_count - r)
230+
231+
Enum.zip(@peddlebike67, n_summed)
232+
|> Enum.each(fn({trainer, coins})->
233+
emission_address = kv_get("bic:epoch:emission_address:#{trainer}")
234+
if emission_address do
235+
kv_increment("bic:coin:balance:#{emission_address}:AMA", coins)
236+
else
237+
kv_increment("bic:coin:balance:#{trainer}:AMA", coins)
238+
end
239+
end)
240+
241+
total_sols = Enum.reduce(trainers_to_recv_emissions, 0, & &2 + elem(&1,1))
242+
if total_sols > 0 do
243+
Enum.each(trainers_to_recv_emissions, fn({trainer, trainer_sols})->
244+
coins = div(trainer_sols * epoch_early_adopter_emission, total_sols)
245+
246+
emission_address = kv_get("bic:epoch:emission_address:#{trainer}")
247+
if emission_address do
248+
kv_increment("bic:coin:balance:#{emission_address}:AMA", coins)
249+
else
250+
kv_increment("bic:coin:balance:#{trainer}:AMA", coins)
251+
end
252+
end)
253+
end
254+
255+
leaders = Enum.map(leaders, fn{pk, _}-> pk end)
256+
257+
#REMOVE THIS LATER, first we must start with a peddle bike as a
258+
#UAV proved to have a lack of skilled pilots
259+
leaders = leaders -- @peddlebike67
260+
new_validators = (@peddlebike67 ++ leaders)
261+
|> Enum.take(top_x)
262+
|> Enum.shuffle()
263+
264+
kv_put("bic:epoch:trainers:#{epoch_next}", new_validators, %{term: true})
265+
266+
height = String.pad_leading("#{env.entry_height+1}", 12, "0")
267+
kv_put("bic:epoch:trainers:height:#{height}", new_validators, %{term: true})
268+
269+
#new difficulty handling
270+
old_diff_bits = kv_get("bic:epoch:diff_bits", %{to_integer: true}) || 24
271+
next_diff_bits = SolDifficulty.next(old_diff_bits, total_sols)
272+
kv_put("bic:epoch:diff_bits", next_diff_bits, %{to_integer: true})
273+
274+
#log for analysis / potential backseek in future upgrade
275+
kv_put("bic:epoch:diff_bits:#{epoch_next}", next_diff_bits, %{to_integer: true})
276+
kv_put("bic:epoch:total_sols:#{epoch_cur}", total_sols, %{to_integer: true})
277+
278+
kv_clear("bic:epoch:solbloom:")
279+
kv_clear("bic:epoch:solutions_count:")
192280
end
193281

194282
def slash_trainer_verify(cur_epoch, malicious_pk, trainers, mask, signature) do

ex/lib/bic/sol.ex

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,20 @@ defmodule BIC.Sol do
3838
a == 0
3939
end
4040

41+
def verify_hash_diff(_epoch, hash, diff_bits) do
42+
<<a::size(diff_bits), _::bitstring>> = hash
43+
a == 0
44+
end
45+
4146
def verify(sol = <<epoch::32-little, _::binary>>, opts \\ %{}) do
4247
cond do
48+
epoch >= 295 ->
49+
usol = unpack(sol)
50+
if opts.segment_vr_hash != usol.segment_vr_hash, do: throw %{error: :segment_vr_hash}
51+
if byte_size(sol) != @sol_size, do: throw(%{error: :invalid_sol_seed_size})
52+
hash = Map.get_lazy(opts, :hash, fn()-> Blake3.hash(sol) end)
53+
vr_b3 = Map.get_lazy(opts, :vr_b3, fn()-> :crypto.strong_rand_bytes(32) end)
54+
verify_hash_diff(epoch, hash, opts.diff_bits) and Blake3.freivalds_e260(sol, vr_b3)
4355
epoch >= 282 ->
4456
usol = unpack(sol)
4557
if opts.segment_vr_hash != usol.segment_vr_hash, do: throw %{error: :segment_vr_hash}

ex/lib/bic/sol_difficulty.ex

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
defmodule SolDifficulty do
2+
@target_sols_epoch 380_000
3+
4+
@tol_num 1
5+
@tol_den 10
6+
7+
@max_step_down 3
8+
@max_step_up 2
9+
@up_slowdown 2
10+
11+
@diff_min_bits 20
12+
@diff_max_bits 64
13+
14+
defp clamp_bits(b), do: b |> max(@diff_min_bits) |> min(@diff_max_bits)
15+
16+
defp ceil_div(a, b), do: div(a + b - 1, b)
17+
18+
defp ilog2_floor(n) when n >= 1, do: do_ilog2(n, 0)
19+
defp do_ilog2(1, acc), do: acc
20+
defp do_ilog2(n, acc), do: do_ilog2(div(n, 2), acc + 1)
21+
22+
defp ceil_log2_ratio(a, b) when a <= b, do: 0
23+
defp ceil_log2_ratio(a, b) do
24+
d0 = ilog2_floor(a) - ilog2_floor(b)
25+
if :erlang.bsl(b, d0) >= a, do: d0, else: d0 + 1
26+
end
27+
28+
def next(prev_bits, sols) do
29+
target = @target_sols_epoch
30+
31+
lo = max(1, div(target * (@tol_den - @tol_num) + div(@tol_den, 2), @tol_den))
32+
hi = div(target * (@tol_den + @tol_num) + div(@tol_den, 2), @tol_den)
33+
34+
cond do
35+
sols == 0 ->
36+
clamp_bits(prev_bits - min(@max_step_down, 3))
37+
38+
sols > hi ->
39+
raw = ceil_log2_ratio(sols, target)
40+
delta = raw
41+
|> ceil_div(@up_slowdown)
42+
|> min(@max_step_up)
43+
|> max(1)
44+
clamp_bits(prev_bits + delta)
45+
46+
sols < lo ->
47+
delta = ceil_log2_ratio(target, max(sols, 1))
48+
|> min(@max_step_down)
49+
|> max(1)
50+
clamp_bits(prev_bits - delta)
51+
52+
true ->
53+
prev_bits
54+
end
55+
end
56+
end

ex/lib/consensus/consensus.ex

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,16 @@ defmodule Consensus do
125125
RocksDB.get("bic:epoch:segment_vr_hash", %{db: db, cf: cf.contractstate})
126126
end
127127

128+
def chain_diff_bits() do
129+
%{db: db, cf: cf} = :persistent_term.get({:rocksdb, Fabric})
130+
RocksDB.get("bic:epoch:diff_bits", %{db: db, cf: cf.contractstate, to_integer: true}) || 24
131+
end
132+
133+
def chain_total_sols() do
134+
%{db: db, cf: cf} = :persistent_term.get({:rocksdb, Fabric})
135+
RocksDB.get("bic:epoch:total_sols", %{db: db, cf: cf.contractstate, to_integer: true}) || 0
136+
end
137+
128138
def chain_pop(pk) do
129139
%{db: db, cf: cf} = :persistent_term.get({:rocksdb, Fabric})
130140
RocksDB.get("bic:epoch:pop:#{pk}", %{db: db, cf: cf.contractstate})

ex/lib/consensus/doms/entry.ex

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,8 +157,9 @@ defmodule Entry do
157157
txus = Enum.map(next_entry.txs, & TX.unpack(&1))
158158
chain_epoch = Consensus.chain_epoch()
159159
segment_vr_hash = Consensus.chain_segment_vr_hash()
160+
diff_bits = Consensus.chain_diff_bits()
160161
Enum.reduce(txus, %{}, fn(txu, batch_state)->
161-
case TXPool.validate_tx(txu, %{epoch: chain_epoch, segment_vr_hash: segment_vr_hash, batch_state: batch_state}) do
162+
case TXPool.validate_tx(txu, %{epoch: chain_epoch, segment_vr_hash: segment_vr_hash, diff_bits: diff_bits, batch_state: batch_state}) do
162163
%{error: :ok, batch_state: batch_state} -> batch_state
163164
%{error: error} when error in [:invalid_tx_nonce, :not_enough_tx_exec_balance] -> throw %{error: error}
164165
_ -> batch_state

ex/lib/consensus/doms/tx.ex

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,13 +128,14 @@ defmodule TX do
128128
def chain_valid(txu) do
129129
#TODO: once more than 1 tx allowed per entry fix this
130130
chainNonce = Consensus.chain_nonce(txu.tx.signer)
131+
chainEpoch = Consensus.chain_epoch()
131132
nonceValid = !chainNonce or txu.tx.nonce > chainNonce
132-
hasBalance = BIC.Base.exec_cost(txu) <= Consensus.chain_balance(txu.tx.signer)
133+
hasBalance = BIC.Base.exec_cost(chainEpoch, txu) <= Consensus.chain_balance(txu.tx.signer)
133134

134135
hasSol = Enum.find_value(txu.tx.actions, fn(a)-> a.function == "submit_sol" and hd(a.args) end)
135136
epochSolValid = if !hasSol do true else
136137
<<sol_epoch::32-little, _::binary>> = hasSol
137-
Consensus.chain_epoch() == sol_epoch
138+
chainEpoch == sol_epoch
138139
end
139140

140141
cond do

0 commit comments

Comments
 (0)