Skip to content

Commit

Permalink
Merge pull request #828 from koinos/827-invoke-system-call-thunks
Browse files Browse the repository at this point in the history
Invoke system call RPC
  • Loading branch information
mvandeberg authored Oct 24, 2023
2 parents 7014100 + 10c2751 commit 2221d2f
Show file tree
Hide file tree
Showing 13 changed files with 6,737 additions and 129 deletions.
51 changes: 39 additions & 12 deletions libraries/chain/controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <koinos/chain/constants.hpp>
#include <koinos/chain/controller.hpp>
#include <koinos/chain/exceptions.hpp>
#include <koinos/chain/host_api.hpp>
#include <koinos/chain/state.hpp>
#include <koinos/chain/system_calls.hpp>

Expand Down Expand Up @@ -79,7 +80,7 @@ std::string format_time( int64_t time )
class controller_impl final
{
public:
controller_impl( uint64_t read_compute_bandwith_limit );
controller_impl( uint64_t read_compute_bandwith_limit, uint32_t syscall_bufsize );
~controller_impl();

void open( const std::filesystem::path& p, const genesis_data& data, fork_resolution_algorithm algo, bool reset );
Expand Down Expand Up @@ -108,6 +109,7 @@ class controller_impl final
std::shared_ptr< vm_manager::vm_backend > _vm_backend;
std::shared_ptr< mq::client > _client;
uint64_t _read_compute_bandwidth_limit;
uint32_t _syscall_bufsize;
std::shared_mutex _cached_head_block_mutex;
std::shared_ptr< const protocol::block > _cached_head_block;

Expand All @@ -117,7 +119,9 @@ class controller_impl final
fork_data get_fork_data( state_db::shared_lock_ptr db_lock );
};

controller_impl::controller_impl( uint64_t read_compute_bandwidth_limit ) : _read_compute_bandwidth_limit( read_compute_bandwidth_limit )
controller_impl::controller_impl( uint64_t read_compute_bandwidth_limit, uint32_t syscall_bufsize ) :
_read_compute_bandwidth_limit( read_compute_bandwidth_limit ),
_syscall_bufsize( syscall_bufsize )
{
_vm_backend = vm_manager::get_vm_backend(); // Default is fizzy
KOINOS_ASSERT( _vm_backend, unknown_backend_exception, "could not get vm backend" );
Expand Down Expand Up @@ -842,35 +846,58 @@ rpc::chain::invoke_system_call_response controller_impl::invoke_system_call( con
);

execution_context ctx( _vm_backend, intent::read_only );
ctx.push_frame( stack_frame {
.call_privilege = privilege::user_mode
} );

stack_frame sframe;

if ( request.has_caller_data() )
{
sframe.contract_id = request.caller_data().caller();
sframe.call_privilege = request.caller_data().caller_privilege();
}
else
{
sframe.call_privilege = privilege::kernel_mode;
}

ctx.push_frame( std::move( sframe ) );

ctx.set_state_node( _db.get_head( _db.get_shared_lock() )->create_anonymous_node() );
ctx.reset_cache();

koinos::chain::execution_result res;
resource_limit_data rl;
rl.set_compute_bandwidth_limit( _read_compute_bandwidth_limit );

ctx.resource_meter().set_resource_limit_data( rl );

system_call_id syscall_id;

if ( request.has_id() )
{
res = ctx.system_call( static_cast< uint32_t >( request.id() ), request.args() );
syscall_id = system_call_id( request.id() );
}
else
{
system_call_id val;
if ( !system_call_id_Parse( request.name(), &val ) )
if ( !system_call_id_Parse( request.name(), &syscall_id ) )
KOINOS_THROW( unknown_system_call_exception, "unknown system call name" );
res = ctx.system_call( val, request.args() );
}

koinos::chain::host_api hapi( ctx );

std::vector< char > buffer( _syscall_bufsize, 0 );
uint32_t bytes_written;
rpc::chain::invoke_system_call_response resp;
resp.set_value( res.res.object() );

hapi.call( syscall_id, &buffer[0], _syscall_bufsize, request.args().c_str(), uint32_t( request.args().size() ), &bytes_written );

resp.set_value( std::string( &buffer[0], bytes_written ) );

return resp;
}

} // detail

