Remove appbill. (#358)

This commit is contained in:
Ravin Perera
2022-01-27 10:39:18 +05:30
committed by GitHub
parent 589199a1af
commit cf695af30b
14 changed files with 12 additions and 891 deletions

View File

@@ -12,15 +12,6 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY build)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-result -Wreturn-type")
# We have 2 executable build outputs: appbill and hpcore
#-------appbill-------
add_executable(appbill
src/bill/appbill.cpp
)
#-------hpcore-------
add_subdirectory(src/killswitch)
@@ -83,13 +74,9 @@ target_link_libraries(hpcore
sqlite3
${CMAKE_DL_LIBS} # Needed for stacktrace support
)
add_dependencies(hpcore
appbill
)
add_custom_command(TARGET hpcore POST_BUILD
# COMMAND strip ./build/hpcore
# COMMAND strip ./build/appbill
COMMAND cp ./test/bin/hpws ./test/bin/hpfs ./build/
)
@@ -99,7 +86,7 @@ target_precompile_headers(hpcore PUBLIC src/pchheader.hpp)
# Requires docker to be runnable without 'sudo'
add_custom_target(docker
COMMAND mkdir -p ./test/local-cluster/bin
COMMAND cp ./build/hpcore ./build/appbill ./test/local-cluster/bin/
COMMAND cp ./build/hpcore ./test/local-cluster/bin/
COMMAND cp ./test/bin/libblake3.so ./test/bin/hpws ./test/bin/hpfs ./test/local-cluster/bin/
COMMAND docker build -t hpcore:latest -f ./test/local-cluster/Dockerfile ./test/local-cluster/bin/
)

View File

