Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Invoke system call RPC #828

Merged
merged 8 commits into from
Oct 24, 2023
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