-
Notifications
You must be signed in to change notification settings - Fork 19
/
LocalBlockchain.v
546 lines (493 loc) · 20.5 KB
/
LocalBlockchain.v
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
(* This file gives two different implementations of the blockchain
execution layer defined in Blockchain.v. Both versions are execution
layers using std++'s finite maps and are thus relatively
efficient. They differ in execution order: one uses a depth-first
execution order, while the other uses a breadth-first execution order. *)
From ConCert.Execution Require Import Blockchain.
From ConCert.Execution Require Import BoundedN.
From ConCert.Execution Require Import ChainedList.
From ConCert.Execution Require Import Containers.
From ConCert.Execution Require Import Monad.
From ConCert.Execution Require Import ResultMonad.
From ConCert.Execution Require Import Serializable.
From ConCert.Utils Require Import Automation.
From ConCert.Utils Require Import Extras.
From ConCert.Utils Require Import RecordUpdate.
From Coq Require Import ZArith.
From Coq Require Import Permutation.
From Coq Require Import List.
From Coq Require Import Psatz.
Import ListNotations.
Section LocalBlockchain.
Local Open Scope bool.
Context {AddrSize : N}.
Context {DepthFirst : bool}.
Definition ContractAddrBase : N := AddrSize / 2.
Global Instance LocalChainBase : ChainBase :=
{| Address := BoundedN AddrSize;
address_eqb := BoundedN.eqb;
address_eqb_spec := BoundedN.eqb_spec;
address_is_contract a := (ContractAddrBase <=? BoundedN.to_N a)%N
|}.
Record LocalChain :=
build_local_chain {
lc_height : nat;
lc_slot : nat;
lc_fin_height : nat;
lc_account_balances : FMap Address Amount;
lc_contract_state : FMap Address SerializedValue;
lc_contracts : FMap Address WeakContract;
}.
MetaCoq Run (make_setters LocalChain).
Definition lc_to_env (lc : LocalChain) : Environment :=
{| env_chain :=
{| chain_height := lc_height lc;
current_slot := lc_slot lc;
finalized_height := lc_fin_height lc; |};
env_account_balances a := with_default 0%Z (FMap.find a (lc_account_balances lc));
env_contract_states a := FMap.find a (lc_contract_state lc);
env_contracts a := FMap.find a (lc_contracts lc); |}.
Global Coercion lc_to_env : LocalChain >-> Environment.
Section ExecuteActions.
Local Open Scope Z.
Definition add_balance
(addr : Address)
(amt : Amount)
(lc : LocalChain)
: LocalChain :=
let update opt := Some (amt + with_default 0 opt) in
lc<|lc_account_balances ::= FMap.partial_alter update addr|>.
Definition transfer_balance
(from to : Address)
(amount : Amount)
(lc : LocalChain)
: LocalChain :=
add_balance to amount (add_balance from (-amount) lc).
Definition get_new_contract_addr (lc : LocalChain) : option Address :=
BoundedN.of_N (ContractAddrBase + N.of_nat (FMap.size (lc_contracts lc))).
Definition add_contract
(addr : Address)
(wc : WeakContract)
(lc : LocalChain) : LocalChain :=
lc<|lc_contracts ::= FMap.add addr wc|>.
Definition set_contract_state
(addr : Address)
(state : SerializedValue)
(lc : LocalChain) : LocalChain :=
lc<|lc_contract_state ::= FMap.add addr state|>.
Definition weak_error_to_error_init
(r : result SerializedValue SerializedValue)
: result SerializedValue ActionEvaluationError :=
bind_error (fun err => init_failed err) r.
Definition weak_error_to_error_receive
(r : result (SerializedValue * list ActionBody) SerializedValue)
: result (SerializedValue * list ActionBody) ActionEvaluationError :=
bind_error (fun err => receive_failed err) r.
Definition send_or_call
(origin : Address)
(from to : Address)
(amount : Amount)
(msg : option SerializedValue)
(lc : LocalChain)
: result (list Action * LocalChain) ActionEvaluationError :=
do if amount <? 0 then Err (amount_negative amount) else Ok tt;
do if amount >? env_account_balances lc from then Err (amount_too_high amount) else Ok tt;
match FMap.find to lc.(lc_contracts) with
| None =>
(* Fail if sending a message to address without contract *)
do if address_is_contract to then Err (no_such_contract to) else Ok tt;
match msg with
| None => Ok ([], transfer_balance from to amount lc)
| Some msg => Err (no_such_contract to)
end
| Some wc =>
do state <- result_of_option (env_contract_states lc to) internal_error;
let lc := transfer_balance from to amount lc in
let ctx := build_ctx origin from to (env_account_balances lc to) amount in
do '(new_state, new_actions) <- weak_error_to_error_receive (wc_receive wc lc ctx state msg);
let lc := set_contract_state to new_state lc in
Ok (map (build_act origin to) new_actions, lc)
end.
Definition deploy_contract
(origin : Address)
(from : Address)
(amount : Amount)
(wc : WeakContract)
(setup : SerializedValue)
(lc : LocalChain)
: result (list Action * LocalChain) ActionEvaluationError :=
do if amount <? 0 then Err (amount_negative amount) else Ok tt;
do if amount >? env_account_balances lc from then Err (amount_too_high amount) else Ok tt;
do contract_addr <- result_of_option (get_new_contract_addr lc) too_many_contracts;
do match FMap.find contract_addr (lc_contracts lc) with
| Some _ => Err internal_error
| None => Ok tt
end;
let lc := transfer_balance from contract_addr amount lc in
let ctx := build_ctx origin from contract_addr amount amount in
do state <- weak_error_to_error_init (wc_init wc lc ctx setup);
let lc := add_contract contract_addr wc lc in
let lc := set_contract_state contract_addr state lc in
Ok ([], lc).
Local Open Scope nat.
Definition execute_action
(act : Action)
(lc : LocalChain)
: result (list Action * LocalChain) ActionEvaluationError :=
match act with
| build_act origin from (act_transfer to amount) =>
send_or_call origin from to amount None lc
| build_act origin from (act_deploy amount wc setup) =>
deploy_contract origin from amount wc setup lc
| build_act origin from (act_call to amount msg) =>
send_or_call origin from to amount (Some msg) lc
end.
Fixpoint execute_actions
(count : nat)
(acts : list Action)
(lc : LocalChain)
(depth_first : bool)
: result LocalChain AddBlockError :=
match count, acts with
| _, [] => Ok lc
| 0, _ => Err action_evaluation_depth_exceeded
| S count, act :: acts =>
match execute_action act lc with
| Ok (next_acts, lc) =>
let acts := if depth_first
then next_acts ++ acts
else acts ++ next_acts in
execute_actions count acts lc depth_first
| Err act_err =>
Err (action_evaluation_error act act_err)
end
end.
Lemma transfer_balance_equiv
(from to : Address)
(amount : Amount)
(lc : LocalChain)
(env : Environment) :
EnvironmentEquiv lc env ->
EnvironmentEquiv
(transfer_balance from to amount lc)
(Blockchain.transfer_balance from to amount env).
Proof.
intros <-.
apply build_env_equiv; auto.
cbn.
intros addr.
unfold Amount in *.
destruct_address_eq; subst;
repeat
(try rewrite FMap.find_partial_alter;
try rewrite FMap.find_partial_alter_ne by auto;
cbn); lia.
Defined.
Lemma set_contract_state_equiv addr state (lc : LocalChain) (env : Environment) :
EnvironmentEquiv lc env ->
EnvironmentEquiv
(set_contract_state addr state lc)
(Blockchain.set_contract_state addr state env).
Proof.
intros <-.
apply build_env_equiv; auto.
intros addr'.
cbn.
unfold set_chain_contract_state.
destruct_address_eq.
- subst. now rewrite FMap.find_add.
- rewrite FMap.find_add_ne; auto.
Defined.
Lemma add_contract_equiv addr wc (lc : LocalChain) (env : Environment) :
EnvironmentEquiv lc env ->
EnvironmentEquiv
(add_contract addr wc lc)
(Blockchain.add_contract addr wc env).
Proof.
intros <-.
apply build_env_equiv; auto.
intros addr'.
cbn.
destruct_address_eq.
- subst. now rewrite FMap.find_add.
- rewrite FMap.find_add_ne; auto.
Defined.
Local Open Scope Z.
Lemma gtb_le x y :
x >? y = false ->
x <= y.
Proof.
intros H.
rewrite Z.gtb_ltb in H.
apply Z.ltb_ge.
auto.
Defined.
Lemma ltb_ge x y :
x <? y = false ->
x >= y.
Proof.
intros H.
apply Z.ltb_ge in H.
lia.
Defined.
Local Hint Resolve gtb_le ltb_ge : core.
Lemma send_or_call_step origin from to amount msg act lc_before new_acts lc_after :
send_or_call origin from to amount msg lc_before = Ok (new_acts, lc_after) ->
act = build_act origin from (match msg with
| None => act_transfer to amount
| Some msg => act_call to amount msg
end) ->
ActionEvaluation lc_before act lc_after new_acts.
Proof.
intros sent act_eq.
unfold send_or_call in sent.
destruct (Z.ltb amount 0) eqn:amount_nonnegative;
[cbn in *; congruence|].
destruct (Z.gtb amount (env_account_balances lc_before from)) eqn:balance_enough;
[cbn in *; congruence|].
destruct (FMap.find to (lc_contracts lc_before)) as [wc|] eqn:to_contract.
- (* there is a contract at destination, so do call *)
destruct (env_contract_states _ _) as [prev_state|] eqn:prev_state_eq;
[|cbn in *; congruence].
cbn -[lc_to_env] in *.
destruct (wc_receive wc _ _ _ _) as [[new_state resp_acts]|] eqn:receive;
[|cbn in *; congruence].
apply (eval_call origin from to amount wc msg prev_state new_state resp_acts);
try solve [cbn in *; auto; congruence].
+ cbn in sent.
inversion_clear sent.
rewrite <- receive.
auto.
+ inversion sent; subst;
now apply set_contract_state_equiv, transfer_balance_equiv.
- (* no contract at destination, so msg should be empty *)
destruct (address_is_contract to) eqn:addr_format; cbn in *; try congruence.
destruct msg; cbn in *; try congruence.
assert (new_acts = []) by congruence; subst new_acts.
apply (eval_transfer origin from to amount); auto.
inversion sent; subst; now apply transfer_balance_equiv.
Defined.
Lemma get_new_contract_addr_is_contract_addr lc addr :
get_new_contract_addr lc = Some addr ->
address_is_contract addr = true.
Proof.
intros get.
unfold get_new_contract_addr in get.
pose proof (BoundedN.of_N_some get) as eq.
destruct addr as [addr prf].
cbn in *; rewrite eq.
match goal with
| [|- context[N.leb ?a ?b = true]] => destruct (N.leb_spec a b); auto; lia
end.
Defined.
Local Hint Resolve get_new_contract_addr_is_contract_addr : core.
Lemma deploy_contract_step origin from amount wc setup act lc_before new_acts lc_after :
deploy_contract origin from amount wc setup lc_before = Ok (new_acts, lc_after) ->
act = build_act origin from (act_deploy amount wc setup) ->
ActionEvaluation lc_before act lc_after new_acts.
Proof.
intros dep act_eq.
unfold deploy_contract in dep.
destruct (Z.ltb amount 0) eqn:amount_nonnegative;
[cbn in *; congruence|].
destruct (Z.gtb amount (env_account_balances lc_before from)) eqn:balance_enough;
[cbn in *; congruence|].
destruct (get_new_contract_addr lc_before) as [contract_addr|] eqn:new_contract_addr;
[|cbn in *; congruence].
cbn -[incoming_txs] in dep.
destruct (FMap.find _ _) eqn:no_contracts; [cbn in *; congruence|].
destruct (wc_init _ _ _ _) as [state|] eqn:recv; [|cbn in *; congruence].
cbn in dep.
assert (new_acts = []) by congruence; subst new_acts.
apply (eval_deploy origin from contract_addr amount wc setup state); eauto.
inversion dep; subst lc_after.
now apply set_contract_state_equiv, add_contract_equiv, transfer_balance_equiv.
Defined.
Local Hint Resolve send_or_call_step deploy_contract_step : core.
Lemma execute_action_step
(act : Action)
(new_acts : list Action)
(lc_before : LocalChain)
(lc_after : LocalChain) :
execute_action act lc_before = Ok (new_acts, lc_after) ->
ActionEvaluation lc_before act lc_after new_acts.
Proof.
intros exec.
unfold execute_action in exec.
destruct act as [orig from body].
destruct body as [to amount|to amount msg|amount wc setup]; eauto.
Defined.
Hint Constructors ChainStep : core.
Hint Constructors ChainedList : core.
Hint Unfold ChainTrace : core.
Lemma execute_actions_trace count acts (lc lc_final : LocalChain) df
(trace : ChainTrace empty_state (build_chain_state lc acts)) :
execute_actions count acts lc df = Ok lc_final ->
ChainTrace empty_state (build_chain_state lc_final []).
Proof.
revert acts lc lc_final trace.
induction count as [| count IH]; intros acts lc lc_final trace exec; cbn in *.
- destruct acts; congruence.
- destruct acts as [|x xs]; try congruence.
destruct (execute_action x lc) as [[new_acts lc_after]|] eqn:exec_once;
cbn in *; try congruence.
set (step := execute_action_step _ _ _ _ exec_once).
refine (IH _ _ _ _ exec).
destruct df.
+ (* depth-first case *)
eauto.
+ (* breadth-first case. Insert permute step. *)
assert (Permutation (new_acts ++ xs) (xs ++ new_acts)) by perm_simplify.
cut (ChainTrace
empty_state
(build_chain_state lc_after (new_acts ++ xs))); eauto.
intros.
econstructor; eauto.
constructor; eauto.
constructor; eauto.
Defined.
End ExecuteActions.
Definition lc_initial : LocalChain :=
{| lc_height := 0;
lc_slot := 0;
lc_fin_height := 0;
lc_account_balances := FMap.empty;
lc_contract_state := FMap.empty;
lc_contracts := FMap.empty; |}.
Record LocalChainBuilder :=
build_local_chain_builder {
lcb_lc : LocalChain;
lcb_trace : ChainTrace empty_state (build_chain_state lcb_lc []);
}.
Definition lcb_initial : LocalChainBuilder :=
{| lcb_lc := lc_initial; lcb_trace := clnil |}.
Definition validate_header (header : BlockHeader) (chain : Chain) : bool :=
(block_height header =? S (chain_height chain))
&& (current_slot chain <? block_slot header)
&& (finalized_height chain <=? block_finalized_height header)
&& (block_finalized_height header <? block_height header)
&& address_not_contract (block_creator header)
&& (block_reward header >=? 0)%Z.
Lemma validate_header_valid header chain :
validate_header header chain = true ->
IsValidNextBlock header chain.
Proof.
intros valid.
unfold validate_header in valid.
repeat
(match goal with
| [H: context[Nat.eqb ?a ?b] |- _] => destruct (Nat.eqb_spec a b)
| [H: context[Nat.ltb ?a ?b] |- _] => destruct (Nat.ltb_spec a b)
| [H: context[Nat.leb ?a ?b] |- _] => destruct (Nat.leb_spec a b)
| [H: context[Z.geb ?a ?b] |- _] => destruct (Z.geb_spec a b)
end; [|repeat rewrite Bool.andb_false_r in valid; cbn in valid; congruence]).
destruct (address_not_contract (block_creator header)) eqn:to_acc;
[|cbn in valid; congruence].
apply Bool.negb_true_iff in to_acc.
apply build_is_valid_next_block; cbn; auto.
lia.
Defined.
Definition find_origin_neq_from (actions : list Action) : option Action :=
find (fun act => address_neqb (act_origin act) (act_from act)) actions.
Lemma validate_origin_neq_from_valid actions :
find_origin_neq_from actions = None ->
Forall (fun act => address_eqb (act_origin act) (act_from act) = true) actions.
Proof.
intros find_none.
unfold find_origin_neq_from in find_none.
specialize (List.find_none _ _ find_none) as all_nin.
cbn in *.
assert (all_nin0 : forall x, In x actions -> (act_origin x =? act_from x)%address = true).
{ intros. now apply ssrbool.negbFE. }
now apply Forall_forall in all_nin0.
Defined.
Definition find_invalid_root_action (actions : list Action) : option Action :=
find (fun act => address_is_contract (act_from act)) actions.
Lemma validate_actions_valid actions :
find_invalid_root_action actions = None ->
Forall (fun act => act_is_from_account act) actions.
Proof.
intros find_none.
unfold find_invalid_root_action in find_none.
specialize (List.find_none _ _ find_none) as all_nin.
unfold act_is_from_account.
now apply Forall_forall in all_nin.
Defined.
Definition add_new_block (header : BlockHeader) (lc : LocalChain) : LocalChain :=
let lc := add_balance (block_creator header) (block_reward header) lc in
lc<|lc_height := block_height header|>
<|lc_slot := block_slot header|>
<|lc_fin_height := block_finalized_height header|>.
Lemma add_new_block_equiv header (lc : LocalChain) (env : Environment) :
EnvironmentEquiv lc env ->
EnvironmentEquiv
(add_new_block header lc)
(Blockchain.add_new_block_to_env header env).
Proof.
intros eq.
apply build_env_equiv; try apply eq; auto.
intros addr.
cbn.
unfold Blockchain.add_balance.
destruct_address_eq.
- subst. rewrite FMap.find_partial_alter.
cbn.
f_equal.
apply eq.
- rewrite FMap.find_partial_alter_ne; auto.
apply eq.
Defined.
(* The computational bits of adding a block *)
Definition add_block_exec
(depth_first : bool)
(lc : LocalChain)
(header : BlockHeader)
(actions : list Action) : result LocalChain AddBlockError :=
do (if validate_header header lc then Ok tt else Err (invalid_header header));
do (match find_origin_neq_from actions with
| Some act => Err (origin_from_mismatch act)
| None => Ok tt
end);
do (match find_invalid_root_action actions with
| Some act => Err (invalid_root_action act)
| None => Ok tt
end);
let lc := add_new_block header lc in
execute_actions 1000 actions lc depth_first.
Local Hint Resolve validate_header_valid validate_actions_valid validate_origin_neq_from_valid : core.
(* Adds a block to the chain by executing the specified chain actions.
Returns the new chain if the execution succeeded (for instance,
transactions need enough funds, contracts should not reject, etc. *)
Definition add_block
(depth_first : bool)
(lcb : LocalChainBuilder)
(header : BlockHeader)
(actions : list Action) : result LocalChainBuilder AddBlockError.
Proof.
set (lcopt := add_block_exec depth_first (lcb_lc lcb) header actions).
unfold add_block_exec in lcopt.
destruct lcopt as [lc|e] eqn:exec; [|exact (Err e)].
subst lcopt.
destruct (validate_header _) eqn:validate; [|cbn in exec; congruence].
destruct (find_origin_neq_from _) eqn:no_origin_neq_from; [cbn in exec; congruence|].
destruct (find_invalid_root_action _) eqn:no_invalid_root_act; [cbn in exec; congruence|].
destruct lcb as [prev_lc_end prev_lcb_trace].
refine (Ok {| lcb_lc := lc; lcb_trace := _ |}).
cbn -[execute_actions] in exec.
refine (execute_actions_trace _ _ _ _ _ _ exec).
refine (snoc prev_lcb_trace _).
apply (step_block _ _ header); auto.
apply add_new_block_equiv.
reflexivity.
Defined.
Definition LocalChainBuilderImpl : ChainBuilderType :=
{| builder_type := LocalChainBuilder;
builder_initial := lcb_initial;
builder_env lcb := lcb_lc lcb;
builder_add_block := add_block DepthFirst;
builder_trace := lcb_trace; |}.
End LocalBlockchain.
Arguments LocalChainBase : clear implicits.
Arguments LocalChainBuilder : clear implicits.
Arguments LocalChainBuilderImpl : clear implicits.
Arguments lcb_initial : clear implicits.