@@ -148,12 +148,6 @@ struct hp_unl_collection
int npl_fd;
};
struct hp_appbill_config
{
char *mode;
char *bin_args;
};
struct hp_round_limits_config
{
size_t user_input_bytes;
@@ -176,7 +170,6 @@ struct hp_config
char *consensus;
char *npl;
uint16_t max_input_ledger_offset;
struct hp_appbill_config appbill;
struct hp_round_limits_config round_limits;
};
@@ -601,8 +594,6 @@ void hp_free_config(struct hp_config *config)
__HP_FREE(config->environment);
__HP_FREE(config->consensus);
__HP_FREE(config->npl);
__HP_FREE(config->appbill.mode);
__HP_FREE(config->appbill.bin_args);
__HP_FREE(config);
}
@@ -720,7 +711,7 @@ struct hp_config *__hp_read_from_patch_file(const int fd)
*/
int __hp_write_to_patch_file(const int fd, const struct hp_config *config)
{
struct iovec iov_vec[5];
struct iovec iov_vec[4];
// {version: + newline + 4 spaces => 21;
const size_t version_len = 21 + strlen(config->version);
char version_buf[version_len];
@@ -772,15 +763,6 @@ int __hp_write_to_patch_file(const int fd, const struct hp_config *config)
iov_vec[2].iov_base = json_buf;
iov_vec[2].iov_len = json_string_len;
// Appbill field valiues.
const char *appbill_json = " \"appbill\": {\n \"mode\": \"%s\",\n \"bin_args\": \"%s\"\n },\n";
const size_t appbill_json_len = 67 + strlen(config->appbill.mode) + strlen(config->appbill.bin_args);
char appbill_buf[appbill_json_len];
sprintf(appbill_buf, appbill_json, config->appbill.mode, config->appbill.bin_args);
iov_vec[3].iov_base = appbill_buf;
iov_vec[3].iov_len = appbill_json_len;
// Round limits field valies.
const char *round_limits_json = " \"round_limits\": {\n"
@@ -804,11 +786,11 @@ int __hp_write_to_patch_file(const int fd, const struct hp_config *config)
sprintf(round_limits_buf, round_limits_json,
user_input_bytes_str, user_output_bytes_str, npl_output_bytes_str,
proc_cpu_seconds_str, proc_mem_bytes_str, proc_ofd_count_str);
iov_vec[4].iov_base = round_limits_buf;
iov_vec[4].iov_len = round_limits_json_len;
iov_vec[3].iov_base = round_limits_buf;
iov_vec[3].iov_len = round_limits_json_len;
if (ftruncate(fd, 0) == -1 || // Clear any previous content in the file.
pwritev(fd, iov_vec, 5, 0) == -1) // Start writing from begining.
pwritev(fd, iov_vec, 4, 0) == -1) // Start writing from begining.
return -1;
return 0;
@@ -886,23 +868,6 @@ void __hp_populate_patch_from_json_object(struct hp_config *config, const struct
{
__HP_ASSIGN_CHAR_PTR(config->npl, elem);
}
else if (strcmp(k->string, "appbill") == 0)
{
struct json_object_s *object = (struct json_object_s *)elem->value->payload;
struct json_object_element_s *sub_ele = object->start;
do
{
if (strcmp(sub_ele->name->string, "mode") == 0)
{
__HP_ASSIGN_CHAR_PTR(config->appbill.mode, sub_ele);
}
else if (strcmp(sub_ele->name->string, "bin_args") == 0)
{
__HP_ASSIGN_CHAR_PTR(config->appbill.bin_args, sub_ele);
}
sub_ele = sub_ele->next;
} while (sub_ele);
}
else if (strcmp(k->string, "round_limits") == 0)
{
struct json_object_s *object = (struct json_object_s *)elem->value->payload;

View File

@@ -1,715 +0,0 @@
/*
App bill default implementation 1
MSB of return value is reserved for appbill error
bits 1-8 indicate whether or not public keys 0-6 passed appbill check
(to be completed)
*/
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <inttypes.h>
#include <fcntl.h>
#include <sys/uio.h>
#include <sys/ioctl.h>
#include <unistd.h>
#define DEBUG 0
#define KEY_SIZE 32
#define RECORD_SIZE 64
#define FILE_BUFFER_SIZE (64*1024*1024) // this will move 0xffff entries at a time
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
#define TABLE_FILE "./state/appbill.table"
#define TABLE_FILE_2 "appbill.table" // if TABLE_FILE can't be found try here
uint64_t new_balance(uint64_t balance, int64_t to_credit) {
if (to_credit < 0 && -to_credit > balance) {
// catch the wrap around
balance = 0;
} else if (to_credit > 0 && to_credit + balance < balance) {
// and here as well
balance = (uint64_t)-1;
} else {
// normal crediting
balance += to_credit;
}
return balance;
}
void print_hex(uint8_t* data, int len) {
for (int c = 0; c < len; ++c)
printf("%02hhx", data[c]);
}
int compar (const void* p1, const void* p2) {
for (uint8_t* c1 = (uint8_t*)p1, * c2 = (uint8_t*)p2; c1 - (uint8_t*)p1 < KEY_SIZE; ++c1, ++c2)
if (*c1 < *c2) return -1;
else if (*c1 > *c2) return +1;
return 0;
}
int correct_for_ed_keys(int argc, char** argv, int incr, int offset) {
// correct for ed keys
for (int i = 0; i < argc; i += incr) {
int len = strlen(argv[i + offset]);
if (len == KEY_SIZE*2 + 2 && (argv[i][0] == 'e' || argv[i][0] == 'E') && (argv[i][1] == 'd' || argv[i][1] == 'D'))
argv[i]+=2;
else if (len != KEY_SIZE*2) {
fprintf(stderr, "appbill received an invalid key %s, expected len=%d actual len=%d\n", argv[i], KEY_SIZE*2, len);
return 128;
}
}
return 0;
}
void key_from_hex(uint8_t* key_in, uint8_t* key_out) {
for (int c = 0; c < 32; ++c) {
uint8_t hn = tolower(key_in[c*2]);
uint8_t ln = tolower(key_in[c*2+1]);
hn = ( hn >= 'a' ? 10 + (hn - 'a') : hn - '0');
ln = ( ln >= 'a' ? 10 + (ln - 'a') : ln - '0');
key_out[c] = (hn * 16) + ln;
}
}
uint64_t uint64_from_bytes(uint8_t* data) {
return
(((uint64_t)(data[0]))<<56) +
(((uint64_t)(data[1]))<<48) +
(((uint64_t)(data[2]))<<40) +
(((uint64_t)(data[3]))<<32) +
(((uint64_t)(data[4]))<<24) +
(((uint64_t)(data[5]))<<16) +
(((uint64_t)(data[6]))<<8) +
(((uint64_t)(data[7])));
}
void uint64_to_bytes(uint8_t* dest, uint64_t x) {
for (int j = 0; j < 8; j++) {
*(dest + (7-j)) = x & 0xff;
x >>= 8;
}
}
// returns 1 if found and populates entry_out, 0 if not found
int binary_file_search(FILE* f, uint8_t* key, uint8_t* entry_out, uint64_t* balance_out, size_t* recordno_out, int* error_out) {
*error_out = 0;
*balance_out = 0;
// find file size
fseek(f, (size_t)0, SEEK_END);
size_t tablesize = ftell(f);
int recordcount = tablesize / RECORD_SIZE;
size_t record = recordcount/2;
size_t search_size = (recordcount == 1 ? 1 : record);
uint8_t entry_array[RECORD_SIZE*2];
uint8_t* entry = entry_array + RECORD_SIZE;
uint8_t* prev_entry = entry_array;
while (search_size) {
if (record == 0) {
// special case, no previous record will be available
fseek(f, 0, SEEK_SET);
int r = fread(entry, 1, RECORD_SIZE, f);
if (r != RECORD_SIZE) {
fprintf(stderr, "failed to read %d bytes\n", RECORD_SIZE);
*error_out = 128;
return 0;
}
memset(prev_entry, 0, RECORD_SIZE);
} else {
fseek(f, (record - 1) * RECORD_SIZE, SEEK_SET);
int r = fread(entry_array, 1, RECORD_SIZE * 2, f);
if (r != RECORD_SIZE*2) {
fprintf(stderr, "failed to read %d bytes\n", RECORD_SIZE*2);
*error_out = 128;
return 0;
}
}
int search_direction = compar(entry, key);
int check_prev = compar(prev_entry, key);
if (DEBUG) {
printf("prevrec: %lu\tkey: ", record-1);
print_hex(prev_entry, RECORD_SIZE);
printf("\tsearch dir: %d\n", check_prev);
printf("record: %lu\tkey: ", record);
print_hex(entry, RECORD_SIZE);
printf("\tsearch dir: %d\n", search_direction);
}
if (search_direction == 0) {
// get the balance
*balance_out = uint64_from_bytes(entry+32);
if (DEBUG) printf("entry found at record %lu with balance=%lu\n", record, *balance_out);
if (entry_out)
for (int i = 0; i < RECORD_SIZE; ++i)
entry_out[i] = entry[i];
*recordno_out = record;
return 1;
}
if (check_prev == 0 && record != 0) {
// get the balance
*balance_out = uint64_from_bytes(prev_entry+32);
if (DEBUG) printf("entry found at record %lu with balance=%lu\n", record-1, *balance_out);
if (entry_out)
for (int i = 0; i < RECORD_SIZE; ++i)
entry_out[i] = prev_entry[i];
*recordno_out = record-1;
return 1;
}
if (search_direction != check_prev ) {
// record doesn't exist, it would go between these two records if it did
if (DEBUG) printf("record doesn't exist, would go between\n");
*recordno_out = record;
return 0;
}
search_size /= 2;
if (search_size < 1) search_size = 1;
if (search_direction > 0) {
record -= search_size;
} else {
record += search_size;
}
if (DEBUG) printf("search size: %lu, current record: %lu, check_prev: %d, dir: %d\n", search_size, record, check_prev, search_direction);
if (record < 0 || record >= recordcount) {
if (DEBUG)
fprintf(stderr, "could not find key record: %lu, recordcount: %d\n", record, recordcount);
if (entry_out)
for (int i = 0; i < RECORD_SIZE; ++i)
entry_out[i] = entry[i];
*recordno_out = record;
*balance_out = 0;
return 0;
}
*recordno_out = record;
}
return 0;
}
// inserts above recordno
// warning: expensive, must copy remaining chunk of file down
int insert_record(FILE* f, uint8_t* entry, size_t recordno) {
static uint8_t* file_buffer = 0; // we're going to reuse this piece of memory until appbill closes so just alloc once
if (!file_buffer)
file_buffer = (uint8_t*)malloc(MAX(FILE_BUFFER_SIZE, RECORD_SIZE));
if (DEBUG) printf("insert_record called with recno=%lu\n", recordno);
//uint8_t* buffer[FILE_BUFFER_SIZE];
size_t offset = recordno * RECORD_SIZE;
fseek(f, (size_t)0, SEEK_END);
size_t size = ftell(f);
// inserting 64 bytes at offset
// we need to first compute the short move at the end
long long tomove = size - offset;
if (tomove <= 0) {
// write the record at the end of the file
if (DEBUG) printf("new write\n");
return fwrite(entry, RECORD_SIZE, 1, f);
} else {
size_t endpiece = tomove % FILE_BUFFER_SIZE;
if (DEBUG) printf("endpiece %lu\n", endpiece);
// the endpiece is always moved, some times there are also no further pieces to move
fseek(f, size - endpiece, SEEK_SET);
fread(file_buffer, 1, endpiece, f);
fseek(f, size - endpiece + RECORD_SIZE, SEEK_SET);
fwrite(file_buffer, 1, endpiece, f);
size_t cursor = size - endpiece;
tomove -= endpiece;
// now if there are any other pieces to move along we can move them
if (size - endpiece >= FILE_BUFFER_SIZE)
for (size_t cursor = size - endpiece - FILE_BUFFER_SIZE; cursor > offset; cursor -= FILE_BUFFER_SIZE) {
if (DEBUG) printf("moving %d sized piece at %lu to %lu - size: %lu - offset: %lu\n", FILE_BUFFER_SIZE, cursor, cursor + RECORD_SIZE, size, offset);
fseek(f, cursor, SEEK_SET);
fread(file_buffer, 1, FILE_BUFFER_SIZE, f);
fseek(f, cursor + RECORD_SIZE, SEEK_SET);
fwrite(file_buffer, 1, FILE_BUFFER_SIZE, f);
}
// not sure why we need to move this last row down, something is slightly wrong with the math above?
fseek(f, offset, SEEK_SET);
fread(file_buffer, 1, RECORD_SIZE, f);
fseek(f, offset + RECORD_SIZE, SEEK_SET);
fwrite(file_buffer, 1, RECORD_SIZE, f);
// finally it's safe to emplace our data
fseek(f, offset, SEEK_SET);
return fwrite(entry, 1, RECORD_SIZE, f);
}
}
int valid_hex(char* hex, int len) {
char* x = hex;
for (; (x-hex) < len && *x != '\0' && *x != '\n' && *x >= '0' && (*x <= '9' || *x >= 'a' && *x <= 'f' || *x >= 'A' && *x <= 'F'); ++x);
return x-hex == len;
}
int pass_through_mode(int argc, char** argv) {
// full argc, argv are in tact in this mode
if (DEBUG)
printf("pass through mode\n");
int teepipe[2];
int error = pipe(teepipe);
if (error) {
fprintf(stderr, "appbill pass through could not create a pipe for teeing fdlist\n");
return 128;
}
FILE* teepipeout = fdopen(teepipe[1], "w");
// todo: make this all zero copy when someone has time to debug tee and vmsplice readmode
// for now we'll just do a dumb read
FILE* f = fopen(TABLE_FILE, "rb+");
if (!f)
f = fopen(TABLE_FILE_2, "rb+");
if (!f) {
fprintf(stderr, "could not open %s or %s\n", TABLE_FILE, TABLE_FILE_2);
return 128;
}
char buf[1024];
int mode = 0;
int bytes_read = 0;
int counter = 0;
int toskip = 0;
uint8_t key[KEY_SIZE];
do {
char c = 0;
bytes_read = 0;
while ( (c = getc( stdin )) != EOF && c != ',' && c != '{' && c != '}' && c != '[' && c != ']' && c != '\n' && c != ':' && bytes_read < 1023 ) {
buf[bytes_read++] = c;
putc(c, teepipeout); // make a copy for the next program
}
if (c != EOF) putc(c, teepipeout); // make a copy for the next program
if (c == EOF)
break;
if (mode == 2)
continue;
buf[bytes_read] = '\0';
if (mode == 0 && strcmp("\"usrfd\"", buf) == 0) {
mode = 1;
continue;
} else if ( mode == 1 && c == '}' ) {
mode = 2;
continue;
}
if (buf[0] == '\0' || !mode)
continue;
++counter %= 3;
// this runs if there's an error in the user's public key
if (toskip) {
toskip--;
continue;
}
if (DEBUG)
printf("mode=%d counter=%d component `%s`\n", mode, counter%3, buf);
if (counter == 1) {
// this is the user key
// remove trailing "
if (!buf[strlen(buf)-1] == '"')
continue;
buf[strlen(buf)-1] = '\0';
// check the key is valid
if (DEBUG)
printf("key length: %lu, proper length: %d\n", strlen(buf+3), KEY_SIZE*2);
if (DEBUG)
printf("hex: `%s`\n", buf+3);
if (strlen(buf+3) != KEY_SIZE*2 || !valid_hex(buf+3, KEY_SIZE*2)) {
toskip = 2;
if (DEBUG)
printf("invald public key %s\n", buf+3);
continue;
}
key_from_hex((uint8_t*)buf+3, key);
if (DEBUG) {
printf("parsed key: ");
print_hex(key, KEY_SIZE);
printf("\n");
}
} else if (counter == 2) {
// this is the user's input fd
int userfd = 0;
if (!sscanf(buf, "%d", &userfd))
continue;
if (DEBUG) printf("mode=2 userfd=%d\n", userfd);
// there might be some bytes pending on this input, if there are we need to bill for them, one coin per byte
/*int nbytes = 0;
ioctl(userfd, FIONREAD, &nbytes);*/
int64_t to_bill = 0; // and one coin per round for being connected too // no change that to 0 because otherwise malicious nodes can drain accounts!
//todo: replace all this rubbish with a properly tested zero copy approach
int userpipe[2];
pipe(userpipe); //todo: handle possible error condition here
FILE* userfile = fdopen(userfd, "r");
FILE* newuserfile = fdopen(userpipe[1], "w");
char x = 0;
while ((x = getc(userfile)) != EOF) {
if (to_bill < (uint64_t)-1)
to_bill++;
putc(x, newuserfile);
}
fclose(newuserfile);
fclose(userfile);
dup2(userpipe[0], userfd);
if (DEBUG)
printf("tobill: %lu\n", to_bill);
// commence billing
int error = 0;
uint64_t balance = 0;
size_t recordno = 0;
uint8_t entry[64];
if (binary_file_search(f, key, entry, &balance, &recordno, &error)) {
// key already exists, update it
if (DEBUG) printf("writing 64 bytes at record:%lu\n", recordno);
fseek(f, recordno*RECORD_SIZE, SEEK_SET);
uint64_t balance = uint64_from_bytes(entry+32);
balance = new_balance(balance, -to_bill);
uint64_to_bytes(entry+32, balance);
fwrite(entry, RECORD_SIZE, 1, f);
} else {
// is user doesn't exist this is an error but we can't do anything about it in passthrough mode so ignore
if (DEBUG) {
printf("user not found key:");
print_hex(key, KEY_SIZE);
printf("\n");
}
continue;
}
}
} while (!feof(stdin));
fflush(teepipeout);
close(teepipe[1]);
dup2(teepipe[0], 0);
fclose(f);
execv(argv[1], argv+1);
return 128;
}
int credit_mode(int argc, char** argv) {
// argc,v start from first useful arguments
if (argc == 0 || argc % 2 == 1) {
fprintf(stderr, "appbill credit mode requires args like: public_key amount public key amount\n");
return 128;
}
if (correct_for_ed_keys(argc, argv, 2, 0))
return 128;
// sanity check our inputs
for (int i = 0; i < argc; i += 2) {
for (char* x = argv[i];; ++x) {
if ( x - argv[i] == KEY_SIZE*2 && *x != '\0' ) {
fprintf(stderr, "appbill was supplied an invalid public key\n");
return 128;
}
if (*x >= 'a' && *x <= 'f' || *x >= '0' && *x <= '9' || *x >= 'A' && *x <= 'F')
continue;
if (*x == '\0')
break;
fprintf(stderr, "appbill was supplied an invalid public key (not hex) char=%c\n", *x);
return 128;
}
for (char* x = argv[i+1]; *x; ++x)
if ( ! (*x >= '0' && *x <= '9' || *x == '-') ) {
fprintf(stderr, "appbill was supplied invalid amount to credit, must be decimal integer entry=%s\n", argv[i+1]);
return 128;
}
int64_t to_credit = 0;
if (!sscanf(argv[i+1], "%ld", &to_credit)) {
fprintf(stderr, "appbill was supplied invalid amount to credit, must be decimal integer entry=%s\n", argv[i+1]);
return 128;
}
}
FILE* f = fopen(TABLE_FILE, "rb+");
if (!f)
f = fopen(TABLE_FILE_2, "rb+");
if (!f) {
fprintf(stderr, "could not open %s or %s\n", TABLE_FILE, TABLE_FILE_2);
return 128;
}
// now the expensive bit
for (int i = 0; i < argc; i += 2) {
uint8_t key[32];
key_from_hex((uint8_t*)argv[i], key);
int64_t to_credit = 0;
if (!sscanf(argv[i+1], "%ld", &to_credit)) // this has been sanity checked above
continue;
int error = 0;
uint64_t balance = 0;
size_t recordno = 0;
uint8_t entry[64];
if (binary_file_search(f, key, entry, &balance, &recordno, &error)) {
// key already exists, update it
if (DEBUG) printf("writing 64 bytes at record:%lu\n", recordno);
fseek(f, recordno*RECORD_SIZE, SEEK_SET);
uint64_t balance = uint64_from_bytes(entry+32);
balance = new_balance(balance, to_credit);
uint64_to_bytes(entry+32, balance);
fwrite(entry, RECORD_SIZE, 1, f);
} else {
if (DEBUG) printf("key needs to be inserted\n");
// key doesn't exist, insert it
uint8_t new_entry[RECORD_SIZE];
for (int i = 0; i < KEY_SIZE; ++i)
new_entry[i] = key[i];
uint64_t balance = new_balance(0, to_credit);
uint64_to_bytes(new_entry+32, balance);
for (int i = KEY_SIZE + 8; i < RECORD_SIZE; ++i)
new_entry[i] = 0;
// get the existing entry
fseek(f, recordno * RECORD_SIZE, SEEK_SET);
fread(entry, 1, RECORD_SIZE, f);
int insert_direction = compar(entry, new_entry);
insert_record(f, new_entry, recordno + (insert_direction < 0 ? 1 : 0));
}
}
fclose(f);
return 0;
}
int check_mode(int argc, char** argv, int print_balances) {
if (DEBUG)
printf("check mode\n");
if (argc > 14 && !print_balances) {
fprintf(stderr, "appbill can only take up to 7 keys at a time\n");
return 128;
}
if (argc == 0 || argc % 2 == 1 && !print_balances) {
fprintf(stderr, "appbill check mode requires a public key%s\n", (print_balances ? "" : " and an amount to check against"));
return 128;
}
if (correct_for_ed_keys(argc, argv, (print_balances ? 1 : 2), 0))
return 128;
for (int i = 0; i < argc; i+= ( print_balances ? 1 : 2 )) {
// check the pubkey
for (char* x = argv[i];; ++x) {
if ( x - argv[i] == KEY_SIZE*2 && *x != '\0' ) {
fprintf(stderr, "appbill was supplied an invalid public key\n");
return 128;
}
if (*x >= 'a' && *x <= 'f' || *x >= '0' && *x <= '9' || *x >= 'A' && *x <= 'F')
continue;
if (*x == '\0')
break;
fprintf(stderr, "appbill was supplied an invalid public key (not hex) char=%c\n", *x);
return 128;
}
if (print_balances)
continue;
// check the bytecount
for (char* x = argv[i+1]; *x != '\0'; ++x) {
if (*x >= '0' && *x <= '9')
continue;
fprintf(stderr, "appbill was supplied invalid byte count %s\n", argv[i+1]);
return 128;
}
}
if (print_balances)
printf("{\n");
// open app bill table
FILE* f = fopen(TABLE_FILE, "rb");
if (!f)
f = fopen(TABLE_FILE_2, "rb");
if (!f) {
fprintf(stderr, "could not open table file at %s or %s\n", TABLE_FILE, TABLE_FILE_2);
return 128;
}
int bits[7];
for (int i = 0; i < 7; ++i)
bits[i] = 0;
// loop keys, check balances
for (int i = 0, j = 0; i < argc; i+=( print_balances ? 1 : 2 ), ++j) {
// convert the argv from hex to binary
uint8_t key[32];
key_from_hex((uint8_t*)argv[i], key);
uint32_t bytecount = 0;
if (!print_balances)
sscanf(argv[i+1], "%d", &bytecount);
int error = 0;
uint64_t balance = 0;
size_t recordno = 0;
if (binary_file_search(f, key, 0, &balance, &recordno, &error)) {
if (j < 7) bits[j] = balance > bytecount;
if (print_balances) {
printf("\t\"");
print_hex(key, KEY_SIZE);
printf("\": %lu%s", balance, (i == argc-1 ? "\n": ",\n"));
}
}
}
if (print_balances)
printf("}");
fclose(f);
if (DEBUG)
for (int i = 0; i < 7; ++i)
printf("bit %d: %d\n", i, bits[i]);
return bits[0] * 64 + bits[1] * 32 + bits[2] * 16 + bits[3] * 8 + bits[4] * 4 + bits[5] * 2 + bits[6];
}
int main(int argc, char** argv) {
// input checks
int mode = 0; // mode 0 is passthrough [ writes ]
if (argc >= 2 && strcmp(argv[1], "--credit") == 0)
mode = 1; // mode 1 credit mode [ writes ]
if (argc >= 2 && strcmp(argv[1], "--check") == 0)
mode = 2; // mode 2 check mode [ read only ]
if (argc >= 2 && strcmp(argv[1], "--balance") == 0)
mode = 3; // mode 3 balance mode [ read only ]
if (mode == 0) {
if (argc < 2) {
fprintf(stderr, "appbill requires an executable to pass execution to as an argument when running in pass through mode\n");
return 128;
}
return pass_through_mode(argc, argv);
}
if (argc < 3) {
fprintf(stderr, "appbill was not supplied sufficient arguments\n");
return 128;
}
argc-=2;
argv+=2;
if (mode == 1)
return credit_mode(argc, argv);
if (mode == 2 || mode == 3)
return check_mode(argc, argv, mode == 3);
fprintf(stderr, "unknown mode, execution should not reach here\n");
return 128;
}

View File

@@ -955,11 +955,6 @@ namespace conf
jdoc.insert_or_assign("npl", contract.is_npl_public ? PUBLIC : PRIVATE);
jdoc.insert_or_assign("max_input_ledger_offset", contract.max_input_ledger_offset);
jsoncons::ojson appbill;
appbill.insert_or_assign("mode", contract.appbill.mode);
appbill.insert_or_assign("bin_args", contract.appbill.bin_args);
jdoc.insert_or_assign("appbill", appbill);
jsoncons::ojson round_limits;
round_limits.insert_or_assign("user_input_bytes", contract.round_limits.user_input_bytes);
round_limits.insert_or_assign("user_output_bytes", contract.round_limits.user_output_bytes);
@@ -1077,10 +1072,6 @@ namespace conf
contract.is_npl_public = jdoc["npl"] == PUBLIC;
contract.max_input_ledger_offset = jdoc["max_input_ledger_offset"].as<uint16_t>();
jpath = "contract.appbill";
contract.appbill.mode = jdoc["appbill"]["mode"].as<std::string>();
contract.appbill.bin_args = jdoc["appbill"]["bin_args"].as<std::string>();
jpath = "contract.round_limits";
contract.round_limits.user_input_bytes = jdoc["round_limits"]["user_input_bytes"].as<size_t>();
contract.round_limits.user_output_bytes = jdoc["round_limits"]["user_output_bytes"].as<size_t>();
@@ -1105,13 +1096,7 @@ namespace conf
if (!contract.bin_args.empty())
util::split_string(contract.runtime_binexec_args, contract.bin_args, " ");
contract.runtime_binexec_args.insert(contract.runtime_binexec_args.begin(), contract.bin_path);
contract.appbill.runtime_args.clear();
// Populate runtime app bill args.
if (!contract.appbill.bin_args.empty())
util::split_string(contract.appbill.runtime_args, contract.appbill.bin_args, " ");
contract.appbill.runtime_args.insert(contract.appbill.runtime_args.begin(), contract.appbill.mode);
// Uncomment for docker-based execution.
// std::string volumearg;
// volumearg.append("type=bind,source=").append(ctx.contract_hpfs_dir).append(",target=/hpfs");

View File

@@ -145,15 +145,6 @@ namespace conf
history_configuration history_config; // Holds history config values. Only applicable if history=custom.
};
struct appbill_config
{
std::string mode; // Binary to execute for appbill.
std::string bin_args; // Any arguments to supply to appbill binary by default.
// Config element which are initialized in memory (This is not directly loaded from the config file)
std::vector<std::string> runtime_args; // Appbill execution args used during runtime.
};
struct round_limits_config
{
size_t user_input_bytes = 0; // Max contract input bytes per user per round.
@@ -188,7 +179,6 @@ namespace conf
bool is_consensus_public = false; // If true, consensus are broadcasted to non-unl nodes as well.
bool is_npl_public = false; // If true, npl messages are broadcasted to non-unl nodes as well.
uint16_t max_input_ledger_offset; // Maximum ledger sequence number offset that can be specified in the input.
appbill_config appbill;
round_limits_config round_limits;
// Config element which are initialized in memory (This is not directly loaded from the config file)

View File

@@ -101,7 +101,6 @@ namespace msg::usrmsg
constexpr const char *REASON_BAD_MSG_FORMAT = "bad_msg_format";
constexpr const char *REASON_INVALID_MSG_TYPE = "invalid_msg_type";
constexpr const char *REASON_BAD_SIG = "bad_sig";
constexpr const char *REASON_APPBILL_BALANCE_EXCEEDED = "appbill_balance_exceeded";
constexpr const char *REASON_MAX_LEDGER_EXPIRED = "max_ledger_expired";
constexpr const char *REASON_MAX_LEDGER_OFFSET_EXCEEDED = "max_ledger_offset_exceeded";
constexpr const char *REASON_NONCE_EXPIRED = "nonce_expired";

View File

@@ -192,19 +192,10 @@ namespace sc
// Write the contract execution args from HotPocket to the stdin (0) of the contract process.
write_contract_args(ctx, user_inputs_fd);
const bool using_appbill = !ctx.args.readonly && !conf::cfg.contract.appbill.mode.empty();
int execv_len = conf::cfg.contract.runtime_binexec_args.size() + 1;
if (using_appbill)
execv_len += conf::cfg.contract.appbill.runtime_args.size();
// Fill process args.
int execv_len = conf::cfg.contract.runtime_binexec_args.size() + 1;
char *execv_args[execv_len];
int j = 0;
if (using_appbill)
{
for (int i = 0; i < conf::cfg.contract.appbill.runtime_args.size(); i++, j++)
execv_args[i] = conf::cfg.contract.appbill.runtime_args[i].data();
}
for (int i = 0; i < conf::cfg.contract.runtime_binexec_args.size(); i++, j++)
execv_args[j] = conf::cfg.contract.runtime_binexec_args[i].data();

View File

@@ -472,12 +472,6 @@ namespace usr
return (nonce_status == 1 ? msg::usrmsg::REASON_NONCE_EXPIRED : msg::usrmsg::REASON_ALREADY_SUBMITTED);
}
if (!verify_appbill_check(user_pubkey, new_total_input_size))
{
LOG_DEBUG << "User input app bill balance exceeded.";
return msg::usrmsg::REASON_APPBILL_BALANCE_EXCEEDED;
}
// Reaching here means the input is successfully validated and we can submit it to consensus.
// Copy the input data into the input store. Contract will read the input from this location.
@@ -489,69 +483,6 @@ namespace usr
return NULL; // Success. No reject reason.
}
/**
* Executes the appbill and verifies whether the user has enough account balance to process the provided input.
* @param pubkey User binary pubkey.
* @param input_len Total bytes length of user input.
* @return Whether the user is allowed to process the input or not.
*/
bool verify_appbill_check(std::string_view pubkey, const size_t input_len)
{
// If appbill not enabled always green light the input.
if (conf::cfg.contract.appbill.mode.empty())
return true;
// execute appbill in --check mode to verify this user can submit a packet/connection to the network
// todo: this can be made more efficient, appbill --check can process 7 at a time
// Fill appbill args
const int len = conf::cfg.contract.appbill.runtime_args.size() + 4;
char *execv_args[len];
for (int i = 0; i < conf::cfg.contract.appbill.runtime_args.size(); i++)
execv_args[i] = conf::cfg.contract.appbill.runtime_args[i].data();
char option[] = "--check";
execv_args[len - 4] = option;
// add the hex encoded public key as the last parameter
std::string hexpubkey = util::to_hex(pubkey);
std::string inputsize = std::to_string(input_len);
execv_args[len - 3] = hexpubkey.data();
execv_args[len - 2] = inputsize.data();
execv_args[len - 1] = NULL;
int pid = fork();
if (pid == 0)
{
// appbill process.
util::fork_detach();
// before execution chdir into a valid the latest state data directory that contains an appbill.table
const std::string appbill_dir = sc::contract_fs.rw_dir + sc::STATE_DIR_PATH;
chdir(appbill_dir.c_str());
int ret = execv(execv_args[0], execv_args);
std::cerr << errno << ": Appbill process execv failed.\n";
return false;
}
else
{
// app bill in check mode takes a very short period of time to execute, typically 1ms
// so we will blocking wait for it here
int status = 0;
waitpid(pid, &status, 0); //todo: check error conditions here
status = WEXITSTATUS(status);
if (status != 128 && status != 0)
{
// this user's key passed appbill
return true;
}
else
{
// user's key did not pass, do not add to user input candidates
LOG_DEBUG << "Appbill validation failed " << hexpubkey << " return code was " << status;
return false;
}
}
}
/**
* Sends any change event notifications to relevant users who are currently connected to the node.
*/

