From 5c0d6b849c7601cb6814ba8330b9530dba4042d5 Mon Sep 17 00:00:00 2001 From: Jack Frain Date: Wed, 5 Nov 2025 14:57:49 -0500 Subject: [PATCH] fix: hb_ao:get_many optimization --- src/dev_json_iface.erl | 35 +++++-------------- src/dev_message.erl | 2 +- src/dev_push.erl | 46 +++++++++++++++---------- src/dev_stack.erl | 78 ++++++++++++++++-------------------------- src/hb_ao.erl | 24 ++++++++++++- 5 files changed, 90 insertions(+), 95 deletions(-) diff --git a/src/dev_json_iface.erl b/src/dev_json_iface.erl index d6ca9e3d9..05476562e 100644 --- a/src/dev_json_iface.erl +++ b/src/dev_json_iface.erl @@ -139,18 +139,16 @@ message_to_json_struct(RawMsg, Features, Opts) -> {CommitmentOwner, CommitmentSignature, CommitmentKeyId} end end, - Last = - hb_ao:get( - <<"anchor">>, - {as, <<"message@1.0">>, MsgWithoutCommitments}, - <<>>, - Opts - ), - DataBytes = - hb_ao:get( - <<"data">>, + {Last, DataBytes, Target, From} = + hb_ao:get_many( + [ + <<"anchor">>, + <<"data">>, + <<"target">>, + <<"from-process">> + ], {as, <<"message@1.0">>, MsgWithoutCommitments}, - <<>>, + [<<>>, <<>>, <<>>, hb_util:encode(Owner)], Opts ), Data = @@ -158,21 +156,6 @@ message_to_json_struct(RawMsg, Features, Opts) -> true -> DataBytes; false -> null end, - Target = - hb_ao:get( - <<"target">>, - {as, <<"message@1.0">>, MsgWithoutCommitments}, - <<>>, - Opts - ), - % Set "From" if From-Process is Tag or set with "Owner" address - From = - hb_ao:get( - <<"from-process">>, - {as, <<"message@1.0">>, MsgWithoutCommitments}, - hb_util:encode(Owner), - Opts - ), #{ <<"Id">> => safe_to_id(ID), % NOTE: In Arweave TXs, these are called "last_tx" diff --git a/src/dev_message.erl b/src/dev_message.erl index e8d15fbdb..1cb9cc917 100644 --- a/src/dev_message.erl +++ b/src/dev_message.erl @@ -691,7 +691,7 @@ set(Base, NewValuesMsg, Opts) -> _ -> % We did overwrite some keys, but do their values match the original? % If not, we must remove the commitments. - case hb_message:match(Merged, Base, Opts) of + case hb_message:match(Merged, Base, strict, Opts) of true -> ?event(message_set, {set_keys_matched, {merged, Merged}}), {ok, Merged}; diff --git a/src/dev_push.erl b/src/dev_push.erl index be0ca045a..4c3c9b745 100644 --- a/src/dev_push.erl +++ b/src/dev_push.erl @@ -648,7 +648,8 @@ full_push_test_() -> Opts = #{ process_async_cache => false, priv_wallet => hb:wallet(), - cache_control => <<"always">> + cache_control => <<"always">>, + store => [hb_test_utils:test_store()] }, Base = dev_process:test_aos_process(Opts), hb_cache:write(Base, Opts), @@ -700,7 +701,8 @@ push_as_identity_test_() -> ComputeID => #{ priv_wallet => ComputeWallet } - } + }, + store => [hb_test_utils:test_store()] }, % Create a new test AOS process, which will use the given identities as % its authority and scheduler. @@ -766,7 +768,8 @@ multi_process_push_test_() -> dev_process:init(), Opts = #{ priv_wallet => hb:wallet(), - cache_control => <<"always">> + cache_control => <<"always">>, + store => [hb_test_utils:test_store()] }, Proc1 = dev_process:test_aos_process(Opts), hb_cache:write(Proc1, Opts), @@ -822,15 +825,9 @@ multi_process_push_test_() -> push_with_redirect_hint_test_disabled() -> {timeout, 30, fun() -> dev_process:init(), - Stores = - [ - #{ - <<"store-module">> => hb_store_fs, - <<"name">> => <<"cache-TEST">> - } - ], - ExtOpts = #{ priv_wallet => ar_wallet:new(), store => Stores }, - LocalOpts = #{ priv_wallet => hb:wallet(), store => Stores }, + StoreOpts = [hb_test_utils:test_store(hb_store_fs)], + ExtOpts = #{ priv_wallet => ar_wallet:new(), store => StoreOpts }, + LocalOpts = #{ priv_wallet => hb:wallet(), store => StoreOpts }, ExtScheduler = hb_http_server:start_node(ExtOpts), ?event(push, {external_scheduler, {location, ExtScheduler}}), % Create the Pong server and client @@ -944,21 +941,22 @@ push_prompts_encoding_change() -> oracle_push_test_() -> {timeout, 30, fun oracle_push/0}. oracle_push() -> dev_process:init(), - Client = dev_process:test_aos_process(), - {ok, _} = hb_cache:write(Client, #{}), - {ok, _} = dev_process:schedule_aos_call(Client, oracle_script()), + Opts = #{ priv_wallet => hb:wallet(), store => [hb_test_utils:test_store()] }, + Client = dev_process:test_aos_process(Opts), + {ok, _} = hb_cache:write(Client, Opts), + {ok, _} = dev_process:schedule_aos_call(Client, oracle_script(), Opts), Res = #{ <<"path">> => <<"push">>, <<"slot">> => 0 }, - {ok, PushResult} = hb_ao:resolve(Client, Res, #{ priv_wallet => hb:wallet() }), + {ok, PushResult} = hb_ao:resolve(Client, Res, Opts), ?event({result, PushResult}), ComputeRes = hb_ao:resolve( Client, <<"now/results/data">>, - #{ priv_wallet => hb:wallet() } + Opts ), ?event({compute_res, ComputeRes}), ?assertMatch({ok, _}, ComputeRes). @@ -974,7 +972,17 @@ nested_push_prompts_encoding_change() -> Opts = #{ priv_wallet => hb:wallet(), cache_control => <<"always">>, - store => hb_opts:get(store) + store => [ + #{ <<"store-module">> => hb_store_lmdb, <<"name">> => <<"cache-TEST/lmdb">> }, + % Include a gateway store so that we can get the legacynet + % process when needed. + #{ <<"store-module">> => hb_store_gateway, + <<"store">> => #{ + <<"store-module">> => hb_store_lmdb, + <<"name">> => <<"cache-TEST/lmdb">> + } + } + ] }, ?event(push_debug, {opts, Opts}), Base = dev_process:test_aos_process(Opts), @@ -990,7 +998,7 @@ nested_push_prompts_encoding_change() -> ?event({test_setup, {base, Base}, {sched_init, SchedInit}}), Script = message_to_legacynet_scheduler_script(), ?event({script, Script}), - {ok, Req} = dev_process:schedule_aos_call(Base, Script), + {ok, Req} = dev_process:schedule_aos_call(Base, Script, Opts), ?event(push, {msg_sched_result, Req}), {ok, StartingMsgSlot} = hb_ao:resolve(Req, #{ <<"path">> => <<"slot">> }, Opts), diff --git a/src/dev_stack.erl b/src/dev_stack.erl index 9461a8920..d9388c1d1 100644 --- a/src/dev_stack.erl +++ b/src/dev_stack.erl @@ -211,45 +211,29 @@ transform(Base, Key, Opts) -> % - The prefixes for the device. % - The prior prefixes for later restoration. ?event({activating_device, DevMsg}), + {IPrefix, OPrefix, PDevice, PIPrefix, POPrefix} = + hb_ao:get_many( + [ + [<<"input-prefixes">>, Key], + [<<"output-prefixes">>, Key], + <<"device">>, + <<"previous-input-prefix">>, + <<"previous-output-prefix">> + ], + {as, dev_message, Base}, + [undefined, undefined, undefined, undefined, undefined], + Opts + ), dev_message:set( Base, #{ <<"device">> => DevMsg, <<"device-key">> => Key, - <<"input-prefix">> => - hb_ao:get( - [<<"input-prefixes">>, Key], - {as, dev_message, Base}, - undefined, - Opts - ), - <<"output-prefix">> => - hb_ao:get( - [<<"output-prefixes">>, Key], - {as, dev_message, Base}, - undefined, - Opts - ), - <<"previous-device">> => - hb_ao:get( - <<"device">>, - {as, dev_message, Base}, - Opts - ), - <<"previous-input-prefix">> => - hb_ao:get( - <<"input-prefix">>, - {as, dev_message, Base}, - undefined, - Opts - ), - <<"previous-output-prefix">> => - hb_ao:get( - <<"output-prefix">>, - {as, dev_message, Base}, - undefined, - Opts - ) + <<"input-prefix">> => IPrefix, + <<"output-prefix">> => OPrefix, + <<"previous-device">> => PDevice, + <<"previous-input-prefix">> => PIPrefix, + <<"previous-output-prefix">> => POPrefix }, Opts ); @@ -270,24 +254,22 @@ resolve_fold(Base, Request, Opts) -> {ok, Raw} when not is_map(Raw) -> {ok, Raw}; {ok, Result} -> + {InputPrefix, OutputPrefix} = + hb_ao:get_many( + [ + <<"previous-input-prefix">>, + <<"previous-output-prefix">> + ], + {as, dev_message, Result}, + [undefined, undefined], + Opts + ), dev_message:set( Result, #{ <<"device">> => InitDevMsg, - <<"input-prefix">> => - hb_ao:get( - <<"previous-input-prefix">>, - {as, dev_message, Result}, - undefined, - Opts - ), - <<"output-prefix">> => - hb_ao:get( - <<"previous-output-prefix">>, - {as, dev_message, Result}, - undefined, - Opts - ), + <<"input-prefix">> => InputPrefix, + <<"output-prefix">> => OutputPrefix, <<"device-key">> => unset, <<"device-stack-previous">> => unset, <<"pass">> => StartingPassValue diff --git a/src/hb_ao.erl b/src/hb_ao.erl index c00ba6c3a..065d8b913 100644 --- a/src/hb_ao.erl +++ b/src/hb_ao.erl @@ -99,7 +99,7 @@ -export([force_message/2]). %%% Shortcuts and tools: -export([keys/1, keys/2, keys/3]). --export([get/2, get/3, get/4, get_first/2, get_first/3]). +-export([get/2, get/3, get/4, get_first/2, get_first/3, get_many/4]). -export([set/3, set/4, remove/2, remove/3]). %%% Exports for tests in hb_ao_test_vectors.erl: -export([deep_set/4]). @@ -936,6 +936,28 @@ get_first([{Base, Path}|Msgs], Default, Opts) -> Value -> Value end. +%% @doc Get the values of multiple paths from a message, only calling set once. +%% Takes a list of paths, a message, a list of defaults, and options. +%% Returns a tuple of values in the same order. +%% This is more efficient than calling get/4 multiple times when working with +%% {as, Device, Msg} tuples because set is only called once. +get_many(Paths, {as, Device, Msg}, Defaults, Opts) -> + ModifiedMsg = set( + Msg, + #{ <<"device">> => Device }, + internal_opts(Opts) + ), + get_many(Paths, ModifiedMsg, Defaults, Opts); +get_many(Paths, Msg, Defaults, Opts) -> + list_to_tuple( + lists:zipwith( + fun(Path, Default) -> + get(Path, Msg, Default, Opts) + end, + Paths, + Defaults + ) + ). %% @doc Shortcut to get the list of keys from a message. keys(Msg) -> keys(Msg, #{}). keys(Msg, Opts) -> keys(Msg, Opts, keep).