Skip to content
This repository has been archived by the owner on Aug 2, 2022. It is now read-only.

Commit

Permalink
Merge pull request #12 from eosnetworkfoundation/get-code-hash
Browse files Browse the repository at this point in the history
get_code_hash
  • Loading branch information
tbfleming authored Jan 19, 2022
2 parents 0bc4b18 + bd89c6b commit f602dfa
Show file tree
Hide file tree
Showing 11 changed files with 186 additions and 19 deletions.
20 changes: 20 additions & 0 deletions libraries/chain/apply_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,26 @@ bool apply_context::is_account( const account_name& account )const {
return nullptr != db.find<account_object,by_name>( account );
}

void apply_context::get_code_hash(
account_name account, uint64_t& code_sequence, fc::sha256& code_hash, uint8_t& vm_type, uint8_t& vm_version) const {

auto obj = db.find<account_metadata_object,by_name>(account);
if(!obj || obj->code_hash == fc::sha256{}) {
if(obj)
code_sequence = obj->code_sequence;
else
code_sequence = 0;
code_hash = {};
vm_type = 0;
vm_version = 0;
} else {
code_sequence = obj->code_sequence;
code_hash = obj->code_hash;
vm_type = obj->vm_type;
vm_version = obj->vm_version;
}
}

void apply_context::require_authorization( const account_name& account ) {
for( uint32_t i=0; i < act->authorization.size(); i++ ) {
if( act->authorization[i].actor == account ) {
Expand Down
8 changes: 8 additions & 0 deletions libraries/chain/controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,7 @@ struct controller_impl {
set_activation_handler<builtin_protocol_feature_t::action_return_value>();
set_activation_handler<builtin_protocol_feature_t::configurable_wasm_limits>();
set_activation_handler<builtin_protocol_feature_t::blockchain_parameters>();
set_activation_handler<builtin_protocol_feature_t::get_code_hash>();

self.irreversible_block.connect([this](const block_state_ptr& bsp) {
wasmif.current_lib(bsp->block_num);
Expand Down Expand Up @@ -3428,6 +3429,13 @@ void controller_impl::on_activation<builtin_protocol_feature_t::blockchain_param
} );
}

template<>
void controller_impl::on_activation<builtin_protocol_feature_t::get_code_hash>() {
db.modify( db.get<protocol_state_object>(), [&]( auto& ps ) {
add_intrinsic_to_whitelist( ps.whitelisted_intrinsics, "get_code_hash" );
} );
}

/// End of protocol feature activation handlers

} } /// eosio::chain
3 changes: 3 additions & 0 deletions libraries/chain/include/eosio/chain/apply_context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,9 @@ class apply_context {
*/
bool is_account(const account_name& account)const;

void get_code_hash(
account_name account, uint64_t& code_sequence, fc::sha256& code_hash, uint8_t& vm_type, uint8_t& vm_version) const;

/**
* Requires that the current action be delivered to account
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ enum class builtin_protocol_feature_t : uint32_t {
wtmsig_block_signatures,
action_return_value,
configurable_wasm_limits,
blockchain_parameters
blockchain_parameters,
get_code_hash,
};

struct protocol_feature_subjective_restrictions {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,8 @@ inline constexpr auto get_intrinsic_table() {
"env.get_wasm_parameters_packed",
"env.set_wasm_parameters_packed",
"env.get_parameters_packed",
"env.set_parameters_packed"
"env.set_parameters_packed",
"env.get_code_hash"
);
}
inline constexpr std::size_t find_intrinsic_index(std::string_view hf) {
Expand Down
37 changes: 31 additions & 6 deletions libraries/chain/include/eosio/chain/webassembly/interface.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,31 @@ namespace webassembly {
*/
bool is_account(account_name account) const;

/**
* Retrieves the code hash for an account, if any.
*
* The result is the packed version of this struct:
*
* struct {
* varuint32 struct_version;
* uint64_t code_sequence;
* fc::sha256 code_hash;
* uint8_t vm_type;
* uint8_t vm_version;
* } result;
*
* @ingroup authorization
* @param account - name of the account to check.
* @param struct_version - use 0.
* @param packed_result - receives the packed result.
*
* @return the size of the packed result.
*/
uint32_t get_code_hash(
account_name account,
uint32_t struct_version,
vm::span<char> packed_result) const;

/**
* Returns the time in microseconds from 1970 of the current block.
*
Expand Down Expand Up @@ -734,13 +759,13 @@ namespace webassembly {
* @param itr - iterator to the table row containing the record to update.
* @param payer - the account that pays for the storage costs.
* @param buffer - new updated record.
*
* @remark This function does not allow changing the primary key of a
* table row. The serialized data that is stored in the table row of a
* primary table may include a primary key and that primary key value
* could be changed by the contract calling the db_update_i64 intrinsic;
*
* @remark This function does not allow changing the primary key of a
* table row. The serialized data that is stored in the table row of a
* primary table may include a primary key and that primary key value
* could be changed by the contract calling the db_update_i64 intrinsic;
* but that does not change the actual primary key of the table row.
*
*
* @pre `itr` points to an existing table row in the table.
* @post the record contained in the table row pointed to by `itr` is replaced with the new updated record.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ namespace eosio { namespace chain { namespace webassembly {
inline static bool is_aliasing(const T& s1, const U& s2) {
std::uintptr_t a_begin = reinterpret_cast<std::uintptr_t>(s1.data());
std::uintptr_t a_end = a_begin + s1.size_bytes();

std::uintptr_t b_begin = reinterpret_cast<std::uintptr_t>(s2.data());
std::uintptr_t b_end = b_begin + s2.size_bytes();

Expand All @@ -86,7 +86,7 @@ namespace eosio { namespace chain { namespace webassembly {

// Intersection interval is [b_begin, std::min(a_end, b_end)).

if (std::min(a_end, b_end) == b_begin) // intersection interval has zero size
if (std::min(a_end, b_end) == b_begin) // intersection interval has zero size
return false;

return true;
Expand Down Expand Up @@ -120,8 +120,8 @@ namespace eosio { namespace chain { namespace webassembly {

namespace detail {
template<typename T>
vm::span<const char> to_span(const vm::argument_proxy<T*>& val) {
return {static_cast<const char*>(val.get_original_pointer()), sizeof(T)};
vm::span<const char> to_span(const vm::argument_proxy<T*>& val) {
return {static_cast<const char*>(val.get_original_pointer()), sizeof(T)};
}

template<typename T>
Expand Down
11 changes: 11 additions & 0 deletions libraries/chain/protocol_feature_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,17 @@ Builtin protocol feature: BLOCKCHAIN_PARAMETERS
Allows privileged contracts to get and set subsets of blockchain parameters.
*/
( builtin_protocol_feature_t::get_code_hash, builtin_protocol_feature_spec{
"GET_CODE_HASH",
fc::variant("d2596697fed14a0840013647b99045022ae6a885089f35a7e78da7a43ad76ed4").as<digest_type>(),
// SHA256 hash of the raw message below within the comment delimiters (do not modify message below).
/*
Builtin protocol feature: GET_CODE_HASH
Enables new `get_code_hash` intrinsic which gets the current code hash of an account.
*/
{}
} )
;


Expand Down
27 changes: 27 additions & 0 deletions libraries/chain/webassembly/authorization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,31 @@ namespace eosio { namespace chain { namespace webassembly {
bool interface::is_account( account_name account ) const {
return context.is_account( account );
}

struct get_code_hash_result {
unsigned_int struct_version;
uint64_t code_sequence;
fc::sha256 code_hash;
uint8_t vm_type;
uint8_t vm_version;
};

uint32_t interface::get_code_hash(
account_name account,
uint32_t struct_version,
vm::span<char> packed_result
) const {
struct_version = std::min(uint32_t(0), struct_version);
get_code_hash_result result = {struct_version};
context.get_code_hash(account, result.code_sequence, result.code_hash, result.vm_type, result.vm_version);

auto s = fc::raw::pack_size(result);
if (s <= packed_result.size()) {
datastream<char*> ds(packed_result.data(), s);
fc::raw::pack(ds, result);
}
return s;
}
}}} // ns eosio::chain::webassembly

FC_REFLECT(eosio::chain::webassembly::get_code_hash_result, (struct_version)(code_sequence)(code_hash)(vm_type)(vm_version))
3 changes: 2 additions & 1 deletion libraries/chain/webassembly/runtimes/eos-vm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ std::unique_ptr<wasm_instantiated_module_interface> eos_vm_runtime<Impl>::instan
template class eos_vm_runtime<eosio::vm::interpreter>;
template class eos_vm_runtime<eosio::vm::jit>;

}
}

