Skip to content

Commit

Permalink
fix: resolve name to contract address up to a given block hash
Browse files Browse the repository at this point in the history
  • Loading branch information
yaboiishere committed Apr 2, 2024
1 parent 2c95930 commit 066925e
Show file tree
Hide file tree
Showing 11 changed files with 144 additions and 99 deletions.
26 changes: 15 additions & 11 deletions lib/ae_mdw/contract.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ defmodule AeMdw.Contract do
AE smart contracts type (signatures) and previous calls information based on direct chain info.
"""

alias AeMdw.Blocks
alias AeMdw.Db.Name
alias AeMdw.EtsCache
alias AeMdw.Log
alias AeMdw.Node
Expand Down Expand Up @@ -215,13 +217,14 @@ defmodule AeMdw.Contract do
|> call_rec_from_id(contract_pk, block_hash)
end

@spec call_tx_info(tx(), DBN.pubkey(), block_hash()) :: {fun_arg_res_or_error(), call()}
def call_tx_info(tx_rec, contract_pk, block_hash) do
@spec call_tx_info(tx(), DBN.pubkey(), DBN.pubkey(), block_hash()) ::
{fun_arg_res_or_error(), call()}
def call_tx_info(tx_rec, contract_pk, contract_or_name_pk, block_hash) do
{:ok, {type_info, _compiler_vsn, _source_hash}} = get_info(contract_pk)
call_id = :aect_call_tx.call_id(tx_rec)
call_data = :aect_call_tx.call_data(tx_rec)

case :aec_chain.get_contract_call(contract_pk, call_id, block_hash) do
case :aec_chain.get_contract_call(contract_or_name_pk, call_id, block_hash) do
{:ok, call} ->
case :aect_call.return_type(call) do
:ok ->
Expand All @@ -244,7 +247,7 @@ defmodule AeMdw.Contract do

{:error, reason} ->
Log.error(
"call_tx_info error reason=#{inspect(reason)} params=#{inspect([contract_pk, call_id, block_hash])}"
"call_tx_info error reason=#{inspect(reason)} params=#{inspect([contract_or_name_pk, call_id, block_hash])}"
)

{{:error, reason}, nil}
Expand Down Expand Up @@ -348,19 +351,20 @@ defmodule AeMdw.Contract do
Enum.group_by(get_events(micro_block), fn {_event_name, %{tx_hash: tx_hash}} -> tx_hash end)
end

@spec maybe_resolve_contract_pk(name_pubkey() | contract_pubkey()) :: contract_pubkey()
def maybe_resolve_contract_pk(contract_or_name_pk) do
@spec maybe_resolve_contract_pk(name_pubkey() | contract_pubkey(), Blocks.block_hash()) ::
contract_pubkey()
def maybe_resolve_contract_pk(contract_or_name_pk, block_hash) do
contract_or_name_pk
|> case do
<<217, 52, 92, _rest::binary>> = name_pk ->
"contract_pubkey"
|> :aec_chain.resolve_namehash(name_pk)
block_hash
|> Name.ptr_resolve(name_pk, "contract_pubkey")
|> case do
{:ok, {:id, :contract, pubkey}} ->
pubkey
{:ok, contract_pk} ->
contract_pk

{:error, reason} ->
raise "#{__MODULE__}.maybe_resolve_contract_pk: failed to resolve contract pubkey with reason: #{inspect(reason)}"
raise "Contract not resolved: #{inspect(contract_or_name_pk)} with reason #{inspect(reason)}"
end

contract_pk ->
Expand Down
14 changes: 8 additions & 6 deletions lib/ae_mdw/db/format.ex
Original file line number Diff line number Diff line change
Expand Up @@ -125,15 +125,16 @@ defmodule AeMdw.Db.Format do
end

defp custom_raw_data(state, :contract_call_tx, tx, tx_rec, signed_tx, block_hash) do
contract_pk =
contract_or_name_pk =
tx_rec
|> :aect_call_tx.contract_id()
|> Db.id_pubkey()
|> Contract.maybe_resolve_contract_pk()

contract_pk = Contract.maybe_resolve_contract_pk(contract_or_name_pk, block_hash)

txi = tx.tx_index
fun_arg_res = AeMdw.Db.Contract.call_fun_arg_res(state, contract_pk, txi)
call_info = format_call_info(signed_tx, contract_pk, block_hash, txi)
call_info = format_call_info(signed_tx, contract_or_name_pk, block_hash, txi)
call_details = Map.merge(call_info, fun_arg_res)
update_in(tx, [:tx], &Map.merge(&1, call_details))
end
Expand Down Expand Up @@ -411,11 +412,12 @@ defmodule AeMdw.Db.Format do
end

defp custom_encode(state, :contract_call_tx, tx, tx_rec, signed_tx, txi, block_hash) do
contract_pk =
contract_or_name_pk =
tx_rec
|> :aect_call_tx.contract_id()
|> Db.id_pubkey()
|> Contract.maybe_resolve_contract_pk()

contract_pk = Contract.maybe_resolve_contract_pk(contract_or_name_pk, block_hash)

fun_arg_res =
state
Expand All @@ -424,7 +426,7 @@ defmodule AeMdw.Db.Format do

call_details =
signed_tx
|> format_call_info(contract_pk, block_hash, txi)
|> format_call_info(contract_or_name_pk, block_hash, txi)
|> Map.merge(fun_arg_res)

aexn_type = DbContract.get_aexn_type(state, contract_pk)
Expand Down
3 changes: 1 addition & 2 deletions lib/ae_mdw/db/mutations/contract_call_mutation.ex
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,13 @@ defmodule AeMdw.Db.ContractCallMutation do
@spec execute(t(), State.t()) :: State.t()
def execute(
%__MODULE__{
contract_pk: contract_or_name_pk,
contract_pk: contract_pk,
txi: txi,
fun_arg_res: fun_arg_res,
call_rec: call_rec
},
state
) do
contract_pk = Contract.maybe_resolve_contract_pk(contract_or_name_pk)
create_txi = Origin.tx_index!(state, {:contract, contract_pk})

state = DBContract.call_write(state, create_txi, txi, fun_arg_res)
Expand Down
16 changes: 12 additions & 4 deletions lib/ae_mdw/db/mutations/write_fields_mutation.ex
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,22 @@ defmodule AeMdw.Db.WriteFieldsMutation do
end)
end

defp resolve_pubkey(state, id, :spend_tx, :recipient_id, block_index) do
defp resolve_pubkey(state, id, type, field, block_index)
when type == :spend_tx and field == :recipient_id
when type == :contract_call_tx and field == :contract_id do
pointee =
case type do
:spend_tx -> "account_pubkey"
:contract_call_tx -> "contract_pubkey"
end

with {:name, name_hash} <- :aeser_id.specialize(id),
{:ok, account_pk} <- Name.ptr_resolve(state, block_index, name_hash) do
account_pk
{:ok, contract_pk} <- Name.ptr_resolve(state, block_index, name_hash, pointee) do
contract_pk
else
{:error, :name_revoked} ->
Log.warn(
"Revoked name used on spend! id: #{inspect(id)}, block_index: #{inspect(block_index)}"
"Revoked name used on contract call! id: #{inspect(id)}, block_index: #{inspect(block_index)}"
)

Name.last_update_pointee_pubkey(state, id)
Expand Down
26 changes: 21 additions & 5 deletions lib/ae_mdw/db/name.ex
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,23 @@ defmodule AeMdw.Db.Name do
plain_name
end

@spec ptr_resolve(state(), Blocks.block_index(), binary()) ::
@spec ptr_resolve(state(), Blocks.block_index(), binary(), binary()) ::
{:ok, binary()} | {:error, :name_revoked}
def ptr_resolve(state, block_index, name_hash) do
with {:ok, account_id} <-
:aens.resolve_hash("account_pubkey", name_hash, ns_tree!(state, block_index)) do
{:ok, Validate.id(account_id)}
def ptr_resolve(state, block_index, name_hash, pointer_key) do
with {:ok, id} <-
:aens.resolve_hash(pointer_key, name_hash, ns_tree!(state, block_index)),
{:ok, valid_id} <- Validate.id(id) do
{:ok, valid_id}
end
end

@spec ptr_resolve(Blocks.block_hash(), binary(), binary()) ::
{:ok, binary()} | {:error, :name_revoked}
def ptr_resolve(block_hash, name_hash, pointer_key) do
with {:ok, id} <-
:aens.resolve_hash(pointer_key, name_hash, ns_tree!(block_hash)),
{:ok, valid_id} <- Validate.id(id) do
{:ok, valid_id}
end
end

Expand Down Expand Up @@ -310,6 +321,11 @@ defmodule AeMdw.Db.Name do
state
|> State.fetch!(Model.Block, block_index)
|> Model.block(:hash)
|> ns_tree!()
end

defp ns_tree!(block_hash) do
block_hash
|> :aec_db.get_block_state()
|> :aec_trees.ns()
end
Expand Down
10 changes: 7 additions & 3 deletions lib/ae_mdw/db/sync/transaction.ex
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,6 @@ defmodule AeMdw.Db.Sync.Transaction do
contract_pk =
tx
|> :aect_create_tx.contract_pubkey()
|> Contract.maybe_resolve_contract_pk()

mutations = Origin.origin_mutations(:contract_create_tx, nil, contract_pk, txi, tx_hash)

Expand Down Expand Up @@ -175,9 +174,14 @@ defmodule AeMdw.Db.Sync.Transaction do
|> :aect_call_tx.contract_id()
|> Db.id_pubkey()

contract_pk = Contract.maybe_resolve_contract_pk(contract_or_name_pk)
contract_pk =
Contract.maybe_resolve_contract_pk(
contract_or_name_pk,
block_hash
)

{fun_arg_res, call_rec} = Contract.call_tx_info(tx, contract_pk, block_hash)
{fun_arg_res, call_rec} =
Contract.call_tx_info(tx, contract_pk, contract_or_name_pk, block_hash)

events_mutations =
SyncContract.events_mutations(
Expand Down
77 changes: 53 additions & 24 deletions test/ae_mdw/contract_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -88,56 +88,85 @@ defmodule AeMdw.ContractTest do
get_contract_call: fn ^contract_pk, ^call_id, ^block_hash -> {:ok, call} end
]}
] do
assert {:error, ^call} = Contract.call_tx_info(call_tx, contract_pk, block_hash)
assert {:error, ^call} =
Contract.call_tx_info(call_tx, contract_pk, contract_pk, block_hash)
end
end
end

describe "maybe_resolve_contract_pk/1" do
describe "maybe_resolve_contract_pk/2" do
setup do
contract_pk = <<1::256>>

name_pk =
<<217, 52, 92, 99, 90, 59, 250, 112, 123, 114, 18, 135, 95, 95, 205, 71, 74, 160, 14, 22,
47, 119, 229, 124, 220, 96, 81, 40, 117, 5, 23, 138>>

%{contract_pk: contract_pk, name_pk: name_pk}
end
fake_mb = "mh_2XGZtE8jxUs2NymKHsytkaLLrk6KY2t2w1FjJxtAUqYZn8Wsdd"

test "it returns contract_pk when name contains contract_pubkey pointer", %{
contract_pk: contract_pk,
name_pk: name_pk
} do
with_mocks [
{:aec_chain, [],
success_mock =
{:aens, [],
[
resolve_namehash: fn "contract_pubkey", ^name_pk ->
resolve_hash: fn "contract_pubkey", ^name_pk, _block ->
{:ok, {:id, :contract, contract_pk}}
end
]}
] do
assert ^contract_pk = Contract.maybe_resolve_contract_pk(name_pk)

fail_mock =
{:aens, [],
[
resolve_hash: fn "contract_pubkey", ^name_pk, _block ->
{:error, :name_not_resolved}
end
]}

common_mocks = [
{:aec_db, [],
[
get_block_state: fn _block_hash -> {:ok, %{}} end
]},
{:aec_trees, [],
[
ns: fn _ -> {:ok, %{}} end
]}
]

%{
contract_pk: contract_pk,
name_pk: name_pk,
fake_mb: fake_mb,
success_mocks: [success_mock | common_mocks],
fail_mocks: [fail_mock | common_mocks]
}
end

test "it returns contract_pk when name contains contract_pubkey pointer", %{
contract_pk: contract_pk,
name_pk: name_pk,
fake_mb: fake_mb,
success_mocks: success_mocks
} do
with_mocks(success_mocks) do
assert ^contract_pk = Contract.maybe_resolve_contract_pk(name_pk, fake_mb)
end
end

test "it returns contract_pk when contract_pk is passed", %{
contract_pk: contract_pk
contract_pk: contract_pk,
fake_mb: fake_mb
} do
assert ^contract_pk = Contract.maybe_resolve_contract_pk(contract_pk)
assert ^contract_pk = Contract.maybe_resolve_contract_pk(contract_pk, fake_mb)
end

test "it returns :error when contract isn't resolved", %{
name_pk: name_pk
name_pk: name_pk,
fake_mb: fake_mb,
fail_mocks: fail_mocks
} do
with_mocks [
{:aec_chain, [],
[
resolve_namehash: fn "contract_pubkey", ^name_pk -> {:error, :state_trees} end
]}
] do
with_mocks fail_mocks do
assert_raise RuntimeError,
"Elixir.AeMdw.Contract.maybe_resolve_contract_pk: failed to resolve contract pubkey",
fn -> Contract.maybe_resolve_contract_pk(name_pk) end
"Contract not resolved: #{inspect(name_pk)} with reason :name_not_resolved",
fn -> Contract.maybe_resolve_contract_pk(name_pk, fake_mb) end
end
end
end
Expand Down

0 comments on commit 066925e

Please sign in to comment.