View File

@@ -108,8 +108,6 @@ namespace usr
const char *validate_user_input_submission(const std::string &user_pubkey, const usr::extracted_user_input &extracted_input,
const uint64_t lcl_seq_no, size_t &total_input_size, std::string &ordered_hash, util::buffer_view &input);
bool verify_appbill_check(std::string_view pubkey, const size_t input_len);
void dispatch_change_events();
} // namespace usr

View File

@@ -11,6 +11,6 @@ RUN apt-get update \
&& rm -rf /var/lib/apt/lists/* \
&& mkdir /usr/local/bin/hotpocket
COPY hpcore hpfs hpws appbill /usr/local/bin/hotpocket/
COPY hpcore hpfs hpws /usr/local/bin/hotpocket/
ENTRYPOINT ["/usr/local/bin/hotpocket/hpcore"]

View File

@@ -8,7 +8,7 @@ njsfile="Dockerfile.ubt.20.04-njs"
# Prepare build context
tmp=$(mktemp -d)
cp $hpcoredir/build/{hpcore,appbill} $hpcoredir/test/bin/{hpfs,hpws,libblake3.so} $tmp/
cp $hpcoredir/build/hpcore $hpcoredir/test/bin/{hpfs,hpws,libblake3.so} $tmp/
strip $tmp/hpcore
# Remove the revision component from hp version to make up the image version.

View File

@@ -1,7 +1,7 @@
# We are going with Hot Pocket NodeJs docker image because sample contracts need NodeJs to run.
FROM hotpocketdev/hotpocket:0.5-ubt.20.04-njs.14
COPY hpcore hpfs hpws appbill /usr/local/bin/hotpocket/
COPY hpcore hpfs hpws /usr/local/bin/hotpocket/
ENTRYPOINT ["/usr/local/bin/hotpocket/hpcore"]

View File

@@ -114,11 +114,7 @@ do
environment: '', \
roundtime: $roundtime, \
consensus: 'public', \
npl: 'public', \
appbill: { \
mode: '', \
bin_args: '' \
}
npl: 'public'
}, null, 2)")
mesh_json=$(node -p "JSON.stringify({...require('./tmp.json').mesh, \
@@ -148,9 +144,8 @@ do
popd > /dev/null 2>&1
# Copy the contract files and appbill.
# Copy the contract files.
eval "cp -r $copyfiles ./node$n/contract_fs/seed/state/"
cp ../bin/appbill ./node$n/contract_fs/seed/state/
done
# Function to generate JSON array string while skiping a given index.
@@ -210,10 +205,6 @@ do
mkdir -p ./node$i/contract_fs/seed/ > /dev/null 2>&1
pushd ./node$i/contract_fs/seed/state/ > /dev/null 2>&1
# Load credit balance for user for appbill testing purposes.
>appbill.table
../../../../../bin/appbill --credit "705bf26354ee4c63c0e5d5d883c07cefc3196d049bd3825f827eb3bc23ead035" 10000
# Copy any more initial state files for testing.
# cp ~/my_big_file .

View File

@@ -335,7 +335,6 @@ if [ $mode = "new" ] || [ $mode = "updatebin" ]; then
rm -r hpfiles > /dev/null 2>&1
mkdir -p $hpfiles/{bin,ssl,nodejs_contract}
strip $hpcore/build/hpcore
strip $hpcore/build/appbill
cp $hpcore/build/{hpcore,hpfs,hpws} $hpfiles/bin/
pushd $hpcore/examples/nodejs_contract/ > /dev/null 2>&1
npm install