template <auto HostFunction, typename... Preconditions>
struct host_function_registrator {
Expand Down Expand Up @@ -358,6 +358,7 @@ REGISTER_HOST_FUNCTION(require_auth2);
REGISTER_HOST_FUNCTION(has_auth);
REGISTER_HOST_FUNCTION(require_recipient);
REGISTER_HOST_FUNCTION(is_account);
REGISTER_HOST_FUNCTION(get_code_hash);

// system api
REGISTER_HOST_FUNCTION(current_time);
Expand Down
82 changes: 76 additions & 6 deletions unittests/api_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1230,7 +1230,7 @@ BOOST_AUTO_TEST_CASE(deferred_inline_action_subjective_limit_failure) { try {
} );
CALL_TEST_FUNCTION(chain, "test_transaction", "send_deferred_transaction_4k_action", {} );
BOOST_CHECK(!trace);
BOOST_CHECK_EXCEPTION(chain.produce_block( fc::seconds(2) ), fc::exception,
BOOST_CHECK_EXCEPTION(chain.produce_block( fc::seconds(2) ), fc::exception,
[](const fc::exception& e) {
return expect_assert_message(e, "inline action too big for nonprivileged account");
}
Expand Down Expand Up @@ -3061,8 +3061,8 @@ BOOST_AUTO_TEST_CASE(action_results_tests) { try {
t.set_code( config::system_account_name, contracts::action_results_wasm() );
t.produce_blocks(1);
call_autoresret_and_check( config::system_account_name,
config::system_account_name,
"retmaxlim"_n,
config::system_account_name,
"retmaxlim"_n,
[&]( const transaction_trace_ptr& res ) {
BOOST_CHECK_EQUAL( res->receipt->status, transaction_receipt::executed );

Expand All @@ -3077,12 +3077,82 @@ BOOST_AUTO_TEST_CASE(action_results_tests) { try {
expected_vec.end() );
} );
t.produce_blocks(1);
BOOST_REQUIRE_THROW(call_autoresret_and_check( config::system_account_name,
config::system_account_name,
"setliminv"_n,
BOOST_REQUIRE_THROW(call_autoresret_and_check( config::system_account_name,
config::system_account_name,
"setliminv"_n,
[&]( auto res ) {}),
action_validate_exception);

} FC_LOG_AND_RETHROW() }

static const char get_code_hash_wast[] = R"=====(
(module
(import "env" "get_code_hash" (func $get_code_hash (param i64 i32 i32 i32) (result i32)))
(import "env" "prints_l" (func $prints_l (param i32 i32)))
(import "env" "printui" (func $printui (param i64)))
(import "env" "printhex" (func $printhex (param i32 i32)))
(memory $0 32)
(data (i32.const 4) ":")
(data (i32.const 8) "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
(export "apply" (func $apply))
(func $apply (param $0 i64) (param $1 i64) (param $2 i64)
(call $printui (i64.extend_u/i32
(call $get_code_hash
(get_local $2)
(i32.const 0)
(i32.const 8)
(i32.const 43)
)
))
(call $prints_l (i32.const 4) (i32.const 1))
(call $printui (i64.load8_u offset=8 (i32.const 0)))
(call $prints_l (i32.const 4) (i32.const 1))
(call $printui (i64.load offset=9 (i32.const 0)))
(call $prints_l (i32.const 4) (i32.const 1))
(call $printhex (i32.const 17) (i32.const 32))
(call $prints_l (i32.const 4) (i32.const 1))
(call $printui (i64.load8_u offset=49 (i32.const 0)))
(call $prints_l (i32.const 4) (i32.const 1))
(call $printui (i64.load8_u offset=50 (i32.const 0)))
)
)
)=====";

BOOST_AUTO_TEST_CASE(get_code_hash_tests) { try {
TESTER t;
t.produce_blocks(2);
t.create_account("gethash"_n);
t.create_account("test"_n);
t.set_code("gethash"_n, get_code_hash_wast);
t.produce_blocks(1);

auto check = [&](account_name acc, uint64_t expected_seq) {
fc::sha256 expected_code_hash;
auto obj = t.control->db().find<account_metadata_object,by_name>(acc);
if(obj)
expected_code_hash = obj->code_hash;
auto expected = "43:0:" + std::to_string(expected_seq) +
":" + expected_code_hash.str() + ":0:0";

signed_transaction trx;
trx.actions.emplace_back(vector<permission_level>{{"gethash"_n, config::active_name}}, "gethash"_n, acc, bytes{});
t.set_transaction_headers(trx, t.DEFAULT_EXPIRATION_DELTA);
trx.sign(t.get_private_key("gethash"_n, "active"), t.control->get_chain_id());
auto tx_trace = t.push_transaction(trx);
BOOST_CHECK_EQUAL(tx_trace->receipt->status, transaction_receipt::executed);
BOOST_REQUIRE(tx_trace->action_traces.front().console == expected);
t.produce_block();
};

check("gethash"_n, 1);
check("nonexisting"_n, 0);
check("test"_n, 0);
t.set_code("test"_n, contracts::test_api_wasm());
check("test"_n, 1);
t.set_code("test"_n, get_code_hash_wast);
check("test"_n, 2);
t.set_code("test"_n, std::vector<uint8_t>{});
check("test"_n, 3);
} FC_LOG_AND_RETHROW() }

BOOST_AUTO_TEST_SUITE_END()

0 comments on commit f602dfa

Please sign in to comment.