From 071eb44bdc0fd63ec521ca0678bc4dfd68e2ff47 Mon Sep 17 00:00:00 2001 From: Aaron Gao Date: Tue, 29 Oct 2024 18:25:49 -0600 Subject: [PATCH] [aptos-framework] token bucket --- .../framework/aptos-framework/doc/overview.md | 2 + .../doc/permissioned_delegation.md | 205 +++++++++++------- .../aptos-framework/sources/account.move | 1 - .../permissioned_delegation.move | 190 ++++++++++++++++ .../account_abstraction/token_bucket.move | 172 +++++++++++++++ .../sources/aptos_account.move | 1 - 6 files changed, 491 insertions(+), 80 deletions(-) create mode 100644 aptos-move/framework/aptos-framework/sources/account_abstraction/permissioned_delegation.move create mode 100644 aptos-move/framework/aptos-framework/sources/account_abstraction/token_bucket.move diff --git a/aptos-move/framework/aptos-framework/doc/overview.md b/aptos-move/framework/aptos-framework/doc/overview.md index c0b9a253ee628..ef46811fd8a4f 100644 --- a/aptos-move/framework/aptos-framework/doc/overview.md +++ b/aptos-move/framework/aptos-framework/doc/overview.md @@ -49,6 +49,7 @@ This is the reference documentation of the Aptos framework. - [`0x1::object`](object.md#0x1_object) - [`0x1::object_code_deployment`](object_code_deployment.md#0x1_object_code_deployment) - [`0x1::optional_aggregator`](optional_aggregator.md#0x1_optional_aggregator) +- [`0x1::permissioned_delegation`](permissioned_delegation.md#0x1_permissioned_delegation) - [`0x1::permissioned_signer`](permissioned_signer.md#0x1_permissioned_signer) - [`0x1::primary_fungible_store`](primary_fungible_store.md#0x1_primary_fungible_store) - [`0x1::randomness`](randomness.md#0x1_randomness) @@ -67,6 +68,7 @@ This is the reference documentation of the Aptos framework. - [`0x1::storage_gas`](storage_gas.md#0x1_storage_gas) - [`0x1::system_addresses`](system_addresses.md#0x1_system_addresses) - [`0x1::timestamp`](timestamp.md#0x1_timestamp) +- [`0x1::token_bucket`](token_bucket.md#0x1_token_bucket) - [`0x1::transaction_context`](transaction_context.md#0x1_transaction_context) - [`0x1::transaction_fee`](transaction_fee.md#0x1_transaction_fee) - [`0x1::transaction_validation`](transaction_validation.md#0x1_transaction_validation) diff --git a/aptos-move/framework/aptos-framework/doc/permissioned_delegation.md b/aptos-move/framework/aptos-framework/doc/permissioned_delegation.md index 2edb6975559be..571c8d6624127 100644 --- a/aptos-move/framework/aptos-framework/doc/permissioned_delegation.md +++ b/aptos-move/framework/aptos-framework/doc/permissioned_delegation.md @@ -5,27 +5,76 @@ +- [Enum `HandleBundle`](#0x1_permissioned_delegation_HandleBundle) - [Resource `Delegation`](#0x1_permissioned_delegation_Delegation) - [Constants](#@Constants_0) +- [Function `fetch_handle`](#0x1_permissioned_delegation_fetch_handle) - [Function `add_permissioned_handle`](#0x1_permissioned_delegation_add_permissioned_handle) - [Function `remove_permissioned_handle`](#0x1_permissioned_delegation_remove_permissioned_handle) - [Function `permissioned_signer_by_key`](#0x1_permissioned_delegation_permissioned_signer_by_key) -- [Function `remove_permissioned_handle_by_delegate`](#0x1_permissioned_delegation_remove_permissioned_handle_by_delegate) - [Function `handle_address_by_key`](#0x1_permissioned_delegation_handle_address_by_key) - [Function `authenticate`](#0x1_permissioned_delegation_authenticate) -- [Function `get_permissioned_signer`](#0x1_permissioned_delegation_get_permissioned_signer) +- [Function `get_storable_permissioned_handle`](#0x1_permissioned_delegation_get_storable_permissioned_handle) -
use 0x1::bcs_stream;
+
use 0x1::auth_data;
+use 0x1::bcs_stream;
 use 0x1::ed25519;
 use 0x1::error;
+use 0x1::option;
 use 0x1::permissioned_signer;
 use 0x1::signer;
 use 0x1::table;
+use 0x1::token_bucket;
 
+ + +## Enum `HandleBundle` + + + +
enum HandleBundle has store
+
+ + + +
+Variants + + +
+V1 + + +
+Fields + + +
+
+handle: permissioned_signer::StorablePermissionedHandle +
+
+ +
+
+bucket: option::Option<token_bucket::Bucket> +
+
+ +
+
+ + +
+ +
+ +
+ ## Resource `Delegation` @@ -43,7 +92,7 @@
-handles: table::Table<ed25519::UnvalidatedPublicKey, permissioned_signer::StorablePermissionedHandle> +handle_bundles: table::Table<ed25519::UnvalidatedPublicKey, permissioned_delegation::HandleBundle>
@@ -103,13 +152,50 @@ + + + + +
const ERATE_LIMITED: u64 = 6;
+
+ + + + + +## Function `fetch_handle` + + + +
fun fetch_handle(bundle: &mut permissioned_delegation::HandleBundle, check_rate_limit: bool): &permissioned_signer::StorablePermissionedHandle
+
+ + + +
+Implementation + + +
inline fun fetch_handle(bundle: &mut HandleBundle, check_rate_limit: bool): &StorablePermissionedHandle {
+    let token_bucket = &mut bundle.bucket;
+    if (check_rate_limit && token_bucket.is_some()) {
+        assert!(token_bucket::request(token_bucket.borrow_mut(), 1), std::error::permission_denied(ERATE_LIMITED));
+    };
+    &bundle.handle
+}
+
+ + + +
+ ## Function `add_permissioned_handle` -
public fun add_permissioned_handle(master: &signer, key: vector<u8>, expiration_time: u64): signer
+
public fun add_permissioned_handle(master: &signer, key: vector<u8>, max_txn_per_minute: option::Option<u64>, expiration_time: u64): signer
 
@@ -121,6 +207,7 @@
public fun add_permissioned_handle(
     master: &signer,
     key: vector<u8>,
+    max_txn_per_minute: Option<u64>,
     expiration_time: u64,
 ): signer acquires Delegation {
     assert!(!is_permissioned_signer(master), error::permission_denied(ENOT_MASTER_SIGNER));
@@ -128,14 +215,15 @@
     let pubkey = ed25519::new_unvalidated_public_key_from_bytes(key);
     if (!exists<Delegation>(addr)) {
         move_to(master, Delegation {
-            handles: table::new()
+            handle_bundles: table::new()
         });
     };
-    let handles = &mut borrow_global_mut<Delegation>(addr).handles;
-    assert!(!table::contains(handles, pubkey), error::already_exists(EHANDLE_EXISTENCE));
+    let handles = &mut borrow_global_mut<Delegation>(addr).handle_bundles;
+    assert!(!handles.contains(pubkey), error::already_exists(EHANDLE_EXISTENCE));
     let handle = permissioned_signer::create_storable_permissioned_handle(master, expiration_time);
-    let permissioned_signer = permissioned_signer::signer_from_storable_permissioned(&handle);
-    table::add(handles, pubkey, handle);
+    let bucket = max_txn_per_minute.map(|capacity|token_bucket::initialize_bucket(capacity));
+    let permissioned_signer = permissioned_signer::signer_from_storable_permissioned_handle(&handle);
+    handles.add(pubkey, HandleBundle::V1 { bucket, handle });
     permissioned_signer
 }
 
@@ -166,9 +254,14 @@ assert!(!is_permissioned_signer(master), error::permission_denied(ENOT_MASTER_SIGNER)); let addr = signer::address_of(master); let pubkey = ed25519::new_unvalidated_public_key_from_bytes(key); - let handles = &mut borrow_global_mut<Delegation>(addr).handles; - assert!(table::contains(handles, pubkey), error::not_found(EHANDLE_EXISTENCE)); - permissioned_signer::destroy_storable_permissioned_handle(table::remove(handles, pubkey)); + let handle_bundles = &mut borrow_global_mut<Delegation>(addr).handle_bundles; + assert!(handle_bundles.contains(pubkey), error::not_found(EHANDLE_EXISTENCE)); + let bundle = handle_bundles.remove(pubkey); + match (bundle) { + HandleBundle::V1 { handle, bucket: _ } => { + permissioned_signer::destroy_storable_permissioned_handle(handle); + } + }; }
@@ -198,51 +291,8 @@ assert!(!is_permissioned_signer(master), error::permission_denied(ENOT_MASTER_SIGNER)); let addr = signer::address_of(master); let pubkey = ed25519::new_unvalidated_public_key_from_bytes(key); - get_permissioned_signer(addr, pubkey) -} -
- - - - - - - -## Function `remove_permissioned_handle_by_delegate` - - - -
public fun remove_permissioned_handle_by_delegate(master: address, signature: vector<u8>): permissioned_signer::StorablePermissionedHandle
-
- - - -
-Implementation - - -
public fun remove_permissioned_handle_by_delegate(
-    master: address,
-    signature: vector<u8>,
-): StorablePermissionedHandle acquires Delegation {
-    let stream = bcs_stream::new(signature);
-    let public_key = new_unvalidated_public_key_from_bytes(
-        bcs_stream::deserialize_vector<u8>(&mut stream, |x| deserialize_u8(x))
-    );
-    let signature = new_signature_from_bytes(
-        bcs_stream::deserialize_vector<u8>(&mut stream, |x| deserialize_u8(x))
-    );
-    assert!(
-        ed25519::signature_verify_strict(
-            &signature,
-            &public_key,
-            vector[1, 2, 3],
-        ),
-        error::permission_denied(EINVALID_SIGNATURE)
-    );
-    let handles = &mut borrow_global_mut<Delegation>(master).handles;
-    assert!(table::contains(handles, public_key), error::not_found(EHANDLE_EXISTENCE));
-    table::remove(handles, public_key)
+    let handle = get_storable_permissioned_handle(addr, pubkey, false);
+    permissioned_signer::signer_from_storable_permissioned_handle(handle)
 }
 