controller::controller( uint64_t read_compute_bandwith_limit ) : _my( std::make_unique< detail::controller_impl >( read_compute_bandwith_limit ) ) {}
controller::controller( uint64_t read_compute_bandwith_limit, uint32_t syscall_bufsize ) :
_my( std::make_unique< detail::controller_impl >( read_compute_bandwith_limit, syscall_bufsize ) ) {}

controller::~controller() = default;

Expand Down
6 changes: 1 addition & 5 deletions libraries/chain/execution_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@

namespace koinos::chain {

namespace constants {
const std::string system = "";
}

execution_context::execution_context( std::shared_ptr< vm_manager::vm_backend > vm_backend, chain::intent i ) :
_vm_backend( vm_backend )
{
Expand Down Expand Up @@ -159,7 +155,7 @@ privilege execution_context::get_privilege() const

const std::string& execution_context::get_contract_id() const
{
for ( auto i = _stack.size() - 1; i >= 0; --i )
for ( auto i = _stack.size(); i-- > 0; )
{
if ( _stack[ i ].contract_id.size() )
return _stack[ i ].contract_id;
Expand Down
85 changes: 42 additions & 43 deletions libraries/chain/host_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ namespace koinos::chain {
host_api::host_api( execution_context& ctx ) : _ctx( ctx ) {}
host_api::~host_api() {}

int32_t host_api::invoke_thunk( uint32_t tid, char* ret_ptr, uint32_t ret_len, const char* arg_ptr, uint32_t arg_len, uint32_t* bytes_written )
int32_t host_api::invoke_thunk( uint32_t tid, char* ret_ptr, uint32_t ret_len, const char* arg_ptr, uint32_t arg_len, uint32_t* bytes_written )
{
KOINOS_ASSERT( _ctx.get_privilege() == privilege::kernel_mode, insufficient_privileges_exception, "'invoke_thunk' must be called from a system context" );

Expand Down Expand Up @@ -63,54 +63,53 @@ int32_t host_api::invoke_thunk( uint32_t tid, char* ret_ptr, uint32_t ret_len, c
return code;
}

int32_t host_api::invoke_system_call( uint32_t sid, char* ret_ptr, uint32_t ret_len, const char* arg_ptr, uint32_t arg_len, uint32_t* bytes_written )
void host_api::call( uint32_t sid, char* ret_ptr, uint32_t ret_len, const char* arg_ptr, uint32_t arg_len, uint32_t* bytes_written )
{
*bytes_written = 0;

with_stack_frame(
_ctx,
stack_frame {
.sid = sid,
.call_privilege = privilege::kernel_mode
},
[&]() {
if ( _ctx.system_call_exists( sid ) )
{
std::string args( arg_ptr, arg_len );
auto exec_res = _ctx.system_call( sid, args );

if ( exec_res.res.has_object() )
{
auto obj_len = exec_res.res.object().size();
KOINOS_ASSERT( obj_len <= ret_len, insufficient_return_buffer_exception, "return buffer is not large enough for the return value" );
memcpy( ret_ptr, exec_res.res.object().data(), obj_len );
*bytes_written = uint32_t( obj_len );
}
}
else
{
auto thunk_id = _ctx.thunk_translation( sid );
KOINOS_ASSERT( thunk_dispatcher::instance().thunk_exists( thunk_id ), unknown_thunk_exception, "thunk ${tid} does not exist", ("tid", thunk_id) );
auto desc = chain::system_call_id_descriptor();
auto enum_value = desc->FindValueByNumber( thunk_id );
KOINOS_ASSERT( enum_value, unknown_thunk_exception, "unrecognized thunk id ${id}", ("id", thunk_id) );
auto compute = _ctx.get_compute_bandwidth( enum_value->name() );
_ctx.resource_meter().use_compute_bandwidth( compute );
thunk_dispatcher::instance().call_thunk( thunk_id, _ctx, ret_ptr, ret_len, arg_ptr, arg_len, bytes_written );
}
}
);
}

int32_t host_api::invoke_system_call( uint32_t sid, char* ret_ptr, uint32_t ret_len, const char* arg_ptr, uint32_t arg_len, uint32_t* bytes_written )
{
int32_t code = 0;
error_data error;

try
{
auto key = util::converter::as< std::string >( sid );
database_object object;

with_stack_frame(
_ctx,
stack_frame {
.sid = sid,
.call_privilege = privilege::kernel_mode
},
[&]() {
if ( _ctx.system_call_exists( sid ) )
{
std::string args( arg_ptr, arg_len );
auto res = _ctx.system_call( sid, args );
code = res.code;

if ( code )
error = res.res.error();
else if ( res.res.has_object() )
{
auto obj_len = res.res.object().size();
KOINOS_ASSERT( obj_len <= ret_len, insufficient_return_buffer_exception, "return buffer is not large enough for the return value" );
memcpy( ret_ptr, res.res.object().data(), obj_len );
*bytes_written = uint32_t( obj_len );
}
else
*bytes_written = 0;
}
else
{
auto thunk_id = _ctx.thunk_translation( sid );
KOINOS_ASSERT( thunk_dispatcher::instance().thunk_exists( thunk_id ), unknown_thunk_exception, "thunk ${tid} does not exist", ("tid", thunk_id) );
auto desc = chain::system_call_id_descriptor();
auto enum_value = desc->FindValueByNumber( thunk_id );
KOINOS_ASSERT( enum_value, unknown_thunk_exception, "unrecognized thunk id ${id}", ("id", thunk_id) );
auto compute = _ctx.get_compute_bandwidth( enum_value->name() );
_ctx.resource_meter().use_compute_bandwidth( compute );
thunk_dispatcher::instance().call_thunk( thunk_id, _ctx, ret_ptr, ret_len, arg_ptr, arg_len, bytes_written );
}
}
);
call( sid, ret_ptr, ret_len, arg_ptr, arg_len, bytes_written );
}
catch ( const koinos::exception& e )
{
Expand Down
2 changes: 1 addition & 1 deletion libraries/chain/include/koinos/chain/controller.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ enum class fork_resolution_algorithm
class controller final
{
public:
controller( uint64_t read_compute_bandwith_limit = 0 );
controller( uint64_t read_compute_bandwith_limit = 0, uint32_t syscall_bufsize = 0 );
~controller();

void open( const std::filesystem::path& p, const chain::genesis_data& data, fork_resolution_algorithm algo, bool reset );
Expand Down
4 changes: 4 additions & 0 deletions libraries/chain/include/koinos/chain/execution_context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@

namespace koinos::chain {

namespace constants {
const std::string system = std::string{};
}

using koinos::state_db::state_node_ptr;
using koinos::state_db::anonymous_state_node_ptr;
using koinos::state_db::abstract_state_node;
Expand Down
1 change: 1 addition & 0 deletions libraries/chain/include/koinos/chain/host_api.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class host_api final : public vm_manager::abstract_host_api

execution_context& _ctx;

void call( uint32_t sid, char* ret_ptr, uint32_t ret_len, const char* arg_ptr, uint32_t arg_len, uint32_t* bytes_written );
virtual int32_t invoke_thunk( uint32_t tid, char* ret_ptr, uint32_t ret_len, const char* arg_ptr, uint32_t arg_len, uint32_t* bytes_written ) override;
virtual int32_t invoke_system_call( uint32_t sid, char* ret_ptr, uint32_t ret_len, const char* arg_ptr, uint32_t arg_len, uint32_t* bytes_written ) override;
virtual int64_t get_meter_ticks() const override;
Expand Down
9 changes: 7 additions & 2 deletions programs/koinos_chain/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@
#define GENESIS_DATA_FILE_DEFAULT "genesis_data.json"
#define READ_COMPUTE_BANDWITH_LIMIT_OPTION "read-compute-bandwidth-limit"
#define READ_COMPUTE_BANDWITH_LIMIT_DEFAULT 10'000'000
#define SYSTEM_CALL_BUFFER_SIZE_OPTION "system-call-buffer-size"
#define SYSTEM_CALL_BUFFER_SIZE_DEFAULT 64'000
#define FORK_ALGORITHM_OPTION "fork-algorithm"
#define FORK_ALGORITHM_DEFAULT FIFO_ALGORITHM

Expand All @@ -81,6 +83,7 @@ int main( int argc, char** argv )
std::string amqp_url, log_level, log_dir, instance_id, fork_algorithm_option;
std::filesystem::path statedir, genesis_data_file;
uint64_t jobs, read_compute_limit;
int32_t syscall_bufsize;
chain::genesis_data genesis_data;
bool reset, log_color, log_datetime;
chain::fork_resolution_algorithm fork_algorithm;
Expand All @@ -105,7 +108,8 @@ int main( int argc, char** argv )
(FORK_ALGORITHM_OPTION ",f", program_options::value< std::string >(), "The fork resolution algorithm to use. Can be 'fifo', 'pob', or 'block-time'. (Default: 'fifo')")
(LOG_DIR_OPTION , program_options::value< std::string >(), "The logging directory")
(LOG_COLOR_OPTION , program_options::value< bool >(), "Log color toggle")
(LOG_DATETIME_OPTION , program_options::value< bool >(), "Log datetime on console toggle");
(LOG_DATETIME_OPTION , program_options::value< bool >(), "Log datetime on console toggle")
(SYSTEM_CALL_BUFFER_SIZE_OPTION , program_options::value< uint32_t >(), "System call RPC invocation buffer size");

program_options::variables_map args;
program_options::store( program_options::parse_command_line( argc, argv, options ), args );
Expand Down Expand Up @@ -157,6 +161,7 @@ int main( int argc, char** argv )
jobs = util::get_option< uint64_t >( JOBS_OPTION, std::max( JOBS_DEFAULT, uint64_t( std::thread::hardware_concurrency() ) ), args, chain_config, global_config );
read_compute_limit = util::get_option< uint64_t >( READ_COMPUTE_BANDWITH_LIMIT_OPTION, READ_COMPUTE_BANDWITH_LIMIT_DEFAULT, args, chain_config, global_config );
fork_algorithm_option = util::get_option< std::string >( FORK_ALGORITHM_OPTION, FORK_ALGORITHM_DEFAULT, args, chain_config, global_config );
syscall_bufsize = util::get_option< uint32_t >( SYSTEM_CALL_BUFFER_SIZE_OPTION, SYSTEM_CALL_BUFFER_SIZE_DEFAULT, args, chain_config, global_config );

std::optional< std::filesystem::path > logdir_path;
if ( !log_dir.empty() )
Expand Down Expand Up @@ -240,7 +245,7 @@ int main( int argc, char** argv )
asio::io_context client_ioc, server_ioc, main_ioc;
auto client = std::make_shared< mq::client >( client_ioc );
auto request_handler = mq::request_handler( server_ioc );
chain::controller controller( read_compute_limit );
chain::controller controller( read_compute_limit, syscall_bufsize );

try
{
Expand Down
1 change: 1 addition & 0 deletions tests/include/koinos/tests/contracts.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const std::string& get_hello_wasm();
const std::string& get_koin_wasm();
const std::string& get_null_bytes_written_wasm();
const std::string& get_syscall_override_wasm();
const std::string& get_syscall_rpc_wasm();

const std::string& get_call_contract_wasm();
const std::string& get_call_system_call_wasm();
Expand Down
Loading

0 comments on commit 2221d2f

Please sign in to comment.