@@ -109,13 +109,13 @@ impl Setup {
109
109
}
110
110
111
111
/// Asserts staged code equals `expected_code`.
112
- async fn assert_staged_code ( & self , expected_code : Option < Vec < u8 > > ) {
112
+ async fn assert_staged_code ( & self , expected_code : Option < & Vec < u8 > > ) {
113
113
let staged = self
114
114
. upgradable_contract
115
115
. up_staged_code ( & self . unauth_account )
116
116
. await
117
117
. expect ( "Call to up_staged_code should succeed" ) ;
118
- assert_eq ! ( staged, expected_code) ;
118
+ assert_eq ! ( staged. as_ref ( ) , expected_code) ;
119
119
}
120
120
121
121
/// Asserts the staging duration of the `Upgradable` contract equals the `expected_duration`.
@@ -210,6 +210,14 @@ fn convert_code_to_crypto_hash(code: &[u8]) -> CryptoHash {
210
210
. expect ( "Code should be converted to CryptoHash" )
211
211
}
212
212
213
+ /// Computes the hash `code` according the to requirements of the `hash` parameter of
214
+ /// `Upgradable::up_deploy_code`.
215
+ fn convert_code_to_deploy_hash ( code : & [ u8 ] ) -> String {
216
+ use near_sdk:: base64:: Engine ;
217
+ let hash = near_sdk:: env:: sha256 ( code) ;
218
+ near_sdk:: base64:: prelude:: BASE64_STANDARD . encode ( hash)
219
+ }
220
+
213
221
/// Smoke test of contract setup.
214
222
#[ tokio:: test]
215
223
async fn test_setup ( ) -> anyhow:: Result < ( ) > {
@@ -332,7 +340,7 @@ async fn test_staging_empty_code_clears_storage() -> anyhow::Result<()> {
332
340
. up_stage_code ( & dao, code. clone ( ) )
333
341
. await ?;
334
342
assert_success_with_unit_return ( res) ;
335
- setup. assert_staged_code ( Some ( code) ) . await ;
343
+ setup. assert_staged_code ( Some ( & code) ) . await ;
336
344
337
345
// Verify staging empty code removes it.
338
346
let res = setup
@@ -436,11 +444,72 @@ async fn test_deploy_code_without_delay() -> anyhow::Result<()> {
436
444
. up_stage_code ( & dao, code. clone ( ) )
437
445
. await ?;
438
446
assert_success_with_unit_return ( res) ;
439
- setup. assert_staged_code ( Some ( code) ) . await ;
447
+ setup. assert_staged_code ( Some ( & code) ) . await ;
448
+
449
+ // Deploy staged code.
450
+ let res = setup
451
+ . upgradable_contract
452
+ . up_deploy_code ( & dao, convert_code_to_deploy_hash ( & code) , None )
453
+ . await ?;
454
+ assert_success_with_unit_return ( res) ;
455
+
456
+ Ok ( ( ) )
457
+ }
458
+
459
+ #[ tokio:: test]
460
+ async fn test_deploy_code_with_hash_success ( ) -> anyhow:: Result < ( ) > {
461
+ let worker = near_workspaces:: sandbox ( ) . await ?;
462
+ let dao = worker. dev_create_account ( ) . await ?;
463
+ let setup = Setup :: new ( worker. clone ( ) , Some ( dao. id ( ) . clone ( ) ) , None ) . await ?;
464
+
465
+ // Stage some code.
466
+ let code = vec ! [ 1 , 2 , 3 ] ;
467
+ let res = setup
468
+ . upgradable_contract
469
+ . up_stage_code ( & dao, code. clone ( ) )
470
+ . await ?;
471
+ assert_success_with_unit_return ( res) ;
472
+ setup. assert_staged_code ( Some ( & code) ) . await ;
440
473
441
474
// Deploy staged code.
442
- let res = setup. upgradable_contract . up_deploy_code ( & dao, None ) . await ?;
475
+ let hash = convert_code_to_deploy_hash ( & code) ;
476
+ let res = setup
477
+ . upgradable_contract
478
+ . up_deploy_code ( & dao, hash, None )
479
+ . await ?;
480
+ assert_success_with_unit_return ( res) ;
481
+
482
+ Ok ( ( ) )
483
+ }
484
+
485
+ /// Verifies failure of `up_deploy_code(hash, ...)` when `hash` does not correspond to the
486
+ /// hash of staged code.
487
+ #[ tokio:: test]
488
+ async fn test_deploy_code_with_hash_invalid_hash ( ) -> anyhow:: Result < ( ) > {
489
+ let worker = near_workspaces:: sandbox ( ) . await ?;
490
+ let dao = worker. dev_create_account ( ) . await ?;
491
+ let setup = Setup :: new ( worker. clone ( ) , Some ( dao. id ( ) . clone ( ) ) , None ) . await ?;
492
+
493
+ // Stage some code.
494
+ let code = vec ! [ 1 , 2 , 3 ] ;
495
+ let res = setup
496
+ . upgradable_contract
497
+ . up_stage_code ( & dao, code. clone ( ) )
498
+ . await ?;
443
499
assert_success_with_unit_return ( res) ;
500
+ setup. assert_staged_code ( Some ( & code) ) . await ;
501
+
502
+ // Deployment is aborted if an invalid hash is provided.
503
+ let res = setup
504
+ . upgradable_contract
505
+ . up_deploy_code ( & dao, "invalid_hash" . to_owned ( ) , None )
506
+ . await ?;
507
+ let actual_hash = convert_code_to_deploy_hash ( & code) ;
508
+ let expected_err = format ! (
509
+ "Upgradable: Cannot deploy due to wrong hash: expected hash: {}" ,
510
+ actual_hash
511
+ ) ;
512
+ assert_failure_with ( res, & expected_err) ;
444
513
445
514
Ok ( ( ) )
446
515
}
@@ -465,10 +534,13 @@ async fn test_deploy_code_and_call_method() -> anyhow::Result<()> {
465
534
. up_stage_code ( & dao, code. clone ( ) )
466
535
. await ?;
467
536
assert_success_with_unit_return ( res) ;
468
- setup. assert_staged_code ( Some ( code) ) . await ;
537
+ setup. assert_staged_code ( Some ( & code) ) . await ;
469
538
470
539
// Deploy staged code.
471
- let res = setup. upgradable_contract . up_deploy_code ( & dao, None ) . await ?;
540
+ let res = setup
541
+ . upgradable_contract
542
+ . up_deploy_code ( & dao, convert_code_to_deploy_hash ( & code) , None )
543
+ . await ?;
472
544
assert_success_with_unit_return ( res) ;
473
545
474
546
// The newly deployed contract defines the function `is_upgraded`. Calling it successfully
@@ -502,7 +574,7 @@ async fn test_deploy_code_with_migration() -> anyhow::Result<()> {
502
574
. up_stage_code ( & dao, code. clone ( ) )
503
575
. await ?;
504
576
assert_success_with_unit_return ( res) ;
505
- setup. assert_staged_code ( Some ( code) ) . await ;
577
+ setup. assert_staged_code ( Some ( & code) ) . await ;
506
578
507
579
// Deploy staged code and call the new contract's `migrate` method.
508
580
let function_call_args = FunctionCallArgs {
@@ -513,7 +585,11 @@ async fn test_deploy_code_with_migration() -> anyhow::Result<()> {
513
585
} ;
514
586
let res = setup
515
587
. upgradable_contract
516
- . up_deploy_code ( & dao, Some ( function_call_args) )
588
+ . up_deploy_code (
589
+ & dao,
590
+ convert_code_to_deploy_hash ( & code) ,
591
+ Some ( function_call_args) ,
592
+ )
517
593
. await ?;
518
594
assert_success_with_unit_return ( res) ;
519
595
@@ -545,7 +621,7 @@ async fn test_deploy_code_with_migration_failure_rollback() -> anyhow::Result<()
545
621
. up_stage_code ( & dao, code. clone ( ) )
546
622
. await ?;
547
623
assert_success_with_unit_return ( res) ;
548
- setup. assert_staged_code ( Some ( code) ) . await ;
624
+ setup. assert_staged_code ( Some ( & code) ) . await ;
549
625
550
626
// Deploy staged code and call the new contract's `migrate_with_failure` method.
551
627
let function_call_args = FunctionCallArgs {
@@ -556,7 +632,11 @@ async fn test_deploy_code_with_migration_failure_rollback() -> anyhow::Result<()
556
632
} ;
557
633
let res = setup
558
634
. upgradable_contract
559
- . up_deploy_code ( & dao, Some ( function_call_args) )
635
+ . up_deploy_code (
636
+ & dao,
637
+ convert_code_to_deploy_hash ( & code) ,
638
+ Some ( function_call_args) ,
639
+ )
560
640
. await ?;
561
641
assert_failure_with ( res, "Failing migration on purpose" ) ;
562
642
@@ -591,21 +671,23 @@ async fn test_deploy_code_in_batch_transaction_pitfall() -> anyhow::Result<()> {
591
671
. up_stage_code ( & dao, code. clone ( ) )
592
672
. await ?;
593
673
assert_success_with_unit_return ( res) ;
594
- setup. assert_staged_code ( Some ( code) ) . await ;
674
+ setup. assert_staged_code ( Some ( & code) ) . await ;
595
675
596
676
// Construct the function call actions to be executed in a batch transaction.
597
677
// Note that we are attaching a call to `migrate_with_failure`, which will fail.
598
678
let fn_call_deploy = near_workspaces:: operations:: Function :: new ( "up_deploy_code" )
599
- . args_json ( json ! ( { "function_call_args" : FunctionCallArgs {
679
+ . args_json ( json ! ( {
680
+ "hash" : convert_code_to_deploy_hash( & code) ,
681
+ "function_call_args" : FunctionCallArgs {
600
682
function_name: "migrate_with_failure" . to_string( ) ,
601
683
arguments: Vec :: new( ) ,
602
684
amount: NearToken :: from_yoctonear( 0 ) ,
603
685
gas: Gas :: from_tgas( 2 ) ,
604
686
} } ) )
605
- . gas ( Gas :: from_tgas ( 201 ) ) ;
687
+ . gas ( Gas :: from_tgas ( 220 ) ) ;
606
688
let fn_call_remove_code = near_workspaces:: operations:: Function :: new ( "up_stage_code" )
607
689
. args_borsh ( Vec :: < u8 > :: new ( ) )
608
- . gas ( Gas :: from_tgas ( 90 ) ) ;
690
+ . gas ( Gas :: from_tgas ( 80 ) ) ;
609
691
610
692
let res = dao
611
693
. batch ( setup. contract . id ( ) )
@@ -658,13 +740,16 @@ async fn test_deploy_code_with_delay() -> anyhow::Result<()> {
658
740
. up_stage_code ( & dao, code. clone ( ) )
659
741
. await ?;
660
742
assert_success_with_unit_return ( res) ;
661
- setup. assert_staged_code ( Some ( code) ) . await ;
743
+ setup. assert_staged_code ( Some ( & code) ) . await ;
662
744
663
745
// Let the staging duration pass.
664
746
fast_forward_beyond ( & worker, staging_duration) . await ;
665
747
666
748
// Deploy staged code.
667
- let res = setup. upgradable_contract . up_deploy_code ( & dao, None ) . await ?;
749
+ let res = setup
750
+ . upgradable_contract
751
+ . up_deploy_code ( & dao, convert_code_to_deploy_hash ( & code) , None )
752
+ . await ?;
668
753
assert_success_with_unit_return ( res) ;
669
754
670
755
Ok ( ( ) )
@@ -688,13 +773,16 @@ async fn test_deploy_code_with_delay_failure_too_early() -> anyhow::Result<()> {
688
773
. up_stage_code ( & dao, code. clone ( ) )
689
774
. await ?;
690
775
assert_success_with_unit_return ( res) ;
691
- setup. assert_staged_code ( Some ( code) ) . await ;
776
+ setup. assert_staged_code ( Some ( & code) ) . await ;
692
777
693
778
// Let some time pass but not enough.
694
779
fast_forward_beyond ( & worker, sdk_duration_from_secs ( 1 ) ) . await ;
695
780
696
781
// Verify trying to deploy staged code fails.
697
- let res = setup. upgradable_contract . up_deploy_code ( & dao, None ) . await ?;
782
+ let res = setup
783
+ . upgradable_contract
784
+ . up_deploy_code ( & dao, convert_code_to_deploy_hash ( & code) , None )
785
+ . await ?;
698
786
assert_failure_with ( res, ERR_MSG_DEPLOY_CODE_TOO_EARLY ) ;
699
787
700
788
// Verify `code` wasn't deployed by calling a function that is defined only in the initial
@@ -717,13 +805,17 @@ async fn test_deploy_code_permission_failure() -> anyhow::Result<()> {
717
805
. up_stage_code ( & dao, code. clone ( ) )
718
806
. await ?;
719
807
assert_success_with_unit_return ( res) ;
720
- setup. assert_staged_code ( Some ( code) ) . await ;
808
+ setup. assert_staged_code ( Some ( & code) ) . await ;
721
809
722
810
// Only the roles passed as `code_deployers` to the `Upgradable` derive macro may successfully
723
811
// call this method.
724
812
let res = setup
725
813
. upgradable_contract
726
- . up_deploy_code ( & setup. unauth_account , None )
814
+ . up_deploy_code (
815
+ & setup. unauth_account ,
816
+ convert_code_to_deploy_hash ( & code) ,
817
+ None ,
818
+ )
727
819
. await ?;
728
820
assert_insufficient_acl_permissions (
729
821
res,
@@ -762,7 +854,10 @@ async fn test_deploy_code_empty_failure() -> anyhow::Result<()> {
762
854
// The staging timestamp is set when staging code and removed when unstaging code. So when there
763
855
// is no code staged, there is no staging timestamp. Hence the error message regarding a missing
764
856
// staging timestamp is expected.
765
- let res = setup. upgradable_contract . up_deploy_code ( & dao, None ) . await ?;
857
+ let res = setup
858
+ . upgradable_contract
859
+ . up_deploy_code ( & dao, "" . to_owned ( ) , None )
860
+ . await ?;
766
861
assert_failure_with ( res, ERR_MSG_NO_STAGING_TS ) ;
767
862
768
863
Ok ( ( ) )
@@ -1003,14 +1098,18 @@ async fn test_acl_permission_scope() -> anyhow::Result<()> {
1003
1098
. up_stage_code ( & code_stager, code. clone ( ) )
1004
1099
. await ?;
1005
1100
assert_success_with_unit_return ( res) ;
1006
- setup. assert_staged_code ( Some ( code) ) . await ;
1101
+ setup. assert_staged_code ( Some ( & code) ) . await ;
1007
1102
1008
1103
// Verify `code_stager` is not authorized to deploy staged code. Only grantees of at least one
1009
1104
// of the roles passed as `code_deployers` to the `Upgradable` derive macro are authorized to
1010
1105
// deploy code.
1011
1106
let res = setup
1012
1107
. upgradable_contract
1013
- . up_deploy_code ( & setup. unauth_account , None )
1108
+ . up_deploy_code (
1109
+ & setup. unauth_account ,
1110
+ convert_code_to_deploy_hash ( & code) ,
1111
+ None ,
1112
+ )
1014
1113
. await ?;
1015
1114
assert_insufficient_acl_permissions (
1016
1115
res,
0 commit comments