From 1755e84989d29b990e0ee5b20bd2b86ce68ce928 Mon Sep 17 00:00:00 2001
From: angieyth <123419931+angieyth@users.noreply.github.com>
Date: Thu, 23 Feb 2023 15:48:09 -0800
Subject: [PATCH] [transaction test] refactoring publishing flow to
 test_context (#6529)

* move helper functions to text_context.rs and fix some typo

* remove a line

* [transaction test] adding new move module for transaction tests and rewrite the test test_get_txn_execute_failed_by_entry_function_execution_failure

* [transaction test] move create_account() to test_context to share with other tests

* [transaction tests] fix lint

* address comments and further refactoring

* rebase and make changes to publish_packages

* refactor root_account and make it async base on #5894

* fix lint errors

* fix merge conflicts
---
 Cargo.lock                                    |   2 +
 api/src/tests/accounts_test.rs                |  16 +-
 api/src/tests/modules.rs                      |  14 +-
 api/src/tests/resource_groups.rs              | 186 ++++--------------
 api/src/tests/state_test.rs                   |  16 +-
 api/src/tests/string_resource_test.rs         |   5 +-
 api/src/tests/transactions_test.rs            | 113 +++++------
 api/src/tests/view_function.rs                |   8 +-
 api/test-context/Cargo.toml                   |   2 +
 api/test-context/move/Move.toml               |   9 +
 .../move/sources/entry_func_fail.move         |   6 +
 api/test-context/src/test_context.rs          | 163 +++++++++++----
 12 files changed, 256 insertions(+), 284 deletions(-)
 create mode 100644 api/test-context/move/Move.toml
 create mode 100644 api/test-context/move/sources/entry_func_fail.move

diff --git a/Cargo.lock b/Cargo.lock
index 24d120c27908c..2610c2550fdd9 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -288,6 +288,7 @@ dependencies = [
  "aptos-db",
  "aptos-executor",
  "aptos-executor-types",
+ "aptos-framework",
  "aptos-genesis",
  "aptos-mempool",
  "aptos-mempool-notifications",
@@ -297,6 +298,7 @@ dependencies = [
  "aptos-types",
  "aptos-vm",
  "aptos-vm-validator",
+ "bcs 0.1.4 (git+https://github.com/aptos-labs/bcs.git?rev=d31fab9d81748e2594be5cd5cdf845786a30562d)",
  "bytes 1.2.1",
  "goldenfile",
  "hyper",
diff --git a/api/src/tests/accounts_test.rs b/api/src/tests/accounts_test.rs
index e8ba7dfa929c1..b958178fd209e 100644
--- a/api/src/tests/accounts_test.rs
+++ b/api/src/tests/accounts_test.rs
@@ -112,12 +112,12 @@ async fn test_account_modules_structs() {
 async fn test_get_account_resources_by_ledger_version() {
     let mut context = new_test_context(current_function_name!());
     let account = context.gen_account();
-    let txn = context.create_user_account(&account);
+    let txn = context.create_user_account(&account).await;
     context.commit_block(&vec![txn.clone()]).await;
 
     let ledger_version_1_resources = context
         .get(&account_resources(
-            &context.root_account().address().to_hex_literal(),
+            &context.root_account().await.address().to_hex_literal(),
         ))
         .await;
     let root_account = find_value(&ledger_version_1_resources, |f| {
@@ -127,7 +127,7 @@ async fn test_get_account_resources_by_ledger_version() {
 
     let ledger_version_0_resources = context
         .get(&account_resources_with_ledger_version(
-            &context.root_account().address().to_hex_literal(),
+            &context.root_account().await.address().to_hex_literal(),
             0,
         ))
         .await;
@@ -143,7 +143,7 @@ async fn test_get_account_resources_by_too_large_ledger_version() {
     let resp = context
         .expect_status_code(404)
         .get(&account_resources_with_ledger_version(
-            &context.root_account().address().to_hex_literal(),
+            &context.root_account().await.address().to_hex_literal(),
             1000000000000000000,
         ))
         .await;
@@ -156,7 +156,7 @@ async fn test_get_account_resources_by_invalid_ledger_version() {
     let resp = context
         .expect_status_code(400)
         .get(&account_resources_with_ledger_version(
-            &context.root_account().address().to_hex_literal(),
+            &context.root_account().await.address().to_hex_literal(),
             -1,
         ))
         .await;
@@ -169,7 +169,7 @@ async fn test_get_account_resources_by_invalid_ledger_version() {
 async fn test_get_account_modules_by_ledger_version() {
     let mut context = new_test_context(current_function_name!());
     let code = "a11ceb0b0300000006010002030205050703070a0c0816100c260900000001000100000102084d794d6f64756c650269640000000000000000000000000b1e55ed00010000000231010200";
-    let mut root_account = context.root_account();
+    let mut root_account = context.root_account().await;
     let txn = root_account.sign_with_transaction_builder(
         context
             .transaction_factory()
@@ -178,7 +178,7 @@ async fn test_get_account_modules_by_ledger_version() {
     context.commit_block(&vec![txn.clone()]).await;
     let modules = context
         .get(&account_modules(
-            &context.root_account().address().to_hex_literal(),
+            &context.root_account().await.address().to_hex_literal(),
         ))
         .await;
 
@@ -186,7 +186,7 @@ async fn test_get_account_modules_by_ledger_version() {
 
     let modules = context
         .get(&account_modules_with_ledger_version(
-            &context.root_account().address().to_hex_literal(),
+            &context.root_account().await.address().to_hex_literal(),
             0,
         ))
         .await;
diff --git a/api/src/tests/modules.rs b/api/src/tests/modules.rs
index c7da52fbf1704..c72956a822057 100644
--- a/api/src/tests/modules.rs
+++ b/api/src/tests/modules.rs
@@ -1,29 +1,25 @@
 // Copyright © Aptos Foundation
 // SPDX-License-Identifier: Apache-2.0
 
-use super::{
-    new_test_context,
-    resource_groups::{build_package, publish_package},
-};
-use aptos_api_test_context::current_function_name;
+use super::new_test_context;
+use aptos_api_test_context::{current_function_name, TestContext};
 use std::path::PathBuf;
 
 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
 async fn test_abi() {
     let mut context = new_test_context(current_function_name!());
-    let mut root_account = context.root_account();
+    let mut root_account = context.root_account().await;
     let mut account = context.gen_account();
     let txn = context.create_user_account_by(&mut root_account, &account);
     context.commit_block(&vec![txn]).await;
 
     // Publish packages
     let named_addresses = vec![("abi".to_string(), account.address())];
-    // TODO: Switch to build_package and publish_package in test_context once they're moved there.
     let txn = futures::executor::block_on(async move {
         let path = PathBuf::from(std::env!("CARGO_MANIFEST_DIR")).join("src/tests/move/pack_abi");
-        build_package(path, named_addresses)
+        TestContext::build_package(path, named_addresses)
     });
-    publish_package(&mut context, &mut account, txn).await;
+    context.publish_package(&mut account, txn).await;
 
     // Get abi.
     let modules = context
diff --git a/api/src/tests/resource_groups.rs b/api/src/tests/resource_groups.rs
index 0d54acd6e81ee..a2e1e4cbeb147 100644
--- a/api/src/tests/resource_groups.rs
+++ b/api/src/tests/resource_groups.rs
@@ -3,33 +3,28 @@
 
 use super::new_test_context;
 use aptos_api_test_context::{current_function_name, TestContext};
-use aptos_cached_packages::aptos_stdlib;
-use aptos_framework::BuiltPackage;
-use aptos_sdk::types::LocalAccount;
-use aptos_types::{account_address::AccountAddress, transaction::TransactionPayload};
-use serde_json::{json, Value};
+use serde_json::json;
 use std::path::PathBuf;
 
 // This test verifies that both READ APIs can seamlessly translate from resource group to resource
 // 1. Create accounts
 // 2. Publish a resource group package
 // 3. Verify default data exists
-// 4. Read the resources from that resource group anad verify they don't exist
+// 4. Read the resources from that resource group and verify they don't exist
 // 5. Init data for that resource group / member
 // 6. Read and ensure data is present
 // 7. Publish another resource group member
-// 8. Read the resources from the new resource group anad verify they don't exist
+// 8. Read the resources from the new resource group and verify they don't exist
 // 9. Init data for that resource group / member
 // 10. Read and ensure data is present
 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
-async fn test_read_resoure_group() {
+async fn test_gen_resource_group() {
     let mut context = new_test_context(current_function_name!());
 
     // Prepare accounts
-    let mut root = context.root_account();
-    let mut admin0 = create_account(&mut context, &mut root).await;
-    let mut admin1 = create_account(&mut context, &mut root).await;
-    let mut user = create_account(&mut context, &mut root).await;
+    let mut admin0 = context.create_account().await;
+    let mut admin1 = context.create_account().await;
+    let mut user = context.create_account().await;
 
     // Publish packages
     let named_addresses = vec![
@@ -41,171 +36,58 @@ async fn test_read_resoure_group() {
     let txn = futures::executor::block_on(async move {
         let path = PathBuf::from(std::env!("CARGO_MANIFEST_DIR"))
             .join("../aptos-move/move-examples/resource_groups/primary");
-        build_package(path, named_addresses_clone)
+        TestContext::build_package(path, named_addresses_clone)
     });
-    publish_package(&mut context, &mut admin0, txn).await;
+    context.publish_package(&mut admin0, txn).await;
 
     let named_addresses_clone = named_addresses.clone();
     let txn = futures::executor::block_on(async move {
         let path = PathBuf::from(std::env!("CARGO_MANIFEST_DIR"))
             .join("../aptos-move/move-examples/resource_groups/secondary");
-        build_package(path, named_addresses_clone)
+        TestContext::build_package(path, named_addresses_clone)
     });
-    publish_package(&mut context, &mut admin1, txn).await;
+    context.publish_package(&mut admin1, txn).await;
 
     // Read default data
     let primary = format!("0x{}::{}::{}", admin0.address(), "primary", "Primary");
     let secondary = format!("0x{}::{}::{}", admin1.address(), "secondary", "Secondary");
 
-    let response = read_resource(&context, &admin0.address(), &primary).await;
-    assert_eq!(response["data"]["value"], "3");
-
-    let response = maybe_read_resource(&context, &admin0.address(), &primary).await;
+    let response = context.gen_resource(&admin0.address(), &primary).await;
     assert_eq!(response.unwrap()["data"]["value"], "3");
 
     // Verify account is empty
-    let response = maybe_read_resource(&context, &user.address(), &primary).await;
+    let response = context.gen_resource(&user.address(), &primary).await;
     assert!(response.is_none());
-    let response = maybe_read_resource(&context, &user.address(), &secondary).await;
+    let response = context.gen_resource(&user.address(), &secondary).await;
     assert!(response.is_none());
 
     // Init secondary
-    execute_entry_function(
-        &mut context,
-        &mut user,
-        &format!("0x{}::secondary::init", admin1.address()),
-        json!([]),
-        json!([55]),
-    )
-    .await;
-    let response = read_resource(&context, &user.address(), &secondary).await;
-    assert_eq!(response["data"]["value"], 55);
-
-    let response = maybe_read_resource(&context, &user.address(), &secondary).await;
+    context
+        .api_execute_entry_function(
+            &mut user,
+            &format!("0x{}::secondary::init", admin1.address()),
+            json!([]),
+            json!([55]),
+        )
+        .await;
+    let response = context.gen_resource(&user.address(), &secondary).await;
     assert_eq!(response.unwrap()["data"]["value"], 55);
 
-    let response = maybe_read_resource(&context, &user.address(), &primary).await;
+    let response = context.gen_resource(&user.address(), &primary).await;
     assert!(response.is_none());
 
     // Init primary
-    execute_entry_function(
-        &mut context,
-        &mut user,
-        &format!("0x{}::primary::init", admin0.address()),
-        json!([]),
-        json!(["35"]),
-    )
-    .await;
-    let response = read_resource(&context, &user.address(), &primary).await;
-    assert_eq!(response["data"]["value"], "35");
-
-    let response = maybe_read_resource(&context, &user.address(), &primary).await;
-    assert_eq!(response.unwrap()["data"]["value"], "35");
-
-    let response = read_resource(&context, &user.address(), &secondary).await;
-    assert_eq!(response["data"]["value"], 55);
-
-    let response = maybe_read_resource(&context, &user.address(), &secondary).await;
-    assert_eq!(response.unwrap()["data"]["value"], 55);
-}
-
-// TODO: The TestContext code is a bit of a mess, the following likely should be added and that
-// code likely needs a good cleanup to merge to a common approach.
-
-async fn create_account(context: &mut TestContext, root: &mut LocalAccount) -> LocalAccount {
-    let account = context.gen_account();
-    let factory = context.transaction_factory();
-    let txn = root.sign_with_transaction_builder(
-        factory
-            .account_transfer(account.address(), 10_000_000)
-            .expiration_timestamp_secs(u64::MAX),
-    );
-
-    let bcs_txn = bcs::to_bytes(&txn).unwrap();
     context
-        .expect_status_code(202)
-        .post_bcs_txn("/transactions", bcs_txn)
-        .await;
-    context.commit_mempool_txns(1).await;
-    account
-}
-
-async fn maybe_read_resource(
-    context: &TestContext,
-    account_address: &AccountAddress,
-    resource: &str,
-) -> Option<Value> {
-    let response = read_resources(context, account_address).await;
-    response
-        .as_array()
-        .unwrap()
-        .iter()
-        .find(|entry| entry["type"] == resource)
-        .cloned()
-}
-
-async fn read_resources(context: &TestContext, account_address: &AccountAddress) -> Value {
-    let request = format!("/accounts/{}/resources", account_address);
-    context.get(&request).await
-}
-
-async fn read_resource(
-    context: &TestContext,
-    account_address: &AccountAddress,
-    resource: &str,
-) -> Value {
-    let request = format!("/accounts/{}/resource/{}", account_address, resource);
-    context.get(&request).await
-}
-
-pub fn build_package(
-    path: PathBuf,
-    named_addresses: Vec<(String, AccountAddress)>,
-) -> TransactionPayload {
-    let mut build_options = aptos_framework::BuildOptions::default();
-    let _ = named_addresses
-        .into_iter()
-        .map(|(name, address)| build_options.named_addresses.insert(name, address))
-        .collect::<Vec<_>>();
-
-    let package = BuiltPackage::build(path, build_options).unwrap();
-    let code = package.extract_code();
-    let metadata = package.extract_metadata().unwrap();
-
-    aptos_stdlib::code_publish_package_txn(bcs::to_bytes(&metadata).unwrap(), code)
-}
-
-pub async fn publish_package(
-    context: &mut TestContext,
-    publisher: &mut LocalAccount,
-    payload: TransactionPayload,
-) {
-    let txn =
-        publisher.sign_with_transaction_builder(context.transaction_factory().payload(payload));
-    let bcs_txn = bcs::to_bytes(&txn).unwrap();
-    context
-        .expect_status_code(202)
-        .post_bcs_txn("/transactions", bcs_txn)
-        .await;
-    context.commit_mempool_txns(1).await;
-}
-
-async fn execute_entry_function(
-    context: &mut TestContext,
-    account: &mut LocalAccount,
-    function: &str,
-    type_args: serde_json::Value,
-    args: serde_json::Value,
-) {
-    context
-        .api_execute_txn(
-            account,
-            json!({
-                "type": "entry_function_payload",
-                "function": function,
-                "type_arguments": type_args,
-                "arguments": args
-            }),
+        .api_execute_entry_function(
+            &mut user,
+            &format!("0x{}::primary::init", admin0.address()),
+            json!([]),
+            json!(["35"]),
         )
         .await;
+    let response = context.gen_resource(&user.address(), &primary).await;
+    assert_eq!(response.unwrap()["data"]["value"], "35");
+
+    let response = context.gen_resource(&user.address(), &secondary).await;
+    assert_eq!(response.unwrap()["data"]["value"], 55);
 }
diff --git a/api/src/tests/state_test.rs b/api/src/tests/state_test.rs
index f49082c4724f0..b307da6d34ee0 100644
--- a/api/src/tests/state_test.rs
+++ b/api/src/tests/state_test.rs
@@ -129,7 +129,7 @@ async fn test_merkle_leaves_with_nft_transfer() {
     let ctx = &mut context;
     let creator = &mut ctx.gen_account();
     let owner = &mut ctx.gen_account();
-    let txn1 = ctx.mint_user_account(creator);
+    let txn1 = ctx.mint_user_account(creator).await;
     let txn2 = ctx.account_transfer(creator, owner, 100_000);
 
     let collection_name = "collection name".to_owned().into_bytes();
@@ -222,14 +222,14 @@ async fn test_get_table_item() {
     let ctx = &mut context;
     let mut account = ctx.gen_account();
     let acc = &mut account;
-    let txn = ctx.create_user_account(acc);
+    let txn = ctx.create_user_account(acc).await;
     ctx.commit_block(&vec![txn.clone()]).await;
     make_test_tables(ctx, acc).await;
 
     // get the TestTables instance
     let tt = ctx
         .api_get_account_resource(
-            acc,
+            acc.address(),
             &acc.address().to_hex_literal(),
             "TableTestData",
             "TestTables",
@@ -322,14 +322,8 @@ async fn make_test_tables(ctx: &mut TestContext, account: &mut LocalAccount) {
 
     ctx.api_publish_module(account, module.try_into().unwrap())
         .await;
-    ctx.api_execute_entry_function(
-        account,
-        "TableTestData",
-        "make_test_tables",
-        json!([]),
-        json!([]),
-    )
-    .await
+    ctx.api_execute_entry_function(account, "make_test_tables", json!([]), json!([]))
+        .await
 }
 
 async fn build_test_module(account: AccountAddress) -> Vec<u8> {
diff --git a/api/src/tests/string_resource_test.rs b/api/src/tests/string_resource_test.rs
index f199bada89d6d..4de58f0227f61 100644
--- a/api/src/tests/string_resource_test.rs
+++ b/api/src/tests/string_resource_test.rs
@@ -15,7 +15,7 @@ use std::convert::TryInto;
 async fn test_renders_move_acsii_string_into_utf8_string() {
     let mut context = new_test_context(current_function_name!());
     let mut account = init_test_account();
-    let txn = context.create_user_account(&account);
+    let txn = context.create_user_account(&account).await;
     context.commit_block(&vec![txn]).await;
 
     // module 0x87342d91af60c3a883a2812c9294c2f8::Message {
@@ -38,7 +38,6 @@ async fn test_renders_move_acsii_string_into_utf8_string() {
     context
         .api_execute_entry_function(
             &mut account,
-            "Message",
             "set_message",
             json!([]),
             json!([hex::encode(b"hello world")]),
@@ -47,7 +46,7 @@ async fn test_renders_move_acsii_string_into_utf8_string() {
 
     let message = context
         .api_get_account_resource(
-            &account,
+            account.address(),
             &account.address().to_hex_literal(),
             "Message",
             "MessageHolder",
diff --git a/api/src/tests/transactions_test.rs b/api/src/tests/transactions_test.rs
index e7aabed467324..8b74cd13856d4 100644
--- a/api/src/tests/transactions_test.rs
+++ b/api/src/tests/transactions_test.rs
@@ -25,6 +25,7 @@ use move_core_types::{
 use poem_openapi::types::ParseFromJSON;
 use rand::{distributions::Alphanumeric, thread_rng, Rng};
 use serde_json::json;
+use std::path::PathBuf;
 
 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
 async fn test_deserialize_genesis_transaction() {
@@ -48,7 +49,7 @@ async fn test_get_transactions_output_genesis_transaction() {
 async fn test_get_transactions_returns_last_page_when_start_version_is_not_specified() {
     let mut context = new_test_context(current_function_name!());
 
-    let mut root_account = context.root_account();
+    let mut root_account = context.root_account().await;
     for _i in 0..20 {
         let account = context.gen_account();
         let txn = context.create_user_account_by(&mut root_account, &account);
@@ -112,7 +113,7 @@ async fn test_get_transactions_param_limit_exceeds_limit() {
 async fn test_get_transactions_output_user_transaction_with_entry_function_payload() {
     let mut context = new_test_context(current_function_name!());
     let account = context.gen_account();
-    let txn = context.create_user_account(&account);
+    let txn = context.create_user_account(&account).await;
     context.commit_block(&vec![txn.clone()]).await;
 
     let txns = context.get("/transactions?start=1").await;
@@ -126,7 +127,7 @@ async fn test_get_transactions_output_user_transaction_with_entry_function_paylo
 async fn test_get_transactions_output_user_transaction_with_module_payload() {
     let mut context = new_test_context(current_function_name!());
     let code = "a11ceb0b0300000006010002030205050703070a0c0816100c260900000001000100000102084d794d6f64756c650269640000000000000000000000000b1e55ed00010000000231010200";
-    let mut root_account = context.root_account();
+    let mut root_account = context.root_account().await;
     let txn = root_account.sign_with_transaction_builder(
         context
             .transaction_factory()
@@ -172,7 +173,7 @@ async fn test_get_transactions_output_user_transaction_with_module_payload() {
 async fn test_post_bcs_format_transaction() {
     let mut context = new_test_context(current_function_name!());
     let account = context.gen_account();
-    let txn = context.create_user_account(&account);
+    let txn = context.create_user_account(&account).await;
     let body = bcs::to_bytes(&txn).unwrap();
     let resp = context
         .expect_status_code(202)
@@ -201,7 +202,7 @@ async fn test_post_invalid_bcs_format_transaction() {
 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
 async fn test_post_invalid_signature_transaction() {
     let mut context = new_test_context(current_function_name!());
-    let txn = context.create_invalid_signature_transaction();
+    let txn = context.create_invalid_signature_transaction().await;
     let body = bcs::to_bytes(&txn).unwrap();
     let resp = context
         .expect_status_code(400)
@@ -215,8 +216,8 @@ async fn test_post_transaction_rejected_by_mempool() {
     let mut context = new_test_context(current_function_name!());
     let account1 = context.gen_account();
     let account2 = context.gen_account();
-    let txn1 = context.create_user_account(&account1);
-    let txn2 = context.create_user_account(&account2);
+    let txn1 = context.create_user_account(&account1).await;
+    let txn2 = context.create_user_account(&account2).await;
 
     context
         .expect_status_code(202)
@@ -236,7 +237,7 @@ async fn test_multi_agent_signed_transaction() {
     let account = context.gen_account();
     let secondary = context.gen_account();
     let factory = context.transaction_factory();
-    let mut root_account = context.root_account();
+    let mut root_account = context.root_account().await;
 
     // Create secondary signer account
     context
@@ -305,7 +306,7 @@ async fn test_multi_ed25519_signed_transaction() {
     let auth_key = AuthenticationKey::multi_ed25519(&public_key);
 
     let factory = context.transaction_factory();
-    let mut root_account = context.root_account();
+    let mut root_account = context.root_account().await;
     // TODO: migrate once multi-ed25519 is supported
     let create_account_txn = root_account.sign_with_transaction_builder(
         factory.create_user_account(&Ed25519PrivateKey::generate_for_testing().public_key()),
@@ -367,7 +368,7 @@ async fn test_multi_ed25519_signed_transaction() {
 async fn test_get_transaction_by_hash() {
     let mut context = new_test_context(current_function_name!());
     let account = context.gen_account();
-    let txn = context.create_user_account(&account);
+    let txn = context.create_user_account(&account).await;
     context.commit_block(&vec![txn.clone()]).await;
 
     let txns = context.get("/transactions?start=2&limit=1").await;
@@ -419,7 +420,7 @@ async fn test_get_transaction_by_version_not_found() {
 async fn test_get_transaction_by_version() {
     let mut context = new_test_context(current_function_name!());
     let account = context.gen_account();
-    let txn = context.create_user_account(&account);
+    let txn = context.create_user_account(&account).await;
     context.commit_block(&vec![txn.clone()]).await;
 
     let txns = context.get("/transactions?start=2&limit=1").await;
@@ -433,7 +434,7 @@ async fn test_get_transaction_by_version() {
 async fn test_get_pending_transaction_by_hash() {
     let mut context = new_test_context(current_function_name!());
     let account = context.gen_account();
-    let txn = context.create_user_account(&account);
+    let txn = context.create_user_account(&account).await;
     let body = bcs::to_bytes(&txn).unwrap();
     let pending_txn = context
         .expect_status_code(202)
@@ -468,7 +469,7 @@ async fn test_get_pending_transaction_by_hash() {
 async fn test_signing_message_with_entry_function_payload() {
     let mut context = new_test_context(current_function_name!());
     let account = context.gen_account();
-    let txn = context.create_user_account(&account);
+    let txn = context.create_user_account(&account).await;
     let payload = json!({
         "type": "entry_function_payload",
         "function": "0x1::aptos_account::create_account",
@@ -485,7 +486,7 @@ async fn test_signing_message_with_payload(
     txn: SignedTransaction,
     payload: serde_json::Value,
 ) {
-    let sender = context.root_account();
+    let sender = context.root_account().await;
     let mut body = json!({
         "sender": sender.address().to_hex_literal(),
         "sequence_number": sender.sequence_number().to_string(),
@@ -517,6 +518,7 @@ async fn test_signing_message_with_payload(
 
     let sig = context
         .root_account()
+        .await
         .private_key()
         .sign_arbitrary_message(signing_msg.inner());
     let expected_sig = match txn.authenticator() {
@@ -550,14 +552,14 @@ async fn test_signing_message_with_payload(
 async fn test_get_account_transactions() {
     let mut context = new_test_context(current_function_name!());
     let account = context.gen_account();
-    let txn = context.create_user_account(&account);
+    let txn = context.create_user_account(&account).await;
     context.commit_block(&vec![txn]).await;
 
     let txns = context
         .get(
             format!(
                 "/accounts/{}/transactions",
-                context.root_account().address()
+                context.root_account().await.address()
             )
             .as_str(),
         )
@@ -571,14 +573,14 @@ async fn test_get_account_transactions() {
 async fn test_get_account_transactions_filter_transactions_by_start_sequence_number() {
     let mut context = new_test_context(current_function_name!());
     let account = context.gen_account();
-    let txn = context.create_user_account(&account);
+    let txn = context.create_user_account(&account).await;
     context.commit_block(&vec![txn]).await;
 
     let txns = context
         .get(
             format!(
                 "/accounts/{}/transactions?start=1",
-                context.root_account().address()
+                context.root_account().await.address()
             )
             .as_str(),
         )
@@ -590,14 +592,14 @@ async fn test_get_account_transactions_filter_transactions_by_start_sequence_num
 async fn test_get_account_transactions_filter_transactions_by_start_sequence_number_is_too_large() {
     let mut context = new_test_context(current_function_name!());
     let account = context.gen_account();
-    let txn = context.create_user_account(&account);
+    let txn = context.create_user_account(&account).await;
     context.commit_block(&vec![txn]).await;
 
     let txns = context
         .get(
             format!(
                 "/accounts/{}/transactions?start=1000",
-                context.root_account().address()
+                context.root_account().await.address()
             )
             .as_str(),
         )
@@ -608,7 +610,7 @@ async fn test_get_account_transactions_filter_transactions_by_start_sequence_num
 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
 async fn test_get_account_transactions_filter_transactions_by_limit() {
     let mut context = new_test_context(current_function_name!());
-    let mut root_account = context.root_account();
+    let mut root_account = context.root_account().await;
     let account1 = context.gen_account();
     let txn1 = context.create_user_account_by(&mut root_account, &account1);
     let account2 = context.gen_account();
@@ -619,7 +621,7 @@ async fn test_get_account_transactions_filter_transactions_by_limit() {
         .get(
             format!(
                 "/accounts/{}/transactions?start=0&limit=1",
-                context.root_account().address()
+                context.root_account().await.address()
             )
             .as_str(),
         )
@@ -630,7 +632,7 @@ async fn test_get_account_transactions_filter_transactions_by_limit() {
         .get(
             format!(
                 "/accounts/{}/transactions?start=0&limit=2",
-                context.root_account().address()
+                context.root_account().await.address()
             )
             .as_str(),
         )
@@ -641,7 +643,7 @@ async fn test_get_account_transactions_filter_transactions_by_limit() {
 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
 async fn test_get_txn_execute_failed_by_invalid_script_payload_bytecode() {
     let context = new_test_context(current_function_name!());
-    let mut root_account = context.root_account();
+    let mut root_account = context.root_account().await;
     let invalid_bytecode = hex::decode("a11ceb0b030000").unwrap();
     let txn = root_account.sign_with_transaction_builder(
         context
@@ -655,7 +657,7 @@ async fn test_get_txn_execute_failed_by_invalid_script_payload_bytecode() {
 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
 async fn test_get_txn_execute_failed_by_invalid_entry_function_address() {
     let context = new_test_context(current_function_name!());
-    let account = context.root_account();
+    let account = context.root_account().await;
     test_get_txn_execute_failed_by_invalid_entry_function(
         context,
         account,
@@ -674,7 +676,7 @@ async fn test_get_txn_execute_failed_by_invalid_entry_function_address() {
 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
 async fn test_get_txn_execute_failed_by_invalid_entry_function_module_name() {
     let context = new_test_context(current_function_name!());
-    let account = context.root_account();
+    let account = context.root_account().await;
     test_get_txn_execute_failed_by_invalid_entry_function(
         context,
         account,
@@ -693,7 +695,7 @@ async fn test_get_txn_execute_failed_by_invalid_entry_function_module_name() {
 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
 async fn test_get_txn_execute_failed_by_invalid_entry_function_name() {
     let context = new_test_context(current_function_name!());
-    let account = context.root_account();
+    let account = context.root_account().await;
     test_get_txn_execute_failed_by_invalid_entry_function(
         context,
         account,
@@ -712,7 +714,7 @@ async fn test_get_txn_execute_failed_by_invalid_entry_function_name() {
 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
 async fn test_get_txn_execute_failed_by_invalid_entry_function_arguments() {
     let context = new_test_context(current_function_name!());
-    let account = context.root_account();
+    let account = context.root_account().await;
     test_get_txn_execute_failed_by_invalid_entry_function(
         context,
         account,
@@ -731,7 +733,7 @@ async fn test_get_txn_execute_failed_by_invalid_entry_function_arguments() {
 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
 async fn test_get_txn_execute_failed_by_missing_entry_function_arguments() {
     let context = new_test_context(current_function_name!());
-    let account = context.root_account();
+    let account = context.root_account().await;
     test_get_txn_execute_failed_by_invalid_entry_function(
         context,
         account,
@@ -752,7 +754,7 @@ async fn test_get_txn_execute_failed_by_entry_function_validation() {
     let mut context = new_test_context(current_function_name!());
     let account = context.gen_account();
     context
-        .commit_block(&vec![context.create_user_account(&account)])
+        .commit_block(&vec![context.create_user_account(&account).await])
         .await;
 
     test_get_txn_execute_failed_by_invalid_entry_function(
@@ -775,7 +777,7 @@ async fn test_get_txn_execute_failed_by_entry_function_invalid_module_name() {
     let mut context = new_test_context(current_function_name!());
     let account = context.gen_account();
     context
-        .commit_block(&vec![context.create_user_account(&account)])
+        .commit_block(&vec![context.create_user_account(&account).await])
         .await;
 
     test_submit_entry_function_api_validation(
@@ -798,7 +800,7 @@ async fn test_get_txn_execute_failed_by_entry_function_invalid_function_name() {
     let mut context = new_test_context(current_function_name!());
     let account = context.gen_account();
     context
-        .commit_block(&vec![context.create_user_account(&account)])
+        .commit_block(&vec![context.create_user_account(&account).await])
         .await;
 
     test_submit_entry_function_api_validation(
@@ -816,38 +818,31 @@ async fn test_get_txn_execute_failed_by_entry_function_invalid_function_name() {
     .await;
 }
 
-#[ignore] // Re-enable when change is moved to new publish flow
 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
 async fn test_get_txn_execute_failed_by_entry_function_execution_failure() {
     let mut context = new_test_context(current_function_name!());
+    let mut admin = context.create_account().await;
 
-    // address 0xA550C18 {
-    //     module Hello {
-    //         fun world() {
-    //             1/0;
-    //         }
-    //         public(script) fun hello() {
-    //             world();
-    //         }
-    //     }
-    // }
-    let hello_entry_fun = hex::decode("a11ceb0b030000000601000203020a050c01070d12081f100c2f24000000010000000002000000000548656c6c6f0568656c6c6f05776f726c640000000000000000000000000a550c180002000000021101020100000000050601000000000000000600000000000000001a010200").unwrap();
-    let mut root_account = context.root_account();
-    let module_txn = root_account
-        .sign_with_transaction_builder(context.transaction_factory().module(hello_entry_fun));
+    let named_addresses = vec![("entry_func_fail".to_string(), admin.address())];
 
-    context.commit_block(&vec![module_txn]).await;
+    let named_addresses_clone = named_addresses.clone();
+    let txn = futures::executor::block_on(async move {
+        let path = PathBuf::from(std::env!("CARGO_MANIFEST_DIR")).join("test-context/move");
+        TestContext::build_package(path, named_addresses_clone)
+    });
+    let txn = context.publish_package(&mut admin, txn).await;
 
-    test_get_txn_execute_failed_by_invalid_entry_function(
-        context,
-        root_account,
-        "0xA550C18",
-        "Hello",
-        "hello",
-        vec![],
-        vec![],
-    )
-    .await
+    let resp = context
+        .get(
+            format!(
+                "/transactions/by_hash/{}",
+                txn.committed_hash().to_hex_literal()
+            )
+            .as_str(),
+        )
+        .await;
+
+    assert!(!resp["success"].as_bool().unwrap(), "{}", pretty(&resp));
 }
 
 #[ignore] // re-enable after cleaning compiled code
@@ -863,7 +858,7 @@ async fn test_get_txn_execute_failed_by_script_execution_failure() {
     let script =
         hex::decode("a11ceb0b030000000105000100000000050601000000000000000600000000000000001a0102")
             .unwrap();
-    let mut root_account = context.root_account();
+    let mut root_account = context.root_account().await;
     let txn = root_account.sign_with_transaction_builder(
         context
             .transaction_factory()
diff --git a/api/src/tests/view_function.rs b/api/src/tests/view_function.rs
index 9f7889bc5cfbd..7316bea302aea 100644
--- a/api/src/tests/view_function.rs
+++ b/api/src/tests/view_function.rs
@@ -10,7 +10,7 @@ async fn test_simple_view() {
     let mut context = new_test_context(current_function_name!());
     let creator = &mut context.gen_account();
     let owner = &mut context.gen_account();
-    let txn1 = context.mint_user_account(creator);
+    let txn1 = context.mint_user_account(creator).await;
     let txn2 = context.account_transfer(creator, owner, 100_000);
 
     context.commit_block(&vec![txn1, txn2]).await;
@@ -34,7 +34,7 @@ async fn test_simple_view_invalid() {
     let mut context = new_test_context(current_function_name!());
     let creator = &mut context.gen_account();
     let owner = &mut context.gen_account();
-    let txn1 = context.mint_user_account(creator);
+    let txn1 = context.mint_user_account(creator).await;
     let txn2 = context.account_transfer(creator, owner, 100_000);
 
     context.commit_block(&vec![txn1, txn2]).await;
@@ -60,7 +60,7 @@ async fn test_versioned_simple_view() {
     let mut context = new_test_context(current_function_name!());
     let creator = &mut context.gen_account();
     let owner = &mut context.gen_account();
-    let txn1 = context.mint_user_account(creator);
+    let txn1 = context.mint_user_account(creator).await;
     let txn2 = context.account_transfer(creator, owner, 100_000);
     let txn3 = context.account_transfer(creator, owner, 100_000);
 
@@ -94,7 +94,7 @@ async fn test_view_tuple() {
     //     }
     // }
     let tuple_module = hex::decode("a11ceb0b0500000006010002030205050704070b1b0826200c461900000001000100000203030d5461626c6554657374446174610c72657475726e5f7475706c65000000000000000000000000000000000000000000000000000000000a550c180001000000030601000000000000000602000000000000000200").unwrap();
-    let mut root_account = context.root_account();
+    let mut root_account = context.root_account().await;
     let module_txn = root_account
         .sign_with_transaction_builder(context.transaction_factory().module(tuple_module));
 
diff --git a/api/test-context/Cargo.toml b/api/test-context/Cargo.toml
index 0700fb34a5382..66e108dd848c2 100644
--- a/api/test-context/Cargo.toml
+++ b/api/test-context/Cargo.toml
@@ -22,6 +22,7 @@ aptos-crypto = { workspace = true }
 aptos-db = { workspace = true, features = ["fuzzing"] }
 aptos-executor = { workspace = true }
 aptos-executor-types = { workspace = true }
+aptos-framework = { workspace = true }
 aptos-genesis = { workspace = true }
 aptos-mempool = { workspace = true, features = ["fuzzing"] }
 aptos-mempool-notifications = { workspace = true }
@@ -31,6 +32,7 @@ aptos-temppath = { workspace = true }
 aptos-types = { workspace = true }
 aptos-vm = { workspace = true }
 aptos-vm-validator = { workspace = true }
+bcs = { workspace = true }
 bytes = { workspace = true }
 goldenfile = { workspace = true }
 hyper = { workspace = true }
diff --git a/api/test-context/move/Move.toml b/api/test-context/move/Move.toml
new file mode 100644
index 0000000000000..bb81e10e4a519
--- /dev/null
+++ b/api/test-context/move/Move.toml
@@ -0,0 +1,9 @@
+[package]
+name = "TransactationTests"
+version = "0.0.0"
+
+[addresses]
+entry_func_failed= "0x1"
+
+[dependencies]
+AptosFramework = { local = "../../../aptos-move/framework/aptos-framework" }
diff --git a/api/test-context/move/sources/entry_func_fail.move b/api/test-context/move/sources/entry_func_fail.move
new file mode 100644
index 0000000000000..8c18096263e24
--- /dev/null
+++ b/api/test-context/move/sources/entry_func_fail.move
@@ -0,0 +1,6 @@
+module entry_func_failed::func {
+    public entry fun init() {
+        // invalid function with 0 as denominator
+        1/0;
+    }
+}
diff --git a/api/test-context/src/test_context.rs b/api/test-context/src/test_context.rs
index 3b4aa841befd9..e5de79ebaa964 100644
--- a/api/test-context/src/test_context.rs
+++ b/api/test-context/src/test_context.rs
@@ -7,6 +7,7 @@ use aptos_api_types::{
     mime_types, HexEncodedBytes, TransactionOnChainData, X_APTOS_CHAIN_ID,
     X_APTOS_LEDGER_TIMESTAMP, X_APTOS_LEDGER_VERSION,
 };
+use aptos_cached_packages::aptos_stdlib;
 use aptos_config::{
     config::{
         NodeConfig, RocksdbConfigs, BUFFERED_STATE_TARGET_ITEMS,
@@ -18,9 +19,11 @@ use aptos_crypto::{ed25519::Ed25519PrivateKey, hash::HashValue, SigningKey};
 use aptos_db::AptosDB;
 use aptos_executor::{block_executor::BlockExecutor, db_bootstrapper};
 use aptos_executor_types::BlockExecutorTrait;
+use aptos_framework::BuiltPackage;
 use aptos_mempool::mocks::MockSharedMempool;
 use aptos_mempool_notifications::MempoolNotificationSender;
 use aptos_sdk::{
+    bcs,
     transaction_builder::TransactionFactory,
     types::{
         account_config::aptos_test_root_address, transaction::SignedTransaction, AccountKey,
@@ -36,7 +39,7 @@ use aptos_types::{
     block_metadata::BlockMetadata,
     chain_id::ChainId,
     ledger_info::{LedgerInfo, LedgerInfoWithSignatures},
-    transaction::{Transaction, TransactionStatus},
+    transaction::{Transaction, TransactionPayload, TransactionStatus},
 };
 use aptos_vm::AptosVM;
 use aptos_vm_validator::vm_validator::VMValidator;
@@ -44,10 +47,12 @@ use bytes::Bytes;
 use hyper::{HeaderMap, Response};
 use rand::SeedableRng;
 use serde_json::{json, Value};
-use std::{boxed::Box, iter::once, net::SocketAddr, sync::Arc, time::Duration};
+use std::{boxed::Box, iter::once, net::SocketAddr, path::PathBuf, sync::Arc, time::Duration};
 use warp::{http::header::CONTENT_TYPE, Filter, Rejection, Reply};
 use warp_reverse_proxy::reverse_proxy_filter;
 
+const TRANSFER_AMOUNT: u64 = 10_000_000;
+
 #[derive(Clone, Debug)]
 pub enum ApiSpecificConfig {
     // The SocketAddr is the address where the Poem backend is running.
@@ -296,8 +301,15 @@ impl TestContext {
         TransactionFactory::new(self.context.chain_id())
     }
 
-    pub fn root_account(&self) -> LocalAccount {
-        LocalAccount::new(aptos_test_root_address(), self.root_key.private_key(), 0)
+    pub async fn root_account(&self) -> LocalAccount {
+        // Fetch the actual root account's sequence number in case it has been used to sign
+        // transactions before.
+        let root_sequence_number = self.get_sequence_number(aptos_test_root_address()).await;
+        LocalAccount::new(
+            aptos_test_root_address(),
+            self.root_key.private_key(),
+            root_sequence_number,
+        )
     }
 
     pub fn latest_state_view(&self) -> DbStateView {
@@ -310,17 +322,35 @@ impl TestContext {
         LocalAccount::generate(self.rng())
     }
 
-    pub fn create_user_account(&self, account: &LocalAccount) -> SignedTransaction {
-        let mut tc = self.root_account();
+    pub async fn create_account(&mut self) -> LocalAccount {
+        let mut root = self.root_account().await;
+        let account = self.gen_account();
+        let factory = self.transaction_factory();
+        let txn = root.sign_with_transaction_builder(
+            factory
+                .account_transfer(account.address(), TRANSFER_AMOUNT)
+                .expiration_timestamp_secs(u64::MAX),
+        );
+
+        let bcs_txn = bcs::to_bytes(&txn).unwrap();
+        self.expect_status_code(202)
+            .post_bcs_txn("/transactions", bcs_txn)
+            .await;
+        self.commit_mempool_txns(1).await;
+        account
+    }
+
+    pub async fn create_user_account(&self, account: &LocalAccount) -> SignedTransaction {
+        let mut tc = self.root_account().await;
         self.create_user_account_by(&mut tc, account)
     }
 
-    pub fn mint_user_account(&self, account: &LocalAccount) -> SignedTransaction {
-        let mut tc = self.root_account();
+    pub async fn mint_user_account(&self, account: &LocalAccount) -> SignedTransaction {
+        let mut tc = self.root_account().await;
         let factory = self.transaction_factory();
         tc.sign_with_transaction_builder(
             factory
-                .account_transfer(account.address(), 10_000_000)
+                .account_transfer(account.address(), TRANSFER_AMOUNT)
                 .expiration_timestamp_secs(u64::MAX),
         )
     }
@@ -352,9 +382,9 @@ impl TestContext {
         )
     }
 
-    pub fn create_invalid_signature_transaction(&mut self) -> SignedTransaction {
+    pub async fn create_invalid_signature_transaction(&mut self) -> SignedTransaction {
         let factory = self.transaction_factory();
-        let root_account = self.root_account();
+        let root_account = self.root_account().await;
         let txn = factory
             .transfer(root_account.address(), 1)
             .sender(root_account.address())
@@ -382,6 +412,37 @@ impl TestContext {
         ret
     }
 
+    pub fn build_package(
+        path: PathBuf,
+        named_addresses: Vec<(String, AccountAddress)>,
+    ) -> TransactionPayload {
+        let mut build_options = aptos_framework::BuildOptions::default();
+        named_addresses.into_iter().for_each(|(name, address)| {
+            build_options.named_addresses.insert(name, address);
+        });
+
+        let package = BuiltPackage::build(path, build_options).unwrap();
+        let code = package.extract_code();
+        let metadata = package.extract_metadata().unwrap();
+
+        aptos_stdlib::code_publish_package_txn(bcs::to_bytes(&metadata).unwrap(), code)
+    }
+
+    pub async fn publish_package(
+        &mut self,
+        publisher: &mut LocalAccount,
+        payload: TransactionPayload,
+    ) -> SignedTransaction {
+        let txn =
+            publisher.sign_with_transaction_builder(self.transaction_factory().payload(payload));
+        let bcs_txn = bcs::to_bytes(&txn).unwrap();
+        self.expect_status_code(202)
+            .post_bcs_txn("/transactions", bcs_txn)
+            .await;
+        self.commit_mempool_txns(1).await;
+        txn
+    }
+
     pub async fn commit_mempool_txns(&mut self, size: u64) {
         let txns = self.mempool.get_txns(size);
         self.commit_block(&txns).await;
@@ -438,19 +499,50 @@ impl TestContext {
             .unwrap();
     }
 
+    pub async fn get_sequence_number(&self, account: AccountAddress) -> u64 {
+        let account_resource = self
+            .gen_resource(&account, "0x1::account::Account")
+            .await
+            .unwrap();
+        account_resource["data"]["sequence_number"]
+            .as_str()
+            .unwrap()
+            .parse::<u64>()
+            .unwrap()
+    }
+
+    // return a specific resource for an account. None if not found.
+    pub async fn gen_resource(
+        &self,
+        account_address: &AccountAddress,
+        resource: &str,
+    ) -> Option<Value> {
+        let request = format!("/accounts/{}/resources", account_address);
+        let response = self.get(&request).await;
+        response
+            .as_array()
+            .unwrap()
+            .iter()
+            .find(|entry| entry["type"] == resource)
+            .cloned()
+    }
+
+    // return all resources for an account
+    pub async fn gen_all_resources(&self, account_address: &AccountAddress) -> Value {
+        let request = format!("/accounts/{}/resources", account_address);
+        self.get(&request).await
+    }
+
     // TODO: Add support for generic_type_params if necessary.
     pub async fn api_get_account_resource(
         &self,
-        account: &LocalAccount,
+        account: AccountAddress,
         resource_account_address: &str,
         module: &str,
         name: &str,
     ) -> serde_json::Value {
         let resources = self
-            .get(&format!(
-                "/accounts/{}/resources",
-                account.address().to_hex_literal()
-            ))
+            .get(&format!("/accounts/{}/resources", account.to_hex_literal()))
             .await;
         let vals: Vec<serde_json::Value> = serde_json::from_value(resources).unwrap();
         vals.into_iter()
@@ -458,11 +550,24 @@ impl TestContext {
             .unwrap()
     }
 
+    // TODO: remove the helper function since we don't publish module directly anymore
+    pub async fn api_publish_module(&mut self, account: &mut LocalAccount, code: HexEncodedBytes) {
+        self.api_execute_txn(
+            account,
+            json!({
+                "type": "module_bundle_payload",
+                "modules" : [
+                    {"bytecode": code},
+                ],
+            }),
+        )
+        .await;
+    }
+
     pub async fn api_execute_entry_function(
         &mut self,
         account: &mut LocalAccount,
-        module: &str,
-        func: &str,
+        function: &str,
         type_args: serde_json::Value,
         args: serde_json::Value,
     ) {
@@ -470,12 +575,7 @@ impl TestContext {
             account,
             json!({
                 "type": "entry_function_payload",
-                "function": format!(
-                    "{}::{}::{}",
-                    account.address().to_hex_literal(),
-                    module,
-                    func
-                ),
+                "function": function,
                 "type_arguments": type_args,
                 "arguments": args
             }),
@@ -483,20 +583,7 @@ impl TestContext {
         .await;
     }
 
-    pub async fn api_publish_module(&mut self, account: &mut LocalAccount, code: HexEncodedBytes) {
-        self.api_execute_txn(
-            account,
-            json!({
-                "type": "module_bundle_payload",
-                "modules" : [
-                    {"bytecode": code},
-                ],
-            }),
-        )
-        .await;
-    }
-
-    pub async fn api_execute_txn(&mut self, account: &mut LocalAccount, payload: Value) {
+    async fn api_execute_txn(&mut self, account: &mut LocalAccount, payload: Value) {
         let mut request = json!({
             "sender": account.address(),
             "sequence_number": account.sequence_number().to_string(),