Add return data implementation

This consists of:
 - syscalls
 - passing return data from invoked to invoker
 - printing to stable log
 - rust and C SDK changes
This commit is contained in:
Sean Young
2021-09-01 10:14:01 +01:00
parent 2dee098b91
commit 098585234d
23 changed files with 641 additions and 35 deletions

View File

@@ -8,6 +8,7 @@
#include <sol/log.h>
#include <sol/assert.h>
#include <sol/deserialize.h>
#include <sol/return_data.h>
static const uint8_t TEST_SUCCESS = 1;
static const uint8_t TEST_PRIVILEGE_ESCALATION_SIGNER = 2;
@@ -26,6 +27,7 @@ static const uint8_t TEST_WRITABLE_DEESCALATION_WRITABLE = 14;
static const uint8_t TEST_NESTED_INVOKE_TOO_DEEP = 15;
static const uint8_t TEST_EXECUTABLE_LAMPORTS = 16;
static const uint8_t ADD_LAMPORTS = 17;
static const uint8_t TEST_RETURN_DATA_TOO_LARGE = 18;
static const int MINT_INDEX = 0;
static const int ARGUMENT_INDEX = 1;
@@ -174,6 +176,32 @@ extern uint64_t entrypoint(const uint8_t *input) {
sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts)));
}
sol_log("Test return data");
{
SolAccountMeta arguments[] = {{accounts[ARGUMENT_INDEX].key, true, true}};
uint8_t data[] = { SET_RETURN_DATA };
uint8_t buf[100];
const SolInstruction instruction = {accounts[INVOKED_PROGRAM_INDEX].key,
arguments, SOL_ARRAY_SIZE(arguments),
data, SOL_ARRAY_SIZE(data)};
// set some return data, so that the callee can check it is cleared
sol_set_return_data((uint8_t[]){1, 2, 3, 4}, 4);
sol_assert(SUCCESS ==
sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts)));
SolPubkey setter;
uint64_t ret = sol_get_return_data(data, sizeof(data), &setter);
sol_assert(ret == sizeof(RETURN_DATA_VAL));
sol_assert(sol_memcmp(data, RETURN_DATA_VAL, sizeof(RETURN_DATA_VAL)));
sol_assert(SolPubkey_same(&setter, accounts[INVOKED_PROGRAM_INDEX].key));
}
sol_log("Test create_program_address");
{
uint8_t seed1[] = {'Y', 'o', 'u', ' ', 'p', 'a', 's', 's',
@@ -542,27 +570,33 @@ extern uint64_t entrypoint(const uint8_t *input) {
break;
}
case TEST_EXECUTABLE_LAMPORTS: {
sol_log("Test executable lamports");
accounts[ARGUMENT_INDEX].executable = true;
*accounts[ARGUMENT_INDEX].lamports -= 1;
*accounts[DERIVED_KEY1_INDEX].lamports +=1;
SolAccountMeta arguments[] = {
{accounts[ARGUMENT_INDEX].key, true, false},
{accounts[DERIVED_KEY1_INDEX].key, true, false},
};
uint8_t data[] = {ADD_LAMPORTS, 0, 0, 0};
SolPubkey program_id;
sol_memcpy(&program_id, params.program_id, sizeof(SolPubkey));
const SolInstruction instruction = {&program_id,
arguments, SOL_ARRAY_SIZE(arguments),
data, SOL_ARRAY_SIZE(data)};
sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts));
*accounts[ARGUMENT_INDEX].lamports += 1;
break;
sol_log("Test executable lamports");
accounts[ARGUMENT_INDEX].executable = true;
*accounts[ARGUMENT_INDEX].lamports -= 1;
*accounts[DERIVED_KEY1_INDEX].lamports +=1;
SolAccountMeta arguments[] = {
{accounts[ARGUMENT_INDEX].key, true, false},
{accounts[DERIVED_KEY1_INDEX].key, true, false},
};
uint8_t data[] = {ADD_LAMPORTS, 0, 0, 0};
SolPubkey program_id;
sol_memcpy(&program_id, params.program_id, sizeof(SolPubkey));
const SolInstruction instruction = {&program_id,
arguments, SOL_ARRAY_SIZE(arguments),
data, SOL_ARRAY_SIZE(data)};
sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts));
*accounts[ARGUMENT_INDEX].lamports += 1;
break;
}
case ADD_LAMPORTS: {
*accounts[0].lamports += 1;
break;
*accounts[0].lamports += 1;
break;
}
case TEST_RETURN_DATA_TOO_LARGE: {
sol_log("Test setting return data too long");
// The actual buffer doesn't matter, just pass null
sol_set_return_data(NULL, 1027);
break;
}
default:

View File

@@ -16,3 +16,6 @@ const uint8_t VERIFY_PRIVILEGE_DEESCALATION = 8;
const uint8_t VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER = 9;
const uint8_t VERIFY_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE = 10;
const uint8_t WRITE_ACCOUNT = 11;
const uint8_t SET_RETURN_DATA = 12;
#define RETURN_DATA_VAL "return data test"

View File

@@ -14,6 +14,9 @@ extern uint64_t entrypoint(const uint8_t *input) {
return ERROR_INVALID_ARGUMENT;
}
// on entry, return data must not be set
sol_assert(sol_get_return_data(NULL, 0, NULL) == 0);
if (params.data_len == 0) {
return SUCCESS;
}
@@ -91,6 +94,12 @@ extern uint64_t entrypoint(const uint8_t *input) {
sol_log("return Ok");
return SUCCESS;
}
case SET_RETURN_DATA: {
sol_set_return_data((const uint8_t*)RETURN_DATA_VAL, sizeof(RETURN_DATA_VAL));
sol_log("set return data");
sol_assert(sol_get_return_data(NULL, 0, NULL) == sizeof(RETURN_DATA_VAL));
return SUCCESS;
}
case RETURN_ERROR: {
sol_log("return error");
return 42;

View File

@@ -0,0 +1,40 @@
/**
* @brief return data Syscall test
*/
#include <solana_sdk.h>
#define DATA "the quick brown fox jumps over the lazy dog"
extern uint64_t entrypoint(const uint8_t *input) {
uint8_t buf[1024];
SolPubkey me;
// There should be no return data on entry
uint64_t ret = sol_get_return_data(NULL, 0, NULL);
sol_assert(ret == 0);
// set some return data
sol_set_return_data((const uint8_t*)DATA, sizeof(DATA));
// ensure the length is correct
ret = sol_get_return_data(NULL, 0, &me);
sol_assert(ret == sizeof(DATA));
// try getting a subset
ret = sol_get_return_data(buf, 4, &me);
sol_assert(ret == sizeof(DATA));
sol_assert(!sol_memcmp(buf, "the ", 4));
// try getting the whole thing
ret = sol_get_return_data(buf, sizeof(buf), &me);
sol_assert(ret == sizeof(DATA));
sol_assert(!sol_memcmp(buf, (const uint8_t*)DATA, sizeof(DATA)));
// done
return SUCCESS;
}