@@ -268,9 +318,8 @@
public fun handle_address_by_key(master: address, key: vector<u8>): address acquires Delegation {
     let pubkey = ed25519::new_unvalidated_public_key_from_bytes(key);
-    let handles = &borrow_global<Delegation>(master).handles;
-    assert!(table::contains(handles, pubkey), error::not_found(EHANDLE_EXISTENCE));
-    permissioned_signer::permission_address(table::borrow(handles, pubkey))
+    let handle = get_storable_permissioned_handle(master, pubkey, false);
+    permissioned_signer::permissions_storage_address(handle)
 }
 
@@ -285,7 +334,7 @@ Authorization function for account abstraction. -
public fun authenticate(account: signer, transaction_hash: vector<u8>, signature: vector<u8>): signer
+
public fun authenticate(account: signer, abstraction_auth_data: auth_data::AbstractionAuthData): signer
 
@@ -294,13 +343,9 @@ Authorization function for account abstraction. Implementation -
public fun authenticate(
-    account: signer,
-    transaction_hash: vector<u8>,
-    signature: vector<u8>
-): signer acquires Delegation {
+
public fun authenticate(account: signer, abstraction_auth_data: AbstractionAuthData): signer acquires Delegation {
     let addr = signer::address_of(&account);
-    let stream = bcs_stream::new(signature);
+    let stream = bcs_stream::new(*auth_data::authenticator(&abstraction_auth_data));
     let public_key = new_unvalidated_public_key_from_bytes(
         bcs_stream::deserialize_vector<u8>(&mut stream, |x| deserialize_u8(x))
     );
@@ -311,11 +356,12 @@ Authorization function for account abstraction.
         ed25519::signature_verify_strict(
             &signature,
             &public_key,
-            transaction_hash,
+            *auth_data::digest(&abstraction_auth_data),
         ),
         error::permission_denied(EINVALID_SIGNATURE)
     );
-    get_permissioned_signer(addr, public_key)
+    let handle = get_storable_permissioned_handle(addr, public_key, true);
+    permissioned_signer::signer_from_storable_permissioned_handle(handle)
 }
 
