|
17 | 17 | module aptos_framework::object {
|
18 | 18 | use std::bcs;
|
19 | 19 | use std::error;
|
| 20 | + use std::features; |
20 | 21 | use std::hash;
|
21 | 22 | use std::signer;
|
22 | 23 | use std::vector;
|
@@ -532,18 +533,11 @@ module aptos_framework::object {
|
532 | 533 |
|
533 | 534 | let current_address = object.owner;
|
534 | 535 | let count = 0;
|
535 |
| - while ({ |
536 |
| - spec { |
537 |
| - invariant count < MAXIMUM_OBJECT_NESTING; |
538 |
| - invariant forall i in 0..count: |
539 |
| - exists<ObjectCore>(current_address) && global<ObjectCore>(current_address).allow_ungated_transfer; |
540 |
| - // invariant forall i in 0..count: |
541 |
| - // current_address == get_transfer_address(global<ObjectCore>(destination).owner, i); |
| 536 | + while (owner != current_address) { |
| 537 | + count = count + 1; |
| 538 | + if (features::max_object_nesting_check_enabled()) { |
| 539 | + assert!(count < MAXIMUM_OBJECT_NESTING, error::out_of_range(EMAXIMUM_NESTING)) |
542 | 540 | };
|
543 |
| - owner != current_address |
544 |
| - }) { |
545 |
| - let count = count + 1; |
546 |
| - assert!(count < MAXIMUM_OBJECT_NESTING, error::out_of_range(EMAXIMUM_NESTING)); |
547 | 541 | // At this point, the first object exists and so the more likely case is that the
|
548 | 542 | // object's owner is not an object. So we return a more sensible error.
|
549 | 543 | assert!(
|
@@ -623,16 +617,11 @@ module aptos_framework::object {
|
623 | 617 | let current_address = object.owner;
|
624 | 618 |
|
625 | 619 | let count = 0;
|
626 |
| - while ({ |
627 |
| - spec { |
628 |
| - invariant count < MAXIMUM_OBJECT_NESTING; |
629 |
| - invariant forall i in 0..count: |
630 |
| - owner != current_address && exists<ObjectCore>(current_address); |
| 620 | + while (owner != current_address) { |
| 621 | + count = count + 1; |
| 622 | + if (features::max_object_nesting_check_enabled()) { |
| 623 | + assert!(count < MAXIMUM_OBJECT_NESTING, error::out_of_range(EMAXIMUM_NESTING)) |
631 | 624 | };
|
632 |
| - owner != current_address |
633 |
| - }) { |
634 |
| - let count = count + 1; |
635 |
| - assert!(count < MAXIMUM_OBJECT_NESTING, error::out_of_range(EMAXIMUM_NESTING)); |
636 | 625 | if (!exists<ObjectCore>(current_address)) {
|
637 | 626 | return false
|
638 | 627 | };
|
@@ -816,4 +805,106 @@ module aptos_framework::object {
|
816 | 805 | let (_, hero) = create_hero(creator);
|
817 | 806 | unburn(creator, hero);
|
818 | 807 | }
|
| 808 | + |
| 809 | + #[test_only] |
| 810 | + inline fun create_simple_object(creator: &signer, seed: vector<u8>): Object<ObjectCore> { |
| 811 | + object_from_constructor_ref<ObjectCore>(&create_named_object(creator, seed)) |
| 812 | + } |
| 813 | + |
| 814 | + #[test(creator = @0x123)] |
| 815 | + #[expected_failure(abort_code = 131078, location = Self)] |
| 816 | + fun test_exceeding_maximum_object_nesting_owns_should_fail(creator: &signer) acquires ObjectCore { |
| 817 | + let feature = features::get_max_object_nesting_check_feature(); |
| 818 | + let fx = account::create_signer_for_test(@0x1); |
| 819 | + features::change_feature_flags(&fx, vector[feature], vector[]); |
| 820 | + |
| 821 | + let obj1 = create_simple_object(creator, b"1"); |
| 822 | + let obj2 = create_simple_object(creator, b"2"); |
| 823 | + let obj3 = create_simple_object(creator, b"3"); |
| 824 | + let obj4 = create_simple_object(creator, b"4"); |
| 825 | + let obj5 = create_simple_object(creator, b"5"); |
| 826 | + let obj6 = create_simple_object(creator, b"6"); |
| 827 | + let obj7 = create_simple_object(creator, b"7"); |
| 828 | + let obj8 = create_simple_object(creator, b"8"); |
| 829 | + let obj9 = create_simple_object(creator, b"9"); |
| 830 | + |
| 831 | + transfer(creator, obj1, object_address(&obj2)); |
| 832 | + transfer(creator, obj2, object_address(&obj3)); |
| 833 | + transfer(creator, obj3, object_address(&obj4)); |
| 834 | + transfer(creator, obj4, object_address(&obj5)); |
| 835 | + transfer(creator, obj5, object_address(&obj6)); |
| 836 | + transfer(creator, obj6, object_address(&obj7)); |
| 837 | + transfer(creator, obj7, object_address(&obj8)); |
| 838 | + transfer(creator, obj8, object_address(&obj9)); |
| 839 | + |
| 840 | + assert!(owns(obj9, signer::address_of(creator)), 1); |
| 841 | + assert!(owns(obj8, signer::address_of(creator)), 1); |
| 842 | + assert!(owns(obj7, signer::address_of(creator)), 1); |
| 843 | + assert!(owns(obj6, signer::address_of(creator)), 1); |
| 844 | + assert!(owns(obj5, signer::address_of(creator)), 1); |
| 845 | + assert!(owns(obj4, signer::address_of(creator)), 1); |
| 846 | + assert!(owns(obj3, signer::address_of(creator)), 1); |
| 847 | + assert!(owns(obj2, signer::address_of(creator)), 1); |
| 848 | + |
| 849 | + // Calling `owns` should fail as the nesting is too deep. |
| 850 | + assert!(owns(obj1, signer::address_of(creator)), 1); |
| 851 | + } |
| 852 | + |
| 853 | + #[test(creator = @0x123)] |
| 854 | + #[expected_failure(abort_code = 131078, location = Self)] |
| 855 | + fun test_exceeding_maximum_object_nesting_transfer_should_fail(creator: &signer) acquires ObjectCore { |
| 856 | + let feature = features::get_max_object_nesting_check_feature(); |
| 857 | + let fx = account::create_signer_for_test(@0x1); |
| 858 | + features::change_feature_flags(&fx, vector[feature], vector[]); |
| 859 | + |
| 860 | + let obj1 = create_simple_object(creator, b"1"); |
| 861 | + let obj2 = create_simple_object(creator, b"2"); |
| 862 | + let obj3 = create_simple_object(creator, b"3"); |
| 863 | + let obj4 = create_simple_object(creator, b"4"); |
| 864 | + let obj5 = create_simple_object(creator, b"5"); |
| 865 | + let obj6 = create_simple_object(creator, b"6"); |
| 866 | + let obj7 = create_simple_object(creator, b"7"); |
| 867 | + let obj8 = create_simple_object(creator, b"8"); |
| 868 | + let obj9 = create_simple_object(creator, b"9"); |
| 869 | + |
| 870 | + transfer(creator, obj1, object_address(&obj2)); |
| 871 | + transfer(creator, obj2, object_address(&obj3)); |
| 872 | + transfer(creator, obj3, object_address(&obj4)); |
| 873 | + transfer(creator, obj4, object_address(&obj5)); |
| 874 | + transfer(creator, obj5, object_address(&obj6)); |
| 875 | + transfer(creator, obj6, object_address(&obj7)); |
| 876 | + transfer(creator, obj7, object_address(&obj8)); |
| 877 | + transfer(creator, obj8, object_address(&obj9)); |
| 878 | + |
| 879 | + // This should fail as the nesting is too deep. |
| 880 | + transfer(creator, obj1, @0x1); |
| 881 | + } |
| 882 | + |
| 883 | + #[test(creator = @0x123)] |
| 884 | + #[expected_failure(abort_code = 131078, location = Self)] |
| 885 | + fun test_cyclic_ownership_transfer_should_fail(creator: &signer) acquires ObjectCore { |
| 886 | + let feature = features::get_max_object_nesting_check_feature(); |
| 887 | + let fx = account::create_signer_for_test(@0x1); |
| 888 | + features::change_feature_flags(&fx, vector[feature], vector[]); |
| 889 | + |
| 890 | + let obj1 = create_simple_object(creator, b"1"); |
| 891 | + // This creates a cycle (self-loop) in ownership. |
| 892 | + transfer(creator, obj1, object_address(&obj1)); |
| 893 | + // This should fails as the ownership is cyclic. |
| 894 | + transfer(creator, obj1, object_address(&obj1)); |
| 895 | + } |
| 896 | + |
| 897 | + #[test(creator = @0x123)] |
| 898 | + #[expected_failure(abort_code = 131078, location = Self)] |
| 899 | + fun test_cyclic_ownership_owns_should_fail(creator: &signer) acquires ObjectCore { |
| 900 | + let feature = features::get_max_object_nesting_check_feature(); |
| 901 | + let fx = account::create_signer_for_test(@0x1); |
| 902 | + features::change_feature_flags(&fx, vector[feature], vector[]); |
| 903 | + |
| 904 | + let obj1 = create_simple_object(creator, b"1"); |
| 905 | + // This creates a cycle (self-loop) in ownership. |
| 906 | + transfer(creator, obj1, object_address(&obj1)); |
| 907 | + // This should fails as the ownership is cyclic. |
| 908 | + let _ = owns(obj1, signer::address_of(creator)); |
| 909 | + } |
819 | 910 | }
|
0 commit comments