@@ -323,13 +369,13 @@ Authorization function for account abstraction.
- + -## Function `get_permissioned_signer` +## Function `get_storable_permissioned_handle` -
fun get_permissioned_signer(master: address, pubkey: ed25519::UnvalidatedPublicKey): signer
+
fun get_storable_permissioned_handle(master: address, pubkey: ed25519::UnvalidatedPublicKey, count_rate: bool): &permissioned_signer::StorablePermissionedHandle
 
@@ -338,12 +384,15 @@ Authorization function for account abstraction. Implementation -
inline fun get_permissioned_signer(master: address, pubkey: UnvalidatedPublicKey): signer {
+
inline fun get_storable_permissioned_handle(
+    master: address,
+    pubkey: UnvalidatedPublicKey,
+    count_rate: bool
+): &StorablePermissionedHandle {
     if (exists<Delegation>(master)) {
-        let handles = &borrow_global<Delegation>(master).handles;
-        if (table::contains(handles, pubkey)) {
-            let signer = permissioned_signer::signer_from_storable_permissioned(table::borrow(handles, pubkey));
-            signer
+        let bundles = &mut borrow_global_mut<Delegation>(master).handle_bundles;
+        if (bundles.contains(pubkey)) {
+            fetch_handle(bundles.borrow_mut(pubkey), count_rate)
         } else {
             abort error::permission_denied(EINVALID_SIGNATURE)
         }
diff --git a/aptos-move/framework/aptos-framework/sources/account.move b/aptos-move/framework/aptos-framework/sources/account.move
index 6f39af5439c09..c435c37a91695 100644
--- a/aptos-move/framework/aptos-framework/sources/account.move
+++ b/aptos-move/framework/aptos-framework/sources/account.move
@@ -1620,7 +1620,6 @@ module aptos_framework::account {
         create_account_unchecked(addr);
         register_coin(addr);
 
-        let eventhandle = &borrow_global(addr).coin_register_events;
         let event = CoinRegister { account: addr, type_info: type_info::type_of() };
 
         let events = event::emitted_events();
diff --git a/aptos-move/framework/aptos-framework/sources/account_abstraction/permissioned_delegation.move b/aptos-move/framework/aptos-framework/sources/account_abstraction/permissioned_delegation.move
new file mode 100644
index 0000000000000..f4e354d85c4a5
--- /dev/null
+++ b/aptos-move/framework/aptos-framework/sources/account_abstraction/permissioned_delegation.move
@@ -0,0 +1,190 @@
+module aptos_framework::permissioned_delegation {
+    use std::error;
+    use std::option::Option;
+    use std::signer;
+    use aptos_std::ed25519;
+    use aptos_std::ed25519::{new_signature_from_bytes, new_unvalidated_public_key_from_bytes, UnvalidatedPublicKey};
+    use aptos_std::table::{Self, Table};
+    use aptos_framework::auth_data;
+    use aptos_framework::auth_data::AbstractionAuthData;
+    use aptos_framework::bcs_stream;
+    use aptos_framework::bcs_stream::deserialize_u8;
+    use aptos_framework::permissioned_signer::{Self, is_permissioned_signer, StorablePermissionedHandle};
+    use aptos_framework::token_bucket;
+    #[test_only]
+    use std::bcs;
+    #[test_only]
+    use std::option;
+
+    const ENOT_MASTER_SIGNER: u64 = 1;
+    const EINVALID_PUBLIC_KEY: u64 = 2;
+    const EPUBLIC_KEY_NOT_FOUND: u64 = 3;
+    const EINVALID_SIGNATURE: u64 = 4;
+    const EHANDLE_EXISTENCE: u64 = 5;
+    const ERATE_LIMITED: u64 = 6;
+
+    enum HandleBundle has store {
+        V1 { handle: StorablePermissionedHandle, bucket: Option }
+    }
+
+    struct Delegation has key {
+        handle_bundles: Table
+    }
+
+    inline fun fetch_handle(bundle: &mut HandleBundle, check_rate_limit: bool): &StorablePermissionedHandle {
+        let token_bucket = &mut bundle.bucket;
+        if (check_rate_limit && token_bucket.is_some()) {
+            assert!(token_bucket::request(token_bucket.borrow_mut(), 1), std::error::permission_denied(ERATE_LIMITED));
+        };
+        &bundle.handle
+    }
+
+    public fun add_permissioned_handle(
+        master: &signer,
+        key: vector,
+        max_txn_per_minute: Option,
+        expiration_time: u64,
+    ): signer acquires Delegation {
+        assert!(!is_permissioned_signer(master), error::permission_denied(ENOT_MASTER_SIGNER));
+        let addr = signer::address_of(master);
+        let pubkey = ed25519::new_unvalidated_public_key_from_bytes(key);
+        if (!exists(addr)) {
+            move_to(master, Delegation {
+                handle_bundles: table::new()
+            });
+        };
+        let handles = &mut borrow_global_mut(addr).handle_bundles;
+        assert!(!handles.contains(pubkey), error::already_exists(EHANDLE_EXISTENCE));
+        let handle = permissioned_signer::create_storable_permissioned_handle(master, expiration_time);
+        let bucket = max_txn_per_minute.map(|capacity|token_bucket::initialize_bucket(capacity));
+        let permissioned_signer = permissioned_signer::signer_from_storable_permissioned_handle(&handle);
+        handles.add(pubkey, HandleBundle::V1 { bucket, handle });
+        permissioned_signer
+    }
+
+    public fun remove_permissioned_handle(
+        master: &signer,
+        key: vector,
+    ) acquires Delegation {
+        assert!(!is_permissioned_signer(master), error::permission_denied(ENOT_MASTER_SIGNER));
+        let addr = signer::address_of(master);
+        let pubkey = ed25519::new_unvalidated_public_key_from_bytes(key);
+        let handle_bundles = &mut borrow_global_mut(addr).handle_bundles;
+        assert!(handle_bundles.contains(pubkey), error::not_found(EHANDLE_EXISTENCE));
+        let bundle = handle_bundles.remove(pubkey);
+        match (bundle) {
+            HandleBundle::V1 { handle, bucket: _ } => {
+                permissioned_signer::destroy_storable_permissioned_handle(handle);
+            }
+        };
+    }
+
+    public fun permissioned_signer_by_key(
+        master: &signer,
+        key: vector,
+    ): signer acquires Delegation {
+        assert!(!is_permissioned_signer(master), error::permission_denied(ENOT_MASTER_SIGNER));
+        let addr = signer::address_of(master);
+        let pubkey = ed25519::new_unvalidated_public_key_from_bytes(key);
+        let handle = get_storable_permissioned_handle(addr, pubkey, false);
+        permissioned_signer::signer_from_storable_permissioned_handle(handle)
+    }
+
+    #[view]
+    public fun handle_address_by_key(master: address, key: vector): address acquires Delegation {
+        let pubkey = ed25519::new_unvalidated_public_key_from_bytes(key);
+        let handle = get_storable_permissioned_handle(master, pubkey, false);
+        permissioned_signer::permissions_storage_address(handle)
+    }
+
+    /// Authorization function for account abstraction.
+    public fun authenticate(account: signer, abstraction_auth_data: AbstractionAuthData): signer acquires Delegation {
+        let addr = signer::address_of(&account);
+        let stream = bcs_stream::new(*auth_data::authenticator(&abstraction_auth_data));
+        let public_key = new_unvalidated_public_key_from_bytes(
+            bcs_stream::deserialize_vector(&mut stream, |x| deserialize_u8(x))
+        );
+        let signature = new_signature_from_bytes(
+            bcs_stream::deserialize_vector(&mut stream, |x| deserialize_u8(x))
+        );
+        assert!(
+            ed25519::signature_verify_strict(
+                &signature,
+                &public_key,
+                *auth_data::digest(&abstraction_auth_data),
+            ),
+            error::permission_denied(EINVALID_SIGNATURE)
+        );
+        let handle = get_storable_permissioned_handle(addr, public_key, true);
+        permissioned_signer::signer_from_storable_permissioned_handle(handle)
+    }
+
+    inline fun get_storable_permissioned_handle(
+        master: address,
+        pubkey: UnvalidatedPublicKey,
+        count_rate: bool
+    ): &StorablePermissionedHandle {
+        if (exists(master)) {
+            let bundles = &mut borrow_global_mut(master).handle_bundles;
+            if (bundles.contains(pubkey)) {
+                fetch_handle(bundles.borrow_mut(pubkey), count_rate)
+            } else {
+                abort error::permission_denied(EINVALID_SIGNATURE)
+            }
+        } else {
+            abort error::permission_denied(EINVALID_SIGNATURE)
+        }
+    }
+
+    #[test_only]
+    use aptos_std::ed25519::{sign_arbitrary_bytes, generate_keys, validated_public_key_to_bytes, Signature};
+    #[test_only]
+    use aptos_framework::account::create_signer_for_test;
+    #[test_only]
+    use aptos_framework::timestamp;
+
+    #[test_only]
+    struct SignatureBundle has drop {
+        pubkey: UnvalidatedPublicKey,
+        signature: Signature,
+    }
+
+    #[test(account = @0xcafe, account_copy = @0xcafe)]
+    fun test_basics(account: signer, account_copy: signer) acquires Delegation {
+        let aptos_framework = create_signer_for_test(@aptos_framework);
+        timestamp::set_time_has_started_for_testing(&aptos_framework);
+        let (sk, vpk) = generate_keys();
+        let signature = sign_arbitrary_bytes(&sk, vector[1, 2, 3]);
+        let pubkey_bytes = validated_public_key_to_bytes(&vpk);
+        let sig_bundle = SignatureBundle {
+            pubkey: new_unvalidated_public_key_from_bytes(pubkey_bytes),
+            signature,
+        };
+        let auth_data = auth_data::create_auth_data(vector[1, 2, 3], bcs::to_bytes(&sig_bundle));
+        assert!(!is_permissioned_signer(&account), 1);
+        add_permissioned_handle(&account, pubkey_bytes, option::none(), 60);
+        let permissioned_signer = authenticate(account, auth_data);
+        assert!(is_permissioned_signer(&permissioned_signer), 2);
+        remove_permissioned_handle(&account_copy, pubkey_bytes);
+    }
+
+    #[test(account = @0xcafe, account_copy = @0xcafe, account_copy_2 = @0xcafe)]
+    #[expected_failure(abort_code = 0x50006, location = Self)]
+    fun test_rate_limit(account: signer, account_copy: signer, account_copy_2: signer) acquires Delegation {
+        let aptos_framework = create_signer_for_test(@aptos_framework);
+        timestamp::set_time_has_started_for_testing(&aptos_framework);
+        let (sk, vpk) = generate_keys();
+        let signature = sign_arbitrary_bytes(&sk, vector[1, 2, 3]);
+        let pubkey_bytes = validated_public_key_to_bytes(&vpk);
+        let sig_bundle = SignatureBundle {
+            pubkey: new_unvalidated_public_key_from_bytes(pubkey_bytes),
+            signature,
+        };
+        let auth_data = auth_data::create_auth_data(vector[1, 2, 3], bcs::to_bytes(&sig_bundle));
+        assert!(!is_permissioned_signer(&account), 1);
+        add_permissioned_handle(&account, pubkey_bytes, option::some(1), 60);
+        authenticate(account, auth_data);
+        authenticate(account_copy, auth_data);
+        remove_permissioned_handle(&account_copy_2, pubkey_bytes);
+    }
+}
diff --git a/aptos-move/framework/aptos-framework/sources/account_abstraction/token_bucket.move b/aptos-move/framework/aptos-framework/sources/account_abstraction/token_bucket.move
new file mode 100644
index 0000000000000..19877ba439708
--- /dev/null
+++ b/aptos-move/framework/aptos-framework/sources/account_abstraction/token_bucket.move
@@ -0,0 +1,172 @@
+module aptos_framework::token_bucket {
+    use aptos_std::math64;
+    use aptos_framework::timestamp;
+
+    // Struct to represent a Token Bucket that refills every minute
+    struct Bucket has key, store, copy, drop {
+        // Maximum number of transactions allowed per minute
+        capacity: u64,
+        // Current number of transactions remaining in this minute
+        tokens: u64,
+        // Transactions added per minute
+        refill_rate_per_minute: u64,
+        // Last time the bucket was refilled (in seconds)
+        last_refill_timestamp: u64,
+        // Time accumulated that hasn't yet added up to a full token
+        fractional_time_accumulated: u64,
+    }
+
+    // Public entry function to initialize a Token Bucket for transactions per minute
+    public fun initialize_bucket(capacity: u64): Bucket {
+        let bucket = Bucket {
+            capacity,
+            tokens: capacity, // Start with a full bucket (full capacity of transactions allowed)
+            refill_rate_per_minute: capacity,
+            last_refill_timestamp: timestamp::now_seconds(),
+            fractional_time_accumulated: 0, // Start with no fractional time accumulated
+        };
+        bucket
+    }
+
+    // Public function to request a transaction from the bucket
+    public fun request(bucket: &mut Bucket, num_token_requested: u64): bool {
+        refill(bucket);
+        if (bucket.tokens >= num_token_requested) {
+            bucket.tokens = bucket.tokens - num_token_requested;
+            true
+        } else {
+            false
+        }
+    }
+
+    // Function to refill the transactions in the bucket based on time passed (in minutes)
+    fun refill(bucket: &mut Bucket) {
+        let current_time = timestamp::now_seconds();
+        let time_passed = current_time - bucket.last_refill_timestamp;
+
+        // Total time passed including fractional accumulated time
+        let total_time = time_passed + bucket.fractional_time_accumulated;
+
+        // Calculate the full tokens that can be added
+        let new_tokens = total_time * bucket.refill_rate_per_minute / 60;
+
+        // Calculate the remaining fractional time
+        let remaining_fractional_time = total_time % 60;
+
+        // Refill the bucket with the full tokens
+        if (new_tokens > 0) {
+            bucket.tokens = math64::min(bucket.tokens + new_tokens, bucket.capacity);
+            bucket.last_refill_timestamp = current_time;
+        };
+
+        // Update the fractional time accumulated for the next refill cycle
+        bucket.fractional_time_accumulated = remaining_fractional_time;
+    }
+
+    #[test(aptos_framework = @0x1)]
+    fun test_initialize_bucket(aptos_framework: &signer) {
+        timestamp::set_time_has_started_for_testing(aptos_framework);
+        let bucket = initialize_bucket(10);
+        assert!(bucket.capacity == 10, 100);
+        assert!(bucket.tokens == 10, 101);
+        assert!(bucket.refill_rate_per_minute == 10, 102);
+    }
+
+    #[test(aptos_framework = @0x1)]
+    fun test_request_success(aptos_framework: &signer) {
+        timestamp::set_time_has_started_for_testing(aptos_framework);
+        let bucket = initialize_bucket(10);
+        let success = request(&mut bucket, 5);
+        assert!(success, 200); // Should succeed since 5 <= 10
+        assert!(bucket.tokens == 5, 201); // Remaining tokens should be 5
+    }
+
+    #[test(aptos_framework = @0x1)]
+    fun test_request_failure(aptos_framework: &signer) {
+        timestamp::set_time_has_started_for_testing(aptos_framework);
+        let bucket = initialize_bucket(10);
+        let success = request(&mut bucket, 15);
+        assert!(!success, 300); // Should fail since 15 > 10
+        assert!(bucket.tokens == 10, 301); // Tokens should remain unchanged
+    }
+
+    #[test(aptos_framework = @0x1)]
+    fun test_refill(aptos_framework: &signer) {
+        timestamp::set_time_has_started_for_testing(aptos_framework);
+        let bucket = initialize_bucket(10);
+
+        // Simulate a passage of 30 seconds
+        timestamp::update_global_time_for_test_secs(timestamp::now_seconds() + 30);
+
+        // Refill the bucket
+        refill(&mut bucket);
+
+        // Should have refilled 5 tokens (half of the capacity)
+        assert!(bucket.tokens == 10, 400); // Bucket was already full, so should remain full
+
+        // Request 5 tokens
+        let success = request(&mut bucket, 5);
+        assert!(success, 401); // Request should succeed
+        assert!(bucket.tokens == 5, 402); // Remaining tokens should be 5
+
+        // Simulate another passage of 30 seconds
+        timestamp::update_global_time_for_test_secs(timestamp::now_seconds() + 30);
+
+        // Refill again
+        refill(&mut bucket);
+
+        // Should refill 5 tokens, bringing back to full
+        assert!(bucket.tokens == 10, 403); // Should now be full again
+    }
+
+    #[test(aptos_framework= @0x1)]
+    fun test_fractional_accumulation(aptos_framework: &signer) {
+        timestamp::set_time_has_started_for_testing(aptos_framework);
+        let bucket = initialize_bucket(10);
+
+        // Simulate 10 seconds passing
+        timestamp::update_global_time_for_test_secs(timestamp::now_seconds() + 10);
+
+        // Refill the bucket
+        refill(&mut bucket);
+
+        // Should add 1/6th of the tokens (because 10 seconds is 1/6th of a minute)
+        assert!(bucket.tokens == 10, 500); // No token will be added since it rounds down
+        assert!(bucket.fractional_time_accumulated == 10, 501); // Accumulate the 10 seconds of fractional time
+
+        // Simulate another 50 seconds passing (total 60 seconds)
+        timestamp::update_global_time_for_test_secs(timestamp::now_seconds() + 50);
+
+        // Refill the bucket again
+        refill(&mut bucket);
+
+        // Should refill 10 tokens (full minute passed)
+        assert!(bucket.tokens == 10, 502); // Should be full now
+        assert!(bucket.fractional_time_accumulated == 0, 503); // Fractional time should reset
+    }
+
+    #[test(aptos_framework= @0x1)]
+    fun test_multiple_refills(aptos_framework: &signer) {
+        timestamp::set_time_has_started_for_testing(aptos_framework);
+        let bucket = initialize_bucket(10);
+
+        // Request 8 tokens
+        let success = request(&mut bucket, 8);
+        assert!(success, 600); // Should succeed
+        assert!(bucket.tokens == 2, 601); // Remaining tokens should be 2
+
+        // Simulate a passage of 30 seconds
+        timestamp::update_global_time_for_test_secs(timestamp::now_seconds() + 30);
+
+        // Refill the bucket
+        refill(&mut bucket);
+        assert!(bucket.tokens == 7, 602); // Should add 5 tokens (half of the refill rate)
+
+        // Simulate another 30 seconds
+        timestamp::update_global_time_for_test_secs(timestamp::now_seconds() + 30);
+
+        // Refill the bucket again
+        refill(&mut bucket);
+        assert!(bucket.tokens == 10, 603); // Should be full again
+    }
+}
diff --git a/aptos-move/framework/aptos-framework/sources/aptos_account.move b/aptos-move/framework/aptos-framework/sources/aptos_account.move
index 2a4276fd6dffc..46e6c8f280604 100644
--- a/aptos-move/framework/aptos-framework/sources/aptos_account.move
+++ b/aptos-move/framework/aptos-framework/sources/aptos_account.move
@@ -322,7 +322,6 @@ module aptos_framework::aptos_account {
         use aptos_framework::permissioned_signer;
 
         let bob = from_bcs::to_address(x"0000000000000000000000000000000000000000000000000000000000000b0b");
-        let carol = from_bcs::to_address(x"00000000000000000000000000000000000000000000000000000000000ca501");
 
         let (burn_cap, mint_cap) = aptos_framework::aptos_coin::initialize_for_test(core);
         create_account(signer::address_of(alice));